diff --git a/README.md b/README.md index 5e07017..f88ab6c 100644 --- a/README.md +++ b/README.md @@ -75,9 +75,16 @@ vless v1协议还处在开发阶段,我随时可能新增、修改定义。 另外上面说的是承载数据支持udp;我们协议的底层传输方式也是全面支持udp的。也就是说可以用udp传输vless数据,然后vless里面还可以传输 udp的承载数据。 +底层用udp传输的话,可以理解为 比 v2ray的mkcp传输方式 更低级的模式,直接用udp传输, 不加任何控制。所以可能丢包,不过肯定是更快的。 + ### tls lazy encrypt (splice) -在最新代码里,还实现了 双向 tls lazy encrypt, 即另一种 xtls的 splice的实现,底层也是会调用splice,本包为了加以区分,就把这种方式叫做 tls lazy encrypt。 +**注意,因为技术实现不同,该功能不兼容xtls。**, 因为为了能够在tls包外进行过滤,我们需要做很多工作,所以技术实现与xtls是不一样的。 + +**lazy功能是对标xtls的,但是不兼容xtls,你用lazy的话,两端必须全用verysimple** + + +在最新代码里,实现了 双向 tls lazy encrypt, 即另一种 xtls的 splice的实现,底层也是会调用splice,本包为了加以区分,就把这种方式叫做 tls lazy encrypt。 tls lazy encrypt 特性 运行时可以用 -lazy 参数打开(服务端客户端都要打开),然后可以用 -pdd 参数 打印 tls 探测输出 @@ -107,7 +114,7 @@ tls lazy encrypt 特性 运行时可以用 -lazy 参数打开(服务端客户 关于splice的一个现有“降速”问题也要看看,(linux 的 forward配置问题),我们这里也是会存在的 https://github.com/XTLS/Xray-core/discussions/59 -**注意,因为技术实现不同,该功能不兼容xtls。**, 因为为了能够在tls包外进行过滤,我们需要做很多工作,所以技术实现与xtls是不一样的。 + #### 总结 tls lazy encrypt 技术优点 diff --git a/main.go b/main.go index 20c0063..272ba23 100644 --- a/main.go +++ b/main.go @@ -267,7 +267,7 @@ type incomingInserverConnState struct { //这里说的多路复用基本指的就是grpc; 如果是 vless内嵌 mux.cool 的话不属于这种情况. - // 要区分 多路复用的包装 是在vless等代理验证 的外部还是内部 + // 要区分 多路复用的包装 是在 vless等代理的握手验证 的外部 还是 内部 baseLocalConn, wrappedConn net.Conn cachedRemoteAddr string @@ -281,20 +281,12 @@ type incomingInserverConnState struct { shouldFallback bool theFallbackFirstBuffer *bytes.Buffer + + isTlsLazyServerEnd bool } -func canLazyEncryptServer(inServer proxy.Server) bool { - //grpc 这种多路复用的链接是绝对无法开启 lazy的, ws 理论上也只有服务端发向客户端的链接 内嵌tls时可以lazy,暂不考虑 - - return inServer.IsUseTLS() && inServer.AdvancedLayer() == "" -} - -func canLazyEncryptClient(outClient proxy.Client) bool { - //grpc 这种多路复用的链接是绝对无法开启 lazy的, ws 理论上也只有服务端发向客户端的链接 内嵌tls时可以lazy,暂不考虑 - - return outClient.IsUseTLS() && outClient.AdvancedLayer() == "" -} - +// handleNewIncomeConnection 会处理 网络层至高级层的数据, +// 然后将代理层的处理发往 handshakeInserver_and_passToOutClient 函数。 func handleNewIncomeConnection(inServer proxy.Server, thisLocalConnectionInstance net.Conn) { iics := incomingInserverConnState{ @@ -302,6 +294,8 @@ func handleNewIncomeConnection(inServer proxy.Server, thisLocalConnectionInstanc inServer: inServer, } + iics.isTlsLazyServerEnd = tls_lazy_encrypt && canLazyEncryptServer(inServer) + wrappedConn := thisLocalConnectionInstance if utils.CanLogInfo() { @@ -318,7 +312,7 @@ func handleNewIncomeConnection(inServer proxy.Server, thisLocalConnectionInstanc if inServer.IsUseTLS() { - if tls_lazy_encrypt && canLazyEncryptServer(inServer) { + if iics.isTlsLazyServerEnd { iics.inServerTlsRawReadRecorder = tlsLayer.NewRecorder() iics.inServerTlsRawReadRecorder.StopRecord() //先不记录,因为一开始是我们自己的tls握手包,没有意义 @@ -338,7 +332,7 @@ func handleNewIncomeConnection(inServer proxy.Server, thisLocalConnectionInstanc return } - if tls_lazy_encrypt && canLazyEncryptServer(inServer) { + if iics.isTlsLazyServerEnd { //此时已经握手完毕,可以记录了 iics.inServerTlsRawReadRecorder.StartRecord() } @@ -630,16 +624,16 @@ afterLocalServerHandshake: // 而在服务端探测时,因为 客户端传来的连接 包了 tls,所以要在tls解包后, vless 解包后,再进行判断; // 所以总之都是要在 inServer 判断 wlc; 总之,含义就是,去检索“用户承载数据”的来源 - if tls_lazy_encrypt && !(!isServerEnd && routedToDirect) { + isTlsLazy_clientEnd := tls_lazy_encrypt && canLazyEncryptClient(client) - if (!isServerEnd && canLazyEncryptClient(client)) || (isServerEnd && canLazyEncryptServer(inServer)) { - if tlsLayer.PDD { - log.Println("loading TLS SniffConn", !isServerEnd) - } + if isTlsLazy_clientEnd || iics.isTlsLazyServerEnd { - wlc = tlsLayer.NewSniffConn(iics.baseLocalConn, wlc, !isServerEnd, tls_lazy_secure) + if tlsLayer.PDD { + log.Println("loading TLS SniffConn", isTlsLazy_clientEnd, iics.isTlsLazyServerEnd) } + wlc = tlsLayer.NewSniffConn(iics.baseLocalConn, wlc, isTlsLazy_clientEnd, tls_lazy_secure) + } //如果目标是udp则要分情况讨论 @@ -698,7 +692,7 @@ afterLocalServerHandshake: } } else { - if !(tls_lazy_encrypt && canLazyEncryptServer(inServer)) { //lazy_encrypt情况比较特殊 + if !(iics.isTlsLazyServerEnd) { //lazy_encrypt情况比较特殊 defer wrappedConn.Close() } @@ -759,7 +753,7 @@ afterLocalServerHandshake: } if utils.CanLogInfo() { - log.Println(client.Name(), iics.cachedRemoteAddr, " want to dial ", proxy.GetFullName(client), targetAddr.UrlString()) + log.Println(client.Name(), iics.cachedRemoteAddr, " request ", targetAddr.UrlString(), "through", proxy.GetVSI_url(client)) } @@ -809,7 +803,7 @@ afterLocalServerHandshake: if client.IsUseTLS() { //即客户端 - if tls_lazy_encrypt && canLazyEncryptClient(client) { + if isTlsLazy_clientEnd { if tls_lazy_secure { // 如果使用secure办法,则我们每次不能先拨号,而是要detect用户的首包后再拨号 @@ -936,7 +930,7 @@ advLayerStep: if !routedToDirect && tls_lazy_encrypt { // 我们加了回落之后,就无法确定 “未使用tls的outClient 一定是在服务端” 了 - if !isServerEnd && canLazyEncryptClient(client) { + if isTlsLazy_clientEnd { if client.IsUseTLS() { //必须是 UserClient @@ -946,7 +940,7 @@ advLayerStep: } } - } else if canLazyEncryptServer(inServer) { + } else if iics.isTlsLazyServerEnd { // 最新代码已经确认,使用uuid 作为 “特殊指令”,所以要求Server必须是一个 proxy.UserServer // 否则将无法开启splice功能。这是为了防止0-rtt 探测; diff --git a/netLayer/addr.go b/netLayer/addr.go index 7141b4c..e3d0a82 100644 --- a/netLayer/addr.go +++ b/netLayer/addr.go @@ -199,9 +199,14 @@ func (a *Addr) String() string { //返回以url表示的 地址. unix的话文件名若带斜杠则会被转义 func (a *Addr) UrlString() string { - str := a.String() + if a.Network != "" { + return a.Network + "://" + url.PathEscape(a.String()) + + } else { + return "tcp://" + a.String() + + } - return a.Network + "://" + url.PathEscape(str) } func (a *Addr) GetNetIPAddr() (na netip.Addr) { @@ -214,11 +219,12 @@ func (a *Addr) GetNetIPAddr() (na netip.Addr) { return } -func (a *Addr) ToUDPAddr() *net.UDPAddr { - switch a.Network { - case "udp", "udp4", "udp6": +func (a *Addr) IsUDP() bool { + return IsStrUDP_network(a.Network) +} - default: +func (a *Addr) ToUDPAddr() *net.UDPAddr { + if !a.IsUDP() { return nil } @@ -343,3 +349,11 @@ func UDPAddr2AddrPort(ua *net.UDPAddr) netip.AddrPort { a, _ := netip.AddrFromSlice(ua.IP) return netip.AddrPortFrom(a, uint16(ua.Port)) } + +func IsStrUDP_network(s string) bool { + switch s { + case "udp", "udp4", "udp6": + return true + } + return false +} diff --git a/netLayer/listen.go b/netLayer/listen.go index fbf3dfe..8b6edf5 100644 --- a/netLayer/listen.go +++ b/netLayer/listen.go @@ -39,6 +39,7 @@ func loopAccept(listener net.Listener, acceptFunc func(net.Conn)) { } // ListenAndAccept 试图监听 所有类型的网络,包括tcp, udp 和 unix domain socket. +// // 非阻塞,在自己的goroutine中监听. func ListenAndAccept(network, addr string, acceptFunc func(net.Conn)) error { switch network { diff --git a/netLayer/splice.go b/netLayer/splice.go index da4cac6..46de7c8 100644 --- a/netLayer/splice.go +++ b/netLayer/splice.go @@ -63,9 +63,9 @@ func TryReadFrom_withSplice(classicWriter io.Writer, maySpliceConn net.Conn, r i /* 分多钟情况, - 1. underlay直接是基础连接(underlay_canSpliceDirectly),且现在直接 CanDirectWrite 就是true, 此时直接 splice - 2. underlay直接是基础连接(underlay_canSpliceDirectly),但现在的连接阶段还不能直接直连,此时要读写一次然后判断一次,直到 CanDirectWrite 变成 true - 3. underlay 不是基础连接,但是 是 Splicer(underlay_canSpliceEventually),且此时我们先等待 underlay已经处于 可直连状态,然后再确保 CanDirectWrite 返回true + 1. underlay直接是基础连接(underlay_canSpliceDirectly),且现在直接 canDirectFunc 就是true, 此时直接 splice + 2. underlay直接是基础连接(underlay_canSpliceDirectly),但现在的连接阶段还不能直接直连,此时要读写一次然后判断一次,直到 canDirectFunc 变成 true + 3. underlay 不是基础连接,但是 是 Splicer(underlay_canSpliceEventually),且此时我们先等待 underlay已经处于 可直连状态, 即 splicer.CanSplice()变成 true,然后再确保 canDirectFunc 返回true 4. underlay啥也不是,直接经典拷贝。 */ @@ -77,7 +77,7 @@ func TryReadFrom_withSplice(classicWriter io.Writer, maySpliceConn net.Conn, r i panic("uc.underlayIsBasic, but can't cast to ReadFrom") } } else { - //循环读写,直到 uc.CanDirectWrite() 和 underlay_canSpliceEventually 变为true + //循环读写,直到 canDirectFunc 和 splicer.CanSplice() 都为true buf := utils.GetPacket() defer utils.PutPacket(buf) diff --git a/proxy/proxy.go b/proxy/proxy.go index de8b68c..9e10ef0 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -55,8 +55,13 @@ type Server interface { // 这里认为, tcp/udp/kcp/raw_socket 是FirstName,具体的协议名称是 LastName, 中间层是 MiddleName // An Example of a full name: tcp+tls+ws+vless func GetFullName(pc ProxyCommon) string { - return pc.Network() + pc.MiddleName() + pc.Name() +} + +// return GetFullName(pc) + "://" + pc.AddrStr() +func GetVSI_url(pc ProxyCommon) string { + + return GetFullName(pc) + "://" + pc.AddrStr() } @@ -64,7 +69,7 @@ func GetFullName(pc ProxyCommon) string { // 一个 ProxyCommon 会内嵌proxy以及上面各层的所有信息; type ProxyCommon interface { Name() string //代理协议名称, 如vless - MiddleName() string //其它VSI层 所使用的协议,如 +tls+ws + MiddleName() string //其它VSI层 所使用的协议,前后被加了加号,如 +tls+ws+ Stop() diff --git a/proxy/vless/client.go b/proxy/vless/client.go index 7ec0d4c..12c6a29 100644 --- a/proxy/vless/client.go +++ b/proxy/vless/client.go @@ -174,7 +174,7 @@ func (c *Client) Handshake(underlay net.Conn, target *netLayer.Addr) (io.ReadWri Conn: underlay, uuid: *c.user, version: c.version, - isUDP: target.Network == "udp", + isUDP: target.IsUDP(), underlayIsBasic: netLayer.IsBasicConn(underlay), }, err } diff --git a/proxy/vless/server.go b/proxy/vless/server.go index f0d73de..8ecba13 100644 --- a/proxy/vless/server.go +++ b/proxy/vless/server.go @@ -359,7 +359,7 @@ realPart: remainFirstBufLen: readbuf.Len(), uuid: thisUUIDBytes, version: int(version), - isUDP: addr.Network == "udp", + isUDP: addr.IsUDP(), underlayIsBasic: netLayer.IsBasicConn(underlay), isServerEnd: true, }, addr, nil diff --git a/tls_lazy.go b/tls_lazy.go index 837b160..da02ffc 100644 --- a/tls_lazy.go +++ b/tls_lazy.go @@ -15,6 +15,26 @@ import ( const tlslazy_willuseSystemCall = runtime.GOOS == "linux" || runtime.GOOS == "darwin" +func canLazyEncryptServer(inServer proxy.Server) bool { + //grpc 这种多路复用的链接是绝对无法开启 lazy的, ws 理论上也只有服务端发向客户端的链接 内嵌tls时可以lazy,暂不考虑 + + return inServer.IsUseTLS() && inServer.AdvancedLayer() == "" +} + +func canLazyEncryptClient(outClient proxy.Client) bool { + //grpc 这种多路复用的链接是绝对无法开启 lazy的, ws 理论上也只有服务端发向客户端的链接 内嵌tls时可以lazy,暂不考虑 + + return outClient.IsUseTLS() && outClient.AdvancedLayer() == "" +} + +func canTargetAddr_tlsLazy(addr *netLayer.Addr) bool { + switch addr.Network { + case "tcp", "tcp4", "tcp6", "unix": + return true + } + return false +} + // tryRawCopy 尝试能否直接对拷,对拷 直接使用 原始 TCPConn,也就是裸奔转发 // 如果在linux上,则和 xtls的splice 含义相同. 在其他系统时,与xtls-direct含义相同。 // 我们内部先 使用 DetectConn进行过滤分析,然后再判断进化为splice 或者退化为普通拷贝