diff --git a/advLayer/advLayer.go b/advLayer/advLayer.go index 49fb8cf..091e8e7 100644 --- a/advLayer/advLayer.go +++ b/advLayer/advLayer.go @@ -51,9 +51,9 @@ type Conf struct { Host string Addr netLayer.Addr Path string - Headers map[string][]string //http headers - IsEarly bool //is 0-rtt or not; for quic and ws. - Extra map[string]any //quic: useHysteria, hysteria_manual, maxbyte; grpc: multiMode + Headers *httpLayer.HeaderPreset + IsEarly bool //is 0-rtt or not; for quic and ws. + Extra map[string]any //quic: useHysteria, hysteria_manual, maxbyte; grpc: multiMode } type Common interface { diff --git a/advLayer/grpcSimple/grpcSimple.go b/advLayer/grpcSimple/grpcSimple.go index 417eae4..bd5fe8a 100644 --- a/advLayer/grpcSimple/grpcSimple.go +++ b/advLayer/grpcSimple/grpcSimple.go @@ -67,23 +67,27 @@ func (Creator) IsMux() bool { return true } -func getTunPath(sn string) string { +func getServiceNameFromConf(conf *advLayer.Conf) (serviceName string) { + if conf.Path != "" { + serviceName = conf.Path + } else { + serviceName = "GunService" + } + return +} + +func getTunPath(serviceName string) string { var sb strings.Builder - sb.Grow(1 + len(sn) + 4) + sb.Grow(1 + len(serviceName) + 4) sb.WriteString("/") - sb.WriteString(sn) + sb.WriteString(serviceName) sb.WriteString("/Tun") return sb.String() } func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) { - var serviceName string - if conf.Path != "" { - serviceName = conf.Path - } else { - serviceName = "GunService" - } + serviceName := getServiceNameFromConf(conf) c := &Client{ Config: Config{ @@ -105,19 +109,30 @@ func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) { Proto: "HTTP/2", ProtoMajor: 2, ProtoMinor: 0, - Header: defaultClientHeader, + } + + if conf.Headers != nil { + h := c.theRequest.Header.Clone() + for k, vs := range defaultClientHeader { + h.Add(k, vs[0]) + } + c.theRequest.Header = h + } else { + c.theRequest.Header = defaultClientHeader } return c, nil } func (Creator) NewServerFromConf(conf *advLayer.Conf) (advLayer.Server, error) { + serviceName := getServiceNameFromConf(conf) s := &Server{ Config: Config{ - ServiceName: conf.Path, + ServiceName: serviceName, Host: conf.Host, }, - path: getTunPath(conf.Path), + path: getTunPath(serviceName), + Headers: conf.Headers, } return s, nil diff --git a/advLayer/grpcSimple/server.go b/advLayer/grpcSimple/server.go index 5f8fe49..10ca4a0 100644 --- a/advLayer/grpcSimple/server.go +++ b/advLayer/grpcSimple/server.go @@ -20,6 +20,8 @@ type Server struct { Config + Headers *httpLayer.HeaderPreset + http2.Server path string diff --git a/advLayer/ws/client.go b/advLayer/ws/client.go index d7f8f23..888e75b 100644 --- a/advLayer/ws/client.go +++ b/advLayer/ws/client.go @@ -10,6 +10,7 @@ import ( "net/url" "github.com/e1732a364fed/v2ray_simple/advLayer" + "github.com/e1732a364fed/v2ray_simple/httpLayer" "github.com/e1732a364fed/v2ray_simple/netLayer" "github.com/e1732a364fed/v2ray_simple/utils" "github.com/gobwas/ws" @@ -23,11 +24,11 @@ type Client struct { path string UseEarlyData bool - headers map[string][]string + headers *httpLayer.HeaderPreset } // 这里默认,传入的path必须 以 "/" 为前缀. 本函数 不对此进行任何检查 -func NewClient(hostAddr, path string, headers map[string][]string, isEarly bool) (*Client, error) { +func NewClient(hostAddr, path string, headers *httpLayer.HeaderPreset, isEarly bool) (*Client, error) { u, err := url.Parse("http://" + hostAddr + path) if err != nil { return nil, err @@ -82,8 +83,11 @@ func (c *Client) Handshake(underlay net.Conn, ed []byte) (net.Conn, error) { // 默认不给出Protocols的话, gobwas就不会发送这个header, 另一端也收不到此header } - if len(c.headers) > 0 { - d.Header = ws.HandshakeHeaderHTTP(c.headers) + + if c.headers != nil && c.headers.Request != nil && len(c.headers.Request.Headers) > 0 { + d.Header = ws.HandshakeHeaderHTTP(c.headers.Request.Headers) + + //实测Header里的Connection会被用到。 } br, _, err := d.Upgrade(underlay, c.requestURL) diff --git a/advLayer/ws/server.go b/advLayer/ws/server.go index 190ba7b..6db2c66 100644 --- a/advLayer/ws/server.go +++ b/advLayer/ws/server.go @@ -25,11 +25,11 @@ type Server struct { UseEarlyData bool Thepath string - headers map[string][]string + headers *httpLayer.HeaderPreset } // 这里默认: 传入的path必须 以 "/" 为前缀. 本函数 不对此进行任何检查. -func NewServer(path string, headers map[string][]string, UseEarlyData bool) *Server { +func NewServer(path string, headers *httpLayer.HeaderPreset, UseEarlyData bool) *Server { return &Server{ //upgrader: upgrader, @@ -85,6 +85,28 @@ func (s *Server) Handshake(underlay net.Conn) (net.Conn, error) { theWrongPath := "" var thePotentialEarlyData []byte + var requestHeaderNotGivenCount int + + noNeedToCheckRequestHeaders := s.headers == nil || s.headers.Request == nil || len(s.headers.Request.Headers) == 0 + + if !noNeedToCheckRequestHeaders { + requestHeaderNotGivenCount = len(s.headers.Request.Headers) + + for k := range s.headers.Request.Headers { + switch k { + case "Host", "Connection", "Upgrade", "Sec-WebSocket-Version", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol", "Sec-WebSocket-Accept", "Sec-WebSocket-Extensions": + requestHeaderNotGivenCount -= 1 + } + } + + } + + var responseHeader ws.HandshakeHeader + + if s.headers != nil && s.headers.Response != nil && len(s.headers.Response.Headers) > 0 { + responseHeader = ws.HandshakeHeaderHTTP(s.headers.Response.Headers) + } + var theUpgrader *ws.Upgrader = &ws.Upgrader{ //因为我们vs的架构,先统一监听tcp;然后再调用Handshake函数 @@ -118,10 +140,32 @@ func (s *Server) Handshake(underlay net.Conn) (net.Conn, error) { } return nil }, - } + OnHeader: func(key, value []byte) error { + if noNeedToCheckRequestHeaders { + return nil + } + vs := s.headers.Request.Headers[string(key)] + if len(vs) > 0 { + for _, v := range vs { + if v == (string(value)) { + requestHeaderNotGivenCount -= 1 + break + } + } + } - if len(s.headers) > 0 { - theUpgrader.Header = ws.HandshakeHeaderHTTP(s.headers) + return nil + }, + Header: responseHeader, + OnBeforeUpgrade: func() (header ws.HandshakeHeader, err error) { + if requestHeaderNotGivenCount > 0 { + if ce := utils.CanLogWarn("ws headers not match"); ce != nil { + ce.Write(zap.Int("requestHeaderNotGivenCount", requestHeaderNotGivenCount)) + } + return nil, ws.RejectConnectionError(ws.RejectionStatus(http.StatusBadRequest)) + } + return responseHeader, nil + }, } if s.UseEarlyData { diff --git a/examples/vless_httpHeader.client.toml b/examples/vless_httpHeader.client.toml index 31f22b0..6b85970 100644 --- a/examples/vless_httpHeader.client.toml +++ b/examples/vless_httpHeader.client.toml @@ -12,16 +12,32 @@ port = 4434 version = 0 insecure = true utls = true -# advancedLayer = "ws" # ws也可应用我们的http header + advancedLayer = "ws" # ws也可应用我们的http header + +path = "/very" [dial.header.request] -version = "1.1" -method = "GET" -path = ["/very","/simple"] # 每次请求随机选择一个值。 +version = "1.1" #如果你用ws, 则这一项不会被用到 +method = "GET" #如果你用ws, 则这一项不会被用到 +path = ["/very","/simple"] # 每次请求随机选择一个值。如果你用ws, 则这一项不会被用到 -# 如果使用 ws, 则 Connection头必须指明为 Upgrade -#headers.Connection = [ "Upgrade" ] -#[dial.header.response] -# 如果使用 ws, 则 Connection头必须指明为 Upgrade, response 也一样 +# 如果使用 ws, 则 dial的 Connection头必须指明为 Upgrade, 这是因为, headers配置的默认行为是,如果你不给出header, 我们就会配置一个默认的header给你,而默认的header的 Connection 是 keep-alive, 这个对于ws的握手是无效的。 + +headers.Connection = [ "Upgrade" ] + +headers.mycustom1 = ["verysimple"] + + +headers.Host = ["www.baidu.com","www.bing.com"] +headers.User-Agent = [ "Mozilla/5.0 (my fake system)"] +headers.Accept-Encoding = [ "gzip, deflate" ] +headers.Pragma = ["no-cache"] + + + + +[dial.header.response] +# 如果使用 ws, 则 Connection头不用给出,因为必须是Upgrade; 你给出也是可以的,但是会被无视。 #headers.Connection = [ "Upgrade" ] +headers.mycustom2 = ["verysimple_is_so_good"] diff --git a/examples/vless_httpHeader.server.toml b/examples/vless_httpHeader.server.toml index 281041e..9f68d0a 100644 --- a/examples/vless_httpHeader.server.toml +++ b/examples/vless_httpHeader.server.toml @@ -9,8 +9,8 @@ fallback = ":80" cert = "cert.pem" key = "cert.key" -# advancedLayer = "ws" - + advancedLayer = "ws" +path = "/very" # 下面是 http伪装头的 配置 # 完全兼容v2ray, 可参考 https://www.v2fly.org/config/transport/tcp.html#httprequestobject @@ -24,22 +24,28 @@ path = ["/very","/simple"] # 每次请求随机选择一个值。 # headers里的数组, 每次请求随机选择一个值。 -# 如果使用 ws, 则 Connection头必须指明为 Upgrade -#headers.Connection = [ "Upgrade" ] - - -#headers.Host = ["www.baidu.com","www.bing.com"] -#headers.User-Agent = [ "Mozilla/5.0 (my fake system)"] -#headers.Accept-Encoding = [ "gzip, deflate" ] #headers.Connection = [ "keep-alive" ] -#headers.Pragma = ["no-cache"] + +# 如果使用 ws, 则 listen的 Connection头 不用给出,因为必须是Upgrade; 你给出也是可以的,但是会被无视。 + +# 如果你给出的Header的首字母小写的,则首字母自动被转换成大写. +headers.mycustom1 = ["verysimple"] + + +headers.Host = ["www.baidu.com","www.bing.com"] +headers.User-Agent = [ "Mozilla/5.0 (my fake system)"] +headers.Accept-Encoding = [ "gzip, deflate" ] +headers.Pragma = ["no-cache"] + + # 还可以配置 response, 道理一样。这里直接省略了,就是使用默认值, 参考 v2ray文档。 -#[listen.header.response] -# 如果使用 ws, 则 Connection头必须指明为 Upgrade, response 也一样 -#headers.Connection = [ "Upgrade" ] +[listen.header.response] +# 如果使用 ws, 则 listen.header.response 的 Connection头必须指明为 Upgrade +headers.Connection = [ "Upgrade" ] +headers.mycustom2 = ["verysimple_is_so_good"] [[dial]] protocol = "direct" diff --git a/httpLayer/header.go b/httpLayer/header.go index e9343ca..453270f 100644 --- a/httpLayer/header.go +++ b/httpLayer/header.go @@ -41,6 +41,34 @@ type HeaderPreset struct { Response *ResponseHeader `toml:"response"` } +// 将Header改为首字母大写 +func (hh *HeaderPreset) Prepare() { + if hh.Request != nil && len(hh.Request.Headers) > 0 { + + var realHeaders http.Header = make(http.Header) + for k, vs := range hh.Request.Headers { + for _, v := range vs { + realHeaders.Add(k, v) + + } + } + + hh.Request.Headers = realHeaders + } + if hh.Response != nil && len(hh.Response.Headers) > 0 { + + var realHeaders http.Header = make(http.Header) + for k, vs := range hh.Response.Headers { + for _, v := range vs { + realHeaders.Add(k, v) + + } + } + + hh.Response.Headers = realHeaders + } +} + //默认值保持与v2ray的配置相同 func (hh *HeaderPreset) AssignDefaultValue() { if hh.Request == nil { @@ -89,6 +117,8 @@ func (hh *HeaderPreset) AssignDefaultValue() { "Pragma": {"no-cache"}, } } + + hh.Prepare() } func (h *HeaderPreset) ReadRequest(underlay net.Conn) (err error, leftBuf *bytes.Buffer) { diff --git a/iics.go b/iics.go new file mode 100644 index 0000000..3bb0955 --- /dev/null +++ b/iics.go @@ -0,0 +1,174 @@ +package v2ray_simple + +import ( + "bytes" + "net" + "net/http" + + "github.com/e1732a364fed/v2ray_simple/httpLayer" + "github.com/e1732a364fed/v2ray_simple/netLayer" + "github.com/e1732a364fed/v2ray_simple/proxy" + "github.com/e1732a364fed/v2ray_simple/tlsLayer" + "github.com/e1732a364fed/v2ray_simple/utils" + "go.uber.org/zap" +) + +//一个贯穿转发流程的关键结构 +type incomingInserverConnState struct { + + // 在多路复用的情况下, 可能产生多个 IncomingInserverConnState, + // 共用一个 baseLocalConn, 但是 wrappedConn 各不相同。 + + baseLocalConn net.Conn // baseLocalConn 是来自客户端的原始传输层链接 + wrappedConn net.Conn // wrappedConn 是层层握手后,代理层握手前 包装的链接,一般为tls层/高级层; + inServer proxy.Server //可为 nil + defaultClient proxy.Client + + cachedRemoteAddr string + theRequestPath string + + inServerTlsConn *tlsLayer.Conn + inServerTlsRawReadRecorder *tlsLayer.Recorder + + isFallbackH2 bool + fallbackH2Request *http.Request + theFallbackFirstBuffer *bytes.Buffer + + fallbackXver int + + isTlsLazyServerEnd bool + + shouldCloseInSerBaseConnWhenFinish bool + + routedToDirect bool + + RoutingEnv *proxy.RoutingEnv //used in passToOutClient +} + +// 在调用 passToOutClient前遇到err时调用, 若找出了buf,设置iics,并返回true +func (iics *incomingInserverConnState) extractFirstBufFromErr(err error) bool { + if ce := utils.CanLogWarn("failed in inServer proxy handshake"); ce != nil { + ce.Write( + zap.String("handler", iics.inServer.AddrStr()), + zap.Error(err), + ) + } + + if !iics.inServer.CanFallback() { + iics.wrappedConn.Close() + return false + } + + //通过err找出 并赋值给 iics.theFallbackFirstBuffer + { + + fe, ok := err.(*utils.ErrBuffer) + if !ok { + // 能fallback 但是返回的 err却不是fallback err,证明遇到了更大问题,可能是底层read问题,所以也不用继续fallback了 + iics.wrappedConn.Close() + return false + } + + if firstbuffer := fe.Buf; firstbuffer == nil { + //不应该,至少能读到1字节的。 + + panic("No FirstBuffer") + + } else { + iics.theFallbackFirstBuffer = firstbuffer + + } + } + return true +} + +//查看当前配置 是否支持fallback, 并获得回落地址。 +// 被 passToOutClient 调用. 若 无fallback则 result < 0, 否则返回所使用的 PROXY protocol 版本, 0 表示 回落但是不用 PROXY protocol. +func checkfallback(iics incomingInserverConnState) (targetAddr netLayer.Addr, result int) { + //先检查 mainFallback,如果mainFallback中各项都不满足 or根本没有 mainFallback 再检查 defaultFallback + + //一般情况下 iics.RoutingEnv 都会给出,但是 如果是 热加载、tproxy、go test、单独自定义 调用 ListenSer 不给出env 等情况的话, iics.RoutingEnv 都是空值 + if iics.RoutingEnv != nil { + + if mf := iics.RoutingEnv.MainFallback; mf != nil { + + var thisFallbackType byte + + theRequestPath := iics.theRequestPath + + if iics.theFallbackFirstBuffer != nil && theRequestPath == "" { + var failreason int + + _, _, theRequestPath, failreason = httpLayer.GetRequestMethod_and_PATH_from_Bytes(iics.theFallbackFirstBuffer.Bytes(), false) + + if failreason != 0 { + theRequestPath = "" + } + + } + + fallback_params := make([]string, 0, 4) + + if theRequestPath != "" { + fallback_params = append(fallback_params, theRequestPath) + thisFallbackType |= httpLayer.Fallback_path + } + + if inServerTlsConn := iics.inServerTlsConn; inServerTlsConn != nil { + //默认似乎默认tls不会给出alpn和sni项?获得的是空值,也许是因为我用了自签名+insecure,所以导致server并不会设置连接好后所协商的ServerName + // 而alpn则也是正常的, 不设置肯定就是空值 + alpn := inServerTlsConn.GetAlpn() + + if alpn != "" { + fallback_params = append(fallback_params, alpn) + thisFallbackType |= httpLayer.Fallback_alpn + + } + + sni := inServerTlsConn.GetSni() + if sni != "" { + fallback_params = append(fallback_params, sni) + thisFallbackType |= httpLayer.Fallback_sni + } + } + + { + fromTag := iics.inServer.GetTag() + + fbResult := mf.GetFallback(fromTag, thisFallbackType, fallback_params...) + if fbResult == nil { + fbResult = mf.GetFallback("", thisFallbackType, fallback_params...) + } + + if ce := utils.CanLogDebug("Fallback check"); ce != nil { + if fbResult != nil { + ce.Write( + zap.String("matched", fbResult.Addr.String()), + ) + } else { + ce.Write( + zap.String("no match", ""), + ) + } + } + if fbResult != nil { + targetAddr = fbResult.Addr + result = fbResult.Xver + return + } + } + + } + + } + + //默认回落, 每个listen配置 都可 有一个自己独享的默认回落 + + if defaultFallbackAddr := iics.inServer.GetFallback(); defaultFallbackAddr != nil { + + targetAddr = *defaultFallbackAddr + result = 0 + + } + return +} diff --git a/main.go b/main.go index 2e0d84f..f3c651a 100644 --- a/main.go +++ b/main.go @@ -4,11 +4,9 @@ import ( "bytes" "crypto/tls" "errors" - "flag" "fmt" "io" "net" - "net/http" "net/url" "os" "sync" @@ -44,25 +42,28 @@ var ( var ( DirectClient, _, _ = proxy.ClientFromURL(proxy.DirectName + "://") - - Tls_lazy_encrypt bool - Tls_lazy_secure bool ) -func init() { +//用于回落到h2c +var ( + h2c_transport = &http2.Transport{ + DialTLS: func(n, a string, cfg *tls.Config) (net.Conn, error) { + return net.Dial(n, a) + }, + AllowHTTP: true, + } - flag.BoolVar(&Tls_lazy_encrypt, "lazy", false, "tls lazy encrypt (splice)") - flag.BoolVar(&Tls_lazy_secure, "ls", false, "tls lazy secure, use special techs to ensure the tls lazy encrypt data can't be detected. Only valid at client end.") - -} + h2c_PROXYprotocolAddrMap = make(map[string]*http2.Transport) + h2c_PROXYprotocolAddrMap_mutex sync.RWMutex +) // ListenSer 函数 是本包 最重要的函数。可以 直接使用 本函数 来手动开启新的 自定义的 转发流程。 // 监听 inServer, 然后试图转发到一个 proxy.Client。如果env没给出,则会转发到 defaultOutClient。 // 若 env 不为 nil, 则会 进行分流或回落。具有env的情况下,可能会转发到 非 defaultOutClient 的其他 proxy.Client. // -// 使用方式可以参考 tcp_test.go, udp_test.go or cmd/verysimple. +// Use cases: refer to tcp_test.go, udp_test.go or cmd/verysimple. // -// 非阻塞. 返回的closer 用于 停止监听,若为 nil则表示监听失败。 +// non-blocking. closer used to stop listening. It means listening failed if closer == nil, func ListenSer(inServer proxy.Server, defaultOutClient proxy.Client, env *proxy.RoutingEnv) (closer io.Closer) { var handleHere bool @@ -157,74 +158,6 @@ func ListenSer(inServer proxy.Server, defaultOutClient proxy.Client, env *proxy. return } -type incomingInserverConnState struct { - - // 在多路复用的情况下, 可能产生多个 IncomingInserverConnState, - // 共用一个 baseLocalConn, 但是 wrappedConn 各不相同。 - - baseLocalConn net.Conn // baseLocalConn 是来自客户端的原始网络层链接 - wrappedConn net.Conn // wrappedConn 是层层握手后,代理层握手前 包装的链接,一般为tls层or高级层; - inServer proxy.Server //可为 nil - defaultClient proxy.Client - - cachedRemoteAddr string - theRequestPath string - - inServerTlsConn *tlsLayer.Conn - inServerTlsRawReadRecorder *tlsLayer.Recorder - - isFallbackH2 bool - fallbackH2Request *http.Request - theFallbackFirstBuffer *bytes.Buffer - - fallbackXver int - - isTlsLazyServerEnd bool - - shouldCloseInSerBaseConnWhenFinish bool - - routedToDirect bool - - RoutingEnv *proxy.RoutingEnv //used in passToOutClient -} - -// 在调用 passToOutClient前遇到err时调用, 若找出了buf,设置iics,并返回true -func (iics *incomingInserverConnState) extractFirstBufFromErr(err error) bool { - if ce := utils.CanLogWarn("failed in inServer proxy handshake"); ce != nil { - ce.Write( - zap.String("handler", iics.inServer.AddrStr()), - zap.Error(err), - ) - } - - if !iics.inServer.CanFallback() { - iics.wrappedConn.Close() - return false - } - - //通过err找出 并赋值给 iics.theFallbackFirstBuffer - { - - fe, ok := err.(*utils.ErrBuffer) - if !ok { - // 能fallback 但是返回的 err却不是fallback err,证明遇到了更大问题,可能是底层read问题,所以也不用继续fallback了 - iics.wrappedConn.Close() - return false - } - - if firstbuffer := fe.Buf; firstbuffer == nil { - //不应该,至少能读到1字节的。 - - panic("No FirstBuffer") - - } else { - iics.theFallbackFirstBuffer = firstbuffer - - } - } - return true -} - // handleNewIncomeConnection 会处理 网络层至高级层的数据, // 然后将代理层的处理发往 handshakeInserver_and_passToOutClient 函数。 // @@ -572,111 +505,8 @@ func handshakeInserver_and_passToOutClient(iics incomingInserverConnState) { } -//查看当前配置 是否支持fallback, 并获得回落地址。 -// 被 passToOutClient 调用. 若 无fallback则 result < 0, 否则返回所使用的 PROXY protocol 版本, 0 表示 回落但是不用 PROXY protocol. -func checkfallback(iics incomingInserverConnState) (targetAddr netLayer.Addr, result int) { - //先检查 mainFallback,如果mainFallback中各项都不满足 or根本没有 mainFallback 再检查 defaultFallback - - //一般情况下 iics.RoutingEnv 都会给出,但是 如果是 热加载、tproxy、go test、单独自定义 调用 ListenSer 不给出env 等情况的话, iics.RoutingEnv 都是空值 - if iics.RoutingEnv != nil { - - if mf := iics.RoutingEnv.MainFallback; mf != nil { - - var thisFallbackType byte - - theRequestPath := iics.theRequestPath - - if iics.theFallbackFirstBuffer != nil && theRequestPath == "" { - var failreason int - - _, _, theRequestPath, failreason = httpLayer.GetRequestMethod_and_PATH_from_Bytes(iics.theFallbackFirstBuffer.Bytes(), false) - - if failreason != 0 { - theRequestPath = "" - } - - } - - fallback_params := make([]string, 0, 4) - - if theRequestPath != "" { - fallback_params = append(fallback_params, theRequestPath) - thisFallbackType |= httpLayer.Fallback_path - } - - if inServerTlsConn := iics.inServerTlsConn; inServerTlsConn != nil { - //默认似乎默认tls不会给出alpn和sni项?获得的是空值,也许是因为我用了自签名+insecure,所以导致server并不会设置连接好后所协商的ServerName - // 而alpn则也是正常的, 不设置肯定就是空值 - alpn := inServerTlsConn.GetAlpn() - - if alpn != "" { - fallback_params = append(fallback_params, alpn) - thisFallbackType |= httpLayer.Fallback_alpn - - } - - sni := inServerTlsConn.GetSni() - if sni != "" { - fallback_params = append(fallback_params, sni) - thisFallbackType |= httpLayer.Fallback_sni - } - } - - { - fromTag := iics.inServer.GetTag() - - fbResult := mf.GetFallback(fromTag, thisFallbackType, fallback_params...) - if fbResult == nil { - fbResult = mf.GetFallback("", thisFallbackType, fallback_params...) - } - - if ce := utils.CanLogDebug("Fallback check"); ce != nil { - if fbResult != nil { - ce.Write( - zap.String("matched", fbResult.Addr.String()), - ) - } else { - ce.Write( - zap.String("no match", ""), - ) - } - } - if fbResult != nil { - targetAddr = fbResult.Addr - result = fbResult.Xver - return - } - } - - } - - } - - //默认回落, 每个listen配置 都可 有一个自己独享的默认回落 - - if defaultFallbackAddr := iics.inServer.GetFallback(); defaultFallbackAddr != nil { - - targetAddr = *defaultFallbackAddr - result = 0 - - } - return -} - -var ( - h2c_transport = &http2.Transport{ - DialTLS: func(n, a string, cfg *tls.Config) (net.Conn, error) { - return net.Dial(n, a) - }, - AllowHTTP: true, - } - - h2c_PROXYprotocolAddrMap = make(map[string]*http2.Transport) - h2c_PROXYprotocolAddrMap_mutex sync.RWMutex -) - //被 handshakeInserver_and_passToOutClient 和 handshakeInserver 的innerMux部分 调用。 -// 会调用 dialClient_andRelay。若isfallback为true,传入的 wlc 和 udp_wlc 必须为nil,targetAddr必须为空值。 +// 会调用 dialClient_andRelay. 若isfallback为true,传入的 wlc 和 udp_wlc 必须为nil,targetAddr必须为空值。 func passToOutClient(iics incomingInserverConnState, isfallback bool, wlc net.Conn, udp_wlc netLayer.MsgConn, targetAddr netLayer.Addr) { ////////////////////////////// 回落阶段 ///////////////////////////////////// @@ -698,8 +528,12 @@ func passToOutClient(iics incomingInserverConnState, isfallback bool, wlc net.Co url, _ := url.Parse(urlStr) rq.URL = url - transport := h2c_transport - if fbResult > 0 { + var transport *http2.Transport + + if fbResult == 0 { + transport = h2c_transport + + } else if fbResult > 0 { h2c_PROXYprotocolAddrMap_mutex.RLock() transport = h2c_PROXYprotocolAddrMap[targetAddrStr] @@ -915,13 +749,11 @@ func passToOutClient(iics incomingInserverConnState, isfallback bool, wlc net.Co } //dialClient 对实际client进行拨号,处理传输层, tls层, 高级层等所有层级后,进行代理层握手。 -// result = 0 表示拨号成功, result = -1 表示 拨号失败, result = 1 表示 拨号成功 并 已经自行处理了转发阶段(用于lazy和 innerMux ); -10 标识因为client为reject而关闭了连接。 -// 在 dialClient_andRelay 中被调用。在udp为multi channel时也有用到 -func dialClient(targetAddr netLayer.Addr, - client proxy.Client, xver int, fallbackFirstBuf *bytes.Buffer, - baseLocalConn, +// result = 0 表示拨号成功, result = -1 表示 拨号失败, result = 1 表示 拨号成功 并 已经自行处理了转发阶段(用于lazy和 innerMux ); -10 标识 因为 client为reject 而关闭了连接。 +// 在 dialClient_andRelay 中被调用。在udp为multi channel时也有用到. +func dialClient(iics incomingInserverConnState, targetAddr netLayer.Addr, + client proxy.Client, wlc net.Conn, - cachedRemoteAddr string, isTlsLazy_clientEnd bool) ( //return values: @@ -979,7 +811,7 @@ func dialClient(targetAddr netLayer.Addr, if ce := utils.CanLogInfo("Request"); ce != nil { ce.Write( - zap.String("from", cachedRemoteAddr), + zap.String("from", iics.cachedRemoteAddr), zap.String("target", targetAddr.UrlString()), zap.String("through", proxy.GetVSI_url(client)), ) @@ -1074,7 +906,7 @@ func dialClient(targetAddr netLayer.Addr, } - if xver > 0 && xver < 3 { + if xver := iics.fallbackXver; xver > 0 && xver < 3 { utils.Debug("Trying to write proxy protocol head") netLayer.WritePROXYprotocol(xver, wlc, clientConn) @@ -1184,8 +1016,8 @@ advLayerHandshakeStep: // rpc error: code = Unavailable desc = connection error: desc = "transport: failed to write client preface: tls: use of closed connection" } - if baseLocalConn != nil { - baseLocalConn.Close() + if iics.baseLocalConn != nil { + iics.baseLocalConn.Close() } result = -1 return @@ -1266,9 +1098,9 @@ advLayerHandshakeStep: if !hasInnerMux { //如果有内层mux,要在dialInnerProxy函数里再读, 而不是在这里读 - if fallbackFirstBuf != nil { + if iics.theFallbackFirstBuffer != nil { - firstPayload = fallbackFirstBuf.Bytes() + firstPayload = iics.theFallbackFirstBuffer.Bytes() } else { firstPayload = utils.GetMTU() @@ -1468,7 +1300,7 @@ func dialClient_andRelay(iics incomingInserverConnState, targetAddr netLayer.Add } } - wrc, udp_wrc, realTargetAddr, clientEndRemoteClientTlsRawReadRecorder, result := dialClient(targetAddr, client, iics.fallbackXver, iics.theFallbackFirstBuffer, iics.baseLocalConn, wlc, iics.cachedRemoteAddr, isTlsLazy_clientEnd) + wrc, udp_wrc, realTargetAddr, clientEndRemoteClientTlsRawReadRecorder, result := dialClient(iics, targetAddr, client, wlc, isTlsLazy_clientEnd) if result != 0 { return } @@ -1514,11 +1346,8 @@ func dialClient_andRelay(iics incomingInserverConnState, targetAddr netLayer.Add } else { - if iics.theFallbackFirstBuffer != nil { - - udp_wrc.WriteMsgTo(iics.theFallbackFirstBuffer.Bytes(), targetAddr) - utils.PutBytes(iics.theFallbackFirstBuffer.Bytes()) - + if ffb := iics.theFallbackFirstBuffer; ffb != nil { + udp_wrc.WriteMsgTo(ffb.Bytes(), targetAddr) } atomic.AddInt32(&ActiveConnectionCount, 1) @@ -1529,7 +1358,7 @@ func dialClient_andRelay(iics incomingInserverConnState, targetAddr netLayer.Add netLayer.RelayUDP_separate(udp_wrc, udp_wlc, &targetAddr, &AllDownloadBytesSinceStart, &AllUploadBytesSinceStart, func(raddr netLayer.Addr) netLayer.MsgConn { utils.Debug("Relaying UDP with MultiChannel,dialfunc called") - _, udp_wrc, _, _, result := dialClient(raddr, client, -1, iics.theFallbackFirstBuffer, iics.baseLocalConn, nil, "", false) + _, udp_wrc, _, _, result := dialClient(iics, raddr, client, nil, false) if ce := utils.CanLogDebug("Relaying UDP with MultiChannel, dialfunc call returned"); ce != nil { ce.Write(zap.Int("result", result)) diff --git a/proxy/client_direct.go b/proxy/client_direct.go index 649f6ac..f59e81b 100644 --- a/proxy/client_direct.go +++ b/proxy/client_direct.go @@ -38,7 +38,7 @@ type DirectClient struct { func (*DirectClient) Name() string { return DirectName } -//若 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 underlay == nil { diff --git a/proxy/common.go b/proxy/common.go index 4fe0ceb..8989671 100644 --- a/proxy/common.go +++ b/proxy/common.go @@ -318,11 +318,9 @@ func (s *ProxyCommonStruct) InitAdvLayer() { if dc := s.dialConf; dc != nil { - var Headers map[string][]string - if dc.HttpHeader != nil { - if dc.HttpHeader.Request != nil { - Headers = dc.HttpHeader.Request.Headers - } + var Headers *httpLayer.HeaderPreset + if creator.CanHandleHeaders() { + Headers = dc.HttpHeader } advClient, err := creator.NewClientFromConf(&advLayer.Conf{ @@ -355,11 +353,10 @@ func (s *ProxyCommonStruct) InitAdvLayer() { if lc := s.listenConf; lc != nil { - var Headers map[string][]string - if lc.HttpHeader != nil { - if lc.HttpHeader.Request != nil { - Headers = lc.HttpHeader.Response.Headers - } + var Headers *httpLayer.HeaderPreset + + if creator.CanHandleHeaders() { + Headers = lc.HttpHeader } var certArray []tls.Certificate diff --git a/tls_lazy.go b/tls_lazy.go index 9a10582..8cea258 100644 --- a/tls_lazy.go +++ b/tls_lazy.go @@ -1,6 +1,7 @@ package v2ray_simple import ( + "flag" "io" "log" "net" @@ -15,8 +16,20 @@ import ( "go.uber.org/zap" ) +var ( + Tls_lazy_encrypt bool + Tls_lazy_secure bool +) + const tlslazy_willuseSystemCall = runtime.GOOS == "linux" || runtime.GOOS == "darwin" +func init() { + + flag.BoolVar(&Tls_lazy_encrypt, "lazy", false, "tls lazy encrypt (splice)") + flag.BoolVar(&Tls_lazy_secure, "ls", false, "tls lazy secure, use special techs to ensure the tls lazy encrypt data can't be detected. Only valid at client end.") + +} + //grpc 这种多路复用的链接是绝对无法开启 lazy的, ws 理论上也只有服务端发向客户端的链接 内嵌tls时可以lazy,但暂不考虑 func canLazyEncryptServer(inServer proxy.Server) bool {