diff --git a/.gitignore b/.gitignore index e802753..2ec2aa7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ proxy *.exe *.exe~ .* +!.gitignore release-* proxy.crt proxy.key diff --git a/CHANGELOG b/CHANGELOG index 36e12be..bc7648b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,47 @@ proxy更新日志 +v4.7 +1.增加了基于gomobile的sdk,对安卓/IOS提供SDK支持. +2.优化了bridge的日志,增加了client和server的掉线日志. +3.优化了sps读取http(s)代理响应的缓冲大小,同时优化了CONNECT请求, + 避免了某些代理服务器返回过多数据导致不能正常通讯的问题. +4.去除了鸡肋连接池功能. +5.优化了所有服务代码,方便对sdk提供支持. +6.增加了SDK手册. +7.增加了GUI客户端(windows/web/android/ios)介绍主页. +8.SPS\HTTP(s)\Socks代理增加了自定义加密传输,只需要通过参数-z和-Z设置一个密码即可. +9.SPS\HTTP(s)\Socks代理增加了压缩传输,只需要通过参数-m和-M设置即可. +10.手册增加了SPS\HTTP(s)\Socks自定义加密的使用示例. +11.手册增加了SPS\HTTP(s)\Socks压缩传输的使用示例. +12.优化了多链接版本的内网穿透,融合了多链接和smux的优点,即能够拥有大的吞吐量, + 同时又具备mux的心跳机制保证了链接的稳定性. +13.手册增加了大量配图. + +v4.6 +1.sps,http(s),socks5,内网穿透都做了大量的超时优化处理,更加稳定. +2.sps增加了强大的树形级联认证支持,可以轻松构建你的认证代理网络. +3.手册增加了6.6对sps认证功能的介绍. + + +v4.5 +1.优化了mux内网穿透连接管理逻辑,增强了稳定性. +2.mux内网穿透增加了tcp和kcp协议支持,之前是tls,现在支持三种协议tcp,tls,kcp. +3.keygen参数增加了用法: proxy keygen usage. +4.http(s)/socks5代理,tls增加了自签名证书支持. +5.建议升级. +v4.4 +1.增加了协议转换sps功能,代理协议转换使用的是sps子命令(socks+https的缩写), +sps本身不提供代理功能,只是接受代理请求"转换并转发"给已经存在的http(s)代理 +或者socks5代理;sps可以把已经存在的http(s)代理或者socks5代理转换为一个端口 +同时支持http(s)和socks5代理,而且http(s)代理支持正向代理和反向代理(SNI),转 +换后的SOCKS5代理不支持UDP功能;另外对于已经存在的http(s)代理或者socks5代理, +支持tls、tcp、kcp三种模式,支持链式连接,也就是可以多个sps结点层级连接构建 +加密通道。 +2.增加了对KCP传输参数的配置,多达17个参数可以自由的配置对kcp传输效率调优。 +3.内网穿透功能,server和client增加了--session-count参数,可以设置server每个 +监听端口到bridge打开的session数量,可以设置client到bridge打开的session数量, +之前都是1个,现在性能提升N倍,N就是你自己设置的--session-count,这个参数很大 +程度上解决了多路复用的拥塞问题,v4.4开始默认10个。 + v4.3 1.优化了参数keygen生成证书逻辑,避免证书出现特征。 2.http(s)和socks代理增加了--dns-address和--dns-ttl参数。 diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 223984c..fae3375 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,41 +1,80 @@ { "ImportPath": "snail007/proxy", - "GoVersion": "go1.8", - "GodepVersion": "v79", + "GoVersion": "go1.9", + "GodepVersion": "v80", "Packages": [ "./..." ], "Deps": [ - { - "ImportPath": "github.com/alecthomas/template", - "Rev": "a0175ee3bccc567396460bf5acd36800cb10c49c" - }, - { - "ImportPath": "github.com/alecthomas/template/parse", - "Rev": "a0175ee3bccc567396460bf5acd36800cb10c49c" - }, - { - "ImportPath": "github.com/alecthomas/units", - "Rev": "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" - }, { "ImportPath": "github.com/golang/snappy", "Rev": "553a641470496b2327abcac10b36396bd98e45c9" }, { "ImportPath": "github.com/miekg/dns", - "Comment": "v1.0.4", - "Rev": "5364553f1ee9cddc7ac8b62dce148309c386695b" + "Comment": "v1.0.4-1-g40b5202", + "Rev": "40b520211179dbf7eaafaa7fe1ffaa1b7d929ee0" + }, + { + "ImportPath": "github.com/xtaci/kcp-go", + "Comment": "v3.19-6-g21da33a", + "Rev": "21da33a6696d67c1bffb3c954366499d613097a6" + }, + { + "ImportPath": "github.com/xtaci/smux", + "Comment": "v1.0.6", + "Rev": "ebec7ef2574b42a7088cd7751176483e0a27d458" + }, + { + "ImportPath": "golang.org/x/crypto/pbkdf2", + "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" + }, + { + "ImportPath": "golang.org/x/crypto/ssh", + "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" + }, + { + "ImportPath": "golang.org/x/time/rate", + "Rev": "6dc17368e09b0e8634d71cac8168d853e869a0c7" + }, + { + "ImportPath": "gopkg.in/alecthomas/kingpin.v2", + "Comment": "v2.2.5", + "Rev": "1087e65c9441605df944fb12c33f0fe7072d18ca" + }, + { + "ImportPath": "golang.org/x/crypto/ed25519", + "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" + }, + { + "ImportPath": "golang.org/x/net/ipv4", + "Rev": "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" + }, + { + "ImportPath": "golang.org/x/net/ipv6", + "Rev": "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" + }, + { + "ImportPath": "golang.org/x/crypto/ed25519/internal/edwards25519", + "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" + }, + { + "ImportPath": "golang.org/x/net/bpf", + "Rev": "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" + }, + { + "ImportPath": "golang.org/x/net/internal/iana", + "Rev": "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" + }, + { + "ImportPath": "golang.org/x/net/internal/socket", + "Rev": "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" }, { "ImportPath": "github.com/pkg/errors", "Comment": "v0.8.0-6-g602255c", "Rev": "602255cdb6deaf1523ea53ac30eae5554ba7bee9" }, - { - "ImportPath": "github.com/templexxx/cpufeat", - "Rev": "3794dfbfb04749f896b521032f69383f24c3687e" - }, { "ImportPath": "github.com/templexxx/reedsolomon", "Comment": "0.1.1-4-g7092926", @@ -51,16 +90,6 @@ "Comment": "v1.0.1-3-g9d99fac", "Rev": "9d99face20b0dd300b7db50b3f69758de41c096a" }, - { - "ImportPath": "github.com/xtaci/kcp-go", - "Comment": "v3.19-6-g21da33a", - "Rev": "21da33a6696d67c1bffb3c954366499d613097a6" - }, - { - "ImportPath": "github.com/xtaci/smux", - "Comment": "v1.0.6", - "Rev": "ebec7ef2574b42a7088cd7751176483e0a27d458" - }, { "ImportPath": "golang.org/x/crypto/blowfish", "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" @@ -69,34 +98,10 @@ "ImportPath": "golang.org/x/crypto/cast5", "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" }, - { - "ImportPath": "golang.org/x/crypto/curve25519", - "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" - }, - { - "ImportPath": "golang.org/x/crypto/ed25519", - "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" - }, - { - "ImportPath": "golang.org/x/crypto/ed25519/internal/edwards25519", - "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" - }, - { - "ImportPath": "golang.org/x/crypto/pbkdf2", - "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" - }, { "ImportPath": "golang.org/x/crypto/salsa20", "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" }, - { - "ImportPath": "golang.org/x/crypto/salsa20/salsa", - "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" - }, - { - "ImportPath": "golang.org/x/crypto/ssh", - "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" - }, { "ImportPath": "golang.org/x/crypto/tea", "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" @@ -110,33 +115,28 @@ "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" }, { - "ImportPath": "golang.org/x/net/bpf", - "Rev": "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" + "ImportPath": "github.com/templexxx/cpufeat", + "Rev": "3794dfbfb04749f896b521032f69383f24c3687e" }, { - "ImportPath": "golang.org/x/net/internal/iana", - "Rev": "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" + "ImportPath": "golang.org/x/crypto/salsa20/salsa", + "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" }, { - "ImportPath": "golang.org/x/net/internal/socket", - "Rev": "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" + "ImportPath": "golang.org/x/crypto/curve25519", + "Rev": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" }, { - "ImportPath": "golang.org/x/net/ipv4", - "Rev": "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" + "ImportPath": "github.com/alecthomas/template", + "Rev": "a0175ee3bccc567396460bf5acd36800cb10c49c" }, { - "ImportPath": "golang.org/x/net/ipv6", - "Rev": "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" + "ImportPath": "github.com/alecthomas/units", + "Rev": "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" }, { - "ImportPath": "golang.org/x/time/rate", - "Rev": "6dc17368e09b0e8634d71cac8168d853e869a0c7" - }, - { - "ImportPath": "gopkg.in/alecthomas/kingpin.v2", - "Comment": "v2.2.5", - "Rev": "1087e65c9441605df944fb12c33f0fe7072d18ca" + "ImportPath": "github.com/alecthomas/template/parse", + "Rev": "a0175ee3bccc567396460bf5acd36800cb10c49c" } ] } diff --git a/README.md b/README.md index ef9667e..fa69f98 100644 --- a/README.md +++ b/README.md @@ -298,13 +298,13 @@ Local HTTP (S) proxy use 28080 port,excute: #### **1.8.KCP protocol transmission** ![1.8](/docs/images/http-kcp.png) -The KCP protocol requires a -B parameter to set a password which can encrypt and decrypt data. +The KCP protocol requires a --kcp-key parameter to set a password which can encrypt and decrypt data. Http first level proxy(VPS,IP:22.22.22.22) -`./proxy http -t kcp -p ":38080" -B mypassword` +`./proxy http -t kcp -p ":38080" --kcp-key mypassword` Http second level proxy(os is Linux) -`./proxy http -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" -B mypassword` +`./proxy http -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" --kcp-key mypassword` Then access to the local 8080 port is access to the proxy's port 38080 on the VPS, and the data is transmitted through the KCP protocol. #### **1.9.HTTP reverse proxy** ![1.9](/docs/images/fxdl.png) @@ -729,13 +729,13 @@ ip: user's IP, for example: 192.168.1.200 If there is no -a or -F or --auth-url parameters, it means to turn off the authentication. #### **5.8.KCP protocol transmission** -The KCP protocol requires a -B parameter which can set a password to encrypt and decrypt data.   +The KCP protocol requires a --kcp-key parameter which can set a password to encrypt and decrypt data.   HTTP first level proxy(VPS,IP:22.22.22.22) -`./proxy socks -t kcp -p ":38080" -B mypassword` +`./proxy socks -t kcp -p ":38080" --kcp-key mypassword` HTTP two level proxy(local os is Linux) -`./proxy socks -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" -B mypassword` +`./proxy socks -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" --kcp-key mypassword` Then access to the local 8080 port is access to the proxy port 38080 on the VPS, and the data is transmitted through the KCP protocol. #### **5.9.Custom DNS** @@ -805,7 +805,7 @@ command: Suppose there is a KCP HTTP (s) proxy (password: demo123): 127.0.0.1:8080. Now we turn it into a common proxy that supports HTTP (s) and Socks5 at the same time. The local port after transformation is 18080. command: -`./proxy sps -S http -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 -B demo123` +`./proxy sps -S http -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123` #### **6.3.SOCKS5 to HTTP(S) + SOCKS5** Suppose there is a common Socks5 proxy: 127.0.0.1:8080, now we turn it into a common proxy that supports HTTP (s) and Socks5 at the same time, and the local port after transformation is 18080. @@ -818,7 +818,7 @@ command: Suppose there is a KCP Socks5 proxy (password: demo123): 127.0.0.1:8080, now we turn it into a common proxy that support HTTP (s) and Socks5 at the same time, and the local port after transformation is 18080. command: -`./proxy sps -S socks -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 -B demo123`   +`./proxy sps -S socks -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123`   #### **6.4.Chain style connection** ![6.4](/docs/images/sps-tls.png) diff --git a/README_ZH.md b/README_ZH.md index f1055f0..21b7f82 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -23,7 +23,9 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 - 集成外部API,HTTP(S),SOCKS5代理认证功能可以与外部HTTP API集成,可以方便的通过外部系统控制代理用户. - 反向代理,支持直接把域名解析到proxy监听的ip,然后proxy就会帮你代理访问需要访问的HTTP(S)网站. - 透明HTTP(S)代理,配合iptables,在网关直接把出去的80,443方向的流量转发到proxy,就能实现无感知的智能路由器代理. -- 协议转换,可以把已经存在的HTTP(S)或SOCKS5代理转换为一个端口同时支持HTTP(S)和SOCKS5代理,转换后的SOCKS5代理不支持UDP功能。 +- 协议转换,可以把已经存在的HTTP(S)或SOCKS5代理转换为一个端口同时支持HTTP(S)和SOCKS5代理,转换后的SOCKS5代理不支持UDP功能,同时支持强大的级联认证功能。 +- 自定义底层加密传输,http(s)\sps\socks代理在tcp之上可以通过tls标准加密以及kcp协议加密tcp数据,除此之外还支持在tls和kcp之后进行自定义加密,也就是说自定义加密和tls|kcp是可以联合使用的,内部采用AES256加密,使用的时候只需要自己定义一个密码即可。 +- 底层压缩高效传输,http(s)\sps\socks代理在tcp之上可以通过自定义加密和tls标准加密以及kcp协议加密tcp数据,在加密之后还可以对数据进行压缩,也就是说压缩功能和自定义加密和tls|kcp是可以联合使用的。 ### Why need these? - 当由于某某原因,我们不能访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道访问我们的服务. @@ -35,7 +37,10 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 - ... -本页是v4.4手册,其他版本手册请点击下面链接查看. +本页是v4.7手册,其他版本手册请点击下面链接查看. +- [v4.6手册](https://github.com/snail007/goproxy/tree/v4.6) +- [v4.5手册](https://github.com/snail007/goproxy/tree/v4.5) +- [v4.4手册](https://github.com/snail007/goproxy/tree/v4.4) - [v4.3手册](https://github.com/snail007/goproxy/tree/v4.3) - [v4.2手册](https://github.com/snail007/goproxy/tree/v4.2) - [v4.0-v4.1手册](https://github.com/snail007/goproxy/tree/v4.1) @@ -82,7 +87,9 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 - [1.9 HTTP(S)反向代理](#19-https反向代理) - [1.10 HTTP(S)透明代理](#110-https透明代理) - [1.11 自定义DNS](#111-自定义dns) - - [1.12 查看帮助](#112-查看帮助) + - [1.12 自定义加密](#112-自定义加密) + - [1.13 压缩传输](#113-压缩传输) + - [1.14 查看帮助](#114-查看帮助) - [2. TCP代理](#2tcp代理) - [2.1 普通一级TCP代理](#21普通一级tcp代理) - [2.2 普通二级TCP代理](#22普通二级tcp代理) @@ -118,14 +125,22 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 - [5.7 认证](#57认证) - [5.8 KCP协议传输](#58kcp协议传输) - [5.9 自定义DNS](#59自定义dns) - - [5.10 查看帮助](#510查看帮助) + - [5.10 自定义加密](#510-自定义加密) + - [5.11 压缩传输](#511-压缩传输) + - [5.12 查看帮助](#512查看帮助) - [6. 代理协议转换](#6代理协议转换) - [6.1 功能介绍](#61-功能介绍) - [6.2 HTTP(S)转HTTP(S)+SOCKS5](#62-https转httpssocks5) - [6.3 SOCKS5转HTTP(S)+SOCKS5](#63-socks5转httpssocks5) - [6.4 链式连接](#64-链式连接) - [6.5 监听多个端口](#65-监听多个端口) - - [6.6 查看帮助](#66-查看帮助) + - [6.6 认证功能](#66-认证功能) + - [6.7 自定义加密](#67-自定义加密) + - [6.8 压缩传输](#68-压缩传输) + - [6.9 查看帮助](#69-查看帮助) +- [7. KCP配置](#7kcp配置) + - [7.1 配置介绍](#71-配置介绍) + - [7.2 详细配置](#72-详细配置) ### Fast Start 提示:所有操作需要root权限. @@ -143,7 +158,7 @@ curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.s 下载地址:https://github.com/snail007/goproxy/releases ```shell cd /root/proxy/ -wget https://github.com/snail007/goproxy/releases/download/v4.4/proxy-linux-amd64.tar.gz +wget https://github.com/snail007/goproxy/releases/download/v4.7/proxy-linux-amd64.tar.gz ``` #### **2.下载自动安装脚本** ```shell @@ -184,7 +199,8 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯, 在linux上并安装了openssl命令,可以直接通过下面的命令生成证书和key文件. `./proxy keygen` 默认会在当前程序目录下面生成证书文件proxy.crt和key文件proxy.key。 - +更多用法:`proxy keygen usage`。 + ### **后台运行** 默认执行proxy之后,如果要保持proxy运行,不能关闭命令行. 如果想在后台运行proxy,命令行可以关闭,只需要在命令最后加上--daemon参数即可. @@ -204,20 +220,19 @@ proxy会fork子进程,然后监控子进程,如果子进程异常退出,5秒后 `./proxy http -g "23.23.23.23"` ### **1.HTTP代理** -#### **1.1.普通HTTP代理** -![1.1](/docs/images/1.1.jpg) +#### **1.1.普通一级HTTP代理** +![1.1](/docs/images/http-1.png) `./proxy http -t tcp -p "0.0.0.0:38080"` #### **1.2.普通二级HTTP代理** +![1.2](/docs/images/http-2.png) 使用本地端口8090,假设上级HTTP代理是`22.22.22.22:8080` `./proxy http -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" ` -默认关闭了连接池,如果要加快访问速度,-L可以开启连接池,10就是连接池大小,0为关闭, -开启连接池在网络不好的情况下,稳定不是很好. -`./proxy http -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -L 10` 我们还可以指定网站域名的黑白名单文件,一行一个域名,匹配规则是最右匹配,比如:baidu.com,匹配的是*.*.baidu.com,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理. `./proxy http -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt` #### **1.3.HTTP二级代理(加密)** +![1.3](/docs/images/http-tls-2.png) 一级HTTP代理(VPS,IP:22.22.22.22) `./proxy http -t tls -p ":38080" -C proxy.crt -K proxy.key` @@ -230,6 +245,7 @@ proxy会fork子进程,然后监控子进程,如果子进程异常退出,5秒后 然后设置你的windos系统中,需要通过代理上网的程序的代理为http模式,地址为:127.0.0.1,端口为:8080,程序即可通过加密通道通过vps上网。 #### **1.4.HTTP三级代理(加密)** +![1.3](/docs/images/http-tls-3.png) 一级HTTP代理VPS_01,IP:22.22.22.22 `./proxy http -t tls -p ":38080" -C proxy.crt -K proxy.key` 二级HTTP代理VPS_02,IP:33.33.33.33 @@ -265,6 +281,7 @@ target:用户访问的URL,比如:http://demo.com:80/1.html或https://www.baidu.c `./proxy http --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key` #### **1.7.HTTP(S)通过SSH中转** +![1.7](/docs/images/http-ssh-1.png) 说明:ssh中转的原理是利用了ssh的转发功能,就是你连接上ssh之后,可以通过ssh代理访问目标地址. 假设有:vps - IP是2.2.2.2, ssh端口是22, ssh用户名是:user, ssh用户密码是:demo @@ -278,16 +295,18 @@ target:用户访问的URL,比如:http://demo.com:80/1.html或https://www.baidu.c `./proxy http -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"` #### **1.8.KCP协议传输** -KCP协议需要-B参数设置一个密码用于加密解密数据 +![1.8](/docs/images/http-kcp.png) +KCP协议需要--kcp-key参数设置一个密码用于加密解密数据 一级HTTP代理(VPS,IP:22.22.22.22) -`./proxy http -t kcp -p ":38080" -B mypassword` +`./proxy http -t kcp -p ":38080" --kcp-key mypassword` 二级HTTP代理(本地Linux) -`./proxy http -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" -B mypassword` +`./proxy http -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" --kcp-key mypassword` 那么访问本地的8080端口就是访问VPS上面的代理端口38080,数据通过kcp协议传输. -#### **1.9 HTTP(S)反向代理** +#### **1.9 HTTP(S)反向代理** +![1.9](/docs/images/fxdl.png) proxy不仅支持在其他软件里面通过设置代理的方式,为其他软件提供代理服务,而且支持直接把请求的网站域名解析到proxy监听的ip上,然后proxy监听80和443端口,那么proxy就会自动为你代理访问需要访问的HTTP(S)网站. 使用方式: @@ -300,7 +319,7 @@ proxy不仅支持在其他软件里面通过设置代理的方式,为其他软 `./proxy http -t tcp -p :80,:443 -T tls -P "2.2.2.2:33080" -C proxy.crt -K proxy.key` 注意: -proxy所在的服务器的DNS解析结果不能受到自定义的解析影响,不然就死循环了. +proxy所在的服务器的DNS解析结果不能受到自定义的解析影响,不然就死循环了,proxy代理最好指定`--dns 8.8.8.8`参数. #### **1.10 HTTP(S)透明代理** 该模式需要具有一定的网络基础,相关概念不懂的请自行搜索解决. @@ -353,19 +372,65 @@ iptables -t nat -A OUTPUT -p tcp -j PROXY 比如: `./proxy http -p ":33080" --dns-address "8.8.8.8:53" --dns-ttl 300` -#### **1.12 查看帮助** +#### **1.12 自定义加密** +proxy的http(s)代理在tcp之上可以通过tls标准加密以及kcp协议加密tcp数据,除此之外还支持在tls和kcp之后进行自定义 +加密,也就是说自定义加密和tls|kcp是可以联合使用的,内部采用AES256加密,使用的时候只需要自己定义一个密码即可, +加密分为两个部分,一部分是本地(-z)是否加密解密,一部分是与上级(-Z)传输是否加密解密. +自定义加密要求两端都是proxy才可以,下面分别用二级,三级为例: + +**二级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy http -t tcp -z demo_password -p :7777` +本地二级执行: +`proxy http -T tcp -P 2.2.2.2:777 -Z demo_password -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级加密传输访问目标网站. + + +**三级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy http -t tcp -z demo_password -p :7777` +二级vps(ip:3.3.3.3)上执行: +`proxy http -T tcp -P 2.2.2.2:7777 -Z demo_password -t tcp -z other_password -p :8888` +本地三级执行: +`proxy http -T tcp -P 3.3.3.3:8888 -Z other_password -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级加密传输访问目标网站. + +#### **1.13 压缩传输** +proxy的http(s)代理在tcp之上可以通过tls标准加密以及kcp协议加密tcp数据,在自定义加密之前还可以对数据进行压缩, +也就是说压缩功能和自定义加密和tls|kcp是可以联合使用的,压缩分为两个部分,一部分是本地(-m)是否压缩传输, +一部分是与上级(-M)传输是否压缩. +压缩要求两端都是proxy才可以,压缩也在一定程度上保护了(加密)数据,下面分别用二级,三级为例: + +**二级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy http -t tcp -m -p :7777` +本地二级执行: +`proxy http -T tcp -P 2.2.2.2:777 -M -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站. + + +**三级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy http -t tcp -m -p :7777` +二级vps(ip:3.3.3.3)上执行: +`proxy http -T tcp -P 2.2.2.2:7777 -M -t tcp -m -p :8888` +本地三级执行: +`proxy http -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站. + +#### **1.14 查看帮助** `./proxy help http` ### **2.TCP代理** #### **2.1.普通一级TCP代理** -![2.1](/docs/images/2.1.png) +![2.1](/docs/images/tcp-1.png) 本地执行: `./proxy tcp -p ":33080" -T tcp -P "192.168.22.33:22"` 那么访问本地33080端口就是访问192.168.22.33的22端口. #### **2.2.普通二级TCP代理** -![2.2](/docs/images/2.2.png) +![2.2](/docs/images/tcp-2.png) VPS(IP:22.22.22.33)执行: `./proxy tcp -p ":33080" -T tcp -P "127.0.0.1:8080"` 本地执行: @@ -373,6 +438,7 @@ VPS(IP:22.22.22.33)执行: 那么访问本地23080端口就是访问22.22.22.33的8080端口. #### **2.3.普通三级TCP代理** +![2.3](/docs/images/tcp-3.png) 一级TCP代理VPS_01,IP:22.22.22.22 `./proxy tcp -p ":38080" -T tcp -P "66.66.66.66:8080"` 二级TCP代理VPS_02,IP:33.33.33.33 @@ -382,17 +448,19 @@ VPS(IP:22.22.22.33)执行: 那么访问本地8080端口就是通过加密TCP隧道访问66.66.66.66的8080端口. #### **2.4.加密二级TCP代理** +![2.4](/docs/images/tcp-tls-2.png) VPS(IP:22.22.22.33)执行: -`./proxy tcp -t tcp -p ":33080" -T tcp -P "127.0.0.1:8080" -C proxy.crt -K proxy.key` +`./proxy tcp -t tls -p ":33080" -T tcp -P "127.0.0.1:8080" -C proxy.crt -K proxy.key` 本地执行: `./proxy tcp -p ":23080" -T tls -P "22.22.22.33:33080" -C proxy.crt -K proxy.key` 那么访问本地23080端口就是通过加密TCP隧道访问22.22.22.33的8080端口. #### **2.5.加密三级TCP代理** +![2.5](/docs/images/tcp-tls-3.png) 一级TCP代理VPS_01,IP:22.22.22.22 -`./proxy tcp -t tcp -p ":38080" -T tcp -P "66.66.66.66:8080" -C proxy.crt -K proxy.key` +`./proxy tcp -t tls -p ":38080" -T tcp -P "66.66.66.66:8080" -C proxy.crt -K proxy.key` 二级TCP代理VPS_02,IP:33.33.33.33 -`./proxy tcp -t tcp -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key` +`./proxy tcp -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key` 三级TCP代理(本地) `./proxy tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key` 那么访问本地8080端口就是通过加密TCP隧道访问66.66.66.66的8080端口. @@ -403,11 +471,13 @@ VPS(IP:22.22.22.33)执行: ### **3.UDP代理** #### **3.1.普通一级UDP代理** +![3.1](/docs/images/udp-1.png) 本地执行: `./proxy udp -p ":5353" -T udp -P "8.8.8.8:53"` 那么访问本地UDP:5353端口就是访问8.8.8.8的UDP:53端口. #### **3.2.普通二级UDP代理** +![3.2](/docs/images/udp-2.png) VPS(IP:22.22.22.33)执行: `./proxy tcp -p ":33080" -T udp -P "8.8.8.8:53"` 本地执行: @@ -415,6 +485,7 @@ VPS(IP:22.22.22.33)执行: 那么访问本地UDP:5353端口就是通过TCP隧道,通过VPS访问8.8.8.8的UDP:53端口. #### **3.3.普通三级UDP代理** +![3.3](/docs/images/udp-3.png) 一级TCP代理VPS_01,IP:22.22.22.22 `./proxy tcp -p ":38080" -T udp -P "8.8.8.8:53"` 二级TCP代理VPS_02,IP:33.33.33.33 @@ -424,17 +495,19 @@ VPS(IP:22.22.22.33)执行: 那么访问本地5353端口就是通过TCP隧道,通过VPS访问8.8.8.8的53端口. #### **3.4.加密二级UDP代理** +![3.4](/docs/images/udp-tls-2.png) VPS(IP:22.22.22.33)执行: -`./proxy tcp -t tcp -p ":33080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key` +`./proxy tcp -t tls -p ":33080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key` 本地执行: `./proxy udp -p ":5353" -T tls -P "22.22.22.33:33080" -C proxy.crt -K proxy.key` 那么访问本地UDP:5353端口就是通过加密TCP隧道,通过VPS访问8.8.8.8的UDP:53端口. #### **3.5.加密三级UDP代理** +![3.5](/docs/images/udp-tls-3.png) 一级TCP代理VPS_01,IP:22.22.22.22 -`./proxy tcp -t tcp -p ":38080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key` +`./proxy tcp -t tls -p ":38080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key` 二级TCP代理VPS_02,IP:33.33.33.33 -`./proxy tcp -t tcp -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key` +`./proxy tcp -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key` 三级TCP代理(本地) `./proxy udp -p ":5353" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key` 那么访问本地5353端口就是通过加密TCP隧道,通过VPS_01访问8.8.8.8的53端口. @@ -590,13 +663,14 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid `./proxy socks -t tcp -p "0.0.0.0:38080"` #### **5.2.普通二级SOCKS5代理** -![5.2](/docs/images/5.2.png) +![5.2](/docs/images/socks-2.png) 使用本地端口8090,假设上级SOCKS5代理是`22.22.22.22:8080` `./proxy socks -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" ` -我们还可以指定网站域名的黑白名单文件,一行一个域名,匹配规则是最右匹配,比如:baidu.com,匹配的是*.*.baidu.com,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理. +我们还可以指定网站域名的黑白名单文件,一行一个域名,匹配规则是最右匹配,比如:baidu.com,匹配的是*.*.baidu.com,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理;如果域名即在黑名单又在白名单中,那么黑名单起作用. `./proxy socks -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt` #### **5.3.SOCKS二级代理(加密)** +![5.3](/docs/images/socks-tls-2.png) 一级SOCKS代理(VPS,IP:22.22.22.22) `./proxy socks -t tls -p ":38080" -C proxy.crt -K proxy.key` @@ -609,6 +683,7 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid 然后设置你的windos系统中,需要通过代理上网的程序的代理为socks5模式,地址为:127.0.0.1,端口为:8080,程序即可通过加密通道通过vps上网。 #### **5.4.SOCKS三级代理(加密)** +![5.4](/docs/images/socks-tls-3.png) 一级SOCKS代理VPS_01,IP:22.22.22.22 `./proxy socks -t tls -p ":38080" -C proxy.crt -K proxy.key` 二级SOCKS代理VPS_02,IP:33.33.33.33 @@ -622,6 +697,7 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid `./proxy socks --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key` #### **5.6.SOCKS通过SSH中转** +![5.6](/docs/images/socks-ssh.png) 说明:ssh中转的原理是利用了ssh的转发功能,就是你连接上ssh之后,可以通过ssh代理访问目标地址. 假设有:vps - IP是2.2.2.2, ssh端口是22, ssh用户名是:user, ssh用户密码是:demo @@ -658,13 +734,13 @@ ip:用户的IP,比如:192.168.1.200 如果没有-a或-F或--auth-url参数,就是关闭认证. #### **5.8.KCP协议传输** -KCP协议需要-B参数设置一个密码用于加密解密数据 +KCP协议需要--kcp-key参数设置一个密码用于加密解密数据 一级HTTP代理(VPS,IP:22.22.22.22) -`./proxy socks -t kcp -p ":38080" -B mypassword` +`./proxy socks -t kcp -p ":38080" --kcp-key mypassword` 二级HTTP代理(本地Linux) -`./proxy socks -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" -B mypassword` +`./proxy socks -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" --kcp-key mypassword` 那么访问本地的8080端口就是访问VPS上面的代理端口38080,数据通过kcp协议传输. #### **5.9.自定义DNS** @@ -673,13 +749,64 @@ KCP协议需要-B参数设置一个密码用于加密解密数据 比如: `./proxy socks -p ":33080" --dns-address "8.8.8.8:53" --dns-ttl 300` -#### **5.10.查看帮助** +#### **5.10 自定义加密** +proxy的socks代理在tcp之上可以通过tls标准加密以及kcp协议加密tcp数据,除此之外还支持在tls和kcp之后进行自定义加密,也就是说自定义加密和tls|kcp是可以联合使用的,内部采用AES256加密,使用的时候只需要自己定义一个密码即可, +加密分为两个部分,一部分是本地(-z)是否加密解密,一部分是与上级(-Z)传输是否加密解密. + +自定义加密要求两端都是proxy才可以. + +下面分别用二级,三级为例: + +**二级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy socks -t tcp -z demo_password -p :7777` +本地二级执行: +`proxy socks -T tcp -P 2.2.2.2:777 -Z demo_password -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级加密传输访问目标网站. + + +**三级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy socks -t tcp -z demo_password -p :7777` +二级vps(ip:3.3.3.3)上执行: +`proxy socks -T tcp -P 2.2.2.2:7777 -Z demo_password -t tcp -z other_password -p :8888` +本地三级执行: +`proxy socks -T tcp -P 3.3.3.3:8888 -Z other_password -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级加密传输访问目标网站. + +#### **5.11 压缩传输** +proxy的socks代理在tcp之上可以通过自定义加密和tls标准加密以及kcp协议加密tcp数据,在自定义加密之前还可以 +对数据进行压缩,也就是说压缩功能和自定义加密和tls|kcp是可以联合使用的,压缩分为两个部分, +一部分是本地(-m)是否压缩传输,一部分是与上级(-M)传输是否压缩. + +压缩要求两端都是proxy才可以,压缩也在一定程度上保护了(加密)数据. + +下面分别用二级,三级为例: + +**二级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy socks -t tcp -m -p :7777` +本地二级执行: +`proxy socks -T tcp -P 2.2.2.2:777 -M -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站. + + +**三级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy socks -t tcp -m -p :7777` +二级vps(ip:3.3.3.3)上执行: +`proxy socks -T tcp -P 2.2.2.2:7777 -M -t tcp -m -p :8888` +本地三级执行: +`proxy socks -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站. + +#### **5.12.查看帮助** `./proxy help socks` ### **6.代理协议转换** #### **6.1 功能介绍** -代理协议转换使用的是sps子命令(socks+https的缩写),sps本身不提供代理功能,只是接受代理请求"转换并转发"给已经存在的http(s)代理或者socks5代理;sps可以把已经存在的http(s)代理或者socks5代理转换为一个端口同时支持http(s)和socks5代理,而且http(s)代理支持正向代理和反向代理(SNI),转换后的SOCKS5代理不支持UDP功能;另外对于已经存在的http(s)代理或者socks5代理,支持tls、tcp、kcp三种模式,支持链式连接,也就是可以多个sps结点层级连接构建加密通道;。 +代理协议转换使用的是sps子命令(socks+https的缩写),sps本身不提供代理功能,只是接受代理请求"转换并转发"给已经存在的http(s)代理或者socks5代理;sps可以把已经存在的http(s)代理或者socks5代理转换为一个端口同时支持http(s)和socks5代理,而且http(s)代理支持正向代理和反向代理(SNI),转换后的SOCKS5代理不支持UDP功能;另外对于已经存在的http(s)代理或者socks5代理,支持tls、tcp、kcp三种模式,支持链式连接,也就是可以多个sps结点层级连接构建加密通道。 #### **6.2 HTTP(S)转HTTP(S)+SOCKS5** 假设已经存在一个普通的http(s)代理:127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5的普通代理,转换后的本地端口为18080。 @@ -692,7 +819,7 @@ KCP协议需要-B参数设置一个密码用于加密解密数据 假设已经存在一个kcp的http(s)代理(密码是:demo123):127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5的普通代理,转换后的本地端口为18080。 命令如下: -`./proxy sps -S http -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 -B demo123` +`./proxy sps -S http -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123` #### **6.3 SOCKS5转HTTP(S)+SOCKS5** 假设已经存在一个普通的socks5代理:127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5的普通代理,转换后的本地端口为18080。 @@ -705,9 +832,10 @@ KCP协议需要-B参数设置一个密码用于加密解密数据 假设已经存在一个kcp的socks5代理(密码是:demo123):127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5的普通代理,转换后的本地端口为18080。 命令如下: -`./proxy sps -S socks -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 -B demo123` +`./proxy sps -S socks -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123` -#### **6.4 链式连接** +#### **6.4 链式连接** +![6.4](/docs/images/sps-tls.png) 上面提过多个sps结点可以层级连接构建加密通道,假设有如下vps和家里的pc电脑。 vps01:2.2.2.2 vps02:3.3.3.3 @@ -730,22 +858,157 @@ vps02:3.3.3.3 一般情况下监听一个端口就可以,不过如果作为反向代理需要同时监听80和443两个端口,那么-p参数是支持的, 格式是:`-p 0.0.0.0:80,0.0.0.0:443`,多个绑定用逗号分隔即可。 -#### **6.6 查看帮助** +#### **6.6 认证功能** +sps支持http(s)\socks5代理认证,可以级联认证,有四个重要的信息: +1:用户发送认证信息`user-auth`。 +2:设置的本地认证信息`local-auth`。 +3:设置的连接上级使用的认证信息`parent-auth`。 +4:最终发送给上级的认证信息`auth-info-to-parent`。 +他们的情况关系如下: + +| user-auth | local-auth | parent-auth | auth-info-to-paren +| ------ | ------ | ------ | ------ +| 有/没有 | 有 | 有 | 来自parent-auth +| 有/没有 | 没有 | 有 | 来自parent-auth +| 有/没有 | 有 | 没有 | 无 +| 没有 | 没有 | 没有 | 无 +| 有 | 没有 | 没有 | 来自user-auth + +对于sps代理我们可以进行用户名密码认证,认证的用户名和密码可以在命令行指定 +`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p ":33080" -a "user1:pass1" -a "user2:pass2"` +多个用户,重复-a参数即可. +也可以放在文件中,格式是一行一个"用户名:密码",然后用-F指定. +`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p ":33080" -F auth-file.txt` + +如果上级有认证,下级可以通过-A参数设置认证信息,比如: +上级:`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p ":33080" -a "user1:pass1" -a "user2:pass2"` +下级:`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -A "user1:pass1" -t tcp -p ":33080" ` + +另外,sps代理,本地认证集成了外部HTTP API认证,我们可以通过--auth-url参数指定一个http url接口地址, +然后有用户连接的时候,proxy会GET方式请求这url,带上下面四个参数,如果返回HTTP状态码204,代表认证成功 +其它情况认为认证失败. +比如: +`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p ":33080" --auth-url "http://test.com/auth.php"` +用户连接的时候,proxy会GET方式请求这url("http://test.com/auth.php"), +带上user,pass,ip,target四个参数: +http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}&target={TARGET} +user:用户名 +pass:密码 +ip:用户的IP,比如:192.168.1.200 +target:如果客户端是http(s)代理请求,这里代表的是请求的完整url,其它情况为空. + +如果没有-a或-F或--auth-url参数,就是关闭本地认证. +如果没有-A参数,连接上级不使用认证. + + +#### **6.7 自定义加密** +proxy的sps代理在tcp之上可以通过tls标准加密以及kcp协议加密tcp数据,除此之外还支持在tls和kcp之后进行 +自定义加密,也就是说自定义加密和tls|kcp是可以联合使用的,内部采用AES256加密,使用的时候只需要自己定义 +一个密码即可,加密分为两个部分,一部分是本地(-z)是否加密解密,一部分是与上级(-Z)传输是否加密解密. + +自定义加密要求两端都是proxy才可以. + +下面分别用二级,三级为例: + +假设已经存在一个http(s)代理:`6.6.6.6:6666` + +**二级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy sps -S http -T tcp -P 6.6.6.6:6666 -t tcp -z demo_password -p :7777` +本地二级执行: +`proxy sps -T tcp -P 2.2.2.2:777 -Z demo_password -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级加密传输访问目标网站. + + +**三级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy sps -S http -T tcp -P 6.6.6.6:6666 -t tcp -z demo_password -p :7777` +二级vps(ip:3.3.3.3)上执行: +`proxy sps -T tcp -P 2.2.2.2:7777 -Z demo_password -t tcp -z other_password -p :8888` +本地三级执行: +`proxy sps -T tcp -P 3.3.3.3:8888 -Z other_password -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级加密传输访问目标网站. + +#### **6.8 压缩传输** +proxy的sps代理在tcp之上可以通过自定义加密和tls标准加密以及kcp协议加密tcp数据,在自定义加密之前还可以 +对数据进行压缩,也就是说压缩功能和自定义加密和tls|kcp是可以联合使用的,压缩分为两个部分, +一部分是本地(-m)是否压缩传输,一部分是与上级(-M)传输是否压缩. + +压缩要求两端都是proxy才可以,压缩也在一定程度上保护了(加密)数据. + +下面分别用二级,三级为例: + +**二级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy sps -t tcp -m -p :7777` +本地二级执行: +`proxy sps -T tcp -P 2.2.2.2:777 -M -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站. + + +**三级实例** +一级vps(ip:2.2.2.2)上执行: +`proxy sps -t tcp -m -p :7777` +二级vps(ip:3.3.3.3)上执行: +`proxy sps -T tcp -P 2.2.2.2:7777 -M -t tcp -m -p :8888` +本地三级执行: +`proxy sps -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080` +这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站. + + +#### **6.9 查看帮助** `./proxy help sps` +### **7.KCP配置** + +#### **7.1 配置介绍** +proxy的很多功能都支持kcp协议,凡是使用了kcp协议的功能都支持这里介绍的配置参数。 +所以这里统一对KCP配置参数进行介绍。 + +#### **7.2 详细配置** +所有的KCP配置参数共有17个,你可以都不用设置,他们都有默认值,如果为了或者最好的效果, +就需要自己根据自己根据网络情况对参数进行配置。由于kcp配置很复杂需要一定的网络基础知识, +如果想获得kcp参数更详细的配置和解说,请自行搜索。每个参数的命令行名称以及默认值和简单的功能说明如下: +``` +--kcp-key="secrect" pre-shared secret between client and server +--kcp-method="aes" encrypt/decrypt method, can be: aes, aes-128, aes-192, salsa20, blowfish, + twofish, cast5, 3des, tea, xtea, xor, sm4, none +--kcp-mode="fast" profiles: fast3, fast2, fast, normal, manual +--kcp-mtu=1350 set maximum transmission unit for UDP packets +--kcp-sndwnd=1024 set send window size(num of packets) +--kcp-rcvwnd=1024 set receive window size(num of packets) +--kcp-ds=10 set reed-solomon erasure coding - datashard +--kcp-ps=3 set reed-solomon erasure coding - parityshard +--kcp-dscp=0 set DSCP(6bit) +--kcp-nocomp disable compression +--kcp-acknodelay be carefull! flush ack immediately when a packet is received +--kcp-nodelay=0 be carefull! +--kcp-interval=50 be carefull! +--kcp-resend=0 be carefull! +--kcp-nc=0 be carefull! no congestion +--kcp-sockbuf=4194304 be carefull! +--kcp-keepalive=10 be carefull! +``` +提示: +参数:--kcp-mode中的四种fast3, fast2, fast, normal模式, +相当于设置了下面四个参数: +normal:`--nodelay=0 --interval=40 --resend=2 --nc=1` +fast :`--nodelay=0 --interval=30 --resend=2 --nc=1` +fast2:`--nodelay=1 --interval=20 --resend=2 --nc=1` +fast3:`--nodelay=1 --interval=10 --resend=2 --nc=1` ### TODO - http,socks代理多个上级负载均衡? - http(s)代理增加pac支持? - 欢迎加群反馈... -### 如何使用源码? -建议go1.8.5,不保证>=1.9能用. -cd进入你的go src目录,新建文件夹snail007, -cd进入snail007,然后git clone https://github.com/snail007/goproxy.git ./proxy 即可. -编译直接:go build -运行: go run *.go -utils是工具包,service是具体的每个服务类. +### 如何使用源码? +建议go1.8.5,不保证>=1.9能用. +cd进入你的go src目录,新建文件夹snail007, +cd进入snail007,然后git clone https://github.com/snail007/goproxy.git ./proxy 即可. +编译直接:go build +运行: go run *.go +utils是工具包,service是具体的每个服务类. ### License Proxy is licensed under GPLv3 license. diff --git a/config.go b/config.go index 04be4b2..dcdfaea 100755 --- a/config.go +++ b/config.go @@ -2,14 +2,18 @@ package main import ( "bufio" + "crypto/sha1" "fmt" "log" "os" "os/exec" "snail007/proxy/services" + "snail007/proxy/services/kcpcfg" "snail007/proxy/utils" "time" + kcp "github.com/xtaci/kcp-go" + "golang.org/x/crypto/pbkdf2" kingpin "gopkg.in/alecthomas/kingpin.v2" ) @@ -40,6 +44,7 @@ func initConfig() (err error) { udpArgs := services.UDPArgs{} socksArgs := services.SocksArgs{} spsArgs := services.SPSArgs{} + kcpArgs := kcpcfg.KCPConfigArgs{} //build srvice args app = kingpin.New("proxy", "happy with proxy") app.Author("snail").Version(APP_VERSION) @@ -47,10 +52,28 @@ func initConfig() (err error) { daemon := app.Flag("daemon", "run proxy in background").Default("false").Bool() forever := app.Flag("forever", "run proxy in forever,fail and retry").Default("false").Bool() logfile := app.Flag("log", "log file path").Default("").String() + kcpArgs.Key = app.Flag("kcp-key", "pre-shared secret between client and server").Default("secrect").String() + kcpArgs.Crypt = app.Flag("kcp-method", "encrypt/decrypt method, can be: aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none").Default("aes").Enum("aes", "aes-128", "aes-192", "salsa20", "blowfish", "twofish", "cast5", "3des", "tea", "xtea", "xor", "sm4", "none") + kcpArgs.Mode = app.Flag("kcp-mode", "profiles: fast3, fast2, fast, normal, manual").Default("fast").Enum("fast3", "fast2", "fast", "normal", "manual") + kcpArgs.MTU = app.Flag("kcp-mtu", "set maximum transmission unit for UDP packets").Default("1350").Int() + kcpArgs.SndWnd = app.Flag("kcp-sndwnd", "set send window size(num of packets)").Default("1024").Int() + kcpArgs.RcvWnd = app.Flag("kcp-rcvwnd", "set receive window size(num of packets)").Default("1024").Int() + kcpArgs.DataShard = app.Flag("kcp-ds", "set reed-solomon erasure coding - datashard").Default("10").Int() + kcpArgs.ParityShard = app.Flag("kcp-ps", "set reed-solomon erasure coding - parityshard").Default("3").Int() + kcpArgs.DSCP = app.Flag("kcp-dscp", "set DSCP(6bit)").Default("0").Int() + kcpArgs.NoComp = app.Flag("kcp-nocomp", "disable compression").Default("false").Bool() + kcpArgs.AckNodelay = app.Flag("kcp-acknodelay", "be carefull! flush ack immediately when a packet is received").Default("true").Bool() + kcpArgs.NoDelay = app.Flag("kcp-nodelay", "be carefull!").Default("0").Int() + kcpArgs.Interval = app.Flag("kcp-interval", "be carefull!").Default("50").Int() + kcpArgs.Resend = app.Flag("kcp-resend", "be carefull!").Default("0").Int() + kcpArgs.NoCongestion = app.Flag("kcp-nc", "be carefull! no congestion").Default("0").Int() + kcpArgs.SockBuf = app.Flag("kcp-sockbuf", "be carefull!").Default("4194304").Int() + kcpArgs.KeepAlive = app.Flag("kcp-keepalive", "be carefull!").Default("10").Int() //########http######### http := app.Command("http", "proxy on http mode") httpArgs.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() + httpArgs.CaCertFile = http.Flag("ca", "ca cert file for tls").Default("").String() httpArgs.CertFile = http.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() httpArgs.KeyFile = http.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() httpArgs.LocalType = http.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") @@ -63,15 +86,12 @@ func initConfig() (err error) { httpArgs.Direct = http.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String() httpArgs.AuthFile = http.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String() httpArgs.Auth = http.Flag("auth", "http basic auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings() - httpArgs.PoolSize = http.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int() httpArgs.CheckParentInterval = http.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int() httpArgs.Local = http.Flag("local", "local ip:port to listen,multiple address use comma split,such as: 0.0.0.0:80,0.0.0.0:443").Short('p').Default(":33080").String() httpArgs.SSHUser = http.Flag("ssh-user", "user for ssh").Short('u').Default("").String() httpArgs.SSHKeyFile = http.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String() httpArgs.SSHKeyFileSalt = http.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String() httpArgs.SSHPassword = http.Flag("ssh-password", "password for ssh").Short('A').Default("").String() - httpArgs.KCPKey = http.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String() - httpArgs.KCPMethod = http.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String() httpArgs.LocalIPS = http.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings() httpArgs.AuthURL = http.Flag("auth-url", "http basic auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String() httpArgs.AuthURLTimeout = http.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int() @@ -79,6 +99,10 @@ func initConfig() (err error) { httpArgs.AuthURLRetry = http.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("1").Int() httpArgs.DNSAddress = http.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() httpArgs.DNSTTL = http.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int() + httpArgs.LocalKey = http.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String() + httpArgs.ParentKey = http.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String() + httpArgs.LocalCompress = http.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool() + httpArgs.ParentCompress = http.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool() //########tcp######### tcp := app.Command("tcp", "proxy on tcp mode") @@ -88,11 +112,8 @@ func initConfig() (err error) { tcpArgs.Timeout = tcp.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('e').Default("2000").Int() tcpArgs.ParentType = tcp.Flag("parent-type", "parent protocol type ").Short('T').Enum("tls", "tcp", "udp", "kcp") tcpArgs.LocalType = tcp.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") - tcpArgs.PoolSize = tcp.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int() tcpArgs.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int() tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() - tcpArgs.KCPKey = tcp.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String() - tcpArgs.KCPMethod = tcp.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String() //########udp######### udp := app.Command("udp", "proxy on udp mode") @@ -101,36 +122,40 @@ func initConfig() (err error) { udpArgs.KeyFile = udp.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() udpArgs.Timeout = udp.Flag("timeout", "tcp timeout milliseconds when connect to parent proxy").Short('t').Default("2000").Int() udpArgs.ParentType = udp.Flag("parent-type", "parent protocol type ").Short('T').Enum("tls", "tcp", "udp") - udpArgs.PoolSize = udp.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int() udpArgs.CheckParentInterval = udp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int() udpArgs.Local = udp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() //########mux-server######### muxServer := app.Command("server", "proxy on mux server mode") muxServerArgs.Parent = muxServer.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() + muxServerArgs.ParentType = muxServer.Flag("parent-type", "parent protocol type ").Default("tls").Short('T').Enum("tls", "tcp", "kcp") muxServerArgs.CertFile = muxServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() muxServerArgs.KeyFile = muxServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() - muxServerArgs.Timeout = muxServer.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int() + muxServerArgs.Timeout = muxServer.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int() muxServerArgs.IsUDP = muxServer.Flag("udp", "proxy on udp mux server mode").Default("false").Bool() muxServerArgs.Key = muxServer.Flag("k", "client key").Default("default").String() muxServerArgs.Route = muxServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings() - muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp mode").Default("false").Bool() + muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp|tls mode").Default("false").Bool() + muxServerArgs.SessionCount = muxServer.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int() //########mux-client######### muxClient := app.Command("client", "proxy on mux client mode") muxClientArgs.Parent = muxClient.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() + muxClientArgs.ParentType = muxClient.Flag("parent-type", "parent protocol type ").Default("tls").Short('T').Enum("tls", "tcp", "kcp") muxClientArgs.CertFile = muxClient.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() muxClientArgs.KeyFile = muxClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() - muxClientArgs.Timeout = muxClient.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int() + muxClientArgs.Timeout = muxClient.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int() muxClientArgs.Key = muxClient.Flag("k", "key same with server").Default("default").String() - muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp mode").Default("false").Bool() + muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp|tls mode").Default("false").Bool() + muxClientArgs.SessionCount = muxClient.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int() //########mux-bridge######### muxBridge := app.Command("bridge", "proxy on mux bridge mode") muxBridgeArgs.CertFile = muxBridge.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() muxBridgeArgs.KeyFile = muxBridge.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() - muxBridgeArgs.Timeout = muxBridge.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int() + muxBridgeArgs.Timeout = muxBridge.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int() muxBridgeArgs.Local = muxBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() + muxBridgeArgs.LocalType = muxBridge.Flag("local-type", "local protocol type ").Default("tls").Short('t').Enum("tls", "tcp", "kcp") //########tunnel-server######### tunnelServer := app.Command("tserver", "proxy on tunnel server mode") @@ -166,6 +191,7 @@ func initConfig() (err error) { 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() socksArgs.SSHUser = socks.Flag("ssh-user", "user for ssh").Short('u').Default("").String() socksArgs.SSHKeyFile = socks.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String() @@ -178,8 +204,6 @@ func initConfig() (err error) { socksArgs.Direct = socks.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String() socksArgs.AuthFile = socks.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String() socksArgs.Auth = socks.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings() - socksArgs.KCPKey = socks.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String() - socksArgs.KCPMethod = socks.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String() socksArgs.LocalIPS = socks.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings() socksArgs.AuthURL = socks.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String() socksArgs.AuthURLTimeout = socks.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int() @@ -187,23 +211,92 @@ func initConfig() (err error) { socksArgs.AuthURLRetry = socks.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int() socksArgs.DNSAddress = socks.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() socksArgs.DNSTTL = socks.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int() + socksArgs.LocalKey = socks.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String() + socksArgs.ParentKey = socks.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String() + socksArgs.LocalCompress = socks.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool() + socksArgs.ParentCompress = socks.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool() + //########socks+http(s)######### sps := app.Command("sps", "proxy on socks+http(s) mode") spsArgs.Parent = sps.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() spsArgs.CertFile = sps.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() spsArgs.KeyFile = sps.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() + spsArgs.CaCertFile = sps.Flag("ca", "ca cert file for tls").Default("").String() spsArgs.Timeout = sps.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('i').Default("2000").Int() spsArgs.ParentType = sps.Flag("parent-type", "parent protocol type ").Short('T').Enum("tls", "tcp", "kcp") spsArgs.LocalType = sps.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") spsArgs.Local = sps.Flag("local", "local ip:port to listen,multiple address use comma split,such as: 0.0.0.0:80,0.0.0.0:443").Short('p').Default(":33080").String() - spsArgs.KCPKey = sps.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String() - spsArgs.KCPMethod = sps.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String() spsArgs.ParentServiceType = sps.Flag("parent-service-type", "parent service type ").Short('S').Enum("http", "socks") spsArgs.DNSAddress = sps.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() spsArgs.DNSTTL = sps.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int() + spsArgs.AuthFile = sps.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String() + spsArgs.Auth = sps.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings() + spsArgs.LocalIPS = sps.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings() + spsArgs.AuthURL = sps.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String() + spsArgs.AuthURLTimeout = sps.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int() + spsArgs.AuthURLOkCode = sps.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int() + spsArgs.AuthURLRetry = sps.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int() + spsArgs.ParentAuth = sps.Flag("parent-auth", "parent socks auth username and password, such as: -A user1:pass1").Short('A').String() + spsArgs.LocalKey = sps.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String() + spsArgs.ParentKey = sps.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String() + spsArgs.LocalCompress = sps.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool() + spsArgs.ParentCompress = sps.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool() //parse args serviceName := kingpin.MustParse(app.Parse(os.Args[1:])) + + //set kcp config + + switch *kcpArgs.Mode { + case "normal": + *kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 0, 40, 2, 1 + case "fast": + *kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 0, 30, 2, 1 + case "fast2": + *kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 1, 20, 2, 1 + case "fast3": + *kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 1, 10, 2, 1 + } + pass := pbkdf2.Key([]byte(*kcpArgs.Key), []byte("snail007-goproxy"), 4096, 32, sha1.New) + + switch *kcpArgs.Crypt { + case "sm4": + kcpArgs.Block, _ = kcp.NewSM4BlockCrypt(pass[:16]) + case "tea": + kcpArgs.Block, _ = kcp.NewTEABlockCrypt(pass[:16]) + case "xor": + kcpArgs.Block, _ = kcp.NewSimpleXORBlockCrypt(pass) + case "none": + kcpArgs.Block, _ = kcp.NewNoneBlockCrypt(pass) + case "aes-128": + kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass[:16]) + case "aes-192": + kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass[:24]) + case "blowfish": + kcpArgs.Block, _ = kcp.NewBlowfishBlockCrypt(pass) + case "twofish": + kcpArgs.Block, _ = kcp.NewTwofishBlockCrypt(pass) + case "cast5": + kcpArgs.Block, _ = kcp.NewCast5BlockCrypt(pass[:16]) + case "3des": + kcpArgs.Block, _ = kcp.NewTripleDESBlockCrypt(pass[:24]) + case "xtea": + kcpArgs.Block, _ = kcp.NewXTEABlockCrypt(pass[:16]) + case "salsa20": + kcpArgs.Block, _ = kcp.NewSalsa20BlockCrypt(pass) + default: + *kcpArgs.Crypt = "aes" + kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass) + } + //attach kcp config + tcpArgs.KCP = kcpArgs + httpArgs.KCP = kcpArgs + socksArgs.KCP = kcpArgs + spsArgs.KCP = kcpArgs + muxBridgeArgs.KCP = kcpArgs + muxServerArgs.KCP = kcpArgs + muxClientArgs.KCP = kcpArgs + flags := log.Ldate if *debug { flags |= log.Lshortfile | log.Lmicroseconds diff --git a/docs/images/fxdl.png b/docs/images/fxdl.png new file mode 100644 index 0000000..297ad5e Binary files /dev/null and b/docs/images/fxdl.png differ diff --git a/docs/images/http-1.png b/docs/images/http-1.png new file mode 100644 index 0000000..225c80d Binary files /dev/null and b/docs/images/http-1.png differ diff --git a/docs/images/http-2.png b/docs/images/http-2.png new file mode 100644 index 0000000..5c70292 Binary files /dev/null and b/docs/images/http-2.png differ diff --git a/docs/images/http-kcp.png b/docs/images/http-kcp.png new file mode 100644 index 0000000..ebd8524 Binary files /dev/null and b/docs/images/http-kcp.png differ diff --git a/docs/images/http-ssh-1.png b/docs/images/http-ssh-1.png new file mode 100644 index 0000000..76ad55d Binary files /dev/null and b/docs/images/http-ssh-1.png differ diff --git a/docs/images/http-tls-2.png b/docs/images/http-tls-2.png new file mode 100644 index 0000000..0a4d49c Binary files /dev/null and b/docs/images/http-tls-2.png differ diff --git a/docs/images/http-tls-3.png b/docs/images/http-tls-3.png new file mode 100644 index 0000000..ee891aa Binary files /dev/null and b/docs/images/http-tls-3.png differ diff --git a/docs/images/socks-2.png b/docs/images/socks-2.png new file mode 100644 index 0000000..ff95189 Binary files /dev/null and b/docs/images/socks-2.png differ diff --git a/docs/images/socks-ssh.png b/docs/images/socks-ssh.png new file mode 100644 index 0000000..66da5f2 Binary files /dev/null and b/docs/images/socks-ssh.png differ diff --git a/docs/images/socks-tls-2.png b/docs/images/socks-tls-2.png new file mode 100644 index 0000000..2dd9c0f Binary files /dev/null and b/docs/images/socks-tls-2.png differ diff --git a/docs/images/socks-tls-3.png b/docs/images/socks-tls-3.png new file mode 100644 index 0000000..5fd78f9 Binary files /dev/null and b/docs/images/socks-tls-3.png differ diff --git a/docs/images/sps-tls.png b/docs/images/sps-tls.png new file mode 100644 index 0000000..1306803 Binary files /dev/null and b/docs/images/sps-tls.png differ diff --git a/docs/images/tcp-1.png b/docs/images/tcp-1.png new file mode 100644 index 0000000..4d65772 Binary files /dev/null and b/docs/images/tcp-1.png differ diff --git a/docs/images/tcp-2.png b/docs/images/tcp-2.png new file mode 100644 index 0000000..2fac3ab Binary files /dev/null and b/docs/images/tcp-2.png differ diff --git a/docs/images/tcp-3.png b/docs/images/tcp-3.png new file mode 100644 index 0000000..89508a3 Binary files /dev/null and b/docs/images/tcp-3.png differ diff --git a/docs/images/tcp-tls-2.png b/docs/images/tcp-tls-2.png new file mode 100644 index 0000000..c4644ff Binary files /dev/null and b/docs/images/tcp-tls-2.png differ diff --git a/docs/images/tcp-tls-3.png b/docs/images/tcp-tls-3.png new file mode 100644 index 0000000..cdc27f1 Binary files /dev/null and b/docs/images/tcp-tls-3.png differ diff --git a/docs/images/udp-1.png b/docs/images/udp-1.png new file mode 100644 index 0000000..c236f09 Binary files /dev/null and b/docs/images/udp-1.png differ diff --git a/docs/images/udp-2.png b/docs/images/udp-2.png new file mode 100644 index 0000000..d89eb6e Binary files /dev/null and b/docs/images/udp-2.png differ diff --git a/docs/images/udp-3.png b/docs/images/udp-3.png new file mode 100644 index 0000000..a1f039e Binary files /dev/null and b/docs/images/udp-3.png differ diff --git a/docs/images/udp-tls-2.png b/docs/images/udp-tls-2.png new file mode 100644 index 0000000..ed4eb4b Binary files /dev/null and b/docs/images/udp-tls-2.png differ diff --git a/docs/images/udp-tls-3.png b/docs/images/udp-tls-3.png new file mode 100644 index 0000000..4ceb456 Binary files /dev/null and b/docs/images/udp-tls-3.png differ diff --git a/install_auto.sh b/install_auto.sh index 6a5b874..fac84d2 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/v4.4/proxy-linux-amd64.tar.gz +wget https://github.com/snail007/goproxy/releases/download/v4.7/proxy-linux-amd64.tar.gz # #install proxy tar zxvf proxy-linux-amd64.tar.gz diff --git a/main.go b/main.go index f01ec8c..bd047ee 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,7 @@ import ( "syscall" ) -const APP_VERSION = "4.4" +const APP_VERSION = "4.7" func main() { err := initConfig() diff --git a/release.sh b/release.sh index 3125887..7426d55 100755 --- a/release.sh +++ b/release.sh @@ -1,5 +1,5 @@ #!/bin/bash -VER="4.4" +VER="4.7" RELEASE="release-${VER}" rm -rf .cert mkdir .cert diff --git a/sdk/README.md b/sdk/README.md new file mode 100644 index 0000000..5955be2 --- /dev/null +++ b/sdk/README.md @@ -0,0 +1,259 @@ + +# Proxy SDK 使用说明 + +支持以下平台: +- Android,`.arr`库 +- IOS,`.framework`库 +- Windows,`.dll`库 +- Linux,`.so`库 +- MacOS,`.dylib`库 + +proxy使用gombile实现了一份go代码编译为android和ios平台下面可以直接调用的sdk类库, +另外还为linux和windows提供sdk支持,基于这些类库,APP开发者可以轻松的开发出各种形式的代理工具。 + +# 下面分平台介绍SDK的用法 + +## Android SDK + +[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy-sdk-android/) [![license](https://img.shields.io/github/license/snail007/goproxy-sdk-android.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy-sdk-android/total.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-android/releases) [![download](https://img.shields.io/github/release/snail007/goproxy-sdk-android.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-android/releases) + +[点击下载Android-SDK](https://github.com/snail007/goproxy-sdk-android/releases) +在Android系统提供的sdk形式是一个后缀为.aar的类库文件,开发的时候只需要把arr类库文件引入android项目即可. + +### Android-SDK使用实例 + +#### 1.导入包 + +```java +import snail007.proxy.Porxy +``` + +#### 2.启动一个服务 + +```java +String serviceID="http01";//这里serviceID是自定义的唯一标识字符串,保证每个启动的服务不一样即可 +String serviceArgs="http -p :8080"; +String err=Proxy.start(serviceID,serviceArgs); +if (!err.isEmpty()){ + //启动失败 + System.out.println("start fail,error:"+err); +}else{ + //启动成功 +} +``` + +#### 3.停止一个服务 + +```java +String serviceID="http01"; +Proxy.stop(serviceID); +//停止完毕 + +``` + +## IOS SDK + +[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy-sdk-ios/) [![license](https://img.shields.io/github/license/snail007/goproxy-sdk-ios.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy-sdk-ios/total.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-ios/releases) [![download](https://img.shields.io/github/release/snail007/goproxy-sdk-ios.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-ios/releases) + +[点击下载IOS-SDK](https://github.com/snail007/goproxy-sdk-ios/releases) +在IOS系统提供的sdk形式是一个后缀为.framework的类库文件夹,开发的时候只需要把类库文件引入项目,然后调用方法即可. + +### IOS-SDK使用实例 + +#### 导入包 + +```objc +#import +``` + +#### 2.启动一个服务 + +```objc +-(IBAction)doStart:(id)sender +{ + //这里serviceID是自定义的唯一标识字符串,保证每个启动的服务不一样 + NSString *serviceID = @"http01"; + NSString *serviceArgs = @"http -p :8080"; + NSString *error = ProxyStart(serviceID,serviceArgs); + + if (error != nil && error.length > 0) + { + NSLog(@"start error %@",error); + }else{ + NSLog(@"启动成功"); + } +} +``` + +#### 3.停止一个服务 + +```objc +-(IBAction)doStop:(id)sender +{ + NSString *serviceID = @"http01"; + ProxyStop(serviceID); + //停止完毕 +} +``` + + +## Windows SDK +[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy-sdk-windows/) [![license](https://img.shields.io/github/license/snail007/goproxy-sdk-windows.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy-sdk-windows/total.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-windows/releases) [![download](https://img.shields.io/github/release/snail007/goproxy-sdk-windows.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-windows/releases) + +[点击下载Windows-SDK](https://github.com/snail007/goproxy-sdk-windows/releases) +在Windows系统提供的sdk形式是一个后缀为.dll的类库文件,开发的时候只需要把dll类库文件加载,然后调用方法即可. + +### Windows-SDK使用实例 +C++示例,不需要包含头文件,只需要加载proxy-sdk.dll即可,ieshims.dll需要和proxy-sdk.dll在一起。 +作者:[yjbdsky](https://github.com/yjbdsky) + +```cpp +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef char *(*GOSTART)(char *s); +typedef char *(*GOSTOP)(char *s); +typedef int(*GOISRUN)(char *s); +HMODULE GODLL = LoadLibrary("proxy-sdk.dll"); + +char * Start(char * p0,char * p1) +{ + if (GODLL != NULL) + { + GOSTART gostart = *(GOSTART)(GetProcAddress(GODLL, "Start")); + if (gostart != NULL){ + printf("%s:%s\n",p0, p1); + char *ret = gostart(p0,p1); + return ret; + } + } + return "Cannot Find dll"; +} +char * Stop(char * p) +{ + if (GODLL != NULL) + { + GOSTOP gostop = *(GOSTOP)(GetProcAddress(GODLL, "Stop")); + if (gostop != NULL){ + printf("%s\n", p); + char *ret = gostop(p); + return ret; + } + } + return "Cannot Find dll"; +} + +int main() +{ + //这里p0是自定义的唯一标识字符串,保证每个启动的服务不一样 + char *p0 = "http01"; + char *p1 = "http -t tcp -p :38080"; + printf("This is demo application.\n"); + //启动服务,返回空字符串说明启动成功;返回非空字符串说明启动失败,返回的字符串是错误原因 + printf("start result %s\n", Start(p0,p1)); + //停止服务,没有返回值 + Stop(p0); + return 0; +} + + +#ifdef __cplusplus +} +#endif +``` + +C++示例2,请移步:[GoProxyForC](https://github.com/SuperPowerLF2/GoProxyForC) + +## Linux SDK +[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy-sdk-linux/) [![license](https://img.shields.io/github/license/snail007/goproxy-sdk-linux.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy-sdk-linux/total.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-linux/releases) [![download](https://img.shields.io/github/release/snail007/goproxy-sdk-linux.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-linux/releases) + +[点击下载Linux-SDK](https://github.com/snail007/goproxy-sdk-linux/releases) +在Linux系统提供的sdk形式是一个后缀为.so的类库文件,开发的时候只需要把so类库加载,调用方法即可. + +### Linux-SDK使用实例 +Linux下面使用的sdk是so文件即libproxy-sdk.so,下面写一个简单的C程序示例,调用so库里面的方法. + +`vi test-proxy.c` + +```c +#include +#include "libproxy-sdk.h" + +int main() { + printf("This is demo application.\n"); + //这里p0是自定义的唯一标识字符串,保证每个启动的服务不一样 + char *p0 = "http01"; + char *p1 = "http -t tcp -p :38080"; + //启动服务,返回空字符串说明启动成功;返回非空字符串说明启动失败,返回的字符串是错误原因 + printf("start result %s\n",Start(p0,p1)); + //停止服务,没有返回值 + Stop(p0); + return 0; +} +``` + +#### 编译test-proxy.c #### +`export LD_LIBRARY_PATH=./ && gcc -o test-proxy test.c libproxy-sdk.so` + +#### 执行 #### +`./test-proxy` + +## MacOS SDK +[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy-sdk-mac/) [![license](https://img.shields.io/github/license/snail007/goproxy-sdk-mac.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy-sdk-mac/total.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-mac/releases) [![download](https://img.shields.io/github/release/snail007/goproxy-sdk-mac.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-mac/releases) + +[点击下载MacOS-SDK](https://github.com/snail007/goproxy-sdk-mac/releases) +在MacOS系统提供的sdk形式是一个后缀为.dylib的类库文件,开发的时候只需要把so类库加载,调用方法即可. + +### MacOS-SDK使用实例 +MacOS下面使用的sdk是dylib文件即libproxy-sdk.dylib,下面写一个简单的Obj-C程序示例,调用dylib库里面的方法. + +```objc +#import "libproxy-sdk.h" +-(IBAction)doStart:(id)sender +{ + char *result = Start("http01", "http -t tcp -p :38080"); + + if (result) + { + printf("started"); + }else{ + printf("not started"); + } + +} +-(IBAction)doStop:(id)sender +{ + Stop("http01"); + +} +``` + +### 关于服务 +proxy的服务有11种,分别是: + +```shell +http +socks +sps +tcp +udp +bridge +server +client +tbridge +tserver +tclient +``` +服务启动时,如果存在正在运行的相同ID的服务,那么之前的服务会被停掉,后面启动的服务覆盖之前的服务. +所以要保证每次启动服务的时候,第一个ID参数唯一. +上面这些服务的具体使用方式和具体参数,可以参考[proxy手册](https://github.com/snail007/goproxy/blob/master/README_ZH.md) +sdk里面的服务不支持手册里面的:--daemon和--forever参数. + + diff --git a/sdk/android-ios/.gitignore b/sdk/android-ios/.gitignore new file mode 100644 index 0000000..6e5e3cc --- /dev/null +++ b/sdk/android-ios/.gitignore @@ -0,0 +1,7 @@ +*.jar +*.aar +*.tar.gz +ios +android +Proxy.framework + \ No newline at end of file diff --git a/sdk/android-ios/release_android.sh b/sdk/android-ios/release_android.sh new file mode 100755 index 0000000..342cc45 --- /dev/null +++ b/sdk/android-ios/release_android.sh @@ -0,0 +1,15 @@ +#/bin/bash +VER="v4.7" +rm -rf sdk-android-*.tar.gz +rm -rf android +mkdir android + +#android +gomobile bind -v -target=android -javapkg=snail007 -ldflags="-s -w" +mv proxy.aar android/snail007.goproxy.sdk.aar +mv proxy-sources.jar android/snail007.goproxy.sdk-sources.jar +cp ../README.md android +tar zcfv sdk-android-${VER}.tar.gz android +rm -rf android + +echo "done." diff --git a/sdk/android-ios/release_ios.sh b/sdk/android-ios/release_ios.sh new file mode 100755 index 0000000..499971b --- /dev/null +++ b/sdk/android-ios/release_ios.sh @@ -0,0 +1,14 @@ +#/bin/bash +VER="v4.7" +rm -rf sdk-ios-*.tar.gz +rm -rf ios +mkdir ios + +#ios XCode required +gomobile bind -v -target=ios -ldflags="-s -w" +mv Proxy.framework ios +cp ../README.md ios +tar zcfv sdk-ios-${VER}.tar.gz ios +rm -rf ios + +echo "done." diff --git a/sdk/android-ios/sdk.go b/sdk/android-ios/sdk.go new file mode 100644 index 0000000..68043c6 --- /dev/null +++ b/sdk/android-ios/sdk.go @@ -0,0 +1,351 @@ +package proxy + +import ( + "crypto/sha1" + "fmt" + "log" + "os" + "snail007/proxy/services" + "snail007/proxy/services/kcpcfg" + "strings" + + kcp "github.com/xtaci/kcp-go" + "golang.org/x/crypto/pbkdf2" + kingpin "gopkg.in/alecthomas/kingpin.v2" +) + +var ( + app *kingpin.Application +) + +//Start +//serviceID : is service identify id,different service's id should be difference +//serviceArgsStr: is the whole command line args string +//such as : +//1."http -t tcp -p :8989" +//2."socks -t tcp -p :8989" +//and so on. +//if an error occured , errStr will be the error reason +//if start success, errStr is empty. +func Start(serviceID, serviceArgsStr string) (errStr string) { + //define args + tcpArgs := services.TCPArgs{} + httpArgs := services.HTTPArgs{} + tunnelServerArgs := services.TunnelServerArgs{} + tunnelClientArgs := services.TunnelClientArgs{} + tunnelBridgeArgs := services.TunnelBridgeArgs{} + muxServerArgs := services.MuxServerArgs{} + muxClientArgs := services.MuxClientArgs{} + muxBridgeArgs := services.MuxBridgeArgs{} + udpArgs := services.UDPArgs{} + socksArgs := services.SocksArgs{} + spsArgs := services.SPSArgs{} + kcpArgs := kcpcfg.KCPConfigArgs{} + //build srvice args + app = kingpin.New("proxy", "happy with proxy") + app.Author("snail").Version("4.7") + debug := app.Flag("debug", "debug log output").Default("false").Bool() + logfile := app.Flag("log", "log file path").Default("").String() + kcpArgs.Key = app.Flag("kcp-key", "pre-shared secret between client and server").Default("secrect").String() + kcpArgs.Crypt = app.Flag("kcp-method", "encrypt/decrypt method, can be: aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none").Default("aes").Enum("aes", "aes-128", "aes-192", "salsa20", "blowfish", "twofish", "cast5", "3des", "tea", "xtea", "xor", "sm4", "none") + kcpArgs.Mode = app.Flag("kcp-mode", "profiles: fast3, fast2, fast, normal, manual").Default("fast3").Enum("fast3", "fast2", "fast", "normal", "manual") + kcpArgs.MTU = app.Flag("kcp-mtu", "set maximum transmission unit for UDP packets").Default("1350").Int() + kcpArgs.SndWnd = app.Flag("kcp-sndwnd", "set send window size(num of packets)").Default("1024").Int() + kcpArgs.RcvWnd = app.Flag("kcp-rcvwnd", "set receive window size(num of packets)").Default("1024").Int() + kcpArgs.DataShard = app.Flag("kcp-ds", "set reed-solomon erasure coding - datashard").Default("10").Int() + kcpArgs.ParityShard = app.Flag("kcp-ps", "set reed-solomon erasure coding - parityshard").Default("3").Int() + kcpArgs.DSCP = app.Flag("kcp-dscp", "set DSCP(6bit)").Default("0").Int() + kcpArgs.NoComp = app.Flag("kcp-nocomp", "disable compression").Default("false").Bool() + kcpArgs.AckNodelay = app.Flag("kcp-acknodelay", "be carefull! flush ack immediately when a packet is received").Default("true").Bool() + kcpArgs.NoDelay = app.Flag("kcp-nodelay", "be carefull!").Default("0").Int() + kcpArgs.Interval = app.Flag("kcp-interval", "be carefull!").Default("50").Int() + kcpArgs.Resend = app.Flag("kcp-resend", "be carefull!").Default("0").Int() + kcpArgs.NoCongestion = app.Flag("kcp-nc", "be carefull! no congestion").Default("0").Int() + kcpArgs.SockBuf = app.Flag("kcp-sockbuf", "be carefull!").Default("4194304").Int() + kcpArgs.KeepAlive = app.Flag("kcp-keepalive", "be carefull!").Default("10").Int() + + //########http######### + http := app.Command("http", "proxy on http mode") + httpArgs.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() + httpArgs.CaCertFile = http.Flag("ca", "ca cert file for tls").Default("").String() + httpArgs.CertFile = http.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() + httpArgs.KeyFile = http.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() + httpArgs.LocalType = http.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") + httpArgs.ParentType = http.Flag("parent-type", "parent protocol type ").Short('T').Enum("tls", "tcp", "ssh", "kcp") + httpArgs.Always = http.Flag("always", "always use parent proxy").Default("false").Bool() + httpArgs.Timeout = http.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("2000").Int() + httpArgs.HTTPTimeout = http.Flag("http-timeout", "check domain if blocked , http request timeout milliseconds when connect to host").Default("3000").Int() + httpArgs.Interval = http.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int() + httpArgs.Blocked = http.Flag("blocked", "blocked domain file , one domain each line").Default("blocked").Short('b').String() + httpArgs.Direct = http.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String() + httpArgs.AuthFile = http.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String() + httpArgs.Auth = http.Flag("auth", "http basic auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings() + httpArgs.CheckParentInterval = http.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int() + httpArgs.Local = http.Flag("local", "local ip:port to listen,multiple address use comma split,such as: 0.0.0.0:80,0.0.0.0:443").Short('p').Default(":33080").String() + httpArgs.SSHUser = http.Flag("ssh-user", "user for ssh").Short('u').Default("").String() + httpArgs.SSHKeyFile = http.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String() + httpArgs.SSHKeyFileSalt = http.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String() + httpArgs.SSHPassword = http.Flag("ssh-password", "password for ssh").Short('A').Default("").String() + httpArgs.LocalIPS = http.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings() + httpArgs.AuthURL = http.Flag("auth-url", "http basic auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String() + httpArgs.AuthURLTimeout = http.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int() + httpArgs.AuthURLOkCode = http.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int() + httpArgs.AuthURLRetry = http.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("1").Int() + httpArgs.DNSAddress = http.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() + httpArgs.DNSTTL = http.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int() + httpArgs.LocalKey = http.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String() + httpArgs.ParentKey = http.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String() + httpArgs.LocalCompress = http.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool() + httpArgs.ParentCompress = http.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool() + + //########tcp######### + tcp := app.Command("tcp", "proxy on tcp mode") + tcpArgs.Parent = tcp.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() + tcpArgs.CertFile = tcp.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() + tcpArgs.KeyFile = tcp.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() + tcpArgs.Timeout = tcp.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('e').Default("2000").Int() + tcpArgs.ParentType = tcp.Flag("parent-type", "parent protocol type ").Short('T').Enum("tls", "tcp", "udp", "kcp") + tcpArgs.LocalType = tcp.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") + tcpArgs.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int() + tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() + + //########udp######### + udp := app.Command("udp", "proxy on udp mode") + udpArgs.Parent = udp.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() + udpArgs.CertFile = udp.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() + udpArgs.KeyFile = udp.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() + udpArgs.Timeout = udp.Flag("timeout", "tcp timeout milliseconds when connect to parent proxy").Short('t').Default("2000").Int() + udpArgs.ParentType = udp.Flag("parent-type", "parent protocol type ").Short('T').Enum("tls", "tcp", "udp") + udpArgs.CheckParentInterval = udp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int() + udpArgs.Local = udp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() + + //########mux-server######### + muxServer := app.Command("server", "proxy on mux server mode") + muxServerArgs.Parent = muxServer.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() + muxServerArgs.ParentType = muxServer.Flag("parent-type", "parent protocol type ").Default("tls").Short('T').Enum("tls", "tcp", "kcp") + muxServerArgs.CertFile = muxServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() + muxServerArgs.KeyFile = muxServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() + muxServerArgs.Timeout = muxServer.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int() + muxServerArgs.IsUDP = muxServer.Flag("udp", "proxy on udp mux server mode").Default("false").Bool() + muxServerArgs.Key = muxServer.Flag("k", "client key").Default("default").String() + muxServerArgs.Route = muxServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings() + muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp|tls mode").Default("false").Bool() + muxServerArgs.SessionCount = muxServer.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int() + + //########mux-client######### + muxClient := app.Command("client", "proxy on mux client mode") + muxClientArgs.Parent = muxClient.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() + muxClientArgs.ParentType = muxClient.Flag("parent-type", "parent protocol type ").Default("tls").Short('T').Enum("tls", "tcp", "kcp") + muxClientArgs.CertFile = muxClient.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() + muxClientArgs.KeyFile = muxClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() + muxClientArgs.Timeout = muxClient.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int() + muxClientArgs.Key = muxClient.Flag("k", "key same with server").Default("default").String() + muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp|tls mode").Default("false").Bool() + muxClientArgs.SessionCount = muxClient.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int() + + //########mux-bridge######### + muxBridge := app.Command("bridge", "proxy on mux bridge mode") + muxBridgeArgs.CertFile = muxBridge.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() + muxBridgeArgs.KeyFile = muxBridge.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() + muxBridgeArgs.Timeout = muxBridge.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int() + muxBridgeArgs.Local = muxBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() + muxBridgeArgs.LocalType = muxBridge.Flag("local-type", "local protocol type ").Default("tls").Short('t').Enum("tls", "tcp", "kcp") + + //########tunnel-server######### + tunnelServer := app.Command("tserver", "proxy on tunnel server mode") + tunnelServerArgs.Parent = tunnelServer.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() + tunnelServerArgs.CertFile = tunnelServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() + tunnelServerArgs.KeyFile = tunnelServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() + tunnelServerArgs.Timeout = tunnelServer.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int() + tunnelServerArgs.IsUDP = tunnelServer.Flag("udp", "proxy on udp tunnel server mode").Default("false").Bool() + tunnelServerArgs.Key = tunnelServer.Flag("k", "client key").Default("default").String() + tunnelServerArgs.Route = tunnelServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings() + + //########tunnel-client######### + tunnelClient := app.Command("tclient", "proxy on tunnel client mode") + tunnelClientArgs.Parent = tunnelClient.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() + tunnelClientArgs.CertFile = tunnelClient.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() + tunnelClientArgs.KeyFile = tunnelClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() + tunnelClientArgs.Timeout = tunnelClient.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int() + tunnelClientArgs.Key = tunnelClient.Flag("k", "key same with server").Default("default").String() + + //########tunnel-bridge######### + tunnelBridge := app.Command("tbridge", "proxy on tunnel bridge mode") + tunnelBridgeArgs.CertFile = tunnelBridge.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() + tunnelBridgeArgs.KeyFile = tunnelBridge.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() + tunnelBridgeArgs.Timeout = tunnelBridge.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int() + tunnelBridgeArgs.Local = tunnelBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() + + //########ssh######### + socks := app.Command("socks", "proxy on ssh mode") + socksArgs.Parent = socks.Flag("parent", "parent ssh address, such as: \"23.32.32.19:22\"").Default("").Short('P').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() + socksArgs.SSHUser = socks.Flag("ssh-user", "user for ssh").Short('u').Default("").String() + socksArgs.SSHKeyFile = socks.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String() + socksArgs.SSHKeyFileSalt = socks.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String() + socksArgs.SSHPassword = socks.Flag("ssh-password", "password for ssh").Short('A').Default("").String() + socksArgs.Always = socks.Flag("always", "always use parent proxy").Default("false").Bool() + socksArgs.Timeout = socks.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("5000").Int() + socksArgs.Interval = socks.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int() + socksArgs.Blocked = socks.Flag("blocked", "blocked domain file , one domain each line").Default("blocked").Short('b').String() + socksArgs.Direct = socks.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String() + socksArgs.AuthFile = socks.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String() + socksArgs.Auth = socks.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings() + socksArgs.LocalIPS = socks.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings() + socksArgs.AuthURL = socks.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String() + socksArgs.AuthURLTimeout = socks.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int() + socksArgs.AuthURLOkCode = socks.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int() + socksArgs.AuthURLRetry = socks.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int() + socksArgs.DNSAddress = socks.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() + socksArgs.DNSTTL = socks.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int() + socksArgs.LocalKey = socks.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String() + socksArgs.ParentKey = socks.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String() + socksArgs.LocalCompress = socks.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool() + socksArgs.ParentCompress = socks.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool() + + //########socks+http(s)######### + sps := app.Command("sps", "proxy on socks+http(s) mode") + spsArgs.Parent = sps.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() + spsArgs.CertFile = sps.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() + spsArgs.KeyFile = sps.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() + spsArgs.CaCertFile = sps.Flag("ca", "ca cert file for tls").Default("").String() + spsArgs.Timeout = sps.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('i').Default("2000").Int() + spsArgs.ParentType = sps.Flag("parent-type", "parent protocol type ").Short('T').Enum("tls", "tcp", "kcp") + spsArgs.LocalType = sps.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") + spsArgs.Local = sps.Flag("local", "local ip:port to listen,multiple address use comma split,such as: 0.0.0.0:80,0.0.0.0:443").Short('p').Default(":33080").String() + spsArgs.ParentServiceType = sps.Flag("parent-service-type", "parent service type ").Short('S').Enum("http", "socks") + spsArgs.DNSAddress = sps.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() + spsArgs.DNSTTL = sps.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int() + spsArgs.AuthFile = sps.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String() + spsArgs.Auth = sps.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings() + spsArgs.LocalIPS = sps.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings() + spsArgs.AuthURL = sps.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String() + spsArgs.AuthURLTimeout = sps.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int() + spsArgs.AuthURLOkCode = sps.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int() + spsArgs.AuthURLRetry = sps.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int() + spsArgs.ParentAuth = sps.Flag("parent-auth", "parent socks auth username and password, such as: -A user1:pass1").Short('A').String() + spsArgs.LocalKey = sps.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String() + spsArgs.ParentKey = sps.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String() + spsArgs.LocalCompress = sps.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool() + spsArgs.ParentCompress = sps.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool() + + //parse args + _args := strings.Fields(strings.Trim(serviceArgsStr, " ")) + args := []string{} + for _, a := range _args { + args = append(args, strings.Trim(a, "\"")) + } + serviceName, err := app.Parse(args) + if err != nil { + return fmt.Sprintf("parse args fail,err: %s", err) + } + //set kcp config + + switch *kcpArgs.Mode { + case "normal": + *kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 0, 40, 2, 1 + case "fast": + *kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 0, 30, 2, 1 + case "fast2": + *kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 1, 20, 2, 1 + case "fast3": + *kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 1, 10, 2, 1 + } + pass := pbkdf2.Key([]byte(*kcpArgs.Key), []byte("snail007-goproxy"), 4096, 32, sha1.New) + + switch *kcpArgs.Crypt { + case "sm4": + kcpArgs.Block, _ = kcp.NewSM4BlockCrypt(pass[:16]) + case "tea": + kcpArgs.Block, _ = kcp.NewTEABlockCrypt(pass[:16]) + case "xor": + kcpArgs.Block, _ = kcp.NewSimpleXORBlockCrypt(pass) + case "none": + kcpArgs.Block, _ = kcp.NewNoneBlockCrypt(pass) + case "aes-128": + kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass[:16]) + case "aes-192": + kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass[:24]) + case "blowfish": + kcpArgs.Block, _ = kcp.NewBlowfishBlockCrypt(pass) + case "twofish": + kcpArgs.Block, _ = kcp.NewTwofishBlockCrypt(pass) + case "cast5": + kcpArgs.Block, _ = kcp.NewCast5BlockCrypt(pass[:16]) + case "3des": + kcpArgs.Block, _ = kcp.NewTripleDESBlockCrypt(pass[:24]) + case "xtea": + kcpArgs.Block, _ = kcp.NewXTEABlockCrypt(pass[:16]) + case "salsa20": + kcpArgs.Block, _ = kcp.NewSalsa20BlockCrypt(pass) + default: + *kcpArgs.Crypt = "aes" + kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass) + } + //attach kcp config + tcpArgs.KCP = kcpArgs + httpArgs.KCP = kcpArgs + socksArgs.KCP = kcpArgs + spsArgs.KCP = kcpArgs + muxBridgeArgs.KCP = kcpArgs + muxServerArgs.KCP = kcpArgs + muxClientArgs.KCP = kcpArgs + + flags := log.Ldate + if *debug { + flags |= log.Lshortfile | log.Lmicroseconds + } else { + flags |= log.Ltime + } + log.SetFlags(flags) + + if *logfile != "" { + f, e := os.OpenFile(*logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) + if e != nil { + log.Fatal(e) + } + log.SetOutput(f) + } + + //regist services and run service + switch serviceName { + case "http": + services.Regist(serviceID, services.NewHTTP(), httpArgs) + case "tcp": + services.Regist(serviceID, services.NewTCP(), tcpArgs) + case "udp": + services.Regist(serviceID, services.NewUDP(), udpArgs) + case "tserver": + services.Regist(serviceID, services.NewTunnelServerManager(), tunnelServerArgs) + case "tclient": + services.Regist(serviceID, services.NewTunnelClient(), tunnelClientArgs) + case "tbridge": + services.Regist(serviceID, services.NewTunnelBridge(), tunnelBridgeArgs) + case "server": + services.Regist(serviceID, services.NewMuxServerManager(), muxServerArgs) + case "client": + services.Regist(serviceID, services.NewMuxClient(), muxClientArgs) + case "bridge": + services.Regist(serviceID, services.NewMuxBridge(), muxBridgeArgs) + case "socks": + services.Regist(serviceID, services.NewSocks(), socksArgs) + case "sps": + services.Regist(serviceID, services.NewSPS(), spsArgs) + } + _, err = services.Run(serviceID) + if err != nil { + return fmt.Sprintf("run service [%s:%s] fail, ERR:%s", serviceID, serviceName, err) + } + return +} + +func Stop(serviceID string) { + services.Stop(serviceID) +} diff --git a/sdk/windows-linux/.gitignore b/sdk/windows-linux/.gitignore new file mode 100644 index 0000000..b3d0383 --- /dev/null +++ b/sdk/windows-linux/.gitignore @@ -0,0 +1,6 @@ +proxy-sdk.dll +proxy-sdk.h +proxy-sdk.so +proxy-sdk.a +*.tar.gz +test.c diff --git a/sdk/windows-linux/ieshims.dll b/sdk/windows-linux/ieshims.dll new file mode 100644 index 0000000..88b03f0 Binary files /dev/null and b/sdk/windows-linux/ieshims.dll differ diff --git a/sdk/windows-linux/release_linux.sh b/sdk/windows-linux/release_linux.sh new file mode 100755 index 0000000..dadda58 --- /dev/null +++ b/sdk/windows-linux/release_linux.sh @@ -0,0 +1,24 @@ +#/bin/bash +VER="v4.7" + +rm -rf sdk-linux-*.tar.gz +rm -rf README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a + +#linux 32bit +CGO_ENABLED=1 GOARCH=386 GOOS=linux go build -buildmode=c-archive -ldflags "-s -w" -o libproxy-sdk.a sdk.go +CGO_ENABLED=1 GOARCH=386 GOOS=linux go build -buildmode=c-shared -ldflags "-s -w" -o libproxy-sdk.so sdk.go +cp ../README.md . +tar zcf sdk-linux-32bit-${VER}.tar.gz README.md libproxy-sdk.so libproxy-sdk.a libproxy-sdk.h +rm -rf README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a + +#linux 64bit +CGO_ENABLED=1 GOARCH=amd64 GOOS=linux go build -buildmode=c-archive -ldflags "-s -w" -o libproxy-sdk.a sdk.go +CGO_ENABLED=1 GOARCH=amd64 GOOS=linux go build -buildmode=c-shared -ldflags "-s -w" -o libproxy-sdk.so sdk.go +cp ../README.md . +tar zcf sdk-linux-64bit-${VER}.tar.gz README.md libproxy-sdk.so libproxy-sdk.a libproxy-sdk.h +rm -rf README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a + +echo "done." + + + diff --git a/sdk/windows-linux/release_mac.sh b/sdk/windows-linux/release_mac.sh new file mode 100755 index 0000000..275bea9 --- /dev/null +++ b/sdk/windows-linux/release_mac.sh @@ -0,0 +1,13 @@ +#/bin/bash +VER="v4.7" + +rm -rf *.tar.gz +rm -rf README.md libproxy-sdk.dylib libproxy-sdk.h + +#mac , macos required +CGO_ENABLED=1 GOARCH=amd64 GOOS=darwin go build -buildmode=c-shared -ldflags "-s -w" -o libproxy-sdk.dylib sdk.go +cp ../README.md . +tar zcf sdk-mac-${VER}.tar.gz README.md libproxy-sdk.dylib libproxy-sdk.h +rm -rf README.md libproxy-sdk.dylib libproxy-sdk.h + +echo "done." diff --git a/sdk/windows-linux/release_windows.sh b/sdk/windows-linux/release_windows.sh new file mode 100755 index 0000000..8603fff --- /dev/null +++ b/sdk/windows-linux/release_windows.sh @@ -0,0 +1,28 @@ +#/bin/bash +VER="v4.7" + +sudo rm /usr/local/go +sudo ln -s /usr/local/go1.10.1 /usr/local/go +rm -rf sdk-windows-*.tar.gz +rm -rf README.md proxy-sdk.h proxy-sdk.dll + +#windows 64bit +#apt-get install gcc-multilib +#apt-get install gcc-mingw-w64 + +#windows 64bit +CC=x86_64-w64-mingw32-gcc GOARCH=amd64 CGO_ENABLED=1 GOOS=windows go build -buildmode=c-shared -ldflags "-s -w" -o proxy-sdk.dll sdk.go +cp ../README.md . +tar zcf sdk-windows-64bit-${VER}.tar.gz README.md proxy-sdk.dll proxy-sdk.h ieshims.dll +rm -rf README.md proxy-sdk.h proxy-sdk.dll + +#windows 32bit +CC=i686-w64-mingw32-gcc-win32 GOARCH=386 CGO_ENABLED=1 GOOS=windows go build -buildmode=c-shared -ldflags "-s -w" -o proxy-sdk.dll sdk.go +cp ../README.md . +tar zcf sdk-windows-32bit-${VER}.tar.gz README.md proxy-sdk.dll proxy-sdk.h ieshims.dll +rm -rf README.md proxy-sdk.h proxy-sdk.dll + +sudo rm /usr/local/go +sudo ln -s /usr/local/go1.8.5 /usr/local/go + +echo "done." diff --git a/sdk/windows-linux/sdk.go b/sdk/windows-linux/sdk.go new file mode 100644 index 0000000..beb32d6 --- /dev/null +++ b/sdk/windows-linux/sdk.go @@ -0,0 +1,19 @@ +package main + +import ( + "C" + sdk "snail007/proxy/sdk/android-ios" +) + +//export Start +func Start(serviceID *C.char,serviceArgsStr *C.char) (errStr *C.char) { + return C.CString(sdk.Start(C.GoString(serviceID),C.GoString(serviceArgsStr))) +} + +//export Stop +func Stop(serviceID *C.char) { + sdk.Stop(C.GoString(serviceID)) +} + +func main() { +} diff --git a/services/args.go b/services/args.go index 7dfa671..2403cc6 100644 --- a/services/args.go +++ b/services/args.go @@ -1,6 +1,10 @@ package services -import "golang.org/x/crypto/ssh" +import ( + "snail007/proxy/services/kcpcfg" + + "golang.org/x/crypto/ssh" +) // tcp := app.Command("tcp", "proxy on tcp mode") // t := tcp.Flag("tcp-timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("2000").Int() @@ -21,39 +25,46 @@ const ( ) type MuxServerArgs struct { - Parent *string - CertFile *string - KeyFile *string - CertBytes []byte - KeyBytes []byte - Local *string - IsUDP *bool - Key *string - Remote *string - Timeout *int - Route *[]string - Mgr *MuxServerManager - IsCompress *bool + Parent *string + ParentType *string + CertFile *string + KeyFile *string + CertBytes []byte + KeyBytes []byte + Local *string + IsUDP *bool + Key *string + Remote *string + Timeout *int + Route *[]string + Mgr *MuxServerManager + IsCompress *bool + SessionCount *int + KCP kcpcfg.KCPConfigArgs } type MuxClientArgs struct { - Parent *string - CertFile *string - KeyFile *string - CertBytes []byte - KeyBytes []byte - Key *string - Timeout *int - IsCompress *bool + Parent *string + ParentType *string + CertFile *string + KeyFile *string + CertBytes []byte + KeyBytes []byte + Key *string + Timeout *int + IsCompress *bool + SessionCount *int + KCP kcpcfg.KCPConfigArgs } type MuxBridgeArgs struct { - Parent *string CertFile *string KeyFile *string CertBytes []byte KeyBytes []byte Local *string + LocalType *string Timeout *int IsCompress *bool + KCP kcpcfg.KCPConfigArgs } type TunnelServerArgs struct { Parent *string @@ -100,16 +111,16 @@ type TCPArgs struct { ParentType *string LocalType *string Timeout *int - PoolSize *int CheckParentInterval *int - KCPMethod *string - KCPKey *string + KCP kcpcfg.KCPConfigArgs } type HTTPArgs struct { Parent *string CertFile *string KeyFile *string + CaCertFile *string + CaCertBytes []byte CertBytes []byte KeyBytes []byte Local *string @@ -127,7 +138,6 @@ type HTTPArgs struct { ParentType *string LocalType *string Timeout *int - PoolSize *int CheckParentInterval *int SSHKeyFile *string SSHKeyFileSalt *string @@ -135,11 +145,14 @@ type HTTPArgs struct { SSHUser *string SSHKeyBytes []byte SSHAuthMethod ssh.AuthMethod - KCPMethod *string - KCPKey *string + KCP kcpcfg.KCPConfigArgs LocalIPS *[]string DNSAddress *string DNSTTL *int + LocalKey *string + ParentKey *string + LocalCompress *bool + ParentCompress *bool } type UDPArgs struct { Parent *string @@ -150,7 +163,6 @@ type UDPArgs struct { Local *string ParentType *string Timeout *int - PoolSize *int CheckParentInterval *int } type SocksArgs struct { @@ -160,6 +172,8 @@ type SocksArgs struct { LocalType *string CertFile *string KeyFile *string + CaCertFile *string + CaCertBytes []byte CertBytes []byte KeyBytes []byte SSHKeyFile *string @@ -179,29 +193,45 @@ type SocksArgs struct { AuthURLOkCode *int AuthURLTimeout *int AuthURLRetry *int - KCPMethod *string - KCPKey *string + KCP kcpcfg.KCPConfigArgs UDPParent *string UDPLocal *string LocalIPS *[]string DNSAddress *string DNSTTL *int + LocalKey *string + ParentKey *string + LocalCompress *bool + ParentCompress *bool } type SPSArgs struct { Parent *string CertFile *string KeyFile *string + CaCertFile *string + CaCertBytes []byte CertBytes []byte KeyBytes []byte Local *string ParentType *string LocalType *string Timeout *int - KCPMethod *string - KCPKey *string + KCP kcpcfg.KCPConfigArgs ParentServiceType *string DNSAddress *string DNSTTL *int + AuthFile *string + Auth *[]string + AuthURL *string + AuthURLOkCode *int + AuthURLTimeout *int + AuthURLRetry *int + LocalIPS *[]string + ParentAuth *string + LocalKey *string + ParentKey *string + LocalCompress *bool + ParentCompress *bool } func (a *SPSArgs) Protocol() string { diff --git a/services/http.go b/services/http.go index 2693417..4ca6203 100644 --- a/services/http.go +++ b/services/http.go @@ -8,6 +8,7 @@ import ( "net" "runtime/debug" "snail007/proxy/utils" + "snail007/proxy/utils/conncrypt" "strconv" "strings" "time" @@ -16,38 +17,56 @@ import ( ) type HTTP struct { - outPool utils.OutPool + outPool utils.OutConn cfg HTTPArgs checker utils.Checker basicAuth utils.BasicAuth sshClient *ssh.Client lockChn chan bool domainResolver utils.DomainResolver + isStop bool + serverChannels []*utils.ServerChannel + userConns utils.ConcurrentMap } func NewHTTP() Service { return &HTTP{ - outPool: utils.OutPool{}, - cfg: HTTPArgs{}, - checker: utils.Checker{}, - basicAuth: utils.BasicAuth{}, - lockChn: make(chan bool, 1), + outPool: utils.OutConn{}, + cfg: HTTPArgs{}, + checker: utils.Checker{}, + basicAuth: utils.BasicAuth{}, + lockChn: make(chan bool, 1), + isStop: false, + serverChannels: []*utils.ServerChannel{}, + userConns: utils.NewConcurrentMap(), } } -func (s *HTTP) CheckArgs() { - var err error +func (s *HTTP) CheckArgs() (err error) { if *s.cfg.Parent != "" && *s.cfg.ParentType == "" { - log.Fatalf("parent type unkown,use -T ") + err = fmt.Errorf("parent type unkown,use -T ") + return } if *s.cfg.ParentType == "tls" || *s.cfg.LocalType == "tls" { - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + if err != nil { + return + } + if *s.cfg.CaCertFile != "" { + s.cfg.CaCertBytes, err = ioutil.ReadFile(*s.cfg.CaCertFile) + if err != nil { + err = fmt.Errorf("read ca file error,ERR:%s", err) + return + } + } } if *s.cfg.ParentType == "ssh" { if *s.cfg.SSHUser == "" { - log.Fatalf("ssh user required") + err = fmt.Errorf("ssh user required") + return } if *s.cfg.SSHKeyFile == "" && *s.cfg.SSHPassword == "" { - log.Fatalf("ssh password or key required") + err = fmt.Errorf("ssh password or key required") + return } if *s.cfg.SSHPassword != "" { @@ -56,7 +75,8 @@ func (s *HTTP) CheckArgs() { var SSHSigner ssh.Signer s.cfg.SSHKeyBytes, err = ioutil.ReadFile(*s.cfg.SSHKeyFile) if err != nil { - log.Fatalf("read key file ERR: %s", err) + err = fmt.Errorf("read key file ERR: %s", err) + return } if *s.cfg.SSHKeyFileSalt != "" { SSHSigner, err = ssh.ParsePrivateKeyWithPassphrase(s.cfg.SSHKeyBytes, []byte(*s.cfg.SSHKeyFileSalt)) @@ -64,13 +84,15 @@ func (s *HTTP) CheckArgs() { SSHSigner, err = ssh.ParsePrivateKey(s.cfg.SSHKeyBytes) } if err != nil { - log.Fatalf("parse ssh private key fail,ERR: %s", err) + err = fmt.Errorf("parse ssh private key fail,ERR: %s", err) + return } s.cfg.SSHAuthMethod = ssh.PublicKeys(SSHSigner) } } + return } -func (s *HTTP) InitService() { +func (s *HTTP) InitService() (err error) { s.InitBasicAuth() if *s.cfg.Parent != "" { s.checker = utils.NewChecker(*s.cfg.HTTPTimeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct) @@ -79,16 +101,22 @@ func (s *HTTP) InitService() { (*s).domainResolver = utils.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL) } if *s.cfg.ParentType == "ssh" { - err := s.ConnectSSH() + err = s.ConnectSSH() if err != nil { - log.Fatalf("init service fail, ERR: %s", err) + err = fmt.Errorf("init service fail, ERR: %s", err) + return } go func() { //循环检查ssh网络连通性 for { + if s.isStop { + return + } conn, err := utils.ConnectHost(s.Resolve(*s.cfg.Parent), *s.cfg.Timeout*2) if err == nil { + conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = conn.Write([]byte{0}) + conn.SetDeadline(time.Time{}) } if err != nil { if s.sshClient != nil { @@ -106,20 +134,43 @@ func (s *HTTP) InitService() { } }() } + return } func (s *HTTP) StopService() { - if s.outPool.Pool != nil { - s.outPool.Pool.ReleaseAll() + defer func() { + e := recover() + if e != nil { + log.Printf("stop http(s) service crashed,%s", e) + } else { + log.Printf("service http(s) stoped") + } + }() + s.isStop = true + s.checker.Stop() + if s.sshClient != nil { + s.sshClient.Close() + } + for _, sc := range s.serverChannels { + if sc.Listener != nil && *sc.Listener != nil { + (*sc.Listener).Close() + } + if sc.UDPListener != nil { + (*sc.UDPListener).Close() + } } } func (s *HTTP) Start(args interface{}) (err error) { s.cfg = args.(HTTPArgs) - s.CheckArgs() + if err = s.CheckArgs(); err != nil { + return + } if *s.cfg.Parent != "" { log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) s.InitOutConnPool() } - s.InitService() + if err = s.InitService(); err != nil { + return + } for _, addr := range strings.Split(*s.cfg.Local, ",") { if addr != "" { host, port, _ := net.SplitHostPort(addr) @@ -128,14 +179,15 @@ func (s *HTTP) Start(args interface{}) (err error) { if *s.cfg.LocalType == TYPE_TCP { err = sc.ListenTCP(s.callback) } else if *s.cfg.LocalType == TYPE_TLS { - err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback) + err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.callback) } else if *s.cfg.LocalType == TYPE_KCP { - err = sc.ListenKCP(*s.cfg.KCPMethod, *s.cfg.KCPKey, s.callback) + err = sc.ListenKCP(s.cfg.KCP, s.callback) } if err != nil { return } log.Printf("%s http(s) proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr()) + s.serverChannels = append(s.serverChannels, &sc) } } return @@ -150,6 +202,14 @@ func (s *HTTP) callback(inConn net.Conn) { log.Printf("http(s) conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack())) } }() + if *s.cfg.LocalCompress { + inConn = utils.NewCompConn(inConn) + } + if *s.cfg.LocalKey != "" { + inConn = conncrypt.New(inConn, &conncrypt.Config{ + Password: *s.cfg.LocalKey, + }) + } var err interface{} var req utils.HTTPRequest req, err = utils.NewHTTPRequest(&inConn, 4096, s.IsBasicAuth(), &s.basicAuth) @@ -181,7 +241,6 @@ func (s *HTTP) callback(inConn net.Conn) { log.Printf("use proxy : %v, %s", useProxy, address) err = s.OutToTCP(useProxy, address, &inConn, &req) - if err != nil { if *s.cfg.Parent == "" { log.Printf("connect to %s fail, ERR:%s", address, err) @@ -201,19 +260,18 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut return } var outConn net.Conn - var _outConn interface{} tryCount := 0 maxTryCount := 5 for { + if s.isStop { + return + } if useProxy { if *s.cfg.ParentType == "ssh" { outConn, err = s.getSSHConn(address) } else { - //log.Printf("%v", s.outPool) - _outConn, err = s.outPool.Pool.Get() - if err == nil { - outConn = _outConn.(net.Conn) - } + // log.Printf("%v", s.outPool) + outConn, err = s.outPool.Get() } } else { outConn, err = utils.ConnectHost(s.Resolve(address), *s.cfg.Timeout) @@ -231,16 +289,25 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut utils.CloseConn(inConn) return } + if *s.cfg.ParentCompress { + outConn = utils.NewCompConn(outConn) + } + if *s.cfg.ParentKey != "" { + outConn = conncrypt.New(outConn, &conncrypt.Config{ + Password: *s.cfg.ParentKey, + }) + } outAddr := outConn.RemoteAddr().String() //outLocalAddr := outConn.LocalAddr().String() - if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") { //https无上级或者上级非代理,proxy需要响应connect请求,并直连目标 err = req.HTTPSReply() } else { //https或者http,上级是代理,proxy需要转发 + outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = outConn.Write(req.HeadBuf) + outConn.SetDeadline(time.Time{}) if err != nil { log.Printf("write to %s , err:%s", *s.cfg.Parent, err) utils.CloseConn(inConn) @@ -250,9 +317,13 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut utils.IoBind((*inConn), outConn, func(err interface{}) { log.Printf("conn %s - %s released [%s]", inAddr, outAddr, req.Host) + s.userConns.Remove(inAddr) }) log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, req.Host) - + if c, ok := s.userConns.Get(inAddr); ok { + (*c.(*net.Conn)).Close() + } + s.userConns.Set(inAddr, inConn) return } @@ -260,7 +331,7 @@ func (s *HTTP) getSSHConn(host string) (outConn net.Conn, err interface{}) { maxTryCount := 1 tryCount := 0 RETRY: - if tryCount >= maxTryCount { + if tryCount >= maxTryCount || s.isStop { return } wait := make(chan bool, 1) @@ -317,16 +388,13 @@ func (s *HTTP) InitOutConnPool() { if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP || *s.cfg.ParentType == TYPE_KCP { //dur int, isTLS bool, certBytes, keyBytes []byte, //parent string, timeout int, InitialCap int, MaxCap int - s.outPool = utils.NewOutPool( + s.outPool = utils.NewOutConn( *s.cfg.CheckParentInterval, *s.cfg.ParentType, - *s.cfg.KCPMethod, - *s.cfg.KCPKey, - s.cfg.CertBytes, s.cfg.KeyBytes, + s.cfg.KCP, + s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.Resolve(*s.cfg.Parent), *s.cfg.Timeout, - *s.cfg.PoolSize, - *s.cfg.PoolSize*2, ) } } diff --git a/services/kcpcfg/args.go b/services/kcpcfg/args.go new file mode 100644 index 0000000..5d3b67c --- /dev/null +++ b/services/kcpcfg/args.go @@ -0,0 +1,24 @@ +package kcpcfg + +import kcp "github.com/xtaci/kcp-go" + +type KCPConfigArgs struct { + Key *string + Crypt *string + Mode *string + MTU *int + SndWnd *int + RcvWnd *int + DataShard *int + ParityShard *int + DSCP *int + NoComp *bool + AckNodelay *bool + NoDelay *int + Interval *int + Resend *int + NoCongestion *int + SockBuf *int + KeepAlive *int + Block kcp.BlockCrypt +} diff --git a/services/mux_bridge.go b/services/mux_bridge.go index e2feac1..8440d21 100644 --- a/services/mux_bridge.go +++ b/services/mux_bridge.go @@ -2,11 +2,15 @@ package services import ( "bufio" + "fmt" "io" "log" + "math/rand" "net" "snail007/proxy/utils" "strconv" + "strings" + "sync" "time" "github.com/xtaci/smux" @@ -15,108 +19,207 @@ import ( type MuxBridge struct { cfg MuxBridgeArgs clientControlConns utils.ConcurrentMap + serverConns utils.ConcurrentMap router utils.ClientKeyRouter + l *sync.Mutex + isStop bool + sc *utils.ServerChannel } func NewMuxBridge() Service { b := &MuxBridge{ cfg: MuxBridgeArgs{}, clientControlConns: utils.NewConcurrentMap(), + serverConns: utils.NewConcurrentMap(), + l: &sync.Mutex{}, + isStop: false, } b.router = utils.NewClientKeyRouter(&b.clientControlConns, 50000) return b } -func (s *MuxBridge) InitService() { - +func (s *MuxBridge) InitService() (err error) { + return } -func (s *MuxBridge) CheckArgs() { +func (s *MuxBridge) CheckArgs() (err error) { if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { - log.Fatalf("cert and key file required") + err = fmt.Errorf("cert and key file required") + return } - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + if *s.cfg.LocalType == TYPE_TLS { + s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + if err != nil { + return + } + } + return } func (s *MuxBridge) StopService() { - + defer func() { + e := recover() + if e != nil { + log.Printf("stop bridge service crashed,%s", e) + } else { + log.Printf("service bridge stoped") + } + }() + s.isStop = true + if s.sc != nil && (*s.sc).Listener != nil { + (*(*s.sc).Listener).Close() + } + for _, g := range s.clientControlConns.Items() { + for _, session := range g.(utils.ConcurrentMap).Items() { + (session.(*smux.Session)).Close() + } + } + for _, c := range s.serverConns.Items() { + (*c.(*net.Conn)).Close() + } } func (s *MuxBridge) Start(args interface{}) (err error) { s.cfg = args.(MuxBridgeArgs) - s.CheckArgs() - s.InitService() + if err = s.CheckArgs(); err != nil { + return + } + if err = s.InitService(); err != nil { + return + } + host, port, _ := net.SplitHostPort(*s.cfg.Local) p, _ := strconv.Atoi(port) sc := utils.NewServerChannel(host, p) - - err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, func(inConn net.Conn) { - reader := bufio.NewReader(inConn) - - var err error - var connType uint8 - var key string - err = utils.ReadPacket(reader, &connType, &key) - if err != nil { - log.Printf("read error,ERR:%s", err) - return - } - switch connType { - case CONN_SERVER: - var serverID string - err = utils.ReadPacketData(reader, &serverID) - if err != nil { - log.Printf("read error,ERR:%s", err) - return - } - log.Printf("server connection %s %s connected", serverID, key) - session, err := smux.Server(inConn, nil) - if err != nil { - utils.CloseConn(&inConn) - log.Printf("server session error,ERR:%s", err) - return - } - for { - stream, err := session.AcceptStream() - if err != nil { - session.Close() - utils.CloseConn(&inConn) - return - } - go s.callback(stream, serverID, key) - } - case CONN_CLIENT: - - log.Printf("client connection %s connected", key) - session, err := smux.Client(inConn, nil) - if err != nil { - utils.CloseConn(&inConn) - log.Printf("client session error,ERR:%s", err) - return - } - s.clientControlConns.Set(key, session) - go func() { - for { - if session.IsClosed() { - s.clientControlConns.Remove(key) - break - } - time.Sleep(time.Second * 5) - } - }() - //log.Printf("set client session,key: %s", key) - } - - }) + if *s.cfg.LocalType == TYPE_TCP { + err = sc.ListenTCP(s.handler) + } else if *s.cfg.LocalType == TYPE_TLS { + err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.handler) + } else if *s.cfg.LocalType == TYPE_KCP { + err = sc.ListenKCP(s.cfg.KCP, s.handler) + } if err != nil { return } - log.Printf("proxy on mux bridge mode %s", (*sc.Listener).Addr()) + s.sc = &sc + log.Printf("%s bridge on %s", *s.cfg.LocalType, (*sc.Listener).Addr()) return } func (s *MuxBridge) Clean() { s.StopService() } +func (s *MuxBridge) handler(inConn net.Conn) { + reader := bufio.NewReader(inConn) + + var err error + var connType uint8 + var key string + inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) + err = utils.ReadPacket(reader, &connType, &key) + inConn.SetDeadline(time.Time{}) + if err != nil { + log.Printf("read error,ERR:%s", err) + return + } + switch connType { + case CONN_SERVER: + var serverID string + inAddr := inConn.RemoteAddr().String() + inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) + err = utils.ReadPacketData(reader, &serverID) + inConn.SetDeadline(time.Time{}) + if err != nil { + log.Printf("read error,ERR:%s", err) + return + } + log.Printf("server connection %s %s connected", serverID, key) + if c, ok := s.serverConns.Get(inAddr); ok { + (*c.(*net.Conn)).Close() + } + s.serverConns.Set(inAddr, &inConn) + session, err := smux.Server(inConn, nil) + if err != nil { + utils.CloseConn(&inConn) + log.Printf("server session error,ERR:%s", err) + return + } + for { + if s.isStop { + return + } + stream, err := session.AcceptStream() + if err != nil { + session.Close() + utils.CloseConn(&inConn) + s.serverConns.Remove(inAddr) + log.Printf("server connection %s %s released", serverID, key) + return + } + go func() { + defer func() { + if e := recover(); e != nil { + log.Printf("bridge callback crashed,err: %s", e) + } + }() + s.callback(stream, serverID, key) + }() + } + case CONN_CLIENT: + log.Printf("client connection %s connected", key) + session, err := smux.Client(inConn, nil) + if err != nil { + utils.CloseConn(&inConn) + log.Printf("client session error,ERR:%s", err) + return + } + keyInfo := strings.Split(key, "-") + if len(keyInfo) != 2 { + utils.CloseConn(&inConn) + log.Printf("client key format error,key:%s", key) + return + } + groupKey := keyInfo[0] + index := keyInfo[1] + s.l.Lock() + defer s.l.Unlock() + if !s.clientControlConns.Has(groupKey) { + item := utils.NewConcurrentMap() + s.clientControlConns.Set(groupKey, &item) + } + _group, _ := s.clientControlConns.Get(groupKey) + group := _group.(*utils.ConcurrentMap) + if v, ok := group.Get(index); ok { + v.(*smux.Session).Close() + } + group.Set(index, session) + // s.clientControlConns.Set(key, session) + go func() { + for { + if s.isStop { + return + } + if session.IsClosed() { + s.l.Lock() + defer s.l.Unlock() + if sess, ok := group.Get(index); ok && sess.(*smux.Session).IsClosed() { + group.Remove(index) + log.Printf("client connection %s released", key) + } + if group.IsEmpty() { + s.clientControlConns.Remove(groupKey) + } + break + } + time.Sleep(time.Second * 5) + } + }() + //log.Printf("set client session,key: %s", key) + } + +} func (s *MuxBridge) callback(inConn net.Conn, serverID, key string) { try := 20 for { + if s.isStop { + return + } try-- if try == 0 { break @@ -124,19 +227,35 @@ func (s *MuxBridge) callback(inConn net.Conn, serverID, key string) { if key == "*" { key = s.router.GetKey() } - session, ok := s.clientControlConns.Get(key) + _group, ok := s.clientControlConns.Get(key) if !ok { - log.Printf("client %s session not exists for server stream %s", key, serverID) + log.Printf("client %s session not exists for server stream %s, retrying...", key, serverID) time.Sleep(time.Second * 3) continue } + group := _group.(*utils.ConcurrentMap) + keys := group.Keys() + keysLen := len(keys) + i := 0 + if keysLen > 0 { + i = rand.Intn(keysLen) + } else { + log.Printf("client %s session empty for server stream %s, retrying...", key, serverID) + time.Sleep(time.Second * 3) + continue + } + index := keys[i] + log.Printf("select client : %s-%s", key, index) + session, _ := group.Get(index) + session.(*smux.Session).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) stream, err := session.(*smux.Session).OpenStream() + session.(*smux.Session).SetDeadline(time.Time{}) if err != nil { log.Printf("%s client session open stream %s fail, err: %s, retrying...", key, serverID, err) time.Sleep(time.Second * 3) continue } else { - log.Printf("%s server %s stream created", key, serverID) + log.Printf("stream %s -> %s created", serverID, key) die1 := make(chan bool, 1) die2 := make(chan bool, 1) go func() { diff --git a/services/mux_client.go b/services/mux_client.go index fa45a78..1c8d772 100644 --- a/services/mux_client.go +++ b/services/mux_client.go @@ -2,6 +2,7 @@ package services import ( "crypto/tls" + "fmt" "io" "log" "net" @@ -13,98 +14,177 @@ import ( ) type MuxClient struct { - cfg MuxClientArgs + cfg MuxClientArgs + isStop bool + sessions utils.ConcurrentMap } func NewMuxClient() Service { return &MuxClient{ - cfg: MuxClientArgs{}, + cfg: MuxClientArgs{}, + isStop: false, + sessions: utils.NewConcurrentMap(), } } -func (s *MuxClient) InitService() { - +func (s *MuxClient) InitService() (err error) { + return } -func (s *MuxClient) CheckArgs() { +func (s *MuxClient) CheckArgs() (err error) { if *s.cfg.Parent != "" { log.Printf("use tls parent %s", *s.cfg.Parent) } else { - log.Fatalf("parent required") + err = fmt.Errorf("parent required") + return } if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { - log.Fatalf("cert and key file required") + err = fmt.Errorf("cert and key file required") + return } - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + if *s.cfg.ParentType == "tls" { + s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + if err != nil { + return + } + } + return } func (s *MuxClient) StopService() { - + defer func() { + e := recover() + if e != nil { + log.Printf("stop client service crashed,%s", e) + } else { + log.Printf("service client stoped") + } + }() + s.isStop = true + for _, sess := range s.sessions.Items() { + sess.(*smux.Session).Close() + } } func (s *MuxClient) Start(args interface{}) (err error) { s.cfg = args.(MuxClientArgs) - s.CheckArgs() - s.InitService() - log.Printf("proxy on mux client mode, compress %v", *s.cfg.IsCompress) - for { - var _conn tls.Conn - _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes) - if err != nil { - log.Printf("connection err: %s, retrying...", err) - time.Sleep(time.Second * 3) - continue - } - conn := net.Conn(&_conn) - _, err = conn.Write(utils.BuildPacket(CONN_CLIENT, *s.cfg.Key)) - if err != nil { - conn.Close() - log.Printf("connection err: %s, retrying...", err) - time.Sleep(time.Second * 3) - continue - } - session, err := smux.Server(conn, nil) - if err != nil { - log.Printf("session err: %s, retrying...", err) - conn.Close() - time.Sleep(time.Second * 3) - continue - } - for { - stream, err := session.AcceptStream() - if err != nil { - log.Printf("accept stream err: %s, retrying...", err) - session.Close() - time.Sleep(time.Second * 3) - break - } - go func() { - var ID, clientLocalAddr, serverID string - err = utils.ReadPacketData(stream, &ID, &clientLocalAddr, &serverID) - if err != nil { - log.Printf("read stream signal err: %s", err) - stream.Close() - return - } - log.Printf("signal revecived,server %s stream %s %s", serverID, ID, clientLocalAddr) - protocol := clientLocalAddr[:3] - localAddr := clientLocalAddr[4:] - if protocol == "udp" { - s.ServeUDP(stream, localAddr, ID) - } else { - s.ServeConn(stream, localAddr, ID) + if err = s.CheckArgs(); err != nil { + return + } + if err = s.InitService(); err != nil { + return + } + log.Printf("client started") + count := 1 + if *s.cfg.SessionCount > 0 { + count = *s.cfg.SessionCount + } + for i := 1; i <= count; i++ { + key := fmt.Sprintf("worker[%d]", i) + log.Printf("session %s started", key) + go func(i int) { + defer func() { + e := recover() + if e != nil { + log.Printf("session worker crashed: %s", e) } }() - } + for { + if s.isStop { + return + } + conn, err := s.getParentConn() + if err != nil { + log.Printf("connection err: %s, retrying...", err) + time.Sleep(time.Second * 3) + continue + } + conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) + _, err = conn.Write(utils.BuildPacket(CONN_CLIENT, fmt.Sprintf("%s-%d", *s.cfg.Key, i))) + conn.SetDeadline(time.Time{}) + if err != nil { + conn.Close() + log.Printf("connection err: %s, retrying...", err) + time.Sleep(time.Second * 3) + continue + } + session, err := smux.Server(conn, nil) + if err != nil { + log.Printf("session err: %s, retrying...", err) + conn.Close() + time.Sleep(time.Second * 3) + continue + } + if _sess, ok := s.sessions.Get(key); ok { + _sess.(*smux.Session).Close() + } + s.sessions.Set(key, session) + for { + if s.isStop { + return + } + stream, err := session.AcceptStream() + if err != nil { + log.Printf("accept stream err: %s, retrying...", err) + session.Close() + time.Sleep(time.Second * 3) + break + } + go func() { + defer func() { + e := recover() + if e != nil { + log.Printf("stream handler crashed: %s", e) + } + }() + var ID, clientLocalAddr, serverID string + stream.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) + err = utils.ReadPacketData(stream, &ID, &clientLocalAddr, &serverID) + stream.SetDeadline(time.Time{}) + if err != nil { + log.Printf("read stream signal err: %s", err) + stream.Close() + return + } + log.Printf("worker[%d] signal revecived,server %s stream %s %s", i, serverID, ID, clientLocalAddr) + protocol := clientLocalAddr[:3] + localAddr := clientLocalAddr[4:] + if protocol == "udp" { + s.ServeUDP(stream, localAddr, ID) + } else { + s.ServeConn(stream, localAddr, ID) + } + }() + } + } + }(i) } - + return } func (s *MuxClient) Clean() { s.StopService() } - +func (s *MuxClient) getParentConn() (conn net.Conn, err error) { + if *s.cfg.ParentType == "tls" { + var _conn tls.Conn + _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err == nil { + conn = net.Conn(&_conn) + } + } else if *s.cfg.ParentType == "kcp" { + conn, err = utils.ConnectKCPHost(*s.cfg.Parent, s.cfg.KCP) + } else { + conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout) + } + return +} func (s *MuxClient) ServeUDP(inConn *smux.Stream, localAddr, ID string) { for { + if s.isStop { + return + } + inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) srcAddr, body, err := utils.ReadUDPPacket(inConn) + inConn.SetDeadline(time.Time{}) if err != nil { log.Printf("udp packet revecived fail, err: %s", err) log.Printf("connection %s released", ID) @@ -112,7 +192,15 @@ func (s *MuxClient) ServeUDP(inConn *smux.Stream, localAddr, ID string) { break } else { //log.Printf("udp packet revecived:%s,%v", srcAddr, body) - go s.processUDPPacket(inConn, srcAddr, localAddr, body) + go func() { + defer func() { + if e := recover(); e != nil { + log.Printf("client processUDPPacket crashed,err: %s", e) + } + }() + s.processUDPPacket(inConn, srcAddr, localAddr, body) + }() + } } @@ -133,13 +221,16 @@ func (s *MuxClient) processUDPPacket(inConn *smux.Stream, srcAddr, localAddr str } conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = conn.Write(body) + conn.SetDeadline(time.Time{}) if err != nil { log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) return } //log.Printf("send udp packet to %s success", dstAddr.String()) buf := make([]byte, 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 { log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) return @@ -147,7 +238,9 @@ func (s *MuxClient) processUDPPacket(inConn *smux.Stream, srcAddr, localAddr str respBody := buf[0:length] //log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody) bs := utils.UDPPacket(srcAddr, respBody) + (*inConn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = (*inConn).Write(bs) + (*inConn).SetDeadline(time.Time{}) if err != nil { log.Printf("send udp response fail ,ERR:%s", err) inConn.Close() @@ -160,6 +253,9 @@ func (s *MuxClient) ServeConn(inConn *smux.Stream, localAddr, ID string) { var outConn net.Conn i := 0 for { + if s.isStop { + return + } i++ outConn, err = utils.ConnectHost(localAddr, *s.cfg.Timeout) if err == nil || i == 3 { diff --git a/services/mux_server.go b/services/mux_server.go index 7699b7b..d5a4860 100644 --- a/services/mux_server.go +++ b/services/mux_server.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "log" + "math/rand" "net" "runtime/debug" "snail007/proxy/utils" @@ -17,18 +18,20 @@ import ( ) type MuxServer struct { - cfg MuxServerArgs - udpChn chan MuxUDPItem - sc utils.ServerChannel - session *smux.Session - lockChn chan bool + cfg MuxServerArgs + udpChn chan MuxUDPItem + sc utils.ServerChannel + sessions utils.ConcurrentMap + lockChn chan bool + isStop bool + udpConn *net.Conn } type MuxServerManager struct { cfg MuxServerArgs udpChn chan MuxUDPItem - sc utils.ServerChannel serverID string + servers []*Service } func NewMuxServerManager() Service { @@ -36,18 +39,25 @@ func NewMuxServerManager() Service { cfg: MuxServerArgs{}, udpChn: make(chan MuxUDPItem, 50000), serverID: utils.Uniqueid(), + servers: []*Service{}, } } + func (s *MuxServerManager) Start(args interface{}) (err error) { s.cfg = args.(MuxServerArgs) - s.CheckArgs() + if err = s.CheckArgs(); err != nil { + return + } if *s.cfg.Parent != "" { - log.Printf("use tls parent %s", *s.cfg.Parent) + log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) } else { - log.Fatalf("parent required") + err = fmt.Errorf("parent required") + return } - s.InitService() + if err = s.InitService(); err != nil { + return + } log.Printf("server id: %s", s.serverID) //log.Printf("route:%v", *s.cfg.Route) @@ -74,23 +84,27 @@ func (s *MuxServerManager) Start(args interface{}) (err error) { remote = fmt.Sprintf("127.0.0.1%s", remote) } err = server.Start(MuxServerArgs{ - CertBytes: s.cfg.CertBytes, - KeyBytes: s.cfg.KeyBytes, - Parent: s.cfg.Parent, - CertFile: s.cfg.CertFile, - KeyFile: s.cfg.KeyFile, - Local: &local, - IsUDP: &IsUDP, - Remote: &remote, - Key: &KEY, - Timeout: s.cfg.Timeout, - Mgr: s, - IsCompress: s.cfg.IsCompress, + CertBytes: s.cfg.CertBytes, + KeyBytes: s.cfg.KeyBytes, + Parent: s.cfg.Parent, + CertFile: s.cfg.CertFile, + KeyFile: s.cfg.KeyFile, + Local: &local, + IsUDP: &IsUDP, + Remote: &remote, + Key: &KEY, + Timeout: s.cfg.Timeout, + Mgr: s, + IsCompress: s.cfg.IsCompress, + SessionCount: s.cfg.SessionCount, + KCP: s.cfg.KCP, + ParentType: s.cfg.ParentType, }) if err != nil { return } + s.servers = append(s.servers, &server) } return } @@ -98,21 +112,34 @@ func (s *MuxServerManager) Clean() { s.StopService() } func (s *MuxServerManager) StopService() { -} -func (s *MuxServerManager) CheckArgs() { - if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { - log.Fatalf("cert and key file required") + for _, server := range s.servers { + (*server).Clean() } - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) } -func (s *MuxServerManager) InitService() { +func (s *MuxServerManager) CheckArgs() (err error) { + if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { + err = fmt.Errorf("cert and key file required") + return + } + if *s.cfg.ParentType == "tls" { + s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + if err != nil { + return + } + } + return +} +func (s *MuxServerManager) InitService() (err error) { + return } func NewMuxServer() Service { return &MuxServer{ - cfg: MuxServerArgs{}, - udpChn: make(chan MuxUDPItem, 50000), - lockChn: make(chan bool, 1), + cfg: MuxServerArgs{}, + udpChn: make(chan MuxUDPItem, 50000), + lockChn: make(chan bool, 1), + sessions: utils.NewConcurrentMap(), + isStop: false, } } @@ -122,19 +149,49 @@ type MuxUDPItem struct { srcAddr *net.UDPAddr } -func (s *MuxServer) InitService() { - s.UDPConnDeamon() -} -func (s *MuxServer) CheckArgs() { - if *s.cfg.Remote == "" { - log.Fatalf("remote required") +func (s *MuxServer) StopService() { + defer func() { + e := recover() + if e != nil { + log.Printf("stop server service crashed,%s", e) + } else { + log.Printf("service server stoped") + } + }() + s.isStop = true + for _, sess := range s.sessions.Items() { + sess.(*smux.Session).Close() } + if s.sc.Listener != nil { + (*s.sc.Listener).Close() + } + if s.sc.UDPListener != nil { + (*s.sc.UDPListener).Close() + } + if s.udpConn != nil { + (*s.udpConn).Close() + } +} +func (s *MuxServer) InitService() (err error) { + s.UDPConnDeamon() + return +} +func (s *MuxServer) CheckArgs() (err error) { + if *s.cfg.Remote == "" { + err = fmt.Errorf("remote required") + return + } + return } func (s *MuxServer) Start(args interface{}) (err error) { s.cfg = args.(MuxServerArgs) - s.CheckArgs() - s.InitService() + if err = s.CheckArgs(); err != nil { + return + } + if err = s.InitService(); err != nil { + return + } host, port, _ := net.SplitHostPort(*s.cfg.Local) p, _ := strconv.Atoi(port) s.sc = utils.NewServerChannel(host, p) @@ -149,7 +206,7 @@ func (s *MuxServer) Start(args interface{}) (err error) { if err != nil { return } - log.Printf("proxy on udp mux server mode %s", (*s.sc.UDPListener).LocalAddr()) + log.Printf("server on %s", (*s.sc.UDPListener).LocalAddr()) } else { err = s.sc.ListenTCP(func(inConn net.Conn) { defer func() { @@ -160,6 +217,9 @@ func (s *MuxServer) Start(args interface{}) (err error) { var outConn net.Conn var ID string for { + if s.isStop { + return + } outConn, ID, err = s.GetOutConn() if err != nil { utils.CloseConn(&outConn) @@ -198,15 +258,19 @@ func (s *MuxServer) Start(args interface{}) (err error) { if err != nil { return } - log.Printf("proxy on mux server mode %s, compress %v", (*s.sc.Listener).Addr(), *s.cfg.IsCompress) + log.Printf("server on %s", (*s.sc.Listener).Addr()) } return } func (s *MuxServer) Clean() { - + s.StopService() } func (s *MuxServer) GetOutConn() (outConn net.Conn, ID string, err error) { - outConn, err = s.GetConn() + i := 1 + if *s.cfg.SessionCount > 0 { + i = rand.Intn(*s.cfg.SessionCount) + } + outConn, err = s.GetConn(fmt.Sprintf("%d", i)) if err != nil { log.Printf("connection err: %s", err) return @@ -216,7 +280,9 @@ func (s *MuxServer) GetOutConn() (outConn net.Conn, ID string, err error) { remoteAddr = "udp:" + *s.cfg.Remote } ID = utils.Uniqueid() + outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = outConn.Write(utils.BuildPacketData(ID, remoteAddr, s.cfg.Mgr.serverID)) + outConn.SetDeadline(time.Time{}) if err != nil { log.Printf("write stream data err: %s ,retrying...", err) utils.CloseConn(&outConn) @@ -224,7 +290,7 @@ func (s *MuxServer) GetOutConn() (outConn net.Conn, ID string, err error) { } return } -func (s *MuxServer) GetConn() (conn net.Conn, err error) { +func (s *MuxServer) GetConn(index string) (conn net.Conn, err error) { select { case s.lockChn <- true: default: @@ -234,34 +300,66 @@ func (s *MuxServer) GetConn() (conn net.Conn, err error) { defer func() { <-s.lockChn }() - if s.session == nil { - var _conn tls.Conn - _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes) + var session *smux.Session + _session, ok := s.sessions.Get(index) + if !ok { + var c net.Conn + c, err = s.getParentConn() if err != nil { - s.session = nil return } - c := net.Conn(&_conn) + c.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = c.Write(utils.BuildPacket(CONN_SERVER, *s.cfg.Key, s.cfg.Mgr.serverID)) + c.SetDeadline(time.Time{}) if err != nil { c.Close() - s.session = nil return } if err == nil { - s.session, err = smux.Client(c, nil) + session, err = smux.Client(c, nil) if err != nil { - s.session = nil return } } + if _sess, ok := s.sessions.Get(index); ok { + _sess.(*smux.Session).Close() + } + s.sessions.Set(index, session) + log.Printf("session[%s] created", index) + go func() { + for { + if s.isStop { + return + } + if session.IsClosed() { + s.sessions.Remove(index) + break + } + time.Sleep(time.Second * 5) + } + }() + } else { + session = _session.(*smux.Session) } - conn, err = s.session.OpenStream() + conn, err = session.OpenStream() if err != nil { - s.session.Close() - s.session = nil + session.Close() + s.sessions.Remove(index) + } + return +} +func (s *MuxServer) getParentConn() (conn net.Conn, err error) { + if *s.cfg.ParentType == "tls" { + var _conn tls.Conn + _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) + if err == nil { + conn = net.Conn(&_conn) + } + } else if *s.cfg.ParentType == "kcp" { + conn, err = utils.ConnectKCPHost(*s.cfg.Parent, s.cfg.KCP) + } else { + conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout) } - return } func (s *MuxServer) UDPConnDeamon() { @@ -275,10 +373,19 @@ func (s *MuxServer) UDPConnDeamon() { var ID string var err error for { + if s.isStop { + return + } item := <-s.udpChn RETRY: + if s.isStop { + return + } if outConn == nil { for { + if s.isStop { + return + } outConn, ID, err = s.GetOutConn() if err != nil { outConn = nil @@ -288,11 +395,17 @@ func (s *MuxServer) UDPConnDeamon() { continue } else { go func(outConn net.Conn, ID string) { - go func() { - // outConn.Close() - }() + if s.udpConn != nil { + (*s.udpConn).Close() + } + s.udpConn = &outConn for { + if s.isStop { + return + } + outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) srcAddrFromConn, body, err := utils.ReadUDPPacket(outConn) + outConn.SetDeadline(time.Time{}) if err != nil { log.Printf("parse revecived udp packet fail, err: %s ,%v", err, body) log.Printf("UDP deamon connection %s exited", ID) @@ -306,7 +419,9 @@ func (s *MuxServer) UDPConnDeamon() { } port, _ := strconv.Atoi(_srcAddr[1]) dstAddr := &net.UDPAddr{IP: net.ParseIP(_srcAddr[0]), Port: port} + s.sc.UDPListener.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = s.sc.UDPListener.WriteToUDP(body, dstAddr) + s.sc.UDPListener.SetDeadline(time.Time{}) if err != nil { log.Printf("udp response to local %s fail,ERR:%s", srcAddrFromConn, err) continue diff --git a/services/service.go b/services/service.go index de280af..4d06d40 100644 --- a/services/service.go +++ b/services/service.go @@ -2,7 +2,6 @@ package services import ( "fmt" - "log" "runtime/debug" ) @@ -19,33 +18,43 @@ type ServiceItem struct { var servicesMap = map[string]*ServiceItem{} func Regist(name string, s Service, args interface{}) { + Stop(name) servicesMap[name] = &ServiceItem{ S: s, Args: args, Name: name, } } +func GetService(name string) *ServiceItem { + if s, ok := servicesMap[name]; ok && s.S != nil { + return s + } + return nil + +} +func Stop(name string) { + if s, ok := servicesMap[name]; ok && s.S != nil { + s.S.Clean() + } +} func Run(name string, args ...interface{}) (service *ServiceItem, err error) { service, ok := servicesMap[name] if ok { - go func() { - defer func() { - err := recover() - if err != nil { - log.Fatalf("%s servcie crashed, ERR: %s\ntrace:%s", name, err, string(debug.Stack())) - } - }() - if len(args) == 1 { - err = service.S.Start(args[0]) - } else { - err = service.S.Start(service.Args) - } - if err != nil { - log.Fatalf("%s servcie fail, ERR: %s", name, err) + defer func() { + e := recover() + if e != nil { + err = fmt.Errorf("%s servcie crashed, ERR: %s\ntrace:%s", name, e, string(debug.Stack())) } }() - } - if !ok { + if len(args) == 1 { + err = service.S.Start(args[0]) + } else { + err = service.S.Start(service.Args) + } + if err != nil { + err = fmt.Errorf("%s servcie fail, ERR: %s", name, err) + } + } else { err = fmt.Errorf("service %s not found", name) } return diff --git a/services/socks.go b/services/socks.go index c123c17..4affee3 100644 --- a/services/socks.go +++ b/services/socks.go @@ -9,6 +9,7 @@ import ( "runtime/debug" "snail007/proxy/utils" "snail007/proxy/utils/aes" + "snail007/proxy/utils/conncrypt" "snail007/proxy/utils/socks" "strings" "time" @@ -23,7 +24,10 @@ type Socks struct { sshClient *ssh.Client lockChn chan bool udpSC utils.ServerChannel + sc *utils.ServerChannel domainResolver utils.DomainResolver + isStop bool + userConns utils.ConcurrentMap } func NewSocks() Service { @@ -32,27 +36,39 @@ func NewSocks() Service { checker: utils.Checker{}, basicAuth: utils.BasicAuth{}, lockChn: make(chan bool, 1), + isStop: false, + userConns: utils.NewConcurrentMap(), } } -func (s *Socks) CheckArgs() { - var err error - if *s.cfg.LocalType == "tls" { - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) +func (s *Socks) CheckArgs() (err error) { + + if *s.cfg.LocalType == "tls" || (*s.cfg.Parent != "" && *s.cfg.ParentType == "tls") { + s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + if err != nil { + return + } + if *s.cfg.CaCertFile != "" { + s.cfg.CaCertBytes, err = ioutil.ReadFile(*s.cfg.CaCertFile) + if err != nil { + err = fmt.Errorf("read ca file error,ERR:%s", err) + return + } + } } if *s.cfg.Parent != "" { if *s.cfg.ParentType == "" { - log.Fatalf("parent type unkown,use -T ") - } - if *s.cfg.ParentType == "tls" { - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + err = fmt.Errorf("parent type unkown,use -T ") + return } if *s.cfg.ParentType == "ssh" { if *s.cfg.SSHUser == "" { - log.Fatalf("ssh user required") + err = fmt.Errorf("ssh user required") + return } if *s.cfg.SSHKeyFile == "" && *s.cfg.SSHPassword == "" { - log.Fatalf("ssh password or key required") + err = fmt.Errorf("ssh password or key required") + return } if *s.cfg.SSHPassword != "" { s.cfg.SSHAuthMethod = ssh.Password(*s.cfg.SSHPassword) @@ -60,7 +76,8 @@ func (s *Socks) CheckArgs() { var SSHSigner ssh.Signer s.cfg.SSHKeyBytes, err = ioutil.ReadFile(*s.cfg.SSHKeyFile) if err != nil { - log.Fatalf("read key file ERR: %s", err) + err = fmt.Errorf("read key file ERR: %s", err) + return } if *s.cfg.SSHKeyFileSalt != "" { SSHSigner, err = ssh.ParsePrivateKeyWithPassphrase(s.cfg.SSHKeyBytes, []byte(*s.cfg.SSHKeyFileSalt)) @@ -68,31 +85,38 @@ func (s *Socks) CheckArgs() { SSHSigner, err = ssh.ParsePrivateKey(s.cfg.SSHKeyBytes) } if err != nil { - log.Fatalf("parse ssh private key fail,ERR: %s", err) + err = fmt.Errorf("parse ssh private key fail,ERR: %s", err) + return } s.cfg.SSHAuthMethod = ssh.PublicKeys(SSHSigner) } } } - + return } -func (s *Socks) InitService() { +func (s *Socks) InitService() (err error) { s.InitBasicAuth() if *s.cfg.DNSAddress != "" { (*s).domainResolver = utils.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL) } s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct) if *s.cfg.ParentType == "ssh" { - err := s.ConnectSSH() - if err != nil { - log.Fatalf("init service fail, ERR: %s", err) + e := s.ConnectSSH() + if e != nil { + err = fmt.Errorf("init service fail, ERR: %s", e) + return } go func() { //循环检查ssh网络连通性 for { + if s.isStop { + return + } conn, err := utils.ConnectHost(s.Resolve(*s.cfg.Parent), *s.cfg.Timeout*2) if err == nil { + conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = conn.Write([]byte{0}) + conn.SetDeadline(time.Time{}) } if err != nil { if s.sshClient != nil { @@ -111,26 +135,48 @@ func (s *Socks) InitService() { log.Println("warn: socks udp not suppored for ssh") } else { s.udpSC = utils.NewServerChannelHost(*s.cfg.UDPLocal) - err := s.udpSC.ListenUDP(s.udpCallback) - if err != nil { - log.Fatalf("init udp service fail, ERR: %s", err) + e := s.udpSC.ListenUDP(s.udpCallback) + if e != nil { + err = fmt.Errorf("init udp service fail, ERR: %s", e) + return } log.Printf("udp socks proxy on %s", s.udpSC.UDPListener.LocalAddr()) } + return } func (s *Socks) StopService() { + defer func() { + e := recover() + if e != nil { + log.Printf("stop socks service crashed,%s", e) + } else { + log.Printf("service socks stoped") + } + }() + s.isStop = true + s.checker.Stop() if s.sshClient != nil { s.sshClient.Close() } if s.udpSC.UDPListener != nil { s.udpSC.UDPListener.Close() } + if s.sc != nil && (*s.sc).Listener != nil { + (*(*s.sc).Listener).Close() + } + for _, c := range s.userConns.Items() { + (*c.(*net.Conn)).Close() + } } func (s *Socks) Start(args interface{}) (err error) { //start() s.cfg = args.(SocksArgs) - s.CheckArgs() - s.InitService() + if err = s.CheckArgs(); err != nil { + return + } + if err = s.InitService(); err != nil { + s.InitService() + } if *s.cfg.Parent != "" { log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) } @@ -138,13 +184,14 @@ func (s *Socks) Start(args interface{}) (err error) { if *s.cfg.LocalType == TYPE_TCP { err = sc.ListenTCP(s.socksConnCallback) } else if *s.cfg.LocalType == TYPE_TLS { - err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.socksConnCallback) + err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.socksConnCallback) } else if *s.cfg.LocalType == TYPE_KCP { - err = sc.ListenKCP(*s.cfg.KCPMethod, *s.cfg.KCPKey, s.socksConnCallback) + err = sc.ListenKCP(s.cfg.KCP, s.socksConnCallback) } if err != nil { return } + s.sc = &sc log.Printf("%s socks proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr()) return } @@ -204,6 +251,7 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) { } conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*5))) _, err = conn.Write(rawB) + conn.SetDeadline(time.Time{}) log.Printf("udp request:%v", len(rawB)) if err != nil { log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) @@ -213,7 +261,9 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) { //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 { log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) conn.Close() @@ -238,10 +288,14 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) { 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{}) log.Printf("udp reply:%v", len(d)) } 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{}) log.Printf("udp reply:%v", len(respBody)) } @@ -260,6 +314,7 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) { } conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*3))) _, err = conn.Write(p.Data()) + conn.SetDeadline(time.Time{}) log.Printf("udp send:%v", len(p.Data())) if err != nil { log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) @@ -268,7 +323,10 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) { } //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 { log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) conn.Close() @@ -285,9 +343,13 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) { 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{}) } 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{}) } log.Printf("udp reply:%v", len(respPacket)) } @@ -300,6 +362,14 @@ func (s *Socks) socksConnCallback(inConn net.Conn) { inConn.Close() } }() + if *s.cfg.LocalCompress { + inConn = utils.NewCompConn(inConn) + } + if *s.cfg.LocalKey != "" { + inConn = conncrypt.New(inConn, &conncrypt.Config{ + Password: *s.cfg.LocalKey, + }) + } //协商开始 //method select request @@ -359,9 +429,15 @@ func (s *Socks) socksConnCallback(inConn net.Conn) { //auth _addr := strings.Split(inConn.RemoteAddr().String(), ":") if s.basicAuth.CheckUserPass(user, pass, _addr[0], "") { + inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) inConn.Write([]byte{0x01, 0x00}) + inConn.SetDeadline(time.Time{}) + } else { + inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) inConn.Write([]byte{0x01, 0x01}) + inConn.SetDeadline(time.Time{}) + utils.CloseConn(&inConn) return } @@ -415,6 +491,9 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque return } for { + if s.isStop { + return + } if *s.cfg.Always { outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr()) } else { @@ -451,6 +530,7 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque request.TCPReply(socks.REP_NETWOR_UNREACHABLE) return } + log.Printf("use proxy %v : %s", useProxy, request.Addr()) request.TCPReply(socks.REP_SUCCESS) @@ -461,6 +541,11 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque utils.IoBind(*inConn, outConn, func(err interface{}) { log.Printf("conn %s - %s released", inAddr, request.Addr()) }) + if c, ok := s.userConns.Get(inAddr); ok { + (*c.(*net.Conn)).Close() + s.userConns.Remove(inAddr) + } + s.userConns.Set(inAddr, inConn) } func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn net.Conn, err interface{}) { switch *s.cfg.ParentType { @@ -471,10 +556,10 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn n case "tcp": if *s.cfg.ParentType == "tls" { var _outConn tls.Conn - _outConn, err = utils.TlsConnectHost(s.Resolve(*s.cfg.Parent), *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes) + _outConn, err = utils.TlsConnectHost(s.Resolve(*s.cfg.Parent), *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) outConn = net.Conn(&_outConn) } else if *s.cfg.ParentType == "kcp" { - outConn, err = utils.ConnectKCPHost(s.Resolve(*s.cfg.Parent), *s.cfg.KCPMethod, *s.cfg.KCPKey) + outConn, err = utils.ConnectKCPHost(s.Resolve(*s.cfg.Parent), s.cfg.KCP) } else { outConn, err = utils.ConnectHost(s.Resolve(*s.cfg.Parent), *s.cfg.Timeout) } @@ -482,27 +567,42 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn n err = fmt.Errorf("connect fail,%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, + }) + } var buf = make([]byte, 1024) //var n int + outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = outConn.Write(methodBytes) + outConn.SetDeadline(time.Time{}) if err != nil { err = fmt.Errorf("write method fail,%s", err) return } + outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = outConn.Read(buf) + outConn.SetDeadline(time.Time{}) if err != nil { err = fmt.Errorf("read method reply fail,%s", err) return } //resp := buf[:n] //log.Printf("resp:%v", resp) - + outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = outConn.Write(reqBytes) + outConn.SetDeadline(time.Time{}) if err != nil { err = fmt.Errorf("write req detail fail,%s", err) return } + outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) _, err = outConn.Read(buf) + outConn.SetDeadline(time.Time{}) if err != nil { err = fmt.Errorf("read req reply fail,%s", err) return @@ -514,7 +614,7 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn n maxTryCount := 1 tryCount := 0 RETRY: - if tryCount >= maxTryCount { + if tryCount >= maxTryCount || s.isStop { return } wait := make(chan bool, 1) diff --git a/services/sps.go b/services/sps.go index fa62ff5..327b532 100644 --- a/services/sps.go +++ b/services/sps.go @@ -2,72 +2,116 @@ package services import ( "bytes" + "encoding/base64" "errors" "fmt" + "io/ioutil" "log" "net" "runtime/debug" "snail007/proxy/utils" + "snail007/proxy/utils/conncrypt" "snail007/proxy/utils/socks" "strconv" "strings" + "time" ) type SPS struct { - outPool utils.OutPool + outPool utils.OutConn cfg SPSArgs domainResolver utils.DomainResolver + basicAuth utils.BasicAuth + serverChannels []*utils.ServerChannel + userConns utils.ConcurrentMap } func NewSPS() Service { return &SPS{ - outPool: utils.OutPool{}, - cfg: SPSArgs{}, + outPool: utils.OutConn{}, + cfg: SPSArgs{}, + basicAuth: utils.BasicAuth{}, + serverChannels: []*utils.ServerChannel{}, + userConns: utils.NewConcurrentMap(), } } -func (s *SPS) CheckArgs() { +func (s *SPS) CheckArgs() (err error) { if *s.cfg.Parent == "" { - log.Fatalf("parent required for %s %s", s.cfg.Protocol(), *s.cfg.Local) + err = fmt.Errorf("parent required for %s %s", s.cfg.Protocol(), *s.cfg.Local) + return } if *s.cfg.ParentType == "" { - log.Fatalf("parent type unkown,use -T ") + err = fmt.Errorf("parent type unkown,use -T ") + return } if *s.cfg.ParentType == TYPE_TLS || *s.cfg.LocalType == TYPE_TLS { - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + if err != nil { + return + } + if *s.cfg.CaCertFile != "" { + s.cfg.CaCertBytes, err = ioutil.ReadFile(*s.cfg.CaCertFile) + if err != nil { + err = fmt.Errorf("read ca file error,ERR:%s", err) + return + } + } } + return } -func (s *SPS) InitService() { +func (s *SPS) InitService() (err error) { s.InitOutConnPool() + if *s.cfg.DNSAddress != "" { + (*s).domainResolver = utils.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL) + } + err = s.InitBasicAuth() + return } func (s *SPS) InitOutConnPool() { if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP || *s.cfg.ParentType == TYPE_KCP { //dur int, isTLS bool, certBytes, keyBytes []byte, //parent string, timeout int, InitialCap int, MaxCap int - s.outPool = utils.NewOutPool( + s.outPool = utils.NewOutConn( 0, *s.cfg.ParentType, - *s.cfg.KCPMethod, - *s.cfg.KCPKey, - s.cfg.CertBytes, s.cfg.KeyBytes, + s.cfg.KCP, + s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, *s.cfg.Parent, *s.cfg.Timeout, - 0, - 0, ) } } func (s *SPS) StopService() { - if s.outPool.Pool != nil { - s.outPool.Pool.ReleaseAll() + defer func() { + e := recover() + if e != nil { + log.Printf("stop sps service crashed,%s", e) + } else { + log.Printf("service sps stoped") + } + }() + for _, sc := range s.serverChannels { + if sc.Listener != nil && *sc.Listener != nil { + (*sc.Listener).Close() + } + if sc.UDPListener != nil { + (*sc.UDPListener).Close() + } + } + for _, c := range s.userConns.Items() { + (*c.(*net.Conn)).Close() } } func (s *SPS) Start(args interface{}) (err error) { s.cfg = args.(SPSArgs) - s.CheckArgs() + if err = s.CheckArgs(); err != nil { + return + } + if err = s.InitService(); err != nil { + return + } log.Printf("use %s %s parent %s", *s.cfg.ParentType, *s.cfg.ParentServiceType, *s.cfg.Parent) - s.InitService() - for _, addr := range strings.Split(*s.cfg.Local, ",") { if addr != "" { host, port, _ := net.SplitHostPort(*s.cfg.Local) @@ -76,14 +120,15 @@ func (s *SPS) Start(args interface{}) (err error) { if *s.cfg.LocalType == TYPE_TCP { err = sc.ListenTCP(s.callback) } else if *s.cfg.LocalType == TYPE_TLS { - err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback) + err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.callback) } else if *s.cfg.LocalType == TYPE_KCP { - err = sc.ListenKCP(*s.cfg.KCPMethod, *s.cfg.KCPKey, s.callback) + err = sc.ListenKCP(s.cfg.KCP, s.callback) } if err != nil { return } log.Printf("%s http(s)+socks proxy on %s", s.cfg.Protocol(), (*sc.Listener).Addr()) + s.serverChannels = append(s.serverChannels, &sc) } } return @@ -98,6 +143,14 @@ func (s *SPS) callback(inConn net.Conn) { log.Printf("%s conn handler crashed with err : %s \nstack: %s", s.cfg.Protocol(), err, string(debug.Stack())) } }() + if *s.cfg.LocalCompress { + inConn = utils.NewCompConn(inConn) + } + if *s.cfg.LocalKey != "" { + inConn = conncrypt.New(inConn, &conncrypt.Config{ + Password: *s.cfg.LocalKey, + }) + } var err error switch *s.cfg.ParentType { case TYPE_KCP: @@ -110,7 +163,7 @@ func (s *SPS) callback(inConn net.Conn) { err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType) } if err != nil { - log.Printf("connect to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err) + log.Printf("connect to %s parent %s fail, ERR:%s from %s", *s.cfg.ParentType, *s.cfg.Parent, err, inConn.RemoteAddr()) utils.CloseConn(&inConn) } } @@ -124,51 +177,32 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { return } address := "" + var auth socks.Auth var forwardBytes []byte //fmt.Printf("%v", header) if header[0] == socks.VERSION_V5 { - //socks - methodReq, e := socks.NewMethodsRequest(*inConn, header) - if e != nil { - log.Printf("new method request err:%s", e) - utils.CloseConn(inConn) - err = e.(error) + //socks5 server + var serverConn *socks.ServerConn + if s.IsBasicAuth() { + serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), &s.basicAuth, "", header) + } else { + serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, "", header) + } + if err = serverConn.Handshake(); err != nil { return } - if !methodReq.Select(socks.Method_NO_AUTH) { - methodReq.Reply(socks.Method_NONE_ACCEPTABLE) - utils.CloseConn(inConn) - log.Printf("none method found : Method_NO_AUTH") - return - } - //method select reply - err = methodReq.Reply(socks.Method_NO_AUTH) - if err != nil { - log.Printf("reply answer data fail,ERR: %s", err) - utils.CloseConn(inConn) - return - } - //request detail - request, e := socks.NewRequest(*inConn) - if e != nil { - log.Printf("read request data fail,ERR: %s", e) - utils.CloseConn(inConn) - err = e.(error) - return - } - if request.CMD() != socks.CMD_CONNECT { - //只支持tcp - request.TCPReply(socks.REP_UNKNOWN) - utils.CloseConn(inConn) - err = errors.New("cmd not supported") - return - } - address = request.Addr() - request.TCPReply(socks.REP_SUCCESS) + address = serverConn.Target() + auth = serverConn.AuthData() } else if bytes.IndexByte(header, '\n') != -1 { //http var request utils.HTTPRequest - request, err = utils.NewHTTPRequest(inConn, 1024, false, nil, header) + (*inConn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) + if s.IsBasicAuth() { + request, err = utils.NewHTTPRequest(inConn, 1024, true, &s.basicAuth, header) + } else { + request, err = utils.NewHTTPRequest(inConn, 1024, false, nil, header) + } + (*inConn).SetDeadline(time.Time{}) if err != nil { log.Printf("new http request fail,ERR: %s", err) utils.CloseConn(inConn) @@ -182,6 +216,17 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { forwardBytes = request.HeadBuf } address = request.Host + var userpass string + if s.IsBasicAuth() { + userpass, err = request.GetAuthDataStr() + if err != nil { + return + } + userpassA := strings.Split(userpass, ":") + if len(userpassA) == 2 { + auth = socks.Auth{User: userpassA[0], Password: userpassA[1]} + } + } } else { log.Printf("unknown request from: %s,%s", (*inConn).RemoteAddr(), string(header)) utils.CloseConn(inConn) @@ -190,22 +235,56 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { } //connect to parent var outConn net.Conn - var _outConn interface{} - _outConn, err = s.outPool.Pool.Get() - if err == nil { - outConn = _outConn.(net.Conn) - } + outConn, err = s.outPool.Get() if err != nil { log.Printf("connect to %s , err:%s", *s.cfg.Parent, err) utils.CloseConn(inConn) return } + if *s.cfg.ParentCompress { + outConn = utils.NewCompConn(outConn) + } + if *s.cfg.ParentKey != "" { + outConn = conncrypt.New(outConn, &conncrypt.Config{ + Password: *s.cfg.ParentKey, + }) + } //ask parent for connect to target address if *s.cfg.ParentServiceType == "http" { //http parent - fmt.Fprintf(outConn, "CONNECT %s HTTP/1.1\r\n", address) - reply := make([]byte, 100) - n, err = outConn.Read(reply) + pb := new(bytes.Buffer) + pb.Write([]byte(fmt.Sprintf("CONNECT %s HTTP/1.1\r\nProxy-Connection: Keep-Alive\r\n", address))) + //Proxy-Authorization:\r\n + u := "" + if *s.cfg.ParentAuth != "" { + a := strings.Split(*s.cfg.ParentAuth, ":") + if len(a) != 2 { + err = fmt.Errorf("parent auth data format error") + return + } + u = fmt.Sprintf("%s:%s", a[0], a[1]) + } else { + if !s.IsBasicAuth() && auth.Password != "" && auth.User != "" { + u = fmt.Sprintf("%s:%s", auth.User, auth.Password) + } + } + if u != "" { + pb.Write([]byte(fmt.Sprintf("Proxy-Authorization:Basic %s\r\n", base64.StdEncoding.EncodeToString([]byte(u))))) + } + pb.Write([]byte("\r\n")) + outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) + _, err = outConn.Write(pb.Bytes()) + outConn.SetDeadline(time.Time{}) + if err != nil { + log.Printf("write CONNECT to %s , err:%s", *s.cfg.Parent, err) + utils.CloseConn(inConn) + utils.CloseConn(&outConn) + return + } + reply := make([]byte, 1024) + outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) + _, err = outConn.Read(reply) + outConn.SetDeadline(time.Time{}) if err != nil { log.Printf("read reply from %s , err:%s", *s.cfg.Parent, err) utils.CloseConn(inConn) @@ -215,53 +294,25 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { //log.Printf("reply: %s", string(reply[:n])) } else { log.Printf("connect %s", address) - //socks parent - //send auth type - _, err = outConn.Write([]byte{0x05, 0x01, 0x00}) - if err != nil { - log.Printf("write method to %s fail, err:%s", *s.cfg.Parent, err) - utils.CloseConn(inConn) - utils.CloseConn(&outConn) + //socks client + var clientConn *socks.ClientConn + if *s.cfg.ParentAuth != "" { + a := strings.Split(*s.cfg.ParentAuth, ":") + if len(a) != 2 { + err = fmt.Errorf("parent auth data format error") + return + } + clientConn = socks.NewClientConn(&outConn, "tcp", address, time.Millisecond*time.Duration(*s.cfg.Timeout), &socks.Auth{User: a[0], Password: a[1]}, header) + } else { + if !s.IsBasicAuth() && auth.Password != "" && auth.User != "" { + clientConn = socks.NewClientConn(&outConn, "tcp", address, time.Millisecond*time.Duration(*s.cfg.Timeout), &auth, header) + } else { + clientConn = socks.NewClientConn(&outConn, "tcp", address, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, header) + } + } + if err = clientConn.Handshake(); err != nil { return } - //read reply - reply := make([]byte, 512) - n, err = outConn.Read(reply) - if err != nil { - log.Printf("read reply from %s , err:%s", *s.cfg.Parent, err) - utils.CloseConn(inConn) - utils.CloseConn(&outConn) - return - } - //log.Printf("method reply %v", reply[:n]) - - //build request - buf, err = s.buildRequest(address) - if err != nil { - log.Printf("build request to %s fail , err:%s", *s.cfg.Parent, err) - utils.CloseConn(inConn) - utils.CloseConn(&outConn) - return - } - //send address request - _, err = outConn.Write(buf) - if err != nil { - log.Printf("write request to %s fail, err:%s", *s.cfg.Parent, err) - utils.CloseConn(inConn) - utils.CloseConn(&outConn) - return - } - //read reply - reply = make([]byte, 512) - n, err = outConn.Read(reply) - if err != nil { - log.Printf("read reply from %s , err:%s", *s.cfg.Parent, err) - utils.CloseConn(inConn) - utils.CloseConn(&outConn) - return - } - - //log.Printf("request reply %v", reply[:n]) } //forward client data to target,if necessary. if len(forwardBytes) > 0 { @@ -272,10 +323,43 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { outAddr := outConn.RemoteAddr().String() utils.IoBind((*inConn), outConn, func(err interface{}) { log.Printf("conn %s - %s released", inAddr, outAddr) + s.userConns.Remove(inAddr) }) log.Printf("conn %s - %s connected", inAddr, outAddr) + if c, ok := s.userConns.Get(inAddr); ok { + (*c.(*net.Conn)).Close() + } + s.userConns.Set(inAddr, &inConn) return } +func (s *SPS) InitBasicAuth() (err error) { + if *s.cfg.DNSAddress != "" { + s.basicAuth = utils.NewBasicAuth(&(*s).domainResolver) + } else { + s.basicAuth = utils.NewBasicAuth(nil) + } + if *s.cfg.AuthURL != "" { + s.basicAuth.SetAuthURL(*s.cfg.AuthURL, *s.cfg.AuthURLOkCode, *s.cfg.AuthURLTimeout, *s.cfg.AuthURLRetry) + log.Printf("auth from %s", *s.cfg.AuthURL) + } + if *s.cfg.AuthFile != "" { + var n = 0 + n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile) + if err != nil { + err = fmt.Errorf("auth-file ERR:%s", err) + return + } + log.Printf("auth data added from file %d , total:%d", n, s.basicAuth.Total()) + } + if len(*s.cfg.Auth) > 0 { + n := s.basicAuth.Add(*s.cfg.Auth) + log.Printf("auth data added %d, total:%d", n, s.basicAuth.Total()) + } + return +} +func (s *SPS) IsBasicAuth() bool { + return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != "" +} func (s *SPS) buildRequest(address string) (buf []byte, err error) { host, portStr, err := net.SplitHostPort(address) if err != nil { diff --git a/services/tcp.go b/services/tcp.go index 019c410..fde2849 100644 --- a/services/tcp.go +++ b/services/tcp.go @@ -14,41 +14,71 @@ import ( ) type TCP struct { - outPool utils.OutPool - cfg TCPArgs + outPool utils.OutConn + cfg TCPArgs + sc *utils.ServerChannel + isStop bool + userConns utils.ConcurrentMap } func NewTCP() Service { return &TCP{ - outPool: utils.OutPool{}, - cfg: TCPArgs{}, + outPool: utils.OutConn{}, + cfg: TCPArgs{}, + isStop: false, + userConns: utils.NewConcurrentMap(), } } -func (s *TCP) CheckArgs() { +func (s *TCP) CheckArgs() (err error) { if *s.cfg.Parent == "" { - log.Fatalf("parent required for %s %s", s.cfg.Protocol(), *s.cfg.Local) + err = fmt.Errorf("parent required for %s %s", s.cfg.Protocol(), *s.cfg.Local) + return } if *s.cfg.ParentType == "" { - log.Fatalf("parent type unkown,use -T ") + err = fmt.Errorf("parent type unkown,use -T ") + return } if *s.cfg.ParentType == TYPE_TLS || *s.cfg.LocalType == TYPE_TLS { - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + if err != nil { + return + } } + return } -func (s *TCP) InitService() { +func (s *TCP) InitService() (err error) { s.InitOutConnPool() + return } func (s *TCP) StopService() { - if s.outPool.Pool != nil { - s.outPool.Pool.ReleaseAll() + defer func() { + e := recover() + if e != nil { + log.Printf("stop tcp service crashed,%s", e) + } else { + log.Printf("service tcp stoped") + } + }() + s.isStop = true + if s.sc.Listener != nil && *s.sc.Listener != nil { + (*s.sc.Listener).Close() + } + if s.sc.UDPListener != nil { + (*s.sc.UDPListener).Close() + } + for _, c := range s.userConns.Items() { + (*c.(*net.Conn)).Close() } } func (s *TCP) Start(args interface{}) (err error) { s.cfg = args.(TCPArgs) - s.CheckArgs() + if err = s.CheckArgs(); err != nil { + return + } + if err = s.InitService(); err != nil { + return + } log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) - s.InitService() - host, port, _ := net.SplitHostPort(*s.cfg.Local) p, _ := strconv.Atoi(port) sc := utils.NewServerChannel(host, p) @@ -56,14 +86,15 @@ func (s *TCP) Start(args interface{}) (err error) { if *s.cfg.LocalType == TYPE_TCP { err = sc.ListenTCP(s.callback) } else if *s.cfg.LocalType == TYPE_TLS { - err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback) + err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.callback) } else if *s.cfg.LocalType == TYPE_KCP { - err = sc.ListenKCP(*s.cfg.KCPMethod, *s.cfg.KCPKey, s.callback) + err = sc.ListenKCP(s.cfg.KCP, s.callback) } if err != nil { return } log.Printf("%s proxy on %s", s.cfg.Protocol(), (*sc.Listener).Addr()) + s.sc = &sc return } @@ -96,11 +127,7 @@ func (s *TCP) callback(inConn net.Conn) { } func (s *TCP) OutToTCP(inConn *net.Conn) (err error) { var outConn net.Conn - var _outConn interface{} - _outConn, err = s.outPool.Pool.Get() - if err == nil { - outConn = _outConn.(net.Conn) - } + outConn, err = s.outPool.Get() if err != nil { log.Printf("connect to %s , err:%s", *s.cfg.Parent, err) utils.CloseConn(inConn) @@ -112,13 +139,22 @@ func (s *TCP) OutToTCP(inConn *net.Conn) (err error) { //outLocalAddr := outConn.LocalAddr().String() utils.IoBind((*inConn), outConn, func(err interface{}) { log.Printf("conn %s - %s released", inAddr, outAddr) + s.userConns.Remove(inAddr) }) log.Printf("conn %s - %s connected", inAddr, outAddr) + if c, ok := s.userConns.Get(inAddr); ok { + (*c.(*net.Conn)).Close() + } + s.userConns.Set(inAddr, &inConn) return } func (s *TCP) OutToUDP(inConn *net.Conn) (err error) { log.Printf("conn created , remote : %s ", (*inConn).RemoteAddr()) for { + if s.isStop { + (*inConn).Close() + return + } srcAddr, body, err := utils.ReadUDPPacket(bufio.NewReader(*inConn)) if err == io.EOF || err == io.ErrUnexpectedEOF { //log.Printf("connection %s released", srcAddr) @@ -168,16 +204,13 @@ func (s *TCP) InitOutConnPool() { if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP || *s.cfg.ParentType == TYPE_KCP { //dur int, isTLS bool, certBytes, keyBytes []byte, //parent string, timeout int, InitialCap int, MaxCap int - s.outPool = utils.NewOutPool( + s.outPool = utils.NewOutConn( *s.cfg.CheckParentInterval, *s.cfg.ParentType, - *s.cfg.KCPMethod, - *s.cfg.KCPKey, - s.cfg.CertBytes, s.cfg.KeyBytes, + s.cfg.KCP, + s.cfg.CertBytes, s.cfg.KeyBytes, nil, *s.cfg.Parent, *s.cfg.Timeout, - *s.cfg.PoolSize, - *s.cfg.PoolSize*2, ) } } diff --git a/services/tunnel_bridge.go b/services/tunnel_bridge.go index bb048dd..8125cf0 100644 --- a/services/tunnel_bridge.go +++ b/services/tunnel_bridge.go @@ -1,12 +1,15 @@ package services import ( - "bufio" + "bytes" + "fmt" "log" "net" "snail007/proxy/utils" "strconv" "time" + + "github.com/xtaci/smux" ) type ServerConn struct { @@ -17,8 +20,7 @@ type TunnelBridge struct { cfg TunnelBridgeArgs serverConns utils.ConcurrentMap clientControlConns utils.ConcurrentMap - // cmServer utils.ConnManager - // cmClient utils.ConnManager + isStop bool } func NewTunnelBridge() Service { @@ -26,213 +28,51 @@ func NewTunnelBridge() Service { cfg: TunnelBridgeArgs{}, serverConns: utils.NewConcurrentMap(), clientControlConns: utils.NewConcurrentMap(), - // cmServer: utils.NewConnManager(), - // cmClient: utils.NewConnManager(), + isStop: false, } } -func (s *TunnelBridge) InitService() { - +func (s *TunnelBridge) InitService() (err error) { + return } -func (s *TunnelBridge) CheckArgs() { +func (s *TunnelBridge) CheckArgs() (err error) { if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { - log.Fatalf("cert and key file required") + err = fmt.Errorf("cert and key file required") + return } - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + return } func (s *TunnelBridge) StopService() { - + defer func() { + e := recover() + if e != nil { + log.Printf("stop tbridge service crashed,%s", e) + } else { + log.Printf("service tbridge stoped") + } + }() + s.isStop = true + for _, sess := range s.clientControlConns.Items() { + (*sess.(*net.Conn)).Close() + } + for _, sess := range s.serverConns.Items() { + (*sess.(ServerConn).Conn).Close() + } } func (s *TunnelBridge) Start(args interface{}) (err error) { s.cfg = args.(TunnelBridgeArgs) - s.CheckArgs() - s.InitService() + if err = s.CheckArgs(); err != nil { + return + } + if err = s.InitService(); err != nil { + return + } host, port, _ := net.SplitHostPort(*s.cfg.Local) p, _ := strconv.Atoi(port) sc := utils.NewServerChannel(host, p) - err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, func(inConn net.Conn) { - //log.Printf("connection from %s ", inConn.RemoteAddr()) - - reader := bufio.NewReader(inConn) - var err error - var connType uint8 - err = utils.ReadPacket(reader, &connType) - if err != nil { - log.Printf("read error,ERR:%s", err) - return - } - switch connType { - case CONN_SERVER: - var key, ID, clientLocalAddr, serverID string - err = utils.ReadPacketData(reader, &key, &ID, &clientLocalAddr, &serverID) - if err != nil { - log.Printf("read error,ERR:%s", err) - return - } - packet := utils.BuildPacketData(ID, clientLocalAddr, serverID) - log.Printf("server connection, key: %s , id: %s %s %s", key, ID, clientLocalAddr, serverID) - - //addr := clientLocalAddr + "@" + ID - s.serverConns.Set(ID, ServerConn{ - Conn: &inConn, - }) - for { - item, ok := s.clientControlConns.Get(key) - if !ok { - log.Printf("client %s control conn not exists", key) - time.Sleep(time.Second * 3) - continue - } - (*item.(*net.Conn)).SetWriteDeadline(time.Now().Add(time.Second * 3)) - _, err := (*item.(*net.Conn)).Write(packet) - (*item.(*net.Conn)).SetWriteDeadline(time.Time{}) - if err != nil { - log.Printf("%s client control conn write signal fail, err: %s, retrying...", key, err) - time.Sleep(time.Second * 3) - continue - } else { - // s.cmServer.Add(serverID, ID, &inConn) - break - } - } - case CONN_CLIENT: - var key, ID, serverID string - err = utils.ReadPacketData(reader, &key, &ID, &serverID) - if err != nil { - log.Printf("read error,ERR:%s", err) - return - } - log.Printf("client connection , key: %s , id: %s, server id:%s", key, ID, serverID) - - serverConnItem, ok := s.serverConns.Get(ID) - if !ok { - inConn.Close() - log.Printf("server conn %s exists", ID) - return - } - serverConn := serverConnItem.(ServerConn).Conn - utils.IoBind(*serverConn, inConn, func(err interface{}) { - s.serverConns.Remove(ID) - // s.cmClient.RemoveOne(key, ID) - // s.cmServer.RemoveOne(serverID, ID) - log.Printf("conn %s released", ID) - }) - // s.cmClient.Add(key, ID, &inConn) - log.Printf("conn %s created", ID) - - case CONN_CLIENT_CONTROL: - var key string - err = utils.ReadPacketData(reader, &key) - if err != nil { - log.Printf("read error,ERR:%s", err) - return - } - log.Printf("client control connection, key: %s", key) - if s.clientControlConns.Has(key) { - item, _ := s.clientControlConns.Get(key) - (*item.(*net.Conn)).Close() - } - s.clientControlConns.Set(key, &inConn) - log.Printf("set client %s control conn", key) - - // case CONN_SERVER_HEARBEAT: - // var serverID string - // err = utils.ReadPacketData(reader, &serverID) - // if err != nil { - // log.Printf("read error,ERR:%s", err) - // return - // } - // log.Printf("server heartbeat connection, id: %s", serverID) - // writeDie := make(chan bool) - // readDie := make(chan bool) - // go func() { - // for { - // inConn.SetWriteDeadline(time.Now().Add(time.Second * 3)) - // _, err = inConn.Write([]byte{0x00}) - // inConn.SetWriteDeadline(time.Time{}) - // if err != nil { - // log.Printf("server heartbeat connection write err %s", err) - // break - // } - // time.Sleep(time.Second * 3) - // } - // close(writeDie) - // }() - // go func() { - // for { - // signal := make([]byte, 1) - // inConn.SetReadDeadline(time.Now().Add(time.Second * 6)) - // _, err := inConn.Read(signal) - // inConn.SetReadDeadline(time.Time{}) - // if err != nil { - // log.Printf("server heartbeat connection read err: %s", err) - // break - // } else { - // // log.Printf("heartbeat from server ,id:%s", serverID) - // } - // } - // close(readDie) - // }() - // select { - // case <-readDie: - // case <-writeDie: - // } - // utils.CloseConn(&inConn) - // s.cmServer.Remove(serverID) - // log.Printf("server heartbeat conn %s released", serverID) - // case CONN_CLIENT_HEARBEAT: - // var clientID string - // err = utils.ReadPacketData(reader, &clientID) - // if err != nil { - // log.Printf("read error,ERR:%s", err) - // return - // } - // log.Printf("client heartbeat connection, id: %s", clientID) - // writeDie := make(chan bool) - // readDie := make(chan bool) - // go func() { - // for { - // inConn.SetWriteDeadline(time.Now().Add(time.Second * 3)) - // _, err = inConn.Write([]byte{0x00}) - // inConn.SetWriteDeadline(time.Time{}) - // if err != nil { - // log.Printf("client heartbeat connection write err %s", err) - // break - // } - // time.Sleep(time.Second * 3) - // } - // close(writeDie) - // }() - // go func() { - // for { - // signal := make([]byte, 1) - // inConn.SetReadDeadline(time.Now().Add(time.Second * 6)) - // _, err := inConn.Read(signal) - // inConn.SetReadDeadline(time.Time{}) - // if err != nil { - // log.Printf("client control connection read err: %s", err) - // break - // } else { - // // log.Printf("heartbeat from client ,id:%s", clientID) - // } - // } - // close(readDie) - // }() - // select { - // case <-readDie: - // case <-writeDie: - // } - // utils.CloseConn(&inConn) - // s.cmClient.Remove(clientID) - // if s.clientControlConns.Has(clientID) { - // item, _ := s.clientControlConns.Get(clientID) - // (*item.(*net.Conn)).Close() - // } - // s.clientControlConns.Remove(clientID) - // log.Printf("client heartbeat conn %s released", clientID) - } - }) + err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.callback) if err != nil { return } @@ -242,3 +82,111 @@ func (s *TunnelBridge) Start(args interface{}) (err error) { func (s *TunnelBridge) Clean() { s.StopService() } +func (s *TunnelBridge) callback(inConn net.Conn) { + var err error + //log.Printf("connection from %s ", inConn.RemoteAddr()) + sess, err := smux.Server(inConn, &smux.Config{ + KeepAliveInterval: 10 * time.Second, + KeepAliveTimeout: time.Duration(*s.cfg.Timeout) * time.Second, + MaxFrameSize: 4096, + MaxReceiveBuffer: 4194304, + }) + if err != nil { + log.Printf("new mux server conn error,ERR:%s", err) + return + } + inConn, err = sess.AcceptStream() + if err != nil { + log.Printf("mux server conn accept error,ERR:%s", err) + return + } + + var buf = make([]byte, 1024) + n, _ := inConn.Read(buf) + reader := bytes.NewReader(buf[:n]) + //reader := bufio.NewReader(inConn) + + var connType uint8 + err = utils.ReadPacket(reader, &connType) + if err != nil { + log.Printf("read error,ERR:%s", err) + return + } + switch connType { + case CONN_SERVER: + var key, ID, clientLocalAddr, serverID string + err = utils.ReadPacketData(reader, &key, &ID, &clientLocalAddr, &serverID) + if err != nil { + log.Printf("read error,ERR:%s", err) + return + } + packet := utils.BuildPacketData(ID, clientLocalAddr, serverID) + log.Printf("server connection, key: %s , id: %s %s %s", key, ID, clientLocalAddr, serverID) + + //addr := clientLocalAddr + "@" + ID + s.serverConns.Set(ID, ServerConn{ + Conn: &inConn, + }) + for { + if s.isStop { + return + } + item, ok := s.clientControlConns.Get(key) + if !ok { + log.Printf("client %s control conn not exists", key) + time.Sleep(time.Second * 3) + continue + } + (*item.(*net.Conn)).SetWriteDeadline(time.Now().Add(time.Second * 3)) + _, err := (*item.(*net.Conn)).Write(packet) + (*item.(*net.Conn)).SetWriteDeadline(time.Time{}) + if err != nil { + log.Printf("%s client control conn write signal fail, err: %s, retrying...", key, err) + time.Sleep(time.Second * 3) + continue + } else { + // s.cmServer.Add(serverID, ID, &inConn) + break + } + } + case CONN_CLIENT: + var key, ID, serverID string + err = utils.ReadPacketData(reader, &key, &ID, &serverID) + if err != nil { + log.Printf("read error,ERR:%s", err) + return + } + log.Printf("client connection , key: %s , id: %s, server id:%s", key, ID, serverID) + + serverConnItem, ok := s.serverConns.Get(ID) + if !ok { + inConn.Close() + log.Printf("server conn %s exists", ID) + return + } + serverConn := serverConnItem.(ServerConn).Conn + utils.IoBind(*serverConn, inConn, func(err interface{}) { + s.serverConns.Remove(ID) + // s.cmClient.RemoveOne(key, ID) + // s.cmServer.RemoveOne(serverID, ID) + log.Printf("conn %s released", ID) + }) + // s.cmClient.Add(key, ID, &inConn) + log.Printf("conn %s created", ID) + + case CONN_CLIENT_CONTROL: + var key string + err = utils.ReadPacketData(reader, &key) + if err != nil { + log.Printf("read error,ERR:%s", err) + return + } + log.Printf("client control connection, key: %s", key) + if s.clientControlConns.Has(key) { + item, _ := s.clientControlConns.Get(key) + (*item.(*net.Conn)).Close() + } + s.clientControlConns.Set(key, &inConn) + log.Printf("set client %s control conn", key) + } +} diff --git a/services/tunnel_client.go b/services/tunnel_client.go index 94daf95..fa3c4e4 100644 --- a/services/tunnel_client.go +++ b/services/tunnel_client.go @@ -8,106 +8,74 @@ import ( "net" "snail007/proxy/utils" "time" + + "github.com/xtaci/smux" ) type TunnelClient struct { - cfg TunnelClientArgs - // cm utils.ConnManager - ctrlConn net.Conn + cfg TunnelClientArgs + ctrlConn net.Conn + isStop bool + userConns utils.ConcurrentMap } func NewTunnelClient() Service { return &TunnelClient{ - cfg: TunnelClientArgs{}, - // cm: utils.NewConnManager(), + cfg: TunnelClientArgs{}, + userConns: utils.NewConcurrentMap(), + isStop: false, } } -func (s *TunnelClient) InitService() { - // s.InitHeartbeatDeamon() +func (s *TunnelClient) InitService() (err error) { + return } -// func (s *TunnelClient) InitHeartbeatDeamon() { -// log.Printf("heartbeat started") -// go func() { -// var heartbeatConn net.Conn -// var ID = *s.cfg.Key -// for { - -// //close all connection -// s.cm.RemoveAll() -// if s.ctrlConn != nil { -// s.ctrlConn.Close() -// } -// utils.CloseConn(&heartbeatConn) -// heartbeatConn, err := s.GetInConn(CONN_CLIENT_HEARBEAT, ID) -// if err != nil { -// log.Printf("heartbeat connection err: %s, retrying...", err) -// time.Sleep(time.Second * 3) -// utils.CloseConn(&heartbeatConn) -// continue -// } -// log.Printf("heartbeat connection created,id:%s", ID) -// writeDie := make(chan bool) -// readDie := make(chan bool) -// go func() { -// for { -// heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3)) -// _, err = heartbeatConn.Write([]byte{0x00}) -// heartbeatConn.SetWriteDeadline(time.Time{}) -// if err != nil { -// log.Printf("heartbeat connection write err %s", err) -// break -// } -// time.Sleep(time.Second * 3) -// } -// close(writeDie) -// }() -// go func() { -// for { -// signal := make([]byte, 1) -// heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6)) -// _, err := heartbeatConn.Read(signal) -// heartbeatConn.SetReadDeadline(time.Time{}) -// if err != nil { -// log.Printf("heartbeat connection read err: %s", err) -// break -// } else { -// //log.Printf("heartbeat from bridge") -// } -// } -// close(readDie) -// }() -// select { -// case <-readDie: -// case <-writeDie: -// } -// } -// }() -// } -func (s *TunnelClient) CheckArgs() { +func (s *TunnelClient) CheckArgs() (err error) { if *s.cfg.Parent != "" { log.Printf("use tls parent %s", *s.cfg.Parent) } else { - log.Fatalf("parent required") + err = fmt.Errorf("parent required") + return } if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { - log.Fatalf("cert and key file required") + err = fmt.Errorf("cert and key file required") + return } - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + return } func (s *TunnelClient) StopService() { - // s.cm.RemoveAll() + defer func() { + e := recover() + if e != nil { + log.Printf("stop tclient service crashed,%s", e) + } else { + log.Printf("service tclient stoped") + } + }() + s.isStop = true + if s.ctrlConn != nil { + s.ctrlConn.Close() + } + for _, c := range s.userConns.Items() { + (*c.(*net.Conn)).Close() + } } func (s *TunnelClient) Start(args interface{}) (err error) { s.cfg = args.(TunnelClientArgs) - s.CheckArgs() - s.InitService() + if err = s.CheckArgs(); err != nil { + return + } + if err = s.InitService(); err != nil { + return + } log.Printf("proxy on tunnel client mode") for { - //close all conn - // s.cm.Remove(*s.cfg.Key) + if s.isStop { + return + } if s.ctrlConn != nil { s.ctrlConn.Close() } @@ -122,6 +90,9 @@ func (s *TunnelClient) Start(args interface{}) (err error) { continue } for { + if s.isStop { + return + } var ID, clientLocalAddr, serverID string err = utils.ReadPacketData(s.ctrlConn, &ID, &clientLocalAddr, &serverID) if err != nil { @@ -161,9 +132,26 @@ func (s *TunnelClient) GetInConn(typ uint8, data ...string) (outConn net.Conn, e } func (s *TunnelClient) GetConn() (conn net.Conn, err error) { var _conn tls.Conn - _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes) + _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) if err == nil { conn = net.Conn(&_conn) + c, e := smux.Client(conn, &smux.Config{ + KeepAliveInterval: 10 * time.Second, + KeepAliveTimeout: time.Duration(*s.cfg.Timeout) * time.Second, + MaxFrameSize: 4096, + MaxReceiveBuffer: 4194304, + }) + if e != nil { + log.Printf("new mux client conn error,ERR:%s", e) + err = e + return + } + conn, e = c.OpenStream() + if e != nil { + log.Printf("mux client conn open stream error,ERR:%s", e) + err = e + return + } } return } @@ -172,6 +160,12 @@ func (s *TunnelClient) ServeUDP(localAddr, ID, serverID string) { var err error // for { for { + if s.isStop { + if inConn != nil { + inConn.Close() + } + return + } // s.cm.RemoveOne(*s.cfg.Key, ID) inConn, err = s.GetInConn(CONN_CLIENT, *s.cfg.Key, ID, serverID) if err != nil { @@ -187,6 +181,9 @@ func (s *TunnelClient) ServeUDP(localAddr, ID, serverID string) { log.Printf("conn %s created", ID) for { + if s.isStop { + return + } srcAddr, body, err := utils.ReadUDPPacket(inConn) if err == io.EOF || err == io.ErrUnexpectedEOF { log.Printf("connection %s released", ID) @@ -243,6 +240,9 @@ func (s *TunnelClient) ServeConn(localAddr, ID, serverID string) { var inConn, outConn net.Conn var err error for { + if s.isStop { + return + } inConn, err = s.GetInConn(CONN_CLIENT, *s.cfg.Key, ID, serverID) if err != nil { utils.CloseConn(&inConn) @@ -256,6 +256,9 @@ func (s *TunnelClient) ServeConn(localAddr, ID, serverID string) { i := 0 for { + if s.isStop { + return + } i++ outConn, err = utils.ConnectHost(localAddr, *s.cfg.Timeout) if err == nil || i == 3 { @@ -274,10 +277,14 @@ func (s *TunnelClient) ServeConn(localAddr, ID, serverID string) { log.Printf("build connection error, err: %s", err) return } + inAddr := inConn.RemoteAddr().String() utils.IoBind(inConn, outConn, func(err interface{}) { log.Printf("conn %s released", ID) - // s.cm.RemoveOne(*s.cfg.Key, ID) + s.userConns.Remove(inAddr) }) - // s.cm.Add(*s.cfg.Key, ID, &inConn) + if c, ok := s.userConns.Get(inAddr); ok { + (*c.(*net.Conn)).Close() + } + s.userConns.Set(inAddr, &inConn) log.Printf("conn %s created", ID) } diff --git a/services/tunnel_server.go b/services/tunnel_server.go index 9ef3763..a977332 100644 --- a/services/tunnel_server.go +++ b/services/tunnel_server.go @@ -11,20 +11,24 @@ import ( "strconv" "strings" "time" + + "github.com/xtaci/smux" ) type TunnelServer struct { - cfg TunnelServerArgs - udpChn chan UDPItem - sc utils.ServerChannel + cfg TunnelServerArgs + udpChn chan UDPItem + sc utils.ServerChannel + isStop bool + udpConn *net.Conn + userConns utils.ConcurrentMap } type TunnelServerManager struct { cfg TunnelServerArgs udpChn chan UDPItem - sc utils.ServerChannel serverID string - // cm utils.ConnManager + servers []*Service } func NewTunnelServerManager() Service { @@ -32,19 +36,24 @@ func NewTunnelServerManager() Service { cfg: TunnelServerArgs{}, udpChn: make(chan UDPItem, 50000), serverID: utils.Uniqueid(), - // cm: utils.NewConnManager(), + servers: []*Service{}, } } func (s *TunnelServerManager) Start(args interface{}) (err error) { s.cfg = args.(TunnelServerArgs) - s.CheckArgs() + if err = s.CheckArgs(); err != nil { + return + } if *s.cfg.Parent != "" { log.Printf("use tls parent %s", *s.cfg.Parent) } else { - log.Fatalf("parent required") + err = fmt.Errorf("parent required") + return } - s.InitService() + if err = s.InitService(); err != nil { + return + } log.Printf("server id: %s", s.serverID) //log.Printf("route:%v", *s.cfg.Route) @@ -84,6 +93,7 @@ func (s *TunnelServerManager) Start(args interface{}) (err error) { if err != nil { return } + s.servers = append(s.servers, &server) } return } @@ -91,72 +101,22 @@ func (s *TunnelServerManager) Clean() { s.StopService() } func (s *TunnelServerManager) StopService() { - // s.cm.RemoveAll() -} -func (s *TunnelServerManager) CheckArgs() { - if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { - log.Fatalf("cert and key file required") + for _, server := range s.servers { + (*server).Clean() } - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) } -func (s *TunnelServerManager) InitService() { - // s.InitHeartbeatDeamon() +func (s *TunnelServerManager) CheckArgs() (err error) { + if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { + err = fmt.Errorf("cert and key file required") + return + } + s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + return +} +func (s *TunnelServerManager) InitService() (err error) { + return } -// func (s *TunnelServerManager) InitHeartbeatDeamon() { -// log.Printf("heartbeat started") -// go func() { -// var heartbeatConn net.Conn -// var ID string -// for { -// //close all connection -// s.cm.Remove(ID) -// utils.CloseConn(&heartbeatConn) -// heartbeatConn, ID, err := s.GetOutConn(CONN_SERVER_HEARBEAT) -// if err != nil { -// log.Printf("heartbeat connection err: %s, retrying...", err) -// time.Sleep(time.Second * 3) -// utils.CloseConn(&heartbeatConn) -// continue -// } -// log.Printf("heartbeat connection created,id:%s", ID) -// writeDie := make(chan bool) -// readDie := make(chan bool) -// go func() { -// for { -// heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3)) -// _, err = heartbeatConn.Write([]byte{0x00}) -// heartbeatConn.SetWriteDeadline(time.Time{}) -// if err != nil { -// log.Printf("heartbeat connection write err %s", err) -// break -// } -// time.Sleep(time.Second * 3) -// } -// close(writeDie) -// }() -// go func() { -// for { -// signal := make([]byte, 1) -// heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6)) -// _, err := heartbeatConn.Read(signal) -// heartbeatConn.SetReadDeadline(time.Time{}) -// if err != nil { -// log.Printf("heartbeat connection read err: %s", err) -// break -// } else { -// // log.Printf("heartbeat from bridge") -// } -// } -// close(readDie) -// }() -// select { -// case <-readDie: -// case <-writeDie: -// } -// } -// }() -// } func (s *TunnelServerManager) GetOutConn(typ uint8) (outConn net.Conn, ID string, err error) { outConn, err = s.GetConn() if err != nil { @@ -174,7 +134,7 @@ func (s *TunnelServerManager) GetOutConn(typ uint8) (outConn net.Conn, ID string } func (s *TunnelServerManager) GetConn() (conn net.Conn, err error) { var _conn tls.Conn - _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes) + _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) if err == nil { conn = net.Conn(&_conn) } @@ -182,8 +142,10 @@ func (s *TunnelServerManager) GetConn() (conn net.Conn, err error) { } func NewTunnelServer() Service { return &TunnelServer{ - cfg: TunnelServerArgs{}, - udpChn: make(chan UDPItem, 50000), + cfg: TunnelServerArgs{}, + udpChn: make(chan UDPItem, 50000), + isStop: false, + userConns: utils.NewConcurrentMap(), } } @@ -193,19 +155,50 @@ type UDPItem struct { srcAddr *net.UDPAddr } -func (s *TunnelServer) InitService() { - s.UDPConnDeamon() -} -func (s *TunnelServer) CheckArgs() { - if *s.cfg.Remote == "" { - log.Fatalf("remote required") +func (s *TunnelServer) StopService() { + defer func() { + e := recover() + if e != nil { + log.Printf("stop server service crashed,%s", e) + } else { + log.Printf("service server stoped") + } + }() + s.isStop = true + + if s.sc.Listener != nil { + (*s.sc.Listener).Close() } + if s.sc.UDPListener != nil { + (*s.sc.UDPListener).Close() + } + if s.udpConn != nil { + (*s.udpConn).Close() + } + for _, c := range s.userConns.Items() { + (*c.(*net.Conn)).Close() + } +} +func (s *TunnelServer) InitService() (err error) { + s.UDPConnDeamon() + return +} +func (s *TunnelServer) CheckArgs() (err error) { + if *s.cfg.Remote == "" { + err = fmt.Errorf("remote required") + return + } + return } func (s *TunnelServer) Start(args interface{}) (err error) { s.cfg = args.(TunnelServerArgs) - s.CheckArgs() - s.InitService() + if err = s.CheckArgs(); err != nil { + return + } + if err = s.InitService(); err != nil { + return + } host, port, _ := net.SplitHostPort(*s.cfg.Local) p, _ := strconv.Atoi(port) s.sc = utils.NewServerChannel(host, p) @@ -231,6 +224,9 @@ func (s *TunnelServer) Start(args interface{}) (err error) { var outConn net.Conn var ID string for { + if s.isStop { + return + } outConn, ID, err = s.GetOutConn(CONN_SERVER) if err != nil { utils.CloseConn(&outConn) @@ -241,12 +237,15 @@ func (s *TunnelServer) Start(args interface{}) (err error) { break } } + inAddr := inConn.RemoteAddr().String() utils.IoBind(inConn, outConn, func(err interface{}) { - // s.cfg.Mgr.cm.RemoveOne(s.cfg.Mgr.serverID, ID) + s.userConns.Remove(inAddr) log.Printf("%s conn %s released", *s.cfg.Key, ID) }) - //add conn - // s.cfg.Mgr.cm.Add(s.cfg.Mgr.serverID, ID, &inConn) + if c, ok := s.userConns.Get(inAddr); ok { + (*c.(*net.Conn)).Close() + } + s.userConns.Set(inAddr, &inConn) log.Printf("%s conn %s created", *s.cfg.Key, ID) }) if err != nil { @@ -280,9 +279,26 @@ func (s *TunnelServer) GetOutConn(typ uint8) (outConn net.Conn, ID string, err e } func (s *TunnelServer) GetConn() (conn net.Conn, err error) { var _conn tls.Conn - _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes) + _conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) if err == nil { conn = net.Conn(&_conn) + c, e := smux.Client(conn, &smux.Config{ + KeepAliveInterval: 10 * time.Second, + KeepAliveTimeout: time.Duration(*s.cfg.Timeout) * time.Second, + MaxFrameSize: 4096, + MaxReceiveBuffer: 4194304, + }) + if e != nil { + log.Printf("new mux client conn error,ERR:%s", e) + err = e + return + } + conn, e = c.OpenStream() + if e != nil { + log.Printf("mux client conn open stream error,ERR:%s", e) + err = e + return + } } return } @@ -299,10 +315,19 @@ func (s *TunnelServer) UDPConnDeamon() { // var cmdChn = make(chan bool, 1000) var err error for { + if s.isStop { + return + } item := <-s.udpChn RETRY: + if s.isStop { + return + } if outConn == nil { for { + if s.isStop { + return + } outConn, ID, err = s.GetOutConn(CONN_SERVER) if err != nil { // cmdChn <- true @@ -313,11 +338,14 @@ func (s *TunnelServer) UDPConnDeamon() { continue } else { go func(outConn net.Conn, ID string) { - go func() { - // <-cmdChn - // outConn.Close() - }() + if s.udpConn != nil { + (*s.udpConn).Close() + } + s.udpConn = &outConn for { + if s.isStop { + return + } srcAddrFromConn, body, err := utils.ReadUDPPacket(outConn) if err == io.EOF || err == io.ErrUnexpectedEOF { log.Printf("UDP deamon connection %s exited", ID) diff --git a/services/udp.go b/services/udp.go index 39a8000..52bab39 100644 --- a/services/udp.go +++ b/services/udp.go @@ -8,6 +8,7 @@ import ( "log" "net" "runtime/debug" + "snail007/proxy/services/kcpcfg" "snail007/proxy/utils" "strconv" "strings" @@ -16,44 +17,68 @@ import ( type UDP struct { p utils.ConcurrentMap - outPool utils.OutPool + outPool utils.OutConn cfg UDPArgs sc *utils.ServerChannel + isStop bool } func NewUDP() Service { return &UDP{ - outPool: utils.OutPool{}, + outPool: utils.OutConn{}, p: utils.NewConcurrentMap(), + isStop: false, } } -func (s *UDP) CheckArgs() { +func (s *UDP) CheckArgs() (err error) { if *s.cfg.Parent == "" { - log.Fatalf("parent required for udp %s", *s.cfg.Local) + err = fmt.Errorf("parent required for udp %s", *s.cfg.Local) + return } if *s.cfg.ParentType == "" { - log.Fatalf("parent type unkown,use -T ") + err = fmt.Errorf("parent type unkown,use -T ") + return } if *s.cfg.ParentType == "tls" { - s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) + if err != nil { + return + } } + return } -func (s *UDP) InitService() { +func (s *UDP) InitService() (err error) { if *s.cfg.ParentType != TYPE_UDP { s.InitOutConnPool() } + return } func (s *UDP) StopService() { - if s.outPool.Pool != nil { - s.outPool.Pool.ReleaseAll() + defer func() { + e := recover() + if e != nil { + log.Printf("stop udp service crashed,%s", e) + } else { + log.Printf("service udp stoped") + } + }() + s.isStop = true + if s.sc.Listener != nil && *s.sc.Listener != nil { + (*s.sc.Listener).Close() + } + if s.sc.UDPListener != nil { + (*s.sc.UDPListener).Close() } } func (s *UDP) Start(args interface{}) (err error) { s.cfg = args.(UDPArgs) - s.CheckArgs() + if err = s.CheckArgs(); err != nil { + return + } log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) - s.InitService() - + if err = s.InitService(); err != nil { + return + } host, port, _ := net.SplitHostPort(*s.cfg.Local) p, _ := strconv.Atoi(port) sc := utils.NewServerChannel(host, p) @@ -94,7 +119,7 @@ func (s *UDP) GetConn(connKey string) (conn net.Conn, isNew bool, err error) { isNew = !s.p.Has(connKey) var _conn interface{} if isNew { - _conn, err = s.outPool.Pool.Get() + _conn, err = s.outPool.Get() if err != nil { return nil, false, err } @@ -108,7 +133,7 @@ func (s *UDP) GetConn(connKey string) (conn net.Conn, isNew bool, err error) { func (s *UDP) OutToTCP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err error) { numLocal := crc32.ChecksumIEEE([]byte(localAddr.String())) numSrc := crc32.ChecksumIEEE([]byte(srcAddr.String())) - mod := uint32(*s.cfg.PoolSize) + mod := uint32(10) if mod == 0 { mod = 10 } @@ -127,6 +152,10 @@ func (s *UDP) OutToTCP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err erro }() log.Printf("conn %d created , local: %s", connKey, srcAddr.String()) for { + if s.isStop { + conn.Close() + return + } srcAddrFromConn, body, err := utils.ReadUDPPacket(bufio.NewReader(conn)) if err == io.EOF || err == io.ErrUnexpectedEOF { //log.Printf("connection %d released", connKey) @@ -205,15 +234,13 @@ func (s *UDP) InitOutConnPool() { if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP { //dur int, isTLS bool, certBytes, keyBytes []byte, //parent string, timeout int, InitialCap int, MaxCap int - s.outPool = utils.NewOutPool( + s.outPool = utils.NewOutConn( *s.cfg.CheckParentInterval, *s.cfg.ParentType, - "", "", - s.cfg.CertBytes, s.cfg.KeyBytes, + kcpcfg.KCPConfigArgs{}, + s.cfg.CertBytes, s.cfg.KeyBytes, nil, *s.cfg.Parent, *s.cfg.Timeout, - *s.cfg.PoolSize, - *s.cfg.PoolSize*2, ) } } diff --git a/utils/conncrypt/conncrypt.go b/utils/conncrypt/conncrypt.go new file mode 100644 index 0000000..f3115e7 --- /dev/null +++ b/utils/conncrypt/conncrypt.go @@ -0,0 +1,95 @@ +package conncrypt + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/sha256" + "hash" + "io" + "net" + + "golang.org/x/crypto/pbkdf2" +) + +//Confg defaults +const DefaultIterations = 2048 +const DefaultKeySize = 32 //256bits +var DefaultHashFunc = sha256.New +var DefaultSalt = []byte(` +(;QUHj.BQ?RXzYSO]ifkXp/G!kFmWyXyEV6Nt!d|@bo+N$L9+SOd,6acYKY_ec+(x"R";\'4&fTAVu92GVA-wxBptOTM^2,iP5%)wnhW +hwk=]Snsgymt!3gbP2pe=J//}1a?lp9ej=&TB!C_V(cT2?z8wyoL_-13fd[] +`) //salt must be predefined in order to derive the same key + +//Config stores the PBKDF2 key generation parameters +type Config struct { + Password string + Salt []byte + Iterations int + KeySize int + HashFunc func() hash.Hash +} + +//New creates an AES encrypted net.Conn by generating +//a key using PBKDF2 with the provided configuration +func New(conn net.Conn, c *Config) net.Conn { + //set defaults + if len(c.Salt) == 0 { + c.Salt = DefaultSalt + } + if c.Iterations == 0 { + c.Iterations = DefaultIterations + } + if c.KeySize != 16 && c.KeySize != 24 && c.KeySize != 32 { + c.KeySize = DefaultKeySize + } + if c.HashFunc == nil { + c.HashFunc = DefaultHashFunc + } + + //generate key + key := pbkdf2.Key([]byte(c.Password), c.Salt, c.Iterations, c.KeySize, c.HashFunc) + + // could use scrypt, but it's a bit slow... + // dk, err := scrypt.Key([]byte(c.Password), c.Salt, 16384, 8, 1, 32) + + //key will be always be the correct size so this will never error + conn, _ = NewFromKey(conn, key) + return conn +} + +//NewFromKey creates an AES encrypted net.Conn using the provided key +func NewFromKey(conn net.Conn, key []byte) (net.Conn, error) { + block, err := aes.NewCipher([]byte(key)) + if err != nil { + return nil, err + } + //hash(key) -> read IV + riv := DefaultHashFunc().Sum(key) + rstream := cipher.NewCFBDecrypter(block, riv[:aes.BlockSize]) + reader := &cipher.StreamReader{S: rstream, R: conn} + //hash(read IV) -> write IV + wiv := DefaultHashFunc().Sum(riv) + wstream := cipher.NewCFBEncrypter(block, wiv[:aes.BlockSize]) + writer := &cipher.StreamWriter{S: wstream, W: conn} + + return &cryptoConn{ + Conn: conn, + r: reader, + w: writer, + }, nil +} + +type cryptoConn struct { + net.Conn + r io.Reader + w io.Writer +} + +//replace read and write methods +func (c *cryptoConn) Read(p []byte) (int, error) { + return c.r.Read(p) +} +func (c *cryptoConn) Write(p []byte) (int, error) { + return c.w.Write(p) +} diff --git a/utils/functions.go b/utils/functions.go index ae6a349..9429a40 100755 --- a/utils/functions.go +++ b/utils/functions.go @@ -18,6 +18,7 @@ import ( "net/http" "os" "os/exec" + "snail007/proxy/services/kcpcfg" "golang.org/x/crypto/pbkdf2" @@ -85,14 +86,14 @@ func ioCopy(dst io.ReadWriter, src io.ReadWriter) (err error) { } } } -func TlsConnectHost(host string, timeout int, certBytes, keyBytes []byte) (conn tls.Conn, err error) { +func TlsConnectHost(host string, timeout int, certBytes, keyBytes, caCertBytes []byte) (conn tls.Conn, err error) { h := strings.Split(host, ":") port, _ := strconv.Atoi(h[1]) - return TlsConnect(h[0], port, timeout, certBytes, keyBytes) + return TlsConnect(h[0], port, timeout, certBytes, keyBytes, caCertBytes) } -func TlsConnect(host string, port, timeout int, certBytes, keyBytes []byte) (conn tls.Conn, err error) { - conf, err := getRequestTlsConfig(certBytes, keyBytes) +func TlsConnect(host string, port, timeout int, certBytes, keyBytes, caCertBytes []byte) (conn tls.Conn, err error) { + conf, err := getRequestTlsConfig(certBytes, keyBytes, caCertBytes) if err != nil { return } @@ -102,8 +103,24 @@ func TlsConnect(host string, port, timeout int, certBytes, keyBytes []byte) (con } return *tls.Client(_conn, conf), err } -func getRequestTlsConfig(certBytes, keyBytes []byte) (conf *tls.Config, err error) { - block, _ := pem.Decode(certBytes) +func getRequestTlsConfig(certBytes, keyBytes, caCertBytes []byte) (conf *tls.Config, err error) { + + var cert tls.Certificate + cert, err = tls.X509KeyPair(certBytes, keyBytes) + if err != nil { + return + } + serverCertPool := x509.NewCertPool() + caBytes := certBytes + if caCertBytes != nil { + caBytes = caCertBytes + + } + ok := serverCertPool.AppendCertsFromPEM(caBytes) + if !ok { + err = errors.New("failed to parse root certificate") + } + block, _ := pem.Decode(caBytes) if block == nil { panic("failed to parse certificate PEM") } @@ -111,34 +128,24 @@ func getRequestTlsConfig(certBytes, keyBytes []byte) (conf *tls.Config, err erro if x509Cert == nil { panic("failed to parse block") } - var cert tls.Certificate - cert, err = tls.X509KeyPair(certBytes, keyBytes) - if err != nil { - return - } - serverCertPool := x509.NewCertPool() - ok := serverCertPool.AppendCertsFromPEM(certBytes) - if !ok { - err = errors.New("failed to parse root certificate") - } conf = &tls.Config{ RootCAs: serverCertPool, Certificates: []tls.Certificate{cert}, - InsecureSkipVerify: false, + InsecureSkipVerify: true, ServerName: x509Cert.Subject.CommonName, - // VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - // opts := x509.VerifyOptions{ - // Roots: serverCertPool, - // } - // for _, rawCert := range rawCerts { - // cert, _ := x509.ParseCertificate(rawCert) - // _, err := cert.Verify(opts) - // if err != nil { - // return err - // } - // } - // return nil - // }, + VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + opts := x509.VerifyOptions{ + Roots: serverCertPool, + } + for _, rawCert := range rawCerts { + cert, _ := x509.ParseCertificate(rawCert) + _, err := cert.Verify(opts) + if err != nil { + return err + } + } + return nil + }, } return } @@ -147,33 +154,36 @@ func ConnectHost(hostAndPort string, timeout int) (conn net.Conn, err error) { conn, err = net.DialTimeout("tcp", hostAndPort, time.Duration(timeout)*time.Millisecond) return } -func ConnectKCPHost(hostAndPort, method, key string) (conn net.Conn, err error) { - kcpconn, err := kcp.DialWithOptions(hostAndPort, GetKCPBlock(method, key), 10, 3) +func ConnectKCPHost(hostAndPort string, config kcpcfg.KCPConfigArgs) (conn net.Conn, err error) { + kcpconn, err := kcp.DialWithOptions(hostAndPort, config.Block, *config.DataShard, *config.ParityShard) if err != nil { return } - kcpconn.SetNoDelay(1, 10, 2, 1) - kcpconn.SetWindowSize(1024, 1024) - kcpconn.SetMtu(1400) - kcpconn.SetACKNoDelay(false) - return kcpconn, err + kcpconn.SetStreamMode(true) + kcpconn.SetWriteDelay(true) + kcpconn.SetNoDelay(*config.NoDelay, *config.Interval, *config.Resend, *config.NoCongestion) + kcpconn.SetMtu(*config.MTU) + kcpconn.SetWindowSize(*config.SndWnd, *config.RcvWnd) + kcpconn.SetACKNoDelay(*config.AckNodelay) + if *config.NoComp { + return kcpconn, err + } + return NewCompStream(kcpconn), err } -func ListenTls(ip string, port int, certBytes, keyBytes []byte) (ln *net.Listener, err error) { - block, _ := pem.Decode(certBytes) - if block == nil { - panic("failed to parse certificate PEM") - } - x509Cert, _ := x509.ParseCertificate(block.Bytes) - if x509Cert == nil { - panic("failed to parse block") - } + +func ListenTls(ip string, port int, certBytes, keyBytes, caCertBytes []byte) (ln *net.Listener, err error) { + var cert tls.Certificate cert, err = tls.X509KeyPair(certBytes, keyBytes) if err != nil { return } clientCertPool := x509.NewCertPool() - ok := clientCertPool.AppendCertsFromPEM(certBytes) + caBytes := certBytes + if caCertBytes != nil { + caBytes = caCertBytes + } + ok := clientCertPool.AppendCertsFromPEM(caBytes) if !ok { err = errors.New("failed to parse root certificate") } @@ -181,21 +191,6 @@ func ListenTls(ip string, port int, certBytes, keyBytes []byte) (ln *net.Listene ClientCAs: clientCertPool, Certificates: []tls.Certificate{cert}, ClientAuth: tls.RequireAndVerifyClientCert, - ServerName: x509Cert.Subject.CommonName, - // VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - // opts := x509.VerifyOptions{ - // Roots: clientCertPool, - // } - // for _, rawCert := range rawCerts { - // cert, _ := x509.ParseCertificate(rawCert) - // _, err := cert.Verify(opts) - // fmt.Println("SERVER ERR:", err) - // if err != nil { - // return err - // } - // } - // return nil - // }, } _ln, err := tls.Listen("tcp", fmt.Sprintf("%s:%d", ip, port), config) if err == nil { @@ -238,27 +233,95 @@ func CloseConn(conn *net.Conn) { } } func Keygen() (err error) { - cmd := exec.Command("sh", "-c", "openssl genrsa -out proxy.key 2048") - out, err := cmd.CombinedOutput() - if err != nil { - log.Printf("err:%s", err) - return - } - fmt.Println(string(out)) CList := []string{"AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AR", "AT", "AU", "AZ", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BR", "BS", "BW", "BY", "BZ", "CA", "CF", "CG", "CH", "CK", "CL", "CM", "CN", "CO", "CR", "CS", "CU", "CY", "CZ", "DE", "DJ", "DK", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FJ", "FR", "GA", "GB", "GD", "GE", "GF", "GH", "GI", "GM", "GN", "GR", "GT", "GU", "GY", "HK", "HN", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IR", "IS", "IT", "JM", "JO", "JP", "KE", "KG", "KH", "KP", "KR", "KT", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "ML", "MM", "MN", "MO", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NR", "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PR", "PT", "PY", "QA", "RO", "RU", "SA", "SB", "SC", "SD", "SE", "SG", "SI", "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", "SY", "SZ", "TD", "TG", "TH", "TJ", "TM", "TN", "TO", "TR", "TT", "TW", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "YE", "YU", "ZA", "ZM", "ZR", "ZW"} domainSubfixList := []string{".com", ".edu", ".gov", ".int", ".mil", ".net", ".org", ".biz", ".info", ".pro", ".name", ".museum", ".coop", ".aero", ".xxx", ".idv", ".ac", ".ad", ".ae", ".af", ".ag", ".ai", ".al", ".am", ".an", ".ao", ".aq", ".ar", ".as", ".at", ".au", ".aw", ".az", ".ba", ".bb", ".bd", ".be", ".bf", ".bg", ".bh", ".bi", ".bj", ".bm", ".bn", ".bo", ".br", ".bs", ".bt", ".bv", ".bw", ".by", ".bz", ".ca", ".cc", ".cd", ".cf", ".cg", ".ch", ".ci", ".ck", ".cl", ".cm", ".cn", ".co", ".cr", ".cu", ".cv", ".cx", ".cy", ".cz", ".de", ".dj", ".dk", ".dm", ".do", ".dz", ".ec", ".ee", ".eg", ".eh", ".er", ".es", ".et", ".eu", ".fi", ".fj", ".fk", ".fm", ".fo", ".fr", ".ga", ".gd", ".ge", ".gf", ".gg", ".gh", ".gi", ".gl", ".gm", ".gn", ".gp", ".gq", ".gr", ".gs", ".gt", ".gu", ".gw", ".gy", ".hk", ".hm", ".hn", ".hr", ".ht", ".hu", ".id", ".ie", ".il", ".im", ".in", ".io", ".iq", ".ir", ".is", ".it", ".je", ".jm", ".jo", ".jp", ".ke", ".kg", ".kh", ".ki", ".km", ".kn", ".kp", ".kr", ".kw", ".ky", ".kz", ".la", ".lb", ".lc", ".li", ".lk", ".lr", ".ls", ".lt", ".lu", ".lv", ".ly", ".ma", ".mc", ".md", ".mg", ".mh", ".mk", ".ml", ".mm", ".mn", ".mo", ".mp", ".mq", ".mr", ".ms", ".mt", ".mu", ".mv", ".mw", ".mx", ".my", ".mz", ".na", ".nc", ".ne", ".nf", ".ng", ".ni", ".nl", ".no", ".np", ".nr", ".nu", ".nz", ".om", ".pa", ".pe", ".pf", ".pg", ".ph", ".pk", ".pl", ".pm", ".pn", ".pr", ".ps", ".pt", ".pw", ".py", ".qa", ".re", ".ro", ".ru", ".rw", ".sa", ".sb", ".sc", ".sd", ".se", ".sg", ".sh", ".si", ".sj", ".sk", ".sl", ".sm", ".sn", ".so", ".sr", ".st", ".sv", ".sy", ".sz", ".tc", ".td", ".tf", ".tg", ".th", ".tj", ".tk", ".tl", ".tm", ".tn", ".to", ".tp", ".tr", ".tt", ".tv", ".tw", ".tz", ".ua", ".ug", ".uk", ".um", ".us", ".uy", ".uz", ".va", ".vc", ".ve", ".vg", ".vi", ".vn", ".vu", ".wf", ".ws", ".ye", ".yt", ".yu", ".yr", ".za", ".zm", ".zw"} C := CList[int(RandInt(4))%len(CList)] ST := RandString(int(RandInt(4) % 10)) O := RandString(int(RandInt(4) % 10)) CN := strings.ToLower(RandString(int(RandInt(4)%10)) + domainSubfixList[int(RandInt(4))%len(domainSubfixList)]) - cmdStr := fmt.Sprintf("openssl req -new -key proxy.key -x509 -days 36500 -out proxy.crt -subj /C=%s/ST=%s/O=%s/CN=%s", C, ST, O, CN) - cmd = exec.Command("sh", "-c", cmdStr) - out, err = cmd.CombinedOutput() - if err != nil { - log.Printf("err:%s", err) - return + //log.Printf("C: %s, ST: %s, O: %s, CN: %s", C, ST, O, CN) + var out []byte + if len(os.Args) == 3 && os.Args[2] == "ca" { + cmd := exec.Command("sh", "-c", "openssl genrsa -out ca.key 2048") + out, err = cmd.CombinedOutput() + if err != nil { + log.Printf("err:%s", err) + return + } + fmt.Println(string(out)) + + cmdStr := fmt.Sprintf("openssl req -new -key ca.key -x509 -days 36500 -out ca.crt -subj /C=%s/ST=%s/O=%s/CN=%s", C, ST, O, "*."+CN) + cmd = exec.Command("sh", "-c", cmdStr) + out, err = cmd.CombinedOutput() + if err != nil { + log.Printf("err:%s", err) + return + } + fmt.Println(string(out)) + } else if len(os.Args) == 5 && os.Args[2] == "ca" && os.Args[3] != "" && os.Args[4] != "" { + certBytes, _ := ioutil.ReadFile("ca.crt") + block, _ := pem.Decode(certBytes) + if block == nil || certBytes == nil { + panic("failed to parse ca certificate PEM") + } + x509Cert, _ := x509.ParseCertificate(block.Bytes) + if x509Cert == nil { + panic("failed to parse block") + } + name := os.Args[3] + days := os.Args[4] + cmd := exec.Command("sh", "-c", "openssl genrsa -out "+name+".key 2048") + out, err = cmd.CombinedOutput() + if err != nil { + log.Printf("err:%s", err) + return + } + fmt.Println(string(out)) + + cmdStr := fmt.Sprintf("openssl req -new -key %s.key -out %s.csr -subj /C=%s/ST=%s/O=%s/CN=%s", name, name, C, ST, O, CN) + fmt.Printf("%s", cmdStr) + cmd = exec.Command("sh", "-c", cmdStr) + out, err = cmd.CombinedOutput() + if err != nil { + log.Printf("err:%s", err) + return + } + fmt.Println(string(out)) + + cmdStr = fmt.Sprintf("openssl x509 -req -days %s -in %s.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out %s.crt", days, name, name) + fmt.Printf("%s", cmdStr) + cmd = exec.Command("sh", "-c", cmdStr) + out, err = cmd.CombinedOutput() + if err != nil { + log.Printf("err:%s", err) + return + } + + fmt.Println(string(out)) + } else if len(os.Args) == 3 && os.Args[2] == "usage" { + fmt.Println(`proxy keygen //generate proxy.crt and proxy.key +proxy keygen ca //generate ca.crt and ca.key +proxy keygen ca client0 30 //generate client0.crt client0.key and use ca.crt sign it with 30 days + `) + } else if len(os.Args) == 2 { + cmd := exec.Command("sh", "-c", "openssl genrsa -out proxy.key 2048") + out, err = cmd.CombinedOutput() + if err != nil { + log.Printf("err:%s", err) + return + } + fmt.Println(string(out)) + + cmdStr := fmt.Sprintf("openssl req -new -key proxy.key -x509 -days 36500 -out proxy.crt -subj /C=%s/ST=%s/O=%s/CN=%s", C, ST, O, CN) + cmd = exec.Command("sh", "-c", cmdStr) + out, err = cmd.CombinedOutput() + if err != nil { + log.Printf("err:%s", err) + return + } + fmt.Println(string(out)) } - fmt.Println(string(out)) + return } func GetAllInterfaceAddr() ([]net.IP, error) { @@ -461,15 +524,15 @@ func SubBytes(bytes []byte, start, end int) []byte { } return bytes[start:end] } -func TlsBytes(cert, key string) (certBytes, keyBytes []byte) { - certBytes, err := ioutil.ReadFile(cert) +func TlsBytes(cert, key string) (certBytes, keyBytes []byte, err error) { + certBytes, err = ioutil.ReadFile(cert) if err != nil { - log.Fatalf("err : %s", err) + err = fmt.Errorf("err : %s", err) return } keyBytes, err = ioutil.ReadFile(key) if err != nil { - log.Fatalf("err : %s", err) + err = fmt.Errorf("err : %s", err) return } return diff --git a/utils/pool.go b/utils/pool.go deleted file mode 100755 index ae30f6f..0000000 --- a/utils/pool.go +++ /dev/null @@ -1,145 +0,0 @@ -package utils - -import ( - "log" - "sync" - "time" -) - -//ConnPool to use -type ConnPool interface { - Get() (conn interface{}, err error) - Put(conn interface{}) - ReleaseAll() - Len() (length int) -} -type poolConfig struct { - Factory func() (interface{}, error) - IsActive func(interface{}) bool - Release func(interface{}) - InitialCap int - MaxCap int -} - -func NewConnPool(poolConfig poolConfig) (pool ConnPool, err error) { - p := netPool{ - config: poolConfig, - conns: make(chan interface{}, poolConfig.MaxCap), - lock: &sync.Mutex{}, - } - //log.Printf("pool MaxCap:%d", poolConfig.MaxCap) - if poolConfig.MaxCap > 0 { - err = p.initAutoFill(false) - if err == nil { - p.initAutoFill(true) - } - } - return &p, nil -} - -type netPool struct { - conns chan interface{} - lock *sync.Mutex - config poolConfig -} - -func (p *netPool) initAutoFill(async bool) (err error) { - var worker = func() (err error) { - for { - //log.Printf("pool fill: %v , len: %d", p.Len() <= p.config.InitialCap/2, p.Len()) - if p.Len() <= p.config.InitialCap/2 { - p.lock.Lock() - errN := 0 - for i := 0; i < p.config.InitialCap; i++ { - c, err := p.config.Factory() - if err != nil { - errN++ - if async { - continue - } else { - p.lock.Unlock() - return err - } - } - select { - case p.conns <- c: - default: - p.config.Release(c) - break - } - if p.Len() >= p.config.InitialCap { - break - } - } - if errN > 0 { - log.Printf("fill conn pool fail , ERRN:%d", errN) - } - p.lock.Unlock() - } - if !async { - return - } - time.Sleep(time.Second * 2) - } - } - if async { - go worker() - } else { - err = worker() - } - return - -} - -func (p *netPool) Get() (conn interface{}, err error) { - // defer func() { - // log.Printf("pool len : %d", p.Len()) - // }() - p.lock.Lock() - defer p.lock.Unlock() - // for { - select { - case conn = <-p.conns: - if p.config.IsActive(conn) { - return - } - p.config.Release(conn) - default: - conn, err = p.config.Factory() - if err != nil { - return nil, err - } - return conn, nil - } - // } - return -} - -func (p *netPool) Put(conn interface{}) { - if conn == nil { - return - } - p.lock.Lock() - defer p.lock.Unlock() - if !p.config.IsActive(conn) { - p.config.Release(conn) - } - select { - case p.conns <- conn: - default: - p.config.Release(conn) - } -} -func (p *netPool) ReleaseAll() { - p.lock.Lock() - defer p.lock.Unlock() - close(p.conns) - for c := range p.conns { - p.config.Release(c) - } - p.conns = make(chan interface{}, p.config.InitialCap) - -} -func (p *netPool) Len() (length int) { - return len(p.conns) -} diff --git a/utils/serve-channel.go b/utils/serve-channel.go index c023518..8547441 100644 --- a/utils/serve-channel.go +++ b/utils/serve-channel.go @@ -5,6 +5,7 @@ import ( "log" "net" "runtime/debug" + "snail007/proxy/services/kcpcfg" "strconv" kcp "github.com/xtaci/kcp-go" @@ -23,7 +24,7 @@ func NewServerChannel(ip string, port int) ServerChannel { ip: ip, port: port, errAcceptHandler: func(err error) { - fmt.Printf("accept error , ERR:%s", err) + log.Printf("accept error , ERR:%s", err) }, } } @@ -41,8 +42,8 @@ func NewServerChannelHost(host string) ServerChannel { func (sc *ServerChannel) SetErrAcceptHandler(fn func(err error)) { sc.errAcceptHandler = fn } -func (sc *ServerChannel) ListenTls(certBytes, keyBytes []byte, fn func(conn net.Conn)) (err error) { - sc.Listener, err = ListenTls(sc.ip, sc.port, certBytes, keyBytes) +func (sc *ServerChannel) ListenTls(certBytes, keyBytes, caCertBytes []byte, fn func(conn net.Conn)) (err error) { + sc.Listener, err = ListenTls(sc.ip, sc.port, certBytes, keyBytes, caCertBytes) if err == nil { go func() { defer func() { @@ -138,11 +139,23 @@ func (sc *ServerChannel) ListenUDP(fn func(packet []byte, localAddr, srcAddr *ne } return } -func (sc *ServerChannel) ListenKCP(method, key string, fn func(conn net.Conn)) (err error) { - var l net.Listener - l, err = kcp.ListenWithOptions(fmt.Sprintf("%s:%d", sc.ip, sc.port), GetKCPBlock(method, key), 10, 3) +func (sc *ServerChannel) ListenKCP(config kcpcfg.KCPConfigArgs, fn func(conn net.Conn)) (err error) { + lis, err := kcp.ListenWithOptions(fmt.Sprintf("%s:%d", sc.ip, sc.port), config.Block, *config.DataShard, *config.ParityShard) if err == nil { - sc.Listener = &l + if err = lis.SetDSCP(*config.DSCP); err != nil { + log.Println("SetDSCP:", err) + return + } + if err = lis.SetReadBuffer(*config.SockBuf); err != nil { + log.Println("SetReadBuffer:", err) + return + } + if err = lis.SetWriteBuffer(*config.SockBuf); err != nil { + log.Println("SetWriteBuffer:", err) + return + } + sc.Listener = new(net.Listener) + *sc.Listener = lis go func() { defer func() { if e := recover(); e != nil { @@ -150,8 +163,8 @@ func (sc *ServerChannel) ListenKCP(method, key string, fn func(conn net.Conn)) ( } }() for { - var conn net.Conn - conn, err = (*sc.Listener).Accept() + //var conn net.Conn + conn, err := lis.AcceptKCP() if err == nil { go func() { defer func() { @@ -159,7 +172,18 @@ func (sc *ServerChannel) ListenKCP(method, key string, fn func(conn net.Conn)) ( log.Printf("kcp connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) } }() - fn(conn) + conn.SetStreamMode(true) + conn.SetWriteDelay(true) + conn.SetNoDelay(*config.NoDelay, *config.Interval, *config.Resend, *config.NoCongestion) + conn.SetMtu(*config.MTU) + conn.SetWindowSize(*config.SndWnd, *config.RcvWnd) + conn.SetACKNoDelay(*config.AckNodelay) + if *config.NoComp { + fn(conn) + } else { + cconn := NewCompStream(conn) + fn(cconn) + } }() } else { sc.errAcceptHandler(err) diff --git a/utils/socks/client.go b/utils/socks/client.go new file mode 100644 index 0000000..619e3c7 --- /dev/null +++ b/utils/socks/client.go @@ -0,0 +1,253 @@ +package socks + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "net" + "strconv" + "time" +) + +var socks5Errors = []string{ + "", + "general failure", + "connection forbidden", + "network unreachable", + "host unreachable", + "connection refused", + "TTL expired", + "command not supported", + "address type not supported", +} + +type Auth struct { + User, Password string +} +type ClientConn struct { + user string + password string + conn *net.Conn + header []byte + timeout time.Duration + addr string + network string + udpAddr string +} + +// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address +// with an optional username and password. See RFC 1928 and RFC 1929. +// target must be a canonical address with a host and port. +// network : tcp udp +func NewClientConn(conn *net.Conn, network, target string, timeout time.Duration, auth *Auth, header []byte) *ClientConn { + s := &ClientConn{ + conn: conn, + network: network, + timeout: timeout, + } + if auth != nil { + s.user = auth.User + s.password = auth.Password + } + if header != nil && len(header) > 0 { + s.header = header + } + if network == "udp" && target == "" { + target = "0.0.0.0:1" + } + s.addr = target + return s +} + +// connect takes an existing connection to a socks5 proxy server, +// and commands the server to extend that connection to target, +// which must be a canonical address with a host and port. +func (s *ClientConn) Handshake() error { + host, portStr, err := net.SplitHostPort(s.addr) + if err != nil { + return err + } + port, err := strconv.Atoi(portStr) + if err != nil { + return errors.New("proxy: failed to parse port number: " + portStr) + } + if port < 1 || port > 0xffff { + return errors.New("proxy: port number out of range: " + portStr) + } + + if err := s.handshake(host); err != nil { + return err + } + buf := []byte{} + if s.network == "tcp" { + buf = append(buf, VERSION_V5, CMD_CONNECT, 0 /* reserved */) + + } else { + buf = append(buf, VERSION_V5, CMD_ASSOCIATE, 0 /* reserved */) + } + if ip := net.ParseIP(host); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + buf = append(buf, ATYP_IPV4) + ip = ip4 + } else { + buf = append(buf, ATYP_IPV6) + } + buf = append(buf, ip...) + } else { + if len(host) > 255 { + return errors.New("proxy: destination host name too long: " + host) + } + buf = append(buf, ATYP_DOMAIN) + buf = append(buf, byte(len(host))) + buf = append(buf, host...) + } + buf = append(buf, byte(port>>8), byte(port)) + (*s.conn).SetDeadline(time.Now().Add(s.timeout)) + if _, err := (*s.conn).Write(buf); err != nil { + return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + (*s.conn).SetDeadline(time.Time{}) + (*s.conn).SetDeadline(time.Now().Add(s.timeout)) + if _, err := io.ReadFull((*s.conn), buf[:4]); err != nil { + return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + (*s.conn).SetDeadline(time.Time{}) + failure := "unknown error" + if int(buf[1]) < len(socks5Errors) { + failure = socks5Errors[buf[1]] + } + + if len(failure) > 0 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) + } + + bytesToDiscard := 0 + switch buf[3] { + case ATYP_IPV4: + bytesToDiscard = net.IPv4len + case ATYP_IPV6: + bytesToDiscard = net.IPv6len + case ATYP_DOMAIN: + (*s.conn).SetDeadline(time.Now().Add(s.timeout)) + _, err := io.ReadFull((*s.conn), buf[:1]) + (*s.conn).SetDeadline(time.Time{}) + if err != nil { + return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + bytesToDiscard = int(buf[0]) + default: + return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) + } + + if cap(buf) < bytesToDiscard { + buf = make([]byte, bytesToDiscard) + } else { + buf = buf[:bytesToDiscard] + } + (*s.conn).SetDeadline(time.Now().Add(s.timeout)) + if _, err := io.ReadFull((*s.conn), buf); err != nil { + return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + (*s.conn).SetDeadline(time.Time{}) + var ip net.IP + ip = buf + ipStr := "" + if bytesToDiscard == net.IPv4len || bytesToDiscard == net.IPv6len { + if ipv4 := ip.To4(); ipv4 != nil { + ipStr = ipv4.String() + } else { + ipStr = ip.To16().String() + } + } + //log.Printf("%v", ipStr) + // Also need to discard the port number + (*s.conn).SetDeadline(time.Now().Add(s.timeout)) + if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil { + return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + p := binary.BigEndian.Uint16([]byte{buf[0], buf[1]}) + //log.Printf("%v", p) + s.udpAddr = net.JoinHostPort(ipStr, fmt.Sprintf("%d", p)) + //log.Printf("%v", s.udpAddr) + (*s.conn).SetDeadline(time.Time{}) + return nil +} +func (s *ClientConn) SendUDP(data []byte, addr string) (respData []byte, err error) { + + c, err := net.DialTimeout("udp", s.udpAddr, s.timeout) + if err != nil { + return + } + conn := c.(*net.UDPConn) + + p := NewPacketUDP() + p.Build(addr, data) + conn.SetDeadline(time.Now().Add(s.timeout)) + conn.Write(p.Bytes()) + conn.SetDeadline(time.Time{}) + + buf := make([]byte, 1024) + conn.SetDeadline(time.Now().Add(s.timeout)) + n, _, err := conn.ReadFrom(buf) + conn.SetDeadline(time.Time{}) + if err != nil { + return + } + respData = buf[:n] + return +} +func (s *ClientConn) handshake(host string) error { + + // the size here is just an estimate + buf := make([]byte, 0, 6+len(host)) + + buf = append(buf, VERSION_V5) + if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { + buf = append(buf, 2 /* num auth methods */, Method_NO_AUTH, Method_USER_PASS) + } else { + buf = append(buf, 1 /* num auth methods */, Method_NO_AUTH) + } + (*s.conn).SetDeadline(time.Now().Add(s.timeout)) + if _, err := (*s.conn).Write(buf); err != nil { + return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + (*s.conn).SetDeadline(time.Time{}) + + (*s.conn).SetDeadline(time.Now().Add(s.timeout)) + if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil { + return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + (*s.conn).SetDeadline(time.Time{}) + + if buf[0] != 5 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) + } + if buf[1] == 0xff { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") + } + + // See RFC 1929 + if buf[1] == Method_USER_PASS { + buf = buf[:0] + buf = append(buf, 1 /* password protocol version */) + buf = append(buf, uint8(len(s.user))) + buf = append(buf, s.user...) + buf = append(buf, uint8(len(s.password))) + buf = append(buf, s.password...) + (*s.conn).SetDeadline(time.Now().Add(s.timeout)) + if _, err := (*s.conn).Write(buf); err != nil { + return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + (*s.conn).SetDeadline(time.Time{}) + (*s.conn).SetDeadline(time.Now().Add(s.timeout)) + if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil { + return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + (*s.conn).SetDeadline(time.Time{}) + if buf[1] != 0 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") + } + } + return nil +} diff --git a/utils/socks/server.go b/utils/socks/server.go new file mode 100644 index 0000000..3cf00be --- /dev/null +++ b/utils/socks/server.go @@ -0,0 +1,226 @@ +package socks + +import ( + "fmt" + "net" + "snail007/proxy/utils" + "strings" + "time" +) + +const ( + Method_NO_AUTH = uint8(0x00) + Method_GSSAPI = uint8(0x01) + Method_USER_PASS = uint8(0x02) + Method_IANA = uint8(0x7F) + Method_RESVERVE = uint8(0x80) + Method_NONE_ACCEPTABLE = uint8(0xFF) + VERSION_V5 = uint8(0x05) + CMD_CONNECT = uint8(0x01) + CMD_BIND = uint8(0x02) + CMD_ASSOCIATE = uint8(0x03) + ATYP_IPV4 = uint8(0x01) + ATYP_DOMAIN = uint8(0x03) + ATYP_IPV6 = uint8(0x04) + REP_SUCCESS = uint8(0x00) + REP_REQ_FAIL = uint8(0x01) + REP_RULE_FORBIDDEN = uint8(0x02) + REP_NETWOR_UNREACHABLE = uint8(0x03) + REP_HOST_UNREACHABLE = uint8(0x04) + REP_CONNECTION_REFUSED = uint8(0x05) + REP_TTL_TIMEOUT = uint8(0x06) + REP_CMD_UNSUPPORTED = uint8(0x07) + REP_ATYP_UNSUPPORTED = uint8(0x08) + REP_UNKNOWN = uint8(0x09) + RSV = uint8(0x00) +) + +var ( + ZERO_IP = []byte{0x00, 0x00, 0x00, 0x00} + ZERO_PORT = []byte{0x00, 0x00} +) + +type ServerConn struct { + target string + user string + password string + conn *net.Conn + timeout time.Duration + auth *utils.BasicAuth + header []byte + ver uint8 + //method + methodsCount uint8 + methods []uint8 + method uint8 + //request + cmd uint8 + reserve uint8 + addressType uint8 + dstAddr string + dstPort string + dstHost string + udpAddress string +} + +func NewServerConn(conn *net.Conn, timeout time.Duration, auth *utils.BasicAuth, udpAddress string, header []byte) *ServerConn { + if udpAddress == "" { + udpAddress = "0.0.0.0:16666" + } + s := &ServerConn{ + conn: conn, + timeout: timeout, + auth: auth, + header: header, + ver: VERSION_V5, + udpAddress: udpAddress, + } + return s + +} +func (s *ServerConn) Close() { + utils.CloseConn(s.conn) +} +func (s *ServerConn) AuthData() Auth { + return Auth{s.user, s.password} +} +func (s *ServerConn) Method() uint8 { + return s.method +} +func (s *ServerConn) Target() string { + return s.target +} +func (s *ServerConn) Handshake() (err error) { + remoteAddr := (*s.conn).RemoteAddr() + //协商开始 + //method select request + var methodReq MethodsRequest + (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) + + methodReq, e := NewMethodsRequest((*s.conn), s.header) + (*s.conn).SetReadDeadline(time.Time{}) + if e != nil { + (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) + methodReq.Reply(Method_NONE_ACCEPTABLE) + (*s.conn).SetReadDeadline(time.Time{}) + err = fmt.Errorf("new methods request fail,ERR: %s", e) + return + } + //log.Printf("%v,s.auth == %v && methodReq.Select(Method_NO_AUTH) %v", methodReq.methods, s.auth, methodReq.Select(Method_NO_AUTH)) + if s.auth == nil && methodReq.Select(Method_NO_AUTH) && !methodReq.Select(Method_USER_PASS) { + // if !methodReq.Select(Method_NO_AUTH) { + // (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) + // methodReq.Reply(Method_NONE_ACCEPTABLE) + // (*s.conn).SetReadDeadline(time.Time{}) + // err = fmt.Errorf("none method found : Method_NO_AUTH") + // return + // } + s.method = Method_NO_AUTH + //method select reply + (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) + err = methodReq.Reply(Method_NO_AUTH) + (*s.conn).SetReadDeadline(time.Time{}) + if err != nil { + err = fmt.Errorf("reply answer data fail,ERR: %s", err) + return + } + // err = fmt.Errorf("% x", methodReq.Bytes()) + } else { + //auth + if !methodReq.Select(Method_USER_PASS) { + (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) + methodReq.Reply(Method_NONE_ACCEPTABLE) + (*s.conn).SetReadDeadline(time.Time{}) + err = fmt.Errorf("none method found : Method_USER_PASS") + return + } + s.method = Method_USER_PASS + //method reply need auth + (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) + err = methodReq.Reply(Method_USER_PASS) + (*s.conn).SetReadDeadline(time.Time{}) + if err != nil { + err = fmt.Errorf("reply answer data fail,ERR: %s", err) + return + } + //read auth + buf := make([]byte, 500) + var n int + (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) + n, err = (*s.conn).Read(buf) + (*s.conn).SetReadDeadline(time.Time{}) + if err != nil { + err = fmt.Errorf("read auth info fail,ERR: %s", err) + return + } + r := buf[:n] + s.user = string(r[2 : r[1]+2]) + s.password = string(r[2+r[1]+1:]) + //err = fmt.Errorf("user:%s,pass:%s", user, pass) + //auth + _addr := strings.Split(remoteAddr.String(), ":") + if s.auth == nil || s.auth.CheckUserPass(s.user, s.password, _addr[0], "") { + (*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout))) + _, err = (*s.conn).Write([]byte{0x01, 0x00}) + (*s.conn).SetDeadline(time.Time{}) + if err != nil { + err = fmt.Errorf("answer auth success to %s fail,ERR: %s", remoteAddr, err) + return + } + } else { + (*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout))) + _, err = (*s.conn).Write([]byte{0x01, 0x01}) + (*s.conn).SetDeadline(time.Time{}) + if err != nil { + err = fmt.Errorf("answer auth fail to %s fail,ERR: %s", remoteAddr, err) + return + } + err = fmt.Errorf("auth fail from %s", remoteAddr) + return + } + } + //request detail + (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) + request, e := NewRequest(*s.conn) + (*s.conn).SetReadDeadline(time.Time{}) + if e != nil { + err = fmt.Errorf("read request data fail,ERR: %s", e) + return + } + //协商结束 + + switch request.CMD() { + case CMD_BIND: + err = request.TCPReply(REP_UNKNOWN) + if err != nil { + err = fmt.Errorf("TCPReply REP_UNKNOWN to %s fail,ERR: %s", remoteAddr, err) + return + } + err = fmt.Errorf("cmd bind not supported, form: %s", remoteAddr) + return + case CMD_CONNECT: + err = request.TCPReply(REP_SUCCESS) + if err != nil { + err = fmt.Errorf("TCPReply REP_SUCCESS to %s fail,ERR: %s", remoteAddr, err) + return + } + case CMD_ASSOCIATE: + err = request.UDPReply(REP_SUCCESS, s.udpAddress) + if err != nil { + err = fmt.Errorf("UDPReply REP_SUCCESS to %s fail,ERR: %s", remoteAddr, err) + return + } + } + + //fill socks info + s.target = request.Addr() + s.methodsCount = methodReq.MethodsCount() + s.methods = methodReq.Methods() + s.cmd = request.CMD() + s.reserve = request.reserve + s.addressType = request.addressType + s.dstAddr = request.dstAddr + s.dstHost = request.dstHost + s.dstPort = request.dstPort + return +} diff --git a/utils/socks/structs.go b/utils/socks/structs.go index ef5b51b..a5ec922 100644 --- a/utils/socks/structs.go +++ b/utils/socks/structs.go @@ -3,44 +3,13 @@ package socks import ( "bytes" "encoding/binary" + "errors" "fmt" "io" "net" "strconv" ) -const ( - Method_NO_AUTH = uint8(0x00) - Method_GSSAPI = uint8(0x01) - Method_USER_PASS = uint8(0x02) - Method_IANA = uint8(0x7F) - Method_RESVERVE = uint8(0x80) - Method_NONE_ACCEPTABLE = uint8(0xFF) - VERSION_V5 = uint8(0x05) - CMD_CONNECT = uint8(0x01) - CMD_BIND = uint8(0x02) - CMD_ASSOCIATE = uint8(0x03) - ATYP_IPV4 = uint8(0x01) - ATYP_DOMAIN = uint8(0x03) - ATYP_IPV6 = uint8(0x04) - REP_SUCCESS = uint8(0x00) - REP_REQ_FAIL = uint8(0x01) - REP_RULE_FORBIDDEN = uint8(0x02) - REP_NETWOR_UNREACHABLE = uint8(0x03) - REP_HOST_UNREACHABLE = uint8(0x04) - REP_CONNECTION_REFUSED = uint8(0x05) - REP_TTL_TIMEOUT = uint8(0x06) - REP_CMD_UNSUPPORTED = uint8(0x07) - REP_ATYP_UNSUPPORTED = uint8(0x08) - REP_UNKNOWN = uint8(0x09) - RSV = uint8(0x00) -) - -var ( - ZERO_IP = []byte{0x00, 0x00, 0x00, 0x00} - ZERO_PORT = []byte{0x00, 0x00} -) - type Request struct { ver uint8 cmd uint8 @@ -57,7 +26,7 @@ func NewRequest(rw io.ReadWriter, header ...[]byte) (req Request, err interface{ var b = make([]byte, 1024) var n int req = Request{rw: rw} - if len(header) == 1 { + if header != nil && len(header) == 1 && len(header[0]) > 1 { b = header[0] n = len(header[0]) } else { @@ -71,7 +40,6 @@ func NewRequest(rw io.ReadWriter, header ...[]byte) (req Request, err interface{ req.cmd = uint8(b[1]) req.reserve = uint8(b[2]) req.addressType = uint8(b[3]) - if b[0] != 0x5 { err = fmt.Errorf("sosck version supported") req.TCPReply(REP_REQ_FAIL) @@ -129,7 +97,7 @@ func (s *Request) NewReply(rep uint8, addr string) []byte { ipv6[4], ipv6[5], ipv6[6], ipv6[7], ipv6[8], ipv6[9], ipv6[10], ipv6[11], ) - if ipv6 != nil && "0000000000255255" != zeroiIPv6 { + if ipb == nil && ipv6 != nil && "0000000000255255" != zeroiIPv6 { atyp = ATYP_IPV6 ipb = ip.To16() } @@ -165,7 +133,7 @@ func NewMethodsRequest(r io.ReadWriter, header ...[]byte) (s MethodsRequest, err s.rw = &r var buf = make([]byte, 300) var n int - if len(header) == 1 { + if header != nil && len(header) == 1 && len(header[0]) > 1 { buf = header[0] n = len(header[0]) } else { @@ -182,7 +150,6 @@ func NewMethodsRequest(r io.ReadWriter, header ...[]byte) (s MethodsRequest, err err = fmt.Errorf("socks methods data length error") return } - s.ver = buf[0] s.methodsCount = buf[1] s.methods = buf[2:n] @@ -195,6 +162,9 @@ func (s *MethodsRequest) Version() uint8 { func (s *MethodsRequest) MethodsCount() uint8 { return s.methodsCount } +func (s *MethodsRequest) Methods() []uint8 { + return s.methods +} func (s *MethodsRequest) Select(method uint8) bool { for _, m := range s.methods { if m == method { @@ -211,17 +181,6 @@ func (s *MethodsRequest) Bytes() []byte { return s.bytes } -type UDPPacket struct { - rsv uint16 - frag uint8 - atype uint8 - dstHost string - dstPort string - data []byte - header []byte - bytes []byte -} - func ParseUDPPacket(b []byte) (p UDPPacket, err error) { p = UDPPacket{} p.frag = uint8(b[2]) @@ -249,6 +208,18 @@ func ParseUDPPacket(b []byte) (p UDPPacket, err error) { p.header = b[:portIndex+2] return } + +type UDPPacket struct { + rsv uint16 + frag uint8 + atype uint8 + dstHost string + dstPort string + data []byte + header []byte + bytes []byte +} + func (s *UDPPacket) Header() []byte { return s.header } @@ -268,3 +239,104 @@ func (s *UDPPacket) Port() string { func (s *UDPPacket) Data() []byte { return s.data } + +type PacketUDP struct { + rsv uint16 + frag uint8 + atype uint8 + dstHost string + dstPort string + data []byte +} + +func NewPacketUDP() (p PacketUDP) { + return PacketUDP{} +} +func (p *PacketUDP) Build(destAddr string, data []byte) (err error) { + host, port, err := net.SplitHostPort(destAddr) + if err != nil { + return + } + p.rsv = 0 + p.frag = 0 + p.dstHost = host + p.dstPort = port + p.atype = ATYP_IPV4 + if ip := net.ParseIP(host); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + p.atype = ATYP_IPV4 + ip = ip4 + } else { + p.atype = ATYP_IPV6 + } + } else { + if len(host) > 255 { + err = errors.New("proxy: destination host name too long: " + host) + return + } + p.atype = ATYP_DOMAIN + } + p.data = data + + return +} +func (p *PacketUDP) Parse(b []byte) (err error) { + p.frag = uint8(b[2]) + if p.frag != 0 { + err = fmt.Errorf("FRAG only support for 0 , %v ,%v", p.frag, b[:4]) + return + } + portIndex := 0 + p.atype = b[3] + switch p.atype { + case ATYP_IPV4: //IP V4 + p.dstHost = net.IPv4(b[4], b[5], b[6], b[7]).String() + portIndex = 8 + case ATYP_DOMAIN: //域名 + domainLen := uint8(b[4]) + p.dstHost = string(b[5 : 5+domainLen]) //b[4]表示域名的长度 + portIndex = int(5 + domainLen) + case ATYP_IPV6: //IP V6 + p.dstHost = net.IP{b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]}.String() + portIndex = 20 + } + p.dstPort = strconv.Itoa(int(b[portIndex])<<8 | int(b[portIndex+1])) + p.data = b[portIndex+2:] + return +} +func (p *PacketUDP) Header() []byte { + header := new(bytes.Buffer) + header.Write([]byte{0x00, 0x00, p.frag, p.atype}) + if p.atype == ATYP_IPV4 { + ip := net.ParseIP(p.dstHost) + header.Write(ip.To4()) + } else if p.atype == ATYP_IPV6 { + ip := net.ParseIP(p.dstHost) + header.Write(ip.To16()) + } else if p.atype == ATYP_DOMAIN { + hBytes := []byte(p.dstHost) + header.WriteByte(byte(len(hBytes))) + header.Write(hBytes) + } + port, _ := strconv.ParseUint(p.dstPort, 10, 64) + portBytes := new(bytes.Buffer) + binary.Write(portBytes, binary.BigEndian, port) + header.Write(portBytes.Bytes()[portBytes.Len()-2:]) + return header.Bytes() +} +func (p *PacketUDP) Bytes() []byte { + packBytes := new(bytes.Buffer) + packBytes.Write(p.Header()) + packBytes.Write(p.data) + return packBytes.Bytes() +} +func (p *PacketUDP) Host() string { + return p.dstHost +} + +func (p *PacketUDP) Port() string { + return p.dstPort +} +func (p *PacketUDP) Data() []byte { + return p.data +} diff --git a/utils/structs.go b/utils/structs.go index fe22c47..dd8daf7 100644 --- a/utils/structs.go +++ b/utils/structs.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/tls" "encoding/base64" - "encoding/binary" "errors" "fmt" "io" @@ -12,11 +11,13 @@ import ( "log" "net" "net/url" + "snail007/proxy/services/kcpcfg" "snail007/proxy/utils/sni" "strings" "sync" "time" + "github.com/golang/snappy" "github.com/miekg/dns" ) @@ -26,6 +27,7 @@ type Checker struct { directMap ConcurrentMap interval int64 timeout int + isStop bool } type CheckerItem struct { IsHTTPS bool @@ -46,6 +48,7 @@ func NewChecker(timeout int, interval int64, blockedFile, directFile string) Che data: NewConcurrentMap(), interval: interval, timeout: timeout, + isStop: false, } ch.blockedMap = ch.loadMap(blockedFile) ch.directMap = ch.loadMap(directFile) @@ -79,6 +82,9 @@ func (c *Checker) loadMap(f string) (dataMap ConcurrentMap) { } return } +func (c *Checker) Stop() { + c.isStop = true +} func (c *Checker) start() { go func() { //log.Printf("checker started") @@ -105,6 +111,9 @@ func (c *Checker) start() { }(v.(CheckerItem)) } time.Sleep(time.Second * time.Duration(c.interval)) + if c.isStop { + return + } } }() } @@ -278,7 +287,11 @@ func (ba *BasicAuth) checkFromURL(userpass, ip, target string) (err error) { } else { err = fmt.Errorf("token error") } - err = fmt.Errorf("auth fail from url %s,resonse code: %d, except: %d , %s , %s", URL, code, ba.authOkCode, ip, string(body)) + b := string(body) + if len(b) > 50 { + b = b[:50] + } + err = fmt.Errorf("auth fail from url %s,resonse code: %d, except: %d , %s , %s", URL, code, ba.authOkCode, ip, b) } if err != nil && tryCount < ba.authRetry { log.Print(err) @@ -315,7 +328,7 @@ func NewHTTPRequest(inConn *net.Conn, bufSize int, isBasicAuth bool, basicAuth * req = HTTPRequest{ conn: inConn, } - if len(header) == 1 { + if header != nil && len(header) == 1 && len(header[0]) > 1 { buf = header[0] n = len(header[0]) } else { @@ -400,17 +413,13 @@ func (req *HTTPRequest) IsHTTPS() bool { return req.Method == "CONNECT" } -func (req *HTTPRequest) BasicAuth() (err error) { - +func (req *HTTPRequest) GetAuthDataStr() (basicInfo string, err error) { // log.Printf("request :%s", string(req.HeadBuf)) - code := "407" authorization := req.getHeader("Proxy-Authorization") - // if authorization == "" { - // authorization = req.getHeader("Authorization") - // code = "401" - // } + + authorization = strings.Trim(authorization, " \r\n\t") if authorization == "" { - fmt.Fprintf((*req.conn), "HTTP/1.1 %s Unauthorized\r\nWWW-Authenticate: Basic realm=\"\"\r\n\r\nUnauthorized", code) + fmt.Fprintf((*req.conn), "HTTP/1.1 %s Unauthorized\r\nWWW-Authenticate: Basic realm=\"\"\r\n\r\nUnauthorized", "407") CloseConn(req.conn) err = errors.New("require auth header data") return @@ -428,6 +437,10 @@ func (req *HTTPRequest) BasicAuth() (err error) { CloseConn(req.conn) return } + basicInfo = string(user) + return +} +func (req *HTTPRequest) BasicAuth() (err error) { addr := strings.Split((*req.conn).RemoteAddr().String(), ":") URL := "" if req.IsHTTPS() { @@ -435,10 +448,14 @@ func (req *HTTPRequest) BasicAuth() (err error) { } else { URL = req.getHTTPURL() } + user, err := req.GetAuthDataStr() + if err != nil { + return + } authOk := (*req.basicAuth).Check(string(user), addr[0], URL) //log.Printf("auth %s,%v", string(user), authOk) if !authOk { - fmt.Fprintf((*req.conn), "HTTP/1.1 %s Unauthorized\r\n\r\nUnauthorized", code) + fmt.Fprintf((*req.conn), "HTTP/1.1 %s Unauthorized\r\n\r\nUnauthorized", "407") CloseConn(req.conn) err = fmt.Errorf("basic auth fail") return @@ -488,243 +505,44 @@ func (req *HTTPRequest) addPortIfNot() (newHost string) { return } -type OutPool struct { - Pool ConnPool - dur int - typ string - certBytes []byte - keyBytes []byte - kcpMethod string - kcpKey string - address string - timeout int +type OutConn struct { + dur int + typ string + certBytes []byte + keyBytes []byte + caCertBytes []byte + kcp kcpcfg.KCPConfigArgs + address string + timeout int } -func NewOutPool(dur int, typ, kcpMethod, kcpKey string, certBytes, keyBytes []byte, address string, timeout int, InitialCap int, MaxCap int) (op OutPool) { - op = OutPool{ - dur: dur, - typ: typ, - certBytes: certBytes, - keyBytes: keyBytes, - kcpMethod: kcpMethod, - kcpKey: kcpKey, - address: address, - timeout: timeout, +func NewOutConn(dur int, typ string, kcp kcpcfg.KCPConfigArgs, certBytes, keyBytes, caCertBytes []byte, address string, timeout int) (op OutConn) { + return OutConn{ + dur: dur, + typ: typ, + certBytes: certBytes, + keyBytes: keyBytes, + caCertBytes: caCertBytes, + kcp: kcp, + address: address, + timeout: timeout, } - var err error - op.Pool, err = NewConnPool(poolConfig{ - IsActive: func(conn interface{}) bool { return true }, - Release: func(conn interface{}) { - if conn != nil { - conn.(net.Conn).SetDeadline(time.Now().Add(time.Millisecond)) - conn.(net.Conn).Close() - // log.Println("conn released") - } - }, - InitialCap: InitialCap, - MaxCap: MaxCap, - Factory: func() (conn interface{}, err error) { - conn, err = op.getConn() - return - }, - }) - if err != nil { - log.Fatalf("init conn pool fail ,%s", err) - } else { - if InitialCap > 0 { - log.Printf("init conn pool success") - op.initPoolDeamon() - } else { - log.Printf("conn pool closed") - } - } - return } -func (op *OutPool) getConn() (conn interface{}, err error) { +func (op *OutConn) Get() (conn net.Conn, err error) { if op.typ == "tls" { var _conn tls.Conn - _conn, err = TlsConnectHost(op.address, op.timeout, op.certBytes, op.keyBytes) + _conn, err = TlsConnectHost(op.address, op.timeout, op.certBytes, op.keyBytes, op.caCertBytes) if err == nil { conn = net.Conn(&_conn) } } else if op.typ == "kcp" { - conn, err = ConnectKCPHost(op.address, op.kcpMethod, op.kcpKey) + conn, err = ConnectKCPHost(op.address, op.kcp) } else { conn, err = ConnectHost(op.address, op.timeout) } return } -func (op *OutPool) initPoolDeamon() { - go func() { - if op.dur <= 0 { - return - } - log.Printf("pool deamon started") - for { - time.Sleep(time.Second * time.Duration(op.dur)) - conn, err := op.getConn() - if err != nil { - log.Printf("pool deamon err %s , release pool", err) - op.Pool.ReleaseAll() - } else { - conn.(net.Conn).SetDeadline(time.Now().Add(time.Millisecond)) - conn.(net.Conn).Close() - } - } - }() -} - -type HeartbeatData struct { - Data []byte - N int - Error error -} -type HeartbeatReadWriter struct { - conn *net.Conn - // rchn chan HeartbeatData - l *sync.Mutex - dur int - errHandler func(err error, hb *HeartbeatReadWriter) - once *sync.Once - datachn chan byte - // rbuf bytes.Buffer - // signal chan bool - rerrchn chan error -} - -func NewHeartbeatReadWriter(conn *net.Conn, dur int, fn func(err error, hb *HeartbeatReadWriter)) (hrw HeartbeatReadWriter) { - hrw = HeartbeatReadWriter{ - conn: conn, - l: &sync.Mutex{}, - dur: dur, - // rchn: make(chan HeartbeatData, 10000), - // signal: make(chan bool, 1), - errHandler: fn, - datachn: make(chan byte, 4*1024), - once: &sync.Once{}, - rerrchn: make(chan error, 1), - // rbuf: bytes.Buffer{}, - } - hrw.heartbeat() - hrw.reader() - return -} - -func (rw *HeartbeatReadWriter) Close() { - CloseConn(rw.conn) -} -func (rw *HeartbeatReadWriter) reader() { - go func() { - //log.Printf("heartbeat read started") - for { - n, data, err := rw.read() - if n == -1 { - continue - } - //log.Printf("n:%d , data:%s ,err:%s", n, string(data), err) - if err == nil { - //fmt.Printf("write data %s\n", string(data)) - for _, b := range data { - rw.datachn <- b - } - } - if err != nil { - //log.Printf("heartbeat reader err: %s", err) - select { - case rw.rerrchn <- err: - default: - } - rw.once.Do(func() { - rw.errHandler(err, rw) - }) - break - } - } - //log.Printf("heartbeat read exited") - }() -} -func (rw *HeartbeatReadWriter) read() (n int, data []byte, err error) { - var typ uint8 - err = binary.Read((*rw.conn), binary.LittleEndian, &typ) - if err != nil { - return - } - if typ == 0 { - // log.Printf("heartbeat revecived") - n = -1 - return - } - var dataLength uint32 - binary.Read((*rw.conn), binary.LittleEndian, &dataLength) - _data := make([]byte, dataLength) - // log.Printf("dataLength:%d , data:%s", dataLength, string(data)) - n, err = (*rw.conn).Read(_data) - //log.Printf("n:%d , data:%s ,err:%s", n, string(data), err) - if err != nil { - return - } - if uint32(n) != dataLength { - err = fmt.Errorf("read short data body") - return - } - data = _data[:n] - return -} -func (rw *HeartbeatReadWriter) heartbeat() { - go func() { - //log.Printf("heartbeat started") - for { - if rw.conn == nil || *rw.conn == nil { - //log.Printf("heartbeat err: conn nil") - break - } - rw.l.Lock() - _, err := (*rw.conn).Write([]byte{0}) - rw.l.Unlock() - if err != nil { - //log.Printf("heartbeat err: %s", err) - rw.once.Do(func() { - rw.errHandler(err, rw) - }) - break - } else { - // log.Printf("heartbeat send ok") - } - time.Sleep(time.Second * time.Duration(rw.dur)) - } - //log.Printf("heartbeat exited") - }() -} -func (rw *HeartbeatReadWriter) Read(p []byte) (n int, err error) { - data := make([]byte, cap(p)) - for i := 0; i < cap(p); i++ { - data[i] = <-rw.datachn - n++ - //fmt.Printf("read %d %v\n", i, data[:n]) - if len(rw.datachn) == 0 { - n = i + 1 - copy(p, data[:n]) - return - } - } - return -} -func (rw *HeartbeatReadWriter) Write(p []byte) (n int, err error) { - defer rw.l.Unlock() - rw.l.Lock() - pkg := new(bytes.Buffer) - binary.Write(pkg, binary.LittleEndian, uint8(1)) - binary.Write(pkg, binary.LittleEndian, uint32(len(p))) - binary.Write(pkg, binary.LittleEndian, p) - bs := pkg.Bytes() - n, err = (*rw.conn).Write(bs) - if err == nil { - n = len(p) - } - return -} - type ConnManager struct { pool ConcurrentMap l *sync.Mutex @@ -924,3 +742,53 @@ func (a *DomainResolver) PrintData() { fmt.Printf("%s:ip[%s],domain[%s],expired at[%d]\n", k, (*d).ip, (*d).domain, (*d).expiredAt) } } +func NewCompStream(conn net.Conn) *CompStream { + c := new(CompStream) + c.conn = conn + c.w = snappy.NewBufferedWriter(conn) + c.r = snappy.NewReader(conn) + return c +} +func NewCompConn(conn net.Conn) net.Conn { + c := CompStream{} + c.conn = conn + c.w = snappy.NewBufferedWriter(conn) + c.r = snappy.NewReader(conn) + return &c +} + +type CompStream struct { + net.Conn + conn net.Conn + w *snappy.Writer + r *snappy.Reader +} + +func (c *CompStream) Read(p []byte) (n int, err error) { + return c.r.Read(p) +} + +func (c *CompStream) Write(p []byte) (n int, err error) { + n, err = c.w.Write(p) + err = c.w.Flush() + return n, err +} + +func (c *CompStream) Close() error { + return c.conn.Close() +} +func (c *CompStream) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} +func (c *CompStream) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} +func (c *CompStream) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} +func (c *CompStream) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} +func (c *CompStream) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} diff --git a/vendor/github.com/golang/snappy/.gitignore b/vendor/github.com/golang/snappy/.gitignore new file mode 100644 index 0000000..042091d --- /dev/null +++ b/vendor/github.com/golang/snappy/.gitignore @@ -0,0 +1,16 @@ +cmd/snappytool/snappytool +testdata/bench + +# These explicitly listed benchmark data files are for an obsolete version of +# snappy_test.go. +testdata/alice29.txt +testdata/asyoulik.txt +testdata/fireworks.jpeg +testdata/geo.protodata +testdata/html +testdata/html_x_4 +testdata/kppkn.gtb +testdata/lcet10.txt +testdata/paper-100k.pdf +testdata/plrabn12.txt +testdata/urls.10K diff --git a/vendor/github.com/miekg/dns/.gitignore b/vendor/github.com/miekg/dns/.gitignore new file mode 100644 index 0000000..776cd95 --- /dev/null +++ b/vendor/github.com/miekg/dns/.gitignore @@ -0,0 +1,4 @@ +*.6 +tags +test.out +a.out diff --git a/vendor/github.com/pkg/errors/.gitignore b/vendor/github.com/pkg/errors/.gitignore new file mode 100755 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/pkg/errors/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/templexxx/cpufeat/.gitignore b/vendor/github.com/templexxx/cpufeat/.gitignore new file mode 100644 index 0000000..a1338d6 --- /dev/null +++ b/vendor/github.com/templexxx/cpufeat/.gitignore @@ -0,0 +1,14 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ diff --git a/vendor/github.com/templexxx/reedsolomon/.gitignore b/vendor/github.com/templexxx/reedsolomon/.gitignore new file mode 100644 index 0000000..902cec0 --- /dev/null +++ b/vendor/github.com/templexxx/reedsolomon/.gitignore @@ -0,0 +1,40 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +/.idea +/backup +/loopunroll/ +cpu.out +mathtool/galois/ +mathtool/matrix/ +mem.out +/examples/ +/.DS_Store +/mathtool/cntinverse +/invert +/bakcup +/buf.svg +*.svg +*.out +/escape diff --git a/vendor/github.com/templexxx/xor/.gitignore b/vendor/github.com/templexxx/xor/.gitignore new file mode 100644 index 0000000..bccf12c --- /dev/null +++ b/vendor/github.com/templexxx/xor/.gitignore @@ -0,0 +1,18 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ +/backup/ +/backup2/ +/.idea +/backup3/ diff --git a/vendor/github.com/xtaci/kcp-go/.gitignore b/vendor/github.com/xtaci/kcp-go/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/xtaci/kcp-go/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/xtaci/smux/.gitignore b/vendor/github.com/xtaci/smux/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/xtaci/smux/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof