diff --git a/examples/doko.client.toml b/examples/doko.client.toml new file mode 100644 index 0000000..707e701 --- /dev/null +++ b/examples/doko.client.toml @@ -0,0 +1,16 @@ +# dokodemo常用于 测试。 它可将任意程序的输出转发到 特定目标地址 + +[[listen]] +protocol = "dokodemo" +network = "udp" # 可以监听tcp,也可以监听 udp +host = "127.0.0.1" +port = 63782 +#target = "udp://127.0.0.1:4444" +target = "udp://2620:1ec:c11::200:4444" #该ipv6只是作为一个示范. 目的是告诉你,在这里不用中括号 + +#关于不用中括号的规定,只在 在url中填充 ipv6地址时存在。这是因为,url中如果填写了中括号的话,会被转义为百分号 + +[[dial]] +protocol = "socks5" +host = "127.0.0.1" +port = 10800 \ No newline at end of file diff --git a/netLayer/addr.go b/netLayer/addr.go index 16c1fca..3fbb5ff 100644 --- a/netLayer/addr.go +++ b/netLayer/addr.go @@ -447,7 +447,7 @@ func (a *Addr) String() string { } -// 返回以url表示的 地址. unix的话文件名若带斜杠则会被转义。ipv6的中括号不会被转义 +// 返回以url表示的 地址. unix的话会被url转义。ipv6的中括号不会被转义 func (a *Addr) UrlString() string { if a.Network != "" { if a.Network == "unix" { @@ -483,7 +483,7 @@ func (a *Addr) GetNetIPAddr() (na netip.Addr) { return } -// a.Network == "udp", "udp4", "udp6" +// IsStrUDP_network(a.Network) func (a *Addr) IsUDP() bool { return IsStrUDP_network(a.Network) } @@ -505,7 +505,7 @@ func (a *Addr) ToTCPAddr() *net.TCPAddr { return ta } -// Returned host string +// if a.IP!=nil, return IP.String() else return a.Name func (a *Addr) HostStr() string { if a.IP == nil { return a.Name @@ -694,7 +694,7 @@ func V2rayGetAddrFrom(buf utils.ByteReader) (addr Addr, err error) { return } -const DualNetworkName = "tcp/udp" +const DualNetworkName = "dual" type TCPUDPAddr struct { *net.TCPAddr diff --git a/proxy/base.go b/proxy/base.go index 0ce2dd4..f8cf602 100644 --- a/proxy/base.go +++ b/proxy/base.go @@ -16,7 +16,7 @@ import ( "go.uber.org/zap" ) -//BaseInterface provides supports for all VSI model layers except proxy layer. +// BaseInterface provides supports for all VSI model layers except proxy layer. type BaseInterface interface { Name() string //代理协议名称, 如vless MiddleName() string //不包含传输层 和 代理层的 其它VSI层 所使用的协议,前后被加了加号,如 +tls+ws+ @@ -39,8 +39,6 @@ type BaseInterface interface { /////////////////// 传输层 /////////////////// - MultiTransportLayer() bool //同时使用tcp和udp来传输数据。direct, socks5 和 shadowsocks 都为true - Network() string //传输层协议,如 tcp, udp, unix, kcp, etc. 这里叫做Network而不是transport, 是遵循 golang 标准包 net包的用法。我们兼容 net的Listen等方法, 可把Network直接作为 net.Listen等方法的 network 参数。 GetXver() int @@ -83,7 +81,8 @@ type BaseInterface interface { // 规定,所有的proxy都要内嵌本struct. 我们用这种方式实现 "继承". // 这是verysimple的架构所要求的。 // verysimple规定,在加载完配置文件后,listen/dial 所使用的全部层级都是完整确定了的. -// 因为所有使用的层级都是确定的,就可以进行针对性优化 +// +// 因为所有使用的层级都是确定的,就可以进行针对性优化 type Base struct { ListenConf *ListenConf DialConf *DialConf @@ -126,10 +125,6 @@ func (b *Base) Network() string { return b.TransportLayer } -func (b *Base) MultiTransportLayer() bool { - return false -} - func (b *Base) LocalAddr() net.Addr { return b.LA } @@ -190,7 +185,7 @@ func (b *Base) InnerMuxEstablished() bool { return b.Innermux != nil && !b.Innermux.IsClosed() } -//placeholder +// placeholder func (b *Base) HasInnerMux() (int, string) { return 0, "" } @@ -235,7 +230,7 @@ func (b *Base) GetClientInnerMuxSession(wrc io.ReadWriteCloser) *smux.Session { } } -//return false. As a placeholder. +// return false. As a placeholder. func (b *Base) IsUDP_MultiChannel() bool { return false } @@ -248,20 +243,16 @@ func (b *Base) GetSockopt() *netLayer.Sockopt { } func (b *Base) setNetwork(network string) { - if network == "" { - b.TransportLayer = "tcp" - } else { - b.TransportLayer = network + b.TransportLayer = network - } } func (b *Base) AdvancedLayer() string { return b.AdvancedL } -//try close inner mux and stop AdvS +// try close inner mux and stop AdvS func (b *Base) Stop() { if b.Innermux != nil { b.Innermux.Close() @@ -272,7 +263,7 @@ func (b *Base) Stop() { } } -//return false. As a placeholder. +// return false. As a placeholder. func (b *Base) CanFallback() bool { return false } @@ -313,7 +304,7 @@ func (b *Base) GetAdvServer() advLayer.Server { return b.AdvS } -//setNetwork, Xver, Tag,Sockopt, IsFullcone, Header,AdvancedL, InitAdvLayer +// setNetwork, Xver, Tag,Sockopt, IsFullcone, Header,AdvancedL, InitAdvLayer func (b *Base) ConfigCommon(cc *CommonConf) { b.setNetwork(cc.Network) @@ -332,7 +323,7 @@ func (b *Base) ConfigCommon(cc *CommonConf) { b.InitAdvLayer() } -//高级层就像代理层一样重要,可以注册多种包,配置选项也比较多。 +// 高级层就像代理层一样重要,可以注册多种包,配置选项也比较多。 func (b *Base) InitAdvLayer() { switch b.AdvancedL { case "": diff --git a/proxy/creator.go b/proxy/creator.go index 147f17b..bfce06b 100644 --- a/proxy/creator.go +++ b/proxy/creator.go @@ -35,8 +35,16 @@ func PrintAllClientNames() { } } -//可通过标准配置或url 来初始化。 +type CreatorCommon interface { + //若为true,则表明该协议可同时使用tcp和udp来传输数据。direct, socks5 和 shadowsocks 都为true。 + //此时,是否开启udp取决于Network(), 如果为dual, 则均支持; 如果仅为tcp或者udp,则不支持。 + // direct的默认Network为dual。 + MultiTransportLayer() bool +} + +// 可通过标准配置或url 来初始化。 type ClientCreator interface { + CreatorCommon //大部分通用内容都会被proxy包解析,方法只需要处理proxy包未知的内容 NewClient(*DialConf) (Client, error) //标准配置 @@ -46,8 +54,10 @@ type ClientCreator interface { //DialConfToURL(url *DialConf, format int) (*url.URL, error) } -//可通过标准配置或url 来初始化。 +// 可通过标准配置或url 来初始化。 type ServerCreator interface { + CreatorCommon + NewServer(*ListenConf) (Server, error) URLToListenConf(url *url.URL, iv *ListenConf, format int) (*ListenConf, error) @@ -102,7 +112,7 @@ func newClient(creator ClientCreator, dc *DialConf, knownTls bool) (Client, erro } if dc.SendThrough != "" { - if c.MultiTransportLayer() { + if c.Network() == netLayer.DualNetworkName { //多个传输层的话,完全由proxy自行配置 localAddr。 } else { st, err := netLayer.StrToNetAddr(c.Network(), dc.SendThrough) @@ -121,7 +131,7 @@ func newClient(creator ClientCreator, dc *DialConf, knownTls bool) (Client, erro } -//SetAddrStr, ConfigCommon +// SetAddrStr, ConfigCommon func configCommonForClient(cli BaseInterface, dc *DialConf) error { if cli.Name() != DirectName { cli.SetAddrStr(dc.GetAddrStrForListenOrDial()) @@ -177,7 +187,7 @@ func newServer(creator ServerCreator, lc *ListenConf, knownTls bool) (Server, er } -//SetAddrStr, setCantRoute,setFallback, ConfigCommon +// SetAddrStr, setCantRoute,setFallback, ConfigCommon func configCommonForServer(ser BaseInterface, lc *ListenConf) error { ser.SetAddrStr(lc.GetAddrStrForListenOrDial()) serc := ser.GetBase() diff --git a/proxy/direct.go b/proxy/direct.go index 82b519f..faebddf 100644 --- a/proxy/direct.go +++ b/proxy/direct.go @@ -1,6 +1,7 @@ package proxy import ( + "errors" "io" "net" "net/url" @@ -14,9 +15,13 @@ const ( DirectURL = DirectName + "://" ) -//implements ClientCreator for direct +// implements ClientCreator for direct type DirectCreator struct{} +func (DirectCreator) MultiTransportLayer() bool { + return true +} + func (DirectCreator) URLToDialConf(url *url.URL, iv *DialConf, format int) (*DialConf, error) { if iv != nil { return iv, nil @@ -31,6 +36,10 @@ func (DirectCreator) URLToDialConf(url *url.URL, iv *DialConf, format int) (*Dia func (DirectCreator) NewClient(dc *DialConf) (Client, error) { d := &DirectClient{} + if dc.Network == "" { + dc.Network = netLayer.DualNetworkName + } + if dc.SendThrough != "" { st, err := netLayer.StrToNetAddr(netLayer.DualNetworkName, dc.SendThrough) if err != nil { @@ -53,12 +62,11 @@ type DirectClient struct { func (*DirectClient) Name() string { return DirectName } -func (*DirectClient) MultiTransportLayer() bool { - return true -} - -//若 underlay 为nil,则会对target进行拨号, 否则返回underlay本身 +// 若 underlay 为nil,则会对target进行拨号, 否则返回underlay本身 func (d *DirectClient) Handshake(underlay net.Conn, firstPayload []byte, target netLayer.Addr) (result io.ReadWriteCloser, err error) { + if d.Network() == "udp" { + return nil, errors.New("direct's network set to udp, but Handshake called") + } if underlay == nil { @@ -97,9 +105,12 @@ func (d *DirectClient) Handshake(underlay net.Conn, firstPayload []byte, target } -//direct的Client的 EstablishUDPChannel 直接 监听一个udp端口,无视传入的net.Conn. +// direct的Client的 EstablishUDPChannel 直接 监听一个udp端口,无视传入的net.Conn. // 这是因为要考虑到fullcone. func (d *DirectClient) EstablishUDPChannel(_ net.Conn, firstPayload []byte, target netLayer.Addr) (netLayer.MsgConn, error) { + if d.Network() == "tcp" { + return nil, errors.New("direct's network set to tcp, but EstablishUDPChannel called") + } if len(firstPayload) == 0 { diff --git a/proxy/dokodemo/server.go b/proxy/dokodemo/server.go index 3ba4ff7..6e018df 100644 --- a/proxy/dokodemo/server.go +++ b/proxy/dokodemo/server.go @@ -1,4 +1,5 @@ -/*Package dokodemo implements a dokodemo-door proxy.Server. +/* +Package dokodemo implements a dokodemo-door proxy.Server. Server that wants to relay data to a dokodemo target address. @@ -6,13 +7,13 @@ dokodemo 是 v2ray的 dokodemo-door 协议的实现。不含透明代理功能 严格来说 dokodemo-door 并不是一个 "协议", 而是一个预先指定目标的转发方式。 -dokodemo 是 listen端, 监听一个普通的tcp端口,试图将一切流量转发到特定的预定义的地址. 并不是直接连接,而是转发到dial。 +dokodemo 是 listen端, 监听一个普通的tcp/udp端口,试图将一切流量转发到特定的预定义的地址. 并不是直接连接,而是转发到dial。 dokodemo 属于 “单目标”代理,而其它proxy.Server 一般都属于 “泛目标”代理。 内部实际上就是 指定了目标的 纯tcp/udp协议,属于监听协议中最简单、最纯粹的一种。 -Example 应用例子 +# Example 应用例子 使用 dokodemo 做监听,用direct 拨号,指定一个target,那么实际上就是把 该监听的节点 与远程target间建立了一个信道; @@ -53,6 +54,10 @@ func init() { type ServerCreator struct{} +func (ServerCreator) MultiTransportLayer() bool { + return false +} + func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format int) (*proxy.ListenConf, error) { if format != proxy.StandardMode { return lc, utils.ErrUnImplemented @@ -76,7 +81,7 @@ func (ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) { return s, nil } -//implements proxy.Server +// implements proxy.Server type Server struct { proxy.Base diff --git a/proxy/http/server.go b/proxy/http/server.go index f83e85d..027acb0 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -1,6 +1,7 @@ -/*Package http implements http proxy for proxy.Server. +/* +Package http implements http proxy for proxy.Server. -Reference +# Reference rfc: https://datatracker.ietf.org/doc/html/rfc7231#section-4.3.6 @@ -8,14 +9,11 @@ about basic auth: https://en.wikipedia.org/wiki/Basic_access_authentication - https://datatracker.ietf.org/doc/html/rfc7617 example header: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== - - */ package http @@ -49,6 +47,10 @@ func init() { type ServerCreator struct{} +func (ServerCreator) MultiTransportLayer() bool { + return false +} + func (ServerCreator) URLToListenConf(u *url.URL, lc *proxy.ListenConf, format int) (*proxy.ListenConf, error) { if format != proxy.UrlStandardFormat { return lc, utils.ErrUnImplemented @@ -93,7 +95,7 @@ func (ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) { return s, nil } -//implements proxy.Server +// implements proxy.Server type Server struct { proxy.Base @@ -249,7 +251,7 @@ func (s *Server) Handshake(underlay net.Conn) (newconn net.Conn, _ netLayer.MsgC return } -//用于纯http的 代理,dial后,第一次要把客户端的数据原封不动发送给远程服务端 +// 用于纯http的 代理,dial后,第一次要把客户端的数据原封不动发送给远程服务端 // 就是说,第一次从 ProxyConn Read时,读到的一定是之前读过的数据,原理有点像 fallback type ProxyConn struct { net.Conn diff --git a/proxy/reject.go b/proxy/reject.go index de7ff16..4ecff39 100644 --- a/proxy/reject.go +++ b/proxy/reject.go @@ -55,9 +55,13 @@ func tryRejectWithHttpRespAndClose(rejectType string, underlay net.Conn) { underlay.Close() //实测,如果不Write 响应,就算Close掉,客户端的连接也不会真正被关闭 } -//implements ClientCreator for reject +// implements ClientCreator and ServerCreator for reject type RejectCreator struct{} +func (RejectCreator) MultiTransportLayer() bool { + return false +} + func (RejectCreator) NewClient(dc *DialConf) (Client, error) { r := &RejectClient{} @@ -110,9 +114,10 @@ func (rc *rejectCommon) initByCommonConf(cc *CommonConf) { } -/*RejectClient implements Client, optionally response a 403 and close the underlay immediately. +/* +RejectClient implements Client, optionally response a 403 and close the underlay immediately. - v2ray的 "blackhole" 名字不准确, 本作 使用 "reject". + v2ray的 "blackhole" 名字不准确, 本作 使用 "reject". 正常的 blackhole,并不会立即关闭连接,而是悄无声息地 读 数据,并舍弃。 而 v2ray的 blackhole是 选择性返回 403错误 后立即关闭连接. 完全是 Reject的特性。 @@ -127,24 +132,24 @@ type RejectClient struct { rejectCommon } -//optionally response 403 and close the underlay, return io.EOF. +// optionally response 403 and close the underlay, return io.EOF. func (c *RejectClient) Handshake(underlay net.Conn, _ []byte, _ netLayer.Addr) (result io.ReadWriteCloser, err error) { tryRejectWithHttpRespAndClose(c.theType, underlay) return nil, io.EOF } -//function the same as Handshake +// function the same as Handshake func (c *RejectClient) EstablishUDPChannel(underlay net.Conn, _ []byte, _ netLayer.Addr) (netLayer.MsgConn, error) { tryRejectWithHttpRespAndClose(c.theType, underlay) return nil, io.EOF } -//mimic the behavior of RejectClient +// mimic the behavior of RejectClient type RejectServer struct { rejectCommon } -//return utils.ErrHandled +// return utils.ErrHandled func (s *RejectServer) Handshake(underlay net.Conn) (_ net.Conn, _ netLayer.MsgConn, _ netLayer.Addr, e error) { tryRejectWithHttpRespAndClose(s.theType, underlay) diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index c88158e..89e7949 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -17,6 +17,9 @@ func init() { type ClientCreator struct{} +func (ClientCreator) MultiTransportLayer() bool { + return false +} func (ClientCreator) URLToDialConf(u *url.URL, dc *proxy.DialConf, format int) (*proxy.DialConf, error) { if format != proxy.UrlStandardFormat { return dc, utils.ErrUnImplemented diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index d926fdf..75c9fe6 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -18,6 +18,9 @@ func init() { type ServerCreator struct{} +func (ServerCreator) MultiTransportLayer() bool { + return false +} func (ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) { uuidStr := lc.Uuid @@ -63,10 +66,6 @@ func (*Server) Name() string { return Name } -func (*Server) MultiTransportLayer() bool { - return true -} - func (s *Server) Handshake(underlay net.Conn) (result net.Conn, msgConn netLayer.MsgConn, targetAddr netLayer.Addr, returnErr error) { result = s.cipher.StreamConn(underlay) readbs := utils.GetBytes(utils.MTU) diff --git a/proxy/shadowsocks/shadowsocks.go b/proxy/shadowsocks/shadowsocks.go index 6a4607d..5b66f76 100644 --- a/proxy/shadowsocks/shadowsocks.go +++ b/proxy/shadowsocks/shadowsocks.go @@ -9,14 +9,11 @@ https://github.com/shadowsocks/shadowsocks-org/wiki/AEAD-Ciphers 这里vs参考了gost的实现。gost中,Connector就相当于 client,Handler就相当于 Server -但是发现,没法一个server同时处理tcp和udp? 也就是说,只能预先指定服务端要处理的协议; +参考阅读 http://overtalk.site/2020/02/25/network-shadowsocks/ -看ss的标准,也没有提及哪一项 可以指定 tcp/udp +注意, shadowsocks 可能同时使用tcp和udp,但是一定会使用到 tcp, shadowsocks 的network只能设置为tcp或者dual -重新阅读上面Protocol页面,参考阅读 http://overtalk.site/2020/02/25/network-shadowsocks/ - -ss不像vmess等协议一样,只使用一种传输层协议来传输 tcp和udp数据;而是:用tcp传tcp,用udp传udp。 -如此的话,特征必很明显。 +如dual话,特征必很明显。 另外,本包是普通的ss AEAD Ciphers ,不过它还是有问题。所以以后要研究ss-2022 diff --git a/proxy/simplesocks/client.go b/proxy/simplesocks/client.go index b326456..9162eb5 100644 --- a/proxy/simplesocks/client.go +++ b/proxy/simplesocks/client.go @@ -18,6 +18,10 @@ func init() { type ClientCreator struct{} +func (ClientCreator) MultiTransportLayer() bool { + return false +} + func (ClientCreator) URLToDialConf(u *url.URL, dc *proxy.DialConf, format int) (*proxy.DialConf, error) { if dc == nil { dc = &proxy.DialConf{} diff --git a/proxy/simplesocks/server.go b/proxy/simplesocks/server.go index 17009b7..9fcbb0e 100644 --- a/proxy/simplesocks/server.go +++ b/proxy/simplesocks/server.go @@ -17,6 +17,10 @@ func init() { type ServerCreator struct{} +func (ServerCreator) MultiTransportLayer() bool { + return false +} + func (ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) { s := &Server{} return s, nil @@ -29,7 +33,7 @@ func (ServerCreator) URLToListenConf(u *url.URL, lc *proxy.ListenConf, format in return lc, nil } -//implements proxy.Server +// implements proxy.Server type Server struct { proxy.Base } @@ -41,7 +45,7 @@ func (*Server) CanFallback() bool { return true //simplesocks理论上当然是支持回落的,但是一般它被用于 innerMux的内层协议,所以用做innerMux内层协议时,要注意不要再回落了。 } -//若握手步骤数据不对, 会返回 ErrDetail 为 utils.ErrInvalidData 的 utils.ErrInErr +// 若握手步骤数据不对, 会返回 ErrDetail 为 utils.ErrInvalidData 的 utils.ErrInErr func (s *Server) Handshake(underlay net.Conn) (result net.Conn, msgConn netLayer.MsgConn, targetAddr netLayer.Addr, returnErr error) { if err := proxy.SetCommonReadTimeout(underlay); err != nil { returnErr = err diff --git a/proxy/socks5/client.go b/proxy/socks5/client.go index 2745907..1ded878 100644 --- a/proxy/socks5/client.go +++ b/proxy/socks5/client.go @@ -1,6 +1,7 @@ package socks5 import ( + "errors" "io" "net" "net/url" @@ -16,6 +17,10 @@ func init() { type ClientCreator struct{} +// true +func (ClientCreator) MultiTransportLayer() bool { + return true +} func (ClientCreator) URLToDialConf(u *url.URL, dc *proxy.DialConf, format int) (*proxy.DialConf, error) { if format != proxy.UrlStandardFormat { @@ -50,7 +55,6 @@ func (*Client) Name() string { } func (c *Client) Handshake(underlay net.Conn, firstPayload []byte, target netLayer.Addr) (result io.ReadWriteCloser, err error) { - if underlay == nil { panic("socks5 client handshake, nil underlay is not allowed") } @@ -155,6 +159,11 @@ func (c *Client) Handshake(underlay net.Conn, firstPayload []byte, target netLay } func (c *Client) EstablishUDPChannel(underlay net.Conn, firstPayload []byte, target netLayer.Addr) (netLayer.MsgConn, error) { + + if c.Network() == "tcp" { + return nil, errors.New("direct's network set to tcp, but EstablishUDPChannel called") + } + var err error serverPort := 0 serverPort, err = Client_EstablishUDPAssociate(underlay) diff --git a/proxy/socks5/server.go b/proxy/socks5/server.go index a611419..9a5e4b6 100644 --- a/proxy/socks5/server.go +++ b/proxy/socks5/server.go @@ -16,8 +16,8 @@ import ( ) // 解读如下: -//ver(5), rep(0,表示成功), rsv(0), atyp(1, 即ipv4), BND.ADDR (ipv4(0,0,0,0)), BND.PORT(0, 2字节) -//这个 BND.ADDR和port 按理说不应该传0的,不过如果只作为本地tcp代理的话应该不影响 +// ver(5), rep(0,表示成功), rsv(0), 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() { @@ -41,6 +41,10 @@ func NewServer() *Server { type ServerCreator struct{} +// 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 { @@ -87,7 +91,7 @@ func (ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) { func (*Server) Name() string { return Name } -//若没有IDMap,则直接写入AuthNone响应,否则返回错误 +// 若没有IDMap,则直接写入AuthNone响应,否则返回错误 func (s *Server) authNone(underlay net.Conn) (returnErr error) { var err error if len(s.IDMap) == 0 { @@ -334,7 +338,12 @@ For: if cmd == CmdUDPAssociate { - utils.Debug("socks5 got 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相同的。 @@ -406,7 +415,7 @@ For: } -//用于socks5服务端的 udp连接, 实现 netLayer.MsgConn +// 用于socks5服务端的 udp连接, 实现 netLayer.MsgConn type ServerUDPConn struct { *net.UDPConn clientSupposedAddr *net.UDPAddr //客户端指定的客户端自己未来将使用的公网UDP的Addr @@ -421,7 +430,7 @@ func (u *ServerUDPConn) Fullcone() bool { return u.fullcone } -//将远程地址发来的响应 传给客户端 +// 将远程地址发来的响应 传给客户端 func (u *ServerUDPConn) WriteMsgTo(bs []byte, raddr netLayer.Addr) error { buf := &bytes.Buffer{} @@ -447,7 +456,7 @@ func (u *ServerUDPConn) WriteMsgTo(bs []byte, raddr netLayer.Addr) error { } -//从 客户端读取 udp请求 +// 从 客户端读取 udp请求 func (u *ServerUDPConn) ReadMsgFrom() ([]byte, netLayer.Addr, error) { var clientSupposedAddrIsNothing bool diff --git a/proxy/socks5/socks5.go b/proxy/socks5/socks5.go index 996bf52..8a7f0d5 100644 --- a/proxy/socks5/socks5.go +++ b/proxy/socks5/socks5.go @@ -1,8 +1,9 @@ -/*Package socks5 provies socks5 proxy for proxy.Client and proxy.Server. +/* +Package socks5 provies socks5 proxy for proxy.Client and proxy.Server. Supports USER/PASSWORD authentication. -Reference +# Reference English: https://www.ietf.org/rfc/rfc1928.txt @@ -14,7 +15,9 @@ USER/PASSWORD authentication rfc: https://datatracker.ietf.org/doc/html/rfc1929 -Off Topic +注意,socks5可能同时使用tcp和udp,但是一定会使用到tcp,socks5的network只能设置为tcp或者dual + +# Off Topic 纵观各种代理协议,vless/vmess/trojan/shadowsocks协议 都借鉴了socks5,有不少类似的地方。 所以 制作代理, 有必要先学习socks5标准。 @@ -23,7 +26,7 @@ package socks5 const Name = "socks5" -//socks5 version number. +// socks5 version number. const Version5 = 0x05 // SOCKS auth type @@ -42,6 +45,7 @@ const ( ) // SOCKS address types as defined in RFC 1928 section 4 +// // Note: vmess/vless用的是123,而这里用的是134,所以是不一样的。 const ( ATypIP4 = 0x1 diff --git a/proxy/socks5http/server.go b/proxy/socks5http/server.go index 194387e..c95e583 100644 --- a/proxy/socks5http/server.go +++ b/proxy/socks5http/server.go @@ -1,8 +1,9 @@ -/*Package socks5http provides listening both socks5 and http at one port. +/* +Package socks5http provides listening both socks5 and http at one port. This package imports proxy/socks5 and proxy/http package. -Naming +# Naming socks5http 与 clash的 "mixed" 等价。之所以不用 "mixed"这个名称,是因为这容易在本作中引起歧义。 @@ -10,15 +11,13 @@ clash是一个客户端,它没有服务端,所以它的监听只是用于内 而本作与v2ray一样,是支持多种服务端协议的,如果也叫 mixed 的话,会让人误以为,这是一个 "万能协议", 啥都能监听, 而这显然是误区。 命名为 socks5http, 则清晰地指出了 该协议的功能。 -Password +# Password 为了避免混淆,本包不支持密码验证。你要是有这么高的密码要求 那你不妨用单独的协议,而不要用混合版。 实际上本包就是先经过http,然后如果不是http代理请求,就会回落到socks5. 所以你可以通过 设计回落的方式来达到 有密码 的 混合端口 的需求。 - - */ package socks5http @@ -43,6 +42,10 @@ func init() { type ServerCreator struct{} +// true +func (ServerCreator) MultiTransportLayer() bool { + return true +} func (ServerCreator) URLToListenConf(u *url.URL, lc *proxy.ListenConf, format int) (*proxy.ListenConf, error) { if lc == nil { diff --git a/proxy/trojan/client.go b/proxy/trojan/client.go index 1a43f83..d1f8fac 100644 --- a/proxy/trojan/client.go +++ b/proxy/trojan/client.go @@ -20,6 +20,9 @@ func init() { type ClientCreator struct{} +func (ClientCreator) MultiTransportLayer() bool { + return false +} func (ClientCreator) URLToDialConf(url *url.URL, dc *proxy.DialConf, format int) (*proxy.DialConf, error) { switch format { case proxy.UrlStandardFormat: diff --git a/proxy/trojan/server.go b/proxy/trojan/server.go index 42cc4e6..9c80f60 100644 --- a/proxy/trojan/server.go +++ b/proxy/trojan/server.go @@ -17,6 +17,9 @@ func init() { type ServerCreator struct{} +func (ServerCreator) MultiTransportLayer() bool { + return false +} func (ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) { uuidStr := lc.Uuid @@ -61,7 +64,7 @@ func newServer(plainPassStr string) *Server { return s } -//implements proxy.Server +// implements proxy.Server type Server struct { proxy.Base @@ -80,7 +83,7 @@ func (*Server) CanFallback() bool { return true } -//若握手步骤数据不对, 会返回 ErrDetail 为 utils.ErrInvalidData 的 utils.ErrInErr +// 若握手步骤数据不对, 会返回 ErrDetail 为 utils.ErrInvalidData 的 utils.ErrInErr func (s *Server) Handshake(underlay net.Conn) (result net.Conn, msgConn netLayer.MsgConn, targetAddr netLayer.Addr, returnErr error) { if err := proxy.SetCommonReadTimeout(underlay); err != nil { returnErr = err diff --git a/proxy/vless/client.go b/proxy/vless/client.go index f633a59..9e82eef 100644 --- a/proxy/vless/client.go +++ b/proxy/vless/client.go @@ -18,6 +18,9 @@ func init() { type ClientCreator struct{} +func (ClientCreator) MultiTransportLayer() bool { + return false +} func (ClientCreator) NewClient(dc *proxy.DialConf) (proxy.Client, error) { uuidStr := dc.Uuid @@ -72,7 +75,7 @@ func (ClientCreator) URLToDialConf(url *url.URL, dc *proxy.DialConf, format int) } -//实现 proxy.UserClient +// 实现 proxy.UserClient type Client struct { proxy.Base @@ -98,7 +101,7 @@ func (c *Client) GetUser() utils.User { return c.user } -//我们只支持 vless v1 的 mux +// 我们只支持 vless v1 的 mux func (c *Client) HasInnerMux() (int, string) { if c.version == 1 && c.use_mux { return 2, "simplesocks" diff --git a/proxy/vless/server.go b/proxy/vless/server.go index d710051..2d1533a 100644 --- a/proxy/vless/server.go +++ b/proxy/vless/server.go @@ -20,7 +20,11 @@ func init() { type ServerCreator struct{} -//如果 lc.Version==0, 则只支持 v0. +func (ServerCreator) MultiTransportLayer() bool { + return false +} + +// 如果 lc.Version==0, 则只支持 v0. func (ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) { uuidStr := lc.Uuid onlyV0 := lc.Version == 0 @@ -45,7 +49,7 @@ func (ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) { } -//如果 v=0, 则只支持 v0. +// 如果 v=0, 则只支持 v0. func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format int) (*proxy.ListenConf, error) { switch format { @@ -85,8 +89,8 @@ func newServerWithConf(uuid string, onlyV0 bool) (*Server, error) { return s, nil } -//Server 同时支持vless v0 和 v1 -//实现 proxy.UserServer 以及 tlsLayer.UserHaser +// Server 同时支持vless v0 和 v1 +// 实现 proxy.UserServer 以及 tlsLayer.UserHaser type Server struct { proxy.Base diff --git a/proxy/vmess/client.go b/proxy/vmess/client.go index 2bdf8b4..9520944 100644 --- a/proxy/vmess/client.go +++ b/proxy/vmess/client.go @@ -49,6 +49,9 @@ func GetEncryptAlgo(dc *proxy.DialConf) (result string) { type ClientCreator struct{} +func (ClientCreator) MultiTransportLayer() bool { + return false +} func (ClientCreator) URLToDialConf(url *url.URL, dc *proxy.DialConf, format int) (*proxy.DialConf, error) { if format != proxy.UrlStandardFormat { return dc, utils.ErrUnImplemented @@ -207,7 +210,7 @@ func (c *ClientConn) CloseConnWithRaddr(_ netLayer.Addr) error { return c.Conn.Close() } -//return false; vmess 标准 是不支持 fullcone的,和vless v0相同 +// return false; vmess 标准 是不支持 fullcone的,和vless v0相同 func (c *ClientConn) Fullcone() bool { return false } diff --git a/proxy/vmess/server.go b/proxy/vmess/server.go index f74ab1b..91145b0 100644 --- a/proxy/vmess/server.go +++ b/proxy/vmess/server.go @@ -69,6 +69,9 @@ func authUserByAuthPairList(bs []byte, authPairList []authPair, antiReplayMachin type ServerCreator struct{} +func (ServerCreator) MultiTransportLayer() bool { + return false +} func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format int) (*proxy.ListenConf, error) { switch format {