Files
v2ray_simple/proxy/socks5/server.go
e1732a364fed a9a746ba2f 多项对接口的修改,为ss做准备:
取消Client的LocalAddr,改为 LocalTCPAddr 和 LocalUDPAddr
删除direct中的对应条目。这样可更清晰地配置双本地地址

将设置sendthrough设置双地址的代码移动到 proxy.newClient函数

这样不仅direct可指定不同的tcp和udp的本地地址,任何client协议都可以了

为ClientCreator 接口 添加 UseUDPAsMsgConn 方法,direct和ss返回true

在ss的client的EstablishUDPChannel进行自行拨号

在ss的server建立后,自动循环监听udp,绕过vs的基本监听机制。因为vs架构的限制,一个代理只能有一个唯一的传输层协议。

ServerCreator 接口 添加 AfterCommonConfServer 方法
2022-12-03 23:51:51 +08:00

564 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package socks5
import (
"bytes"
"errors"
"fmt"
"net"
"net/url"
"time"
"github.com/e1732a364fed/v2ray_simple/netLayer"
"github.com/e1732a364fed/v2ray_simple/utils"
"go.uber.org/zap"
"github.com/e1732a364fed/v2ray_simple/proxy"
)
// 解读如下:
// ver5, rep0表示成功, rsv0, atyp(1, 即ipv4), BND.ADDR ipv4(0,0,0,0), BND.PORT(0, 2字节)
// 这个 BND.ADDR和port 按理说不应该传0的不过如果只作为本地tcp代理的话应该不影响
var commmonTCP_HandshakeReply = []byte{Version5, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
func init() {
proxy.RegisterServer(Name, &ServerCreator{})
}
type Server struct {
proxy.Base
*utils.MultiUserMap
TrustClient bool //如果为true则每次握手读取客户端响应前, 不设置deadline. 这能减少一些开销, 但要保证客户端确实可信,不是坏蛋。如果客户端无法被信任,比如在公网或者 不止你一个人使用则一定要为false否则会被攻击导致Server卡住, 造成大量悬垂连接。
}
func NewServer() *Server {
s := &Server{
MultiUserMap: utils.NewMultiUserMap(),
}
s.StoreKeyByStr = true
return s
}
type ServerCreator struct{ proxy.CreatorCommonStruct }
// true
func (ServerCreator) MultiTransportLayer() bool {
return true
}
func (ServerCreator) URLToListenConf(u *url.URL, lc *proxy.ListenConf, format int) (*proxy.ListenConf, error) {
if format != proxy.UrlStandardFormat {
return lc, utils.ErrUnImplemented
}
if lc == nil {
lc = &proxy.ListenConf{}
}
if p, set := u.User.Password(); set {
user := u.User.Username()
pass := p
lc.Users = append(lc.Users, utils.UserConf{
User: user,
Pass: pass,
})
}
return lc, nil
}
func (ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) {
s := NewServer()
if str := lc.Uuid; str != "" {
var userPass utils.UserPass
if userPass.InitWithStr(str) {
s.AddUser(&userPass)
} else {
if ce := utils.CanLogWarn("socks5: user and password format malformed. Will not use default uuid"); ce != nil {
ce.Write()
}
}
}
if len(lc.Users) > 0 {
for _, uc := range lc.Users {
up := utils.NewUserPass(uc)
s.AddUser(up)
}
}
return s, nil
}
func (*Server) Name() string { return Name }
// 若没有IDMap则直接写入AuthNone响应否则返回错误
func (s *Server) authNone(underlay net.Conn) (returnErr error) {
var err error
if len(s.IDMap) == 0 {
_, err = underlay.Write([]byte{Version5, AuthNone})
if err != nil {
returnErr = fmt.Errorf("failed to write hello response: %w", err)
return
}
} else {
returnErr = errors.New("require Password but got AuthNone")
}
return
}
// 处理tcp收到的请求. 注意, udp associate后的 udp请求并不 直接 通过此函数处理, 而是由 UDPConn 处理
func (s *Server) Handshake(underlay net.Conn) (result net.Conn, udpChannel netLayer.MsgConn, targetAddr netLayer.Addr, returnErr error) {
if !s.TrustClient {
if err := proxy.SetCommonReadTimeout(underlay); err != nil {
returnErr = err
return
}
defer netLayer.PersistConn(underlay)
}
bs := utils.GetMTU()
defer utils.PutBytes(bs)
// Read hello message
// 一般免密握手包发来的是 [5 1 0]
n, err := underlay.Read(bs)
if err != nil || n < 3 {
returnErr = fmt.Errorf("failed to read hello: %v, %d", err, n)
return
}
version := bs[0]
if version != Version5 {
returnErr = fmt.Errorf("unsupported socks version %v", version)
return
}
nmethods := int(bs[1])
if nmethods == 0 || n < 2+nmethods {
underlay.Write([]byte{Version5, AuthNoACCEPTABLE})
returnErr = fmt.Errorf("nmethods==0||n < 2+nmethods, %d, n=%d", nmethods, n)
return
}
authed := false
netLayer.PersistConn(underlay)
var dealtNone, dealtPass bool //所有method只能给出一次否则就是非法。
For:
for i := 2; i < 2+nmethods; i++ {
method := bs[i]
switch method {
//只支持 none和Password两种method其它method给出的话也不能认为是非法只不过不加以处理。
case AuthNone:
if dealtNone {
break For
}
dealtNone = true
if len(s.IDMap) != 0 {
continue
} else {
returnErr = s.authNone(underlay)
if returnErr != nil {
return
} else {
authed = true
break For
}
}
case AuthPassword:
if dealtPass {
break For
}
dealtPass = true
if len(s.IDMap) == 0 {
returnErr = errors.New("not require Password but got AuthPassword")
continue
} else {
_, err = underlay.Write([]byte{Version5, AuthPassword})
if err != nil {
returnErr = fmt.Errorf("failed to write hello response: %w", err)
continue
}
}
var authBs []byte
if n == 2+nmethods {
if !s.TrustClient {
if err := proxy.SetCommonReadTimeout(underlay); err != nil {
returnErr = err
return
}
}
n, err = underlay.Read(bs)
if !s.TrustClient {
netLayer.PersistConn(underlay)
}
if err != nil {
returnErr = fmt.Errorf("read socks5 auth failed: %w", err)
continue
}
authBs = bs[:n]
} else {
authBs = bs[3:n]
}
if len(authBs) < 5 || authBs[0] != 1 || authBs[1] == 0 {
returnErr = fmt.Errorf("read socks5 auth failed: %w", err)
continue
}
ulen := authBs[1]
if int(ulen)+2 > n {
returnErr = fmt.Errorf("read socks5 auth failed, ulen too long but data too short %d", n)
continue
}
ubytes := authBs[2 : 2+ulen]
plen := authBs[2+ulen]
if int(ulen)+2+int(plen) > n {
returnErr = fmt.Errorf("read socks5 auth failed, ulen too long but data too short %d", n)
continue
}
pbytes := authBs[2+ulen+1 : 2+ulen+1+plen]
thisUP := utils.NewUserPassByData(ubytes, pbytes)
if s.AuthUserByStr(thisUP.AuthStr()) != nil {
_, err = underlay.Write([]byte{1, 0})
if err != nil {
returnErr = fmt.Errorf("failed to write auth response: %w", err)
return
}
authed = true
returnErr = nil
break For
} else {
_, err = underlay.Write([]byte{1, 1})
returnErr = err
return
}
}
}
if !authed {
underlay.Write([]byte{Version5, AuthNoACCEPTABLE})
returnErr = fmt.Errorf("socks5 not authed , %w", returnErr)
return
} else {
returnErr = nil
}
if !s.TrustClient {
if err := proxy.SetCommonReadTimeout(underlay); err != nil {
returnErr = err
return
}
}
// Read command message
n, err = underlay.Read(bs)
if !s.TrustClient {
netLayer.PersistConn(underlay)
}
if err != nil || n < 7 { // Shortest length is 7
returnErr = fmt.Errorf("read socks5 failed, msgTooShort: %v, %d", err, n)
return
}
// 一般可以为 5 1 0 3 n3表示域名n是域名长度然后域名很可能是 119 119 119 46 开头,表示 www.
// 比如百度就是 [5 1 0 3 13 119 119 119 46 98]
cmd := bs[1]
if cmd == CmdBind {
returnErr = fmt.Errorf("unsuppoted command %v", cmd)
return
}
l := 2
off := 4
var theIP net.IP
switch bs[3] {
case ATypIP4:
l += net.IPv4len
theIP = make(net.IP, net.IPv4len)
case ATypIP6:
l += net.IPv6len
theIP = make(net.IP, net.IPv6len)
case ATypDomain:
l += int(bs[4])
off = 5
default:
returnErr = fmt.Errorf("unknown address type %v", bs[3])
return
}
if len(bs[off:]) < l {
returnErr = utils.ErrInErr{ErrDesc: "short command request", ErrDetail: utils.ErrInvalidData, Data: []any{len(bs[off:]), l, bs[:n], string(bs[:n])}}
return
}
var theName string
if theIP != nil {
copy(theIP, bs[off:])
} else {
theName = string(bs[off : off+l-2])
}
thePort := int(bs[off+l-2])<<8 | int(bs[off+l-1])
//根据 socks5标准“使用UDP ASSOCIATE时客户端的请求包中(DST.ADDR, DST.PORT)不再是目标的地址而是客户端指定本身用于发送UDP数据包的地址和端口”
//然后服务器会传回专门适用于客户端的 一个 服务器的 udp的ip和地址然后之后客户端再专门 向udp地址发送连接此tcp连接就已经没用。
//总之UDP Associate方法并不是 UDP over TCP完全不同而且过程中握手用tcp传输用udp使用到了两个连接。
//不过一般作为NAT内网中的客户端是无法知道自己实际呈现给服务端的udp地址的, 所以没什么太大用
// 但是, 如果不指定的话udp associate 就会认为未来一切发往我们新生成的端口的连接都是属于该客户端, 显然有风险
// 更不用说这样 的 udp associate 会重复使用很多 随机udp端口特征很明显。
// 总之 udp associate 只能用于内网环境。
//旧代码每次遇到 associate都会返回一个新的随机端口而实际上这应该是有问题的
//如果一些不良的socks5客户端 每次 udp请求都使用 associate的话会造成端口数量无限增长最后产生 too many open files 错误。
if cmd == CmdUDPAssociate {
//utils.Debug("socks5 got CmdUDPAssociate")
if s.Network() == "tcp" {
returnErr = errors.New("socks5's network set to tcp, but got CmdUDPAssociate from client")
return
}
//这里我们serverAddr直接返回0.0.0.0即可,也实在想不到谁会返回 另一个ip地址出来。肯定应该和原ip相同的。
//随机生成一个端口专门用于处理该客户端。这是我的想法。
bindPort := netLayer.RandPort(true, true, 0)
udpPreparedAddr := &net.UDPAddr{
IP: []byte{0, 0, 0, 0},
Port: bindPort,
}
udpRC, err := net.ListenUDP("udp", udpPreparedAddr)
if err != nil {
returnErr = errors.New("UDPAssociate: unable to listen udp")
return
}
//ver5, rep0表示成功, rsv0, atyp(1, 即ipv4), BND.ADDR(4字节的ipv4) , BND.PORT(2字节)
reply := [10]byte{Version5, 0x00, 0x00, 0x01, 0, 0, 0, 0, byte(int16(bindPort) >> 8), byte(int16(bindPort) << 8 >> 8)}
_, err = underlay.Write(reply[:])
if err != nil {
returnErr = fmt.Errorf("failed to write command response: %w", err)
return
}
//theName有可能是ip的形式比如浏览器一般不会自己dns把一切ip和域名都当作域名传入socks5代理
if ip := net.ParseIP(theName); ip != nil {
theIP = ip
}
clientFutureAddr := netLayer.Addr{
IP: theIP,
Name: theName,
Port: thePort,
Network: "udp",
}
uc := &ServerUDPConn{
clientSupposedAddr: clientFutureAddr.ToUDPAddr(), //这里为了解析域名, 就用了 netLayer.Addr 作为中介的方式
UDPConn: udpRC,
fullcone: s.IsFullcone,
}
return nil, uc, clientFutureAddr, nil
} else {
_, err = underlay.Write(commmonTCP_HandshakeReply)
if err != nil {
returnErr = fmt.Errorf("failed to write command response: %w", err)
return
}
//theName有可能是ip的形式比如浏览器一般不会自己dns把一切ip和域名都当作域名传入socks5代理
if ip := net.ParseIP(theName); ip != nil {
theIP = ip
}
targetAddr = netLayer.Addr{
IP: theIP,
Name: theName,
Port: thePort,
}
return underlay, nil, targetAddr, nil
}
}
// 用于socks5服务端的 udp连接, 实现 netLayer.MsgConn
type ServerUDPConn struct {
*net.UDPConn
clientSupposedAddr *net.UDPAddr //客户端指定的客户端自己未来将使用的公网UDP的Addr
fullcone bool
}
func (u *ServerUDPConn) CloseConnWithRaddr(raddr netLayer.Addr) error {
return u.Close()
}
func (u *ServerUDPConn) Fullcone() bool {
return u.fullcone
}
// 将远程地址发来的响应 传给客户端
func (u *ServerUDPConn) WriteMsgTo(bs []byte, raddr netLayer.Addr) error {
buf := &bytes.Buffer{}
buf.WriteByte(0) //rsv
buf.WriteByte(0) //rsv
buf.WriteByte(0) //frag
var atyp byte = ATypIP4
if len(raddr.IP) > 4 {
atyp = ATypIP6
}
buf.WriteByte(atyp)
buf.Write(raddr.IP)
buf.WriteByte(byte(int16(raddr.Port) >> 8))
buf.WriteByte(byte(int16(raddr.Port) << 8 >> 8))
buf.Write(bs)
//必须要指明raddr
_, err := u.UDPConn.WriteToUDP(buf.Bytes(), u.clientSupposedAddr)
return err
}
// 从 客户端读取 udp请求
func (u *ServerUDPConn) ReadMsgFrom() ([]byte, netLayer.Addr, error) {
var clientSupposedAddrIsNothing bool
if u.clientSupposedAddr == nil || len(u.clientSupposedAddr.IP) < 3 || u.clientSupposedAddr.IP.IsUnspecified() {
clientSupposedAddrIsNothing = true
}
bs := utils.GetPacket()
if u.fullcone {
u.UDPConn.SetReadDeadline(time.Now().Add(netLayer.UDP_fullcone_timeout))
} else {
u.UDPConn.SetReadDeadline(time.Now().Add(netLayer.UDP_timeout))
}
n, addr, err := u.UDPConn.ReadFromUDP(bs)
if err != nil {
if n <= 0 {
return nil, netLayer.Addr{}, err
}
return bs[:n], netLayer.NewAddrFromUDPAddr(addr), err
}
if n < 6 {
return nil, netLayer.Addr{}, utils.ErrInErr{ErrDesc: "socks5 UDPConn short read", Data: n}
}
if !clientSupposedAddrIsNothing {
if !addr.IP.Equal(u.clientSupposedAddr.IP) || addr.Port != u.clientSupposedAddr.Port {
//just random attack message,
//但是有些其他socks5客户端确实会导致这个问题所以不应直接退出; 见issue 157
// socks5 本 不应 用在公网更不应在公网开启udp转发。所以这里给一个warning已经足够。
if ce := utils.CanLogWarn("socks5 got udp from a different source rather than the expected one"); ce != nil {
ce.Write(
zap.String("expected", u.clientSupposedAddr.String()),
zap.String("real", addr.String()),
)
}
clientSupposedAddrIsNothing = true
//return nil, netLayer.Addr{}, utils.ErrInErr{ErrDesc: "socks5 UDPConn ReadMsg failed, addr not coming from supposed client addr", ErrDetail: utils.ErrInvalidData, Data: addr.String()}
}
}
atyp := bs[3]
l := 2 //supposed Minimum Remain Data Lenth, 包含Port的2字节
off := 4 //offset from which the addr data really starts
var theIP net.IP
switch atyp {
case ATypIP4:
l += net.IPv4len
theIP = make(net.IP, net.IPv4len)
case ATypIP6:
l += net.IPv6len
theIP = make(net.IP, net.IPv6len)
case ATypDomain:
l += int(bs[4])
off = 5
default:
return nil, netLayer.Addr{}, utils.ErrInErr{ErrDesc: "socks5 read UDPConn unknown atype", Data: atyp}
}
if len(bs[off:]) < l {
return nil, netLayer.Addr{}, utils.ErrInErr{ErrDesc: "socks5 UDPConn short command request", Data: atyp}
}
var theName string
if theIP != nil {
copy(theIP, bs[off:])
} else {
theName = string(bs[off : off+l-2])
}
thePort := int(bs[off+l-2])<<8 | int(bs[off+l-1])
newStart := off + l
if clientSupposedAddrIsNothing {
u.clientSupposedAddr = addr
}
return bs[newStart:n], netLayer.Addr{
IP: theIP,
Name: theName,
Port: thePort,
Network: "udp",
}, nil
}