diff --git a/README.md b/README.md index fed7c38..941cf6a 100644 --- a/README.md +++ b/README.md @@ -81,13 +81,13 @@ api服务器。 ## 技术详情 ### 关于vless v1 -这里的v1是我自己制定的,总是要摸着石头过河嘛。标准的讨论详见 [vless_v1](docs/vless_v1.md) +这里的v1是 verysimple 自己制定的,总是要摸着石头过河嘛。标准的讨论详见 [vless_v1](docs/vless_v1.md) 在客户端的 配置url中,添加 `?version=1` 即可生效。 总之,强制tls,简单修订了一下协议格式,然后重点完善了fullcone。 -我 实现了 一种独创的 非mux型“隔离信道”方法的 udp over tcp 的fullcone +verysimple 实现了 一种独创的 非mux型“分离信道”方法的 udp over tcp 的fullcone 测试 fullcone 的话,由于目前 verysimple 客户端只支持socks5入口,可以考虑先用v2ray + Netch或者透明代理 等方法监听本地网卡的所有请求,发送到 verysimple 客户端的socks5端口,然后 verysimple 客户端 再用 vless v1 发送到 verysimple vless v1 + direct 的服务端。 @@ -172,10 +172,6 @@ tls lazy encrypt 特性 运行时可以用 -lazy 参数打开(服务端客户 在不使用新协议时,lazy只能通过不lazy tls1.2的方式来解决此问题, 即裸奔转发 tls1.3、加密转发 tls1.2. -### ws/grpc/quic - -目前最新代码已经支持了ws/grpc/quic,并且对于ws/grpc/quic,我设计的vless v1协议将会针对它们 有专门的udp优化。 - ## 安装方式: ### 下载安装 diff --git a/backup b/backup index c52edc4..8298650 100644 --- a/backup +++ b/backup @@ -436,3 +436,162 @@ func RelayUDP_to_Direct(extractor UDP_Extractor) { } } + + +package vless + +import ( + "bufio" + "io" + "net" + + "github.com/hahahrfool/v2ray_simple/netLayer" + "github.com/hahahrfool/v2ray_simple/utils" +) + +func (s *Server) Get_CRUMFURS(id string) *CRUMFURS { + bs, err := utils.StrToUUID(id) + if err != nil { + return nil + } + return s.userCRUMFURS[bs] +} + +type CRUMFURS struct { + net.Conn + hasAdvancedLayer bool //在用ws或grpc时,这个开关保持打开 +} + +func (c *CRUMFURS) WriteUDPResponse(a net.UDPAddr, b []byte) (err error) { + atype := netLayer.AtypIP4 + if len(a.IP) > 4 { + atype = netLayer.AtypIP6 + } + buf := utils.GetBuf() + + buf.WriteByte(atype) + buf.Write(a.IP) + buf.WriteByte(byte(int16(a.Port) >> 8)) + buf.WriteByte(byte(int16(a.Port) << 8 >> 8)) + + if !c.hasAdvancedLayer { + lb := int16(len(b)) + + buf.WriteByte(byte(lb >> 8)) + buf.WriteByte(byte(lb << 8 >> 8)) + } + buf.Write(b) + + _, err = c.Write(buf.Bytes()) + + utils.PutBuf(buf) + return +} + +// 把在 CRUMFURS 信道中 获取到的 未知流量 转发到 UDPResponseWriter (本作中就是 转发到 inServer 中, 而且 只有 socks5 这一种 inServer 实现了该方法, 见 main.go) +func (c *Client) handle_CRUMFURS(UMFURS_conn net.Conn) { + + if c.udpResponseChan == nil { + c.is_CRUMFURS_established = false + return + } + + for { + //之前讨论了,udp信息通通要传长度头,CRUMFURS 也不例外,在 没有AdvancedLayer时,统一都要加udp长度头 + + if c.AdvancedL != "" { + + buf_for_umfurs := utils.GetPacket() + n, err := UMFURS_conn.Read(buf_for_umfurs) + if err != nil { + break + } + if n < 7 { + + break + } + msg := buf_for_umfurs[:n] + atyp := msg[0] + portIndex := net.IPv4len + switch atyp { + case netLayer.AtypIP6: + portIndex = net.IPv6len + default: + //不合法,必须是ipv4或者ipv6 + break + + } + theIP := make(net.IP, portIndex) + copy(theIP, msg[1:portIndex]) + + port := int16(msg[portIndex])<<8 + int16(msg[portIndex+1]) + + c.udpResponseChan <- netLayer.UDPAddrData{ + Addr: net.UDPAddr{ + IP: theIP, + Port: int(port), + }, + Data: msg[portIndex+2:], + } + } else { + if c.crumfursBuf == nil { + c.crumfursBuf = bufio.NewReader(UMFURS_conn) + } + + atyp, err := c.crumfursBuf.ReadByte() + if err != nil { + break + } + + ipLen := net.IPv4len + switch atyp { + case netLayer.AtypIP6: + ipLen = net.IPv6len + default: + //不合法,必须是ipv4或者ipv6 + break + } + + theIP := make(net.IP, ipLen) + _, err = c.crumfursBuf.Read(theIP) + if err != nil { + break + } + + twoBytes, err := c.crumfursBuf.Peek(2) + if err != nil { + break + } + + port := int(int16(twoBytes[0])<<8 + int16(twoBytes[1])) + + c.crumfursBuf.Discard(2) + + twoBytes, err = c.crumfursBuf.Peek(2) + if err != nil { + break + } + + packetLen := int16(twoBytes[0])<<8 + int16(twoBytes[1]) + c.crumfursBuf.Discard(2) + + msg := make([]byte, packetLen) + + _, err = io.ReadFull(c.crumfursBuf, msg) + if err != nil { + break + } + + c.udpResponseChan <- netLayer.UDPAddrData{ + Addr: net.UDPAddr{ + IP: theIP, + Port: port, + }, + Data: msg, + } + + } + } + + c.is_CRUMFURS_established = false +} diff --git a/docs/vless_v1.md b/docs/vless_v1.md index 02eea4f..a06baec 100644 --- a/docs/vless_v1.md +++ b/docs/vless_v1.md @@ -1,6 +1,6 @@ # vless v1 标准 -本v1标准参考v0标准,并着重针对addon和udp部分进行了一些改动。原标准请参考 +本v1标准参考v0标准,并着重针对addon和udp部分进行了一些改动。原v0标准请参考 https://github.com/v2ray/v2ray-core/issues/2636 @@ -49,27 +49,18 @@ vless 的v0 始终处于beta中,而且 【udp长度】的包头仅仅在 commi 在 分离信道传输时, - 一,首个客户端握手包的握手部分采用与tcp相同的格式 + 一,客户端发送的格式: 首个客户端握手包的握手部分采用与tcp相同的格式 接着是udp数据包,前两字节为数据长度的2字节,然后是承载数据。 - 二,非首包的格式: - - 剩余数据包中,因为目标raddr已经确定,所以不再有 经典传输中的 port、atype、addr部分。 - - 不过, 因为我们服务端发往 客户端的方向 需要传输 UMFURS 信息,所以我们需要一字节来指示本次数据包的意义。 - - 客户端 发往 服务端方向 的 数据格式: - - 两字节数据包长度,然后跟着承载数据。 + 二,服务端发送的格式: + 因为我们服务端发往 客户端的方向 有时需要传输 UMFURS 信息,所以我们需要一字节来指示本次数据包的意义。 - 服务端 发往 客户端方向 的 数据格式: + 第一字节为数据包意义指示, + + 0标识 普通 来自原始raddr的数据包,此时 第二字节和第三字节为数据长度,然后跟着数据; - 第一字节为意义指示, - - 0标识 普通 来自原始raddr的数据包,此时 第二字节和第三字节为数据长度,然后跟着数据; - - 1标识 UMFURS 数据, 此时,第 2、3字节 为 port。第4字节为 atype,然后是变长的addr内容,定义与tcp的握手的包头一致。接着是udp数据包长度的2字节,然后是承载数据。 + 1标识 UMFURS 数据, 此时,第 2、3字节 为 port。第4字节为 atype,然后是变长的addr内容,定义与tcp的握手的包头一致。接着是udp数据包长度的2字节,然后是承载数据。 diff --git a/docs/vless_v1_discussion.md b/docs/vless_v1_discussion.md index 463552d..ea4d9d2 100644 --- a/docs/vless_v1_discussion.md +++ b/docs/vless_v1_discussion.md @@ -20,7 +20,7 @@ vless v0中服务端 的回复也是有数据头的,第一字节版本号, 而且udp的话, 会有 crumfurs 模式以及 普通多路复用模式这两种模式, 所以还是要加以区分.所以v1的addon部分依然不能删掉; 不过我在这里做一些变化, v1 的addon第一字节不再是 addon长度, 而是addon类型,之后可以通过addon类型来判断addon长度. -在v1 传递udp握手时, addon第一字节为1时, 表示 “选择传输udp方式“,第二字节为0时表示使用普通多路复用模式(即与trojan相同), 第二字节为1时表示使用 crumfurs模式. 使用 crumfurs模式时, 单个通道不会再传输 raddr,因为仅用于单个raddr的信息传输. +在v1 传递udp握手时, addon第一字节为1时, 表示 “选择传输udp使用 分离信道方式“,此时 单个通道不会再传输 raddr,因为仅用于单个raddr的信息传输. 所以v1的 tcp读写特别简单,一旦握手成功,不会有任何数据头产生,没有丝毫额外开销,直接直连 (与trojan相同) @@ -35,16 +35,11 @@ https://www.zhihu.com/question/29916578 所以,vless v1外面包websocket的话,是不需要再重新加长度的,加了就是多此一举。 -所以,我规定 vless v1 的udp实现中,会判断连接本身,如果连接 使用的是websocket或者类似的本身就加了数据长度头的,则udp包无需再加长度。 - -也就是说,v1不再是一个与底层连接无关的协议,判断是否有ws或者grpc这种高级协议的存在,对于这些进行判断,就可以进行优化。 - 虽然udp加长度只是加了两字节,但这只是write部分,read部分的话,为了防止粘包,就加了buffer,确实是多了一层内存读写操作,所以能精简就要精简,毕竟视频直播视频聊天等这种使用udp的部分都是 流量大户。总之这种优化能视udp的使用用途有不同程度的性能提升 而且作为翻墙要素我们是推荐ws或grpc来配合cdn的,所以还真是有用 -v1主要还是 隔离信道的 udp over tcp 的 fullcone 的实现属于重要的创新,上面提到的优化也就占20%的 v1协议的独特之处。请接着读下文查看fullcone部分 - +隔离信道的 udp over tcp 的 fullcone 的实现属于 v1 重要的创新 ## 关于udp over tcp 的 vless 的 fullcone @@ -162,7 +157,7 @@ UMFURS 信息内容:1字节atype,几字节的IP(视ip为ipv4和ipv6. 因 CRUMFURS 信道与普通UDP信道一样,要传 udp长度头。在应用ws或者grpc后则应不传,这个等我实现ws或grpc了再说 ### 流量特征的避免 -另外,为了避免 【同时具有两个tcp链接,且一个tcp链接只有入方向,没有出方向】 的流量特征,我们实际上不能让crumfurs成为真正的单独通道。 +不过,为了避免 【同时具有两个tcp链接,且一个tcp链接只有入方向,没有出方向】 的流量特征,我们实际上不能让crumfurs成为真正的单独通道。 所以, 我们 crumfurs信息的传输要隐藏在普通链接中。 所以我们udp数据头部除了长度头之外,还要有一字节的 crumfurs标识,如果为0 则意味着没有 crumfurs信息产生,而如果为1的话,后面会附带crumfurs信息。 diff --git a/examples/vless_v1.client.toml b/examples/vless_v1.client.toml new file mode 100644 index 0000000..1929efd --- /dev/null +++ b/examples/vless_v1.client.toml @@ -0,0 +1,20 @@ +[[listen]] +tag = "my_socks5" +protocol = "socks5" +host = "127.0.0.1" +port = 10800 + +[[dial]] +tag = "my_vlesss1" +protocol = "vlesss" +uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" +host = "127.0.0.1" +port = 4433 +version = 1 +insecure = true +utls = true +extra = { vless1_udp_multi = true } + +# 用vless1_udp_multi开启vless v1的 分离信道传输udp功能. udp分离信道发送 性能会比默认的 多路复用 发送性能要高。 +# 性能比较可以参考 大流量tcp下载时 普通tls+vless 与 tls+vless+grpc 直接的差距. 因为grpc也是多路复用的, 所以遇到大流量多线程下载时,grpc就性能很差了。 我们udp也是同理。 使用【分离信道】的话,更适合 bt下载这种 多线程udp下载的情况。 +# 分离信道 又可以叫 【多信道】,因此我们在这里 用 multi 来表示。 \ No newline at end of file diff --git a/examples/vless_v1.server.toml b/examples/vless_v1.server.toml new file mode 100644 index 0000000..c26f61c --- /dev/null +++ b/examples/vless_v1.server.toml @@ -0,0 +1,20 @@ +[[listen]] +tag = "my_vlesss1" +protocol = "vlesss" +uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" +host = "0.0.0.0" +port = 4433 +version = 1 +insecure = true +fallback = ":80" +cert = "cert.pem" +key = "cert.key" + +[[dial]] +tag = "mydirect" +protocol = "direct" +# fullcone = true # 默认的fullcone是关闭状态, 可以取消注释以打开 + +[[route]] +inTag = ["my_vlesss1"] +dialTag = "mydirect" diff --git a/examples/vlesss.server.toml b/examples/vlesss.server.toml index 1b029f4..f745bfb 100644 --- a/examples/vlesss.server.toml +++ b/examples/vlesss.server.toml @@ -13,7 +13,7 @@ key = "cert.key" [[dial]] protocol = "direct" -# fullcone = true # 默认的fullcone是关闭状态, 可以取消注释以打开. 不过vless v0的话没啥用, 还是要等v1. +# fullcone = true # 默认的fullcone是关闭状态, 可以取消注释以打开. 不过vless v0的话没用, v1有用. # fallback这一项是可选的,如果没有的话,或者未匹配,则默认使用listen提供的fallback # 如果listen也没提供fallback,那就会直接断开连接 diff --git a/netLayer/route_conf.go b/netLayer/route_conf.go index 0facecd..e6ac11b 100644 --- a/netLayer/route_conf.go +++ b/netLayer/route_conf.go @@ -3,6 +3,7 @@ package netLayer import ( "net" "net/netip" + "reflect" "regexp" "strings" @@ -46,6 +47,18 @@ func LoadRuleForRouteSet(rule *RuleConf) (rs *RouteSet) { rs.OutTag = value case []string: rs.OutTags = value + case []any: + list := make([]string, 0, len(value)) + for i, v := range value { + if s, ok := v.(string); ok { + list = append(list, s) + } else { + if ce := utils.CanLogErr("Route outTags list has not string type"); ce != nil { + ce.Write(zap.Int("index", i), zap.String("type", reflect.TypeOf(v).String()), zap.Any("value", v)) + } + } + } + rs.OutTags = list } for _, c := range rule.Countries { diff --git a/proxy/vless/client.go b/proxy/vless/client.go index 12821e1..4ec3824 100644 --- a/proxy/vless/client.go +++ b/proxy/vless/client.go @@ -46,10 +46,20 @@ func (_ ClientCreator) NewClient(dc *proxy.DialConf) (proxy.Client, error) { } v := dc.Version - if v >= 0 { + if v > 0 { if v == 1 { c.version = 1 + + if dc.Extra != nil { + if thing := dc.Extra["vless1_udp_multi"]; thing != nil { + if udp_multi, ok := thing.(bool); ok && udp_multi { + c.udp_multi = true + } + } + } + } else { + return nil, utils.ErrInErr{ErrDesc: "given version bigger than 1", ErrDetail: utils.ErrNotImplemented} } } @@ -71,8 +81,22 @@ func NewClientByURL(url *url.URL) (proxy.Client, error) { if vStr != "" { v, err := strconv.Atoi(vStr) if err == nil { - if v == 1 { + if v == 0 { + + } else if v == 1 { c.version = 1 + + vless1_udp_multiStr := url.Query().Get("vless1_udp_multi") + + if vless1_udp_multiStr == "true" || vless1_udp_multiStr == "1" { + if ce := utils.CanLogDebug("vless v1 using udp multi"); ce != nil { + ce.Write() + } + c.udp_multi = true + } + + } else { + return nil, utils.ErrInErr{ErrDesc: "given version bigger than 1", ErrDetail: utils.ErrNotImplemented} } } } @@ -143,8 +167,15 @@ func (c *Client) EstablishUDPChannel(underlay net.Conn, target netLayer.Addr) (n _, err = underlay.Write(buf.Bytes()) utils.PutBuf(buf) + target.Network = "udp" - return &UDPConn{Conn: underlay, version: c.version, isClientEnd: true, raddr: target}, err + return &UDPConn{ + Conn: underlay, + version: c.version, + isClientEnd: true, + raddr: target, + udp_multi: c.udp_multi, + }, err } diff --git a/proxy/vless/server.go b/proxy/vless/server.go index a577721..08a2d02 100644 --- a/proxy/vless/server.go +++ b/proxy/vless/server.go @@ -25,7 +25,6 @@ func init() { type Server struct { proxy.ProxyCommonStruct userHashes map[[16]byte]*proxy.V2rayUser - //userCRUMFURS map[[16]byte]*CRUMFURS mux4Hashes sync.RWMutex } @@ -39,7 +38,6 @@ func (_ ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) { } s := &Server{ userHashes: make(map[[16]byte]*proxy.V2rayUser), - //userCRUMFURS: make(map[[16]byte]*CRUMFURS), } s.addV2User(id) @@ -59,7 +57,6 @@ func NewServer(url *url.URL) (proxy.Server, error) { } s := &Server{ userHashes: make(map[[16]byte]*proxy.V2rayUser), - //userCRUMFURS: make(map[[16]byte]*CRUMFURS), } s.addV2User(id) diff --git a/proxy/vless/udpConn.go b/proxy/vless/udpConn.go index a37f78f..3ff4341 100644 --- a/proxy/vless/udpConn.go +++ b/proxy/vless/udpConn.go @@ -97,7 +97,7 @@ func (uc *UDPConn) readData_with_len() ([]byte, error) { return nil, err } - l := int(int16(b1)<<8 + int16(b2)) + l := int(uint16(b1)<<8 + uint16(b2)) bs := utils.GetBytes(l) n, err := io.ReadFull(uc.bufr, bs) diff --git a/proxy/vless/v1_crumfurs.go.bak b/proxy/vless/v1_crumfurs.go.bak deleted file mode 100644 index 6cdfab3..0000000 --- a/proxy/vless/v1_crumfurs.go.bak +++ /dev/null @@ -1,157 +0,0 @@ -package vless - -import ( - "bufio" - "io" - "net" - - "github.com/hahahrfool/v2ray_simple/netLayer" - "github.com/hahahrfool/v2ray_simple/utils" -) - -func (s *Server) Get_CRUMFURS(id string) *CRUMFURS { - bs, err := utils.StrToUUID(id) - if err != nil { - return nil - } - return s.userCRUMFURS[bs] -} - -type CRUMFURS struct { - net.Conn - hasAdvancedLayer bool //在用ws或grpc时,这个开关保持打开 -} - -func (c *CRUMFURS) WriteUDPResponse(a net.UDPAddr, b []byte) (err error) { - atype := netLayer.AtypIP4 - if len(a.IP) > 4 { - atype = netLayer.AtypIP6 - } - buf := utils.GetBuf() - - buf.WriteByte(atype) - buf.Write(a.IP) - buf.WriteByte(byte(int16(a.Port) >> 8)) - buf.WriteByte(byte(int16(a.Port) << 8 >> 8)) - - if !c.hasAdvancedLayer { - lb := int16(len(b)) - - buf.WriteByte(byte(lb >> 8)) - buf.WriteByte(byte(lb << 8 >> 8)) - } - buf.Write(b) - - _, err = c.Write(buf.Bytes()) - - utils.PutBuf(buf) - return -} - -// 把在 CRUMFURS 信道中 获取到的 未知流量 转发到 UDPResponseWriter (本作中就是 转发到 inServer 中, 而且 只有 socks5 这一种 inServer 实现了该方法, 见 main.go) -func (c *Client) handle_CRUMFURS(UMFURS_conn net.Conn) { - - if c.udpResponseChan == nil { - c.is_CRUMFURS_established = false - return - } - - for { - //之前讨论了,udp信息通通要传长度头,CRUMFURS 也不例外,在 没有AdvancedLayer时,统一都要加udp长度头 - - if c.AdvancedL != "" { - - buf_for_umfurs := utils.GetPacket() - n, err := UMFURS_conn.Read(buf_for_umfurs) - if err != nil { - break - } - if n < 7 { - - break - } - msg := buf_for_umfurs[:n] - atyp := msg[0] - portIndex := net.IPv4len - switch atyp { - case netLayer.AtypIP6: - portIndex = net.IPv6len - default: - //不合法,必须是ipv4或者ipv6 - break - - } - theIP := make(net.IP, portIndex) - copy(theIP, msg[1:portIndex]) - - port := int16(msg[portIndex])<<8 + int16(msg[portIndex+1]) - - c.udpResponseChan <- netLayer.UDPAddrData{ - Addr: net.UDPAddr{ - IP: theIP, - Port: int(port), - }, - Data: msg[portIndex+2:], - } - } else { - if c.crumfursBuf == nil { - c.crumfursBuf = bufio.NewReader(UMFURS_conn) - } - - atyp, err := c.crumfursBuf.ReadByte() - if err != nil { - break - } - - ipLen := net.IPv4len - switch atyp { - case netLayer.AtypIP6: - ipLen = net.IPv6len - default: - //不合法,必须是ipv4或者ipv6 - break - } - - theIP := make(net.IP, ipLen) - _, err = c.crumfursBuf.Read(theIP) - if err != nil { - break - } - - twoBytes, err := c.crumfursBuf.Peek(2) - if err != nil { - break - } - - port := int(int16(twoBytes[0])<<8 + int16(twoBytes[1])) - - c.crumfursBuf.Discard(2) - - twoBytes, err = c.crumfursBuf.Peek(2) - if err != nil { - break - } - - packetLen := int16(twoBytes[0])<<8 + int16(twoBytes[1]) - c.crumfursBuf.Discard(2) - - msg := make([]byte, packetLen) - - _, err = io.ReadFull(c.crumfursBuf, msg) - if err != nil { - break - } - - c.udpResponseChan <- netLayer.UDPAddrData{ - Addr: net.UDPAddr{ - IP: theIP, - Port: port, - }, - Data: msg, - } - - } - } - - c.is_CRUMFURS_established = false -} diff --git a/proxy/vless/vless_test.go b/proxy/vless/vless_test.go index e30c1cb..1ebc4d6 100644 --- a/proxy/vless/vless_test.go +++ b/proxy/vless/vless_test.go @@ -3,6 +3,7 @@ package vless_test import ( "bytes" "crypto/rand" + "fmt" "io" "log" "net" @@ -13,6 +14,7 @@ import ( "github.com/hahahrfool/v2ray_simple/netLayer" "github.com/hahahrfool/v2ray_simple/proxy" + "github.com/hahahrfool/v2ray_simple/utils" ) func TestVLess0(t *testing.T) { @@ -116,17 +118,26 @@ func testVLess(version int, port string, t *testing.T) { } func TestVLess0_udp(t *testing.T) { - testVLessUDP(0, netLayer.RandPortStr(), t) + testVLessUDP(0, netLayer.RandPortStr(), 0, t) } func TestVLess1_udp(t *testing.T) { - testVLessUDP(1, "9738", t) //无法使用 testVLessUDP,见其注释 + testVLessUDP(1, netLayer.RandPortStr(), 0, t) +} + +func TestVLess1_udp_multi(t *testing.T) { + testVLessUDP(1, netLayer.RandPortStr(), 1, t) } // 完整模拟整个 vless 的udp请求 过程,即 客户端连接代理服务器,代理服务器试图访问远程服务器,这里是使用的模拟的办法模拟出一个远程udp服务器; // 其他tcp测试因为比较简单,不需要第二步测试,而这里需要 -func testVLessUDP(version int, port string, t *testing.T) { - url := "vless://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:" + port + "?version=" + strconv.Itoa(version) +func testVLessUDP(version int, port string, use_multi int, t *testing.T) { + utils.LogLevel = utils.Log_debug + utils.InitLog() + + fmtStr := "vless://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:%s?version=%d&vless1_udp_multi=%d" + + url := fmt.Sprintf(fmtStr, port, version, use_multi) fakeServerEndLocalServer, hase, errx := proxy.ServerFromURL(url) if hase { t.Log("fakeClientEndLocalServer parse err", errx) diff --git a/test_test.go b/test_test.go index 51108ec..67a52cd 100644 --- a/test_test.go +++ b/test_test.go @@ -49,27 +49,32 @@ func TestDNSLookup_CN(t *testing.T) { */ func TestUDP_dokodemo_vless(t *testing.T) { - testUDP_dokodemo_protocol("vless", 0, "tcp", t) + testUDP_dokodemo_protocol("vless", 0, "tcp", false, t) } func TestUDP_dokodemo_vless_v1(t *testing.T) { - testUDP_dokodemo_protocol("vless", 1, "tcp", t) + testUDP_dokodemo_protocol("vless", 1, "tcp", false, t) +} + +func TestUDP_dokodemo_vless_v1_udpMulti(t *testing.T) { + testUDP_dokodemo_protocol("vless", 1, "tcp", true, t) } func TestUDP_dokodemo_trojan(t *testing.T) { - testUDP_dokodemo_protocol("trojan", 0, "tcp", t) + testUDP_dokodemo_protocol("trojan", 0, "tcp", false, t) } func TestUDP_dokodemo_trojan_through_udp(t *testing.T) { - testUDP_dokodemo_protocol("trojan", 0, "udp", t) + testUDP_dokodemo_protocol("trojan", 0, "udp", false, t) } //经实测,udp dokodemo->vless/trojan (tcp/udp)->udp direct 来请求dns 是毫无问题的。 -func testUDP_dokodemo_protocol(protocol string, version int, network string, t *testing.T) { +func testUDP_dokodemo_protocol(protocol string, version int, network string, multi bool, t *testing.T) { utils.LogLevel = utils.Log_debug utils.InitLog() - const testClientConfFormatStr = ` + //同时监听两个dokodemo, 发向不同raddr, 这样就可以模拟 多raddr目标时的 vless v1的udp_multi的情况 + var testClientConfFormatStr = ` [[listen]] protocol = "dokodemo" network = "udp" @@ -77,6 +82,13 @@ host = "127.0.0.1" port = %s target = "udp://8.8.8.8:53" +[[listen]] +protocol = "dokodemo" +network = "udp" +host = "127.0.0.1" +port = %s +target = "udp://114.114.114.114:53" + [[dial]] protocol = "%s" uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" @@ -86,10 +98,16 @@ version = %d insecure = true network = "%s" ` + if multi { + testClientConfFormatStr += "\nextra = { vless1_udp_multi = true }" + } + clientListenPort := netLayer.RandPortStr() + clientListen2Port := netLayer.RandPortStr() clientDialPort := netLayer.RandPortStr() - testClientConfStr := fmt.Sprintf(testClientConfFormatStr, clientListenPort, protocol, clientDialPort, version, network) + testClientConfStr := fmt.Sprintf(testClientConfFormatStr, clientListenPort, + clientListen2Port, protocol, clientDialPort, version, network) const testServerConfFormatStr = ` [[dial]] @@ -129,6 +147,12 @@ network = "%s" t.FailNow() } + clientEndInServer2, err := proxy.NewServer(clientConf.Listen[1]) + if err != nil { + t.Log("can not create clientEndInServer: ", err) + t.FailNow() + } + // vless out clientEndOutClient, err := proxy.NewClient(clientConf.Dial[0]) if err != nil { @@ -150,6 +174,7 @@ network = "%s" } listenSer(clientEndInServer, clientEndOutClient, false) + listenSer(clientEndInServer2, clientEndOutClient, false) listenSer(serverEndInServer, serverEndOutClient, false) m := new(dns.Msg) @@ -176,6 +201,27 @@ network = "%s" t.Log("arecord is ", aa.A) } } + + r, _, err = c.Exchange(m, "127.0.0.1:"+clientListen2Port) + if r == nil { + t.Log("test2, error: ", err.Error()) + t.FailNow() + } + + if r.Rcode != dns.RcodeSuccess { + t.Log("test2, err2 ", r.Rcode, r) + t.FailNow() + } + + for _, a := range r.Answer { + t.Log("test2, header is", a.Header()) + t.Log("test2, string is", a.String()) + t.Log("test2, a is ", a) + + if aa, ok := a.(*dns.A); ok { + t.Log("test2, arecord is ", aa.A) + } + } } func TestLoadTomlConf(t *testing.T) {