From 375eb9aa665276babda5d5beffe053177f72e6b0 Mon Sep 17 00:00:00 2001 From: "arraykeys@gmail.com" Date: Thu, 30 Aug 2018 15:34:13 +0800 Subject: [PATCH] v5.4 --- CHANGELOG | 3 + README_ZH.md | 54 +++++++++++++++-- config.go | 6 +- sdk/CHANGELOG | 3 + sdk/android-ios/sdk.go | 6 +- services/mux/mux_client.go | 43 +++++++++++-- services/mux/mux_server.go | 44 ++++++++++++-- services/tcp/tcp.go | 85 +++++++++++++++++--------- services/tunnel/tunnel_bridge.go | 1 - services/tunnel/tunnel_client.go | 36 +++++++++-- services/tunnel/tunnel_server.go | 79 ++++++++++++++---------- utils/functions.go | 3 + utils/jumper/jumper.go | 100 +++++++++++++++++++++++++++++++ 13 files changed, 383 insertions(+), 80 deletions(-) create mode 100644 utils/jumper/jumper.go diff --git a/CHANGELOG b/CHANGELOG index d38072f..71c2141 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,9 @@ v5.4 1.优化了获取本地IP信息导致CPU过高的问题. 2.所有服务都增加了--nolog参数,可以关闭日志输出,节省CPU. 3.优化sdk,支持并发启动/关闭操作. +4.修复了多连接版本的内网穿透,tserver连接不能正确释放的bug. +5.内网穿透增加了client/tclient和server/tserver使用代理连接bridge/tbridge的功能,详细内容参考手册. +6.TCP端口映射(TCP代理)增加了使用代理连接上级的功能,详细内容参考手册. v5.3 1.优化了socks_client握手端口判断,避免了sstap测试UDP失败的问题. diff --git a/README_ZH.md b/README_ZH.md index e86a901..80668a5 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -87,14 +87,15 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 - [1.12 自定义加密](#112-自定义加密) - [1.13 压缩传输](#113-压缩传输) - [1.14 查看帮助](#114-查看帮助) -- [2. TCP代理](#2tcp代理) +- [2. TCP代理(端口映射)](#2tcp代理) - [2.1 普通一级TCP代理](#21普通一级tcp代理) - [2.2 普通二级TCP代理](#22普通二级tcp代理) - [2.3 普通三级TCP代理](#23普通三级tcp代理) - [2.4 加密二级TCP代理](#24加密二级tcp代理) - [2.5 加密三级TCP代理](#25加密三级tcp代理) - - [2.6 查看帮助](#26查看帮助) -- [3. UDP代理](#3udp代理) + - [2.6 通过代理连接上级](#26通过代理连接上级) + - [2.7 查看帮助](#27查看帮助) +- [3. UDP代理(端口映射)](#3udp代理) - [3.1 普通一级UDP代理](#31普通一级udp代理) - [3.2 普通二级UDP代理](#32普通二级udp代理) - [3.3 普通三级UDP代理](#33普通三级udp代理) @@ -109,7 +110,8 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 - [4.5 高级用法一](#45高级用法一) - [4.6 高级用法一](#46高级用法二) - [4.7 server的-r参数](#47server的-r参数) - - [4.8 查看帮助](#48查看帮助) + - [4.8 server和client通过代理连接bridge](#48server和client通过代理连接bridge) + - [4.9 查看帮助](#49查看帮助) - [5. SOCKS5代理](#5socks5代理) - [5.1 普通SOCKS5代理](#51普通socks5代理) - [5.2 普通二级SOCKS5代理](#52普通二级socks5代理) @@ -506,7 +508,27 @@ VPS(IP:22.22.22.33)执行: `./proxy tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key` 那么访问本地8080端口就是通过加密TCP隧道访问66.66.66.66的8080端口. -#### **2.6.查看帮助** +#### **2.6.通过代理连接上级** +有时候proxy所在的网络不能直接访问外网,需要通过一个https或者socks5代理才能上网,那么这个时候 +-J参数就可以帮助你让proxy的tcp端口映射的时候通过https或者socks5代理去连接上级-P,将外部端口映射到本地. +-J参数格式如下: + +https代理写法: +代理需要认证,用户名:username 密码:password +https://username:password@host:port +代理不需要认证 +https://host:port + +socks5代理写法: +代理需要认证,用户名:username 密码:password +socks5://username:password@host:port +代理不需要认证 +socks5://host:port + +host:代理的IP或者域名 +port:代理的端口 + +#### **2.7.查看帮助** `./proxy help tcp` ### **3.UDP代理** @@ -693,7 +715,27 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid 4.7.3.LOCAL_IP为空默认是:`0.0.0.0`,CLIENT_LOCAL_HOST为空默认是:`127.0.0.1`; -#### **4.8.查看帮助** +#### **4.8.server和client通过代理连接bridge** +有时候server或者client所在的网络不能直接访问外网,需要通过一个https或者socks5代理才能上网,那么这个时候 +-J参数就可以帮助你让server或者client通过https或者socks5代理去连接bridge. +-J参数格式如下: + +https代理写法: +代理需要认证,用户名:username 密码:password +https://username:password@host:port +代理不需要认证 +https://host:port + +socks5代理写法: +代理需要认证,用户名:username 密码:password +socks5://username:password@host:port +代理不需要认证 +socks5://host:port + +host:代理的IP或者域名 +port:代理的端口 + +#### **4.9.查看帮助** `./proxy help bridge` `./proxy help server` `./proxy help client` diff --git a/config.go b/config.go index 00c56f9..0e9da16 100755 --- a/config.go +++ b/config.go @@ -124,8 +124,8 @@ func initConfig() (err error) { tcpArgs.Timeout = tcp.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('e').Default("2000").Int() tcpArgs.ParentType = tcp.Flag("parent-type", "parent protocol type ").Short('T').Enum("tls", "tcp", "udp", "kcp") tcpArgs.LocalType = tcp.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") - tcpArgs.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int() tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() + tcpArgs.Jumper = tcp.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() //########udp######### udp := app.Command("udp", "proxy on udp mode") @@ -149,6 +149,7 @@ func initConfig() (err error) { muxServerArgs.Route = muxServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings() muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp|tls mode").Default("false").Bool() muxServerArgs.SessionCount = muxServer.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int() + muxServerArgs.Jumper = muxServer.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() //########mux-client######### muxClient := app.Command("client", "proxy on mux client mode") @@ -160,6 +161,7 @@ func initConfig() (err error) { muxClientArgs.Key = muxClient.Flag("k", "key same with server").Default("default").String() muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp|tls mode").Default("false").Bool() muxClientArgs.SessionCount = muxClient.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int() + muxClientArgs.Jumper = muxClient.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() //########mux-bridge######### muxBridge := app.Command("bridge", "proxy on mux bridge mode") @@ -178,6 +180,7 @@ func initConfig() (err error) { tunnelServerArgs.IsUDP = tunnelServer.Flag("udp", "proxy on udp tunnel server mode").Default("false").Bool() tunnelServerArgs.Key = tunnelServer.Flag("k", "client key").Default("default").String() tunnelServerArgs.Route = tunnelServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings() + tunnelServerArgs.Jumper = tunnelServer.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() //########tunnel-client######### tunnelClient := app.Command("tclient", "proxy on tunnel client mode") @@ -186,6 +189,7 @@ func initConfig() (err error) { tunnelClientArgs.KeyFile = tunnelClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() tunnelClientArgs.Timeout = tunnelClient.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int() tunnelClientArgs.Key = tunnelClient.Flag("k", "key same with server").Default("default").String() + tunnelClientArgs.Jumper = tunnelClient.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() //########tunnel-bridge######### tunnelBridge := app.Command("tbridge", "proxy on tunnel bridge mode") diff --git a/sdk/CHANGELOG b/sdk/CHANGELOG index fd37799..9336b59 100644 --- a/sdk/CHANGELOG +++ b/sdk/CHANGELOG @@ -1,4 +1,7 @@ SDK更新日志 +v5.4 +1.去掉了无用参数 + v5.3 1.增加了支持日志输出回调的方法: diff --git a/sdk/android-ios/sdk.go b/sdk/android-ios/sdk.go index ec058e9..576cd5d 100644 --- a/sdk/android-ios/sdk.go +++ b/sdk/android-ios/sdk.go @@ -139,8 +139,8 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback) tcpArgs.Timeout = tcp.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('e').Default("2000").Int() tcpArgs.ParentType = tcp.Flag("parent-type", "parent protocol type ").Short('T').Enum("tls", "tcp", "udp", "kcp") tcpArgs.LocalType = tcp.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") - tcpArgs.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int() tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() + tcpArgs.Jumper = tcp.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() //########udp######### udp := app.Command("udp", "proxy on udp mode") @@ -164,6 +164,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback) muxServerArgs.Route = muxServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings() muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp|tls mode").Default("false").Bool() muxServerArgs.SessionCount = muxServer.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int() + muxServerArgs.Jumper = muxServer.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() //########mux-client######### muxClient := app.Command("client", "proxy on mux client mode") @@ -175,6 +176,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback) muxClientArgs.Key = muxClient.Flag("k", "key same with server").Default("default").String() muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp|tls mode").Default("false").Bool() muxClientArgs.SessionCount = muxClient.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int() + muxClientArgs.Jumper = muxClient.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() //########mux-bridge######### muxBridge := app.Command("bridge", "proxy on mux bridge mode") @@ -193,6 +195,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback) tunnelServerArgs.IsUDP = tunnelServer.Flag("udp", "proxy on udp tunnel server mode").Default("false").Bool() tunnelServerArgs.Key = tunnelServer.Flag("k", "client key").Default("default").String() tunnelServerArgs.Route = tunnelServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings() + tunnelServerArgs.Jumper = tunnelServer.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() //########tunnel-client######### tunnelClient := app.Command("tclient", "proxy on tunnel client mode") @@ -201,6 +204,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback) tunnelClientArgs.KeyFile = tunnelClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() tunnelClientArgs.Timeout = tunnelClient.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int() tunnelClientArgs.Key = tunnelClient.Flag("k", "key same with server").Default("default").String() + tunnelClientArgs.Jumper = tunnelClient.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() //########tunnel-bridge######### tunnelBridge := app.Command("tbridge", "proxy on tunnel bridge mode") diff --git a/services/mux/mux_client.go b/services/mux/mux_client.go index 3b55819..a40914f 100644 --- a/services/mux/mux_client.go +++ b/services/mux/mux_client.go @@ -12,6 +12,7 @@ import ( "github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services/kcpcfg" "github.com/snail007/goproxy/utils" + "github.com/snail007/goproxy/utils/jumper" //"github.com/xtaci/smux" smux "github.com/hashicorp/yamux" ) @@ -28,12 +29,14 @@ type MuxClientArgs struct { IsCompress *bool SessionCount *int KCP kcpcfg.KCPConfigArgs + Jumper *string } type MuxClient struct { cfg MuxClientArgs isStop bool sessions utils.ConcurrentMap log *logger.Logger + jumper *jumper.Jumper } func NewMuxClient() services.Service { @@ -65,6 +68,19 @@ func (s *MuxClient) CheckArgs() (err error) { return } } + if *s.cfg.Jumper != "" { + if *s.cfg.ParentType != "tls" && *s.cfg.ParentType != "tcp" { + err = fmt.Errorf("jumper only worked of -T is tls or tcp") + return + } + var j jumper.Jumper + j, err = jumper.New(*s.cfg.Jumper, time.Millisecond*time.Duration(*s.cfg.Timeout)) + if err != nil { + err = fmt.Errorf("parse jumper fail, err %s", err) + return + } + s.jumper = &j + } return } func (s *MuxClient) StopService() { @@ -182,15 +198,32 @@ func (s *MuxClient) Clean() { } func (s *MuxClient) getParentConn() (conn net.Conn, err error) { if *s.cfg.ParentType == "tls" { - var _conn tls.Conn - _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) - if err == nil { - conn = net.Conn(&_conn) + if s.jumper == nil { + var _conn tls.Conn + _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err == nil { + conn = net.Conn(&_conn) + } + } else { + conf, err := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err != nil { + return nil, err + } + var _c net.Conn + _c, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout)) + if err == nil { + conn = net.Conn(tls.Client(_c, conf)) + } } + } else if *s.cfg.ParentType == "kcp" { conn, err = utils.ConnectKCPHost(*s.cfg.Parent, s.cfg.KCP) } else { - conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout) + if s.jumper == nil { + conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout) + } else { + conn, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout)) + } } return } diff --git a/services/mux/mux_server.go b/services/mux/mux_server.go index d6ebef8..b9321fe 100644 --- a/services/mux/mux_server.go +++ b/services/mux/mux_server.go @@ -15,6 +15,7 @@ import ( "github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services/kcpcfg" "github.com/snail007/goproxy/utils" + "github.com/snail007/goproxy/utils/jumper" "github.com/golang/snappy" //"github.com/xtaci/smux" @@ -38,6 +39,7 @@ type MuxServerArgs struct { IsCompress *bool SessionCount *int KCP kcpcfg.KCPConfigArgs + Jumper *string } type MuxUDPItem struct { @@ -121,6 +123,7 @@ func (s *MuxServerManager) Start(args interface{}, log *logger.Logger) (err erro SessionCount: s.cfg.SessionCount, KCP: s.cfg.KCP, ParentType: s.cfg.ParentType, + Jumper: s.cfg.Jumper, }, log) if err != nil { @@ -164,6 +167,7 @@ type MuxServer struct { isStop bool udpConn *net.Conn log *logger.Logger + jumper *jumper.Jumper } func NewMuxServer() services.Service { @@ -208,6 +212,19 @@ func (s *MuxServer) CheckArgs() (err error) { err = fmt.Errorf("remote required") return } + if *s.cfg.Jumper != "" { + if *s.cfg.ParentType != "tls" && *s.cfg.ParentType != "tcp" { + err = fmt.Errorf("jumper only worked of -T is tls or tcp") + return + } + var j jumper.Jumper + j, err = jumper.New(*s.cfg.Jumper, time.Millisecond*time.Duration(*s.cfg.Timeout)) + if err != nil { + err = fmt.Errorf("parse jumper fail, err %s", err) + return + } + s.jumper = &j + } return } @@ -378,15 +395,32 @@ func (s *MuxServer) GetConn(index string) (conn net.Conn, err error) { } func (s *MuxServer) getParentConn() (conn net.Conn, err error) { if *s.cfg.ParentType == "tls" { - var _conn tls.Conn - _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) - if err == nil { - conn = net.Conn(&_conn) + if s.jumper == nil { + var _conn tls.Conn + _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err == nil { + conn = net.Conn(&_conn) + } + } else { + conf, err := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err != nil { + return nil, err + } + var _c net.Conn + _c, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout)) + if err == nil { + conn = net.Conn(tls.Client(_c, conf)) + } } + } else if *s.cfg.ParentType == "kcp" { conn, err = utils.ConnectKCPHost(*s.cfg.Parent, s.cfg.KCP) } else { - conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout) + if s.jumper == nil { + conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout) + } else { + conn, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout)) + } } return } diff --git a/services/tcp/tcp.go b/services/tcp/tcp.go index 8f7dd75..1ca2105 100644 --- a/services/tcp/tcp.go +++ b/services/tcp/tcp.go @@ -2,6 +2,7 @@ package tcp import ( "bufio" + "crypto/tls" "fmt" "io" logger "log" @@ -12,36 +13,36 @@ import ( "github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services/kcpcfg" "github.com/snail007/goproxy/utils" + "github.com/snail007/goproxy/utils/jumper" "strconv" ) type TCPArgs struct { - Parent *string - CertFile *string - KeyFile *string - CertBytes []byte - KeyBytes []byte - Local *string - ParentType *string - LocalType *string - Timeout *int - CheckParentInterval *int - KCP kcpcfg.KCPConfigArgs + Parent *string + CertFile *string + KeyFile *string + CertBytes []byte + KeyBytes []byte + Local *string + ParentType *string + LocalType *string + Timeout *int + KCP kcpcfg.KCPConfigArgs + Jumper *string } type TCP struct { - outPool utils.OutConn cfg TCPArgs sc *utils.ServerChannel isStop bool userConns utils.ConcurrentMap log *logger.Logger + jumper *jumper.Jumper } func NewTCP() services.Service { return &TCP{ - outPool: utils.OutConn{}, cfg: TCPArgs{}, isStop: false, userConns: utils.NewConcurrentMap(), @@ -62,10 +63,23 @@ func (s *TCP) CheckArgs() (err error) { return } } + if *s.cfg.Jumper != "" { + if *s.cfg.ParentType != "tls" && *s.cfg.ParentType != "tcp" { + err = fmt.Errorf("jumper only worked of -T is tls or tcp") + return + } + var j jumper.Jumper + j, err = jumper.New(*s.cfg.Jumper, time.Millisecond*time.Duration(*s.cfg.Timeout)) + if err != nil { + err = fmt.Errorf("parse jumper fail, err %s", err) + return + } + s.jumper = &j + } return } func (s *TCP) InitService() (err error) { - s.InitOutConnPool() + return } func (s *TCP) StopService() { @@ -146,7 +160,7 @@ func (s *TCP) callback(inConn net.Conn) { } func (s *TCP) OutToTCP(inConn *net.Conn) (err error) { var outConn net.Conn - outConn, err = s.outPool.Get() + outConn, err = s.GetParentConn() if err != nil { s.log.Printf("connect to %s , err:%s", *s.cfg.Parent, err) utils.CloseConn(inConn) @@ -219,17 +233,34 @@ func (s *TCP) OutToUDP(inConn *net.Conn) (err error) { return } -func (s *TCP) InitOutConnPool() { - if *s.cfg.ParentType == "tls" || *s.cfg.ParentType == "tcp" || *s.cfg.ParentType == "kcp" { - //dur int, isTLS bool, certBytes, keyBytes []byte, - //parent string, timeout int, InitialCap int, MaxCap int - s.outPool = utils.NewOutConn( - *s.cfg.CheckParentInterval, - *s.cfg.ParentType, - s.cfg.KCP, - s.cfg.CertBytes, s.cfg.KeyBytes, nil, - *s.cfg.Parent, - *s.cfg.Timeout, - ) +func (s *TCP) GetParentConn() (conn net.Conn, err error) { + if *s.cfg.ParentType == "tls" { + if s.jumper == nil { + var _conn tls.Conn + _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err == nil { + conn = net.Conn(&_conn) + } + } else { + conf, err := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err != nil { + return nil, err + } + var _c net.Conn + _c, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout)) + if err == nil { + conn = net.Conn(tls.Client(_c, conf)) + } + } + + } else if *s.cfg.ParentType == "kcp" { + conn, err = utils.ConnectKCPHost(*s.cfg.Parent, s.cfg.KCP) + } else { + if s.jumper == nil { + conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout) + } else { + conn, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout)) + } } + return } diff --git a/services/tunnel/tunnel_bridge.go b/services/tunnel/tunnel_bridge.go index d409a61..f02981c 100644 --- a/services/tunnel/tunnel_bridge.go +++ b/services/tunnel/tunnel_bridge.go @@ -30,7 +30,6 @@ type TunnelBridgeArgs struct { KeyBytes []byte Local *string Timeout *int - Mux *bool } type ServerConn struct { //ClientLocalAddr string //tcp:2.2.22:333@ID diff --git a/services/tunnel/tunnel_client.go b/services/tunnel/tunnel_client.go index d759e2e..d83c3f6 100644 --- a/services/tunnel/tunnel_client.go +++ b/services/tunnel/tunnel_client.go @@ -11,6 +11,7 @@ import ( "github.com/snail007/goproxy/services" "github.com/snail007/goproxy/utils" + "github.com/snail007/goproxy/utils/jumper" //"github.com/xtaci/smux" smux "github.com/hashicorp/yamux" ) @@ -28,7 +29,7 @@ type TunnelClientArgs struct { KeyBytes []byte Key *string Timeout *int - Mux *bool + Jumper *string } type TunnelClient struct { cfg TunnelClientArgs @@ -36,6 +37,7 @@ type TunnelClient struct { isStop bool userConns utils.ConcurrentMap log *logger.Logger + jumper *jumper.Jumper } func NewTunnelClient() services.Service { @@ -62,6 +64,18 @@ func (s *TunnelClient) CheckArgs() (err error) { return } s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + if err != nil { + return + } + if *s.cfg.Jumper != "" { + var j jumper.Jumper + j, err = jumper.New(*s.cfg.Jumper, time.Millisecond*time.Duration(*s.cfg.Timeout)) + if err != nil { + err = fmt.Errorf("parse jumper fail, err %s", err) + return + } + s.jumper = &j + } return } func (s *TunnelClient) StopService() { @@ -151,10 +165,24 @@ func (s *TunnelClient) GetInConn(typ uint8, data ...string) (outConn net.Conn, e return } func (s *TunnelClient) GetConn() (conn net.Conn, err error) { - var _conn tls.Conn - _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if s.jumper == nil { + var _conn tls.Conn + _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err == nil { + conn = net.Conn(&_conn) + } + } else { + conf, err := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err != nil { + return nil, err + } + var _c net.Conn + _c, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout)) + if err == nil { + conn = net.Conn(tls.Client(_c, conf)) + } + } if err == nil { - conn = net.Conn(&_conn) c, e := smux.Client(conn, &smux.Config{ AcceptBacklog: 256, EnableKeepAlive: true, diff --git a/services/tunnel/tunnel_server.go b/services/tunnel/tunnel_server.go index 858f168..98f4904 100644 --- a/services/tunnel/tunnel_server.go +++ b/services/tunnel/tunnel_server.go @@ -14,6 +14,7 @@ import ( "github.com/snail007/goproxy/services" "github.com/snail007/goproxy/utils" + "github.com/snail007/goproxy/utils/jumper" //"github.com/xtaci/smux" smux "github.com/hashicorp/yamux" @@ -32,7 +33,7 @@ type TunnelServerArgs struct { Timeout *int Route *[]string Mgr *TunnelServerManager - Mux *bool + Jumper *string } type UDPItem struct { packet *[]byte @@ -105,6 +106,7 @@ func (s *TunnelServerManager) Start(args interface{}, log *logger.Logger) (err e Key: &KEY, Timeout: s.cfg.Timeout, Mgr: s, + Jumper: s.cfg.Jumper, }, log) if err != nil { @@ -134,30 +136,6 @@ func (s *TunnelServerManager) InitService() (err error) { return } -func (s *TunnelServerManager) GetOutConn(typ uint8) (outConn net.Conn, ID string, err error) { - outConn, err = s.GetConn() - if err != nil { - s.log.Printf("connection err: %s", err) - return - } - ID = s.serverID - _, err = outConn.Write(utils.BuildPacket(typ, s.serverID)) - if err != nil { - s.log.Printf("write connection data err: %s ,retrying...", err) - utils.CloseConn(&outConn) - return - } - return -} -func (s *TunnelServerManager) GetConn() (conn net.Conn, err error) { - var _conn tls.Conn - _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) - if err == nil { - conn = net.Conn(&_conn) - } - return -} - type TunnelServer struct { cfg TunnelServerArgs udpChn chan UDPItem @@ -166,6 +144,7 @@ type TunnelServer struct { udpConn *net.Conn userConns utils.ConcurrentMap log *logger.Logger + jumper *jumper.Jumper } func NewTunnelServer() services.Service { @@ -210,6 +189,15 @@ func (s *TunnelServer) CheckArgs() (err error) { err = fmt.Errorf("remote required") return } + if *s.cfg.Jumper != "" { + var j jumper.Jumper + j, err = jumper.New(*s.cfg.Jumper, time.Millisecond*time.Duration(*s.cfg.Timeout)) + if err != nil { + err = fmt.Errorf("parse jumper fail, err %s", err) + return + } + s.jumper = &j + } return } @@ -301,11 +289,26 @@ func (s *TunnelServer) GetOutConn(typ uint8) (outConn net.Conn, ID string, err e return } func (s *TunnelServer) GetConn() (conn net.Conn, err error) { - var _conn tls.Conn - _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) + var dconn net.Conn + if s.jumper == nil { + var _conn tls.Conn + _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err == nil { + dconn = net.Conn(&_conn) + } + } else { + conf, err := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err != nil { + return nil, err + } + var _c net.Conn + _c, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout)) + if err == nil { + dconn = net.Conn(tls.Client(_c, conf)) + } + } if err == nil { - conn = net.Conn(&_conn) - c, e := smux.Client(conn, &smux.Config{ + sess, e := smux.Client(dconn, &smux.Config{ AcceptBacklog: 256, EnableKeepAlive: true, KeepAliveInterval: 9 * time.Second, @@ -316,14 +319,30 @@ func (s *TunnelServer) GetConn() (conn net.Conn, err error) { if e != nil { s.log.Printf("new mux client conn error,ERR:%s", e) err = e + dconn.Close() return } - conn, e = c.OpenStream() + conn, e = sess.OpenStream() if e != nil { s.log.Printf("mux client conn open stream error,ERR:%s", e) err = e + dconn.Close() return } + go func() { + defer func() { + _ = recover() + }() + timer := time.NewTicker(time.Second * 3) + for { + <-timer.C + if sess.NumStreams() == 0 { + sess.Close() + timer.Stop() + return + } + } + }() } return } diff --git a/utils/functions.go b/utils/functions.go index 0e960fb..9cca81a 100755 --- a/utils/functions.go +++ b/utils/functions.go @@ -108,6 +108,9 @@ func TlsConnect(host string, port, timeout int, certBytes, keyBytes, caCertBytes } return *tls.Client(_conn, conf), err } +func TlsConfig(certBytes, keyBytes, caCertBytes []byte) (conf *tls.Config, err error) { + return getRequestTlsConfig(certBytes, keyBytes, caCertBytes) +} func getRequestTlsConfig(certBytes, keyBytes, caCertBytes []byte) (conf *tls.Config, err error) { var cert tls.Certificate diff --git a/utils/jumper/jumper.go b/utils/jumper/jumper.go new file mode 100644 index 0000000..e66c85a --- /dev/null +++ b/utils/jumper/jumper.go @@ -0,0 +1,100 @@ +package jumper + +import ( + "bytes" + "encoding/base64" + "fmt" + "net" + "net/url" + "time" + + "golang.org/x/net/proxy" +) + +type Jumper struct { + proxyURL *url.URL + timeout time.Duration +} +type socks5Dialer struct { + timeout time.Duration +} + +func (s socks5Dialer) Dial(network, addr string) (net.Conn, error) { + return net.DialTimeout(network, addr, s.timeout) +} + +func New(proxyURL string, timeout time.Duration) (j Jumper, err error) { + u, err := url.Parse(proxyURL) + if err != nil { + return + } + j = Jumper{ + proxyURL: u, + timeout: timeout, + } + return +} +func (j *Jumper) Dial(address string, timeout time.Duration) (conn net.Conn, err error) { + switch j.proxyURL.Scheme { + case "https": + return j.dialHTTPS(address, timeout) + case "socks5": + return j.dialSOCKS5(address, timeout) + default: + return nil, fmt.Errorf("unkown scheme of %s", j.proxyURL.String()) + } +} +func (j *Jumper) dialHTTPS(address string, timeout time.Duration) (conn net.Conn, err error) { + conn, err = net.DialTimeout("tcp", j.proxyURL.Host, timeout) + if err != nil { + return + } + pb := new(bytes.Buffer) + pb.Write([]byte(fmt.Sprintf("CONNECT %s HTTP/1.1\r\n", address))) + pb.WriteString("Proxy-Connection: Keep-Alive\r\n") + if j.proxyURL.User != nil { + p, _ := j.proxyURL.User.Password() + u := fmt.Sprintf("%s:%s", j.proxyURL.User.Username(), p) + pb.Write([]byte(fmt.Sprintf("Proxy-Authorization: Basic %s\r\n", base64.StdEncoding.EncodeToString([]byte(u))))) + } + pb.Write([]byte("\r\n")) + _, err = conn.Write(pb.Bytes()) + if err != nil { + conn.Close() + conn = nil + err = fmt.Errorf("error connecting to proxy: %s", err) + return + } + reply := make([]byte, 1024) + conn.SetDeadline(time.Now().Add(timeout)) + n, err := conn.Read(reply) + conn.SetDeadline(time.Time{}) + if err != nil { + err = fmt.Errorf("error read reply from proxy: %s", err) + conn.Close() + conn = nil + return + } + if bytes.Index(reply[:n], []byte("200")) == -1 { + err = fmt.Errorf("error greeting to proxy, response: %s", string(reply[:n])) + conn.Close() + conn = nil + return + } + return +} +func (j *Jumper) dialSOCKS5(address string, timeout time.Duration) (conn net.Conn, err error) { + auth := &proxy.Auth{} + if j.proxyURL.User != nil { + auth.User = j.proxyURL.User.Username() + auth.Password, _ = j.proxyURL.User.Password() + } else { + auth = nil + } + dialSocksProxy, err := proxy.SOCKS5("tcp", j.proxyURL.Host, auth, socks5Dialer{timeout: timeout}) + if err != nil { + err = fmt.Errorf("error connecting to proxy: %s", err) + return + } + return dialSocksProxy.Dial("tcp", address) +}