Files
v2ray_simple/main.go
2023-02-12 11:38:03 +08:00

1810 lines
48 KiB
Go
Raw Permalink 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 v2ray_simple
import (
"bytes"
"crypto/tls"
"errors"
"io"
"net"
"net/url"
"os"
"sync"
"sync/atomic"
"time"
"go.uber.org/zap"
"golang.org/x/net/http2"
"github.com/e1732a364fed/v2ray_simple/advLayer"
"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"
)
// statistics
type GlobalInfo struct {
ActiveConnectionCount int32
AllDownloadBytesSinceStart uint64
AllUploadBytesSinceStart uint64
}
var (
//一个默认的 非 fullcone 的 direct Client
DirectClient, _ = proxy.ClientFromURL(proxy.DirectURL)
)
// 用于回落到h2c
var (
fallback_h2c_transport = &http2.Transport{
DialTLS: func(n, a string, cfg *tls.Config) (net.Conn, error) {
// if ce := utils.CanLogDebug("fallback_h2c_transport, got dial"); ce != nil {
// ce.Write(zap.String("a", n))
// }
return net.Dial(n, a)
},
AllowHTTP: true,
}
fb_h2c_PROXYprotocolAddrMap = make(map[string]*http2.Transport)
fb_h2c_PROXYprotocolAddrMap_mutex sync.RWMutex
)
/*
ListenSer 函数 是本包 最重要的函数。可以 直接使用 本函数 来手动开启新的 自定义的 转发流程。
监听 inServer, 然后试图转发到一个 proxy.Client。如果env没给出则会转发到 defaultOutClient。
若 env 不为 nil, 则会 进行分流或回落。具有env的情况下可能会转发到 非 defaultOutClient 的其他 proxy.Client.
inServer and defaultOutClient must not be nil.
Use cases: refer to tcp_test.go, udp_test.go or cmd/verysimple.
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, gi *GlobalInfo) (closer io.Closer) {
var extraCloser io.Closer
var is bool
var tcp, udp int
//tproxy,tun 和 shadowsocks(udp) 都用到了 SelfListen
if is, tcp, udp = inServer.SelfListen(); is {
var tcpFunc func(netLayer.TCPRequestInfo)
var udpFunc func(netLayer.UDPRequestInfo)
if ce := utils.CanLogInfo("Listening"); ce != nil {
ce.Write(
zap.String("tag", inServer.GetTag()),
zap.String("protocol", proxy.GetFullName(inServer)),
zap.String("listen_addr", inServer.AddrStr()),
zap.String("defaultClient", proxy.GetFullName(defaultOutClient)),
zap.String("dial_addr", defaultOutClient.AddrStr()),
)
}
if tcp == 1 {
tcpFunc = func(tcpInfo netLayer.TCPRequestInfo) {
passToOutClient(incomingInserverConnState{
inTag: inServer.GetTag(),
useSniffing: inServer.Sniffing(),
wrappedConn: tcpInfo.Conn,
defaultClient: defaultOutClient,
routingEnv: env,
GlobalInfo: gi,
}, false, tcpInfo.Conn, nil, tcpInfo.Target)
}
}
if udp == 1 {
udpFunc = func(udpInfo netLayer.UDPRequestInfo) {
passToOutClient(incomingInserverConnState{
inTag: inServer.GetTag(),
useSniffing: inServer.Sniffing(),
defaultClient: defaultOutClient,
routingEnv: env,
GlobalInfo: gi,
}, false, nil, udpInfo.MsgConn, udpInfo.Target)
}
}
closer = inServer.(proxy.ListenerServer).StartListen(tcpFunc, udpFunc)
//可以直接return的值: 1,1 1,-1, -1,1 ;
//还需继续监听的值: 1,0 0,1
if tcp+udp != 1 {
return
} else {
extraCloser = closer
}
}
var handleHere bool
advs := inServer.GetAdvServer()
if advs != nil {
handleHere = advs.IsSuper() && advs.IsMux()
}
if handleHere {
//如果像quic一样自行处理传输层至tls层之间的部分则我们跳过 handleNewIncomeConnection 函数
// 拿到连接后直接调用 handshakeInserver_and_passToOutClient
superSer := advs.(advLayer.SuperMuxServer)
closer = superSer.StartListen(func(newConn net.Conn) {
iics := incomingInserverConnState{
wrappedConn: newConn,
inServer: inServer,
defaultClient: defaultOutClient,
routingEnv: env,
GlobalInfo: gi,
}
iics.genID()
handshakeInserver_and_passToOutClient(iics)
})
if closer == nil {
utils.Error("Failed in SuperMuxServer StartListen ")
return
}
if ce := utils.CanLogInfo("Listening Super AdvLayer"); ce != nil {
ce.Write(
zap.String("protocol", proxy.GetFullName(inServer)),
zap.String("addr", inServer.AddrStr()),
)
}
return
}
var err error
network := inServer.Network()
if network == netLayer.DualNetworkName && is {
//设置这里实际监听的传输层协议
if is != (tcp == 1 && udp == 1) {
if tcp == 1 {
network = "udp"
} else {
network = "tcp"
}
}
}
closer, err = netLayer.ListenAndAccept(
network,
inServer.AddrStr(),
inServer.GetSockopt(),
inServer.GetXver(),
func(conn net.Conn) {
handleNewIncomeConnection(inServer, defaultOutClient, conn, env, gi)
},
)
if err == nil {
if ce := utils.CanLogInfo("Listening"); ce != nil {
ce.Write(
zap.String("tag", inServer.GetTag()),
zap.String("protocol", proxy.GetFullName(inServer)),
zap.String("listen_addr", inServer.AddrStr()),
zap.String("defaultClient", proxy.GetFullName(defaultOutClient)),
zap.String("dial_addr", defaultOutClient.AddrStr()),
)
}
if extraCloser != nil {
closer = &utils.MultiCloser{Closers: []io.Closer{closer, extraCloser}}
}
} else {
if err != nil {
if ce := utils.CanLogErr("ListenSer failed"); ce != nil {
ce.Write(
zap.String("addr", inServer.AddrStr()),
zap.Error(err),
)
}
}
if extraCloser != nil {
closer = extraCloser
}
}
return
}
// handleNewIncomeConnection 会处理 网络层至高级层的数据,
// 然后将代理层的处理发往 handshakeInserver_and_passToOutClient 函数。
//
// 在 ListenSer 中被调用。
func handleNewIncomeConnection(inServer proxy.Server, defaultClientForThis proxy.Client, thisLocalConnectionInstance net.Conn, env *proxy.RoutingEnv, gi *GlobalInfo) {
iics := incomingInserverConnState{
baseLocalConn: thisLocalConnectionInstance,
inServer: inServer,
defaultClient: defaultClientForThis,
routingEnv: env,
isTlsLazyServerEnd: inServer.IsLazyTls() && CanLazyEncrypt(inServer),
GlobalInfo: gi,
}
iics.genID()
wrappedConn := thisLocalConnectionInstance
if ce := iics.CanLogInfo("New Accepted Conn"); ce != nil {
var addrstr string
if inServer.Network() == "unix" {
addrstr = inServer.AddrStr()
} else {
addrstr = wrappedConn.RemoteAddr().String()
}
ce.Write(
zap.String("from", addrstr),
zap.String("handler", proxy.GetVSI_url(inServer, "")),
)
iics.cachedRemoteAddr = addrstr
}
////////////////////////////// tls层 /////////////////////////////////////
//此时baseLocalConn里面 正常情况下, 服务端看到的是 客户端的golang的tls 拨号发出的 tls数据
// 客户端看到的是 socks5的数据 我们首先就是要看看socks5里的数据是不是tls而socks5自然 IsUseTLS 是false
// 如果是服务端的话,那就是 inServer.IsUseTLS == true, 此时,我们正常握手,然后我们需要判断的是它承载的数据
// 每次tls试图从 原始连接 读取内容时,都会附带把原始数据写入到 这个 Recorder中
if inServer.IsUseTLS() {
if iics.isTlsLazyServerEnd {
tlsRecorder := tlsLayer.NewRecorder()
iics.inServerTlsRawReadRecorder = tlsRecorder
tlsRecorder.StopRecord() //先不记录因为一开始是我们自己的tls握手包没有意义
teeConn := tlsLayer.NewTeeConn(wrappedConn, tlsRecorder)
wrappedConn = teeConn
}
tConf := inServer.GetBase().TlsConf
if tConf.RejectUnknownSni {
bs := utils.GetPacket()
n, e := wrappedConn.Read(bs)
if e != nil {
if ce := iics.CanLogWarn("tls preread failed"); ce != nil {
ce.Write(zap.Error(e))
}
utils.PutPacket(bs)
wrappedConn.Close()
return
}
firstpayload := bs[:n]
tlsSniff := new(tlsLayer.ComSniff)
tlsSniff.CommonDetect(firstpayload, true, true)
if tlsSniff.SniffedServerName == "" {
if ce := iics.CanLogWarn("tls rejectUnknownSni, client didn't provide sni"); ce != nil {
ce.Write()
}
utils.PutPacket(bs)
wrappedConn.Close()
return
}
//shadowTls自己没有防御功能完全靠我们包外过滤
if tConf.IsShadowTls() {
if tlsSniff.SniffedServerName != tConf.Host {
if ce := iics.CanLogWarn("tls rejectUnknownSni, client sni not match"); ce != nil {
ce.Write(zap.String("sni", tlsSniff.SniffedServerName), zap.String("shouldBe", tConf.Host))
}
utils.PutPacket(bs)
wrappedConn.Close()
return
}
}
wrappedConn = &netLayer.ReadWrapper{
Conn: wrappedConn,
OptionalReader: io.MultiReader(bytes.NewBuffer(firstpayload), wrappedConn), RemainFirstBufLen: n,
}
}
tlsConn, err := inServer.GetTLS_Server().Handshake(wrappedConn)
if err != nil {
if ce := iics.CanLogErr("Failed in TLS handshake"); ce != nil {
ce.Write(
zap.String("inServer", inServer.AddrStr()),
zap.Error(err),
)
}
//see #241
if !errors.Is(err, netLayer.ErrDoNotClose) {
wrappedConn.Close()
}
return
}
if iics.isTlsLazyServerEnd {
//此时已经握手完毕,可以记录了
iics.inServerTlsRawReadRecorder.StartRecord()
}
realtlsConn, ok := tlsConn.(tlsLayer.Conn)
if ok {
iics.inServerTlsConn = realtlsConn
}
wrappedConn = tlsConn
}
adv := inServer.AdvancedLayer()
advSer := inServer.GetAdvServer()
////////////////////////////// header 层 /////////////////////////////////////
if header := inServer.HasHeader(); header != nil {
//高级层 均可以自行处理header, 不需要额外http包装
if !(advSer != nil && advSer.CanHandleHeaders()) {
var ho *heapObj = iics.heapObj
if iics.heapObj == nil {
ho = &heapObj{}
iics.heapObj = ho
}
wrappedConn = &httpLayer.HeaderConn{
Conn: wrappedConn,
H: header,
IsServerEnd: true,
ReadOkCallback: func(b *bytes.Buffer) {
ho.headerPass = true
ho.wholeBuffer = b
},
}
}
}
////////////////////////////// 高级层 /////////////////////////////////////
if adv != "" {
//这里分 super, mux 和 default 三种情况,实际上对应了 quicgrpc 和ws
switch {
case advSer.IsSuper():
//Super 根本没调用 handleNewIncomeConnection (本函数),所以不在此处理
// 详见 ListenSer
case advSer.IsMux(): //grpc
muxSer := advSer.(advLayer.MuxServer)
muxSer.StartHandle(wrappedConn, func(newGConn net.Conn) {
iics.wrappedConn = newGConn
handshakeInserver_and_passToOutClient(iics)
}, func(fallbackMeta httpLayer.FallbackMeta) {
newiics := iics
newiics.fallbackRequestPath = fallbackMeta.Path
newiics.fallbackFirstBuffer = fallbackMeta.H1RequestBuf
newiics.wrappedConn = fallbackMeta.Conn
newiics.isFallbackH2 = fallbackMeta.IsH2
newiics.fallbackH2Request = fallbackMeta.H2Request
newiics.fallbackRW = fallbackMeta.H2RW
passToOutClient(newiics, true, nil, nil, netLayer.Addr{})
})
return
default: //ws
singleSer := advSer.(advLayer.SingleServer)
wsConn, err := singleSer.Handshake(wrappedConn)
if err != nil {
if errors.Is(err, httpLayer.ErrShouldFallback) {
meta := wsConn.(httpLayer.FallbackMeta)
iics.fallbackRequestPath = meta.Path
iics.fallbackFirstBuffer = meta.H1RequestBuf
iics.wrappedConn = meta.Conn
if meta.XFF != nil {
iics.cachedRemoteAddr = meta.XFF.String()
}
if ce := iics.CanLogDebug("Single AdvLayer Check failed, will fallback."); ce != nil {
ce.Write(
zap.String("handler", inServer.AddrStr()),
zap.String("advLayer", adv),
zap.String("Reason", meta.Reason),
zap.String("validPath", advSer.GetPath()),
zap.String("gotMethod", meta.Method),
zap.String("gotPath", meta.Path),
zap.String("from", iics.cachedRemoteAddr),
)
}
passToOutClient(iics, true, nil, nil, netLayer.Addr{})
return
} else {
if ce := iics.CanLogErr("InServer Single AdvLayer handshake failed"); ce != nil {
ce.Write(
zap.String("handler", inServer.AddrStr()),
zap.String("advLayer", adv),
zap.Error(err),
)
}
wrappedConn.Close()
return
}
} else {
wrappedConn = wsConn
}
} // switch adv
} //if adv !=""
iics.wrappedConn = wrappedConn
handshakeInserver_and_passToOutClient(iics)
}
// 被 handshakeInserver_and_passToOutClient 调用
func handshakeInserver(iics *incomingInserverConnState) (wlc net.Conn, udp_wlc netLayer.MsgConn, targetAddr netLayer.Addr, err error) {
inServer := iics.inServer
if inServer == nil {
err = utils.ErrInErr{ErrDesc: "Failed handshakeInserver, nil inServer"}
return
}
wlc, udp_wlc, targetAddr, err = inServer.Handshake(iics.wrappedConn)
if err != nil {
if ce := iics.CanLogWarn("Failed handshakeInserver"); ce != nil {
ce.Write(
zap.String("handler", iics.inServer.AddrStr()),
zap.String("client RemoteAddr", iics.getRealRAddr()),
zap.Error(err),
)
}
return
}
if udp_wlc != nil && inServer.Name() == "socks5" {
// socks5的 udp associate返回的是 clientFutureAddr, 而不是实际客户的第一个请求.
//所以我们要读一次才能进行下一步。
firstSocks5RequestData, firstSocks5RequestAddr, err2 := udp_wlc.ReadMsg()
if err2 != nil {
if ce := iics.CanLogWarn("Failed in socks5 read"); ce != nil {
ce.Write(
zap.String("handler", inServer.AddrStr()),
zap.Error(err2),
)
}
err = err2
return
}
iics.fallbackFirstBuffer = bytes.NewBuffer(firstSocks5RequestData)
targetAddr = firstSocks5RequestAddr
}
////////////////////////////// 内层mux阶段 /////////////////////////////////////
if muxInt, innerProxyName := inServer.HasInnerMux(); muxInt > 0 {
mm, ok := wlc.(proxy.MuxMarker)
if !ok {
return
}
if !mm.IsMux() {
return
}
innerSerConf := proxy.ListenConf{
CommonConf: proxy.CommonConf{
Protocol: innerProxyName,
},
}
innerSer, err2 := proxy.NewServer(&innerSerConf)
if err2 != nil {
if ce := iics.CanLogDebug("Failed mux inner proxy server creation"); ce != nil {
ce.Write(zap.Error(err))
}
err = err2
return
}
session := inServer.GetServerInnerMuxSession(mm)
if session == nil {
err = utils.ErrFailed
return
}
//内层mux要对每一个子连接单独进行 子代理协议握手 以及 outClient的拨号。
go func() {
for {
if ce := iics.CanLogDebug("Try inServer accept smux stream "); ce != nil {
ce.Write()
}
stream, err := session.AcceptStream()
if err != nil {
if ce := iics.CanLogWarn("Failed try mux inServer accept stream "); ce != nil {
ce.Write(zap.Error(err))
}
session.Close()
return
}
if ce := iics.CanLogDebug("inServer got inner mux stream"); ce != nil {
ce.Write(zap.String("innerProxyName", innerProxyName))
}
go func() {
wlc1, udp_wlc1, targetAddr1, err1 := innerSer.Handshake(stream)
if err1 != nil {
if ce := iics.CanLogDebug("Failed inServer mux inner proxy handshake"); ce != nil {
ce.Write(zap.Error(err1))
}
newiics := *iics
if !newiics.extractFirstBufFromErr(err1) {
return
}
passToOutClient(newiics, true, wlc1, udp_wlc1, targetAddr1)
} else {
if ce := iics.CanLogDebug("OK in inServer mux stream handshake "); ce != nil {
ce.Write(zap.String("targetAddr", targetAddr1.String()))
}
newiics := *iics
newiics.isInner = true
//试图将user赋值给simplesocks, 使其在内部的对user的分流依旧可用
if wlc != nil {
if u, ok := wlc.(utils.User); ok {
if wlc1 != nil {
if us, ok := wlc1.(utils.UserAssigner); ok {
us.SetUser(u)
}
} else if udp_wlc1 != nil {
if us, ok := udp_wlc1.(utils.UserAssigner); ok {
us.SetUser(u)
}
}
}
} else if udp_wlc != nil {
if u, ok := udp_wlc.(utils.User); ok {
if udp_wlc1 != nil {
if us, ok := udp_wlc1.(utils.UserAssigner); ok {
us.SetUser(u)
}
}
}
}
passToOutClient(newiics, false, wlc1, udp_wlc1, targetAddr1)
}
}()
}
}()
err = utils.ErrHandled
return
}
return
}
// 本函数 处理inServer的代理层数据并在试图处理 分流和回落后,调用 passToOutClient 将流量导向目标。
//
// iics 不使用指针, 因为iics不能公用因为 在多路复用时 iics.wrappedConn 是会变化的。
//
// 被 handleNewIncomeConnection 和 ListenSer 调用。
func handshakeInserver_and_passToOutClient(iics incomingInserverConnState) {
wlc, udp_wlc, targetAddr, err := handshakeInserver(&iics)
switch err {
case nil:
passToOutClient(iics, false, wlc, udp_wlc, targetAddr)
case utils.ErrHandled:
return
default: //回落
if !iics.extractFirstBufFromErr(err) {
return
}
if iics.isTlsLazyServerEnd {
iics.isTlsLazyServerEnd = false
/*
我们这里取巧认为只要进行了回落那么客户端就一定不是vs客户端进而不再应用lazy
关联 issue #158
开启了lazy后如果进行回落了那么是要先从iics.firstbuffer 来读的但是目前lazy函数根本不接收 iics.
而回落一般用于正常客户端的浏览网页一般的客户端就是浏览器是不用lazy的所以可以如此取巧
*/
}
passToOutClient(iics, true, nil, nil, netLayer.Addr{})
}
}
// 被 handshakeInserver_and_passToOutClient 和 handshakeInserver 的innerMux部分 以及 tproxy 调用。 iics.inServer可能为nil。
// 本函数 可能是 本文件中 最长的 函数。分别处理 回落firstpayloadsniffdns解析分流以及lazy最终转发到 某个 outClient。
//
// 最终会调用 dialClient_andRelay. 若isfallback为true传入的 wlc 和 udp_wlc 必须为niltargetAddr必须为空值。
func passToOutClient(iics incomingInserverConnState, isfallback bool, wlc net.Conn, udp_wlc netLayer.MsgConn, targetAddr netLayer.Addr) {
//这里的 iics.inServer 是可能为nil的所以一定要判断一下否则会空指针闪退
////////////////////////////// 回落阶段 /////////////////////////////////////
if isfallback {
fallbackTargetAddr, fbResult := iics.checkfallback()
if fbResult >= 0 {
targetAddr = fallbackTargetAddr
wlc = iics.wrappedConn
if iics.isFallbackH2 {
//h2 的fallback 非常特殊,要单独处理. 下面进行h2c拨号并向真实h2c服务器发起请求。
rq := iics.fallbackH2Request
if targetAddr.Network != "unix" {
rq.Host = targetAddr.Name
u, _ := url.Parse("https://" + targetAddr.String() + iics.fallbackRequestPath)
rq.URL = u
} else {
//rq.Host = "" //如果host设成了 unix的地址依然无法被nginx正常响应
rq.URL, _ = url.Parse("http://127.0.0.1" + iics.fallbackRequestPath)
}
var transport *http2.Transport
newUdsTransport := func(doAfterDial func(conn net.Conn)) *http2.Transport {
return &http2.Transport{
DialTLS: func(n, a string, cfg *tls.Config) (net.Conn, error) {
//实测如果是uds这里的a会为 :443, 丢失了uds监听文件信息
c, e := net.Dial("unix", targetAddr.String())
if doAfterDial != nil {
doAfterDial(c)
}
return c, e
},
AllowHTTP: true,
}
}
if fbResult == 0 {
transport = fallback_h2c_transport
if targetAddr.Network == "unix" {
transport = newUdsTransport(nil)
}
} else if fbResult > 0 {
var wlcRaddrStr string
if wlcraddr := wlc.RemoteAddr(); wlcraddr != nil {
wlcRaddrStr = wlcraddr.String()
}
fb_h2c_PROXYprotocolAddrMap_mutex.RLock()
transport = fb_h2c_PROXYprotocolAddrMap[wlcRaddrStr]
fb_h2c_PROXYprotocolAddrMap_mutex.RUnlock()
//因为一个客户端可能向我们服务器发起多个h2子连接共用同一个wlc,我们如果能
// 共用 这种情况的transport就可以节约到达实际服务器的tcp链接数量而且
// 无缝粘贴了两个h2连接.
// 因为 PROXYprotocol 头部对于每个wlc都是不同的, 所以才用到多个transport和map这种办法。
if transport == nil {
if targetAddr.Network == "unix" {
transport = newUdsTransport(func(conn net.Conn) {
netLayer.WritePROXYprotocol(fbResult, wlc, conn)
})
} else {
transport = &http2.Transport{
DialTLS: func(n, a string, cfg *tls.Config) (net.Conn, error) {
conn, e := net.Dial(n, a)
netLayer.WritePROXYprotocol(fbResult, wlc, conn)
return conn, e
},
AllowHTTP: true,
}
}
if wlcRaddrStr != "" {
fb_h2c_PROXYprotocolAddrMap_mutex.Lock()
fb_h2c_PROXYprotocolAddrMap[wlcRaddrStr] = transport
fb_h2c_PROXYprotocolAddrMap_mutex.Unlock()
}
}
}
rsp, err := transport.RoundTrip(rq)
defer wlc.Close()
if err != nil {
if ce := iics.CanLogErr("Failed in fallback h2 RoundTrip"); ce != nil {
ce.Write(
zap.Error(err),
zap.String("real addr", targetAddr.UrlString()),
)
}
return
}
for k, v := range rsp.Header {
iics.fallbackRW.Header().Set(k, v[0])
}
iics.fallbackRW.WriteHeader(rsp.StatusCode)
netLayer.TryCopy(wlc, rsp.Body, iics.id)
return
}
}
iics.fallbackXver = fbResult
} else {
iics.fallbackXver = -1
}
// inServer 握手失败,且 没有任何回落可用时的情况, 在这里我们直接退出。
if wlc == nil && udp_wlc == nil {
if ce := iics.CanLogWarn("Invalid request and no matched fallback, hung up"); ce != nil {
ce.Write(zap.String("client RemoteAddr", iics.cachedRemoteAddr))
}
if wc := iics.wrappedConn; wc != nil {
//应该返回一个http400错误这样更逼真一些
// 不过有一些高级层不属于 http1.1如grpc和 quic此时通过 如下方式处理
//本默认响应并不智能,如果你要智能、真实的响应,还是要自行配置好 回落。
if rejectConn, ok := wc.(netLayer.RejectConn); ok {
if rejectConn.RejectBehaviorDefined() {
rejectConn.Reject()
}
} else {
wc.SetWriteDeadline(time.Now().Add(time.Second))
wc.Write([]byte(httpLayer.GetNginx400Response()))
}
wc.Close()
}
return
}
////////////////////////////// 读取 First Payload 阶段 /////////////////////////////////////
//因为无论是 sniffing还是后面 proxy的握手抑或是 ws的 earlydata都要预先获得用户数据所以要提前读一下
//serverEnd 的 lazy 比较特殊,要自己读
if !iics.isTlsLazyServerEnd {
isudp := targetAddr.IsUDP()
if iics.fallbackFirstBuffer != nil {
iics.firstPayload = iics.fallbackFirstBuffer.Bytes()
iics.fallbackFirstBuffer = nil
if isudp {
iics.udpFirstTarget = targetAddr
}
} else {
if !isudp && wlc != nil {
bs := utils.GetMTU()
wlc.SetReadDeadline(time.Now().Add(proxy.FirstPayloadTimeout))
n, err := wlc.Read(bs)
wlc.SetReadDeadline(time.Time{})
if err != nil {
notTimeout := true
if errors.Is(err, os.ErrDeadlineExceeded) {
notTimeout = false
} else if toe, ok := err.(*net.OpError); ok { //在tun中使用gvisor时会遇到这种情况
if toe.Timeout() {
notTimeout = false
}
}
if notTimeout {
if n <= 0 {
if ce := iics.CanLogWarn("Failed in reading first payload, not because of timeout, will hung up"); ce != nil {
ce.Write(
zap.String("target", targetAddr.String()),
zap.Error(err),
)
}
wlc.Close()
return
}
} else {
if ce := iics.CanLogInfo("Read first payload but timeout, will relay without first payload."); ce != nil {
ce.Write(
zap.String("target", targetAddr.String()),
zap.Error(err),
)
}
}
}
if n > 0 {
iics.firstPayload = bs[:n]
}
} else if isudp && udp_wlc != nil {
udp_wlc.SetReadDeadline(time.Now().Add(proxy.FirstPayloadTimeout))
bs, targetAd, err := udp_wlc.ReadMsg()
udp_wlc.SetReadDeadline(time.Time{})
if err != nil {
if !errors.Is(err, os.ErrDeadlineExceeded) {
if ce := iics.CanLogErr("Failed in reading first udp payload, not because of timeout, will hung up"); ce != nil {
ce.Write(
zap.String("target", targetAddr.String()),
zap.Error(err),
)
}
udp_wlc.Close()
return
} else {
if ce := iics.CanLogWarn("Read first udp payload but timeout, will relay without first payload."); ce != nil {
ce.Write(
zap.String("target", targetAddr.String()),
zap.Error(err),
)
}
}
}
if len(bs) > 0 {
iics.firstPayload = bs
iics.udpFirstTarget = targetAd
}
} //if !isudp && wlc != nil {
}
} //if !iics.isTlsLazyServerEnd {
var tlsSniff *tlsLayer.ComSniff
inServer := iics.inServer
////////////////////////////// Sniff阶段 /////////////////////////////////////
//tls请求和纯http请求是可以嗅探 host的嗅探可以帮助我们使用 geosite 精准分流,所以是很有用的
if len(iics.firstPayload) > 0 {
inserverMarkedSniffing := false
if inServer == nil {
inserverMarkedSniffing = iics.useSniffing
} else {
inserverMarkedSniffing = inServer.Sniffing()
}
dialIslazy := iics.defaultClient.IsLazyTls()
shouldSniff := inserverMarkedSniffing || dialIslazy
if shouldSniff {
tlsSniff = new(tlsLayer.ComSniff)
if !iics.isTlsLazyServerEnd {
tlsSniff.Isclient = true
}
tlsSniff.CommonDetect(iics.firstPayload, true, inserverMarkedSniffing && !(iics.isTlsLazyServerEnd || dialIslazy))
if sni := tlsSniff.SniffedServerName; sni != "" {
if ce := iics.CanLogDebug("Sniffed Sni"); ce != nil {
ce.Write(zap.String("sni", sni))
}
targetAddr.Name = sni
}
}
}
////////////////////////////// DNS解析阶段 /////////////////////////////////////
//dns解析会试图解析域名并将ip放入 targetAddr中
// 因为在direct时netLayer.Addr 拨号时会优先选用ip拨号而且我们下面的分流阶段 如果使用ip的话
// 可以利用geoip文件, 可以做到国别分流.
if iics.routingEnv != nil && iics.routingEnv.DnsMachine != nil && (targetAddr.Name != "" && len(targetAddr.IP) == 0) && targetAddr.Network != "unix" {
if ce := iics.CanLogDebug("Dns querying"); ce != nil {
ce.Write(zap.String("domain", targetAddr.Name))
}
ip := iics.routingEnv.DnsMachine.Query(targetAddr.Name)
if ip != nil {
targetAddr.IP = ip
if ce2 := iics.CanLogDebug("Dns result"); ce2 != nil {
ce2.Write(zap.String("domain", targetAddr.Name), zap.String("ip", ip.String()))
}
}
}
//此时 targetAddr已经完全确定
////////////////////////////// 分流阶段 /////////////////////////////////////
var client proxy.Client = iics.defaultClient
routed := false
//尝试分流, 获取到真正要发向 的 outClient
if re := iics.routingEnv; re != nil && re.RoutePolicy != nil && !(inServer != nil && inServer.CantRoute()) {
desc := &netLayer.TargetDescription{
Addr: targetAddr,
}
if inServer != nil {
desc.InTag = inServer.GetTag()
} else {
desc.InTag = iics.inTag
}
if uc, ok := wlc.(utils.User); ok {
desc.UserIdentityStr = uc.IdentityStr()
} else if uc, ok := udp_wlc.(utils.User); ok {
desc.UserIdentityStr = uc.IdentityStr()
}
if ce := iics.CanLogDebug("Try routing"); ce != nil {
ce.Write(zap.Any("source", desc))
}
outtag := re.RoutePolicy.CalcuOutTag(desc)
if len(re.ClientsTagMap) > 0 {
if tagC := re.GetClient(outtag); tagC != nil {
client = tagC
routed = true
if ce := iics.CanLogInfo("Route"); ce != nil {
ce.Write(
zap.String("to outtag", outtag),
zap.String("with addr", client.AddrStr()),
zap.String("and protocol", proxy.GetFullName(client)),
zap.Any("for source", desc),
)
}
}
}
if !routed && outtag == proxy.DirectName {
client = DirectClient
iics.routedToDirect = true
routed = true
if ce := iics.CanLogInfo("Route to direct"); ce != nil {
ce.Write(
zap.String("target", targetAddr.UrlString()),
)
}
}
}
if !routed {
if ce := iics.CanLogDebug("Default Route"); ce != nil {
ce.Write(
zap.Any("source", targetAddr.String()),
zap.String("client", proxy.GetFullName(client)),
zap.String("addr", client.AddrStr()),
)
}
}
////////////////////////////// 特殊处理阶段 /////////////////////////////////////
// 下面几段用于处理 tls lazy
var isTlsLazy_clientEnd bool
if targetAddr.IsUDP() {
//udp数据是无法splice的因为不是入口处是真udp就是出口处是真udp; 同样暂不考虑级连情况.
if iics.isTlsLazyServerEnd {
iics.isTlsLazyServerEnd = false
//此时 inServer的tls还被包了一个Recorder所以我们赶紧关闭记录, 避免产生额外开销
iics.inServerTlsRawReadRecorder.StopRecord()
}
} else {
isTlsLazy_clientEnd = client.IsLazyTls() && CanLazyEncrypt(client) //比如dial是 tls+vless 这种
}
// 我们在客户端 lazy_encrypt 探测时读取socks5 传来的信息,因为这个 就是要发送到 outClient 的信息所以就不需要等包上vless、tls后再判断了, 直接解包 socks5 对 tls 进行判断
//
// 而在服务端探测时,因为 客户端传来的连接 包了 tls所以要在tls解包后, vless 解包后,再进行判断;
// 所以总之都是要在 inServer 判断 wlc; 总之,含义就是,去检索“用户承载数据”的来源
if isTlsLazy_clientEnd || iics.isTlsLazyServerEnd {
wlc = tlsLayer.NewSniffConn(iics.baseLocalConn, wlc, isTlsLazy_clientEnd, tls_lazy_secure, tlsSniff)
}
//这一段代码是去判断是否要在转发结束后自动关闭连接, 主要是socks5 和lazy 的特殊情况
if targetAddr.IsUDP() {
if inServer != nil {
switch inServer.Name() {
case "socks5", "socks5http":
// UDP Associate
// 因为socks5的 UDP Associate 办法是较为特殊的不使用现有tcp而是新建立udp所以此时该tcp连接已经没用了
// 但是根据socks5标准这个tcp链接同样是 keep alive的否则客户端就会认为服务端挂掉了.
// 另外,此时 targetAddr.IsUDP 只是用于告知此链接是udp Associate并不包含实际地址信息
default:
iics.shouldCloseInSerBaseConnWhenFinish = true
}
}
} else {
//lazy_encrypt情况比较特殊基础连接何时被关闭会在tlslazy相关代码中处理。
// 如果不是lazy的情况的话转发结束后要自动关闭
if !iics.isTlsLazyServerEnd {
//实测 grpc.Conn 被调用了Close 也不会实际关闭连接而是会卡住阻塞直到底层tcp连接被关闭后才会返回
// 但是我们还是 直接避免这种情况
if inServer != nil && !(inServer.GetAdvServer() != nil && inServer.GetAdvServer().IsMux()) {
iics.shouldCloseInSerBaseConnWhenFinish = true
}
}
}
////////////////////////////// 拨号阶段 /////////////////////////////////////
dialClient_andRelay(iics, targetAddr, client, isTlsLazy_clientEnd, wlc, udp_wlc)
}
// dialClient 对实际client进行拨号处理传输层, tls层, 高级层等所有层级后,进行代理层握手。
// 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, udp_wlc netLayer.MsgConn,
isTlsLazy_clientEnd bool) (
//return values:
wrc io.ReadWriteCloser,
udp_wrc netLayer.MsgConn,
realTargetAddr netLayer.Addr,
clientEndRemoteClientTlsRawReadRecorder *tlsLayer.Recorder,
result int) {
if client.Name() == proxy.RejectName {
if wlc != nil {
client.Handshake(wlc, nil, netLayer.Addr{})
} else if udp_wlc != nil {
client.Handshake(netLayer.MsgConnNetAdapter{MsgConn: udp_wlc}, nil, netLayer.Addr{})
}
result = -10
return
}
isudp := targetAddr.IsUDP()
hasInnerMux := false
var innerProxyName string
{
var muxInt int
if muxInt, innerProxyName = client.HasInnerMux(); muxInt == 2 {
hasInnerMux = true
//先过滤掉 innermux 通道已经建立的情况, 此时我们不必再次外部拨号,而是直接进行内层拨号.
client.Lock()
if client.InnerMuxEstablished() {
client.Unlock()
if ce := iics.CanLogInfo("Mux Request"); ce != nil {
ce.Write(
zap.String("From", iics.cachedRemoteAddr),
zap.String("Target", targetAddr.UrlString()),
zap.String("through", proxy.GetVSI_url(client, targetAddr.Network)),
)
}
wrc1, realudp_wrc, result1 := dialInnerProxy(iics, client, wlc, nil, innerProxyName, targetAddr, isudp)
if result1 == 0 {
if wrc1 != nil {
wrc = wrc1
}
if realudp_wrc != nil {
udp_wrc = realudp_wrc
}
result = result1
return
} else {
if ce := iics.CanLogDebug("Failed in client inner mux dialing innerProxy , will redial"); ce != nil {
ce.Write()
}
}
} else {
//在实测时 发现,可能出现并发问题,比如在加载图多的网页时,很容易碰到
//此时如果是两个连接同时 发出,而且 尚未 建立 innerMux
// 则如果不加锁的话 ,两个连接 会同时 获取到 client.InnerMuxEstablished() 为 false
// 这会导致同时试图拨号 innerMux而这是错误的
//我们只允许有一个 innerMux 连接存在如果有多个的话那么最新拨号的innerMux 会覆盖以前的拨号,
// 导致 以前的 innerMux 成为了 悬垂连接,而且会导致 相关联的请求卡住。
// 所以使用defer, 来确保同一时间只有一个smux的拨号
defer client.Unlock()
}
}
}
var err error
//先确认拨号地址
//direct的话自己是没有目的地址的直接使用 请求的地址
// 而其它代理的话, realTargetAddr会被设成实际配置的代理的地址
realTargetAddr = targetAddr
if ce := iics.CanLogInfo("Request"); ce != nil {
if iics.fallbackXver >= 0 {
if iics.fallbackXver > 0 {
ce.Write(
zap.String("Fallback from", iics.cachedRemoteAddr),
zap.String("Target", targetAddr.UrlString()),
zap.String("through", proxy.GetVSI_url(client, targetAddr.Network)),
zap.Int("with xver", iics.fallbackXver),
)
} else {
ce.Write(
zap.String("Fallback from", iics.cachedRemoteAddr),
zap.String("Target", targetAddr.UrlString()),
zap.String("through", proxy.GetVSI_url(client, targetAddr.Network)),
)
}
} else {
ce.Write(
zap.String("From", iics.cachedRemoteAddr),
zap.String("Target", targetAddr.UrlString()),
zap.String("through", proxy.GetVSI_url(client, targetAddr.Network)),
)
}
}
if client.AddrStr() != "" {
realTargetAddr, err = netLayer.NewAddr(client.AddrStr())
if err != nil {
if ce := iics.CanLogErr("Err at dial client convert addr "); ce != nil {
ce.Write(zap.Error(err))
}
result = -1
return
}
realTargetAddr.Network = client.Network()
}
var clientConn net.Conn //拨号所得到的net.Conn, 在下面代码中 会一层层进行包装
var dialedCommonConn any
switch realTargetAddr.Network {
case netLayer.DualNetworkName:
realTargetAddr.Network = targetAddr.Network
}
dialhere := !(client.Name() == proxy.DirectName)
/*
direct的udp是自己拨号的因为它用到了udp的fullcone
不是的话,也要分情况:
如果是单路的, 则我们在此dial, 如果是多路复用, 则不行, 因为要复用同一个连接
Instead, 我们要试图 取出已经拨号好了的 连接
*/
adv := client.AdvancedLayer()
advClient := client.GetAdvClient()
var muxC advLayer.MuxClient
if dialhere {
if adv != "" && advClient.IsMux() {
muxC = advClient.(advLayer.MuxClient)
if advClient.IsSuper() {
dialedCommonConn, err = muxC.GetCommonConn(nil)
if dialedCommonConn != nil && err == nil {
goto advLayerHandshakeStep
} else {
if ce := iics.CanLogErr("Failed in Super AdvLayer GetCommonConn"); ce != nil {
ce.Write(
zap.Error(err),
)
}
result = -1
return
}
} else {
dialedCommonConn, err = muxC.GetCommonConn(nil)
if dialedCommonConn != nil && err == nil {
goto advLayerHandshakeStep
}
}
}
var na net.Addr
switch realTargetAddr.Network {
case "tcp":
na = client.LocalTCPAddr()
case "udp":
na = client.LocalUDPAddr()
}
clientConn, err = realTargetAddr.Dial(client.GetSockopt(), na)
if err != nil {
if err == netLayer.ErrMachineCantConnectToIpv6 {
//如果一开始就知道机器没有ipv6地址那么该错误就不是error等级而是warning等级
if ce := iics.CanLogWarn("Machine HasNo ipv6 but got ipv6 request"); ce != nil {
ce.Write(
zap.String("target", realTargetAddr.String()),
)
}
} else {
//虽然拨号失败,但是不能认为我们一定有错误, 因为很可能申请的ip本身就是不可达的, 所以不是error等级而是warn等级
if ce := iics.CanLogWarn("Failed dialing"); ce != nil {
ce.Write(
zap.String("target", realTargetAddr.String()),
zap.Error(err),
)
}
}
result = -1
return
}
} else {
goto shakeStep
}
if xver := iics.fallbackXver; xver > 0 && xver < 3 {
if clientConn == nil {
clientConn, err = realTargetAddr.Dial(nil, nil)
if err != nil {
result = -1
return
}
}
_, err = netLayer.WritePROXYprotocol(xver, wlc, clientConn)
if err != nil {
if ce := iics.CanLogErr("Failed in WritePROXYprotocol"); ce != nil {
ce.Write(zap.String("target", targetAddr.String()), zap.Error(err))
}
result = -1
return
}
}
////////////////////////////// tls握手阶段 /////////////////////////////////////
if client.IsUseTLS() {
if isTlsLazy_clientEnd {
if tls_lazy_secure && wlc != nil {
// 如果使用secure办法则我们每次不能先拨号而是要detect用户的首包后再拨号
// 这种情况只需要客户端操作, 此时我们wrc直接传入原始的 刚拨号好的 tcp连接即 clientConn
// 而且为了避免黑客攻击或探测我们要使用uuid作为特殊指令此时需要 UserServer和 UserClient
if uc := client.(proxy.UserClient); uc != nil {
tryTlsLazyRawRelay(iics.id, true, uc, nil, targetAddr, clientConn, wlc, nil, true, nil)
}
result = 1
return
} else {
clientEndRemoteClientTlsRawReadRecorder = tlsLayer.NewRecorder()
teeConn := tlsLayer.NewTeeConn(clientConn, clientEndRemoteClientTlsRawReadRecorder)
clientConn = teeConn
}
}
tlsConn, err2 := client.GetTLS_Client().Handshake(clientConn)
if err2 != nil {
if ce := iics.CanLogErr("Failed in handshake outClient tls"); ce != nil {
ce.Write(zap.String("target", targetAddr.String()), zap.Error(err2))
}
result = -1
return
}
clientConn = tlsConn
}
////////////////////////////// header 层 /////////////////////////////////////
if header := client.HasHeader(); header != nil && !(advClient != nil && advClient.CanHandleHeaders()) {
clientConn = &httpLayer.HeaderConn{
Conn: clientConn,
H: header,
}
}
////////////////////////////// 高级层握手阶段 /////////////////////////////////////
advLayerHandshakeStep:
if adv != "" {
switch {
//quic
case advClient.IsSuper():
clientConn, err = muxC.DialSubConn(dialedCommonConn)
if err != nil {
if ce := iics.CanLogErr("Failed in DialSubConn"); ce != nil {
ce.Write(
zap.Error(err),
)
}
result = -1
return
}
//grpc
case advClient.IsMux():
if dialedCommonConn == nil {
dialedCommonConn, err = muxC.GetCommonConn(clientConn)
if dialedCommonConn == nil {
if ce := iics.CanLogErr("Failed in GetCommonConn"); ce != nil {
ce.Write(
zap.Error(err),
)
}
result = -1
return
}
}
clientConn, err = muxC.DialSubConn(dialedCommonConn)
if err != nil {
if ce := iics.CanLogErr("Failed in grpc.DialNewSubConn"); ce != nil {
ce.Write(zap.Error(err))
//如果底层tcp连接被关闭了的话错误会是
// rpc error: code = Unavailable desc = connection error: desc = "transport: failed to write client preface: tls: use of closed connection"
}
if iics.baseLocalConn != nil {
iics.baseLocalConn.Close()
}
result = -1
return
}
//ws
default:
var edlen int
if !hasInnerMux && advClient.IsEarly() && wlc != nil {
edlen = len(iics.firstPayload)
}
var wc net.Conn
wcs := advClient.(advLayer.SingleClient)
wc, err = wcs.Handshake(clientConn, edlen)
if err != nil {
if ce := iics.CanLogErr("Failed in handshake Single AdvLayer"); ce != nil {
ce.Write(
zap.String("advLayer", adv),
zap.String("target", targetAddr.String()),
zap.Error(err),
)
}
result = -1
return
}
clientConn = wc
}
}
shakeStep:
////////////////////////////// 代理层 握手阶段 /////////////////////////////////////
if !isudp || hasInnerMux {
//udp但是有innermux时 依然用handshake, 而不是 EstablishUDPChannel
var ed []byte
if !hasInnerMux {
//如果有内层mux则 firstPayload 不在本 dialClient 函数写入, 要在 dialInnerProxy 里 写入, 因为还有 innerMux层 和 inner Proxy 层 尚未拨号
if l := len(iics.firstPayload); l > 0 {
ed = iics.firstPayload
if ce := iics.CanLogDebug("handshake client with first payload"); ce != nil {
ce.Write(
zap.Int("len", l),
)
}
} else {
iics.firstPayload = nil
}
}
wrc, err = client.Handshake(clientConn, ed, targetAddr)
if err != nil {
if ce := iics.CanLogErr("Failed in Handshake client"); ce != nil {
ce.Write(
zap.String("target", targetAddr.String()),
zap.Error(err),
)
}
result = -1
return
}
} else {
theAddr := targetAddr
if len(iics.firstPayload) > 0 {
theAddr = iics.udpFirstTarget
}
udp_wrc, err = client.EstablishUDPChannel(clientConn, iics.firstPayload, theAddr)
if err != nil {
if ce := iics.CanLogErr("Failed in EstablishUDPChannel"); ce != nil {
ce.Write(
zap.String("target", targetAddr.String()),
zap.Error(err),
)
}
result = -1
return
}
}
////////////////////////////// 建立内层 mux 阶段 /////////////////////////////////////
if hasInnerMux {
//我们目前的实现中mux统一使用smux v1, 即 smux.DefaultConfig返回的值。这可以兼容trojan-go的实现。
wrc, udp_wrc, result = dialInnerProxy(iics, client, wlc, wrc, innerProxyName, targetAddr, isudp)
}
return
} //dialClient
// 在 dialClient 中调用。 如果调用不成功则result < 0. 若成功, 则 result == 0.
func dialInnerProxy(iics incomingInserverConnState, client proxy.Client, wlc net.Conn, wrc io.ReadWriteCloser, innerProxyName string, targetAddr netLayer.Addr, isudp bool) (realwrc io.ReadWriteCloser, realudp_wrc netLayer.MsgConn, result int) {
smuxSession := client.GetClientInnerMuxSession(wrc)
if smuxSession == nil {
result = -1
if ce := iics.CanLogErr("Failed dialInnerProxy, smuxSession == nil"); ce != nil {
ce.Write()
}
return
}
stream, err := smuxSession.OpenStream()
if err != nil {
client.CloseInnerMuxSession() //发现就算 OpenStream 失败, session也不会自动被关闭, 需要我们手动关一下。
if ce := iics.CanLogWarn("Failed dialInnerProxy"); ce != nil {
ce.Write(zap.Error(err))
}
result = -1
return
}
muxDialConf := proxy.DialConf{
CommonConf: proxy.CommonConf{
Protocol: innerProxyName,
},
}
muxClient, err := proxy.NewClient(&muxDialConf)
if err != nil {
if ce := iics.CanLogDebug("mux inner proxy client creation failed"); ce != nil {
ce.Write(zap.Error(err))
}
result = -1
return
}
if isudp {
theAddr := targetAddr
if len(iics.firstPayload) > 0 {
theAddr = iics.udpFirstTarget
}
realudp_wrc, err = muxClient.EstablishUDPChannel(stream, iics.firstPayload, theAddr)
if err != nil {
if ce := iics.CanLogDebug("mux inner proxy client handshake failed"); ce != nil {
ce.Write(zap.Error(err))
}
result = -1
return
}
} else {
realwrc, err = muxClient.Handshake(stream, iics.firstPayload, targetAddr)
if err != nil {
if ce := iics.CanLogDebug("mux inner proxy client handshake failed"); ce != nil {
ce.Write(zap.Error(err))
}
result = -1
return
}
}
return
} //dialInnerProxy
// dialClient_andRelay 进行实际转发(Copy)。被 passToOutClient 调用.
// targetAddr为用户所请求的地址。
// client为真实要拨号的client可能会与iics里的defaultClient不同。以client为准。
// wlc为调用者所提供的 此请求的 来源 链接
func dialClient_andRelay(iics incomingInserverConnState, targetAddr netLayer.Addr, client proxy.Client, isTlsLazy_clientEnd bool, wlc net.Conn, udp_wlc netLayer.MsgConn) {
//在内层mux时, 不能因为单个传输完毕就关闭整个连接
if innerMuxResult, _ := client.HasInnerMux(); innerMuxResult == 0 {
if iics.shouldCloseInSerBaseConnWhenFinish && !iics.isInner {
if iics.baseLocalConn != nil {
defer iics.baseLocalConn.Close()
}
}
if wlc != nil {
defer wlc.Close()
}
}
wrc, udp_wrc, realTargetAddr, clientEndRemoteClientTlsRawReadRecorder, result := dialClient(iics, targetAddr, client, wlc, udp_wlc, isTlsLazy_clientEnd)
if result != 0 {
return
}
////////////////////////////// 实际转发阶段 /////////////////////////////////////
if !targetAddr.IsUDP() {
if !iics.routedToDirect {
// 我们加了回落之后,就无法确定 “未使用tls的outClient 一定是在服务端” 了
if isTlsLazy_clientEnd {
if client.IsUseTLS() {
//必须是 UserClient
if userClient := client.(proxy.UserClient); userClient != nil {
tryTlsLazyRawRelay(iics.id, false, userClient, nil, netLayer.Addr{}, wrc, wlc, iics.baseLocalConn, true, clientEndRemoteClientTlsRawReadRecorder)
return
}
}
} else if iics.isTlsLazyServerEnd {
// 最新代码已经确认使用uuid 作为 “特殊指令”所以要求Server必须是一个 proxy.UserServer
// 否则将无法开启splice功能。这是为了防止0-rtt 探测;
if userServer, ok := iics.inServer.(proxy.UserServer); ok {
tryTlsLazyRawRelay(iics.id, false, nil, userServer, netLayer.Addr{}, wrc, wlc, iics.baseLocalConn, false, iics.inServerTlsRawReadRecorder)
return
}
}
}
if gi := iics.GlobalInfo; gi != nil {
atomic.AddInt32(&gi.ActiveConnectionCount, 1)
netLayer.Relay(&realTargetAddr, wrc, wlc, iics.id, &gi.AllDownloadBytesSinceStart, &gi.AllUploadBytesSinceStart)
atomic.AddInt32(&gi.ActiveConnectionCount, -1)
} else {
netLayer.Relay(&realTargetAddr, wrc, wlc, iics.id, nil, nil)
}
return
} else {
if ffb := iics.fallbackFirstBuffer; ffb != nil {
udp_wrc.WriteMsg(ffb.Bytes(), targetAddr)
}
var ac *int32
var adc *uint64
var auc *uint64
if iics.GlobalInfo != nil {
ac = &iics.GlobalInfo.ActiveConnectionCount
adc = &iics.GlobalInfo.AllDownloadBytesSinceStart
auc = &iics.GlobalInfo.AllUploadBytesSinceStart
atomic.AddInt32(ac, 1)
}
if client.IsUDP_MultiChannel() {
if ce := iics.CanLogDebug("Relaying UDP with MultiChannel"); ce != nil {
ce.Write()
}
netLayer.RelayUDP_separate(udp_wrc, udp_wlc, &targetAddr, adc, auc, func(raddr netLayer.Addr) netLayer.MsgConn {
if ce := iics.CanLogDebug("Relaying UDP with MultiChannel,dialfunc called"); ce != nil {
ce.Write()
}
_, udp_wrc, _, _, result := dialClient(iics, raddr, client, nil, udp_wlc, false)
if ce := iics.CanLogDebug("Relaying UDP with MultiChannel, dialfunc call returned"); ce != nil {
ce.Write(zap.Int("result", result))
}
if result == 0 {
return udp_wrc
}
return nil
})
} else {
netLayer.RelayUDP(udp_wrc, udp_wlc, adc, auc)
}
if ac != nil {
atomic.AddInt32(ac, -1)
}
return
}
}