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
[](https://github.com/snail007/goproxy/) []() [](https://github.com/snail007/goproxy/releases) [](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代理服务
[](https://github.com/snail007/goproxy/) []() [](https://github.com/snail007/goproxy/releases) [](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')