diff --git a/CHANGELOG b/CHANGELOG index a241863..53d67bd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,9 @@ proxy更新日志 +v5.2 +1.修复了HTTP(S)\SPS反向代理无法正常工作的问题. +2.优化了智能判断,减少不必要的DNS解析. +3.重构了SOCKS和SPS的UDP功能,基于UDP的游戏加速嗖嗖的. + v5.1 1.优化了kcp默认mtu配置,调整为450. 2.优化了HTTP(S)\SOCKS5代理智能判断,更加精确。 diff --git a/README.md b/README.md index 595f9bd..33648c0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,24 @@ Proxy is a high performance HTTP, HTTPS, HTTPS, websocket, TCP, UDP, Socks5 prox [![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy/) [![license](https://img.shields.io/github/license/snail007/goproxy.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy/total.svg?style=plastic)](https://github.com/snail007/goproxy/releases) [![download](https://img.shields.io/github/release/snail007/goproxy.svg?style=plastic)](https://github.com/snail007/goproxy/releases) -[中文手册](/README_ZH.md) **[全平台GUI版本](/gui/README.md)** **[全平台SDK](/sdk/README.md)** +**[中文手册](/README_ZH.md)** + +**[全平台图形界面版本](/gui/README.md)** + +**[全平台SDK](/sdk/README.md)** + + +### How to use the source code? + +Pull Request is welcomed. +Recommend go1.10.1. +`go get github.com/snail007/goproxy` +use command cd to enter your go SRC directory +then cd to enter `github.com/snail007/goproxy`. +Direct compilation:`go build -o proxy` +execution: `go run *.go` +`utils` is a toolkit, and `service` is a specific service class. + ### Features - chain-style proxy: the program itself can be a primary proxy, and if a parent proxy is set, it can be used as a second level proxy or even a N level proxy. @@ -37,27 +54,7 @@ Proxy is a high performance HTTP, HTTPS, HTTPS, websocket, TCP, UDP, Socks5 prox - ...   -This page is the v5.1 manual, and the other version of the manual can be checked by the following link. -- [v5.1 manual](https://github.com/snail007/goproxy/tree/v5.1) -- [v4.9 manual](https://github.com/snail007/goproxy/tree/v4.9) -- [v4.8 manual](https://github.com/snail007/goproxy/tree/v4.8) -- [v4.7 manual](https://github.com/snail007/goproxy/tree/v4.7) -- [v4.6 manual](https://github.com/snail007/goproxy/tree/v4.6) -- [v4.5 manual](https://github.com/snail007/goproxy/tree/v4.5) -- [v4.4 manual](https://github.com/snail007/goproxy/tree/v4.4) -- [v4.3 manual](https://github.com/snail007/goproxy/tree/v4.3) -- [v4.2 manual](https://github.com/snail007/goproxy/tree/v4.2) -- [v4.0-4.1 manual](https://github.com/snail007/goproxy/tree/v4.1) -- [v3.9 manual](https://github.com/snail007/goproxy/tree/v3.9) -- [v3.8 manual](https://github.com/snail007/goproxy/tree/v3.8) -- [v3.6-v3.7 manual](https://github.com/snail007/goproxy/tree/v3.6) -- [v3.5 manual](https://github.com/snail007/goproxy/tree/v3.5) -- [v3.4 manual](https://github.com/snail007/goproxy/tree/v3.4) -- [v3.3 manual](https://github.com/snail007/goproxy/tree/v3.3) -- [v3.2 manual](https://github.com/snail007/goproxy/tree/v3.2) -- [v3.1 manual](https://github.com/snail007/goproxy/tree/v3.1) -- [v3.0 manual](https://github.com/snail007/goproxy/tree/v3.0) -- [v2.x manual](https://github.com/snail007/goproxy/tree/v2.2) +This page is the v5.2 manual, and the other version of the manual can be checked by the following [link](docs/old-release.md). ### How to find the organization? [Click to join the proxy group of gitter](https://gitter.im/go-proxy/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link)   @@ -1088,15 +1085,6 @@ First, you need to clone the project to your account, and then modify the code o Finally, Pull Request to dev branch of goproxy project, and contribute code for efficiency. PR needs to explain what changes have been made and why you change them. -### How to use the source code? -Recommend go1.10.1. -`go get github.com/snail007/goproxy` -use command cd to enter your go SRC directory -then cd to enter `github.com/snail007/goproxy`. -Direct compilation:`go build -o proxy` -execution: `go run *.go` -`utils` is a toolkit, and `service` is a specific service class. - ### License Proxy is licensed under GPLv3 license. ### Contact diff --git a/README_ZH.md b/README_ZH.md index 07b2ebc..6860b20 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -7,7 +7,17 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 [![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy/) [![license](https://img.shields.io/github/license/snail007/goproxy.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy/total.svg?style=plastic)](https://github.com/snail007/goproxy/releases) [![download](https://img.shields.io/github/release/snail007/goproxy.svg?style=plastic)](https://github.com/snail007/goproxy/releases) -**[English Manual](/README.md)** **[全平台GUI版本](/gui/README.md)** **[全平台SDK](/sdk/README.md)** +**[English Manual](/README.md)** + +**[全平台图形界面版本](/gui/README.md)** + +**[全平台SDK](/sdk/README.md)** + +### 如何贡献代码(Pull Request)? + +欢迎加入一起发展壮大proxy.首先需要clone本项目到自己的帐号下面, +然后在dev分支上面修改代码,最后发Pull Request到goproxy项目的dev分支即可, +为了高效贡献代码,pr的时候需要说明做了什么变更,原因是什么. ### Features - 链式代理,程序本身可以作为一级代理,如果设置了上级代理那么可以作为二级代理,乃至N级代理. @@ -38,7 +48,7 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 - ... -本页是v5.1手册,其他版本手册请点击[这里](docs/old-release.md)查看. +本页是v5.2手册,其他版本手册请点击[这里](docs/old-release.md)查看. ### 怎么找到组织? @@ -689,7 +699,14 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid `./proxy help client` ### **5.SOCKS5代理** -提示:SOCKS5代理,支持CONNECT,UDP协议,不支持BIND,支持用户名密码认证. +提示: + +SOCKS5代理,支持CONNECT,UDP协议,不支持BIND,支持用户名密码认证. + +***如果你的VPS是阿里云,腾讯云这种VPS,就是ifconfig看不见你的公网IP,只能看见内网IP,*** + +***那么需要加上`-g VPS公网IP`参数,SOCKS5代理的UDP功能才能正常工作。*** + #### **5.1.普通SOCKS5代理** `./proxy socks -t tcp -p "0.0.0.0:38080"` @@ -1109,11 +1126,6 @@ fast3:`--nodelay=1 --interval=10 --resend=2 --nc=1` - http(s)代理增加pac支持? - 欢迎加群反馈... -### 如何贡献代码(Pull Request)? -首先需要clone本项目到自己的帐号下面,然后在dev分支上面修改代码, -最后发Pull Request到goproxy项目的dev分支即可,为了高效贡献代码, -pr的时候需要说明做了什么变更,原因是什么. - ### 如何使用源码? 建议go1.10.1. `go get github.com/snail007/goproxy` @@ -1133,5 +1145,3 @@ QQ交流群:189618940 如果proxy帮助你解决了很多问题,你可以通过下面的捐赠更好的支持proxy. - - diff --git a/config.go b/config.go index 7b633db..547f26f 100755 --- a/config.go +++ b/config.go @@ -198,8 +198,6 @@ func initConfig() (err error) { socksArgs.ParentType = socks.Flag("parent-type", "parent protocol type ").Default("tcp").Short('T').Enum("tls", "tcp", "kcp", "ssh") socksArgs.LocalType = socks.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") socksArgs.Local = socks.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() - socksArgs.UDPParent = socks.Flag("udp-parent", "udp parent address, such as: \"23.32.32.19:33090\"").Default("").Short('X').String() - socksArgs.UDPLocal = socks.Flag("udp-local", "udp local ip:port to listen").Short('x').Default(":33090").String() socksArgs.CertFile = socks.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() socksArgs.CaCertFile = socks.Flag("ca", "ca cert file for tls").Default("").String() socksArgs.KeyFile = socks.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() diff --git a/docs/old-release.md b/docs/old-release.md index aba1110..7204b03 100644 --- a/docs/old-release.md +++ b/docs/old-release.md @@ -1,5 +1,6 @@ # Old Versions of Proxy +- [v5.1手册](https://github.com/snail007/goproxy/tree/v5.1) - [v5.0手册](https://github.com/snail007/goproxy/tree/v5.0) - [v4.9手册](https://github.com/snail007/goproxy/tree/v4.9) - [v4.8手册](https://github.com/snail007/goproxy/tree/v4.8) diff --git a/install_auto.sh b/install_auto.sh index 6f1723f..63b7672 100755 --- a/install_auto.sh +++ b/install_auto.sh @@ -5,7 +5,7 @@ if [ -e /tmp/proxy ]; then fi mkdir /tmp/proxy cd /tmp/proxy -wget https://github.com/snail007/goproxy/releases/download/v5.1/proxy-linux-amd64.tar.gz +wget https://github.com/snail007/goproxy/releases/download/v5.2/proxy-linux-amd64.tar.gz # #install proxy tar zxvf proxy-linux-amd64.tar.gz diff --git a/main.go b/main.go index 66239c7..9d56bb9 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,7 @@ import ( "github.com/snail007/goproxy/services" ) -const APP_VERSION = "5.1" +const APP_VERSION = "5.2" func main() { err := initConfig() diff --git a/release.sh b/release.sh index 2c505c5..19c7481 100755 --- a/release.sh +++ b/release.sh @@ -1,5 +1,5 @@ #!/bin/bash -VER="5.1" +VER="5.2" RELEASE="release-${VER}" rm -rf .cert mkdir .cert diff --git a/sdk/android-ios/dns.go b/sdk/android-ios/dns.go index bb2dc4a..b08290d 100644 --- a/sdk/android-ios/dns.go +++ b/sdk/android-ios/dns.go @@ -76,7 +76,7 @@ func (s *DNS) InitService() (err error) { nil, &net.Dialer{ Timeout: 5 * time.Second, - KeepAlive: 5 * time.Second, + KeepAlive: 2 * time.Second, }, ) if err != nil { diff --git a/sdk/android-ios/sdk.go b/sdk/android-ios/sdk.go index 1033d62..db1882a 100644 --- a/sdk/android-ios/sdk.go +++ b/sdk/android-ios/sdk.go @@ -194,8 +194,6 @@ func Start(serviceID, serviceArgsStr string) (errStr string) { socksArgs.ParentType = socks.Flag("parent-type", "parent protocol type ").Default("tcp").Short('T').Enum("tls", "tcp", "kcp", "ssh") socksArgs.LocalType = socks.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") socksArgs.Local = socks.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() - socksArgs.UDPParent = socks.Flag("udp-parent", "udp parent address, such as: \"23.32.32.19:33090\"").Default("").Short('X').String() - socksArgs.UDPLocal = socks.Flag("udp-local", "udp local ip:port to listen").Short('x').Default(":33090").String() socksArgs.CertFile = socks.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() socksArgs.CaCertFile = socks.Flag("ca", "ca cert file for tls").Default("").String() socksArgs.KeyFile = socks.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() diff --git a/services/http/http.go b/services/http/http.go index 16d41a3..a1a14aa 100644 --- a/services/http/http.go +++ b/services/http/http.go @@ -277,10 +277,11 @@ func (s *HTTP) callback(inConn net.Conn) { } else if *s.cfg.Always { useProxy = true } else { - k := s.Resolve(address) - s.checker.Add(address, k) - //var n, m uint - useProxy, _, _ = s.checker.IsBlocked(k) + var isInMap bool + useProxy, isInMap, _, _ = s.checker.IsBlocked(address) + if !isInMap { + s.checker.Add(address, s.Resolve(address)) + } //s.log.Printf("blocked ? : %v, %s , fail:%d ,success:%d", useProxy, address, n, m) } } @@ -344,7 +345,6 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut Password: *s.cfg.ParentKey, }) } - outAddr := outConn.RemoteAddr().String() //outLocalAddr := outConn.LocalAddr().String() if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") { @@ -353,8 +353,8 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut } else { //https或者http,上级是代理,proxy需要转发 outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - //直连目标或上级非代理,清理HTTP头部的代理头信息 - if !useProxy || *s.cfg.ParentType == "ssh" { + //直连目标或上级非代理或非SNI,清理HTTP头部的代理头信息. + if (!useProxy || *s.cfg.ParentType == "ssh") && !req.IsSNI { _, err = outConn.Write(utils.RemoveProxyHeaders(req.HeadBuf)) } else { _, err = outConn.Write(req.HeadBuf) @@ -524,6 +524,7 @@ func (s *HTTP) Resolve(address string) string { ip, err := s.domainResolver.Resolve(address) if err != nil { s.log.Printf("dns error %s , ERR:%s", address, err) + return address } return ip } diff --git a/services/mux/mux_bridge.go b/services/mux/mux_bridge.go index e80b7c5..73ed365 100644 --- a/services/mux/mux_bridge.go +++ b/services/mux/mux_bridge.go @@ -88,7 +88,7 @@ func (s *MuxBridge) StopService() { (*(*s.sc).Listener).Close() } for _, g := range s.clientControlConns.Items() { - for _, session := range g.(utils.ConcurrentMap).Items() { + for _, session := range g.(*utils.ConcurrentMap).Items() { (session.(*smux.Session)).Close() } } diff --git a/services/socks/socks.go b/services/socks/socks.go index b740966..5020c50 100644 --- a/services/socks/socks.go +++ b/services/socks/socks.go @@ -3,6 +3,7 @@ package socks import ( "crypto/tls" "fmt" + "io" "io/ioutil" logger "log" "net" @@ -13,7 +14,6 @@ import ( "github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services/kcpcfg" "github.com/snail007/goproxy/utils" - "github.com/snail007/goproxy/utils/aes" "github.com/snail007/goproxy/utils/conncrypt" "github.com/snail007/goproxy/utils/socks" "golang.org/x/crypto/ssh" @@ -48,8 +48,6 @@ type SocksArgs struct { AuthURLTimeout *int AuthURLRetry *int KCP kcpcfg.KCPConfigArgs - UDPParent *string - UDPLocal *string LocalIPS *[]string DNSAddress *string DNSTTL *int @@ -59,27 +57,31 @@ type SocksArgs struct { ParentCompress *bool } type Socks struct { - cfg SocksArgs - checker utils.Checker - basicAuth utils.BasicAuth - sshClient *ssh.Client - lockChn chan bool - udpSC utils.ServerChannel - sc *utils.ServerChannel - domainResolver utils.DomainResolver - isStop bool - userConns utils.ConcurrentMap - log *logger.Logger + cfg SocksArgs + checker utils.Checker + basicAuth utils.BasicAuth + sshClient *ssh.Client + lockChn chan bool + udpSC utils.ServerChannel + sc *utils.ServerChannel + domainResolver utils.DomainResolver + isStop bool + userConns utils.ConcurrentMap + log *logger.Logger + udpRelatedPacketConns utils.ConcurrentMap + udpLocalKey []byte + udpParentKey []byte } func NewSocks() services.Service { return &Socks{ - cfg: SocksArgs{}, - checker: utils.Checker{}, - basicAuth: utils.BasicAuth{}, - lockChn: make(chan bool, 1), - isStop: false, - userConns: utils.NewConcurrentMap(), + cfg: SocksArgs{}, + checker: utils.Checker{}, + basicAuth: utils.BasicAuth{}, + lockChn: make(chan bool, 1), + isStop: false, + userConns: utils.NewConcurrentMap(), + udpRelatedPacketConns: utils.NewConcurrentMap(), } } @@ -103,17 +105,6 @@ func (s *Socks) CheckArgs() (err error) { err = fmt.Errorf("parent type unkown,use -T ") return } - host, _, e := net.SplitHostPort(*s.cfg.Parent) - if e != nil { - err = fmt.Errorf("parent format error : %s", e) - return - } - if *s.cfg.UDPParent == "" { - *s.cfg.UDPParent = net.JoinHostPort(host, "33090") - } - if strings.HasPrefix(*s.cfg.UDPParent, ":") { - *s.cfg.UDPParent = net.JoinHostPort(host, strings.TrimLeft(*s.cfg.UDPParent, ":")) - } if *s.cfg.ParentType == "ssh" { if *s.cfg.SSHUser == "" { err = fmt.Errorf("ssh user required") @@ -145,6 +136,9 @@ func (s *Socks) CheckArgs() (err error) { } } } + s.udpLocalKey = s.LocalUDPKey() + s.udpParentKey = s.ParentUDPKey() + //s.log.Printf("udpLocalKey : %v , udpParentKey : %v", s.udpLocalKey, s.udpParentKey) return } func (s *Socks) InitService() (err error) { @@ -186,14 +180,6 @@ func (s *Socks) InitService() (err error) { } if *s.cfg.ParentType == "ssh" { s.log.Printf("warn: socks udp not suppored for ssh") - } else { - s.udpSC = utils.NewServerChannelHost(*s.cfg.UDPLocal, s.log) - e := s.udpSC.ListenUDP(s.udpCallback) - if e != nil { - err = fmt.Errorf("init udp service fail, ERR: %s", e) - return - } - s.log.Printf("udp socks proxy on %s", s.udpSC.UDPListener.LocalAddr()) } return } @@ -220,6 +206,9 @@ func (s *Socks) StopService() { for _, c := range s.userConns.Items() { (*c.(*net.Conn)).Close() } + for _, c := range s.udpRelatedPacketConns.Items() { + (*c.(*net.UDPConn)).Close() + } } func (s *Socks) Start(args interface{}, log *logger.Logger) (err error) { s.log = log @@ -234,9 +223,6 @@ func (s *Socks) Start(args interface{}, log *logger.Logger) (err error) { if *s.cfg.Parent != "" { s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) } - if *s.cfg.UDPParent != "" { - s.log.Printf("use socks udp parent %s", *s.cfg.UDPParent) - } sc := utils.NewServerChannelHost(*s.cfg.Local, s.log) if *s.cfg.LocalType == "tcp" { err = sc.ListenTCP(s.socksConnCallback) @@ -255,165 +241,7 @@ func (s *Socks) Start(args interface{}, log *logger.Logger) (err error) { func (s *Socks) Clean() { s.StopService() } -func (s *Socks) UDPKey() []byte { - return s.cfg.KeyBytes[:32] -} -func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) { - rawB := b - var err error - if *s.cfg.LocalType == "tls" { - //decode b - rawB, err = goaes.Decrypt(s.UDPKey(), b) - if err != nil { - s.log.Printf("decrypt udp packet fail from %s", srcAddr.String()) - return - } - } - p, err := socks.ParseUDPPacket(rawB) - s.log.Printf("udp revecived:%v", len(p.Data())) - if err != nil { - s.log.Printf("parse udp packet fail, ERR:%s", err) - return - } - //防止死循环 - if s.IsDeadLoop((*localAddr).String(), p.Host()) { - s.log.Printf("dead loop detected , %s", p.Host()) - return - } - //s.log.Printf("##########udp to -> %s:%s###########", p.Host(), p.Port()) - if *s.cfg.Parent != "" { - //有上级代理,转发给上级 - if *s.cfg.ParentType == "tls" { - //encode b - rawB, err = goaes.Encrypt(s.UDPKey(), rawB) - if err != nil { - s.log.Printf("encrypt udp data fail to %s", *s.cfg.Parent) - return - } - } - parent := *s.cfg.UDPParent - if parent == "" { - parent = *s.cfg.Parent - } - dstAddr, err := net.ResolveUDPAddr("udp", s.Resolve(parent)) - if err != nil { - s.log.Printf("can't resolve address: %s", err) - return - } - clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} - conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr) - if err != nil { - s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err) - return - } - conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*5))) - _, err = conn.Write(rawB) - conn.SetDeadline(time.Time{}) - s.log.Printf("udp request:%v", len(rawB)) - if err != nil { - s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) - conn.Close() - return - } - //s.log.Printf("send udp packet to %s success", dstAddr.String()) - buf := make([]byte, 10*1024) - conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - length, _, err := conn.ReadFromUDP(buf) - conn.SetDeadline(time.Time{}) - if err != nil { - s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) - conn.Close() - return - } - respBody := buf[0:length] - s.log.Printf("udp response:%v", len(respBody)) - //s.log.Printf("revecived udp packet from %s", dstAddr.String()) - if *s.cfg.ParentType == "tls" { - //decode b - respBody, err = goaes.Decrypt(s.UDPKey(), respBody) - if err != nil { - s.log.Printf("encrypt udp data fail to %s", *s.cfg.Parent) - conn.Close() - return - } - } - if *s.cfg.LocalType == "tls" { - d, err := goaes.Encrypt(s.UDPKey(), respBody) - if err != nil { - s.log.Printf("encrypt udp data fail from %s", dstAddr.String()) - conn.Close() - return - } - s.udpSC.UDPListener.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - s.udpSC.UDPListener.WriteToUDP(d, srcAddr) - s.udpSC.UDPListener.SetDeadline(time.Time{}) - s.log.Printf("udp reply:%v", len(d)) - d = nil - } else { - s.udpSC.UDPListener.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - s.udpSC.UDPListener.WriteToUDP(respBody, srcAddr) - s.udpSC.UDPListener.SetDeadline(time.Time{}) - s.log.Printf("udp reply:%v", len(respBody)) - } - - } else { - //本地代理 - dstAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(s.Resolve(p.Host()), p.Port())) - if err != nil { - s.log.Printf("can't resolve address: %s", err) - return - } - clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} - conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr) - if err != nil { - s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err) - return - } - conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*3))) - _, err = conn.Write(p.Data()) - conn.SetDeadline(time.Time{}) - s.log.Printf("udp send:%v", len(p.Data())) - if err != nil { - s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) - conn.Close() - return - } - //s.log.Printf("send udp packet to %s success", dstAddr.String()) - buf := make([]byte, 10*1024) - conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - length, _, err := conn.ReadFromUDP(buf) - conn.SetDeadline(time.Time{}) - - if err != nil { - s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) - conn.Close() - return - } - respBody := buf[0:length] - //封装来自真实服务器的数据,返回给访问者 - respPacket := p.NewReply(respBody) - //s.log.Printf("revecived udp packet from %s", dstAddr.String()) - if *s.cfg.LocalType == "tls" { - d, err := goaes.Encrypt(s.UDPKey(), respPacket) - if err != nil { - s.log.Printf("encrypt udp data fail from %s", dstAddr.String()) - conn.Close() - return - } - s.udpSC.UDPListener.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - s.udpSC.UDPListener.WriteToUDP(d, srcAddr) - s.udpSC.UDPListener.SetDeadline(time.Time{}) - d = nil - } else { - s.udpSC.UDPListener.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - s.udpSC.UDPListener.WriteToUDP(respPacket, srcAddr) - s.udpSC.UDPListener.SetDeadline(time.Time{}) - } - s.log.Printf("udp reply:%v", len(respPacket)) - } - -} func (s *Socks) socksConnCallback(inConn net.Conn) { defer func() { if err := recover(); err != nil { @@ -438,7 +266,9 @@ func (s *Socks) socksConnCallback(inConn net.Conn) { if err != nil { methodReq.Reply(socks.Method_NONE_ACCEPTABLE) utils.CloseConn(&inConn) - s.log.Printf("new methods request fail,ERR: %s", err) + if err != io.EOF { + s.log.Printf("new methods request fail,ERR: %s", err) + } return } @@ -526,16 +356,7 @@ func (s *Socks) socksConnCallback(inConn net.Conn) { } } -func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) { - if *s.cfg.ParentType == "ssh" { - utils.CloseConn(inConn) - return - } - host, _, _ := net.SplitHostPort((*inConn).LocalAddr().String()) - _, port, _ := net.SplitHostPort(s.udpSC.UDPListener.LocalAddr().String()) - s.log.Printf("proxy udp on %s", net.JoinHostPort(host, port)) - request.UDPReply(socks.REP_SUCCESS, net.JoinHostPort(host, port)) -} + func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) { var outConn net.Conn var err interface{} @@ -554,7 +375,7 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque return } if *s.cfg.Always { - outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr()) + outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr(), true) } else { if *s.cfg.Parent != "" { host, _, _ := net.SplitHostPort(request.Addr()) @@ -562,12 +383,14 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque if utils.IsIternalIP(host, *s.cfg.Always) { useProxy = false } else { - k := s.Resolve(request.Addr()) - s.checker.Add(request.Addr(), k) - useProxy, _, _ = s.checker.IsBlocked(k) + var isInMap bool + useProxy, isInMap, _, _ = s.checker.IsBlocked(request.Addr()) + if !isInMap { + s.checker.Add(request.Addr(), s.Resolve(request.Addr())) + } } if useProxy { - outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr()) + outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr(), true) } else { outConn, err = utils.ConnectHost(s.Resolve(request.Addr()), *s.cfg.Timeout) } @@ -607,7 +430,7 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque } s.userConns.Set(inAddr, inConn) } -func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn net.Conn, err interface{}) { +func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string, handshake bool) (outConn net.Conn, err interface{}) { switch *s.cfg.ParentType { case "kcp": fallthrough @@ -635,6 +458,9 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn n Password: *s.cfg.ParentKey, }) } + if !handshake { + return + } var buf = make([]byte, 1024) //var n int outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) @@ -804,6 +630,7 @@ func (s *Socks) Resolve(address string) string { ip, err := s.domainResolver.Resolve(address) if err != nil { s.log.Printf("dns error %s , ERR:%s", address, err) + return address } return ip } diff --git a/services/socks/udp.go b/services/socks/udp.go new file mode 100644 index 0000000..ad71a01 --- /dev/null +++ b/services/socks/udp.go @@ -0,0 +1,321 @@ +package socks + +import ( + "crypto/md5" + "fmt" + "net" + "runtime/debug" + "strconv" + "strings" + "time" + + "github.com/snail007/goproxy/utils" + goaes "github.com/snail007/goproxy/utils/aes" + "github.com/snail007/goproxy/utils/socks" +) + +func (s *Socks) ParentUDPKey() (key []byte) { + switch *s.cfg.ParentType { + case "tcp": + if *s.cfg.ParentKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *Socks) LocalUDPKey() (key []byte) { + switch *s.cfg.LocalType { + case "tcp": + if *s.cfg.LocalKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + if *s.cfg.ParentType == "ssh" { + utils.CloseConn(inConn) + return + } + srcIP, _, _ := net.SplitHostPort((*inConn).RemoteAddr().String()) + inconnRemoteAddr := (*inConn).RemoteAddr().String() + localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} + udpListener, err := net.ListenUDP("udp", localAddr) + if err != nil { + (*inConn).Close() + udpListener.Close() + s.log.Printf("udp bind fail , %s", err) + return + } + host, _, _ := net.SplitHostPort((*inConn).LocalAddr().String()) + _, port, _ := net.SplitHostPort(udpListener.LocalAddr().String()) + if len(*s.cfg.LocalIPS) > 0 { + host = (*s.cfg.LocalIPS)[0] + } + s.log.Printf("proxy udp on %s , for %s", net.JoinHostPort(host, port), inconnRemoteAddr) + request.UDPReply(socks.REP_SUCCESS, net.JoinHostPort(host, port)) + s.userConns.Set(inconnRemoteAddr, inConn) + var ( + outUDPConn *net.UDPConn + outconn net.Conn + outconnLocalAddr string + isClosedErr = func(err error) bool { + return err != nil && strings.Contains(err.Error(), "use of closed network connection") + } + destAddr *net.UDPAddr + ) + var clean = func(msg, err string) { + raddr := "" + if outUDPConn != nil { + raddr = outUDPConn.RemoteAddr().String() + outUDPConn.Close() + } + if msg != "" { + if raddr != "" { + s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr) + } else { + s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr) + } + } + (*inConn).Close() + udpListener.Close() + s.userConns.Remove(inconnRemoteAddr) + if outconn != nil { + outconn.Close() + } + if outconnLocalAddr != "" { + s.userConns.Remove(outconnLocalAddr) + } + } + defer clean("", "") + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + (*inConn).SetReadDeadline(time.Time{}) + if _, err := (*inConn).Read(buf); err != nil { + clean("udp related tcp conn disconnected with read", err.Error()) + } + }() + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn write crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + for { + (*inConn).SetWriteDeadline(time.Now().Add(time.Second * 5)) + if _, err := (*inConn).Write([]byte{0x00}); err != nil { + clean("udp related tcp conn disconnected with write", err.Error()) + return + } + (*inConn).SetWriteDeadline(time.Time{}) + time.Sleep(time.Second * 5) + } + }() + useProxy := true + if *s.cfg.Parent != "" { + dstHost, _, _ := net.SplitHostPort(request.Addr()) + if utils.IsIternalIP(dstHost, *s.cfg.Always) { + useProxy = false + } else { + var isInMap bool + useProxy, isInMap, _, _ = s.checker.IsBlocked(request.Addr()) + if !isInMap { + s.checker.Add(request.Addr(), s.Resolve(request.Addr())) + } + } + } else { + useProxy = false + } + if useProxy { + //parent proxy + outconn, err := s.getOutConn(nil, nil, "", false) + if err != nil { + clean("connnect fail", fmt.Sprintf("%s", err)) + return + } + client := socks.NewClientConn(&outconn, "udp", request.Addr(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil) + if err = client.Handshake(); err != nil { + clean("handshake fail", fmt.Sprintf("%s", err)) + return + } + //outconnRemoteAddr := outconn.RemoteAddr().String() + outconnLocalAddr = outconn.LocalAddr().String() + s.userConns.Set(outconnLocalAddr, &outconn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + outconn.SetReadDeadline(time.Time{}) + if _, err := outconn.Read(buf); err != nil { + clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err)) + } + }() + //forward to parent udp + //s.log.Printf("parent udp address %s", client.UDPAddr) + destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr) + } + s.log.Printf("use proxy %v : udp %s", useProxy, request.Addr()) + //relay + for { + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + n, srcAddr, err := udpListener.ReadFromUDP(buf) + if err != nil { + s.log.Printf("udp listener read fail, %s", err.Error()) + if isClosedErr(err) { + return + } + continue + } + srcIP0, _, _ := net.SplitHostPort(srcAddr.String()) + //IP not match drop it + if srcIP != srcIP0 { + continue + } + p := socks.NewPacketUDP() + //convert data to raw + if len(s.udpLocalKey) > 0 { + var v []byte + v, err = goaes.Decrypt(s.udpLocalKey, buf[:n]) + if err == nil { + err = p.Parse(v) + } + } else { + err = p.Parse(buf[:n]) + } + //err = p.Parse(buf[:n]) + if err != nil { + s.log.Printf("udp listener parse packet fail, %s", err.Error()) + continue + } + + port, _ := strconv.Atoi(p.Port()) + + if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok { + if destAddr == nil { + destAddr = &net.UDPAddr{IP: net.ParseIP(p.Host()), Port: port} + } + outUDPConn, err = net.DialUDP("udp", localAddr, destAddr) + if err != nil { + s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr) + continue + } + s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp out->local io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + defer s.udpRelatedPacketConns.Remove(srcAddr.String()) + //out->local io copy + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + for { + n, err := outUDPConn.Read(buf) + if err != nil { + s.log.Printf("read out udp data fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } + //var dlen = n + if useProxy { + //forward to local + var v []byte + //convert parent data to raw + if len(s.udpParentKey) > 0 { + v, err = goaes.Decrypt(s.udpParentKey, buf[:n]) + if err != nil { + s.log.Printf("udp outconn parse packet fail, %s", err.Error()) + continue + } + } else { + v = buf[:n] + } + //now v is raw, try convert v to local + if len(s.udpLocalKey) > 0 { + v, _ = goaes.Encrypt(s.udpLocalKey, v) + } + _, err = udpListener.WriteTo(v, srcAddr) + // _, err = udpListener.WriteTo(buf[:n], srcAddr) + } else { + rp := socks.NewPacketUDP() + rp.Build(destAddr.String(), buf[:n]) + v := rp.Bytes() + //dlen = len(v) + //rp.Bytes() v is raw, try convert to local + if len(s.udpLocalKey) > 0 { + v, _ = goaes.Encrypt(s.udpLocalKey, v) + } + _, err = udpListener.WriteTo(v, srcAddr) + } + + if err != nil { + s.udpRelatedPacketConns.Remove(srcAddr.String()) + s.log.Printf("write out data to local fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } else { + //s.log.Printf("send udp data to local success , len %d, for : %s", dlen, srcAddr) + } + } + }() + } else { + outUDPConn = v.(*net.UDPConn) + } + //local->out io copy + if useProxy { + //forward to parent + //p is raw, now convert it to parent + var v []byte + if len(s.udpParentKey) > 0 { + v, _ = goaes.Encrypt(s.udpParentKey, p.Bytes()) + } else { + v = p.Bytes() + } + _, err = outUDPConn.Write(v) + // _, err = outUDPConn.Write(p.Bytes()) + } else { + _, err = outUDPConn.Write(p.Data()) + } + if err != nil { + if isClosedErr(err) { + return + } + s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr) + continue + } else { + //s.log.Printf("send udp data to remote success , len %d, for : %s", len(p.Data()), srcAddr) + } + } + +} diff --git a/services/sps/socksudp.go b/services/sps/socksudp.go new file mode 100644 index 0000000..e59c7d9 --- /dev/null +++ b/services/sps/socksudp.go @@ -0,0 +1,306 @@ +package sps + +import ( + "crypto/md5" + "fmt" + "net" + "runtime/debug" + "strconv" + "strings" + "time" + + "github.com/snail007/goproxy/utils" + goaes "github.com/snail007/goproxy/utils/aes" + "github.com/snail007/goproxy/utils/conncrypt" + "github.com/snail007/goproxy/utils/socks" +) + +func (s *SPS) ParentUDPKey() (key []byte) { + switch *s.cfg.ParentType { + case "tcp": + if *s.cfg.ParentKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *SPS) LocalUDPKey() (key []byte) { + switch *s.cfg.LocalType { + case "tcp": + if *s.cfg.LocalKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *SPS) proxyUDP(inConn *net.Conn, serverConn *socks.ServerConn) { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + if *s.cfg.ParentType == "ssh" { + utils.CloseConn(inConn) + return + } + srcIP, _, _ := net.SplitHostPort((*inConn).RemoteAddr().String()) + inconnRemoteAddr := (*inConn).RemoteAddr().String() + localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} + udpListener := serverConn.UDPConnListener + s.log.Printf("proxy udp on %s , for %s", udpListener.LocalAddr(), inconnRemoteAddr) + s.userConns.Set(inconnRemoteAddr, inConn) + var ( + outUDPConn *net.UDPConn + outconn net.Conn + outconnLocalAddr string + isClosedErr = func(err error) bool { + return err != nil && strings.Contains(err.Error(), "use of closed network connection") + } + destAddr *net.UDPAddr + ) + var clean = func(msg, err string) { + raddr := "" + if outUDPConn != nil { + raddr = outUDPConn.RemoteAddr().String() + outUDPConn.Close() + } + if msg != "" { + if raddr != "" { + s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr) + } else { + s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr) + } + } + (*inConn).Close() + udpListener.Close() + s.userConns.Remove(inconnRemoteAddr) + if outconn != nil { + outconn.Close() + } + if outconnLocalAddr != "" { + s.userConns.Remove(outconnLocalAddr) + } + } + defer clean("", "") + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + (*inConn).SetReadDeadline(time.Time{}) + if _, err := (*inConn).Read(buf); err != nil { + clean("udp related tcp conn disconnected with read", err.Error()) + } + }() + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn write crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + for { + (*inConn).SetWriteDeadline(time.Now().Add(time.Second * 5)) + if _, err := (*inConn).Write([]byte{0x00}); err != nil { + clean("udp related tcp conn disconnected with write", err.Error()) + return + } + (*inConn).SetWriteDeadline(time.Time{}) + time.Sleep(time.Second * 5) + } + }() + //parent proxy + outconn, err := s.outPool.Get() + //outconn, err := s.GetParentConn(nil, nil, "", false) + if err != nil { + clean("connnect fail", fmt.Sprintf("%s", err)) + return + } + if *s.cfg.ParentCompress { + outconn = utils.NewCompConn(outconn) + } + if *s.cfg.ParentKey != "" { + outconn = conncrypt.New(outconn, &conncrypt.Config{ + Password: *s.cfg.ParentKey, + }) + } + + s.log.Printf("connect %s for udp", serverConn.Target()) + //socks client + var client *socks.ClientConn + auth := serverConn.AuthData() + if *s.cfg.ParentAuth != "" { + a := strings.Split(*s.cfg.ParentAuth, ":") + if len(a) != 2 { + err = fmt.Errorf("parent auth data format error") + return + } + client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), &socks.Auth{User: a[0], Password: a[1]}, nil) + } else { + if !s.IsBasicAuth() && auth.Password != "" && auth.User != "" { + client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), &auth, nil) + } else { + client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil) + } + } + + if err = client.Handshake(); err != nil { + clean("handshake fail", fmt.Sprintf("%s", err)) + return + } + + //outconnRemoteAddr := outconn.RemoteAddr().String() + outconnLocalAddr = outconn.LocalAddr().String() + s.userConns.Set(outconnLocalAddr, &outconn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + outconn.SetReadDeadline(time.Time{}) + if _, err := outconn.Read(buf); err != nil { + clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err)) + } + }() + //forward to parent udp + //s.log.Printf("parent udp address %s", client.UDPAddr) + destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr) + //relay + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + for { + n, srcAddr, err := udpListener.ReadFromUDP(buf) + if err != nil { + s.log.Printf("udp listener read fail, %s", err.Error()) + if isClosedErr(err) { + return + } + continue + } + srcIP0, _, _ := net.SplitHostPort(srcAddr.String()) + //IP not match drop it + if srcIP != srcIP0 { + continue + } + p := socks.NewPacketUDP() + //convert data to raw + if len(s.udpLocalKey) > 0 { + var v []byte + v, err = goaes.Decrypt(s.udpLocalKey, buf[:n]) + if err == nil { + err = p.Parse(v) + } + } else { + err = p.Parse(buf[:n]) + } + if err != nil { + s.log.Printf("udp listener parse packet fail, %s", err.Error()) + continue + } + + port, _ := strconv.Atoi(p.Port()) + + if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok { + if destAddr == nil { + destAddr = &net.UDPAddr{IP: net.ParseIP(p.Host()), Port: port} + } + outUDPConn, err = net.DialUDP("udp", localAddr, destAddr) + if err != nil { + s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr) + continue + } + s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp out->local io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + defer s.udpRelatedPacketConns.Remove(srcAddr.String()) + //out->local io copy + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + for { + outUDPConn.SetReadDeadline(time.Now().Add(time.Second * 5)) + n, err := outUDPConn.Read(buf) + outUDPConn.SetReadDeadline(time.Time{}) + if err != nil { + s.log.Printf("read out udp data fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } + //var dlen = n + //forward to local + var v []byte + //convert parent data to raw + if len(s.udpParentKey) > 0 { + v, err = goaes.Decrypt(s.udpParentKey, buf[:n]) + if err != nil { + s.log.Printf("udp outconn parse packet fail, %s", err.Error()) + continue + } + } else { + v = buf[:n] + } + //now v is raw, try convert v to local + if len(s.udpLocalKey) > 0 { + v, _ = goaes.Encrypt(s.udpLocalKey, v) + } + _, err = udpListener.WriteTo(v, srcAddr) + // _, err = udpListener.WriteTo(buf[:n], srcAddr) + + if err != nil { + s.udpRelatedPacketConns.Remove(srcAddr.String()) + s.log.Printf("write out data to local fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } else { + //s.log.Printf("send udp data to local success , len %d, for : %s", dlen, srcAddr) + } + } + }() + } else { + outUDPConn = v.(*net.UDPConn) + } + //local->out io copy + //forward to parent + //p is raw, now convert it to parent + var v []byte + if len(s.udpParentKey) > 0 { + v, _ = goaes.Encrypt(s.udpParentKey, p.Bytes()) + } else { + v = p.Bytes() + } + _, err = outUDPConn.Write(v) + // _, err = outUDPConn.Write(p.Bytes()) + if err != nil { + if isClosedErr(err) { + return + } + s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr) + continue + } else { + //s.log.Printf("send udp data to remote success , len %d, for : %s", len(p.Data()), srcAddr) + } + } + +} diff --git a/services/sps/sps.go b/services/sps/sps.go index 68591c7..8060987 100644 --- a/services/sps/sps.go +++ b/services/sps/sps.go @@ -17,6 +17,7 @@ import ( "github.com/snail007/goproxy/services/kcpcfg" "github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils/conncrypt" + "github.com/snail007/goproxy/utils/sni" "github.com/snail007/goproxy/utils/socks" ) @@ -52,22 +53,26 @@ type SPSArgs struct { DisableSocks5 *bool } type SPS struct { - outPool utils.OutConn - cfg SPSArgs - domainResolver utils.DomainResolver - basicAuth utils.BasicAuth - serverChannels []*utils.ServerChannel - userConns utils.ConcurrentMap - log *logger.Logger + outPool utils.OutConn + cfg SPSArgs + domainResolver utils.DomainResolver + basicAuth utils.BasicAuth + serverChannels []*utils.ServerChannel + userConns utils.ConcurrentMap + log *logger.Logger + udpRelatedPacketConns utils.ConcurrentMap + udpLocalKey []byte + udpParentKey []byte } func NewSPS() services.Service { return &SPS{ - outPool: utils.OutConn{}, - cfg: SPSArgs{}, - basicAuth: utils.BasicAuth{}, - serverChannels: []*utils.ServerChannel{}, - userConns: utils.NewConcurrentMap(), + outPool: utils.OutConn{}, + cfg: SPSArgs{}, + basicAuth: utils.BasicAuth{}, + serverChannels: []*utils.ServerChannel{}, + userConns: utils.NewConcurrentMap(), + udpRelatedPacketConns: utils.NewConcurrentMap(), } } func (s *SPS) CheckArgs() (err error) { @@ -92,6 +97,8 @@ func (s *SPS) CheckArgs() (err error) { } } } + s.udpLocalKey = s.LocalUDPKey() + s.udpParentKey = s.ParentUDPKey() return } func (s *SPS) InitService() (err error) { @@ -209,6 +216,11 @@ func (s *SPS) callback(inConn net.Conn) { } } func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { + enableUDP := *s.cfg.ParentServiceType == "socks" + udpIP, _, _ := net.SplitHostPort((*inConn).LocalAddr().String()) + if len(*s.cfg.LocalIPS) > 0 { + udpIP = (*s.cfg.LocalIPS)[0] + } bInConn := utils.NewBufferedConn(*inConn) //important //action read will regist read event to system, @@ -218,7 +230,7 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { bInConn.ReadByte() bInConn.UnreadByte() - n := 8 + n := 2048 if n > bInConn.Buffered() { n = bInConn.Buffered() } @@ -228,12 +240,12 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { (*inConn).Close() return } - + isSNI, _ := sni.ServerNameFromBytes(h) *inConn = bInConn address := "" var auth socks.Auth var forwardBytes []byte - //fmt.Printf("%v", header) + //fmt.Printf("%v", h) if utils.IsSocks5(h) { if *s.cfg.DisableSocks5 { (*inConn).Close() @@ -242,16 +254,20 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { //socks5 server var serverConn *socks.ServerConn if s.IsBasicAuth() { - serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), &s.basicAuth, "", nil) + serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), &s.basicAuth, enableUDP, udpIP, nil) } else { - serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, "", nil) + serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, enableUDP, udpIP, nil) } if err = serverConn.Handshake(); err != nil { return } address = serverConn.Target() auth = serverConn.AuthData() - } else if utils.IsHTTP(h) { + if serverConn.IsUDP() { + s.proxyUDP(inConn, serverConn) + return + } + } else if utils.IsHTTP(h) || isSNI != "" { if *s.cfg.DisableHTTP { (*inConn).Close() return @@ -495,6 +511,7 @@ func (s *SPS) Resolve(address string) string { ip, err := s.domainResolver.Resolve(address) if err != nil { s.log.Printf("dns error %s , ERR:%s", address, err) + return address } return ip } diff --git a/utils/cert/cert.go b/utils/cert/cert.go index 858f6e5..ac25ba5 100644 --- a/utils/cert/cert.go +++ b/utils/cert/cert.go @@ -73,7 +73,7 @@ func CreateSignCert(rootCa *x509.Certificate, rootKey *rsa.PrivateKey, domainOrI buf := x509.MarshalPKCS1PrivateKey(priKey) keyPem := &pem.Block{ - Type: "PRIVATE KEY", + Type: "RSA PRIVATE KEY", Bytes: buf, } keyBytes = pem.EncodeToMemory(keyPem) diff --git a/utils/conncrypt/conncrypt.go b/utils/conncrypt/conncrypt.go index f3115e7..82641e8 100644 --- a/utils/conncrypt/conncrypt.go +++ b/utils/conncrypt/conncrypt.go @@ -12,8 +12,8 @@ import ( ) //Confg defaults -const DefaultIterations = 2048 -const DefaultKeySize = 32 //256bits +const DefaultIterations = 1024 +const DefaultKeySize = 24 //256bits var DefaultHashFunc = sha256.New var DefaultSalt = []byte(` (;QUHj.BQ?RXzYSO]ifkXp/G!kFmWyXyEV6Nt!d|@bo+N$L9+ 1800 { + item.FailCount = 0 + item.SuccessCount = 0 + } if err != nil { item.FailCount = item.FailCount + 1 } else { item.SuccessCount = item.SuccessCount + 1 } - c.data.Set(item.Host, item) + item.Lasttime = now + c.data.Set(item.Domain, item) } }(v.(CheckerItem)) } @@ -124,32 +126,37 @@ func (c *Checker) start() { } func (c *Checker) isNeedCheck(item CheckerItem) bool { var minCount uint = 5 - if (item.SuccessCount >= minCount && item.SuccessCount > item.FailCount) || - (item.FailCount >= minCount && item.SuccessCount > item.FailCount) || - c.domainIsInMap(item.Host, false) || - c.domainIsInMap(item.Host, true) { + var now = time.Now().Unix() + if (item.SuccessCount >= minCount && item.SuccessCount > item.FailCount && now-item.Lasttime < 1800) || + (item.FailCount >= minCount && item.SuccessCount > item.FailCount && now-item.Lasttime < 1800) || + c.domainIsInMap(item.Domain, false) || + c.domainIsInMap(item.Domain, true) { return false } return true } -func (c *Checker) IsBlocked(address string) (blocked bool, failN, successN uint) { - if c.domainIsInMap(address, true) { - //log.Printf("%s in blocked ? true", address) - return true, 0, 0 +func (c *Checker) IsBlocked(domain string) (blocked, isInMap bool, failN, successN uint) { + h, _, _ := net.SplitHostPort(domain) + if h != "" { + domain = h } - if c.domainIsInMap(address, false) { + if c.domainIsInMap(domain, true) { + //log.Printf("%s in blocked ? true", address) + return true, true, 0, 0 + } + if c.domainIsInMap(domain, false) { //log.Printf("%s in direct ? true", address) - return false, 0, 0 + return false, true, 0, 0 } - _item, ok := c.data.Get(address) + _item, ok := c.data.Get(domain) if !ok { //log.Printf("%s not in map, blocked true", address) - return true, 0, 0 + return true, false, 0, 0 } item := _item.(CheckerItem) - return item.FailCount >= item.SuccessCount, item.FailCount, item.SuccessCount + return (item.FailCount >= item.SuccessCount) && (time.Now().Unix()-item.Lasttime < 1800), true, item.FailCount, item.SuccessCount } func (c *Checker) domainIsInMap(address string, blockedMap bool) bool { u, err := url.Parse("http://" + address) @@ -174,16 +181,20 @@ func (c *Checker) domainIsInMap(address string, blockedMap bool) bool { } return false } -func (c *Checker) Add(key, address string) { - if c.domainIsInMap(key, false) || c.domainIsInMap(key, true) { +func (c *Checker) Add(domain, address string) { + h, _, _ := net.SplitHostPort(domain) + if h != "" { + domain = h + } + if c.domainIsInMap(domain, false) || c.domainIsInMap(domain, true) { return } var item CheckerItem item = CheckerItem{ - Host: address, - Key: key, + Domain: domain, + Address: address, } - c.data.SetIfAbsent(item.Host, item) + c.data.SetIfAbsent(item.Domain, item) } type BasicAuth struct { @@ -329,6 +340,7 @@ type HTTPRequest struct { isBasicAuth bool basicAuth *BasicAuth log *logger.Logger + IsSNI bool } func NewHTTPRequest(inConn *net.Conn, bufSize int, isBasicAuth bool, basicAuth *BasicAuth, log *logger.Logger, header ...[]byte) (req HTTPRequest, err error) { @@ -360,6 +372,7 @@ func NewHTTPRequest(inConn *net.Conn, bufSize int, isBasicAuth bool, basicAuth * //sni success req.Method = "SNI" req.hostOrURL = "https://" + serverName + ":443" + req.IsSNI = true } else { //sni fail , try http index := bytes.IndexByte(req.HeadBuf, '\n')