mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-05 08:46:59 +08:00

对一般用户而言,还是需要使用Info等级 来了解一下 一般的 日志情况,等到使用熟练之后,且确认运行没有错误后, 可以自行调为 warning 来提升性能 发现 bubble包 还自己引入了 命令行参数,这十分不可取,所以我们还是直接使用其代码。 将其它包中 的 命令行参数 统一 移动 到 cmd/verysimple 中;tls lazy 特性因为还在 调试阶段,所以 命令行参数 仍然放到 v2ray_simple 包中。
1465 lines
40 KiB
Go
1465 lines
40 KiB
Go
package v2ray_simple
|
||
|
||
import (
|
||
"bytes"
|
||
"errors"
|
||
"flag"
|
||
"io"
|
||
"log"
|
||
"net"
|
||
"os"
|
||
"strings"
|
||
"sync/atomic"
|
||
"time"
|
||
|
||
"go.uber.org/zap"
|
||
|
||
"github.com/e1732a364fed/v2ray_simple/advLayer/grpc"
|
||
"github.com/e1732a364fed/v2ray_simple/advLayer/quic"
|
||
"github.com/e1732a364fed/v2ray_simple/advLayer/ws"
|
||
"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"
|
||
|
||
_ "github.com/e1732a364fed/v2ray_simple/proxy/dokodemo"
|
||
_ "github.com/e1732a364fed/v2ray_simple/proxy/http"
|
||
_ "github.com/e1732a364fed/v2ray_simple/proxy/simplesocks"
|
||
_ "github.com/e1732a364fed/v2ray_simple/proxy/socks5"
|
||
_ "github.com/e1732a364fed/v2ray_simple/proxy/trojan"
|
||
_ "github.com/e1732a364fed/v2ray_simple/proxy/vless"
|
||
)
|
||
|
||
//统计数据
|
||
var (
|
||
ActiveConnectionCount int32
|
||
AllDownloadBytesSinceStart uint64
|
||
AllUploadBytesSinceStart uint64
|
||
)
|
||
|
||
var (
|
||
DirectClient, _, _ = proxy.ClientFromURL("direct://")
|
||
|
||
ServersTagMap = make(map[string]proxy.Server)
|
||
ClientsTagMap = make(map[string]proxy.Client)
|
||
|
||
Tls_lazy_encrypt bool
|
||
Tls_lazy_secure bool
|
||
|
||
//有时需要测试到单一网站的流量,此时为了避免其它干扰,可声明 一下 该域名,然后程序里会进行过滤
|
||
//uniqueTestDomain string
|
||
)
|
||
|
||
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.")
|
||
|
||
//flag.StringVar(&uniqueTestDomain, "td", "", "test a single domain, like www.domain.com. Only valid when loglevel=0")
|
||
|
||
}
|
||
|
||
//非阻塞. 可以 直接使用 ListenSer 函数 来手动开启新的转发流程。
|
||
// 若 env 为 nil, 则不会 进行路由或回落
|
||
func ListenSer(inServer proxy.Server, defaultOutClientForThis proxy.Client, env *proxy.RoutingEnv) (thisListener net.Listener) {
|
||
|
||
var err error
|
||
|
||
//quic
|
||
if inServer.IsHandleInitialLayers() {
|
||
//如果像quic一样自行处理传输层至tls层之间的部分,则我们跳过 handleNewIncomeConnection 函数
|
||
// 拿到连接后直接调用 handshakeInserver_and_passToOutClient
|
||
|
||
handleFunc := inServer.HandleInitialLayersFunc()
|
||
if handleFunc == nil {
|
||
panic("inServer.IsHandleInitialLayers but inServer.HandleInitialLayersFunc() returns nil")
|
||
}
|
||
|
||
//baseConn可以为nil,quic就是如此
|
||
newConnChan, baseConn := handleFunc()
|
||
if newConnChan == nil {
|
||
utils.Error("StarthandleInitialLayers can't extablish baseConn")
|
||
return
|
||
}
|
||
|
||
go func() {
|
||
for {
|
||
newConn, ok := <-newConnChan
|
||
if !ok {
|
||
utils.Error("read from SuperProxy not ok")
|
||
|
||
quic.CloseConn(baseConn)
|
||
|
||
return
|
||
}
|
||
|
||
iics := incomingInserverConnState{
|
||
wrappedConn: newConn,
|
||
//baseLocalConn: baseConn, //quic是没有baseLocalConn的,因为基于udp
|
||
// 或者说虽然有baseConn,但是并不与子连接一一对应.那个conn更类似于一个listener
|
||
inServer: inServer,
|
||
defaultClient: defaultOutClientForThis,
|
||
}
|
||
|
||
go handshakeInserver_and_passToOutClient(iics)
|
||
}
|
||
|
||
}()
|
||
|
||
if ce := utils.CanLogInfo("Listening"); ce != nil {
|
||
|
||
ce.Write(
|
||
zap.String("protocol", proxy.GetFullName(inServer)),
|
||
zap.String("addr", inServer.AddrStr()),
|
||
)
|
||
}
|
||
return
|
||
}
|
||
|
||
handleFunc := func(conn net.Conn) {
|
||
handleNewIncomeConnection(inServer, defaultOutClientForThis, conn, env)
|
||
}
|
||
|
||
network := inServer.Network()
|
||
thisListener, err = netLayer.ListenAndAccept(network, inServer.AddrStr(), inServer.GetSockopt(), handleFunc)
|
||
|
||
if err == nil {
|
||
if ce := utils.CanLogInfo("Listening"); ce != nil {
|
||
|
||
ce.Write(
|
||
zap.String("protocol", proxy.GetFullName(inServer)),
|
||
zap.String("addr", inServer.AddrStr()),
|
||
)
|
||
}
|
||
|
||
} else {
|
||
if err != nil {
|
||
utils.ZapLogger.Error(
|
||
"can not listen inServer on", zap.String("addr", inServer.AddrStr()), zap.Error(err))
|
||
|
||
}
|
||
}
|
||
return
|
||
}
|
||
|
||
type incomingInserverConnState struct {
|
||
|
||
// 在多路复用的情况下, 可能产生多个 IncomingInserverConnState,
|
||
// 共用一个 baseLocalConn, 但是 wrappedConn 各不相同。
|
||
|
||
//这里说的多路复用指的是grpc/quic 这种包在代理层外部的; vless内嵌 mux.cool 或者trojan内嵌smux+simplesocks 则不属于这种情况, 它们属于 innerMux
|
||
|
||
// 要区分 多路复用的包装 是在 vless等代理的握手验证 的外部 还是 内部
|
||
|
||
baseLocalConn net.Conn // baseLocalConn 是来自客户端的原始网络层链接
|
||
wrappedConn net.Conn // wrappedConn 是层层握手后,代理层握手前 包装的链接,一般为tls层或者高级层;
|
||
inServer proxy.Server
|
||
defaultClient proxy.Client
|
||
|
||
cachedRemoteAddr string
|
||
theRequestPath string
|
||
|
||
inServerTlsConn *tlsLayer.Conn
|
||
inServerTlsRawReadRecorder *tlsLayer.Recorder
|
||
|
||
theFallbackFirstBuffer *bytes.Buffer
|
||
|
||
isTlsLazyServerEnd bool
|
||
|
||
shouldCloseInSerBaseConnWhenFinish bool
|
||
|
||
routedToDirect bool
|
||
|
||
RoutingEnv *proxy.RoutingEnv //used in passToOutClient
|
||
}
|
||
|
||
// handleNewIncomeConnection 会处理 网络层至高级层的数据,
|
||
// 然后将代理层的处理发往 handshakeInserver_and_passToOutClient 函数。
|
||
//
|
||
// 在 listenSer 中被调用。
|
||
func handleNewIncomeConnection(inServer proxy.Server, defaultClientForThis proxy.Client, thisLocalConnectionInstance net.Conn, env *proxy.RoutingEnv) {
|
||
|
||
iics := incomingInserverConnState{
|
||
baseLocalConn: thisLocalConnectionInstance,
|
||
inServer: inServer,
|
||
defaultClient: defaultClientForThis,
|
||
RoutingEnv: env,
|
||
}
|
||
|
||
iics.isTlsLazyServerEnd = Tls_lazy_encrypt && canLazyEncryptServer(inServer)
|
||
|
||
wrappedConn := thisLocalConnectionInstance
|
||
|
||
if ce := utils.CanLogInfo("New Accepted Conn"); ce != nil {
|
||
|
||
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 {
|
||
iics.inServerTlsRawReadRecorder = tlsLayer.NewRecorder()
|
||
|
||
iics.inServerTlsRawReadRecorder.StopRecord() //先不记录,因为一开始是我们自己的tls握手包,没有意义
|
||
teeConn := tlsLayer.NewTeeConn(wrappedConn, iics.inServerTlsRawReadRecorder)
|
||
|
||
wrappedConn = teeConn
|
||
}
|
||
|
||
tlsConn, err := inServer.GetTLS_Server().Handshake(wrappedConn)
|
||
if err != nil {
|
||
|
||
if ce := utils.CanLogErr("tls handshake failed"); ce != nil {
|
||
ce.Write(
|
||
zap.String("inServer", inServer.AddrStr()),
|
||
zap.Error(err),
|
||
)
|
||
|
||
}
|
||
wrappedConn.Close()
|
||
return
|
||
}
|
||
|
||
if iics.isTlsLazyServerEnd {
|
||
//此时已经握手完毕,可以记录了
|
||
iics.inServerTlsRawReadRecorder.StartRecord()
|
||
}
|
||
|
||
iics.inServerTlsConn = tlsConn
|
||
wrappedConn = tlsConn
|
||
|
||
}
|
||
|
||
adv := inServer.AdvancedLayer()
|
||
|
||
////////////////////////////// header 层 /////////////////////////////////////
|
||
|
||
if header := inServer.HasHeader(); header != nil {
|
||
|
||
//websocket 可以自行处理header, 不需要额外http包装
|
||
if adv != "ws" {
|
||
wrappedConn = &httpLayer.HeaderConn{
|
||
Conn: wrappedConn,
|
||
H: header,
|
||
IsServerEnd: true,
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
////////////////////////////// 高级层 /////////////////////////////////////
|
||
|
||
if adv != "" {
|
||
switch adv {
|
||
//quic虽然也是adv层,但是因为根本没调用 handleNewIncomeConnection 函数,所以不在此处理
|
||
//
|
||
|
||
case "grpc":
|
||
//grpc不太一样, 它是多路复用的
|
||
// 每一条建立好的 grpc 可以随时产生对新目标的请求,
|
||
|
||
// 我们直接循环监听然后分别用 新goroutine发向 handshakeInserver_and_passToOutClient
|
||
|
||
if ce := utils.CanLogDebug("start upgrade grpc"); ce != nil {
|
||
ce.Write()
|
||
}
|
||
|
||
grpcs := inServer.GetGRPC_Server() //这个grpc server是在配置阶段初始化好的.
|
||
|
||
grpcs.StartHandle(wrappedConn)
|
||
|
||
//start之后,客户端就会利用同一条tcp链接 来发送多个 请求,自此就不能直接用原来的链接了;
|
||
// 新的子请求被 grpc包 抽象成了 抽象的 conn
|
||
//遇到chan被关闭的情况后,就会自动关闭底层连接并退出整个函数。
|
||
for {
|
||
newGConn, ok := <-grpcs.NewConnChan
|
||
if !ok {
|
||
if ce := utils.CanLogWarn("grpc getNewSubConn not ok"); ce != nil {
|
||
ce.Write()
|
||
}
|
||
|
||
iics.baseLocalConn.Close()
|
||
return
|
||
}
|
||
|
||
iics.wrappedConn = newGConn
|
||
|
||
go handshakeInserver_and_passToOutClient(iics)
|
||
}
|
||
|
||
case "ws":
|
||
|
||
//从ws开始就可以应用fallback了
|
||
// 但是,不能先upgrade, 因为我们要用path分流, 所以我们要先预读;
|
||
// 否则的话,正常的http流量频繁地被用不到的 ws 过滤器处理,会损失很多性能,而且gobwas没办法简洁地保留初始buffer.
|
||
|
||
var rp httpLayer.RequestParser
|
||
re := rp.ReadAndParse(wrappedConn)
|
||
if re != nil {
|
||
if re == httpLayer.ErrNotHTTP_Request {
|
||
if ce := utils.CanLogErr("WS check ErrNotHTTP_Request"); ce != nil {
|
||
ce.Write(
|
||
zap.String("handler", inServer.AddrStr()),
|
||
)
|
||
}
|
||
|
||
} else {
|
||
if ce := utils.CanLogErr("WS check handshake read failed"); ce != nil {
|
||
|
||
ce.Write(
|
||
zap.String("handler", inServer.AddrStr()),
|
||
)
|
||
}
|
||
}
|
||
|
||
wrappedConn.Close()
|
||
return
|
||
}
|
||
|
||
wss := inServer.GetWS_Server()
|
||
|
||
if rp.Method != "GET" || wss.Thepath != rp.Path {
|
||
iics.theRequestPath = rp.Path
|
||
iics.theFallbackFirstBuffer = rp.WholeRequestBuf
|
||
|
||
if ce := utils.CanLogDebug("WS check err"); ce != nil {
|
||
|
||
ce.Write(
|
||
zap.String("handler", inServer.AddrStr()),
|
||
zap.String("reason", "path/method not match"),
|
||
zap.String("validPath", wss.Thepath),
|
||
zap.String("gotMethod", rp.Method),
|
||
zap.String("gotPath", rp.Path),
|
||
)
|
||
}
|
||
|
||
passToOutClient(iics, true, nil, nil, netLayer.Addr{})
|
||
return
|
||
|
||
}
|
||
|
||
//此时path和method都已经匹配了, 如果还不能通过那就说明后面的header等数据不满足ws的upgrade请求格式, 肯定是非法数据了,也不用再回落
|
||
wsConn, err := wss.Handshake(rp.WholeRequestBuf, wrappedConn)
|
||
if err != nil {
|
||
if ce := utils.CanLogErr("InServer ws handshake failed"); ce != nil {
|
||
|
||
ce.Write(
|
||
zap.String("handler", inServer.AddrStr()),
|
||
zap.Error(err),
|
||
)
|
||
}
|
||
|
||
wrappedConn.Close()
|
||
return
|
||
|
||
}
|
||
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
|
||
|
||
wlc, udp_wlc, targetAddr, err = inServer.Handshake(iics.wrappedConn)
|
||
|
||
if err == nil {
|
||
if udp_wlc != nil && inServer.Name() == "socks5" {
|
||
// socks5的 udp associate返回的是 clientFutureAddr, 而不是实际客户的第一个请求.
|
||
//所以我们要读一次才能进行下一步。
|
||
|
||
firstSocks5RequestData, firstSocks5RequestAddr, err2 := udp_wlc.ReadMsgFrom()
|
||
if err2 != nil {
|
||
if ce := utils.CanLogWarn("failed in socks5 read"); ce != nil {
|
||
ce.Write(
|
||
zap.String("handler", inServer.AddrStr()),
|
||
zap.Error(err2),
|
||
)
|
||
}
|
||
err = err2
|
||
return
|
||
}
|
||
|
||
iics.theFallbackFirstBuffer = bytes.NewBuffer(firstSocks5RequestData)
|
||
|
||
targetAddr = firstSocks5RequestAddr
|
||
}
|
||
|
||
////////////////////////////// 内层mux阶段 /////////////////////////////////////
|
||
|
||
if muxInt, innerProxyName := inServer.HasInnerMux(); muxInt > 0 {
|
||
if mh, ok := wlc.(proxy.MuxMarker); ok {
|
||
|
||
innerSerConf := proxy.ListenConf{
|
||
CommonConf: proxy.CommonConf{
|
||
Protocol: innerProxyName,
|
||
},
|
||
}
|
||
|
||
innerSer, err2 := proxy.NewServer(&innerSerConf)
|
||
if err2 != nil {
|
||
if ce := utils.CanLogDebug("mux inner proxy server creation failed"); ce != nil {
|
||
ce.Write(zap.Error(err))
|
||
}
|
||
err = err2
|
||
return
|
||
}
|
||
|
||
session := inServer.GetServerInnerMuxSession(mh)
|
||
|
||
if session == nil {
|
||
err = utils.ErrFailed
|
||
return
|
||
}
|
||
|
||
//内层mux要对每一个子连接单独进行 子代理协议握手 以及 outClient的拨号。
|
||
|
||
go func() {
|
||
|
||
for {
|
||
utils.Debug("inServer try accept smux stream ")
|
||
|
||
stream, err := session.AcceptStream()
|
||
if err != nil {
|
||
if ce := utils.CanLogDebug("mux inServer try accept stream failed"); ce != nil {
|
||
ce.Write(zap.Error(err))
|
||
}
|
||
|
||
session.Close()
|
||
return
|
||
}
|
||
|
||
go func() {
|
||
if ce := utils.CanLogDebug("inServer got mux stream"); ce != nil {
|
||
ce.Write(zap.String("innerProxyName", innerProxyName))
|
||
}
|
||
|
||
wlc1, udp_wlc1, targetAddr1, err1 := innerSer.Handshake(stream)
|
||
|
||
if err1 != nil {
|
||
if ce := utils.CanLogDebug("mux inner proxy handshake failed"); ce != nil {
|
||
ce.Write(zap.Error(err1))
|
||
}
|
||
newiics := *iics
|
||
|
||
if !findoutFirstBuf(err1, &newiics) {
|
||
return
|
||
}
|
||
passToOutClient(newiics, true, wlc1, udp_wlc1, targetAddr1)
|
||
|
||
} else {
|
||
|
||
if ce := utils.CanLogDebug("inServer mux stream handshake ok"); ce != nil {
|
||
ce.Write(zap.String("targetAddr1", targetAddr1.String()))
|
||
}
|
||
|
||
passToOutClient(*iics, false, wlc1, udp_wlc1, targetAddr1)
|
||
|
||
}
|
||
|
||
}()
|
||
|
||
}
|
||
}()
|
||
|
||
err = utils.ErrHandled
|
||
return
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// 在调用 passToOutClient前遇到err时调用, 若找出了buf,设置iics,并返回true
|
||
func findoutFirstBuf(err error, iics *incomingInserverConnState) 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.ErrFirstBuffer)
|
||
if !ok {
|
||
// 能fallback 但是返回的 err却不是fallback err,证明遇到了更大问题,可能是底层read问题,所以也不用继续fallback了
|
||
iics.wrappedConn.Close()
|
||
return false
|
||
}
|
||
|
||
if firstbuffer := fe.First; firstbuffer == nil {
|
||
//不应该,至少能读到1字节的。
|
||
|
||
panic("No FirstBuffer")
|
||
|
||
} else {
|
||
iics.theFallbackFirstBuffer = firstbuffer
|
||
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
// 本函数 处理inServer的代理层数据,并在试图处理 分流和回落后,将流量导向目标,并开始Copy。
|
||
// iics 不使用指针, 因为iics不能公用,因为 在多路复用时 iics.wrappedConn 是会变化的。
|
||
//
|
||
//被 handleNewIncomeConnection 调用。
|
||
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 !findoutFirstBuf(err, &iics) {
|
||
return
|
||
}
|
||
|
||
passToOutClient(iics, true, nil, nil, netLayer.Addr{})
|
||
}
|
||
|
||
}
|
||
|
||
//查看当前配置 是否支持fallback, 并获得回落地址。
|
||
// 被 passToOutClient 调用
|
||
func checkfallback(iics incomingInserverConnState) (targetAddr netLayer.Addr, wlc net.Conn) {
|
||
//先检查 mainFallback,如果mainFallback中各项都不满足 或者根本没有 mainFallback 再检查 defaultFallback
|
||
|
||
if mf := iics.RoutingEnv.MainFallback; mf != nil {
|
||
|
||
utils.Debug("Fallback check")
|
||
|
||
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
|
||
}
|
||
}
|
||
|
||
{
|
||
fbAddr := mf.GetFallback(thisFallbackType, fallback_params...)
|
||
|
||
if ce := utils.CanLogDebug("Fallback check"); ce != nil {
|
||
if fbAddr != nil {
|
||
ce.Write(
|
||
zap.String("matched", fbAddr.String()),
|
||
)
|
||
} else {
|
||
ce.Write(
|
||
zap.String("no match", ""),
|
||
)
|
||
}
|
||
}
|
||
if fbAddr != nil {
|
||
targetAddr = *fbAddr
|
||
wlc = iics.wrappedConn
|
||
return
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
//默认回落, 每个listen配置 都可 有一个自己独享的默认回落
|
||
|
||
if defaultFallbackAddr := iics.inServer.GetFallback(); defaultFallbackAddr != nil {
|
||
|
||
targetAddr = *defaultFallbackAddr
|
||
wlc = iics.wrappedConn
|
||
|
||
}
|
||
return
|
||
}
|
||
|
||
//被 handshakeInserver_and_passToOutClient 和 handshakeInserver 的innerMux部分 调用,会调用 dialClient_andRelay
|
||
func passToOutClient(iics incomingInserverConnState, isfallback bool, wlc net.Conn, udp_wlc netLayer.MsgConn, targetAddr netLayer.Addr) {
|
||
|
||
////////////////////////////// 回落阶段 /////////////////////////////////////
|
||
|
||
if isfallback {
|
||
|
||
fallbackTargetAddr, fallbackWlc := checkfallback(iics)
|
||
if fallbackWlc != nil {
|
||
targetAddr = fallbackTargetAddr
|
||
wlc = fallbackWlc
|
||
}
|
||
}
|
||
|
||
if wlc == nil && udp_wlc == nil {
|
||
//无wlc证明 inServer 握手失败,且 没有任何回落可用, 直接退出。
|
||
utils.Debug("invalid request and no matched fallback, hung up")
|
||
if iics.wrappedConn != nil {
|
||
iics.wrappedConn.Close()
|
||
|
||
}
|
||
return
|
||
}
|
||
|
||
//此时 targetAddr已经完全确定
|
||
|
||
////////////////////////////// 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 := utils.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 := utils.CanLogDebug("Dns result"); ce2 != nil {
|
||
ce2.Write(zap.String("domain", targetAddr.Name), zap.String("ip", ip.String()))
|
||
}
|
||
}
|
||
}
|
||
|
||
////////////////////////////// 分流阶段 /////////////////////////////////////
|
||
|
||
var client proxy.Client = iics.defaultClient
|
||
routed := false
|
||
|
||
inServer := iics.inServer
|
||
|
||
//尝试分流, 获取到真正要发向 的 outClient
|
||
if iics.RoutingEnv != nil && iics.RoutingEnv.RoutePolicy != nil && !(inServer != nil && inServer.CantRoute()) {
|
||
|
||
desc := &netLayer.TargetDescription{
|
||
Addr: targetAddr,
|
||
}
|
||
if inServer != nil {
|
||
desc.Tag = inServer.GetTag()
|
||
}
|
||
|
||
if ce := utils.CanLogDebug("try routing"); ce != nil {
|
||
ce.Write(zap.Any("source", desc))
|
||
}
|
||
|
||
outtag := iics.RoutingEnv.RoutePolicy.GetOutTag(desc)
|
||
|
||
if outtag == "direct" {
|
||
client = DirectClient
|
||
iics.routedToDirect = true
|
||
routed = true
|
||
|
||
if ce := utils.CanLogInfo("Route to direct"); ce != nil {
|
||
ce.Write(
|
||
zap.String("target", targetAddr.UrlString()),
|
||
)
|
||
}
|
||
} else {
|
||
|
||
if tagC, ok := ClientsTagMap[outtag]; ok {
|
||
client = tagC
|
||
routed = true
|
||
if ce := utils.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 {
|
||
if ce := utils.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 = Tls_lazy_encrypt && canLazyEncryptClient(client)
|
||
|
||
}
|
||
|
||
// 我们在客户端 lazy_encrypt 探测时,读取socks5 传来的信息,因为这个 就是要发送到 outClient 的信息,所以就不需要等包上vless、tls后再判断了, 直接解包 socks5 对 tls 进行判断
|
||
//
|
||
// 而在服务端探测时,因为 客户端传来的连接 包了 tls,所以要在tls解包后, vless 解包后,再进行判断;
|
||
// 所以总之都是要在 inServer 判断 wlc; 总之,含义就是,去检索“用户承载数据”的来源
|
||
|
||
if isTlsLazy_clientEnd || iics.isTlsLazyServerEnd {
|
||
|
||
if tlsLayer.PDD {
|
||
log.Printf("loading TLS SniffConn %t %t\n", isTlsLazy_clientEnd, iics.isTlsLazyServerEnd)
|
||
}
|
||
|
||
wlc = tlsLayer.NewSniffConn(iics.baseLocalConn, wlc, isTlsLazy_clientEnd, Tls_lazy_secure)
|
||
|
||
}
|
||
|
||
//这一段代码是去判断是否要在转发结束后自动关闭连接, 主要是socks5 和lazy 的特殊情况
|
||
|
||
if targetAddr.IsUDP() {
|
||
|
||
if inServer != nil {
|
||
switch inServer.Name() {
|
||
case "socks5":
|
||
// 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.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 )。
|
||
// 在 dialClient_andRelay 中被调用。在udp为multi channel时也有用到
|
||
func dialClient(targetAddr netLayer.Addr,
|
||
client proxy.Client,
|
||
baseLocalConn,
|
||
wlc net.Conn,
|
||
cachedRemoteAddr string,
|
||
isTlsLazy_clientEnd bool) (
|
||
|
||
//return values:
|
||
wrc io.ReadWriteCloser,
|
||
udp_wrc netLayer.MsgConn,
|
||
realTargetAddr netLayer.Addr,
|
||
clientEndRemoteClientTlsRawReadRecorder *tlsLayer.Recorder,
|
||
result int) {
|
||
|
||
isudp := targetAddr.IsUDP()
|
||
|
||
hasInnerMux := false
|
||
var innerProxyName string
|
||
|
||
{
|
||
var muxInt int
|
||
|
||
if muxInt, innerProxyName = client.HasInnerMux(); muxInt == 2 {
|
||
hasInnerMux = true
|
||
|
||
//先过滤掉 innermux 通道已经建立的情况, 此时我们不必再次外部拨号,而是直接进行内层拨号.
|
||
if client.InnerMuxEstablished() {
|
||
wrc1, realudp_wrc, result1 := dialInnerProxy(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 {
|
||
utils.Debug("mux failed, will redial")
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
var err error
|
||
//先确认拨号地址
|
||
|
||
//direct的话自己是没有目的地址的,直接使用 请求的地址
|
||
// 而其它代理的话, realTargetAddr会被设成实际配置的代理的地址
|
||
realTargetAddr = targetAddr
|
||
|
||
/*
|
||
if ce := utils.CanLogDebug("request isn't the appointed domain"); ce != nil {
|
||
|
||
|
||
if uniqueTestDomain != "" && uniqueTestDomain != targetAddr.Name {
|
||
|
||
ce.Write(
|
||
zap.String("request", targetAddr.String()),
|
||
zap.String("uniqueTestDomain", uniqueTestDomain),
|
||
)
|
||
result = -1
|
||
return
|
||
|
||
}
|
||
}
|
||
*/
|
||
|
||
if ce := utils.CanLogInfo("Request"); ce != nil {
|
||
|
||
ce.Write(
|
||
zap.String("from", cachedRemoteAddr),
|
||
zap.String("target", targetAddr.UrlString()),
|
||
zap.String("through", proxy.GetVSI_url(client)),
|
||
)
|
||
}
|
||
|
||
if client.AddrStr() != "" {
|
||
|
||
realTargetAddr, err = netLayer.NewAddr(client.AddrStr())
|
||
if err != nil {
|
||
|
||
if ce := utils.CanLogErr("dial client convert addr err"); ce != nil {
|
||
ce.Write(zap.Error(err))
|
||
}
|
||
result = -1
|
||
return
|
||
}
|
||
realTargetAddr.Network = client.Network()
|
||
}
|
||
var clientConn net.Conn
|
||
|
||
var grpcClientConn grpc.ClientConn
|
||
var dialedCommonConn any
|
||
|
||
dialHere := !(client.Name() == "direct" && isudp)
|
||
|
||
// direct 的udp 是自己拨号的,因为要考虑到fullcone。
|
||
//
|
||
// 不是direct的udp的话,也要分情况:
|
||
//如果是单路的, 则我们在此dial, 如果是多路复用, 则不行, 因为要复用同一个连接
|
||
// Instead, 我们要试图从grpc中取出已经拨号好了的 grpc链接
|
||
adv := client.AdvancedLayer()
|
||
|
||
if dialHere {
|
||
|
||
switch adv {
|
||
case "grpc":
|
||
|
||
grpcClientConn = grpc.GetEstablishedConnFor(&realTargetAddr)
|
||
|
||
if grpcClientConn != nil {
|
||
//如果有已经建立好的连接,则跳过传输层拨号和tls阶段
|
||
goto advLayerHandshakeStep
|
||
}
|
||
case "quic":
|
||
dialedCommonConn = client.GetQuic_Client().DialCommonConn(false, nil)
|
||
if dialedCommonConn != nil {
|
||
goto advLayerHandshakeStep
|
||
} else {
|
||
|
||
result = -1
|
||
return
|
||
}
|
||
}
|
||
|
||
clientConn, err = realTargetAddr.Dial()
|
||
|
||
if err != nil {
|
||
if err == netLayer.ErrMachineCantConnectToIpv6 {
|
||
//如果一开始就知道机器没有ipv6地址,那么该错误就不是error等级,而是warning等级
|
||
|
||
if ce := utils.CanLogWarn("Machine HasNo ipv6 but got ipv6 request"); ce != nil {
|
||
ce.Write(
|
||
zap.String("target", realTargetAddr.String()),
|
||
)
|
||
}
|
||
|
||
} else {
|
||
//虽然拨号失败,但是不能认为我们一定有错误, 因为很可能申请的ip本身就是不可达的, 所以不是error等级而是warn等级
|
||
if ce := utils.CanLogWarn("failed dialing"); ce != nil {
|
||
ce.Write(
|
||
zap.String("target", realTargetAddr.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 {
|
||
tryTlsLazyRawCopy(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 := utils.CanLogErr("failed in handshake outClient tls"); ce != nil {
|
||
ce.Write(zap.String("target", targetAddr.String()), zap.Error(err))
|
||
}
|
||
|
||
result = -1
|
||
return
|
||
}
|
||
|
||
clientConn = tlsConn
|
||
|
||
}
|
||
|
||
////////////////////////////// header 层 /////////////////////////////////////
|
||
|
||
if header := client.HasHeader(); header != nil && adv != "ws" {
|
||
clientConn = &httpLayer.HeaderConn{
|
||
Conn: clientConn,
|
||
H: header,
|
||
}
|
||
|
||
}
|
||
|
||
////////////////////////////// 高级层握手阶段 /////////////////////////////////////
|
||
|
||
advLayerHandshakeStep:
|
||
|
||
if adv != "" {
|
||
switch adv {
|
||
case "quic":
|
||
qclient := client.GetQuic_Client()
|
||
clientConn, err = qclient.DialSubConn(dialedCommonConn)
|
||
if err != nil {
|
||
eStr := err.Error()
|
||
if strings.Contains(eStr, "too many") {
|
||
|
||
if ce := utils.CanLogDebug("DialSubConn got full session, open another one"); ce != nil {
|
||
ce.Write(
|
||
zap.String("full reason", eStr),
|
||
)
|
||
}
|
||
|
||
//第一条连接已满,再开一条session
|
||
dialedCommonConn = qclient.DialCommonConn(true, dialedCommonConn)
|
||
if dialedCommonConn == nil {
|
||
//再dial还是nil,也许是暂时性的网络错误, 先退出
|
||
result = -1
|
||
return
|
||
}
|
||
|
||
clientConn, err = qclient.DialSubConn(dialedCommonConn)
|
||
if err != nil {
|
||
if ce := utils.CanLogErr("DialSubConn failed after redialed new session"); ce != nil {
|
||
ce.Write(
|
||
zap.Error(err),
|
||
)
|
||
}
|
||
result = -1
|
||
return
|
||
}
|
||
} else {
|
||
if ce := utils.CanLogErr("DialSubConnFunc failed"); ce != nil {
|
||
ce.Write(
|
||
zap.Error(err),
|
||
)
|
||
}
|
||
result = -1
|
||
return
|
||
}
|
||
|
||
}
|
||
case "grpc":
|
||
if grpcClientConn == nil {
|
||
grpcClientConn, err = grpc.ClientHandshake(clientConn, &realTargetAddr)
|
||
if err != nil {
|
||
if ce := utils.CanLogErr("grpc.ClientHandshake failed"); ce != nil {
|
||
ce.Write(zap.Error(err))
|
||
|
||
}
|
||
if baseLocalConn != nil {
|
||
baseLocalConn.Close()
|
||
|
||
}
|
||
result = -1
|
||
return
|
||
}
|
||
|
||
}
|
||
|
||
clientConn, err = grpc.DialNewSubConn(client.Path(), grpcClientConn, &realTargetAddr, client.IsGrpcClientMultiMode())
|
||
if err != nil {
|
||
if ce := utils.CanLogErr("grpc.DialNewSubConn failed"); 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 baseLocalConn != nil {
|
||
baseLocalConn.Close()
|
||
}
|
||
result = -1
|
||
return
|
||
}
|
||
|
||
case "ws":
|
||
wsClient := client.GetWS_Client()
|
||
|
||
var ed []byte
|
||
|
||
if wsClient.UseEarlyData && wlc != nil {
|
||
//若配置了 MaxEarlyDataLen,则我们先读一段;
|
||
edBuf := utils.GetPacket()
|
||
edBuf = edBuf[:ws.MaxEarlyDataLen]
|
||
n, e := wlc.Read(edBuf)
|
||
if e != nil {
|
||
if ce := utils.CanLogErr("failed to read ws early data"); ce != nil {
|
||
ce.Write(zap.Error(e))
|
||
}
|
||
result = -1
|
||
return
|
||
}
|
||
ed = edBuf[:n]
|
||
|
||
if ce := utils.CanLogDebug("will send early data"); ce != nil {
|
||
ce.Write(
|
||
zap.Int("len", n),
|
||
)
|
||
}
|
||
|
||
}
|
||
|
||
// 我们verysimple的架构是 ws握手之后,再进行vless握手
|
||
// 但是如果要传输earlydata的话,则必须要在握手阶段就 预知 vless 的所有数据才行
|
||
// 所以我们需要一种特殊方法
|
||
|
||
var wc net.Conn
|
||
|
||
if len(ed) > 0 {
|
||
wc, err = wsClient.HandshakeWithEarlyData(clientConn, ed)
|
||
|
||
} else {
|
||
wc, err = wsClient.Handshake(clientConn)
|
||
|
||
}
|
||
|
||
if err != nil {
|
||
if ce := utils.CanLogErr("failed in handshake ws"); ce != nil {
|
||
ce.Write(
|
||
zap.String("target", targetAddr.String()),
|
||
zap.Error(err),
|
||
)
|
||
}
|
||
result = -1
|
||
return
|
||
}
|
||
|
||
clientConn = wc
|
||
}
|
||
}
|
||
|
||
////////////////////////////// 代理层 握手阶段 /////////////////////////////////////
|
||
|
||
if !isudp || hasInnerMux {
|
||
//udp但是有innermux时 依然用handshake, 而不是 EstablishUDPChannel
|
||
var firstPayload []byte
|
||
|
||
if !hasInnerMux { //如果有内层mux,要在dialInnerProxy函数里再读
|
||
firstPayload = utils.GetMTU()
|
||
|
||
wlc.SetReadDeadline(time.Now().Add(proxy.FirstPayloadTimeout))
|
||
n, err := wlc.Read(firstPayload)
|
||
if err != nil {
|
||
|
||
if !errors.Is(err, os.ErrDeadlineExceeded) {
|
||
if ce := utils.CanLogErr("Read first payload failed not because of timeout, will hung up"); ce != nil {
|
||
ce.Write(
|
||
zap.String("target", targetAddr.String()),
|
||
zap.Error(err),
|
||
)
|
||
}
|
||
|
||
clientConn.Close()
|
||
wlc.Close()
|
||
result = -1
|
||
return
|
||
} else {
|
||
if ce := utils.CanLogWarn("Read first payload but timeout, will relay without first payload."); ce != nil {
|
||
ce.Write(
|
||
zap.String("target", targetAddr.String()),
|
||
zap.Error(err),
|
||
)
|
||
}
|
||
}
|
||
|
||
}
|
||
wlc.SetReadDeadline(time.Time{})
|
||
firstPayload = firstPayload[:n]
|
||
}
|
||
|
||
if len(firstPayload) > 0 {
|
||
if ce := utils.CanLogDebug("handshake client with first payload"); ce != nil {
|
||
ce.Write(
|
||
zap.Int("len", len(firstPayload)),
|
||
)
|
||
}
|
||
}
|
||
|
||
wrc, err = client.Handshake(clientConn, firstPayload, targetAddr)
|
||
if err != nil {
|
||
if ce := utils.CanLogErr("Handshake client failed"); ce != nil {
|
||
ce.Write(
|
||
zap.String("target", targetAddr.String()),
|
||
zap.Error(err),
|
||
)
|
||
}
|
||
result = -1
|
||
return
|
||
}
|
||
|
||
} else {
|
||
|
||
udp_wrc, err = client.EstablishUDPChannel(clientConn, targetAddr)
|
||
if err != nil {
|
||
if ce := utils.CanLogErr("EstablishUDPChannel failed"); ce != nil {
|
||
ce.Write(
|
||
zap.String("target", targetAddr.String()),
|
||
zap.Error(err),
|
||
)
|
||
}
|
||
result = -1
|
||
return
|
||
}
|
||
}
|
||
|
||
////////////////////////////// 建立内层 mux 阶段 /////////////////////////////////////
|
||
if hasInnerMux {
|
||
//我们目前的实现中,mux统一使用smux v1, 即 smux.DefaultConfig返回的值。这可以兼容trojan的实现。
|
||
|
||
wrc, udp_wrc, result = dialInnerProxy(client, wlc, wrc, innerProxyName, targetAddr, isudp)
|
||
}
|
||
|
||
return
|
||
} //dialClient
|
||
|
||
//在 dialClient 中调用。 如果调用不成功,则result == -1
|
||
func dialInnerProxy(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
|
||
utils.Debug("dialInnerProxy return fail 1")
|
||
return
|
||
}
|
||
|
||
stream, err := smuxSession.OpenStream()
|
||
if err != nil {
|
||
client.CloseInnerMuxSession() //发现就算 OpenStream 失败, session也不会自动被关闭, 需要我们手动关一下。
|
||
|
||
if ce := utils.CanLogDebug("dialInnerProxy return fail 2"); 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 := utils.CanLogDebug("mux inner proxy client creation failed"); ce != nil {
|
||
ce.Write(zap.Error(err))
|
||
}
|
||
result = -1
|
||
return
|
||
}
|
||
if isudp {
|
||
realudp_wrc, err = muxClient.EstablishUDPChannel(stream, targetAddr)
|
||
if err != nil {
|
||
if ce := utils.CanLogDebug("mux inner proxy client handshake failed"); ce != nil {
|
||
ce.Write(zap.Error(err))
|
||
}
|
||
result = -1
|
||
return
|
||
}
|
||
} else {
|
||
|
||
firstPayload := utils.GetMTU()
|
||
|
||
wlc.SetReadDeadline(time.Now().Add(proxy.FirstPayloadTimeout))
|
||
n, err := wlc.Read(firstPayload)
|
||
|
||
if err != nil {
|
||
if ce := utils.CanLogErr("Read innermux first payload failed"); ce != nil {
|
||
ce.Write(
|
||
zap.String("target", targetAddr.String()),
|
||
zap.Error(err),
|
||
)
|
||
}
|
||
|
||
if !errors.Is(err, os.ErrDeadlineExceeded) {
|
||
if ce := utils.CanLogErr("Read innermux first payload failed not because of timeout, will hung up"); ce != nil {
|
||
ce.Write(
|
||
zap.String("target", targetAddr.String()),
|
||
zap.Error(err),
|
||
)
|
||
}
|
||
|
||
stream.Close()
|
||
wlc.Close()
|
||
result = -1
|
||
return
|
||
}
|
||
|
||
} else {
|
||
if ce := utils.CanLogDebug("innerMux got first payload"); ce != nil {
|
||
ce.Write(
|
||
zap.String("target", targetAddr.String()),
|
||
zap.Int("payloadLen", n),
|
||
)
|
||
}
|
||
}
|
||
wlc.SetReadDeadline(time.Time{})
|
||
|
||
realwrc, err = muxClient.Handshake(stream, firstPayload[:n], targetAddr)
|
||
if err != nil {
|
||
if ce := utils.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 {
|
||
if iics.baseLocalConn != nil {
|
||
defer iics.baseLocalConn.Close()
|
||
}
|
||
}
|
||
|
||
if wlc != nil {
|
||
defer wlc.Close()
|
||
|
||
}
|
||
}
|
||
|
||
wrc, udp_wrc, realTargetAddr, clientEndRemoteClientTlsRawReadRecorder, result := dialClient(targetAddr, client, iics.baseLocalConn, wlc, iics.cachedRemoteAddr, isTlsLazy_clientEnd)
|
||
if result != 0 {
|
||
return
|
||
}
|
||
|
||
////////////////////////////// 实际转发阶段 /////////////////////////////////////
|
||
|
||
if !targetAddr.IsUDP() {
|
||
|
||
if Tls_lazy_encrypt && !iics.routedToDirect {
|
||
|
||
// 我们加了回落之后,就无法确定 “未使用tls的outClient 一定是在服务端” 了
|
||
if isTlsLazy_clientEnd {
|
||
|
||
if client.IsUseTLS() {
|
||
//必须是 UserClient
|
||
if userClient := client.(proxy.UserClient); userClient != nil {
|
||
tryTlsLazyRawCopy(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 {
|
||
tryTlsLazyRawCopy(false, nil, userServer, netLayer.Addr{}, wrc, wlc, iics.baseLocalConn, false, iics.inServerTlsRawReadRecorder)
|
||
return
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if iics.theFallbackFirstBuffer != nil {
|
||
//这里注意,因为是把 tls解密了之后的数据发送到目标地址,所以这种方式只支持转发到本机纯http服务器
|
||
wrc.Write(iics.theFallbackFirstBuffer.Bytes())
|
||
utils.PutBytes(iics.theFallbackFirstBuffer.Bytes()) //这个Buf不是从utils.GetBuf创建的,而是从一个 GetBytes的[]byte 包装 的,所以我们要PutBytes,而不是PutBuf
|
||
}
|
||
|
||
atomic.AddInt32(&ActiveConnectionCount, 1)
|
||
|
||
netLayer.Relay(&realTargetAddr, wrc, wlc, &AllDownloadBytesSinceStart, &AllUploadBytesSinceStart)
|
||
|
||
atomic.AddInt32(&ActiveConnectionCount, -1)
|
||
|
||
return
|
||
|
||
} else {
|
||
|
||
if iics.theFallbackFirstBuffer != nil {
|
||
|
||
udp_wrc.WriteMsgTo(iics.theFallbackFirstBuffer.Bytes(), targetAddr)
|
||
utils.PutBytes(iics.theFallbackFirstBuffer.Bytes())
|
||
|
||
}
|
||
|
||
atomic.AddInt32(&ActiveConnectionCount, 1)
|
||
|
||
if client.IsUDP_MultiChannel() {
|
||
utils.Debug("Relaying UDP with MultiChannel")
|
||
|
||
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, iics.baseLocalConn, nil, "", false)
|
||
|
||
if ce := utils.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, &AllDownloadBytesSinceStart, &AllUploadBytesSinceStart)
|
||
|
||
}
|
||
|
||
atomic.AddInt32(&ActiveConnectionCount, -1)
|
||
|
||
return
|
||
}
|
||
|
||
}
|