161 Commits
v5.4 ... imgbot

Author SHA1 Message Date
ImgBotApp
bee025e6f0 [ImgBot] Optimize images
*Total -- 815.67kb -> 652.94kb (19.95%)

/vendor/github.com/xtaci/kcp-go/donate.png -- 4.32kb -> 2.43kb (43.67%)
/docs/images/5.2.png -- 12.26kb -> 8.70kb (29.02%)
/docs/images/tcp-3.png -- 21.86kb -> 16.34kb (25.25%)
/docs/images/udp-3.png -- 22.86kb -> 17.12kb (25.09%)
/docs/images/fxdl.png -- 33.46kb -> 25.09kb (25.01%)
/vendor/github.com/xtaci/kcp-go/frame.png -- 35.16kb -> 26.43kb (24.82%)
/docs/images/http-tls-2.png -- 24.64kb -> 18.54kb (24.78%)
/docs/images/http-tls-3.png -- 29.66kb -> 22.31kb (24.76%)
/docs/images/http-ssh-1.png -- 29.06kb -> 21.89kb (24.68%)
/docs/images/tcp-tls-3.png -- 24.60kb -> 18.56kb (24.55%)
/docs/images/http-kcp.png -- 25.42kb -> 19.19kb (24.51%)
/docs/images/udp-tls-3.png -- 25.61kb -> 19.34kb (24.46%)
/docs/images/socks-ssh.png -- 24.47kb -> 18.56kb (24.12%)
/docs/images/http-2.png -- 21.60kb -> 16.50kb (23.61%)
/docs/images/http-1.png -- 18.26kb -> 14.06kb (23.02%)
/docs/images/socks-tls-3.png -- 24.67kb -> 19.02kb (22.88%)
/docs/images/tcp-tls-2.png -- 19.21kb -> 15.10kb (21.37%)
/docs/images/tcp-2.png -- 17.44kb -> 13.83kb (20.7%)
/docs/images/sps-tls.png -- 23.78kb -> 18.91kb (20.47%)
/docs/images/udp-tls-2.png -- 22.06kb -> 17.67kb (19.92%)
/docs/images/udp-2.png -- 20.14kb -> 16.16kb (19.79%)
/docs/images/socks-tls-2.png -- 19.06kb -> 15.39kb (19.24%)
/docs/images/socks-2.png -- 17.54kb -> 14.29kb (18.57%)
/docs/images/logo.jpg -- 118.72kb -> 96.96kb (18.32%)
/docs/images/udp-1.png -- 12.81kb -> 10.67kb (16.7%)
/docs/images/tcp-1.png -- 13.98kb -> 11.66kb (16.61%)
/docs/images/1.1.jpg -- 94.13kb -> 83.31kb (11.49%)
/docs/images/2.1.png -- 26.48kb -> 24.44kb (7.71%)
/docs/images/2.2.png -- 32.43kb -> 30.46kb (6.09%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2020-02-03 08:06:25 +00:00
arraykeys@gmail.com
198281b5eb Merge branch 'dev' 2019-01-24 16:35:04 +08:00
arraykeys@gmail.com
426d711d0b a 2019-01-24 16:34:49 +08:00
arraykeys@gmail.com
73d3b60a2f Merge branch 'dev' 2019-01-23 12:36:16 +08:00
arraykeys@gmail.com
b336b9ce03 a 2019-01-23 12:36:06 +08:00
arraykeys@gmail.com
9654efb952 Merge branch 'dev' 2019-01-22 16:04:50 +08:00
snail007
c3262fc0cb Merge pull request #211 from yincongcyincong/dev
Dev
2019-01-22 16:04:21 +08:00
yincongcyincong
2fef2c0eaa Update README.md 2019-01-22 15:52:26 +08:00
yincongcyincong
3d33cdc9f1 Update README.md 2019-01-22 15:47:20 +08:00
arraykeys@gmail.com
509ad47a71 a 2019-01-22 15:03:53 +08:00
arraykeys@gmail.com
c14a3b2773 a 2019-01-22 14:50:03 +08:00
arraykeys@gmail.com
e6e61b3cdb v6.9 2019-01-22 14:30:45 +08:00
arraykeys@gmail.com
303587f91b fix #210 2019-01-22 14:24:38 +08:00
arraykeys@gmail.com
5c3fd53fab 修复了socks5代理错误处理超时的问题. 2019-01-21 16:58:21 +08:00
arraykeys@gmail.com
0c675e6ff6 sps代理增加了对单个上级认证信息的支持,如果没有单独设置,就使用全局-A设置.
现在上级格式: YTpi#2.2.2.2:33080@1
  说明:
  YTpi 是经过base64编码的认证信息,比如是http(s)/socks原始认证信息a:b,用户是a密码是b,base64编码之后是:YTpi
     如果是ss,那么a就是加密方法,b是密码,比如:aes-192-cfb:your_pass,base64编码之后是:YWVzLTE5Mi1jZmI6eW91cl9wYXNz
  # 是间隔符号,如果有认证信息,必须有#,没有认证信息可以省略#.
  2.2.2.2:33080 是上级地址
  @1 是设置权重,可以参考手册权重部分.
2019-01-21 16:21:46 +08:00
arraykeys@gmail.com
942b026a05 sps代理增加了对单个上级认证信息的支持,如果没有单独设置,就使用全局-A设置.
现在上级格式: YTpi#2.2.2.2:33080@1
  说明:
  YTpi 是经过base64编码的认证信息,比如是http(s)/socks原始认证信息a:b,用户是a密码是b,base64编码之后是:YTpi
     如果是ss,那么a就是加密方法,b是密码,比如:aes-192-cfb:your_pass,base64编码之后是:YWVzLTE5Mi1jZmI6eW91cl9wYXNz
  # 是间隔符号,如果有认证信息,必须有#,没有认证信息可以省略#.
  2.2.2.2:33080 是上级地址
  @1 是设置权重,可以参考手册权重部分.
2019-01-21 13:33:51 +08:00
arraykeys@gmail.com
0b347b7f8d sps代理增加了对单个上级认证信息的支持,如果没有单独设置,就使用全局-A设置.
现在上级格式: YTpi#2.2.2.2:33080@1
  说明:
  YTpi 是经过base64编码的认证信息,比如是http(s)/socks原始认证信息a:b,用户是a密码是b,base64编码之后是:YTpi
     如果是ss,那么a就是加密方法,b是密码,比如:aes-192-cfb:your_pass,base64编码之后是:YWVzLTE5Mi1jZmI6eW91cl9wYXNz
  # 是间隔符号,如果有认证信息,必须有#,没有认证信息可以省略#.
  2.2.2.2:33080 是上级地址
  @1 是设置权重,可以参考手册权重部分.
2019-01-21 12:15:54 +08:00
arraykeys
87322c335e a 2019-01-19 18:54:42 +08:00
arraykeys@gmail.com
1bf4b38268 fix sps start crash 2019-01-07 12:43:44 +08:00
snail007
60b1742088 Merge pull request #205 from snail007/master
fix qq group no.
2018-12-30 17:08:03 +08:00
snail007
266fa6f6fa Update README.md 2018-12-30 17:06:28 +08:00
snail007
02c3c9b374 Update README_ZH.md 2018-12-30 17:05:46 +08:00
arraykeys@gmail.com
8f7da3ed94 sdk log->os.Stdout 2018-12-29 18:28:45 +08:00
arraykeys@gmail.com
a5a6e8645a A 2018-12-29 17:06:07 +08:00
arraykeys@gmail.com
83022d3efc v6.8 2018-12-29 13:29:45 +08:00
arraykeys@gmail.com
78143ce638 Merge branch 'master' into dev 2018-12-27 17:18:48 +08:00
snail007
cee38766c5 Update README.md 2018-12-27 17:17:26 +08:00
snail007
c66147c686 Merge pull request #201 from snail007/master
add more en markdown
2018-12-25 13:13:04 +08:00
snail007
08e149b180 Merge pull request #200 from denji/master
Dockerfile refresh
2018-12-25 13:08:31 +08:00
Denis Denisov
e3f8b652f4 README en-US markdown 2018-12-25 05:03:48 +02:00
Denis Denisov
6a4a45c96b Dockerfile refresh 2018-12-25 04:37:25 +02:00
arraykeys@gmail.com
e5bf95bdc8 a 2018-12-21 17:54:37 +08:00
arraykeys@gmail.com
ed1e7253f3 fix #194
fix #134
2018-12-19 17:28:24 +08:00
arraykeys@gmail.com
db43daea0b Merge branch 'master' into dev 2018-12-14 13:12:14 +08:00
snail007
0359ef40e1 Merge pull request #196 from pkptzx/master
修正Dockerfile无法构建
2018-12-14 13:11:47 +08:00
arraykeys@gmail.com
89b5744e27 Merge branch 'a' into dev 2018-12-14 13:08:52 +08:00
arraykeys@gmail.com
18f293a7aa a 2018-12-14 13:08:34 +08:00
码魂
acf70602ff 1.修正go-src下载地址错误.地址应该为:https://dl.google.com/go/goxxxxx.src.tar.gz
参考:https://blog.csdn.net/warrially/article/details/79879119

2.修正Dockerfile构建失败.
原因: 不知道作者为什么写的时候丢失了镜像名称,不知道为什么多了个build.更搞不清楚为什么这个Dockerfile写的如此复杂,我也懒得简化它了,只修正问题吧...
2018-12-13 19:48:37 +08:00
arraykeys@gmail.com
ebba94b9d1 add profiling support for sdk 2018-12-06 13:01:39 +08:00
arraykeys@gmail.com
23c379faf9 add profiling support for sdk 2018-12-06 10:12:04 +08:00
arraykeys@gmail.com
124852b3a2 add profiling support for sdk 2018-12-05 17:08:05 +08:00
arraykeys@gmail.com
e6d557f61e add profiling support for sdk 2018-12-05 16:27:58 +08:00
arraykeys@gmail.com
0a2ed2e498 add profiling support for sdk 2018-12-05 14:52:59 +08:00
arraykeys@gmail.com
4ce5fd463d add profiling support for sdk 2018-12-05 14:52:00 +08:00
arraykeys@gmail.com
367cfb36dd add profiling support for sdk 2018-12-05 14:09:32 +08:00
arraykeys@gmail.com
57555ffc1e a 2018-12-03 18:06:38 +08:00
arraykeys@gmail.com
8a86a53bd2 add local_ip for auth url 2018-12-03 15:40:52 +08:00
snail007
4a7b1af383 Merge pull request #189 from yincongcyincong/dev
Dev
2018-12-03 11:32:33 +08:00
arraykeys@gmail.com
78e631f551 fix #188 2018-12-03 11:30:55 +08:00
yincongcyincong
0af83540d3 Update README.md 2018-12-03 09:59:26 +08:00
arraykeys@gmail.com
9004913483 a 2018-11-30 13:12:35 +08:00
arraykeys@gmail.com
44909ea6c6 v6.6 2018-11-30 10:56:35 +08:00
arraykeys@gmail.com
a0cd66e319 增加智能模式参数 2018-11-30 10:47:31 +08:00
arraykeys@gmail.com
8c4e5025ed v6.6 2018-11-30 10:16:11 +08:00
arraykeys@gmail.com
442e7b7c01 a 2018-11-30 10:10:39 +08:00
arraykeys@gmail.com
05dfbe6f8a a 2018-11-29 11:23:24 +08:00
arraykeys@gmail.com
c64324227b sdk adding CloseIntelligent 2018-11-21 14:16:21 +08:00
snail007
1388f2e008 Merge pull request #179 from x22x22/x22x22
add close intelligent HTTP, SOCKS5 proxy
2018-11-20 19:51:42 +08:00
x22x22
2752d79248 add close intelligent HTTP, SOCKS5 proxy
为了方便用于代理上网行为管理, 有时候不需要智能判断代理是否可用而进行跳转, 所以增加一个关闭智能跳转代理的参数.
2018-11-20 12:45:24 +08:00
arraykeys@gmail.com
7aa24afcf2 Merge remote-tracking branch 'origin/dev' into dev 2018-11-19 18:39:34 +08:00
arraykeys@gmail.com
b3622e709c a 2018-11-19 18:39:04 +08:00
snail007
02a58189ad Merge pull request #173 from RickieL/RickieL-patch-1
typo fix
2018-11-08 13:57:43 +08:00
yongfu
f42184daaf Update README.md
typo fix
2018-11-08 09:41:29 +08:00
arraykeys@gmail.com
7754fcc13e v6.5 2018-11-01 13:40:02 +08:00
arraykeys@gmail.com
d421b79071 v6.6 2018-11-01 13:39:17 +08:00
arraykeys@gmail.com
80146fbe0d 修复合并企业版遗留的一些bug. 2018-11-01 12:59:56 +08:00
arraykeys@gmail.com
e95a1f8ad5 Merge branch 'master' into dev
# Conflicts:
#	CHANGELOG
2018-10-31 14:54:18 +08:00
arraykeys@gmail.com
9e496f246c a 2018-10-31 14:49:37 +08:00
arraykeys@gmail.com
f0389cdd5b fix #164 2018-10-26 14:25:45 +08:00
arraykeys@gmail.com
bcca92affc 优化了socks5的UDP功能可能存在的内存占用过多问题. 2018-10-23 14:08:29 +08:00
arraykeys@gmail.com
421e72188b a 2018-10-19 16:35:45 +08:00
arraykeys@gmail.com
cc8416e6bf a 2018-10-16 13:00:07 +08:00
arraykeys@gmail.com
fb09a100e1 Merge branch 'dev' 2018-10-16 11:27:38 +08:00
arraykeys@gmail.com
1b1091d75f a 2018-10-16 11:27:18 +08:00
arraykeys@gmail.com
ea30beb79b v6.3 2018-10-15 13:05:37 +08:00
arraykeys@gmail.com
99881e2c70 fix #156 2018-10-09 14:23:22 +08:00
arraykeys@gmail.com
325acb2942 fix #156 2018-10-09 14:21:00 +08:00
arraykeys@gmail.com
6babe1ea10 update vendor 2018-09-28 14:59:03 +08:00
arraykeys@gmail.com
6c586e2b78 Merge branch 'dev' 2018-09-26 12:08:30 +08:00
arraykeys@gmail.com
6917ff3347 a 2018-09-21 18:01:58 +08:00
arraykeys@gmail.com
284bb83d64 1.修复encrypt.Conn释放内存,导致的潜在panic问题.
2.修复了basic认证,处理认证文件没有正确处理注释的bug.
3.修正了ssh中转手册参数-A调整为-D.
2018-09-21 13:57:46 +08:00
arraykeys@gmail.com
1b29112a2c 修复encrypt.Conn释放内存,导致的潜在panic问题. 2018-09-21 12:02:54 +08:00
arraykeys@gmail.com
b9afc98230 修复encrypt.Conn释放内存,导致的潜在panic问题. 2018-09-21 12:01:46 +08:00
arraykeys@gmail.com
20761d2183 修复encrypt.Conn释放内存,导致的潜在panic问题. 2018-09-21 12:01:13 +08:00
arraykeys@gmail.com
1ab07c81ab a 2018-09-19 15:45:15 +08:00
arraykeys@gmail.com
07efc2c8de Merge branch 'dev' 2018-09-19 12:15:15 +08:00
arraykeys@gmail.com
ef8737063b v6.1 2018-09-19 12:14:16 +08:00
arraykeys@gmail.com
b5e0fa4895 v6.1 2018-09-19 11:51:01 +08:00
arraykeys@gmail.com
02513a9449 fix #146
server channel统一使用core
2018-09-19 11:34:34 +08:00
arraykeys@gmail.com
0859452475 优化负载均衡,当只有一个节点的时候,不再启动健康检查. 2018-09-18 16:14:06 +08:00
arraykeys@gmail.com
42d18ca1d3 优化负载均衡,当只有一个节点的时候,不再启动健康检查. 2018-09-18 16:08:59 +08:00
arraykeys@gmail.com
08b3715bda a 2018-09-18 15:36:10 +08:00
arraykeys@gmail.com
253783573a a 2018-09-18 15:34:38 +08:00
arraykeys@gmail.com
56dc3fdc07 a 2018-09-18 11:34:25 +08:00
arraykeys@gmail.com
af20893551 a 2018-09-18 11:30:48 +08:00
arraykeys@gmail.com
85178223e0 5.优化了DST,防止意外crash.
6.修复了mapx的Keys()方法的bug导致内网穿透bridge不稳定的问题.
2018-09-14 18:20:45 +08:00
arraykeys@gmail.com
e116bf8897 update kcpcfg import path 2018-09-14 16:12:56 +08:00
arraykeys@gmail.com
0fffedebd7 优化域名检测 2018-09-14 11:56:11 +08:00
arraykeys@gmail.com
1e259b5c6f 优化crashed日志 2018-09-14 11:48:51 +08:00
arraykeys@gmail.com
7bb5bb86dc 优化crashed日志 2018-09-14 11:47:50 +08:00
snail007
18716cb3a2 Merge pull request #143 from Quasilyte/quasilyte/docStub
core/tproxy: replace doc-comment stubs
2018-09-14 11:43:36 +08:00
snail007
1aca777c6d Merge pull request #142 from Quasilyte/quasilyte/underef
simplify (*x).f to x.f where it does not change the semantics
2018-09-14 11:42:34 +08:00
snail007
3a5b47b84a Merge pull request #141 from Quasilyte/quasilyte/commentedOutCode
remove some commented-out (dead) code
2018-09-14 11:40:51 +08:00
snail007
8bda30c4cf Merge pull request #140 from Quasilyte/quasilyte/emptyFallthrough
services: simplify switch statements with case clause lists
2018-09-14 11:37:25 +08:00
snail007
03ef384c48 Update install_auto.sh 2018-09-14 11:37:06 +08:00
Iskander Sharipov
5ace1eef1d core/tproxy: replace doc-comment stubs
Found using https://go-critic.github.io/overview#docStub-ref
2018-09-13 21:31:05 +03:00
Iskander Sharipov
d270b4a468 simplify (*x).f to x.f where it does not change the semantics
Found using https://go-critic.github.io/overview#underef-ref
2018-09-13 21:16:50 +03:00
Iskander Sharipov
6154d4173a remove some commented-out (dead) code
There are much more places, removed only few of them
to make review simpler (we can remove more later).

Found using https://go-critic.github.io/overview#commentedOutCode-ref
2018-09-13 21:04:07 +03:00
Iskander Sharipov
70067d67b9 services: simplify switch statements with case clause lists
Case clause lists are simpler and more compact than
multiple cases with fallthrough.

Found using https://go-critic.github.io/overview#emptyFallthrough-ref
2018-09-13 20:46:16 +03:00
arraykeys@gmail.com
8befdbc89c 黑白名单支持设置顶级域了,比如:com,匹配所有的.com域名 2018-09-12 11:20:03 +08:00
snail007
900b975c6a Update install_auto.sh 2018-09-11 19:30:47 +08:00
snail007
2ddb03a6c7 Merge pull request #139 from snail007/master
6.1
2018-09-11 19:29:16 +08:00
arraykeys@gmail.com
4145a31e5b 黑白名单支持设置顶级域了,比如:com,匹配所有的.com域名 2018-09-11 18:37:09 +08:00
arraykeys@gmail.com
96717f0c33 减少buf大小,降低高并发情况下内存占用量. 2018-09-10 14:42:04 +08:00
arraykeys@gmail.com
09348211b6 优化TCPS内存释放 2018-09-10 13:56:59 +08:00
arraykeys@gmail.com
179f9d63ab Merge remote-tracking branch 'origin/master' 2018-09-10 13:40:42 +08:00
arraykeys@gmail.com
7692446fd8 优化TCPS内存释放 2018-09-10 13:40:15 +08:00
yincongcyincong
69f20e1d7a Update README.md 2018-09-07 09:19:26 +08:00
snail007
0bebee1537 Merge pull request #133 from snail007/dev
update readme
2018-09-07 09:09:35 +08:00
snail007
61569c3a92 Merge pull request #132 from yincongcyincong/dev
Dev
2018-09-07 09:07:16 +08:00
yincongcyincong
0d6e10ad33 Update README.md 2018-09-07 08:53:17 +08:00
yincongcyincong
77a6300dae Update README.md 2018-09-06 18:48:07 +08:00
yincongcyincong
8f268b8b56 Update README.md 2018-09-06 18:04:10 +08:00
arraykeys@gmail.com
efb5710727 v6.0 2018-09-06 16:16:10 +08:00
arraykeys@gmail.com
7fc0b21764 Merge branch 'dev' 2018-09-06 16:10:00 +08:00
snail007
2f524010a1 Merge pull request #131 from smlrepo/master
fix build breakage
2018-09-06 16:09:18 +08:00
Lonnie Liu
c537240d3d fix build breakage 2018-09-06 00:31:02 -07:00
yincongcyincong
d3640f7519 Update README.md 2018-09-06 14:06:23 +08:00
arraykeys@gmail.com
6339e1ff53 Merge branch 'dev' 2018-09-06 12:41:47 +08:00
arraykeys@gmail.com
55098e2d22 v6.0 2018-09-06 12:14:03 +08:00
arraykeys@gmail.com
5a81229a9c 优化udp代理 2018-09-05 18:11:28 +08:00
arraykeys@gmail.com
7d59d9ce4d 优化udp代理 2018-09-05 18:02:31 +08:00
yincongcyincong
575326bed1 Update README.md 2018-09-05 17:32:53 +08:00
arraykeys@gmail.com
20ed5b1c7b 优化错误捕获,防止意外crash
优化停止服务,释放内存
2018-09-05 15:10:33 +08:00
arraykeys@gmail.com
997f438dfb 优化错误捕获,防止意外crash
优化停止服务,释放内存
2018-09-05 15:00:31 +08:00
arraykeys@gmail.com
e2f7377259 Merge branch 'devtomerge' into dev 2018-09-05 11:45:45 +08:00
arraykeys@gmail.com
6fb3457bd2 optimise tunnel nat udp module 2018-09-05 11:43:53 +08:00
snail007
462ae3deff Update CHANGELOG 2018-09-04 22:21:38 +08:00
arraykeys@gmail.com
44fd8b383b add core 2018-09-04 18:00:27 +08:00
arraykeys@gmail.com
ade587db6d add core 2018-09-04 17:56:38 +08:00
arraykeys@gmail.com
9a1f5de686 add core 2018-09-04 17:46:43 +08:00
arraykeys@gmail.com
98479e860d Merge branch 'enterprise' into devtomerge 2018-09-04 17:40:43 +08:00
arraykeys@gmail.com
6f71eef98f Merge branch 'enterprise' into devtomerge 2018-09-04 17:36:03 +08:00
arraykeys@gmail.com
b76458722b Merge branch 'enterprise' into devtomerge 2018-09-04 17:06:14 +08:00
arraykeys@gmail.com
003538cdaf Merge branch 'enterprise' into devtomerge 2018-09-04 16:28:03 +08:00
arraykeys@gmail.com
c7c52a0788 Merge branch 'enterprise' into devtomerge 2018-09-04 16:13:44 +08:00
arraykeys@gmail.com
167442e288 Merge branch 'enterprise' into devtomerge 2018-09-04 16:08:12 +08:00
arraykeys@gmail.com
397c5e08f8 merge enterprise 2018-09-04 16:06:44 +08:00
arraykeys@gmail.com
a993b1bb9d merge enterprise 2018-09-04 16:00:08 +08:00
arraykeys@gmail.com
89be79b6c6 merge enterprise 2018-09-04 15:59:57 +08:00
arraykeys@gmail.com
4d1c9ffa1f a 2018-09-04 15:09:08 +08:00
snail007
1d7eee0e69 Merge pull request #130 from okcy1016/master
Update README_ZH.md
2018-08-31 23:01:20 +08:00
ChaosYoung
bb0ddf3cec Update README_ZH.md 2018-08-31 21:38:14 +08:00
arraykeys@gmail.com
b3876d21be update README.md for v5.4 2018-08-30 17:23:05 +08:00
snail007
a26d7ea2c2 Merge pull request #129 from yincongcyincong/dev
Dev
2018-08-30 17:20:36 +08:00
yincongcyincong
249ce1a6d3 Merge branch 'dev' into dev 2018-08-30 17:17:21 +08:00
yincongcyincong
3280e82da3 Update README.md 2018-08-30 17:14:51 +08:00
yincongcyincong
2aab40624f Update README.md 2018-08-30 17:12:15 +08:00
arraykeys@gmail.com
187a3f58dd 优化多连接版本内网穿透连接释放逻辑 2018-08-30 17:00:21 +08:00
arraykeys@gmail.com
0de6c174a2 update sdk to v5.4 2018-08-30 16:35:31 +08:00
arraykeys@gmail.com
f9a2c3cd75 update sdk to v5.4 2018-08-30 16:35:06 +08:00
384 changed files with 196027 additions and 2402 deletions

13
AUTHORIZATION.md Normal file
View File

@ -0,0 +1,13 @@
# GoProxy special authorization
1. gproxy uses GPLv3 source code open agreement, without permission, based on the project development software, derivative software, related software, must strictly abide by GPLv3, otherwise once found,
Will be harshly punished.
2. If the company or individual uses the project code to develop related software, derivative software, and does not want to comply with the GPLv3 agreement, need to obtain the author's "GoProxy special authorization" written authorization.
3. If "GoPro special authorization"is not available on this page,the" GoPro special authorization"is invalid.
4. A valid authorization number and expiration date are listed below.
Authorization number | Authorization validity period
:--- | :---

13
AUTHORIZATION_ZH.md Normal file
View File

@ -0,0 +1,13 @@
# GoProxy特殊授权
1.goproxy采用GPLv3源代码开放协议,未经许可,基于本项目开发的软件,衍生软件,相关软件,必须严格遵守GPLv3,否则一经发现,
将严厉追责.
2.如果公司或个人使用本项目代码开发相关软件,衍生软件,又不想遵守GPLv3协议,需要取得作者的"GoProxy特殊授权"书面授权.
3.如果本页面查询不到"GoProxy特殊授权"书面授权信息,则"GoProxy特殊授权"书面授权无效.
4.下面列出了有效的授权编号和有效期.
授权编号 | 授权有效期
:--- | :---

View File

@ -1,4 +1,88 @@
proxy更新日志 proxy更新日志
v6.9
1.修复了sps的start潜在的crash问题.
2.sps代理增加了--parent-tls-single参数用来支持单向tls上级。
3.sps代理增加了对单个上级认证信息的支持,如果没有单独设置,就使用全局-A设置.
现在上级格式: -P YTpi#2.2.2.2:33080@1
说明:
YTpi 是经过base64编码的认证信息,比如是http(s)/socks原始认证信息a:b,用户是a密码是b,base64编码之后是:YTpi
如果是ss,那么a就是加密方法,b是密码,比如:aes-192-cfb:your_pass,base64编码之后是:YWVzLTE5Mi1jZmI6eW91cl9wYXNz
# 是间隔符号,如果有认证信息,必须有#,没有认证信息可以省略#.
2.2.2.2:33080 是上级地址
@1 是设置权重,可以参考手册权重部分.
4.修复了socks5代理错误处理超时的问题.
5.修复了http(s)代理错误处理-Z的问题.
v6.8
1.HTTP(S)\SOCKS5代理,API认证功能,发送给认证接口的参数增加了本地IP,local_ip字段,
代表用户访问的是本地服务器的哪个IP.
2.fix #194 , fix #134 , 代理更稳定.
3.增加了一波英文文档.
v6.6
1.优化了limitconn的关闭逻辑,释放更多资源.
2.http(s)\socks代理增加了--intelligent,智能模式设置,可以是intelligent|direct|parent三者之一,
默认是:intelligent.每个值的含义如下.
--intelligent=direct,不在blocked里面的目标都直连.
--intelligent=parent,不在direct里面的目标都走上级.
--intelligent=intelligent,blocked和direct里面都没有的目标,智能判断是否使用上级访问目标.
v6.5
1.修复了合并企业版遗留的一些bug.
v6.4
1.http(s)代理增加了--jumper参数,可以穿透外部代理连接上级.
2.优化了socks5代理UDP功能可能存在的内存占用过多问题.
3.优化了jumper,避免某些情况下不能正确返回错误的问题.
4.sps代理增加了--jumper参数,可以穿透外部代理连接上级.
5.修复了--debug不能正常工作的问题.
v6.3
1.fixed #156
2.修复DNS代理,没有定时保存缓存结果到文件.重启会降低查询速度.
v6.2
1.修复encrypt.Conn释放内存,导致的潜在panic问题.
2.修复了basic认证,处理认证文件没有正确处理注释的bug.
3.修正了ssh中转手册参数-A调整为-D.
v6.1
1.黑白名单支持设置顶级域了,比如:com,匹配所有的.com域名
2.优化TCPS内存释放.
3.优化了域名检查.
4.内网穿透增加了TCPS和TOU协议,
TCPS提供了多种自定义加密TCP方式传输.
TOU提供了TCP over UDP,多种自定义加密UDP方式传输TCP数据.
5.优化了DST,防止意外crash.
6.修复了mapx的Keys()方法的bug导致内网穿透bridge不稳定的问题.
7.修复了部分服务不能绑定IPv6地址的bug.
v6.0 企业版开源啦
本次更新主要是把企业版开源,把企业版代码合并到现在的开源goproxy当中,继续遵循GPLv3,免费开源,
之所以直接跳过5.x,用6.0版本号是为了与现有开源版本做一个明显的区分,下面功能主要来自企业版.
企业版代码结构更合理,核心与开源版本有很大区别,与此同时企业版有一个core开发库,基于此库可以
几行代码实现自己高度定制化的各种网络安全传输服务器和客户端和代理服务器与客户端.与此同时企
业版独创了TCPS协议,处于应用层和TCP层之间,可以为应用提供透明化的安全传输功能,另外还对dst协
议进行了一些改造,集成到goproxy中,实现了tcp over udp功能,那么除了kcp之外现在还可以选择dst
作为底层的tcp over udp的传输.下一步加入插件机制,定制功能可以使用插件方式开发了,热插拔,
不需要修改goproxy二进制可以插件so或者dylib注入.
1.预编译的二进制增加了armv8支持.
2.预编译的mipsle和mips二进制增加了softfloat支持.
3.优化连接HTTP(s)上级代理的CONNECT指令,附带更多的信息.
4.重构了内网穿透的UDP功能,性能大幅度提升,可以愉快的与异地基友玩依赖UDP的局域网游戏了.
5.重构了UDP端口映射,性能大幅度提升.
6.HTTP(S)\SOCKS5\SPS代理支持上级负载均衡,可以同时指定多个上级.
7.SPS支持HTTP(S)\SOCKS5\SS协议相互转换.
8.HTTP(S)\SOCKS5\SPS代理支持限速.
9.HTTP(S)\SOCKS5代理支持指定出口IP.
10.SOCKS5代理支持级联认证.
11.修复了tclient可能意外退出的bug.
12.优化了错误捕获,防止意外crash.
13.优化了停止服务,释放内存.
v5.4 v5.4
1.优化了获取本地IP信息导致CPU过高的问题. 1.优化了获取本地IP信息导致CPU过高的问题.
2.所有服务都增加了--nolog参数,可以关闭日志输出,节省CPU. 2.所有服务都增加了--nolog参数,可以关闭日志输出,节省CPU.

View File

@ -1,82 +0,0 @@
FROM alpine:3.7 AS builder
RUN apk add --no-cache \
ca-certificates
# set up nsswitch.conf for Go's "netgo" implementation
# - https://github.com/golang/go/blob/go1.9.1/src/net/conf.go#L194-L275
# - docker run --rm debian:stretch grep '^hosts:' /etc/nsswitch.conf
RUN [ ! -e /etc/nsswitch.conf ] && echo 'hosts: files dns' > /etc/nsswitch.conf
ENV GOLANG_VERSION 1.10.3
# make-sure-R0-is-zero-before-main-on-ppc64le.patch: https://github.com/golang/go/commit/9aea0e89b6df032c29d0add8d69ba2c95f1106d9 (Go 1.9)
#COPY *.patch /go-alpine-patches/
RUN set -eux; \
apk add --no-cache --virtual .build-deps \
bash \
gcc \
musl-dev \
openssl \
go \
; \
export \
# set GOROOT_BOOTSTRAP such that we can actually build Go
GOROOT_BOOTSTRAP="$(go env GOROOT)" \
# ... and set "cross-building" related vars to the installed system's values so that we create a build targeting the proper arch
# (for example, if our build host is GOARCH=amd64, but our build env/image is GOARCH=386, our build needs GOARCH=386)
GOOS="$(go env GOOS)" \
GOARCH="$(go env GOARCH)" \
GOHOSTOS="$(go env GOHOSTOS)" \
GOHOSTARCH="$(go env GOHOSTARCH)" \
; \
# also explicitly set GO386 and GOARM if appropriate
# https://github.com/docker-library/golang/issues/184
apkArch="$(apk --print-arch)"; \
case "$apkArch" in \
armhf) export GOARM='6' ;; \
x86) export GO386='387' ;; \
esac; \
\
wget -O go.tgz "https://golang.org/dl/go$GOLANG_VERSION.src.tar.gz"; \
echo '567b1cc66c9704d1c019c50bef946272e911ec6baf244310f87f4e678be155f2 *go.tgz' | sha256sum -c -; \
tar -C /usr/local -xzf go.tgz; \
rm go.tgz; \
\
cd /usr/local/go/src; \
for p in /go-alpine-patches/*.patch; do \
[ -f "$p" ] || continue; \
patch -p2 -i "$p"; \
done; \
./make.bash; \
\
rm -rf /go-alpine-patches; \
apk del .build-deps; \
\
export PATH="/usr/local/go/bin:$PATH"; \
go version
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
WORKDIR $GOPATH
ARG GOPROXY_VERSION=master
RUN apk update; apk upgrade; \
apk add --no-cache git; \
cd /go/src/; \
mkdir github.com; \
mkdir github.com/snail007; \
cd github.com/snail007; \
git clone https://github.com/snail007/goproxy.git; \
cd goproxy; \
git checkout ${GOPROXY_VERSION}; \
CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -a -installsuffix cgo -o proxy; \
chmod 0777 proxy
FROM 1.10.3-stretch
RUN mkdir /proxy && chmod 0777 /proxy
COPY --from=builder builder /go/src/github.com/snail007/goproxy/proxy /proxy/
CMD cd /proxy && /proxy ${OPTS}

20
Gopkg.lock generated
View File

@ -1,6 +1,12 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/Yawning/chacha20"
packages = ["."]
revision = "e3b1f968fc6397b51d963fee8ec8711a47bc0ce8"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "github.com/alecthomas/template" name = "github.com/alecthomas/template"
@ -28,6 +34,12 @@
packages = ["."] packages = ["."]
revision = "3520598351bb3500a49ae9563f5539666ae0a27c" revision = "3520598351bb3500a49ae9563f5539666ae0a27c"
[[projects]]
name = "github.com/juju/ratelimit"
packages = ["."]
revision = "59fac5042749a5afb9af70e813da1dd5474f0167"
version = "1.0.1"
[[projects]] [[projects]]
name = "github.com/klauspost/cpuid" name = "github.com/klauspost/cpuid"
packages = ["."] packages = ["."]
@ -119,6 +131,12 @@
] ]
revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196" revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "dad3d9fb7b6e83d0f9ac8f54670f6334c3a287b4"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "golang.org/x/time" name = "golang.org/x/time"
@ -134,6 +152,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "eab0ef29432489696d8bfd524757dcb03a83f91c329f2d05c36da70df850360d" inputs-digest = "15e4e23c0695db1458b3dc5514c8765be091a420c923b24bb9e186f43400995f"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

294
README.md
View File

@ -1,6 +1,7 @@
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/logo.jpg?raw=true" width="200"/> <img src="https://github.com/snail007/goproxy/blob/master/docs/images/logo.jpg?raw=true" width="200"/>
Proxy is a high performance HTTP, HTTPS, HTTPS, websocket, TCP, UDP, Socks5 proxy server implemented by golang. It supports parent proxy,nat forward,TCP/UDP port forwarding, SSH transfer, TLS encrypted transmission, protocol conversion. you can expose a local server behind a NAT or firewall to the internet, secure DNS proxy.   Proxy is a high performance HTTP, HTTPS, HTTPS, websocket, TCP, UDP, Socks5, ss proxy server implemented by golang. It supports parent proxy,nat forward,TCP/UDP port forwarding, SSH transfer, TLS encrypted transmission, protocol conversion. you can expose a local server behind a NAT or firewall to the internet, secure DNS proxy.  
[Download](https://github.com/snail007/goproxy/releases)
--- ---
@ -8,9 +9,11 @@ Proxy is a high performance HTTP, HTTPS, HTTPS, websocket, TCP, UDP, Socks5 prox
**[中文手册](/README_ZH.md)** **[中文手册](/README_ZH.md)**
**[全平台图形界面版本](/gui/README.md)** **[Full-platform graphical interface version](/gui/README.md)**
**[全平台SDK](/sdk/README.md)** **[Full platform SDK](https://github.com/snail007/goproxy-sdk/blob/master/README.md)**
**[GoProxy special authorization](/AUTHORIZATION.md)**
### How to contribute to the code (Pull Request)? ### How to contribute to the code (Pull Request)?
@ -33,10 +36,15 @@ PR needs to explain what changes have been made and why you change them.
- The integrated external API, HTTP (S): SOCKS5 proxy authentication can be integrated with the external HTTP API, which can easily control the user's access through the external system. - The integrated external API, HTTP (S): SOCKS5 proxy authentication can be integrated with the external HTTP API, which can easily control the user's access through the external system.
- Reverse proxy: goproxy supports directly parsing the domain to proxy monitor IP, and then proxy will help you to access the HTTP (S) site that you need to access. - Reverse proxy: goproxy supports directly parsing the domain to proxy monitor IP, and then proxy will help you to access the HTTP (S) site that you need to access.
- Transparent proxy: with the iptables, goproxy can directly forward the 80 and 443 port's traffic to proxy in the gateway, and can realize the unaware intelligent router proxy. - Transparent proxy: with the iptables, goproxy can directly forward the 80 and 443 port's traffic to proxy in the gateway, and can realize the unaware intelligent router proxy.
- Protocol conversion: The existing HTTP (S) or SOCKS5 proxy can be converted to a proxy which support both HTTP (S) and SOCKS5 by one port, but the converted SOCKS5 proxy does not support the UDP function.Also support powerful cascading authentication.   - Protocol conversion: The existing HTTP (S) or SOCKS5 or ss proxy can be converted to a proxy which support HTTP (S), SOCKS5 and ss by one port, if the converted SOCKS5 and ss proxy's parent proxy is SOCKS5, which can support the UDP function.Also support powerful cascading authentication.  
- Custom underlying encrypted transmission, HTTP(s)\sps\socks proxy can encrypt TCP data through TLS standard encryption and KCP protocol encryption. In addition, it also supports custom encryption after TLS and KCP. That is to say, custom encryption and tls|kcp can be used together. The internal uses AES256 encryption, and it only needs to define one password by yourself when is used. - Custom underlying encrypted transmission, HTTP(s)\sps\socks proxy can encrypt TCP data through TLS standard encryption and KCP protocol encryption. In addition, it also supports custom encryption after TLS and KCP. That is to say, custom encryption and tls|kcp can be used together. The internal uses AES256 encryption, and it only needs to define one password by yourself when is used.
- Low level compression and efficient transmissionThe HTTP(s)\sps\socks proxy can encrypt TCP data through a custom encryption and TLS standard encryption and KCP protocol encryption, and can also compress the data after encryption. That is to say, the compression and custom encryption and tls|kcp can be used together. - Low level compression and efficient transmissionThe HTTP(s)\sps\socks proxy can encrypt TCP data through a custom encryption and TLS standard encryption and KCP protocol encryption, and can also compress the data after encryption. That is to say, the compression and custom encryption and tls|kcp can be used together.
- The secure DNS proxy, Through the DNS proxy provided by the local proxy, you can encrypted communicate with the father proxy to realize the DNS query of security and pollution prevention. - The secure DNS proxy, Through the DNS proxy provided by the local proxy, you can encrypted communicate with the father proxy to realize the DNS query of security and pollution prevention.
- Load balance,High availability,HTTP(S)\SOCKS5\SPS proxy support Superior load balance and high availability. Multiple superiors repeat -P parameters.
- Designated exporting IP,HTTP(S)\SOCKS5\SPS proxy supports the client to connect with the entry IP,Using the entry IP as the exporting IP to visit the target website。If the entry IP is the intranet IPExporting IP will not use entry IP
- Support speed limit. HTTP (S) \SOCKS5\SPS proxy supports speed limit.
- SOCKS5 proxy supports cascade authentication.
- Certificate parameters use base64 data. By default, the - C, - K parameters are the path of the CRT certificate and key file. If “base64://” begins, the subsequent data is thought to be Base64 encoded which will be decoded and used.
### Why need these? ### Why need these?
- Because for some reason, we cannot access our services elsewhere. We can build a secure tunnel to access our services through multiple connected proxy nodes.   - Because for some reason, we cannot access our services elsewhere. We can build a secure tunnel to access our services through multiple connected proxy nodes.  
@ -48,7 +56,8 @@ PR needs to explain what changes have been made and why you change them.
- ...   - ...  
This page is the v5.3 manual, and the other version of the manual can be checked by the following [link](docs/old-release.md). This page is the v6.0 manual, and the other version of the manual can be checked by the following [link](docs/old-release.md).
### How to find the organization? ### How to find the organization?
[Click to join the proxy group of gitter](https://gitter.im/go-proxy/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link)   [Click to join the proxy group of gitter](https://gitter.im/go-proxy/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link)  
@ -71,6 +80,7 @@ This page is the v5.3 manual, and the other version of the manual can be checked
- [Safety advice](#safety-advice) - [Safety advice](#safety-advice)
### Manual catalogues ### Manual catalogues
- [Load balance and high available](#load-balance-and-high-available)
- [1.HTTP proxy](#1http-proxy) - [1.HTTP proxy](#1http-proxy)
- [1.1 Common HTTP proxy](#11common-http-proxy) - [1.1 Common HTTP proxy](#11common-http-proxy)
- [1.2 Common HTTP second level proxy](#12common-http-second-level-proxy) - [1.2 Common HTTP second level proxy](#12common-http-second-level-proxy)
@ -87,14 +97,20 @@ This page is the v5.3 manual, and the other version of the manual can be checked
- [1.11 Custom DNS](#111custom-dns) - [1.11 Custom DNS](#111custom-dns)
- [1.12 Custom encryption](#112-custom-encryption) - [1.12 Custom encryption](#112-custom-encryption)
- [1.13 Compressed transmission](#113-compressed-transmission) - [1.13 Compressed transmission](#113-compressed-transmission)
- [1.14 View help](#114view-help) - [1.14 load balance](#114-load-balance)
- [1.15 speed limit](#115-speed-limit)
- [1.16 Designated exporting IP](#116-designated-export-ip)
- [1.17 Certificate parameters using Base64 data](#117-certificate-parameters-using-Base64-data)
- [1.18 Intelligent mode](#118-intelligent-mode)
- [1.19 View help](#119view-help)
- [2.TCP proxy](#2tcp-proxy) - [2.TCP proxy](#2tcp-proxy)
- [2.1 Common TCP first level proxy](#21common-tcp-first-level-proxy) - [2.1 Common TCP first level proxy](#21common-tcp-first-level-proxy)
- [2.2 Common TCP second level proxy](#22common-tcp-second-level-proxy) - [2.2 Common TCP second level proxy](#22common-tcp-second-level-proxy)
- [2.3 Common TCP third level proxy](#23common-tcp-third-level-proxy) - [2.3 Common TCP third level proxy](#23common-tcp-third-level-proxy)
- [2.4 TCP second level encrypted proxy](#24tcp-second-level-encrypted-proxy) - [2.4 TCP second level encrypted proxy](#24tcp-second-level-encrypted-proxy)
- [2.5 TCP third level encrypted proxy](#25tcp-third-level-encrypted-proxy) - [2.5 TCP third level encrypted proxy](#25tcp-third-level-encrypted-proxy)
- [2.6 View help](#26view-help) - [2.6 Connect parents proxy through other proxy](#26connect-parents-proxy-through-other-proxy)
- [2.7 View help](#27view-help)
- [3.UDP proxy](#3udp-proxy) - [3.UDP proxy](#3udp-proxy)
- [3.1 Common UDP first level proxy](#31common-udp-first-level-proxy) - [3.1 Common UDP first level proxy](#31common-udp-first-level-proxy)
- [3.2 Common UDP second level proxy](#32common-udp-second-level-proxy) - [3.2 Common UDP second level proxy](#32common-udp-second-level-proxy)
@ -110,7 +126,8 @@ This page is the v5.3 manual, and the other version of the manual can be checked
- [4.5 Advanced usage 1](#45advanced-usage-1) - [4.5 Advanced usage 1](#45advanced-usage-1)
- [4.6 Advanced usage 2](#46advanced-usage-2) - [4.6 Advanced usage 2](#46advanced-usage-2)
- [4.7 -r parameters of server](#47-r-parameters-of-server) - [4.7 -r parameters of server](#47-r-parameters-of-server)
- [4.8 View help](#48view-help) - [4.8 Server and client connect bridge through proxy](#48server-and-client-connect-bridge-through-proxy)
- [4.9 View help](#49view-help)
- [5.SOCKS5 proxy](#5socks5-proxy) - [5.SOCKS5 proxy](#5socks5-proxy)
- [5.1 Common SOCKS5 proxy](#51common-socks5-proxy) - [5.1 Common SOCKS5 proxy](#51common-socks5-proxy)
- [5.2 Common SOCKS5 second level proxy](#52common-socks5-second-level-proxy) - [5.2 Common SOCKS5 second level proxy](#52common-socks5-second-level-proxy)
@ -125,17 +142,28 @@ This page is the v5.3 manual, and the other version of the manual can be checked
- [5.9 Custom DNS](#59custom-dns) - [5.9 Custom DNS](#59custom-dns)
- [5.10 Custom encryption](#510custom-encryption) - [5.10 Custom encryption](#510custom-encryption)
- [5.11 Compressed transmission](#511compressed-transmission) - [5.11 Compressed transmission](#511compressed-transmission)
- [5.12 View help](#512view-help) - [5.12 load balance](#512-load-balance)
- [5.13 speed limit](#513-speed-limit)
- [5.14 Designated exporting IP](#514-designated-exporting-ip)
- [5.15 Cascade authentication](#515-cascade-authentication)
- [5.16 Certificate parameters using Base64 data](#516-certificate-parameters-using-base64-data)
- [5.17 Intelligent mode](#517-intelligent-mode)
- [5.18 View help](#518view-help)
- [6.Proxy protocol conversion](#6proxy-protocol-conversion) - [6.Proxy protocol conversion](#6proxy-protocol-conversion)
- [6.1 Functional introduction](#61functional-introduction) - [6.1 Functional introduction](#61functional-introduction)
- [6.2 HTTP(S) to HTTP(S) + SOCKS5](#62http-to-http-socks5) - [6.2 HTTP(S) to HTTP(S) + SOCKS5](#62http-to-http-socks5)
- [6.3 SOCKS5 to HTTP(S) + SOCKS5](#63socks5-to-http-socks5) - [6.3 SOCKS5 to HTTP(S) + SOCKS5](#63socks5-to-http-socks5)
- [6.4 Chain style connection](#64chain-style-connection) - [6.4 SS to HTTP(S)+SOCKS5+SS](#64-ss-to-httpssocks5ss)
- [6.5 Listening on multiple ports](#65listening-on-multiple-ports) - [6.5 Chain style connection](#65chain-style-connection)
- [6.6 Authentication](#66authentication) - [6.6 Listening on multiple ports](#66listening-on-multiple-ports)
- [6.7 Custom encryption](#67-custom-encryption) - [6.7 Authentication](#67authentication)
- [6.8 Compressed transmission](#68-compressed-transmission) - [6.8 Custom encryption](#68-custom-encryption)
- [6.9 View Help](#69view-help) - [6.9 Compressed transmission](#69-compressed-transmission)
- [6.10 Disable-protocol](#610-disable-protocol)
- [6.11 speed limit](#611-speed-limit)
- [6.12 Designated exporting IP](#612-designated-exporting-ip)
- [6.13 Certificate parameters using Base64 data](#613-certificate-parameters-using-base64-data)
- [6.14 View Help](#614view-help)
- [7.KCP Configuration](#7kcp-configuration) - [7.KCP Configuration](#7kcp-configuration)
- [7.1 Configuration introduction](#71configuration-introduction) - [7.1 Configuration introduction](#71configuration-introduction)
- [7.2 Configuration details](#72configuration-details) - [7.2 Configuration details](#72configuration-details)
@ -152,7 +180,7 @@ tips:all operations require root permissions.
```shell ```shell
curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.sh | bash curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.sh | bash
``` ```
The installation is completed, the configuration directory is /etc/proxy, more detailed use of the method referred to the following manual for further understanding. The installation is completed, the configuration directory is /etc/proxy, For more detailed usage, please refer to the manual above to further understand the functions you want to use.
If the installation fails or your VPS is not a linux64 system, please follow the semi-automatic step below: If the installation fails or your VPS is not a linux64 system, please follow the semi-automatic step below:
#### Manual installation #### Manual installation
@ -161,7 +189,8 @@ If the installation fails or your VPS is not a linux64 system, please follow the
Download address: https://github.com/snail007/goproxy/releases Download address: https://github.com/snail007/goproxy/releases
```shell ```shell
cd /root/proxy/ cd /root/proxy/
wget https://github.com/snail007/goproxy/releases/download/v5.3/proxy-linux-amd64.tar.gz wget https://github.com/snail007/goproxy/releases/download/v6.0/proxy-linux-amd64.tar.gz
``` ```
#### **2.Download the automatic installation script** #### **2.Download the automatic installation script**
```shell ```shell
@ -173,12 +202,14 @@ chmod +x install.sh
#### Docker installation #### Docker installation
[docker](https://hub.docker.com/r/snail007/goproxy)
Dockerfile root of project uses multistage build and alpine project to comply with best practices. Uses golang 1.10.3 for building as noted in the project README.md and will be pretty small image. total extracted size will be 17.3MB for goproxy latest version. Dockerfile root of project uses multistage build and alpine project to comply with best practices. Uses golang 1.10.3 for building as noted in the project README.md and will be pretty small image. total extracted size will be 17.3MB for goproxy latest version.
The default build process builds the master branch (latest commits/ cutting edge), and it can be configured to build specific version, just edit Dockerfile before build, following builds release version 5.3: The default build process builds the master branch (latest commits/ cutting edge), and it can be configured to build specific version, just edit Dockerfile before build, following builds release version 6.0:
``` ```
ARG GOPROXY_VERSION=v5.3 ARG GOPROXY_VERSION=v6.0
``` ```
To Run: To Run:
@ -257,6 +288,20 @@ When vps is behind the NAT, the network card IP on VPS is an internal network IP
Assuming that your VPS outer external network IP is 23.23.23.23, the following command sets the 23.23.23.23 through the -g parameter. Assuming that your VPS outer external network IP is 23.23.23.23, the following command sets the 23.23.23.23 through the -g parameter.
`./proxy http -g "23.23.23.23"` `./proxy http -g "23.23.23.23"`
### **Load balance and high available**
HTTP(S)\SOCKS5\SPS proxy support Superior load balance and high availability. Multiple superiors repeat -P parameters.
Load balancing have 5 kinds of policy, It can be specified by the `--lb-method` parameter.:
roundrobin take turns
leastconn Using minimum connection number
leasttime Use minimum connection time
hash Use the client address to calculate a fixed superior
weight According to the weight and connection number of each superior, choose a superior
Tips:
The load balance check interval can be set by `--lb-retrytime`, unit milliseconds.
Load balancing connection timeout can be set by `--lb-timeout`, unit milliseconds.
If the load balance policy is weighted (weight), the -P format is: 2.2.2.2:3880@1,1 is the weight which is greater than 0.
If the load balance strategy is hash, the default is to select the parent based on the client address, and the parent can be selected by switching `- lb-hashtarget', using the access destination address.
### **1.HTTP proxy** ### **1.HTTP proxy**
#### **1.1.common HTTP proxy** #### **1.1.common HTTP proxy**
![1.1](/docs/images/http-1.png) ![1.1](/docs/images/http-1.png)
@ -283,7 +328,7 @@ accessing the local 8080 port is accessing the proxy port 38080 above VPS.  
HTTP second level proxy(local windows) HTTP second level proxy(local windows)
`./proxy.exe http -t tcp -p ":8080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key` `./proxy.exe http -t tcp -p ":8080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
In your windos system, the mode of the program that needs to surf the Internet by proxy is setted up as HTTP mode, the address is 127.0.0.1, the port is: 8080, the program can go through the encrypted channel through VPS to surf on the internet. In your windows system, the mode of the program that needs to surf the Internet by proxy is setted up as HTTP mode, the address is 127.0.0.1, the port is: 8080, the program can go through the encrypted channel through VPS to surf on the internet.
#### **1.4.HTTP third level encrypted proxy** #### **1.4.HTTP third level encrypted proxy**
![1.4](/docs/images/http-tls-3.png) ![1.4](/docs/images/http-tls-3.png)
@ -455,7 +500,40 @@ Local third level execution:
`proxy http -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080` `proxy http -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080`
through this way, When you visits the website by local proxy 8080, it visits the target website by compressed transmission with the parents proxy. through this way, When you visits the website by local proxy 8080, it visits the target website by compressed transmission with the parents proxy.
#### **1.14.view help** ### **1.14 Load balance**
HTTP (S) proxy supports superior load balance, and multiple -P parameters can be repeated by multiple superiors.
`proxy http --lb-method=hash -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080`
#### **1.14.1 Set retry interval and timeout time**
`proxy http --lb-method=leastconn --lb-retrytime 300 --lb-timeout 300 -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080 -t tcp -p :33080`
#### **1.14.2 Set weight**
`proxy http --lb-method=weight -T tcp -P 1.1.1.1:33080@1 -P 2.1.1.1:33080@2 -P 3.1.1.1:33080@1 -t tcp -p :33080`
#### **1.14.3 Use target address to select superior**
`proxy http --lb-hashtarget --lb-method=leasttime -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080 -t tcp -p :33080`
### **1.15 Speed limit**
The speed limit is 100K, which can be specified through the `-l` parameter, for example: 100K 1.5M. 0 means unlimited.
`proxy http -t tcp -p 2.2.2.2:33080 -l 100K`
### **1.16 Designated exporting IP**
The `--bind-listen` parameter open the client's ability to access the target site with an entry IP connection, using the entry IP as the exporting IP. If the entry IP is the intranet IP, the exporting IP will not use the entry IP..
`proxy http -t tcp -p 2.2.2.2:33080 --bind-listen`
### **1.17 Certificate parameters using Base64 data**
By default, the -C and -K parameters are the paths of CRT certificates and key files,
If it is the beginning of base64://, then it is considered that the data behind is Base64 encoded and will be used after decoding.
#### **1.18 Intelligent mode**
Intelligent mode setting which can be one of intelligent|direct|parent.
default:intelligent.
The meaning of each value is as follows:
`--intelligent=direct`, Targets that are not in blocked directly connected.
`--intelligent=parent`, Targets that are not in direct connect to parent proxy.
`--intelligent=intelligent`, Targets that are not in direct and blocked Neither can intelligently judge on whether to connetc parent proxy.
#### **1.19.view help**
`./proxy help http` `./proxy help http`
### **2.TCP proxy** ### **2.TCP proxy**
@ -502,7 +580,26 @@ TCP third level proxy (local)
`./proxy tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key` `./proxy tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
Then access to the local 8080 port is to access the 8080 port of the 66.66.66.66 by encrypting the TCP tunnel. Then access to the local 8080 port is to access the 8080 port of the 66.66.66.66 by encrypting the TCP tunnel.
#### **2.6.view help** #### **2.6.Connect parents proxy through other proxy**
Sometimes the proxy network can not directly access the external network,which need to use a HTTPS or Socks5 proxy to access the Internet. then The -J parameter can help you connect to the parent proxy through the HTTPS or Socks5 proxy when proxy's TCP port is mapped, which can map external port to local.
-J param format:
https proxy:
proxy need authentication,username: username password:password
https://username:password@host:port
proxy don't need authentication
https://host:port
socks5 proxy:
proxy need authentication,username: username password:password
socks5://username:password@host:port
proxy don't need authentication
socks5://host:port
host:proxy's domain or ip
port:proxy's port
#### **2.7.view help**
`./proxy help tcp` `./proxy help tcp`
### **3.UDP proxy** ### **3.UDP proxy**
@ -686,7 +783,26 @@ Procedure:
4.7.3.LOCAL_IP is empty which means LOCAL_IP is `0.0.0.0`, CLIENT_LOCAL_HOST is empty which means LOCAL_IP is `127.0.0.1`. 4.7.3.LOCAL_IP is empty which means LOCAL_IP is `0.0.0.0`, CLIENT_LOCAL_HOST is empty which means LOCAL_IP is `127.0.0.1`.
#### **4.8.view help** #### **4.8.server and client connect bridge through proxy**
Sometimes the server or client can not directly access the external network,which need to use a HTTPS or Socks5 proxy to access the Internet. then The -J parameter can help server and client connect to the bridge through the HTTPS or Socks5 proxy.
-J param format:
https proxy:
proxy need authentication,username: username password:password
https://username:password@host:port
proxy don't need authentication
https://host:port
socks5 proxy:
proxy need authentication,username: username password:password
socks5://username:password@host:port
proxy don't need authentication
socks5://host:port
host:proxy's domain or ip
port:proxy's port
#### **4.9.view help**
`./proxy help bridge` `./proxy help bridge`
`./proxy help server` `./proxy help server`
`./proxy help client` `./proxy help client`
@ -826,41 +942,88 @@ Local third level execution:
`proxy socks -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080` `proxy socks -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080`
through this way, When you visits the website by local proxy 8080, it visits the target website by compressed transmission with the parents proxy. through this way, When you visits the website by local proxy 8080, it visits the target website by compressed transmission with the parents proxy.
#### **5.12.view help** #### **5.12 Load balance**
SOCKS proxy supports the load balancing of superior authorities, and the -P parameters can be repeated by multiple superiors.
`proxy socks --lb-method=hash -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080 -p :33080 -t tcp`
#### **5.12.1 Set retry interval and timeout time**
`proxy socks --lb-method=leastconn --lb-retrytime 300 --lb-timeout 300 -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080 -p :33080 -t tcp`
#### **5.12.2 Set weight**
`proxy socks --lb-method=weight -T tcp -P 1.1.1.1:33080@1 -P 2.1.1.1:33080@2 -P 3.1.1.1:33080@1 -p :33080 -t tcp`
#### **5.12.3 Use target address to select parent proxy**
`proxy socks --lb-hashtarget --lb-method=leasttime -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080 -p :33080 -t tcp`
#### **5.13 Speed limit**
The speed limit is 100K, which can be specified through the -l parameter, for example: 100K 1.5M. 0 means unlimited.
`proxy socks -t tcp -p 2.2.2.2:33080 -l 100K`
#### **5.14 Designated exporting IP**
The `- bind-listen` parameter opens the client's ability to access the target site with an entry IP connection, using the entry IP as the exporting IP. If the entry IP is the intranet IP, the exporting IP will not use the entry IP..
`proxy socks -t tcp -p 2.2.2.2:33080 --bind-listen`
#### **5.15 Cascade authentication**
SOCKS5 supports cascading authentication, and -A can set up parents proxy's authentication information..
parents proxy:
`proxy socks -t tcp -p 2.2.2.2:33080 -a user:pass`
localhost:
`proxy socks -T tcp -P 2.2.2.2:33080 -A user:pass -t tcp -p :33080`
#### **5.16 Certificate parameters using Base64 data**
By default, the -C and -K parameters are the paths of CRT certificates and key files,
If it is the beginning of base64://, then it is considered that the data behind is Base64 encoded and will be used after decoding.
#### **5.17 Intelligent mode**
Intelligent mode setting which can be one of intelligent|direct|parent.
default:intelligent.
The meaning of each value is as follows:
`--intelligent=direct`, Targets that are not in blocked directly connected.
`--intelligent=parent`, Targets that are not in direct connect to parent proxy.
`--intelligent=intelligent`, Targets that are not in direct and blocked Neither can intelligently judge on whether to connetc parent proxy.
#### **5.18.view help**
`./proxy help socks` `./proxy help socks`
### **6.Proxy protocol conversion** ### **6.Proxy protocol conversion**
#### **6.1.Functional introduction** #### **6.1.Functional introduction**
The proxy protocol conversion use the SPS subcommand (abbreviation of socks+https), SPS itself does not provide the proxy function, just accept the proxy request and then converse protocol and forwarded to the existing HTTP (s) or Socks5 proxy. SPS can use existing HTTP (s) or Socks5 proxy converse to support HTTP (s) and Socks5 HTTP (s) proxy at the same time by one port, and proxy supports forward and reverse proxy (SNI), SOCKS5 proxy which is also does support UDP when parent is Socks5. in addition to the existing HTTP or Socks5 proxy, which supports TLS, TCP, KCP three modes and chain-style connection. That is more than one SPS node connection can build encryption channel. The proxy protocol conversion use the SPS subcommand, SPS itself does not provide the proxy function, just accept the proxy request and then converse protocol and forwarded to the existing HTTP (s) or Socks5 proxy. SPS can use existing HTTP (s) or Socks5 proxy converse to support HTTP (s) and Socks5 HTTP (s) proxy at the same time by one port, and proxy supports forward and reverse proxy (SNI), SOCKS5 proxy which is also does support UDP when parent is Socks5. in addition to the existing HTTP or Socks5 proxy, which supports TLS, TCP, KCP three modes and chain-style connection. That is more than one SPS node connection can build encryption channel.
#### **6.2.HTTP(S) to HTTP(S) + SOCKS5** #### **6.2.HTTP(S) to HTTP(S) + SOCKS5**
Suppose there is a common HTTP (s) proxy: 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. Suppose there is a common HTTP (s) proxy: 127.0.0.1:8080. Now we turn it into a common proxy that supports HTTP (s), Socks5 and ss at the same time. The local port after transformation is 18080. ss's Encryption method is aes-192-cfb and its password is pass.
command command
`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p :18080` `./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p :18080 -h aes-192-cfb -j pass`
Suppose that there is a TLS HTTP (s) proxy: 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, TLS needs certificate file. Suppose that there is a TLS HTTP (s) proxy: 127.0.0.1:8080. Now we turn it into a common proxy that supports HTTP (s), Socks5 and ss at the same time. The local port after transformation is 18080, TLS needs certificate filess's Encryption method is aes-192-cfb and its password is pass.
command command
`./proxy sps -S http -T tls -P 127.0.0.1:8080 -t tcp -p :18080 -C proxy.crt -K proxy.key` `./proxy sps -S http -T tls -P 127.0.0.1:8080 -t tcp -p :18080 -C proxy.crt -K proxy.key -h aes-192-cfb -j pass`
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. 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), Socks5 and ss at the same time. The local port after transformation is 18080. ss's Encryption method is aes-192-cfb and its password is pass.
command command
`./proxy sps -S http -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123` `./proxy sps -S http -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123 -h aes-192-cfb -j pass`
#### **6.3.SOCKS5 to HTTP(S) + SOCKS5** #### **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. Suppose there is a common Socks5 proxy: 127.0.0.1:8080, now we turn it into a common proxy that supports HTTP (s), Socks5 and ss at the same time, and the local port after transformation is 18080. ss's Encryption method is aes-192-cfb and its password is pass.
command command
`./proxy sps -S socks -T tcp -P 127.0.0.1:8080 -t tcp -p :18080` `./proxy sps -S socks -T tcp -P 127.0.0.1:8080 -t tcp -p :18080 -h aes-192-cfb -j pass`
Suppose there is a TLS Socks5 proxy: 127.0.0.1:8080. Now we turn it into a common proxy that support HTTP (s) and Socks5 at the same time. The local port after transformation is 18080, TLS needs certificate file. Suppose there is a TLS Socks5 proxy: 127.0.0.1:8080. Now we turn it into a common proxy that supports HTTP (s), Socks5 and ss at the same time. The local port after transformation is 18080, TLS needs certificate file. ss's Encryption method is aes-192-cfb and its password is pass.
command command
`./proxy sps -S socks -T tls -P 127.0.0.1:8080 -t tcp -p :18080 -C proxy.crt -K proxy.key` `./proxy sps -S socks -T tls -P 127.0.0.1:8080 -t tcp -p :18080 -C proxy.crt -K proxy.key -h aes-192-cfb -j pass`
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. Suppose there is a KCP Socks5 proxy (password: demo123): 127.0.0.1:8080, now we turn it into a common proxy that supports HTTP (s), Socks5 and ss at the same time, and the local port after transformation is 18080. ss's Encryption method is aes-192-cfb and its password is pass.
command command
`./proxy sps -S socks -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123`   `./proxy sps -S socks -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123 -h aes-192-cfb -j pass`  
#### **6.4.Chain style connection** #### **6.4 SS to HTTP(S)+SOCKS5+SS**
SPS support the SS protocol with the local authorities. The parent proxy can be SPS or standard SS services.
By default, SPS provides three proxies, HTTP (S), SOCKS5 and SPS. the converted SOCKS5 and SS support UDP when the parent proxy is SOCKS5.
Suppose there is an ordinary SS or SPS proxy (open SS, encryption: aes-256-cfb, password: Demo)127.0.0.1:8080,Now we turn it into a common proxy that supports both http (s) and Socks5 and ss. The converted local port is 18080, and the converted ss encryption mode is aes-192-cfb, ss password:pass.
command
`./proxy sps -S socks -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123` `./proxy sps -S ss -H aes-256-cfb -J pass -T tcp -P 127.0.0.1:8080 -t tcp -p :18080 -h aes-192-cfb -j pass`.
#### **6.5.Chain style connection**
![6.4](/docs/images/sps-tls.png) ![6.4](/docs/images/sps-tls.png)
It is mentioned above that multiple SPS nodes can be connected to build encrypted channels, assuming you have the following VPS and a PC. It is mentioned above that multiple SPS nodes can be connected to build encrypted channels, assuming you have the following VPS and a PC.
vps012.2.2.2 vps012.2.2.2
@ -880,11 +1043,11 @@ Then run a SPS node on the PCexcute
finish。 finish。
#### **6.5.Listening on multiple ports** #### **6.6.Listening on multiple ports**
In general, listening one port is enough, but if you need to monitor 80 and 443 ports at the same time as a reverse proxy, the -p parameter can support it. In general, listening one port is enough, but if you need to monitor 80 and 443 ports at the same time as a reverse proxy, the -p parameter can support it.
The format is`-p 0.0.0.0:80,0.0.0.0:443`, Multiple bindings are separated by a comma. The format is`-p 0.0.0.0:80,0.0.0.0:443`, Multiple bindings are separated by a comma.
#### **6.6.Authentication** #### **6.7.Authentication**
SPS supports HTTP(s)\socks5 proxy authentication, which can concatenate authentication, there are four important information: SPS supports HTTP(s)\socks5 proxy authentication, which can concatenate authentication, there are four important information:
1:Users send authentication information`user-auth` 1:Users send authentication information`user-auth`
2:Local authentication information set up`local-auth` 2:Local authentication information set up`local-auth`
@ -926,7 +1089,20 @@ target: if the client is the HTTP (s) proxy request, this represents the complet
If there is no -a or -F or --auth-url parameters, local authentication is closed. If there is no -a or -F or --auth-url parameters, local authentication is closed.
If there is no -A parameter, the connection to the father proxy does not use authentication. If there is no -A parameter, the connection to the father proxy does not use authentication.
#### **6.7 Custom encryption** **Setting up separate authentication information**
If there are many different parent proxys and their passwords are the same or different, then authentication information can be set for each parent proxy.
At the same time, a global authentication information can be set with the - A parameter. If a parent proxy does not set the authentication information separately, the global authentication information can be used.
Authentication information is written together with parent proxy.
format: YTpi#2.2.2.2:33080@1
Explain:
YTpi is the Authentication information encoded by Base64, For example, http (s)/socks original authentication information, a:b,the user is a and the password is b, which is YTpi after Base64 encoding.
if it is ss, A is the encryption method and B is the password, for example, aes-192-cfb:your_pass, which is YWVzLTE5Mi1jZmI6eW91cl9wYXNz after Base64 encoding.
\# is an interval symbol. If there is authentication information, there must be #. No authentication information can be omitted #
2.2.2.2:33080 is parent proxy's address
@1 is weights, Nothing can be omitted. Detailed instructions can be referred to in the manual.***weights***
#### **6.8 Custom encryption**
HTTP(s) proxy can encrypt TCP data by TLS standard encryption and KCP protocol encryption, in addition to supporting custom encryption after TLS and KCP, That is to say, custom encryption and tls|kcp can be combined to use. The internal AES256 encryption is used, and it only needs to define one password by yourself. Encryption is divided into two parts, the one is whether the local (-z) is encrypted and decrypted, the other is whether the parents (-Z) is encrypted and decrypted. HTTP(s) proxy can encrypt TCP data by TLS standard encryption and KCP protocol encryption, in addition to supporting custom encryption after TLS and KCP, That is to say, custom encryption and tls|kcp can be combined to use. The internal AES256 encryption is used, and it only needs to define one password by yourself. Encryption is divided into two parts, the one is whether the local (-z) is encrypted and decrypted, the other is whether the parents (-Z) is encrypted and decrypted.
Custom encryption requires both ends are proxy. Next, we use two level example and three level example as examples: Custom encryption requires both ends are proxy. Next, we use two level example and three level example as examples:
Suppose there is already a HTTP (s) proxy:`6.6.6.6:6666` Suppose there is already a HTTP (s) proxy:`6.6.6.6:6666`
@ -947,7 +1123,7 @@ Local third level execution:
`proxy sps -T tcp -P 3.3.3.3:8888 -Z other_password -t tcp -p :8080` `proxy sps -T tcp -P 3.3.3.3:8888 -Z other_password -t tcp -p :8080`
through this way, When you visits the website by local proxy 8080, it visits the target website by encryption transmission with the parents proxy. through this way, When you visits the website by local proxy 8080, it visits the target website by encryption transmission with the parents proxy.
#### **6.8 Compressed transmission** #### **6.9 Compressed transmission**
HTTP(s) proxy can encrypt TCP data through TCP standard encryption and KCP protocol encryption, and can also compress data before custom encryption. HTTP(s) proxy can encrypt TCP data through TCP standard encryption and KCP protocol encryption, and can also compress data before custom encryption.
That is to say, compression and custom encryption and tls|kcp can be used together, compression is divided into two parts, the one is whether the local (-z) is compressed transmission, the other is whether the parents (-Z) is compressed transmission. That is to say, compression and custom encryption and tls|kcp can be used together, compression is divided into two parts, the one is whether the local (-z) is compressed transmission, the other is whether the parents (-Z) is compressed transmission.
The compression requires both ends are proxy. Compression also protects the (encryption) data in certain extent. we use two level example and three level example as examples: The compression requires both ends are proxy. Compression also protects the (encryption) data in certain extent. we use two level example and three level example as examples:
@ -962,13 +1138,36 @@ through this way, When you visits the website by local proxy 8080, it visits the
**three level example** **three level example**
First level VPS (ip:2.2.2.2) execution: First level VPS (ip:2.2.2.2) execution:
`proxy sps -t tcp -m -p :7777` `proxy sps -t tcp -m -p :7777`
Second level VPS (ip:3.3.3.3) execution:: Second level VPS (ip:3.3.3.3) execution:
`proxy sps -T tcp -P 2.2.2.2:7777 -M -t tcp -m -p :8888` `proxy sps -T tcp -P 2.2.2.2:7777 -M -t tcp -m -p :8888`
Local third level execution: Local third level execution:
`proxy sps -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080` `proxy sps -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080`
through this way, When you visits the website by local proxy 8080, it visits the target website by compressed transmission with the parents proxy. through this way, When you visits the website by local proxy 8080, it visits the target website by compressed transmission with the parents proxy.
#### **6.9.view help** #### **6.10 Disable protocol**
By default, SPS's port supports two proxy protocols, http (s) and socks5, and we can disable a protocol with parameters.
for example:
1.Disable the HTTP (S) proxy, retaining only the SOCKS5 proxy,parameter:`--disable-http`.
`proxy sps -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080 --disable-http`
1.Disable the SOCKS5 proxy, retaining only the HTTP (S) proxy,parameter:`--disable-socks`.
`proxy sps -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080 --disable-http`
#### **6.11 Speed limit**
Suppose there has a SOCKS5 parent proxy:
`proxy socks -p 2.2.2.2:33080 -z password -t tcp`
SPS lower speed limit 100K
`proxy sps -S socks -P 2.2.2.2:33080 -T tcp -Z password -l 100K -t tcp -p :33080`
It can be specified through the `-l` parameter, for example: 100K 1.5M. 0 means unlimited.
#### **6.12 Designated exporting IP**
The `- bind-listen` parameter opens the client's ability to access the target site with an entry IP connection, using the entry IP as the exporting IP. If the entry IP is the intranet IP, the exporting IP will not use the entry IP.
`proxy sps -S socks -P 2.2.2.2:33080 -T tcp -Z password -l 100K -t tcp --bind-listen -p :33080`
#### **6.13 Certificate parameters using Base64 data**
By default, the -C and -K parameters are the paths of CRT certificates and key files,
If it is the beginning of base64://, then it is considered that the data behind is Base64 encoded and will be used after decoding.
#### **6.14.view help**
`./proxy help sps` `./proxy help sps`
### **7.KCP Configuration** ### **7.KCP Configuration**
@ -1086,16 +1285,15 @@ execution: `go run *.go`
### License ### License
Proxy is licensed under GPLv3 license. Proxy is licensed under GPLv3 license.
### Contact ### Contact
proxy QQ group:189618940 proxy QQ group: 793015219 , 189618940 (full)
### Donation ### Donation
if proxy help you a lot,you can support us by: if proxy help you a lot,you can support us by:
### AliPay ### AliPay
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/alipay.jpg?raw=true" width="200"/> <img src="https://github.com/snail007/goproxy/blob/master/docs/images/alipay.jpg?raw=true" width="200"/>
### Wechat Pay ### Wechat Pay
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/wxpay.jpg?raw=true" width="200"/> <img src="https://github.com/snail007/goproxy/blob/master/docs/images/wxpay.jpg?raw=true" width="200"/>

View File

@ -1,7 +1,7 @@
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/logo.jpg?raw=true" width="200"/> <img src="https://github.com/snail007/goproxy/blob/master/docs/images/logo.jpg?raw=true" width="200"/>
Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务器,支持正向代理、反向代理、透明代理、内网穿透、TCP/UDP端口映射、SSH中转、TLS加密传输、协议转换、防污染DNS代理。 Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5,ss代理服务器,支持正向代理、反向代理、透明代理、内网穿透、TCP/UDP端口映射、SSH中转、TLS加密传输、协议转换、防污染DNS代理。
[点击下载](https://github.com/snail007/goproxy/releases) 官方QQ交流群:189618940 [点击下载](https://github.com/snail007/goproxy/releases) 官方QQ交流群: 793015219 (2群), 189618940 (1群满)
--- ---
@ -9,9 +9,11 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
**[English Manual](/README.md)** **[English Manual](/README.md)**
**[全平台图形界面版本](/gui/README.md)** **[全平台图形界面版本](/gui/README_ZH.md)**
**[全平台SDK](/sdk/README.md)** **[全平台SDK](https://github.com/snail007/goproxy-sdk/blob/master/README_ZH.md)**
**[GoProxy特殊授权](/AUTHORIZATION_ZH.md)**
### 如何贡献代码(Pull Request)? ### 如何贡献代码(Pull Request)?
@ -33,10 +35,16 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
- 集成外部APIHTTP(S),SOCKS5代理认证功能可以与外部HTTP API集成可以方便的通过外部系统控制代理用户 - 集成外部APIHTTP(S),SOCKS5代理认证功能可以与外部HTTP API集成可以方便的通过外部系统控制代理用户
- 反向代理,支持直接把域名解析到proxy监听的ip,然后proxy就会帮你代理访问需要访问的HTTP(S)网站. - 反向代理,支持直接把域名解析到proxy监听的ip,然后proxy就会帮你代理访问需要访问的HTTP(S)网站.
- 透明HTTP(S)代理,配合iptables,在网关直接把出去的80,443方向的流量转发到proxy,就能实现无感知的智能路由器代理. - 透明HTTP(S)代理,配合iptables,在网关直接把出去的80,443方向的流量转发到proxy,就能实现无感知的智能路由器代理.
- 协议转换可以把已经存在的HTTP(S)或SOCKS5代理转换为一个端口同时支持HTTP(S)和SOCKS5代理转换后的SOCKS5代理不支持UDP功能,同时支持强大的级联认证功能。 - 协议转换可以把已经存在的HTTP(S)或SOCKS5或SS代理转换为一个端口同时支持HTTP(S)和SOCKS5和SS代理转换后的SOCKS5和SS代理如果上级是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之后进行自定义加密,也就是说自定义加密和tls|kcp是可以联合使用的,内部采用AES256加密,使用的时候只需要自己定义一个密码即可。
- 底层压缩高效传输http(s)\sps\socks代理在tcp之上可以通过自定义加密和tls标准加密以及kcp协议加密tcp数据,在加密之后还可以对数据进行压缩,也就是说压缩功能和自定义加密和tls|kcp是可以联合使用的。 - 底层压缩高效传输http(s)\sps\socks代理在tcp之上可以通过自定义加密和tls标准加密以及kcp协议加密tcp数据,在加密之后还可以对数据进行压缩,也就是说压缩功能和自定义加密和tls|kcp是可以联合使用的。
- 安全的DNS代理可以通过本地的proxy提供的DNS代理服务器与上级代理加密通讯实现安全防污染的DNS查询。 - 安全的DNS代理可以通过本地的proxy提供的DNS代理服务器与上级代理加密通讯实现安全防污染的DNS查询。
- 负载均衡,高可用,HTTP(S)\SOCKS5\SPS代理支持上级负载均衡和高可用,多个上级重复-P参数即可.
- 指定出口IP,HTTP(S)\SOCKS5\SPS代理支持客户端用入口IP连接过来的,就用入口IP作为出口IP访问目标网站的功能。如果入口IP是内网IP出口IP不会使用入口IP
- 支持限速,HTTP(S)\SOCKS5\SPS代理支持限速.
- SOCKS5代理支持级联认证.
- 证书参数使用base64数据,默认情况下-C,-K参数是crt证书和key文件的路径,如果是base64://开头,那么就认为后面的数据是base64编码的,会解码后使用.
### Why need these? ### Why need these?
- 当由于某某原因,我们不能访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道访问我们的服务. - 当由于某某原因,我们不能访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道访问我们的服务.
@ -48,7 +56,7 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
- ... - ...
本页是v5.4手册,其他版本手册请点击[这里](docs/old-release.md)查看. 本页是v5.4手册,其他版本手册请点击[这里](docs/old-release-zh.md)查看.
### 怎么找到组织? ### 怎么找到组织?
@ -70,8 +78,9 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
- [安全建议](#安全建议) - [安全建议](#安全建议)
### 手册目录 ### 手册目录
- [负载均衡和高可用](#负载均衡和高可用)
- [1. HTTP代理](#1http代理) - [1. HTTP代理](#1http代理)
- [1.1 普通HTTP代理](#11普通http代理) - [1.1 普通HTTP代理](#11普通一级http代理)
- [1.2 普通二级HTTP代理](#12普通二级http代理) - [1.2 普通二级HTTP代理](#12普通二级http代理)
- [1.3 HTTP二级代理(加密)](#13http二级代理加密) - [1.3 HTTP二级代理(加密)](#13http二级代理加密)
- [1.4 HTTP三级代理(加密)](#14http三级代理加密) - [1.4 HTTP三级代理(加密)](#14http三级代理加密)
@ -86,7 +95,12 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
- [1.11 自定义DNS](#111-自定义dns) - [1.11 自定义DNS](#111-自定义dns)
- [1.12 自定义加密](#112-自定义加密) - [1.12 自定义加密](#112-自定义加密)
- [1.13 压缩传输](#113-压缩传输) - [1.13 压缩传输](#113-压缩传输)
- [1.14 查看帮助](#114-查看帮助) - [1.14 负载均衡](#114-负载均衡)
- [1.15 限速](#115-限速)
- [1.16 指定出口IP](#116-指定出口ip)
- [1.17 证书参数使用base64数据](#117-证书参数使用base64数据)
- [1.18 智能模式](#118-智能模式)
- [1.19 查看帮助](#119-查看帮助)
- [2. TCP代理(端口映射)](#2tcp代理) - [2. TCP代理(端口映射)](#2tcp代理)
- [2.1 普通一级TCP代理](#21普通一级tcp代理) - [2.1 普通一级TCP代理](#21普通一级tcp代理)
- [2.2 普通二级TCP代理](#22普通二级tcp代理) - [2.2 普通二级TCP代理](#22普通二级tcp代理)
@ -126,18 +140,28 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
- [5.9 自定义DNS](#59自定义dns) - [5.9 自定义DNS](#59自定义dns)
- [5.10 自定义加密](#510-自定义加密) - [5.10 自定义加密](#510-自定义加密)
- [5.11 压缩传输](#511-压缩传输) - [5.11 压缩传输](#511-压缩传输)
- [5.12 查看帮助](#512查看帮助) - [5.12 负载均衡](#512-负载均衡)
- [5.13 限速](#513-限速)
- [5.14 指定出口IP](#514-指定出口ip)
- [5.15 级联认证](#515-级联认证)
- [5.16 证书参数使用base64数据](#516-证书参数使用base64数据)
- [5.17 智能模式](#517-智能模式)
- [5.18 查看帮助](#518-查看帮助)
- [6. 代理协议转换](#6代理协议转换) - [6. 代理协议转换](#6代理协议转换)
- [6.1 功能介绍](#61-功能介绍) - [6.1 功能介绍](#61-功能介绍)
- [6.2 HTTP(S)转HTTP(S)+SOCKS5](#62-https转httpssocks5) - [6.2 HTTP(S)转HTTP(S)+SOCKS5+SS](#62-https转httpssocks5ss)
- [6.3 SOCKS5转HTTP(S)+SOCKS5](#63-socks5转httpssocks5) - [6.3 SOCKS5转HTTP(S)+SOCKS5+SS](#63-socks5转httpssocks5ss)
- [6.4 链式连接](#64-链式连接) - [6.4 SS转HTTP(S)+SOCKS5+SS](#64-ss转httpssocks5ss)
- [6.5 监听多个端口](#65-监听多个端口) - [6.5 链式连接](#65-链式连接)
- [6.6 认证功能](#66-认证功能) - [6.6 监听多个端口](#66-监听多个端口)
- [6.7 自定义加密](#67-自定义加密) - [6.7 认证功能](#67-认证功能)
- [6.8 压缩传输](#68-压缩传输) - [6.8 自定义加密](#68-自定义加密)
- [6.9 禁用协议](#69-禁用协议) - [6.9 压缩传输](#69-压缩传输)
- [6.10 查看帮助](#610-查看帮助) - [6.10 禁用协议](#610-禁用协议)
- [6.11 限速](#611-限速)
- [6.12 指定出口IP](#612-指定出口ip)
- [6.13 证书参数使用base64数据](#613-证书参数使用base64数据)
- [6.14 查看帮助](#614-查看帮助)
- [7. KCP配置](#7kcp配置) - [7. KCP配置](#7kcp配置)
- [7.1 配置介绍](#71-配置介绍) - [7.1 配置介绍](#71-配置介绍)
- [7.2 详细配置](#72-详细配置) - [7.2 详细配置](#72-详细配置)
@ -149,21 +173,27 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
提示:所有操作需要root权限. 提示:所有操作需要root权限.
#### 自动安装 #### 自动安装
#### **0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.** #### **0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.**
```shell ```shell
curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.sh | bash curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.sh | bash
``` ```
安装完成,配置目录是/etc/proxy,更详细的使用方法参考下面的进一步了解.
安装完成,配置目录是/etc/proxy,更详细的使用方法请参考上面的手册目录,进一步了解你想要使用的功能.
如果安装失败或者你的vps不是linux64位系统,请按照下面的半自动步骤安装: 如果安装失败或者你的vps不是linux64位系统,请按照下面的半自动步骤安装:
#### 手动安装 #### 手动安装
#### **1.下载proxy** #### **1.下载proxy**
下载地址:https://github.com/snail007/goproxy/releases 下载地址:https://github.com/snail007/goproxy/releases/latest
下面以v6.2为例,如果有最新版,请使用最新版链接.
```shell ```shell
cd /root/proxy/ cd /root/proxy/
wget https://github.com/snail007/goproxy/releases/download/v5.4/proxy-linux-amd64.tar.gz wget https://github.com/snail007/goproxy/releases/download/v6.2/proxy-linux-amd64.tar.gz
``` ```
#### **2.下载自动安装脚本** #### **2.下载自动安装脚本**
```shell ```shell
cd /root/proxy/ cd /root/proxy/
wget https://raw.githubusercontent.com/snail007/goproxy/master/install.sh wget https://raw.githubusercontent.com/snail007/goproxy/master/install.sh
@ -172,6 +202,9 @@ chmod +x install.sh
``` ```
#### Docker安装 #### Docker安装
[docker](https://hub.docker.com/r/snail007/goproxy)
项目根目录的Dockerfile文件用来构建,使用golang 1.10.3,构建基于goproxy的master分支最新版本, 项目根目录的Dockerfile文件用来构建,使用golang 1.10.3,构建基于goproxy的master分支最新版本,
全部大小17.3MB,默认情况下使用master分支,不过可以通过修改配置文件Dockerfile 全部大小17.3MB,默认情况下使用master分支,不过可以通过修改配置文件Dockerfile
或者使用参数GOPROXY_VERSION指定构建的goproxy版本. 或者使用参数GOPROXY_VERSION指定构建的goproxy版本.
@ -260,6 +293,32 @@ proxy会fork子进程,然后监控子进程,如果子进程异常退出,5秒后
假设你的vps外网ip是23.23.23.23,下面命令通过-g参数设置23.23.23.23 假设你的vps外网ip是23.23.23.23,下面命令通过-g参数设置23.23.23.23
`./proxy http -g "23.23.23.23"` `./proxy http -g "23.23.23.23"`
### **负载均衡和高可用**
HTTP(S)\SOCKS5\SPS代理支持上级负载均衡和高可用,多个上级重复-P参数即可.
负载均衡策略支持5种,可以通过`--lb-method`参数指定:
roundrobin 轮流使用
leastconn 使用最小连接数的
leasttime 使用连接时间最小的
hash 使用根据客户端地址计算出一个固定上级
weight 根据每个上级的权重和连接数情况,选择出一个上级
提示:
负载均衡检查时间间隔可以通过`--lb-retrytime`设置,单位毫秒
负载均衡连接超时时间可以通过`--lb-timeout`设置,单位毫秒
如果负载均衡策略是权重(weight),-P格式为:2.2.2.2:3880@1,1就是权重,大于0的整数.
如果负载均衡策略是hash,默认是根据客户端地址选择上级,可以通过开关`--lb-hashtarget`使用访问的目标地址选择上级.
### **1.HTTP代理** ### **1.HTTP代理**
#### **1.1.普通一级HTTP代理** #### **1.1.普通一级HTTP代理**
![1.1](/docs/images/http-1.png) ![1.1](/docs/images/http-1.png)
@ -310,11 +369,12 @@ proxy会fork子进程,然后监控子进程,如果子进程异常退出,5秒后
比如: 比如:
`./proxy http -t tcp -p ":33080" --auth-url "http://test.com/auth.php"` `./proxy http -t tcp -p ":33080" --auth-url "http://test.com/auth.php"`
用户连接的时候,proxy会GET方式请求这url("http://test.com/auth.php"), 用户连接的时候,proxy会GET方式请求这url("http://test.com/auth.php"),
带上user,pass,ip,target四个参数: 带上user,pass,ip,local_ip,target五个参数:
http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}&target={TARGET} http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}&local_ip={LOCAL_IP}&target={TARGET}
user:用户名 user:用户名
pass:密码 pass:密码
ip:用户的IP,比如:192.168.1.200 ip:用户的IP,比如:192.168.1.200
local_ip:用户访问的服务器的IP,比如:3.3.3.3
target:用户访问的URL,比如:http://demo.com:80/1.html或https://www.baidu.com:80 target:用户访问的URL,比如:http://demo.com:80/1.html或https://www.baidu.com:80
如果没有-a或-F或--auth-url参数,就是关闭Basic认证. 如果没有-a或-F或--auth-url参数,就是关闭Basic认证.
@ -332,7 +392,7 @@ target:用户访问的URL,比如:http://demo.com:80/1.html或https://www.baidu.c
##### ***1.7.1 ssh用户名和密码的方式*** ##### ***1.7.1 ssh用户名和密码的方式***
本地HTTP(S)代理28080端口,执行: 本地HTTP(S)代理28080端口,执行:
`./proxy http -T ssh -P "2.2.2.2:22" -u user -A demo -t tcp -p ":28080"` `./proxy http -T ssh -P "2.2.2.2:22" -u user -D demo -t tcp -p ":28080"`
##### ***1.7.2 ssh用户名和密钥的方式*** ##### ***1.7.2 ssh用户名和密钥的方式***
本地HTTP(S)代理28080端口,执行: 本地HTTP(S)代理28080端口,执行:
`./proxy http -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"` `./proxy http -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"`
@ -461,7 +521,51 @@ proxy的http(s)代理在tcp之上可以通过tls标准加密以及kcp协议加
`proxy http -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080` `proxy http -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080`
这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站. 这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站.
#### **1.14 查看帮助** ### **1.14 负载均衡**
HTTP(S)代理支持上级负载均衡,多个上级重复-P参数即可.
`proxy http --lb-method=hash -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080`
#### **1.14.1 设置重试间隔和超时时间**
`proxy http --lb-method=leastconn --lb-retrytime 300 --lb-timeout 300 -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080 -t tcp -p :33080`
#### **1.14.2 设置权重**
`proxy http --lb-method=weight -T tcp -P 1.1.1.1:33080@1 -P 2.1.1.1:33080@2 -P 3.1.1.1:33080@1 -t tcp -p :33080`
#### **1.14.3 使用目标地址选择上级**
`proxy http --lb-hashtarget --lb-method=hash -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080 -t tcp -p :33080`
### **1.15 限速**
限速100K,通过`-l`参数即可指定,比如:100K 1.5M . 0意味着无限制.
`proxy http -t tcp -p 2.2.2.2:33080 -l 100K`
### **1.16 指定出口IP**
`--bind-listen`参数就可以开启客户端用入口IP连接过来的,就用入口IP作为出口IP访问目标网站的功能。如果入口IP是内网IP出口IP不会使用入口IP。
`proxy http -t tcp -p 2.2.2.2:33080 --bind-listen`
### **1.17 证书参数使用base64数据**
默认情况下-C,-K参数是crt证书和key文件的路径,
如果是base64://开头,那么就认为后面的数据是base64编码的,会解码后使用.
#### **1.18 智能模式**
智能模式设置,可以是intelligent|direct|parent三者之一.
默认是:intelligent.
每个值的含义如下:
`--intelligent=direct`,不在blocked里面的目标都直连.
`--intelligent=parent`,不在direct里面的目标都走上级.
`--intelligent=intelligent`,blocked和direct里面都没有的目标,智能判断是否使用上级访问目标.
#### **1.19 查看帮助**
`./proxy help http` `./proxy help http`
### **2.TCP代理** ### **2.TCP代理**
@ -795,7 +899,7 @@ SOCKS5代理,支持CONNECT,UDP协议,不支持BIND,支持用户名密码认证.
##### ***5.6.1 ssh用户名和密码的方式*** ##### ***5.6.1 ssh用户名和密码的方式***
本地SOCKS5代理28080端口,执行: 本地SOCKS5代理28080端口,执行:
`./proxy socks -T ssh -P "2.2.2.2:22" -u user -A demo -t tcp -p ":28080"` `./proxy socks -T ssh -P "2.2.2.2:22" -u user -D demo -t tcp -p ":28080"`
##### ***5.6.2 ssh用户名和密钥的方式*** ##### ***5.6.2 ssh用户名和密钥的方式***
本地SOCKS5代理28080端口,执行: 本地SOCKS5代理28080端口,执行:
`./proxy socks -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"` `./proxy socks -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"`
@ -815,11 +919,12 @@ SOCKS5代理,支持CONNECT,UDP协议,不支持BIND,支持用户名密码认证.
比如: 比如:
`./proxy socks -t tcp -p ":33080" --auth-url "http://test.com/auth.php"` `./proxy socks -t tcp -p ":33080" --auth-url "http://test.com/auth.php"`
用户连接的时候,proxy会GET方式请求这url("http://test.com/auth.php"), 用户连接的时候,proxy会GET方式请求这url("http://test.com/auth.php"),
带上user,pass,ip,个参数: 带上user,pass,ip,local_ip四个参数:
http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP} http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}&local_ip={LOCAL_IP}
user:用户名 user:用户名
pass:密码 pass:密码
ip:用户的IP,比如:192.168.1.200 ip:用户的IP,比如:192.168.1.200
local_ip:用户访问的服务器的IP,比如:3.3.3.3
如果没有-a或-F或--auth-url参数,就是关闭认证. 如果没有-a或-F或--auth-url参数,就是关闭认证.
@ -890,41 +995,106 @@ proxy的socks代理在tcp之上可以通过自定义加密和tls标准加密以
`proxy socks -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080` `proxy socks -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080`
这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站. 这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站.
#### **5.12.查看帮助**
#### **5.12 负载均衡**
SOCKS代理支持上级负载均衡,多个上级重复-P参数即可.
`proxy socks --lb-method=hash -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080 -p :33080 -t tcp`
#### **5.12.1 设置重试间隔和超时时间**
`proxy socks --lb-method=leastconn --lb-retrytime 300 --lb-timeout 300 -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080 -p :33080 -t tcp`
#### **5.12.2 设置权重**
`proxy socks --lb-method=weight -T tcp -P 1.1.1.1:33080@1 -P 2.1.1.1:33080@2 -P 3.1.1.1:33080@1 -p :33080 -t tcp`
#### **5.12.3 使用目标地址选择上级**
`proxy socks --lb-hashtarget --lb-method=hash -T tcp -P 1.1.1.1:33080 -P 2.1.1.1:33080 -P 3.1.1.1:33080 -p :33080 -t tcp`
#### **5.13 限速**
限速100K,通过`-l`参数即可指定,比如:100K 1.5M . 0意味着无限制.
`proxy socks -t tcp -p 2.2.2.2:33080 -l 100K`
#### **5.14 指定出口IP**
`--bind-listen`参数就可以开启客户端用入口IP连接过来的,就用入口IP作为出口IP访问目标网站的功能。如果入口IP是内网IP出口IP不会使用入口IP。
`proxy socks -t tcp -p 2.2.2.2:33080 --bind-listen`
#### **5.15 级联认证**
SOCKS5支持级联认证,-A可以设置上级认证信息.
上级:
`proxy socks -t tcp -p 2.2.2.2:33080 -a user:pass`
本地:
`proxy socks -T tcp -P 2.2.2.2:33080 -A user:pass -t tcp -p :33080`
#### **5.16 证书参数使用base64数据**
默认情况下-C,-K参数是crt证书和key文件的路径,
如果是base64://开头,那么就认为后面的数据是base64编码的,会解码后使用.
#### **5.17 智能模式**
智能模式设置,可以是intelligent|direct|parent三者之一.
默认是:intelligent.
每个值的含义如下:
`--intelligent=direct`,不在blocked里面的目标都直连.
`--intelligent=parent`,不在direct里面的目标都走上级.
`--intelligent=intelligent`,blocked和direct里面都没有的目标,智能判断是否使用上级访问目标.
#### **5.18.查看帮助**
`./proxy help socks` `./proxy help socks`
### **6.代理协议转换** ### **6.代理协议转换**
#### **6.1 功能介绍** #### **6.1 功能介绍**
代理协议转换使用的是sps子命令(socks+https的缩写)sps本身不提供代理功能只是接受代理请求"转换并转发"给已经存在的http(s)代理或者socks5代理sps可以把已经存在的http(s)代理或者socks5代理转换为一个端口同时支持http(s)和socks5代理而且http(s)代理支持正向代理和反向代理(SNI)转换后的SOCKS5代理当上级是SOCKS5时仍然支持UDP功能另外对于已经存在的http(s)代理或者socks5代理支持tls、tcp、kcp三种模式支持链式连接也就是可以多个sps结点层级连接构建加密通道。 代理协议转换使用的是sps子命令sps本身不提供代理功能只是接受代理请求"转换并转发"给已经存在的http(s)代理或者socks5代理或者ss代理sps可以把已经存在的http(s)代理或者socks5代理或ss代理转换为一个端口同时支持http(s)和socks5和ss的代理而且http(s)代理支持正向代理和反向代理(SNI)转换后的SOCKS5代理当上级是SOCKS5或者SS时仍然支持UDP功能另外对于已经存在的http(s)代理或者socks5代理支持tls、tcp、kcp三种模式支持链式连接也就是可以多个sps结点层级连接构建加密通道。
#### **6.2 HTTP(S)转HTTP(S)+SOCKS5** #### **6.2 HTTP(S)转HTTP(S)+SOCKS5+SS**
假设已经存在一个普通的http(s)代理127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5的普通代理,转换后的本地端口为18080。 假设已经存在一个普通的http(s)代理127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5和ss的普通代理,转换后的本地端口为18080,ss加密方式:aes-192-cfb,ss密码:pass
命令如下: 命令如下:
`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p :18080` `./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p :18080 -h aes-192-cfb -j pass`
假设已经存在一个tls的http(s)代理127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5的普通代理,转换后的本地端口为18080tls需要证书文件。 假设已经存在一个tls的http(s)代理127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5和ss的普通代理,转换后的本地端口为18080tls需要证书文件,ss加密方式:aes-192-cfb,ss密码:pass
命令如下: 命令如下:
`./proxy sps -S http -T tls -P 127.0.0.1:8080 -t tcp -p :18080 -C proxy.crt -K proxy.key` `./proxy sps -S http -T tls -P 127.0.0.1:8080 -t tcp -p :18080 -C proxy.crt -K proxy.key -h aes-192-cfb -j pass`
假设已经存在一个kcp的http(s)代理密码是demo123127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5的普通代理,转换后的本地端口为18080。 假设已经存在一个kcp的http(s)代理密码是demo123127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5和ss的普通代理,转换后的本地端口为18080,ss加密方式:aes-192-cfb,ss密码:pass
命令如下: 命令如下:
`./proxy sps -S http -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123` `./proxy sps -S http -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123 -h aes-192-cfb -j pass`
#### **6.3 SOCKS5转HTTP(S)+SOCKS5** #### **6.3 SOCKS5转HTTP(S)+SOCKS5+SS**
假设已经存在一个普通的socks5代理127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5的普通代理,转换后的本地端口为18080。 假设已经存在一个普通的socks5代理127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5和ss的普通代理,转换后的本地端口为18080,ss加密方式:aes-192-cfb,ss密码:pass
命令如下: 命令如下:
`./proxy sps -S socks -T tcp -P 127.0.0.1:8080 -t tcp -p :18080` `./proxy sps -S socks -T tcp -P 127.0.0.1:8080 -t tcp -p :18080 -h aes-192-cfb -j pass`
假设已经存在一个tls的socks5代理127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5的普通代理,转换后的本地端口为18080tls需要证书文件。 假设已经存在一个tls的socks5代理127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5和ss的普通代理,转换后的本地端口为18080tls需要证书文件,ss加密方式:aes-192-cfb,ss密码:pass
命令如下: 命令如下:
`./proxy sps -S socks -T tls -P 127.0.0.1:8080 -t tcp -p :18080 -C proxy.crt -K proxy.key` `./proxy sps -S socks -T tls -P 127.0.0.1:8080 -t tcp -p :18080 -C proxy.crt -K proxy.key -h aes-192-cfb -j pass`
假设已经存在一个kcp的socks5代理密码是demo123127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5的普通代理,转换后的本地端口为18080。 假设已经存在一个kcp的socks5代理密码是demo123127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5和ss的普通代理,转换后的本地端口为18080,ss加密方式:aes-192-cfb,ss密码:pass
命令如下: 命令如下:
`./proxy sps -S socks -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123` `./proxy sps -S socks -T kcp -P 127.0.0.1:8080 -t tcp -p :18080 --kcp-key demo123 -h aes-192-cfb -j pass`
#### **6.4 链式连接** #### **6.4 SS转HTTP(S)+SOCKS5+SS**
SPS上级和本地支持ss协议,上级可以是SPS或者标准的ss服务.
SPS本地默认提供HTTP(S)\SOCKS5\SPS三种代理,当上级是SOCKS5时转换后的SOCKS5和SS支持UDP功能.
假设已经存在一个普通的SS或者SPS代理(开启了ss,加密方式:aes-256-cfb,密码:demo)127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5和ss的普通代理,转换后的本地端口为18080,转换后的ss加密方式:aes-192-cfb,ss密码:pass。
命令如下:
`./proxy sps -S ss -H aes-256-cfb -J pass -T tcp -P 127.0.0.1:8080 -t tcp -p :18080 -h aes-192-cfb -j pass`.
#### **6.5 链式连接**
![6.4](/docs/images/sps-tls.png) ![6.4](/docs/images/sps-tls.png)
上面提过多个sps结点可以层级连接构建加密通道假设有如下vps和家里的pc电脑。 上面提过多个sps结点可以层级连接构建加密通道假设有如下vps和家里的pc电脑。
vps012.2.2.2 vps012.2.2.2
@ -944,11 +1114,11 @@ vps023.3.3.3
完成。 完成。
#### **6.5 监听多个端口** #### **6.6 监听多个端口**
一般情况下监听一个端口就可以不过如果作为反向代理需要同时监听80和443两个端口那么-p参数是支持的 一般情况下监听一个端口就可以不过如果作为反向代理需要同时监听80和443两个端口那么-p参数是支持的
格式是:`-p 0.0.0.0:80,0.0.0.0:443`,多个绑定用逗号分隔即可。 格式是:`-p 0.0.0.0:80,0.0.0.0:443`,多个绑定用逗号分隔即可。
#### **6.6 认证功能** #### **6.7 认证功能**
sps支持http(s)\socks5代理认证,可以级联认证,有四个重要的信息: sps支持http(s)\socks5代理认证,可以级联认证,有四个重要的信息:
1:用户发送认证信息`user-auth` 1:用户发送认证信息`user-auth`
2:设置的本地认证信息`local-auth` 2:设置的本地认证信息`local-auth`
@ -990,8 +1160,20 @@ target:如果客户端是http(s)代理请求,这里代表的是请求的完整ur
如果没有-a或-F或--auth-url参数,就是关闭本地认证. 如果没有-a或-F或--auth-url参数,就是关闭本地认证.
如果没有-A参数,连接上级不使用认证. 如果没有-A参数,连接上级不使用认证.
**设置单独认证信息**
#### **6.7 自定义加密** 如果存在多个不同上级,而且他们的密码有的一样有的不一样,那么可以针对每个上级设置认证信息,
同时还可以用-A参数设置一个全局认证信息,如果某个上级没有单独设置认证信息就使用全局设置的认证信息.
认证信息和上级写在一起.
格式是: YTpi#2.2.2.2:33080@1
说明:
YTpi 是经过base64编码的认证信息,比如是http(s)/socks原始认证信息a:b,用户是a密码是b,base64编码之后是:YTpi
如果是ss,那么a就是加密方法,b是密码,比如:aes-192-cfb:your_pass,base64编码之后是:YWVzLTE5Mi1jZmI6eW91cl9wYXNz
# 是间隔符号,如果有认证信息,必须有#,没有认证信息可以省略#
2.2.2.2:33080 是上级地址
@1 是设置权重,没有可以省略,详细说明可以参考手册***权重部分***
#### **6.8 自定义加密**
proxy的sps代理在tcp之上可以通过tls标准加密以及kcp协议加密tcp数据,除此之外还支持在tls和kcp之后进行 proxy的sps代理在tcp之上可以通过tls标准加密以及kcp协议加密tcp数据,除此之外还支持在tls和kcp之后进行
自定义加密,也就是说自定义加密和tls|kcp是可以联合使用的,内部采用AES256加密,使用的时候只需要自己定义 自定义加密,也就是说自定义加密和tls|kcp是可以联合使用的,内部采用AES256加密,使用的时候只需要自己定义
一个密码即可,加密分为两个部分,一部分是本地(-z)是否加密解密,一部分是与上级(-Z)传输是否加密解密. 一个密码即可,加密分为两个部分,一部分是本地(-z)是否加密解密,一部分是与上级(-Z)传输是否加密解密.
@ -1019,7 +1201,7 @@ proxy的sps代理在tcp之上可以通过tls标准加密以及kcp协议加密tcp
`proxy sps -T tcp -P 3.3.3.3:8888 -Z other_password -t tcp -p :8080` `proxy sps -T tcp -P 3.3.3.3:8888 -Z other_password -t tcp -p :8080`
这样通过本地代理8080访问网站的时候就是通过与上级加密传输访问目标网站. 这样通过本地代理8080访问网站的时候就是通过与上级加密传输访问目标网站.
#### **6.8 压缩传输** #### **6.9 压缩传输**
proxy的sps代理在tcp之上可以通过自定义加密和tls标准加密以及kcp协议加密tcp数据,在自定义加密之前还可以 proxy的sps代理在tcp之上可以通过自定义加密和tls标准加密以及kcp协议加密tcp数据,在自定义加密之前还可以
对数据进行压缩,也就是说压缩功能和自定义加密和tls|kcp是可以联合使用的,压缩分为两个部分, 对数据进行压缩,也就是说压缩功能和自定义加密和tls|kcp是可以联合使用的,压缩分为两个部分,
一部分是本地(-m)是否压缩传输,一部分是与上级(-M)传输是否压缩. 一部分是本地(-m)是否压缩传输,一部分是与上级(-M)传输是否压缩.
@ -1045,8 +1227,7 @@ proxy的sps代理在tcp之上可以通过自定义加密和tls标准加密以及
`proxy sps -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080` `proxy sps -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080`
这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站. 这样通过本地代理8080访问网站的时候就是通过与上级压缩传输访问目标网站.
#### **6.10 禁用协议**
#### **6.9 禁用协议**
SPS默认情况下一个端口支持http(s)和socks5两种代理协议,我们可以通过参数禁用某个协议 SPS默认情况下一个端口支持http(s)和socks5两种代理协议,我们可以通过参数禁用某个协议
比如: 比如:
1.禁用HTTP(S)代理功能只保留SOCKS5代理功能,参数:`--disable-http`. 1.禁用HTTP(S)代理功能只保留SOCKS5代理功能,参数:`--disable-http`.
@ -1055,7 +1236,31 @@ SPS默认情况下一个端口支持http(s)和socks5两种代理协议,我们可
1.禁用SOCKS5代理功能只保留HTTP(S)代理功能,参数:`--disable-socks`. 1.禁用SOCKS5代理功能只保留HTTP(S)代理功能,参数:`--disable-socks`.
`proxy sps -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080 --disable-http` `proxy sps -T tcp -P 3.3.3.3:8888 -M -t tcp -p :8080 --disable-http`
#### **6.10 查看帮助** #### **6.11 限速**
假设存在SOCKS5上级:
`proxy socks -p 2.2.2.2:33080 -z password -t tcp`
sps下级,限速100K
`proxy sps -S socks -P 2.2.2.2:33080 -T tcp -Z password -l 100K -t tcp -p :33080`
通过`-l`参数即可指定,比如:100K 1.5M . 0意味着无限制.
#### **6.12 指定出口IP**
`--bind-listen`参数就可以开启客户端用入口IP连接过来的,就用入口IP作为出口IP访问目标网站的功能。如果入口IP是内网IP出口IP不会使用入口IP。
`proxy sps -S socks -P 2.2.2.2:33080 -T tcp -Z password -l 100K -t tcp --bind-listen -p :33080`
#### **6.13 证书参数使用base64数据**
默认情况下-C,-K参数是crt证书和key文件的路径,
如果是base64://开头,那么就认为后面的数据是base64编码的,会解码后使用.
#### **6.14 查看帮助**
`./proxy help sps` `./proxy help sps`
### **7.KCP配置** ### **7.KCP配置**
@ -1180,7 +1385,7 @@ utils是工具包,service是具体的每个服务类.
### License ### License
Proxy is licensed under GPLv3 license. Proxy is licensed under GPLv3 license.
### Contact ### Contact
QQ交流群:189618940 官方QQ交流群: 793015219 (2群), 189618940 (1群满)
### Donation ### Donation

1
VERSION Normal file
View File

@ -0,0 +1 @@
6.9

136
config.go
View File

@ -10,23 +10,25 @@ import (
"os/exec" "os/exec"
"path" "path"
"path/filepath" "path/filepath"
"runtime/debug"
"runtime/pprof" "runtime/pprof"
"strings"
"time" "time"
"github.com/snail007/goproxy/core/lib/kcpcfg"
encryptconn "github.com/snail007/goproxy/core/lib/transport/encrypt"
sdk "github.com/snail007/goproxy/sdk/android-ios" sdk "github.com/snail007/goproxy/sdk/android-ios"
"github.com/snail007/goproxy/services" services "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
httpx "github.com/snail007/goproxy/services/http" httpx "github.com/snail007/goproxy/services/http"
keygenx "github.com/snail007/goproxy/services/keygen" keygenx "github.com/snail007/goproxy/services/keygen"
mux "github.com/snail007/goproxy/services/mux" mux "github.com/snail007/goproxy/services/mux"
socksx "github.com/snail007/goproxy/services/socks" socksx "github.com/snail007/goproxy/services/socks"
spsx "github.com/snail007/goproxy/services/sps" spsx "github.com/snail007/goproxy/services/sps"
tcpx "github.com/snail007/goproxy/services/tcp" tcpx "github.com/snail007/goproxy/services/tcp"
tunnel "github.com/snail007/goproxy/services/tunnel" tunnelx "github.com/snail007/goproxy/services/tunnel"
udpx "github.com/snail007/goproxy/services/udp" udpx "github.com/snail007/goproxy/services/udp"
kcp "github.com/xtaci/kcp-go" kcp "github.com/xtaci/kcp-go"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
kingpin "gopkg.in/alecthomas/kingpin.v2" kingpin "gopkg.in/alecthomas/kingpin.v2"
) )
@ -35,17 +37,18 @@ var (
app *kingpin.Application app *kingpin.Application
service *services.ServiceItem service *services.ServiceItem
cmd *exec.Cmd cmd *exec.Cmd
cpuProfilingFile, memProfilingFile, blockProfilingFile, goroutineProfilingFile, threadcreateProfilingFile *os.File cpuProfilingFile, memProfilingFile, blockProfilingFile,
isDebug bool goroutineProfilingFile, threadcreateProfilingFile *os.File
isDebug *bool
) )
func initConfig() (err error) { func initConfig() (err error) {
//define args //define args
tcpArgs := tcpx.TCPArgs{} tcpArgs := tcpx.TCPArgs{}
httpArgs := httpx.HTTPArgs{} httpArgs := httpx.HTTPArgs{}
tunnelServerArgs := tunnel.TunnelServerArgs{} tunnelServerArgs := tunnelx.TunnelServerArgs{}
tunnelClientArgs := tunnel.TunnelClientArgs{} tunnelClientArgs := tunnelx.TunnelClientArgs{}
tunnelBridgeArgs := tunnel.TunnelBridgeArgs{} tunnelBridgeArgs := tunnelx.TunnelBridgeArgs{}
muxServerArgs := mux.MuxServerArgs{} muxServerArgs := mux.MuxServerArgs{}
muxClientArgs := mux.MuxClientArgs{} muxClientArgs := mux.MuxClientArgs{}
muxBridgeArgs := mux.MuxBridgeArgs{} muxBridgeArgs := mux.MuxBridgeArgs{}
@ -55,11 +58,10 @@ func initConfig() (err error) {
dnsArgs := sdk.DNSArgs{} dnsArgs := sdk.DNSArgs{}
keygenArgs := keygenx.KeygenArgs{} keygenArgs := keygenx.KeygenArgs{}
kcpArgs := kcpcfg.KCPConfigArgs{} kcpArgs := kcpcfg.KCPConfigArgs{}
//build srvice args //build srvice args
app = kingpin.New("proxy", "happy with proxy") app = kingpin.New("proxy", "happy with proxy")
app.Author("snail").Version(APP_VERSION) app.Author("snail").Version(APP_VERSION)
debug := app.Flag("debug", "debug log output").Default("false").Bool() isDebug = app.Flag("debug", "debug log output").Default("false").Bool()
daemon := app.Flag("daemon", "run proxy in background").Default("false").Bool() 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() forever := app.Flag("forever", "run proxy in forever,fail and retry").Default("false").Bool()
logfile := app.Flag("log", "log file path").Default("").String() logfile := app.Flag("log", "log file path").Default("").String()
@ -84,7 +86,7 @@ func initConfig() (err error) {
//########http######### //########http#########
http := app.Command("http", "proxy on http mode") 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.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').Strings()
httpArgs.CaCertFile = http.Flag("ca", "ca cert file for tls").Default("").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.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.KeyFile = http.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
@ -115,7 +117,16 @@ func initConfig() (err error) {
httpArgs.ParentKey = http.Flag("parent-key", "the password for auto encrypt/decrypt parent 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.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() httpArgs.ParentCompress = http.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
httpArgs.Intelligent = http.Flag("intelligent", "settting intelligent HTTP, SOCKS5 proxy mode, can be <intelligent|direct|parent>").Default("intelligent").Enum("intelligent", "direct", "parent")
httpArgs.LoadBalanceMethod = http.Flag("lb-method", "load balance method when use multiple parent,can be <roundrobin|leastconn|leasttime|hash|weight>").Default("roundrobin").Enum("roundrobin", "weight", "leastconn", "leasttime", "hash")
httpArgs.LoadBalanceTimeout = http.Flag("lb-timeout", "tcp milliseconds timeout of connecting to parent").Default("500").Int()
httpArgs.LoadBalanceRetryTime = http.Flag("lb-retrytime", "sleep time milliseconds after checking").Default("1000").Int()
httpArgs.LoadBalanceHashTarget = http.Flag("lb-hashtarget", "use target address to choose parent for LB").Default("false").Bool()
httpArgs.LoadBalanceOnlyHA = http.Flag("lb-onlyha", "use only `high availability mode` to choose parent for LB").Default("false").Bool()
httpArgs.RateLimit = http.Flag("rate-limit", "rate limit (bytes/second) of each connection, such as: 100K 1.5M . 0 means no limitation").Short('l').Default("0").String()
httpArgs.BindListen = http.Flag("bind-listen", "using listener binding IP when connect to target").Short('B').Default("false").Bool()
httpArgs.Jumper = http.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
httpArgs.Debug = isDebug
//########tcp######### //########tcp#########
tcp := app.Command("tcp", "proxy on tcp mode") 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.Parent = tcp.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
@ -124,6 +135,7 @@ 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.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 <tls|tcp|kcp|udp>").Short('T').Enum("tls", "tcp", "udp", "kcp") tcpArgs.ParentType = tcp.Flag("parent-type", "parent protocol type <tls|tcp|kcp|udp>").Short('T').Enum("tls", "tcp", "udp", "kcp")
tcpArgs.LocalType = tcp.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") tcpArgs.LocalType = tcp.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp")
tcpArgs.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
tcpArgs.Jumper = tcp.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() tcpArgs.Jumper = tcp.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
@ -140,7 +152,7 @@ func initConfig() (err error) {
//########mux-server######### //########mux-server#########
muxServer := app.Command("server", "proxy on mux server mode") 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.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 <tls|tcp|kcp>").Default("tls").Short('T').Enum("tls", "tcp", "kcp") muxServerArgs.ParentType = muxServer.Flag("parent-type", "parent protocol type <tls|tcp|tcps|kcp|tou>").Default("tls").Short('T').Enum("tls", "tcp", "tcps", "kcp", "tou")
muxServerArgs.CertFile = muxServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() 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.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.Timeout = muxServer.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
@ -150,11 +162,15 @@ func initConfig() (err error) {
muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp|tls 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() muxServerArgs.SessionCount = muxServer.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
muxServerArgs.Jumper = muxServer.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() muxServerArgs.Jumper = muxServer.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
muxServerArgs.TCPSMethod = muxServer.Flag("tcps-method", "method of parent tcps's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxServerArgs.TCPSPassword = muxServer.Flag("tcps-password", "password of parent tcps's encrpyt/decrypt").Default("snail007's_goproxy").String()
muxServerArgs.TOUMethod = muxServer.Flag("tou-method", "method of parent tou's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxServerArgs.TOUPassword = muxServer.Flag("tou-password", "password of parent tou's encrpyt/decrypt").Default("snail007's_goproxy").String()
//########mux-client######### //########mux-client#########
muxClient := app.Command("client", "proxy on mux client mode") 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.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 <tls|tcp|kcp>").Default("tls").Short('T').Enum("tls", "tcp", "kcp") muxClientArgs.ParentType = muxClient.Flag("parent-type", "parent protocol type <tls|tcp|tcps|kcp|tou>").Default("tls").Short('T').Enum("tls", "tcp", "tcps", "kcp", "tou")
muxClientArgs.CertFile = muxClient.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() 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.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.Timeout = muxClient.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
@ -162,6 +178,10 @@ func initConfig() (err error) {
muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp|tls 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() muxClientArgs.SessionCount = muxClient.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
muxClientArgs.Jumper = muxClient.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() muxClientArgs.Jumper = muxClient.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
muxClientArgs.TCPSMethod = muxClient.Flag("tcps-method", "method of parent tcps's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxClientArgs.TCPSPassword = muxClient.Flag("tcps-password", "password of parent tcps's encrpyt/decrypt").Default("snail007's_goproxy").String()
muxClientArgs.TOUMethod = muxClient.Flag("tou-method", "method of parent tou's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxClientArgs.TOUPassword = muxClient.Flag("tou-password", "password of parent tou's encrpyt/decrypt").Default("snail007's_goproxy").String()
//########mux-bridge######### //########mux-bridge#########
muxBridge := app.Command("bridge", "proxy on mux bridge mode") muxBridge := app.Command("bridge", "proxy on mux bridge mode")
@ -169,7 +189,11 @@ func initConfig() (err error) {
muxBridgeArgs.KeyFile = muxBridge.Flag("key", "key file for tls").Short('K').Default("proxy.key").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.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.Local = muxBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
muxBridgeArgs.LocalType = muxBridge.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tls").Short('t').Enum("tls", "tcp", "kcp") muxBridgeArgs.LocalType = muxBridge.Flag("local-type", "local protocol type <tls|tcp|tcps|kcp|tou>").Default("tls").Short('t').Enum("tls", "tcp", "tcps", "kcp", "tou")
muxBridgeArgs.TCPSMethod = muxBridge.Flag("tcps-method", "method of local tcps's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxBridgeArgs.TCPSPassword = muxBridge.Flag("tcps-password", "password of local tcps's encrpyt/decrypt").Default("snail007's_goproxy").String()
muxBridgeArgs.TOUMethod = muxBridge.Flag("tou-method", "method of local tou's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxBridgeArgs.TOUPassword = muxBridge.Flag("tou-password", "password of local tou's encrpyt/decrypt").Default("snail007's_goproxy").String()
//########tunnel-server######### //########tunnel-server#########
tunnelServer := app.Command("tserver", "proxy on tunnel server mode") tunnelServer := app.Command("tserver", "proxy on tunnel server mode")
@ -198,9 +222,9 @@ func initConfig() (err error) {
tunnelBridgeArgs.Timeout = tunnelBridge.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int() 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() tunnelBridgeArgs.Local = tunnelBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
//########ssh######### //########socks#########
socks := app.Command("socks", "proxy on ssh mode") 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.Parent = socks.Flag("parent", "parent ssh address, such as: \"23.32.32.19:22\"").Default("").Short('P').Strings()
socksArgs.ParentType = socks.Flag("parent-type", "parent protocol type <tls|tcp|kcp|ssh>").Default("tcp").Short('T').Enum("tls", "tcp", "kcp", "ssh") socksArgs.ParentType = socks.Flag("parent-type", "parent protocol type <tls|tcp|kcp|ssh>").Default("tcp").Short('T').Enum("tls", "tcp", "kcp", "ssh")
socksArgs.LocalType = socks.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") socksArgs.LocalType = socks.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp")
socksArgs.Local = socks.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() socksArgs.Local = socks.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
@ -210,7 +234,7 @@ func initConfig() (err error) {
socksArgs.SSHUser = socks.Flag("ssh-user", "user for ssh").Short('u').Default("").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.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.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.SSHPassword = socks.Flag("ssh-password", "password for ssh").Short('D').Default("").String()
socksArgs.Always = socks.Flag("always", "always use parent proxy").Default("false").Bool() 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.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.Interval = socks.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int()
@ -223,16 +247,26 @@ func initConfig() (err error) {
socksArgs.AuthURLTimeout = socks.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int() 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.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.AuthURLRetry = socks.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int()
socksArgs.ParentAuth = socks.Flag("parent-auth", "parent socks auth username and password, such as: -A user1:pass1").Short('A').String()
socksArgs.DNSAddress = socks.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() 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.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.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.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.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() socksArgs.ParentCompress = socks.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
socksArgs.Intelligent = socks.Flag("intelligent", "settting intelligent HTTP, SOCKS5 proxy mode, can be <intelligent|direct|parent>").Default("intelligent").Enum("intelligent", "direct", "parent")
socksArgs.LoadBalanceMethod = socks.Flag("lb-method", "load balance method when use multiple parent,can be <roundrobin|leastconn|leasttime|hash|weight>").Default("roundrobin").Enum("roundrobin", "weight", "leastconn", "leasttime", "hash")
socksArgs.LoadBalanceTimeout = socks.Flag("lb-timeout", "tcp milliseconds timeout of connecting to parent").Default("500").Int()
socksArgs.LoadBalanceRetryTime = socks.Flag("lb-retrytime", "sleep time milliseconds after checking").Default("1000").Int()
socksArgs.LoadBalanceHashTarget = socks.Flag("lb-hashtarget", "use target address to choose parent for LB").Default("false").Bool()
socksArgs.LoadBalanceOnlyHA = socks.Flag("lb-onlyha", "use only `high availability mode` to choose parent for LB").Default("false").Bool()
socksArgs.RateLimit = socks.Flag("rate-limit", "rate limit (bytes/second) of each connection, such as: 100K 1.5M . 0 means no limitation").Short('l').Default("0").String()
socksArgs.BindListen = socks.Flag("bind-listen", "using listener binding IP when connect to target").Short('B').Default("false").Bool()
socksArgs.Debug = isDebug
//########socks+http(s)######### //########sps#########
sps := app.Command("sps", "proxy on socks+http(s) mode") 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.Parent = sps.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').Strings()
spsArgs.CertFile = sps.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").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.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.CaCertFile = sps.Flag("ca", "ca cert file for tls").Default("").String()
@ -240,7 +274,7 @@ func initConfig() (err error) {
spsArgs.ParentType = sps.Flag("parent-type", "parent protocol type <tls|tcp|kcp>").Short('T').Enum("tls", "tcp", "kcp") spsArgs.ParentType = sps.Flag("parent-type", "parent protocol type <tls|tcp|kcp>").Short('T').Enum("tls", "tcp", "kcp")
spsArgs.LocalType = sps.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") spsArgs.LocalType = sps.Flag("local-type", "local protocol type <tls|tcp|kcp>").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.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 <http|socks>").Short('S').Enum("http", "socks") spsArgs.ParentServiceType = sps.Flag("parent-service-type", "parent service type <http|socks|ss>").Short('S').Enum("http", "socks", "ss")
spsArgs.DNSAddress = sps.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() 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.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.AuthFile = sps.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
@ -255,8 +289,22 @@ func initConfig() (err error) {
spsArgs.ParentKey = sps.Flag("parent-key", "the password for auto encrypt/decrypt parent 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.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() spsArgs.ParentCompress = sps.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
spsArgs.SSMethod = sps.Flag("ss-method", "the following methods are supported: aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, rc4-md5-6, chacha20, salsa20, rc4, table, des-cfb, chacha20-ietf; if you use ss client , \"-t tcp\" is required").Short('h').Default("aes-256-cfb").String()
spsArgs.SSKey = sps.Flag("ss-key", "if you use ss client , \"-t tcp\" is required").Short('j').Default("sspassword").String()
spsArgs.ParentSSMethod = sps.Flag("parent-ss-method", "the following methods are supported: aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, rc4-md5-6, chacha20, salsa20, rc4, table, des-cfb, chacha20-ietf; if you use ss server as parent, \"-T tcp\" is required").Short('H').Default("aes-256-cfb").String()
spsArgs.ParentSSKey = sps.Flag("parent-ss-key", "if you use ss server as parent, \"-T tcp\" is required").Short('J').Default("sspassword").String()
spsArgs.DisableHTTP = sps.Flag("disable-http", "disable http(s) proxy").Default("false").Bool() spsArgs.DisableHTTP = sps.Flag("disable-http", "disable http(s) proxy").Default("false").Bool()
spsArgs.DisableSocks5 = sps.Flag("disable-socks", "disable socks proxy").Default("false").Bool() spsArgs.DisableSocks5 = sps.Flag("disable-socks", "disable socks proxy").Default("false").Bool()
spsArgs.DisableSS = sps.Flag("disable-ss", "disable ss proxy").Default("false").Bool()
spsArgs.LoadBalanceMethod = sps.Flag("lb-method", "load balance method when use multiple parent,can be <roundrobin|leastconn|leasttime|hash|weight>").Default("roundrobin").Enum("roundrobin", "weight", "leastconn", "leasttime", "hash")
spsArgs.LoadBalanceTimeout = sps.Flag("lb-timeout", "tcp milliseconds timeout of connecting to parent").Default("500").Int()
spsArgs.LoadBalanceRetryTime = sps.Flag("lb-retrytime", "sleep time milliseconds after checking").Default("1000").Int()
spsArgs.LoadBalanceHashTarget = sps.Flag("lb-hashtarget", "use target address to choose parent for LB").Default("false").Bool()
spsArgs.LoadBalanceOnlyHA = sps.Flag("lb-onlyha", "use only `high availability mode` to choose parent for LB").Default("false").Bool()
spsArgs.RateLimit = sps.Flag("rate-limit", "rate limit (bytes/second) of each connection, such as: 100K 1.5M . 0 means no limitation").Short('l').Default("0").String()
spsArgs.Jumper = sps.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Default("").String()
spsArgs.ParentTLSSingle = sps.Flag("parent-tls-single", "conntect to parent insecure skip verify").Default("false").Bool()
spsArgs.Debug = isDebug
//########dns######### //########dns#########
dns := app.Command("dns", "proxy on dns server mode") dns := app.Command("dns", "proxy on dns server mode")
@ -287,8 +335,6 @@ func initConfig() (err error) {
//parse args //parse args
serviceName := kingpin.MustParse(app.Parse(os.Args[1:])) serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
isDebug = *debug
//set kcp config //set kcp config
switch *kcpArgs.Mode { switch *kcpArgs.Mode {
@ -345,7 +391,7 @@ func initConfig() (err error) {
log := logger.New(os.Stderr, "", logger.Ldate|logger.Ltime) log := logger.New(os.Stderr, "", logger.Ldate|logger.Ltime)
flags := logger.Ldate flags := logger.Ldate
if *debug { if *isDebug {
flags |= logger.Lshortfile | logger.Lmicroseconds flags |= logger.Lshortfile | logger.Lmicroseconds
cpuProfilingFile, _ = os.Create("cpu.prof") cpuProfilingFile, _ = os.Create("cpu.prof")
memProfilingFile, _ = os.Create("memory.prof") memProfilingFile, _ = os.Create("memory.prof")
@ -357,7 +403,6 @@ func initConfig() (err error) {
flags |= logger.Ltime flags |= logger.Ltime
} }
log.SetFlags(flags) log.SetFlags(flags)
if *nolog { if *nolog {
log.SetOutput(ioutil.Discard) log.SetOutput(ioutil.Discard)
} else if *logfile != "" { } else if *logfile != "" {
@ -391,6 +436,11 @@ func initConfig() (err error) {
} }
} }
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for { for {
if cmd != nil { if cmd != nil {
cmd.Process.Kill() cmd.Process.Kill()
@ -410,11 +460,21 @@ func initConfig() (err error) {
scanner := bufio.NewScanner(cmdReader) scanner := bufio.NewScanner(cmdReader)
scannerStdErr := bufio.NewScanner(cmdReaderStderr) scannerStdErr := bufio.NewScanner(cmdReaderStderr)
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for scanner.Scan() { for scanner.Scan() {
fmt.Println(scanner.Text()) fmt.Println(scanner.Text())
} }
}() }()
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for scannerStdErr.Scan() { for scannerStdErr.Scan() {
fmt.Println(scannerStdErr.Text()) fmt.Println(scannerStdErr.Text())
} }
@ -436,7 +496,7 @@ func initConfig() (err error) {
} }
if *logfile == "" { if *logfile == "" {
poster() poster()
if *debug { if *isDebug {
log.Println("[profiling] cpu profiling save to file : cpu.prof") log.Println("[profiling] cpu profiling save to file : cpu.prof")
log.Println("[profiling] memory profiling save to file : memory.prof") log.Println("[profiling] memory profiling save to file : memory.prof")
log.Println("[profiling] block profiling save to file : block.prof") log.Println("[profiling] block profiling save to file : block.prof")
@ -444,7 +504,7 @@ func initConfig() (err error) {
log.Println("[profiling] threadcreate profiling save to file : threadcreate.prof") log.Println("[profiling] threadcreate profiling save to file : threadcreate.prof")
} }
} }
//regist services and run service
//regist services and run service //regist services and run service
switch serviceName { switch serviceName {
case "http": case "http":
@ -454,11 +514,11 @@ func initConfig() (err error) {
case "udp": case "udp":
services.Regist(serviceName, udpx.NewUDP(), udpArgs, log) services.Regist(serviceName, udpx.NewUDP(), udpArgs, log)
case "tserver": case "tserver":
services.Regist(serviceName, tunnel.NewTunnelServerManager(), tunnelServerArgs, log) services.Regist(serviceName, tunnelx.NewTunnelServerManager(), tunnelServerArgs, log)
case "tclient": case "tclient":
services.Regist(serviceName, tunnel.NewTunnelClient(), tunnelClientArgs, log) services.Regist(serviceName, tunnelx.NewTunnelClient(), tunnelClientArgs, log)
case "tbridge": case "tbridge":
services.Regist(serviceName, tunnel.NewTunnelBridge(), tunnelBridgeArgs, log) services.Regist(serviceName, tunnelx.NewTunnelBridge(), tunnelBridgeArgs, log)
case "server": case "server":
services.Regist(serviceName, mux.NewMuxServerManager(), muxServerArgs, log) services.Regist(serviceName, mux.NewMuxServerManager(), muxServerArgs, log)
case "client": case "client":
@ -474,7 +534,6 @@ func initConfig() (err error) {
case "keygen": case "keygen":
services.Regist(serviceName, keygenx.NewKeygen(), keygenArgs, log) services.Regist(serviceName, keygenx.NewKeygen(), keygenArgs, log)
} }
service, err = services.Run(serviceName, nil) service, err = services.Run(serviceName, nil)
if err != nil { if err != nil {
log.Fatalf("run service [%s] fail, ERR:%s", serviceName, err) log.Fatalf("run service [%s] fail, ERR:%s", serviceName, err)
@ -483,16 +542,7 @@ func initConfig() (err error) {
} }
func poster() { func poster() {
fmt.Printf(` fmt.Printf(`Proxy Enterprise Version v%s`+" by snail , blog : http://www.host900.com/\n\n", APP_VERSION)
######## ######## ####### ## ## ## ##
## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ####
######## ######## ## ## ### ##
## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ##
## ## ## ####### ## ## ##
v%s`+" by snail , blog : http://www.host900.com/\n\n", APP_VERSION)
} }
func saveProfiling() { func saveProfiling() {
goroutine := pprof.Lookup("goroutine") goroutine := pprof.Lookup("goroutine")

135
core/cs/client/client.go Normal file
View File

@ -0,0 +1,135 @@
package client
import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"net"
"strconv"
"strings"
"time"
"github.com/snail007/goproxy/core/dst"
"github.com/snail007/goproxy/core/lib/kcpcfg"
compressconn "github.com/snail007/goproxy/core/lib/transport"
encryptconn "github.com/snail007/goproxy/core/lib/transport/encrypt"
kcp "github.com/xtaci/kcp-go"
)
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, caCertBytes)
}
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
}
_conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), time.Duration(timeout)*time.Millisecond)
if err != nil {
return
}
return *tls.Client(_conn, conf), err
}
func TlsConfig(certBytes, keyBytes, caCertBytes []byte) (conf *tls.Config, err error) {
return getRequestTlsConfig(certBytes, keyBytes, caCertBytes)
}
func getRequestTlsConfig(certBytes, keyBytes, caCertBytes []byte) (conf *tls.Config, err error) {
var cert tls.Certificate
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")
}
x509Cert, _ := x509.ParseCertificate(block.Bytes)
if x509Cert == nil {
panic("failed to parse block")
}
conf = &tls.Config{
RootCAs: serverCertPool,
Certificates: []tls.Certificate{cert},
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
},
}
return
}
func TCPConnectHost(hostAndPort string, timeout int) (conn net.Conn, err error) {
conn, err = net.DialTimeout("tcp", hostAndPort, time.Duration(timeout)*time.Millisecond)
return
}
func TCPSConnectHost(hostAndPort string, method, password string, compress bool, timeout int) (conn net.Conn, err error) {
conn, err = net.DialTimeout("tcp", hostAndPort, time.Duration(timeout)*time.Millisecond)
if err != nil {
return
}
if compress {
conn = compressconn.NewCompConn(conn)
}
conn, err = encryptconn.NewConn(conn, method, password)
return
}
func TOUConnectHost(hostAndPort string, method, password string, compress bool, timeout int) (conn net.Conn, err error) {
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{})
if err != nil {
panic(err)
}
// Create a DST mux around the packet connection with the default max
// packet size.
mux := dst.NewMux(udpConn, 0)
conn, err = mux.Dial("dst", hostAndPort)
if compress {
conn = compressconn.NewCompConn(conn)
}
conn, err = encryptconn.NewConn(conn, method, password)
return
}
func KCPConnectHost(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.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 compressconn.NewCompStream(kcpconn), err
}

341
core/cs/server/server.go Normal file
View File

@ -0,0 +1,341 @@
package server
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
logger "log"
"net"
"runtime/debug"
"strconv"
tou "github.com/snail007/goproxy/core/dst"
compressconn "github.com/snail007/goproxy/core/lib/transport"
transportc "github.com/snail007/goproxy/core/lib/transport"
encryptconn "github.com/snail007/goproxy/core/lib/transport/encrypt"
"github.com/snail007/goproxy/core/lib/kcpcfg"
kcp "github.com/xtaci/kcp-go"
)
func init() {
}
type ServerChannel struct {
ip string
port int
Listener *net.Listener
UDPListener *net.UDPConn
errAcceptHandler func(err error)
log *logger.Logger
TOUServer *tou.Mux
}
func NewServerChannel(ip string, port int, log *logger.Logger) ServerChannel {
return ServerChannel{
ip: ip,
port: port,
log: log,
errAcceptHandler: func(err error) {
log.Printf("accept error , ERR:%s", err)
},
}
}
func NewServerChannelHost(host string, log *logger.Logger) ServerChannel {
h, port, _ := net.SplitHostPort(host)
p, _ := strconv.Atoi(port)
return ServerChannel{
ip: h,
port: p,
log: log,
errAcceptHandler: func(err error) {
log.Printf("accept error , ERR:%s", err)
},
}
}
func (s *ServerChannel) SetErrAcceptHandler(fn func(err error)) {
s.errAcceptHandler = fn
}
func (s *ServerChannel) ListenSingleTLS(certBytes, keyBytes, caCertBytes []byte, fn func(conn net.Conn)) (err error) {
return s._ListenTLS(certBytes, keyBytes, caCertBytes, fn, true)
}
func (s *ServerChannel) ListenTLS(certBytes, keyBytes, caCertBytes []byte, fn func(conn net.Conn)) (err error) {
return s._ListenTLS(certBytes, keyBytes, caCertBytes, fn, false)
}
func (s *ServerChannel) _ListenTLS(certBytes, keyBytes, caCertBytes []byte, fn func(conn net.Conn), single bool) (err error) {
s.Listener, err = s.listenTLS(s.ip, s.port, certBytes, keyBytes, caCertBytes, single)
if err == nil {
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("ListenTLS crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
var conn net.Conn
conn, err = (*s.Listener).Accept()
if err == nil {
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("tls connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(conn)
}()
} else {
s.errAcceptHandler(err)
(*s.Listener).Close()
break
}
}
}()
}
return
}
func (s *ServerChannel) listenTLS(ip string, port int, certBytes, keyBytes, caCertBytes []byte, single bool) (ln *net.Listener, err error) {
var cert tls.Certificate
cert, err = tls.X509KeyPair(certBytes, keyBytes)
if err != nil {
return
}
config := &tls.Config{
Certificates: []tls.Certificate{cert},
}
if !single {
clientCertPool := x509.NewCertPool()
caBytes := certBytes
if caCertBytes != nil {
caBytes = caCertBytes
}
ok := clientCertPool.AppendCertsFromPEM(caBytes)
if !ok {
err = errors.New("failed to parse root certificate")
}
config.ClientCAs = clientCertPool
config.ClientAuth = tls.RequireAndVerifyClientCert
}
_ln, err := tls.Listen("tcp", net.JoinHostPort(ip, fmt.Sprintf("%d", port)), config)
if err == nil {
ln = &_ln
}
return
}
func (s *ServerChannel) ListenTCPS(method, password string, compress bool, fn func(conn net.Conn)) (err error) {
_, err = encryptconn.NewCipher(method, password)
if err != nil {
return
}
return s.ListenTCP(func(c net.Conn) {
if compress {
c = transportc.NewCompConn(c)
}
c, _ = encryptconn.NewConn(c, method, password)
fn(c)
})
}
func (s *ServerChannel) ListenTCP(fn func(conn net.Conn)) (err error) {
var l net.Listener
l, err = net.Listen("tcp", net.JoinHostPort(s.ip, fmt.Sprintf("%d", s.port)))
if err == nil {
s.Listener = &l
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("ListenTCP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
var conn net.Conn
conn, err = (*s.Listener).Accept()
if err == nil {
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("tcp connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(conn)
}()
} else {
s.errAcceptHandler(err)
(*s.Listener).Close()
break
}
}
}()
}
return
}
func (s *ServerChannel) ListenUDP(fn func(listener *net.UDPConn, packet []byte, localAddr, srcAddr *net.UDPAddr)) (err error) {
addr := &net.UDPAddr{IP: net.ParseIP(s.ip), Port: s.port}
l, err := net.ListenUDP("udp", addr)
if err == nil {
s.UDPListener = l
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("ListenUDP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
var buf = make([]byte, 2048)
n, srcAddr, err := (*s.UDPListener).ReadFromUDP(buf)
if err == nil {
packet := buf[0:n]
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp data handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(s.UDPListener, packet, addr, srcAddr)
}()
} else {
s.errAcceptHandler(err)
(*s.UDPListener).Close()
break
}
}
}()
}
return
}
func (s *ServerChannel) ListenKCP(config kcpcfg.KCPConfigArgs, fn func(conn net.Conn), log *logger.Logger) (err error) {
lis, err := kcp.ListenWithOptions(net.JoinHostPort(s.ip, fmt.Sprintf("%d", s.port)), config.Block, *config.DataShard, *config.ParityShard)
if err == nil {
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
}
s.Listener = new(net.Listener)
*s.Listener = lis
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("ListenKCP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
conn, err := lis.AcceptKCP()
if err == nil {
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("kcp connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
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 := transportc.NewCompStream(conn)
fn(cconn)
}
}()
} else {
s.errAcceptHandler(err)
(*s.Listener).Close()
break
}
}
}()
}
return
}
func (s *ServerChannel) ListenTOU(method, password string, compress bool, fn func(conn net.Conn)) (err error) {
addr := &net.UDPAddr{IP: net.ParseIP(s.ip), Port: s.port}
s.UDPListener, err = net.ListenUDP("udp", addr)
if err != nil {
s.log.Println(err)
return
}
s.TOUServer = tou.NewMux(s.UDPListener, 0)
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("ListenRUDP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
var conn net.Conn
conn, err = (*s.TOUServer).Accept()
if err == nil {
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("tcp connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
if compress {
conn = compressconn.NewCompConn(conn)
}
conn, err = encryptconn.NewConn(conn, method, password)
if err != nil {
conn.Close()
s.log.Println(err)
return
}
fn(conn)
}()
} else {
s.errAcceptHandler(err)
s.TOUServer.Close()
s.UDPListener.Close()
break
}
}
}()
return
}
func (s *ServerChannel) Close() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("close crashed :\n%s\n%s", e, string(debug.Stack()))
}
}()
if s.Listener != nil && *s.Listener != nil {
(*s.Listener).Close()
}
if s.TOUServer != nil {
s.TOUServer.Close()
}
if s.UDPListener != nil {
s.UDPListener.Close()
}
}
func (s *ServerChannel) Addr() string {
defer func() {
if e := recover(); e != nil {
s.log.Printf("close crashed :\n%s\n%s", e, string(debug.Stack()))
}
}()
if s.Listener != nil && *s.Listener != nil {
return (*s.Listener).Addr().String()
}
if s.UDPListener != nil {
return s.UDPListener.LocalAddr().String()
}
return ""
}

View File

@ -0,0 +1,49 @@
package tests
import (
"log"
"net"
"os"
"testing"
ctransport "github.com/snail007/goproxy/core/cs/client"
stransport "github.com/snail007/goproxy/core/cs/server"
)
func TestTCPS(t *testing.T) {
l := log.New(os.Stderr, "", log.LstdFlags)
s := stransport.NewServerChannelHost(":", l)
err := s.ListenTCPS("aes-256-cfb", "password", true, func(inconn net.Conn) {
buf := make([]byte, 2048)
_, err := inconn.Read(buf)
if err != nil {
t.Error(err)
return
}
_, err = inconn.Write([]byte("okay"))
if err != nil {
t.Error(err)
return
}
})
if err != nil {
t.Fatal(err)
}
client, err := ctransport.TCPSConnectHost((*s.Listener).Addr().String(), "aes-256-cfb", "password", true, 1000)
if err != nil {
t.Fatal(err)
}
defer client.Close()
_, err = client.Write([]byte("test"))
if err != nil {
t.Fatal(err)
}
b := make([]byte, 20)
n, err := client.Read(b)
if err != nil {
t.Fatal(err)
}
if string(b[:n]) != "okay" {
t.Fatalf("client revecive okay excepted,revecived : %s", string(b[:n]))
}
}

603
core/dst/conn.go Normal file
View File

@ -0,0 +1,603 @@
// Copyright 2014 The DST Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package dst
import (
"bytes"
crand "crypto/rand"
"encoding/binary"
"fmt"
"io"
"runtime/debug"
"math/rand"
"net"
"sync"
"sync/atomic"
"time"
)
const (
defExpTime = 100 * time.Millisecond // N * (4 * RTT + RTTVar + SYN)
expCountClose = 8 // close connection after this many Exps
minTimeClose = 5 * time.Second // if at least this long has passed
maxInputBuffer = 8 << 20 // bytes
muxBufferPackets = 128 // buffer size of channel between mux and reader routine
rttMeasureWindow = 32 // number of packets to track for RTT averaging
rttMeasureSample = 128 // Sample every ... packet for RTT
// number of bytes to subtract from MTU when chunking data, to try to
// avoid fragmentation
sliceOverhead = 8 /*pppoe, similar*/ + 20 /*ipv4*/ + 8 /*udp*/ + 16 /*dst*/
)
func init() {
// Properly seed the random number generator that we use for sequence
// numbers and stuff.
buf := make([]byte, 8)
if n, err := crand.Read(buf); n != 8 || err != nil {
panic("init random failure")
}
rand.Seed(int64(binary.BigEndian.Uint64(buf)))
}
// TODO: export this interface when it's usable from the outside
type congestionController interface {
Ack()
NegAck()
Exp()
SendWindow() int
PacketRate() int // PPS
UpdateRTT(time.Duration)
}
// Conn is an SDT connection carried over a Mux.
type Conn struct {
// Set at creation, thereafter immutable:
mux *Mux
dst net.Addr
connID connectionID
remoteConnID connectionID
in chan packet
cc congestionController
packetSize int
closed chan struct{}
closeOnce sync.Once
// Touched by more than one goroutine, needs locking.
nextSeqNoMut sync.Mutex
nextSeqNo sequenceNo
inbufMut sync.Mutex
inbufCond *sync.Cond
inbuf bytes.Buffer
expMut sync.Mutex
exp *time.Timer
sendBuffer *sendBuffer // goroutine safe
packetDelays [rttMeasureWindow]time.Duration
packetDelaysSlot int
packetDelaysMut sync.Mutex
// Owned by the reader routine, needs no locking
recvBuffer packetList
nextRecvSeqNo sequenceNo
lastAckedSeqNo sequenceNo
lastNegAckedSeqNo sequenceNo
expCount int
expReset time.Time
// Only accessed atomically
packetsIn int64
packetsOut int64
bytesIn int64
bytesOut int64
resentPackets int64
droppedPackets int64
outOfOrderPackets int64
// Special
debugResetRecvSeqNo chan sequenceNo
}
func newConn(m *Mux, dst net.Addr) *Conn {
conn := &Conn{
mux: m,
dst: dst,
nextSeqNo: sequenceNo(rand.Uint32()),
packetSize: maxPacketSize,
in: make(chan packet, muxBufferPackets),
closed: make(chan struct{}),
sendBuffer: newSendBuffer(m),
exp: time.NewTimer(defExpTime),
debugResetRecvSeqNo: make(chan sequenceNo),
expReset: time.Now(),
}
conn.lastAckedSeqNo = conn.nextSeqNo - 1
conn.inbufCond = sync.NewCond(&conn.inbufMut)
conn.cc = newWindowCC()
conn.sendBuffer.SetWindowAndRate(conn.cc.SendWindow(), conn.cc.PacketRate())
conn.recvBuffer.Resize(128)
return conn
}
func (c *Conn) start() {
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
}
}()
c.reader()
}()
}
func (c *Conn) reader() {
if debugConnection {
log.Println(c, "reader() starting")
defer log.Println(c, "reader() exiting")
}
for {
select {
case <-c.closed:
// Ack any received but not yet acked messages.
c.sendAck(0)
// Send a shutdown message.
c.nextSeqNoMut.Lock()
c.mux.write(packet{
src: c.connID,
dst: c.dst,
hdr: header{
packetType: typeShutdown,
connID: c.remoteConnID,
sequenceNo: c.nextSeqNo,
},
})
c.nextSeqNo++
c.nextSeqNoMut.Unlock()
atomic.AddInt64(&c.packetsOut, 1)
atomic.AddInt64(&c.bytesOut, dstHeaderLen)
return
case pkt := <-c.in:
atomic.AddInt64(&c.packetsIn, 1)
atomic.AddInt64(&c.bytesIn, dstHeaderLen+int64(len(pkt.data)))
c.expCount = 1
switch pkt.hdr.packetType {
case typeData:
c.rcvData(pkt)
case typeAck:
c.rcvAck(pkt)
case typeNegAck:
c.rcvNegAck(pkt)
case typeShutdown:
c.rcvShutdown(pkt)
default:
log.Println("Unhandled packet", pkt)
continue
}
case <-c.exp.C:
c.eventExp()
c.resetExp()
case n := <-c.debugResetRecvSeqNo:
// Back door for testing
c.lastAckedSeqNo = n - 1
c.nextRecvSeqNo = n
}
}
}
func (c *Conn) eventExp() {
c.expCount++
if c.sendBuffer.lost.Len() > 0 || c.sendBuffer.send.Len() > 0 {
c.cc.Exp()
c.sendBuffer.SetWindowAndRate(c.cc.SendWindow(), c.cc.PacketRate())
c.sendBuffer.ScheduleResend()
if debugConnection {
log.Println(c, "did resends due to Exp")
}
if c.expCount > expCountClose && time.Since(c.expReset) > minTimeClose {
if debugConnection {
log.Println(c, "close due to Exp")
}
// We're shutting down due to repeated exp:s. Don't wait for the
// send buffer to drain, which it would otherwise do in
// c.Close()..
c.sendBuffer.CrashStop()
c.Close()
}
}
}
func (c *Conn) rcvAck(pkt packet) {
ack := pkt.hdr.sequenceNo
if debugConnection {
log.Printf("%v read Ack %v", c, ack)
}
c.cc.Ack()
if ack%rttMeasureSample == 0 {
if ts := timestamp(binary.BigEndian.Uint32(pkt.data)); ts > 0 {
if delay := time.Duration(timestampMicros()-ts) * time.Microsecond; delay > 0 {
c.packetDelaysMut.Lock()
c.packetDelays[c.packetDelaysSlot] = delay
c.packetDelaysSlot = (c.packetDelaysSlot + 1) % len(c.packetDelays)
c.packetDelaysMut.Unlock()
if rtt, n := c.averageDelay(); n > 8 {
c.cc.UpdateRTT(rtt)
}
}
}
}
c.sendBuffer.Acknowledge(ack)
c.sendBuffer.SetWindowAndRate(c.cc.SendWindow(), c.cc.PacketRate())
c.resetExp()
}
func (c *Conn) averageDelay() (time.Duration, int) {
var total time.Duration
var n int
c.packetDelaysMut.Lock()
for _, d := range c.packetDelays {
if d != 0 {
total += d
n++
}
}
c.packetDelaysMut.Unlock()
if n == 0 {
return 0, 0
}
return total / time.Duration(n), n
}
func (c *Conn) rcvNegAck(pkt packet) {
nak := pkt.hdr.sequenceNo
if debugConnection {
log.Printf("%v read NegAck %v", c, nak)
}
c.sendBuffer.NegativeAck(nak)
//c.cc.NegAck()
c.resetExp()
}
func (c *Conn) rcvShutdown(pkt packet) {
// XXX: We accept shutdown packets somewhat from the future since the
// sender will number the shutdown after any packets that might still be
// in the write buffer. This should be fixed to let the write buffer empty
// on close and reduce the window here.
if pkt.LessSeq(c.nextRecvSeqNo + 128) {
if debugConnection {
log.Println(c, "close due to shutdown")
}
c.Close()
}
}
func (c *Conn) rcvData(pkt packet) {
if debugConnection {
log.Println(c, "recv data", pkt.hdr)
}
if pkt.LessSeq(c.nextRecvSeqNo) {
if debugConnection {
log.Printf("%v old packet received; seq %v, expected %v", c, pkt.hdr.sequenceNo, c.nextRecvSeqNo)
}
atomic.AddInt64(&c.droppedPackets, 1)
return
}
if debugConnection {
log.Println(c, "into recv buffer:", pkt)
}
c.recvBuffer.InsertSorted(pkt)
if c.recvBuffer.LowestSeq() == c.nextRecvSeqNo {
for _, pkt := range c.recvBuffer.PopSequence(^sequenceNo(0)) {
if debugConnection {
log.Println(c, "from recv buffer:", pkt)
}
// An in-sequence packet.
c.nextRecvSeqNo = pkt.hdr.sequenceNo + 1
c.sendAck(pkt.hdr.timestamp)
c.inbufMut.Lock()
for c.inbuf.Len() > len(pkt.data)+maxInputBuffer {
c.inbufCond.Wait()
select {
case <-c.closed:
return
default:
}
}
c.inbuf.Write(pkt.data)
c.inbufCond.Broadcast()
c.inbufMut.Unlock()
}
} else {
if debugConnection {
log.Printf("%v lost; seq %v, expected %v", c, pkt.hdr.sequenceNo, c.nextRecvSeqNo)
}
c.recvBuffer.InsertSorted(pkt)
c.sendNegAck()
atomic.AddInt64(&c.outOfOrderPackets, 1)
}
}
func (c *Conn) sendAck(ts timestamp) {
if c.lastAckedSeqNo == c.nextRecvSeqNo {
return
}
var buf [4]byte
binary.BigEndian.PutUint32(buf[:], uint32(ts))
c.mux.write(packet{
src: c.connID,
dst: c.dst,
hdr: header{
packetType: typeAck,
connID: c.remoteConnID,
sequenceNo: c.nextRecvSeqNo,
},
data: buf[:],
})
atomic.AddInt64(&c.packetsOut, 1)
atomic.AddInt64(&c.bytesOut, dstHeaderLen)
if debugConnection {
log.Printf("%v send Ack %v", c, c.nextRecvSeqNo)
}
c.lastAckedSeqNo = c.nextRecvSeqNo
}
func (c *Conn) sendNegAck() {
if c.lastNegAckedSeqNo == c.nextRecvSeqNo {
return
}
c.mux.write(packet{
src: c.connID,
dst: c.dst,
hdr: header{
packetType: typeNegAck,
connID: c.remoteConnID,
sequenceNo: c.nextRecvSeqNo,
},
})
atomic.AddInt64(&c.packetsOut, 1)
atomic.AddInt64(&c.bytesOut, dstHeaderLen)
if debugConnection {
log.Printf("%v send NegAck %v", c, c.nextRecvSeqNo)
}
c.lastNegAckedSeqNo = c.nextRecvSeqNo
}
func (c *Conn) resetExp() {
d, _ := c.averageDelay()
d = d*4 + 10*time.Millisecond
if d < defExpTime {
d = defExpTime
}
c.expMut.Lock()
c.exp.Reset(d)
c.expMut.Unlock()
}
// String returns a string representation of the connection.
func (c *Conn) String() string {
return fmt.Sprintf("%v/%v/%v", c.connID, c.LocalAddr(), c.RemoteAddr())
}
// Read reads data from the connection.
// Read can be made to time out and return a Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetReadDeadline.
func (c *Conn) Read(b []byte) (n int, err error) {
defer func() {
if e := recover(); e != nil {
n = 0
err = io.EOF
}
}()
c.inbufMut.Lock()
defer c.inbufMut.Unlock()
for c.inbuf.Len() == 0 {
select {
case <-c.closed:
return 0, io.EOF
default:
}
c.inbufCond.Wait()
}
return c.inbuf.Read(b)
}
// Write writes data to the connection.
// Write can be made to time out and return a Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
func (c *Conn) Write(b []byte) (n int, err error) {
select {
case <-c.closed:
return 0, ErrClosedConn
default:
}
sent := 0
sliceSize := c.packetSize - sliceOverhead
for i := 0; i < len(b); i += sliceSize {
nxt := i + sliceSize
if nxt > len(b) {
nxt = len(b)
}
slice := b[i:nxt]
sliceCopy := c.mux.buffers.Get().([]byte)[:len(slice)]
copy(sliceCopy, slice)
c.nextSeqNoMut.Lock()
pkt := packet{
src: c.connID,
dst: c.dst,
hdr: header{
packetType: typeData,
sequenceNo: c.nextSeqNo,
connID: c.remoteConnID,
},
data: sliceCopy,
}
c.nextSeqNo++
c.nextSeqNoMut.Unlock()
if err := c.sendBuffer.Write(pkt); err != nil {
return sent, err
}
atomic.AddInt64(&c.packetsOut, 1)
atomic.AddInt64(&c.bytesOut, int64(len(slice)+dstHeaderLen))
sent += len(slice)
c.resetExp()
}
return sent, nil
}
// Close closes the connection.
// Any blocked Read or Write operations will be unblocked and return errors.
func (c *Conn) Close() error {
defer func() {
_ = recover()
}()
c.closeOnce.Do(func() {
if debugConnection {
log.Println(c, "explicit close start")
defer log.Println(c, "explicit close done")
}
// XXX: Ugly hack to implement lingering sockets...
time.Sleep(4 * defExpTime)
c.sendBuffer.Stop()
c.mux.removeConn(c)
close(c.closed)
c.inbufMut.Lock()
c.inbufCond.Broadcast()
c.inbufMut.Unlock()
})
return nil
}
// LocalAddr returns the local network address.
func (c *Conn) LocalAddr() net.Addr {
return c.mux.Addr()
}
// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
return c.dst
}
// SetDeadline sets the read and write deadlines associated
// with the connection. It is equivalent to calling both
// SetReadDeadline and SetWriteDeadline.
//
// A deadline is an absolute time after which I/O operations
// fail with a timeout (see type Error) instead of
// blocking. The deadline applies to all future I/O, not just
// the immediately following call to Read or Write.
//
// An idle timeout can be implemented by repeatedly extending
// the deadline after successful Read or Write calls.
//
// A zero value for t means I/O operations will not time out.
//
// BUG(jb): SetDeadline is not implemented.
func (c *Conn) SetDeadline(t time.Time) error {
return ErrNotImplemented
}
// SetReadDeadline sets the deadline for future Read calls.
// A zero value for t means Read will not time out.
//
// BUG(jb): SetReadDeadline is not implemented.
func (c *Conn) SetReadDeadline(t time.Time) error {
return ErrNotImplemented
}
// SetWriteDeadline sets the deadline for future Write calls.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
// A zero value for t means Write will not time out.
//
// BUG(jb): SetWriteDeadline is not implemented.
func (c *Conn) SetWriteDeadline(t time.Time) error {
return ErrNotImplemented
}
type Statistics struct {
DataPacketsIn int64
DataPacketsOut int64
DataBytesIn int64
DataBytesOut int64
ResentPackets int64
DroppedPackets int64
OutOfOrderPackets int64
}
// String returns a printable represetnation of the Statistics.
func (s Statistics) String() string {
return fmt.Sprintf("PktsIn: %d, PktsOut: %d, BytesIn: %d, BytesOut: %d, PktsResent: %d, PktsDropped: %d, PktsOutOfOrder: %d",
s.DataPacketsIn, s.DataPacketsOut, s.DataBytesIn, s.DataBytesOut, s.ResentPackets, s.DroppedPackets, s.OutOfOrderPackets)
}
// GetStatistics returns a snapsht of the current connection statistics.
func (c *Conn) GetStatistics() Statistics {
return Statistics{
DataPacketsIn: atomic.LoadInt64(&c.packetsIn),
DataPacketsOut: atomic.LoadInt64(&c.packetsOut),
DataBytesIn: atomic.LoadInt64(&c.bytesIn),
DataBytesOut: atomic.LoadInt64(&c.bytesOut),
ResentPackets: atomic.LoadInt64(&c.resentPackets),
DroppedPackets: atomic.LoadInt64(&c.droppedPackets),
OutOfOrderPackets: atomic.LoadInt64(&c.outOfOrderPackets),
}
}

29
core/dst/cookie.go Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2014 The DST Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package dst
import (
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"net"
)
var cookieKey = make([]byte, 16)
func init() {
_, err := rand.Reader.Read(cookieKey)
if err != nil {
panic(err)
}
}
func cookie(remote net.Addr) uint32 {
hash := sha256.New()
hash.Write([]byte(remote.String()))
hash.Write(cookieKey)
bs := hash.Sum(nil)
return binary.BigEndian.Uint32(bs)
}

26
core/dst/debug.go Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2014 The DST Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package dst
import (
"os"
"strings"
)
var (
debugConnection bool
debugMux bool
debugCC bool
)
func init() {
debug := make(map[string]bool)
for _, s := range strings.Split(os.Getenv("DSTDEBUG"), ",") {
debug[strings.TrimSpace(s)] = true
}
debugConnection = debug["conn"]
debugMux = debug["mux"]
debugCC = debug["cc"]
}

12
core/dst/doc.go Normal file
View File

@ -0,0 +1,12 @@
// Copyright 2014 The DST Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
/*
Package dst implements the Datagram Stream Transfer protocol.
DST is a way to get reliable stream connections (like TCP) on top of UDP.
*/
package dst

23
core/dst/errors.go Normal file
View File

@ -0,0 +1,23 @@
// Copyright 2014 The DST Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package dst
// Error represents the various dst-internal error conditions.
type Error struct {
Err string
}
// Error returns a string representation of the error.
func (e Error) Error() string {
return e.Err
}
var (
ErrClosedConn = &Error{"operation on closed connection"}
ErrClosedMux = &Error{"operation on closed mux"}
ErrHandshakeTimeout = &Error{"handshake timeout"}
ErrNotDST = &Error{"network is not dst"}
ErrNotImplemented = &Error{"not implemented"}
)

429
core/dst/mux.go Normal file
View File

@ -0,0 +1,429 @@
// Copyright 2014 The DST Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package dst
import (
"fmt"
"runtime/debug"
"net"
"sync"
"time"
)
const (
maxIncomingRequests = 1024
maxPacketSize = 500
handshakeTimeout = 5 * time.Second
handshakeInterval = 1 * time.Second
)
// Mux is a UDP multiplexer of DST connections.
type Mux struct {
conn net.PacketConn
packetSize int
conns map[connectionID]*Conn
handshakes map[connectionID]chan packet
connsMut sync.Mutex
incoming chan *Conn
closed chan struct{}
closeOnce sync.Once
buffers *sync.Pool
}
// NewMux creates a new DST Mux on top of a packet connection.
func NewMux(conn net.PacketConn, packetSize int) *Mux {
if packetSize <= 0 {
packetSize = maxPacketSize
}
m := &Mux{
conn: conn,
packetSize: packetSize,
conns: map[connectionID]*Conn{},
handshakes: make(map[connectionID]chan packet),
incoming: make(chan *Conn, maxIncomingRequests),
closed: make(chan struct{}),
buffers: &sync.Pool{
New: func() interface{} {
return make([]byte, packetSize)
},
},
}
// Attempt to maximize buffer space. Start at 16 MB and work downwards 0.5
// MB at a time.
if conn, ok := conn.(*net.UDPConn); ok {
for buf := 16384 * 1024; buf >= 512*1024; buf -= 512 * 1024 {
err := conn.SetReadBuffer(buf)
if err == nil {
if debugMux {
log.Println(m, "read buffer is", buf)
}
break
}
}
for buf := 16384 * 1024; buf >= 512*1024; buf -= 512 * 1024 {
err := conn.SetWriteBuffer(buf)
if err == nil {
if debugMux {
log.Println(m, "write buffer is", buf)
}
break
}
}
}
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
m.readerLoop()
}()
return m
}
// Accept waits for and returns the next connection to the listener.
func (m *Mux) Accept() (net.Conn, error) {
return m.AcceptDST()
}
// AcceptDST waits for and returns the next connection to the listener.
func (m *Mux) AcceptDST() (*Conn, error) {
conn, ok := <-m.incoming
if !ok {
return nil, ErrClosedMux
}
return conn, nil
}
// Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors.
func (m *Mux) Close() error {
var err error = ErrClosedMux
m.closeOnce.Do(func() {
err = m.conn.Close()
close(m.incoming)
close(m.closed)
})
return err
}
// Addr returns the listener's network address.
func (m *Mux) Addr() net.Addr {
return m.conn.LocalAddr()
}
// Dial connects to the address on the named network.
//
// Network must be "dst".
//
// Addresses have the form host:port. If host is a literal IPv6 address or
// host name, it must be enclosed in square brackets as in "[::1]:80",
// "[ipv6-host]:http" or "[ipv6-host%zone]:80". The functions JoinHostPort and
// SplitHostPort manipulate addresses in this form.
//
// Examples:
// Dial("dst", "12.34.56.78:80")
// Dial("dst", "google.com:http")
// Dial("dst", "[2001:db8::1]:http")
// Dial("dst", "[fe80::1%lo0]:80")
func (m *Mux) Dial(network, addr string) (net.Conn, error) {
return m.DialDST(network, addr)
}
// Dial connects to the address on the named network.
//
// Network must be "dst".
//
// Addresses have the form host:port. If host is a literal IPv6 address or
// host name, it must be enclosed in square brackets as in "[::1]:80",
// "[ipv6-host]:http" or "[ipv6-host%zone]:80". The functions JoinHostPort and
// SplitHostPort manipulate addresses in this form.
//
// Examples:
// Dial("dst", "12.34.56.78:80")
// Dial("dst", "google.com:http")
// Dial("dst", "[2001:db8::1]:http")
// Dial("dst", "[fe80::1%lo0]:80")
func (m *Mux) DialDST(network, addr string) (*Conn, error) {
if network != "dst" {
return nil, ErrNotDST
}
dst, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
resp := make(chan packet)
m.connsMut.Lock()
connID := m.newConnID()
m.handshakes[connID] = resp
m.connsMut.Unlock()
conn, err := m.clientHandshake(dst, connID, resp)
m.connsMut.Lock()
defer m.connsMut.Unlock()
delete(m.handshakes, connID)
if err != nil {
return nil, err
}
m.conns[connID] = conn
return conn, nil
}
// handshake performs the client side handshake (i.e. Dial)
func (m *Mux) clientHandshake(dst net.Addr, connID connectionID, resp chan packet) (*Conn, error) {
if debugMux {
log.Printf("%v dial %v connID %v", m, dst, connID)
}
nextHandshake := time.NewTimer(0)
defer nextHandshake.Stop()
handshakeTimeout := time.NewTimer(handshakeTimeout)
defer handshakeTimeout.Stop()
var remoteCookie uint32
seqNo := randomSeqNo()
for {
select {
case <-m.closed:
// Failure. The mux has been closed.
return nil, ErrClosedConn
case <-handshakeTimeout.C:
// Handshake timeout. Close and abort.
return nil, ErrHandshakeTimeout
case <-nextHandshake.C:
// Send a handshake request.
m.write(packet{
src: connID,
dst: dst,
hdr: header{
packetType: typeHandshake,
flags: flagRequest,
connID: 0,
sequenceNo: seqNo,
timestamp: timestampMicros(),
},
data: handshakeData{uint32(m.packetSize), connID, remoteCookie}.marshal(),
})
nextHandshake.Reset(handshakeInterval)
case pkt := <-resp:
hd := unmarshalHandshakeData(pkt.data)
if pkt.hdr.flags&flagCookie == flagCookie {
// We should resend the handshake request with a different cookie value.
remoteCookie = hd.cookie
nextHandshake.Reset(0)
} else if pkt.hdr.flags&flagResponse == flagResponse {
// Successfull handshake response.
conn := newConn(m, dst)
conn.connID = connID
conn.remoteConnID = hd.connID
conn.nextRecvSeqNo = pkt.hdr.sequenceNo + 1
conn.packetSize = int(hd.packetSize)
if conn.packetSize > m.packetSize {
conn.packetSize = m.packetSize
}
conn.nextSeqNo = seqNo + 1
conn.start()
return conn, nil
}
}
}
}
func (m *Mux) readerLoop() {
buf := make([]byte, m.packetSize)
for {
buf = buf[:cap(buf)]
n, from, err := m.conn.ReadFrom(buf)
if err != nil {
m.Close()
return
}
buf = buf[:n]
hdr := unmarshalHeader(buf)
var bufCopy []byte
if len(buf) > dstHeaderLen {
bufCopy = m.buffers.Get().([]byte)[:len(buf)-dstHeaderLen]
copy(bufCopy, buf[dstHeaderLen:])
}
pkt := packet{hdr: hdr, data: bufCopy}
if debugMux {
log.Println(m, "read", pkt)
}
if hdr.packetType == typeHandshake {
m.incomingHandshake(from, hdr, bufCopy)
} else {
m.connsMut.Lock()
conn, ok := m.conns[hdr.connID]
m.connsMut.Unlock()
if ok {
conn.in <- packet{
dst: nil,
hdr: hdr,
data: bufCopy,
}
} else if debugMux && hdr.packetType != typeShutdown {
log.Printf("packet %v for unknown conn %v", hdr, hdr.connID)
}
}
}
}
func (m *Mux) incomingHandshake(from net.Addr, hdr header, data []byte) {
if hdr.connID == 0 {
// A new incoming handshake request.
m.incomingHandshakeRequest(from, hdr, data)
} else {
// A response to an ongoing handshake.
m.incomingHandshakeResponse(from, hdr, data)
}
}
func (m *Mux) incomingHandshakeRequest(from net.Addr, hdr header, data []byte) {
if hdr.flags&flagRequest != flagRequest {
log.Printf("Handshake pattern with flags 0x%x to connID zero", hdr.flags)
return
}
hd := unmarshalHandshakeData(data)
correctCookie := cookie(from)
if hd.cookie != correctCookie {
// Incorrect or missing SYN cookie. Send back a handshake
// with the expected one.
m.write(packet{
dst: from,
hdr: header{
packetType: typeHandshake,
flags: flagResponse | flagCookie,
connID: hd.connID,
timestamp: timestampMicros(),
},
data: handshakeData{
packetSize: uint32(m.packetSize),
cookie: correctCookie,
}.marshal(),
})
return
}
seqNo := randomSeqNo()
m.connsMut.Lock()
connID := m.newConnID()
conn := newConn(m, from)
conn.connID = connID
conn.remoteConnID = hd.connID
conn.nextSeqNo = seqNo + 1
conn.nextRecvSeqNo = hdr.sequenceNo + 1
conn.packetSize = int(hd.packetSize)
if conn.packetSize > m.packetSize {
conn.packetSize = m.packetSize
}
conn.start()
m.conns[connID] = conn
m.connsMut.Unlock()
m.write(packet{
dst: from,
hdr: header{
packetType: typeHandshake,
flags: flagResponse,
connID: hd.connID,
sequenceNo: seqNo,
timestamp: timestampMicros(),
},
data: handshakeData{
connID: conn.connID,
packetSize: uint32(conn.packetSize),
}.marshal(),
})
m.incoming <- conn
}
func (m *Mux) incomingHandshakeResponse(from net.Addr, hdr header, data []byte) {
m.connsMut.Lock()
handShake, ok := m.handshakes[hdr.connID]
m.connsMut.Unlock()
if ok {
// This is a response to a handshake in progress.
handShake <- packet{
dst: nil,
hdr: hdr,
data: data,
}
} else if debugMux && hdr.packetType != typeShutdown {
log.Printf("Handshake packet %v for unknown conn %v", hdr, hdr.connID)
}
}
func (m *Mux) write(pkt packet) (int, error) {
buf := m.buffers.Get().([]byte)
buf = buf[:dstHeaderLen+len(pkt.data)]
pkt.hdr.marshal(buf)
copy(buf[dstHeaderLen:], pkt.data)
if debugMux {
log.Println(m, "write", pkt)
}
n, err := m.conn.WriteTo(buf, pkt.dst)
m.buffers.Put(buf)
return n, err
}
func (m *Mux) String() string {
return fmt.Sprintf("Mux-%v", m.Addr())
}
// Find a unique connection ID
func (m *Mux) newConnID() connectionID {
for {
connID := randomConnID()
if _, ok := m.conns[connID]; ok {
continue
}
if _, ok := m.handshakes[connID]; ok {
continue
}
return connID
}
}
func (m *Mux) removeConn(c *Conn) {
m.connsMut.Lock()
delete(m.conns, c.connID)
m.connsMut.Unlock()
}

119
core/dst/packetlist.go Normal file
View File

@ -0,0 +1,119 @@
// Copyright 2014 The DST Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package dst
type packetList struct {
packets []packet
slot int
}
// CutLessSeq cuts packets from the start of the list with sequence numbers
// lower than seq. Returns the number of packets that were cut.
func (l *packetList) CutLessSeq(seq sequenceNo) int {
var i, cut int
for i = range l.packets {
if i == l.slot {
break
}
if !l.packets[i].LessSeq(seq) {
break
}
cut++
}
if cut > 0 {
l.Cut(cut)
}
return cut
}
func (l *packetList) Cut(n int) {
copy(l.packets, l.packets[n:])
l.slot -= n
}
func (l *packetList) Full() bool {
return l.slot == len(l.packets)
}
func (l *packetList) All() []packet {
return l.packets[:l.slot]
}
func (l *packetList) Append(pkt packet) bool {
if l.slot == len(l.packets) {
return false
}
l.packets[l.slot] = pkt
l.slot++
return true
}
func (l *packetList) AppendAll(pkts []packet) {
l.packets = append(l.packets[:l.slot], pkts...)
l.slot += len(pkts)
}
func (l *packetList) Cap() int {
return len(l.packets)
}
func (l *packetList) Len() int {
return l.slot
}
func (l *packetList) Resize(s int) {
if s <= cap(l.packets) {
l.packets = l.packets[:s]
} else {
t := make([]packet, s)
copy(t, l.packets)
l.packets = t
}
}
func (l *packetList) InsertSorted(pkt packet) {
for i := range l.packets {
if i >= l.slot {
l.packets[i] = pkt
l.slot++
return
}
if pkt.hdr.sequenceNo == l.packets[i].hdr.sequenceNo {
return
}
if pkt.Less(l.packets[i]) {
copy(l.packets[i+1:], l.packets[i:])
l.packets[i] = pkt
if l.slot < len(l.packets) {
l.slot++
}
return
}
}
}
func (l *packetList) LowestSeq() sequenceNo {
return l.packets[0].hdr.sequenceNo
}
func (l *packetList) PopSequence(maxSeq sequenceNo) []packet {
highSeq := l.packets[0].hdr.sequenceNo
if highSeq >= maxSeq {
return nil
}
var i int
for i = 1; i < l.slot; i++ {
seq := l.packets[i].hdr.sequenceNo
if seq != highSeq+1 || seq >= maxSeq {
break
}
highSeq++
}
pkts := make([]packet, i)
copy(pkts, l.packets[:i])
l.Cut(i)
return pkts
}

155
core/dst/packets.go Normal file
View File

@ -0,0 +1,155 @@
// Copyright 2014 The DST Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package dst
import (
"encoding/binary"
"fmt"
"net"
)
const dstHeaderLen = 12
type packetType int8
const (
typeHandshake packetType = 0x0
typeData = 0x1
typeAck = 0x2
typeNegAck = 0x3
typeShutdown = 0x4
)
func (t packetType) String() string {
switch t {
case typeData:
return "data"
case typeHandshake:
return "handshake"
case typeAck:
return "ack"
case typeNegAck:
return "negAck"
case typeShutdown:
return "shutdown"
default:
return "unknown"
}
}
type connectionID uint32
func (c connectionID) String() string {
return fmt.Sprintf("Ci%08x", uint32(c))
}
type sequenceNo uint32
func (s sequenceNo) String() string {
return fmt.Sprintf("Sq%d", uint32(s))
}
type timestamp uint32
func (t timestamp) String() string {
return fmt.Sprintf("Ts%d", uint32(t))
}
const (
flagRequest = 1 << 0 // This packet is a handshake request
flagResponse = 1 << 1 // This packet is a handshake response
flagCookie = 1 << 2 // This packet contains a coookie challenge
)
type header struct {
packetType packetType // 4 bits
flags uint8 // 4 bits
connID connectionID // 24 bits
sequenceNo sequenceNo
timestamp timestamp
}
func (h header) marshal(bs []byte) {
binary.BigEndian.PutUint32(bs, uint32(h.connID&0xffffff))
bs[0] = h.flags | uint8(h.packetType)<<4
binary.BigEndian.PutUint32(bs[4:], uint32(h.sequenceNo))
binary.BigEndian.PutUint32(bs[8:], uint32(h.timestamp))
}
func unmarshalHeader(bs []byte) header {
var h header
h.packetType = packetType(bs[0] >> 4)
h.flags = bs[0] & 0xf
h.connID = connectionID(binary.BigEndian.Uint32(bs) & 0xffffff)
h.sequenceNo = sequenceNo(binary.BigEndian.Uint32(bs[4:]))
h.timestamp = timestamp(binary.BigEndian.Uint32(bs[8:]))
return h
}
func (h header) String() string {
return fmt.Sprintf("header{type=%s flags=0x%x connID=%v seq=%v time=%v}", h.packetType, h.flags, h.connID, h.sequenceNo, h.timestamp)
}
type handshakeData struct {
packetSize uint32
connID connectionID
cookie uint32
}
func (h handshakeData) marshalInto(data []byte) {
binary.BigEndian.PutUint32(data[0:], h.packetSize)
binary.BigEndian.PutUint32(data[4:], uint32(h.connID))
binary.BigEndian.PutUint32(data[8:], h.cookie)
}
func (h handshakeData) marshal() []byte {
var data [12]byte
h.marshalInto(data[:])
return data[:]
}
func unmarshalHandshakeData(data []byte) handshakeData {
var h handshakeData
h.packetSize = binary.BigEndian.Uint32(data[0:])
h.connID = connectionID(binary.BigEndian.Uint32(data[4:]))
h.cookie = binary.BigEndian.Uint32(data[8:])
return h
}
func (h handshakeData) String() string {
return fmt.Sprintf("handshake{size=%d connID=%v cookie=0x%08x}", h.packetSize, h.connID, h.cookie)
}
type packet struct {
src connectionID
dst net.Addr
hdr header
data []byte
}
func (p packet) String() string {
var dst string
if p.dst != nil {
dst = "dst=" + p.dst.String() + " "
}
switch p.hdr.packetType {
case typeHandshake:
return fmt.Sprintf("%spacket{src=%v %v %v}", dst, p.src, p.hdr, unmarshalHandshakeData(p.data))
default:
return fmt.Sprintf("%spacket{src=%v %v data[:%d]}", dst, p.src, p.hdr, len(p.data))
}
}
func (p packet) LessSeq(seq sequenceNo) bool {
diff := seq - p.hdr.sequenceNo
if diff == 0 {
return false
}
return diff < 1<<31
}
func (a packet) Less(b packet) bool {
return a.LessSeq(b.hdr.sequenceNo)
}

268
core/dst/sendbuffer.go Normal file
View File

@ -0,0 +1,268 @@
// Copyright 2014 The DST Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package dst
import (
"fmt"
"runtime/debug"
"sync"
"github.com/juju/ratelimit"
)
/*
sendWindow
v
[S|S|S|S|Q|Q|Q|Q| | | | | | | | | ]
^ ^writeSlot
sendSlot
*/
type sendBuffer struct {
mux *Mux // we send packets here
scheduler *ratelimit.Bucket // sets send rate for packets
sendWindow int // maximum number of outstanding non-acked packets
packetRate int // target pps
send packetList // buffered packets
sendSlot int // buffer slot from which to send next packet
lost packetList // list of packets reported lost by timeout
lostSlot int // next lost packet to resend
closed bool
closing bool
mut sync.Mutex
cond *sync.Cond
}
const (
schedulerRate = 1e6
schedulerCapacity = schedulerRate / 40
)
// newSendBuffer creates a new send buffer with a zero window.
// SetRateAndWindow() must be called to set an initial packet rate and send
// window before using.
func newSendBuffer(m *Mux) *sendBuffer {
b := &sendBuffer{
mux: m,
scheduler: ratelimit.NewBucketWithRate(schedulerRate, schedulerCapacity),
}
b.cond = sync.NewCond(&b.mut)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
b.writerLoop()
}()
return b
}
// Write puts a new packet in send buffer and schedules a send. Blocks when
// the window size is or would be exceeded.
func (b *sendBuffer) Write(pkt packet) error {
b.mut.Lock()
defer b.mut.Unlock()
for b.send.Full() || b.send.Len() >= b.sendWindow {
if b.closing {
return ErrClosedConn
}
if debugConnection {
log.Println(b, "Write blocked")
}
b.cond.Wait()
}
if !b.send.Append(pkt) {
panic("bug: append failed")
}
b.cond.Broadcast()
return nil
}
// Acknowledge removes packets with lower sequence numbers from the loss list
// or send buffer.
func (b *sendBuffer) Acknowledge(seq sequenceNo) {
b.mut.Lock()
if cut := b.lost.CutLessSeq(seq); cut > 0 {
if debugConnection {
log.Println(b, "cut", cut, "from loss list")
}
// Next resend should always start with the first packet, regardless
// of what we might already have resent previously.
b.lostSlot = 0
b.cond.Broadcast()
}
if cut := b.send.CutLessSeq(seq); cut > 0 {
if debugConnection {
log.Println(b, "cut", cut, "from send list")
}
b.sendSlot -= cut
b.cond.Broadcast()
}
b.mut.Unlock()
}
func (b *sendBuffer) NegativeAck(seq sequenceNo) {
b.mut.Lock()
pkts := b.send.PopSequence(seq)
if cut := len(pkts); cut > 0 {
b.lost.AppendAll(pkts)
if debugConnection {
log.Println(b, "cut", cut, "from send list, adding to loss list")
log.Println(seq, pkts)
}
b.sendSlot -= cut
b.lostSlot = 0
b.cond.Broadcast()
}
b.mut.Unlock()
}
// ScheduleResend arranges for a resend of all currently unacknowledged
// packets.
func (b *sendBuffer) ScheduleResend() {
b.mut.Lock()
if b.sendSlot > 0 {
// There are packets that have been sent but not acked. Move them from
// the send buffer to the loss list for retransmission.
if debugConnection {
log.Println(b, "scheduled resend from send list", b.sendSlot)
}
// Append the packets to the loss list and rewind the send buffer
b.lost.AppendAll(b.send.All()[:b.sendSlot])
b.send.Cut(b.sendSlot)
b.sendSlot = 0
b.cond.Broadcast()
}
if b.lostSlot > 0 {
// Also resend whatever was already in the loss list
if debugConnection {
log.Println(b, "scheduled resend from loss list", b.lostSlot)
}
b.lostSlot = 0
b.cond.Broadcast()
}
b.mut.Unlock()
}
// SetWindowAndRate sets the window size (in packets) and packet rate (in
// packets per second) to use when sending.
func (b *sendBuffer) SetWindowAndRate(sendWindow, packetRate int) {
b.mut.Lock()
if debugConnection {
log.Println(b, "new window & rate", sendWindow, packetRate)
}
b.packetRate = packetRate
b.sendWindow = sendWindow
if b.sendWindow > b.send.Cap() {
b.send.Resize(b.sendWindow)
b.cond.Broadcast()
}
b.mut.Unlock()
}
// Stop stops the send buffer from any doing further sending, but waits for
// the current buffers to be drained.
func (b *sendBuffer) Stop() {
b.mut.Lock()
if b.closed || b.closing {
return
}
b.closing = true
for b.lost.Len() > 0 || b.send.Len() > 0 {
b.cond.Wait()
}
b.closed = true
b.cond.Broadcast()
b.mut.Unlock()
}
// CrashStop stops the send buffer from any doing further sending, without
// waiting for buffers to drain.
func (b *sendBuffer) CrashStop() {
b.mut.Lock()
if b.closed || b.closing {
return
}
b.closing = true
b.closed = true
b.cond.Broadcast()
b.mut.Unlock()
}
func (b *sendBuffer) String() string {
return fmt.Sprintf("sendBuffer@%p", b)
}
func (b *sendBuffer) writerLoop() {
if debugConnection {
log.Println(b, "writer() starting")
defer log.Println(b, "writer() exiting")
}
b.scheduler.Take(schedulerCapacity)
for {
var pkt packet
b.mut.Lock()
for b.lostSlot >= b.sendWindow ||
(b.sendSlot == b.send.Len() && b.lostSlot == b.lost.Len()) {
if b.closed {
b.mut.Unlock()
return
}
if debugConnection {
log.Println(b, "writer() paused", b.lostSlot, b.sendSlot, b.sendWindow, b.lost.Len())
}
b.cond.Wait()
}
if b.lostSlot < b.lost.Len() {
pkt = b.lost.All()[b.lostSlot]
pkt.hdr.timestamp = timestampMicros()
b.lostSlot++
if debugConnection {
log.Println(b, "resend", b.lostSlot, b.lost.Len(), b.sendWindow, pkt.hdr.connID, pkt.hdr.sequenceNo)
}
} else if b.sendSlot < b.send.Len() {
pkt = b.send.All()[b.sendSlot]
pkt.hdr.timestamp = timestampMicros()
b.sendSlot++
if debugConnection {
log.Println(b, "send", b.sendSlot, b.send.Len(), b.sendWindow, pkt.hdr.connID, pkt.hdr.sequenceNo)
}
}
b.cond.Broadcast()
packetRate := b.packetRate
b.mut.Unlock()
if pkt.dst != nil {
b.scheduler.Wait(schedulerRate / int64(packetRate))
b.mux.write(pkt)
}
}
}

29
core/dst/util.go Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2014 The DST Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package dst
import (
logger "log"
"math/rand"
"os"
"time"
)
var log = logger.New(os.Stderr, "", logger.LstdFlags)
func SetLogger(l *logger.Logger) {
log = l
}
func timestampMicros() timestamp {
return timestamp(time.Now().UnixNano() / 1000)
}
func randomSeqNo() sequenceNo {
return sequenceNo(rand.Uint32())
}
func randomConnID() connectionID {
return connectionID(rand.Uint32() & 0xffffff)
}

144
core/dst/windowcc.go Normal file
View File

@ -0,0 +1,144 @@
// Copyright 2014 The DST Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package dst
import (
"fmt"
"io"
"os"
"time"
)
type windowCC struct {
minWindow int
maxWindow int
currentWindow int
minRate int
maxRate int
currentRate int
targetRate int
curRTT time.Duration
minRTT time.Duration
statsFile io.WriteCloser
start time.Time
}
func newWindowCC() *windowCC {
var statsFile io.WriteCloser
if debugCC {
statsFile, _ = os.Create(fmt.Sprintf("cc-log-%d.csv", time.Now().Unix()))
fmt.Fprintf(statsFile, "ms,minWin,maxWin,curWin,minRate,maxRate,curRate,minRTT,curRTT\n")
}
return &windowCC{
minWindow: 1, // Packets
maxWindow: 16 << 10,
currentWindow: 1,
minRate: 100, // PPS
maxRate: 80e3, // Roughly 1 Gbps at 1500 bytes per packet
currentRate: 100,
targetRate: 1000,
minRTT: 10 * time.Second,
statsFile: statsFile,
start: time.Now(),
}
}
func (w *windowCC) Ack() {
if w.curRTT > w.minRTT+100*time.Millisecond {
return
}
changed := false
if w.currentWindow < w.maxWindow {
w.currentWindow++
changed = true
}
if w.currentRate != w.targetRate {
w.currentRate = (w.currentRate*7 + w.targetRate) / 8
changed = true
}
if changed && debugCC {
w.log()
log.Println("Ack", w.currentWindow, w.currentRate)
}
}
func (w *windowCC) NegAck() {
if w.currentWindow > w.minWindow {
w.currentWindow /= 2
}
if w.currentRate > w.minRate {
w.currentRate /= 2
}
if debugCC {
w.log()
log.Println("NegAck", w.currentWindow, w.currentRate)
}
}
func (w *windowCC) Exp() {
w.currentWindow = w.minWindow
if debugCC {
w.log()
log.Println("Exp", w.currentWindow, w.currentRate)
}
}
func (w *windowCC) SendWindow() int {
if w.currentWindow < w.minWindow {
return w.minWindow
}
if w.currentWindow > w.maxWindow {
return w.maxWindow
}
return w.currentWindow
}
func (w *windowCC) PacketRate() int {
if w.currentRate < w.minRate {
return w.minRate
}
if w.currentRate > w.maxRate {
return w.maxRate
}
return w.currentRate
}
func (w *windowCC) UpdateRTT(rtt time.Duration) {
w.curRTT = rtt
if w.curRTT < w.minRTT {
w.minRTT = w.curRTT
if debugCC {
log.Println("Min RTT", w.minRTT)
}
}
if w.curRTT > w.minRTT+200*time.Millisecond && w.targetRate > 2*w.minRate {
w.targetRate -= w.minRate
} else if w.curRTT < w.minRTT+20*time.Millisecond && w.targetRate < w.maxRate {
w.targetRate += w.minRate
}
if debugCC {
w.log()
log.Println("RTT", w.curRTT, "target rate", w.targetRate, "current rate", w.currentRate, "current window", w.currentWindow)
}
}
func (w *windowCC) log() {
if w.statsFile == nil {
return
}
fmt.Fprintf(w.statsFile, "%.02f,%d,%d,%d,%d,%d,%d,%.02f,%.02f\n", time.Since(w.start).Seconds()*1000, w.minWindow, w.maxWindow, w.currentWindow, w.minRate, w.maxRate, w.currentRate, w.minRTT.Seconds()*1000, w.curRTT.Seconds()*1000)
}

52
core/lib/buf/leakybuf.go Normal file
View File

@ -0,0 +1,52 @@
// Provides leaky buffer, based on the example in Effective Go.
package buf
type LeakyBuf struct {
bufSize int // size of each buffer
freeList chan []byte
}
const LeakyBufSize = 2048 // data.len(2) + hmacsha1(10) + data(4096)
const maxNBuf = 2048
var LeakyBuffer = NewLeakyBuf(maxNBuf, LeakyBufSize)
func Get() (b []byte) {
return LeakyBuffer.Get()
}
func Put(b []byte) {
LeakyBuffer.Put(b)
}
// NewLeakyBuf creates a leaky buffer which can hold at most n buffer, each
// with bufSize bytes.
func NewLeakyBuf(n, bufSize int) *LeakyBuf {
return &LeakyBuf{
bufSize: bufSize,
freeList: make(chan []byte, n),
}
}
// Get returns a buffer from the leaky buffer or create a new buffer.
func (lb *LeakyBuf) Get() (b []byte) {
select {
case b = <-lb.freeList:
default:
b = make([]byte, lb.bufSize)
}
return
}
// Put add the buffer into the free buffer pool for reuse. Panic if the buffer
// size is not the same with the leaky buffer's. This is intended to expose
// error usage of leaky buffer.
func (lb *LeakyBuf) Put(b []byte) {
if len(b) != lb.bufSize {
panic("invalid buffer size that's put into leaky buffer")
}
select {
case lb.freeList <- b:
default:
}
return
}

68
core/lib/ioutils/utils.go Normal file
View File

@ -0,0 +1,68 @@
package ioutils
import (
"io"
logger "log"
lbuf "github.com/snail007/goproxy/core/lib/buf"
)
func IoBind(dst io.ReadWriteCloser, src io.ReadWriteCloser, fn func(err interface{}), log *logger.Logger) {
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("bind crashed %s", err)
}
}()
e1 := make(chan interface{}, 1)
e2 := make(chan interface{}, 1)
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("bind crashed %s", err)
}
}()
//_, err := io.Copy(dst, src)
err := ioCopy(dst, src)
e1 <- err
}()
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("bind crashed %s", err)
}
}()
//_, err := io.Copy(src, dst)
err := ioCopy(src, dst)
e2 <- err
}()
var err interface{}
select {
case err = <-e1:
//log.Printf("e1")
case err = <-e2:
//log.Printf("e2")
}
src.Close()
dst.Close()
if fn != nil {
fn(err)
}
}()
}
func ioCopy(dst io.ReadWriter, src io.ReadWriter) (err error) {
buf := lbuf.LeakyBuffer.Get()
defer lbuf.LeakyBuffer.Put(buf)
n := 0
for {
n, err = src.Read(buf)
if n > 0 {
if _, e := dst.Write(buf[0:n]); e != nil {
return e
}
}
if err != nil {
return
}
}
}

24
core/lib/kcpcfg/args.go Normal file
View File

@ -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
}

355
core/lib/mapx/map.go Normal file
View File

@ -0,0 +1,355 @@
package mapx
import (
"encoding/json"
"fmt"
"runtime/debug"
"sync"
)
var SHARD_COUNT = 32
// A "thread" safe map of type string:Anything.
// To avoid lock bottlenecks this map is dived to several (SHARD_COUNT) map shards.
type ConcurrentMap []*ConcurrentMapShared
// A "thread" safe string to anything map.
type ConcurrentMapShared struct {
items map[string]interface{}
sync.RWMutex // Read Write mutex, guards access to internal map.
}
// Creates a new concurrent map.
func NewConcurrentMap() ConcurrentMap {
m := make(ConcurrentMap, SHARD_COUNT)
for i := 0; i < SHARD_COUNT; i++ {
m[i] = &ConcurrentMapShared{items: make(map[string]interface{})}
}
return m
}
// Returns shard under given key
func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared {
return m[uint(fnv32(key))%uint(SHARD_COUNT)]
}
func (m ConcurrentMap) MSet(data map[string]interface{}) {
for key, value := range data {
shard := m.GetShard(key)
shard.Lock()
shard.items[key] = value
shard.Unlock()
}
}
// Sets the given value under the specified key.
func (m ConcurrentMap) Set(key string, value interface{}) {
// Get map shard.
shard := m.GetShard(key)
shard.Lock()
shard.items[key] = value
shard.Unlock()
}
// Callback to return new element to be inserted into the map
// It is called while lock is held, therefore it MUST NOT
// try to access other keys in same map, as it can lead to deadlock since
// Go sync.RWLock is not reentrant
type UpsertCb func(exist bool, valueInMap interface{}, newValue interface{}) interface{}
// Insert or Update - updates existing element or inserts a new one using UpsertCb
func (m ConcurrentMap) Upsert(key string, value interface{}, cb UpsertCb) (res interface{}) {
shard := m.GetShard(key)
shard.Lock()
v, ok := shard.items[key]
res = cb(ok, v, value)
shard.items[key] = res
shard.Unlock()
return res
}
// Sets the given value under the specified key if no value was associated with it.
func (m ConcurrentMap) SetIfAbsent(key string, value interface{}) bool {
// Get map shard.
shard := m.GetShard(key)
shard.Lock()
_, ok := shard.items[key]
if !ok {
shard.items[key] = value
}
shard.Unlock()
return !ok
}
// Retrieves an element from map under given key.
func (m ConcurrentMap) Get(key string) (interface{}, bool) {
// Get shard
shard := m.GetShard(key)
shard.RLock()
// Get item from shard.
val, ok := shard.items[key]
shard.RUnlock()
return val, ok
}
// Returns the number of elements within the map.
func (m ConcurrentMap) Count() int {
count := 0
for i := 0; i < SHARD_COUNT; i++ {
shard := m[i]
shard.RLock()
count += len(shard.items)
shard.RUnlock()
}
return count
}
// Looks up an item under specified key
func (m ConcurrentMap) Has(key string) bool {
// Get shard
shard := m.GetShard(key)
shard.RLock()
// See if element is within shard.
_, ok := shard.items[key]
shard.RUnlock()
return ok
}
// Removes an element from the map.
func (m ConcurrentMap) Remove(key string) {
// Try to get shard.
shard := m.GetShard(key)
shard.Lock()
delete(shard.items, key)
shard.Unlock()
}
// Removes an element from the map and returns it
func (m ConcurrentMap) Pop(key string) (v interface{}, exists bool) {
// Try to get shard.
shard := m.GetShard(key)
shard.Lock()
v, exists = shard.items[key]
delete(shard.items, key)
shard.Unlock()
return v, exists
}
// Checks if map is empty.
func (m ConcurrentMap) IsEmpty() bool {
return m.Count() == 0
}
// Used by the Iter & IterBuffered functions to wrap two variables together over a channel,
type Tuple struct {
Key string
Val interface{}
}
// Returns an iterator which could be used in a for range loop.
//
// Deprecated: using IterBuffered() will get a better performence
func (m ConcurrentMap) Iter() <-chan Tuple {
chans := snapshot(m)
ch := make(chan Tuple)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
fanIn(chans, ch)
}()
return ch
}
// Returns a buffered iterator which could be used in a for range loop.
func (m ConcurrentMap) IterBuffered() <-chan Tuple {
chans := snapshot(m)
total := 0
for _, c := range chans {
total += cap(c)
}
ch := make(chan Tuple, total)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
fanIn(chans, ch)
}()
return ch
}
// Returns a array of channels that contains elements in each shard,
// which likely takes a snapshot of `m`.
// It returns once the size of each buffered channel is determined,
// before all the channels are populated using goroutines.
func snapshot(m ConcurrentMap) (chans []chan Tuple) {
chans = make([]chan Tuple, SHARD_COUNT)
wg := sync.WaitGroup{}
wg.Add(SHARD_COUNT)
// Foreach shard.
for index, shard := range m {
go func(index int, shard *ConcurrentMapShared) {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
// Foreach key, value pair.
shard.RLock()
chans[index] = make(chan Tuple, len(shard.items))
wg.Done()
for key, val := range shard.items {
chans[index] <- Tuple{key, val}
}
shard.RUnlock()
close(chans[index])
}(index, shard)
}
wg.Wait()
return chans
}
// fanIn reads elements from channels `chans` into channel `out`
func fanIn(chans []chan Tuple, out chan Tuple) {
wg := sync.WaitGroup{}
wg.Add(len(chans))
for _, ch := range chans {
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
func(ch chan Tuple) {
for t := range ch {
out <- t
}
wg.Done()
}(ch)
}()
}
wg.Wait()
close(out)
}
// Returns all items as map[string]interface{}
func (m ConcurrentMap) Items() map[string]interface{} {
tmp := make(map[string]interface{})
// Insert items to temporary map.
for item := range m.IterBuffered() {
tmp[item.Key] = item.Val
}
return tmp
}
// Iterator callback,called for every key,value found in
// maps. RLock is held for all calls for a given shard
// therefore callback sess consistent view of a shard,
// but not across the shards
type IterCb func(key string, v interface{})
// Callback based iterator, cheapest way to read
// all elements in a map.
func (m ConcurrentMap) IterCb(fn IterCb) {
for idx := range m {
shard := (m)[idx]
shard.RLock()
for key, value := range shard.items {
fn(key, value)
}
shard.RUnlock()
}
}
// Return all keys as []string
func (m ConcurrentMap) Keys() []string {
count := m.Count()
ch := make(chan string, count)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
// Foreach shard.
wg := sync.WaitGroup{}
wg.Add(SHARD_COUNT)
for _, shard := range m {
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
func(shard *ConcurrentMapShared) {
// Foreach key, value pair.
shard.RLock()
for key := range shard.items {
ch <- key
}
shard.RUnlock()
wg.Done()
}(shard)
}()
}
wg.Wait()
close(ch)
}()
// Generate keys
keys := make([]string, 0, count)
for k := range ch {
keys = append(keys, k)
}
return keys
}
//Reviles ConcurrentMap "private" variables to json marshal.
func (m ConcurrentMap) MarshalJSON() ([]byte, error) {
// Create a temporary map, which will hold all item spread across shards.
tmp := make(map[string]interface{})
// Insert items to temporary map.
for item := range m.IterBuffered() {
tmp[item.Key] = item.Val
}
return json.Marshal(tmp)
}
func fnv32(key string) uint32 {
hash := uint32(2166136261)
const prime32 = uint32(16777619)
for i := 0; i < len(key); i++ {
hash *= prime32
hash ^= uint32(key[i])
}
return hash
}
// Concurrent map uses Interface{} as its value, therefor JSON Unmarshal
// will probably won't know which to type to unmarshal into, in such case
// we'll end up with a value of type map[string]interface{}, In most cases this isn't
// out value type, this is why we've decided to remove this functionality.
// func (m *ConcurrentMap) UnmarshalJSON(b []byte) (err error) {
// // Reverse process of Marshal.
// tmp := make(map[string]interface{})
// // Unmarshal into a single map.
// if err := json.Unmarshal(b, &tmp); err != nil {
// return nil
// }
// // foreach key,value pair in temporary map insert into our concurrent map.
// for key, val := range tmp {
// m.Set(key, val)
// }
// return nil
// }

159
core/lib/socks5/socks5.go Normal file
View File

@ -0,0 +1,159 @@
package socks5
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"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}
)
var Socks5Errors = []string{
"",
"general failure",
"connection forbidden",
"network unreachable",
"host unreachable",
"connection refused",
"TTL expired",
"command not supported",
"address type not supported",
}
// Auth contains authentication parameters that specific Dialers may require.
type UsernamePassword struct {
Username, Password string
}
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
}

View File

@ -0,0 +1,62 @@
package transport
import (
"net"
"time"
"github.com/golang/snappy"
)
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() (err error) {
err = c.conn.Close()
c.r = nil
c.w = nil
return
}
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)
}

View File

@ -0,0 +1,56 @@
package encrypt
import (
"crypto/cipher"
"fmt"
"io"
"net"
lbuf "github.com/snail007/goproxy/core/lib/buf"
)
var (
lBuf = lbuf.NewLeakyBuf(2048, 2048)
)
type Conn struct {
net.Conn
*Cipher
w io.Writer
r io.Reader
}
func NewConn(c net.Conn, method, password string) (conn net.Conn, err error) {
cipher0, err := NewCipher(method, password)
if err != nil {
return
}
conn = &Conn{
Conn: c,
Cipher: cipher0,
r: &cipher.StreamReader{S: cipher0.ReadStream, R: c},
w: &cipher.StreamWriter{S: cipher0.WriteStream, W: c},
}
return
}
func (s *Conn) Read(b []byte) (n int, err error) {
if s.r == nil {
return 0, fmt.Errorf("use of closed network connection")
}
return s.r.Read(b)
}
func (s *Conn) Write(b []byte) (n int, err error) {
if s.w == nil {
return 0, fmt.Errorf("use of closed network connection")
}
return s.w.Write(b)
}
func (s *Conn) Close() (err error) {
if s.Cipher != nil {
err = s.Conn.Close()
s.Cipher = nil
s.r = nil
s.w = nil
}
return
}

View File

@ -0,0 +1,185 @@
package encrypt
import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/md5"
"crypto/rc4"
"crypto/sha256"
"errors"
lbuf "github.com/snail007/goproxy/core/lib/buf"
"github.com/Yawning/chacha20"
"golang.org/x/crypto/blowfish"
"golang.org/x/crypto/cast5"
)
const leakyBufSize = 2048
const maxNBuf = 2048
var leakyBuf = lbuf.NewLeakyBuf(maxNBuf, leakyBufSize)
var errEmptyPassword = errors.New("proxy key")
func md5sum(d []byte) []byte {
h := md5.New()
h.Write(d)
return h.Sum(nil)
}
func evpBytesToKey(password string, keyLen int) (key []byte) {
const md5Len = 16
cnt := (keyLen-1)/md5Len + 1
m := make([]byte, cnt*md5Len)
copy(m, md5sum([]byte(password)))
// Repeatedly call md5 until bytes generated is enough.
// Each call to md5 uses data: prev md5 sum + password.
d := make([]byte, md5Len+len(password))
start := 0
for i := 1; i < cnt; i++ {
start += md5Len
copy(d, m[start-md5Len:start])
copy(d[md5Len:], password)
copy(m[start:], md5sum(d))
}
return m[:keyLen]
}
type DecOrEnc int
const (
Decrypt DecOrEnc = iota
Encrypt
)
func newStream(block cipher.Block, err error, key, iv []byte,
doe DecOrEnc) (cipher.Stream, error) {
if err != nil {
return nil, err
}
if doe == Encrypt {
return cipher.NewCFBEncrypter(block, iv), nil
} else {
return cipher.NewCFBDecrypter(block, iv), nil
}
}
func newAESCFBStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
block, err := aes.NewCipher(key)
return newStream(block, err, key, iv, doe)
}
func newAESCTRStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return cipher.NewCTR(block, iv), nil
}
func newDESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
block, err := des.NewCipher(key)
return newStream(block, err, key, iv, doe)
}
func newBlowFishStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
block, err := blowfish.NewCipher(key)
return newStream(block, err, key, iv, doe)
}
func newCast5Stream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
block, err := cast5.NewCipher(key)
return newStream(block, err, key, iv, doe)
}
func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) {
h := md5.New()
h.Write(key)
h.Write(iv)
rc4key := h.Sum(nil)
return rc4.NewCipher(rc4key)
}
func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) {
return chacha20.NewCipher(key, iv)
}
func newChaCha20IETFStream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) {
return chacha20.NewCipher(key, iv)
}
type cipherInfo struct {
keyLen int
ivLen int
newStream func(key, iv []byte, doe DecOrEnc) (cipher.Stream, error)
}
var cipherMethod = map[string]*cipherInfo{
"aes-128-cfb": {16, 16, newAESCFBStream},
"aes-192-cfb": {24, 16, newAESCFBStream},
"aes-256-cfb": {32, 16, newAESCFBStream},
"aes-128-ctr": {16, 16, newAESCTRStream},
"aes-192-ctr": {24, 16, newAESCTRStream},
"aes-256-ctr": {32, 16, newAESCTRStream},
"des-cfb": {8, 8, newDESStream},
"bf-cfb": {16, 8, newBlowFishStream},
"cast5-cfb": {16, 8, newCast5Stream},
"rc4-md5": {16, 16, newRC4MD5Stream},
"rc4-md5-6": {16, 6, newRC4MD5Stream},
"chacha20": {32, 8, newChaCha20Stream},
"chacha20-ietf": {32, 12, newChaCha20IETFStream},
}
func GetCipherMethods() (keys []string) {
keys = []string{}
for k := range cipherMethod {
keys = append(keys, k)
}
return
}
func CheckCipherMethod(method string) error {
if method == "" {
method = "aes-256-cfb"
}
_, ok := cipherMethod[method]
if !ok {
return errors.New("Unsupported encryption method: " + method)
}
return nil
}
type Cipher struct {
WriteStream cipher.Stream
ReadStream cipher.Stream
key []byte
info *cipherInfo
}
func NewCipher(method, password string) (c *Cipher, err error) {
if password == "" {
return nil, errEmptyPassword
}
mi, ok := cipherMethod[method]
if !ok {
return nil, errors.New("Unsupported encryption method: " + method)
}
key := evpBytesToKey(password, mi.keyLen)
c = &Cipher{key: key, info: mi}
if err != nil {
return nil, err
}
//hash(key) -> read IV
riv := sha256.New().Sum(c.key)[:c.info.ivLen]
c.ReadStream, err = c.info.newStream(c.key, riv, Decrypt)
if err != nil {
return nil, err
} //hash(read IV) -> write IV
wiv := sha256.New().Sum(riv)[:c.info.ivLen]
c.WriteStream, err = c.info.newStream(c.key, wiv, Encrypt)
if err != nil {
return nil, err
}
return c, nil
}

234
core/lib/udp/udp.go Normal file
View File

@ -0,0 +1,234 @@
package udputils
import (
"fmt"
logger "log"
"net"
"runtime/debug"
"strings"
"time"
bufx "github.com/snail007/goproxy/core/lib/buf"
mapx "github.com/snail007/goproxy/core/lib/mapx"
)
type CreateOutUDPConnFn func(listener *net.UDPConn, srcAddr *net.UDPAddr, packet []byte) (outconn *net.UDPConn, err error)
type CleanFn func(srcAddr string)
type BeforeSendFn func(listener *net.UDPConn, srcAddr *net.UDPAddr, b []byte) (sendB []byte, err error)
type BeforeReplyFn func(listener *net.UDPConn, srcAddr *net.UDPAddr, outconn *net.UDPConn, b []byte) (replyB []byte, err error)
type IOBinder struct {
outConns mapx.ConcurrentMap
listener *net.UDPConn
createOutUDPConnFn CreateOutUDPConnFn
log *logger.Logger
timeout time.Duration
cleanFn CleanFn
inTCPConn *net.Conn
outTCPConn *net.Conn
beforeSendFn BeforeSendFn
beforeReplyFn BeforeReplyFn
}
func NewIOBinder(listener *net.UDPConn, log *logger.Logger) *IOBinder {
return &IOBinder{
listener: listener,
outConns: mapx.NewConcurrentMap(),
log: log,
}
}
func (s *IOBinder) Factory(fn CreateOutUDPConnFn) *IOBinder {
s.createOutUDPConnFn = fn
return s
}
func (s *IOBinder) AfterReadFromClient(fn BeforeSendFn) *IOBinder {
s.beforeSendFn = fn
return s
}
func (s *IOBinder) AfterReadFromServer(fn BeforeReplyFn) *IOBinder {
s.beforeReplyFn = fn
return s
}
func (s *IOBinder) Timeout(timeout time.Duration) *IOBinder {
s.timeout = timeout
return s
}
func (s *IOBinder) Clean(fn CleanFn) *IOBinder {
s.cleanFn = fn
return s
}
func (s *IOBinder) AliveWithServeConn(srcAddr string, inTCPConn *net.Conn) *IOBinder {
s.inTCPConn = inTCPConn
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
buf := make([]byte, 1)
(*inTCPConn).SetReadDeadline(time.Time{})
if _, err := (*inTCPConn).Read(buf); err != nil {
s.log.Printf("udp related tcp conn of client disconnected with read , %s", err.Error())
s.clean(srcAddr)
}
}()
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
for {
(*inTCPConn).SetWriteDeadline(time.Now().Add(time.Second * 5))
if _, err := (*inTCPConn).Write([]byte{0x00}); err != nil {
s.log.Printf("udp related tcp conn of client disconnected with write , %s", err.Error())
s.clean(srcAddr)
return
}
(*inTCPConn).SetWriteDeadline(time.Time{})
time.Sleep(time.Second * 5)
}
}()
return s
}
func (s *IOBinder) AliveWithClientConn(srcAddr string, outTCPConn *net.Conn) *IOBinder {
s.outTCPConn = outTCPConn
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
buf := make([]byte, 1)
(*outTCPConn).SetReadDeadline(time.Time{})
if _, err := (*outTCPConn).Read(buf); err != nil {
s.log.Printf("udp related tcp conn to parent disconnected with read , %s", err.Error())
s.clean(srcAddr)
}
}()
return s
}
func (s *IOBinder) Run() (err error) {
var (
isClosedErr = func(err error) bool {
return err != nil && strings.Contains(err.Error(), "use of closed network connection")
}
isTimeoutErr = func(err error) bool {
if err == nil {
return false
}
e, ok := err.(net.Error)
return ok && e.Timeout()
}
isRefusedErr = func(err error) bool {
return err != nil && strings.Contains(err.Error(), "connection refused")
}
)
for {
buf := bufx.Get()
defer bufx.Put(buf)
n, srcAddr, err := s.listener.ReadFromUDP(buf)
if err != nil {
s.log.Printf("read from client error %s", err)
if isClosedErr(err) {
return err
}
continue
}
var data []byte
if s.beforeSendFn != nil {
data, err = s.beforeSendFn(s.listener, srcAddr, buf[:n])
if err != nil {
s.log.Printf("beforeSend retured an error , %s", err)
continue
}
} else {
data = buf[:n]
}
inconnRemoteAddr := srcAddr.String()
var outconn *net.UDPConn
if v, ok := s.outConns.Get(inconnRemoteAddr); !ok {
outconn, err = s.createOutUDPConnFn(s.listener, srcAddr, data)
if err != nil {
s.log.Printf("connnect fail %s", err)
return err
}
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
defer func() {
s.clean(srcAddr.String())
}()
buf := bufx.Get()
defer bufx.Put(buf)
for {
if s.timeout > 0 {
outconn.SetReadDeadline(time.Now().Add(s.timeout))
}
n, srcAddr, err := outconn.ReadFromUDP(buf)
if err != nil {
s.log.Printf("read from remote error %s", err)
if isClosedErr(err) || isTimeoutErr(err) || isRefusedErr(err) {
return
}
continue
}
data := buf[:n]
if s.beforeReplyFn != nil {
data, err = s.beforeReplyFn(s.listener, srcAddr, outconn, buf[:n])
if err != nil {
s.log.Printf("beforeReply retured an error , %s", err)
continue
}
}
_, err = s.listener.WriteTo(data, srcAddr)
if err != nil {
s.log.Printf("write to remote error %s", err)
if isClosedErr(err) {
return
}
continue
}
}
}()
} else {
outconn = v.(*net.UDPConn)
}
s.log.Printf("use decrpyted data , %v", data)
_, err = outconn.Write(data)
if err != nil {
s.log.Printf("write to remote error %s", err)
if isClosedErr(err) {
return err
}
}
}
}
func (s *IOBinder) clean(srcAddr string) *IOBinder {
if v, ok := s.outConns.Get(srcAddr); ok {
(*v.(*net.UDPConn)).Close()
s.outConns.Remove(srcAddr)
}
if s.inTCPConn != nil {
(*s.inTCPConn).Close()
}
if s.outTCPConn != nil {
(*s.outTCPConn).Close()
}
if s.cleanFn != nil {
s.cleanFn(srcAddr)
}
return s
}
func (s *IOBinder) Close() {
for _, c := range s.outConns.Items() {
(*c.(*net.UDPConn)).Close()
}
}

View File

@ -0,0 +1,31 @@
// Package proxy provides support for a variety of protocols to proxy network
// data.
package client
import (
"net"
"time"
socks5c "github.com/snail007/goproxy/core/lib/socks5"
socks5 "github.com/snail007/goproxy/core/proxy/client/socks5"
)
// A Dialer is a means to establish a connection.
type Dialer interface {
// Dial connects to the given address via the proxy.
DialConn(conn *net.Conn, network, addr string) (err error)
}
// Auth contains authentication parameters that specific Dialers may require.
type Auth struct {
User, Password string
}
func SOCKS5(timeout time.Duration, auth *Auth) (Dialer, error) {
var a *socks5c.UsernamePassword
if auth != nil {
a = &socks5c.UsernamePassword{auth.User, auth.Password}
}
d := socks5.NewDialer(a, timeout)
return d, nil
}

View File

@ -0,0 +1,263 @@
package socks5
import (
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"strconv"
"time"
socks5c "github.com/snail007/goproxy/core/lib/socks5"
)
type Dialer struct {
timeout time.Duration
usernamePassword *socks5c.UsernamePassword
}
// NewDialer returns a new Dialer that dials through the provided
// proxy server's network and address.
func NewDialer(auth *socks5c.UsernamePassword, timeout time.Duration) *Dialer {
if auth != nil && auth.Password == "" && auth.Username == "" {
auth = nil
}
return &Dialer{
usernamePassword: auth,
timeout: timeout,
}
}
func (d *Dialer) DialConn(conn *net.Conn, network, addr string) (err error) {
client := NewClientConn(conn, network, addr, d.timeout, d.usernamePassword, nil)
err = client._Handshake()
return
}
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 *socks5c.UsernamePassword, header []byte) *ClientConn {
s := &ClientConn{
conn: conn,
network: network,
timeout: timeout,
}
if auth != nil {
s.user = auth.Username
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.auth(host); err != nil {
return err
}
buf := []byte{}
if s.network == "tcp" {
buf = append(buf, socks5c.VERSION_V5, socks5c.CMD_CONNECT, 0 /* reserved */)
} else {
buf = append(buf, socks5c.VERSION_V5, socks5c.CMD_ASSOCIATE, 0 /* reserved */)
}
if ip := net.ParseIP(host); ip != nil {
if ip4 := ip.To4(); ip4 != nil {
buf = append(buf, socks5c.ATYP_IPV4)
ip = ip4
} else {
buf = append(buf, socks5c.ATYP_IPV6)
}
buf = append(buf, ip...)
} else {
if len(host) > 255 {
return errors.New("proxy: destination host name too long: " + host)
}
buf = append(buf, socks5c.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(socks5c.Socks5Errors) {
failure = socks5c.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 socks5c.ATYP_IPV4:
bytesToDiscard = net.IPv4len
case socks5c.ATYP_IPV6:
bytesToDiscard = net.IPv6len
case socks5c.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 := socks5c.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) auth(host string) error {
// the size here is just an estimate
buf := make([]byte, 0, 6+len(host))
buf = append(buf, socks5c.VERSION_V5)
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
buf = append(buf, 2 /* num auth methods */, socks5c.Method_NO_AUTH, socks5c.Method_USER_PASS)
} else {
buf = append(buf, 1 /* num auth methods */, socks5c.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] == socks5c.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
}

View File

@ -0,0 +1,79 @@
package tests
import (
"io/ioutil"
"net"
"os"
"strings"
"testing"
"time"
proxyclient "github.com/snail007/goproxy/core/proxy/client"
sdk "github.com/snail007/goproxy/sdk/android-ios"
)
func TestSocks5(t *testing.T) {
estr := sdk.Start("s1", "socks -p :8185 --log test.log")
if estr != "" {
t.Fatal(estr)
}
p, e := proxyclient.SOCKS5(time.Second, nil)
if e != nil {
t.Error(e)
} else {
c, e := net.Dial("tcp", "127.0.0.1:8185")
if e != nil {
t.Fatal(e)
}
e = p.DialConn(&c, "tcp", "www.baidu.com:80")
if e != nil {
t.Fatal(e)
}
_, e = c.Write([]byte("Get / http/1.1\r\nHost: www.baidu.com\r\n"))
if e != nil {
t.Fatal(e)
}
b, e := ioutil.ReadAll(c)
if e != nil {
t.Fatal(e)
}
if !strings.HasPrefix(string(b), "HTTP") {
t.Fatalf("request baidu fail:%s", string(b))
}
}
sdk.Stop("s1")
os.Remove("test.log")
}
func TestSocks5Auth(t *testing.T) {
estr := sdk.Start("s1", "socks -p :8185 -a u:p --log test.log")
if estr != "" {
t.Fatal(estr)
}
p, e := proxyclient.SOCKS5(time.Second, &proxyclient.Auth{User: "u", Password: "p"})
if e != nil {
t.Error(e)
} else {
c, e := net.Dial("tcp", "127.0.0.1:8185")
if e != nil {
t.Fatal(e)
}
e = p.DialConn(&c, "tcp", "www.baidu.com:80")
if e != nil {
t.Fatal(e)
}
_, e = c.Write([]byte("Get / http/1.1\r\nHost: www.baidu.com\r\n"))
if e != nil {
t.Fatal(e)
}
b, e := ioutil.ReadAll(c)
if e != nil {
t.Fatal(e)
}
if !strings.HasPrefix(string(b), "HTTP") {
t.Fatalf("request baidu fail:%s", string(b))
}
}
sdk.Stop("s1")
os.Remove("test.log")
}

View File

@ -0,0 +1,375 @@
package socks5
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
"strconv"
"strings"
"time"
socks5c "github.com/snail007/goproxy/core/lib/socks5"
)
type BasicAuther interface {
CheckUserPass(username, password, userIP, localIP, toTarget string) bool
}
type Request struct {
ver uint8
cmd uint8
reserve uint8
addressType uint8
dstAddr string
dstPort string
dstHost string
bytes []byte
rw io.ReadWriter
}
func NewRequest(rw io.ReadWriter, header ...[]byte) (req Request, err interface{}) {
var b = make([]byte, 1024)
var n int
req = Request{rw: rw}
if header != nil && len(header) == 1 && len(header[0]) > 1 {
b = header[0]
n = len(header[0])
} else {
n, err = rw.Read(b[:])
if err != nil {
err = fmt.Errorf("read req data fail,ERR: %s", err)
return
}
}
req.ver = uint8(b[0])
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(socks5c.REP_REQ_FAIL)
return
}
switch b[3] {
case 0x01: //IP V4
req.dstHost = net.IPv4(b[4], b[5], b[6], b[7]).String()
case 0x03: //域名
req.dstHost = string(b[5 : n-2]) //b[4]表示域名的长度
case 0x04: //IP V6
req.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()
}
req.dstPort = strconv.Itoa(int(b[n-2])<<8 | int(b[n-1]))
req.dstAddr = net.JoinHostPort(req.dstHost, req.dstPort)
req.bytes = b[:n]
return
}
func (s *Request) Bytes() []byte {
return s.bytes
}
func (s *Request) Addr() string {
return s.dstAddr
}
func (s *Request) Host() string {
return s.dstHost
}
func (s *Request) Port() string {
return s.dstPort
}
func (s *Request) AType() uint8 {
return s.addressType
}
func (s *Request) CMD() uint8 {
return s.cmd
}
func (s *Request) TCPReply(rep uint8) (err error) {
_, err = s.rw.Write(s.NewReply(rep, "0.0.0.0:0"))
return
}
func (s *Request) UDPReply(rep uint8, addr string) (err error) {
_, err = s.rw.Write(s.NewReply(rep, addr))
return
}
func (s *Request) NewReply(rep uint8, addr string) []byte {
var response bytes.Buffer
host, port, _ := net.SplitHostPort(addr)
ip := net.ParseIP(host)
ipb := ip.To4()
atyp := socks5c.ATYP_IPV4
ipv6 := ip.To16()
zeroiIPv6 := fmt.Sprintf("%d%d%d%d%d%d%d%d%d%d%d%d",
ipv6[0], ipv6[1], ipv6[2], ipv6[3],
ipv6[4], ipv6[5], ipv6[6], ipv6[7],
ipv6[8], ipv6[9], ipv6[10], ipv6[11],
)
if ipb == nil && ipv6 != nil && "0000000000255255" != zeroiIPv6 {
atyp = socks5c.ATYP_IPV6
ipb = ip.To16()
}
porti, _ := strconv.Atoi(port)
portb := make([]byte, 2)
binary.BigEndian.PutUint16(portb, uint16(porti))
// log.Printf("atyp : %v", atyp)
// log.Printf("ip : %v", []byte(ip))
response.WriteByte(socks5c.VERSION_V5)
response.WriteByte(rep)
response.WriteByte(socks5c.RSV)
response.WriteByte(atyp)
response.Write(ipb)
response.Write(portb)
return response.Bytes()
}
type MethodsRequest struct {
ver uint8
methodsCount uint8
methods []uint8
bytes []byte
rw *io.ReadWriter
}
func NewMethodsRequest(r io.ReadWriter, header ...[]byte) (s MethodsRequest, err interface{}) {
defer func() {
if err == nil {
err = recover()
}
}()
s = MethodsRequest{}
s.rw = &r
var buf = make([]byte, 300)
var n int
if header != nil && len(header) == 1 && len(header[0]) > 1 {
buf = header[0]
n = len(header[0])
} else {
n, err = r.Read(buf)
if err != nil {
return
}
}
if buf[0] != 0x05 {
err = fmt.Errorf("socks version not supported")
return
}
if n != int(buf[1])+int(2) {
err = fmt.Errorf("socks methods data length error")
return
}
s.ver = buf[0]
s.methodsCount = buf[1]
s.methods = buf[2:n]
s.bytes = buf[:n]
return
}
func (s *MethodsRequest) Version() uint8 {
return s.ver
}
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 {
return true
}
}
return false
}
func (s *MethodsRequest) Reply(method uint8) (err error) {
_, err = (*s.rw).Write([]byte{byte(socks5c.VERSION_V5), byte(method)})
return
}
func (s *MethodsRequest) Bytes() []byte {
return s.bytes
}
type ServerConn struct {
target string
user string
password string
conn *net.Conn
timeout time.Duration
auth *BasicAuther
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 *BasicAuther, udpAddress string, header []byte) *ServerConn {
if udpAddress == "" {
udpAddress = "0.0.0.0:16666"
}
s := &ServerConn{
conn: conn,
timeout: timeout,
auth: auth,
header: header,
ver: socks5c.VERSION_V5,
udpAddress: udpAddress,
}
return s
}
func (s *ServerConn) Close() {
(*s.conn).Close()
}
func (s *ServerConn) AuthData() socks5c.UsernamePassword {
return socks5c.UsernamePassword{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()
localAddr := (*s.conn).LocalAddr()
//协商开始
//method select request
var methodReq MethodsRequest
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
methodReq, e := NewMethodsRequest((*s.conn), s.header)
(*s.conn).SetReadDeadline(time.Time{})
if e != nil {
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
methodReq.Reply(socks5c.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(socks5c.Method_NO_AUTH) && !methodReq.Select(socks5c.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 = socks5c.Method_NO_AUTH
//method select reply
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
err = methodReq.Reply(socks5c.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(socks5c.Method_USER_PASS) {
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
methodReq.Reply(socks5c.Method_NONE_ACCEPTABLE)
(*s.conn).SetReadDeadline(time.Time{})
err = fmt.Errorf("none method found : Method_USER_PASS")
return
}
s.method = socks5c.Method_USER_PASS
//method reply need auth
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
err = methodReq.Reply(socks5c.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(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
_userAddr := strings.Split(remoteAddr.String(), ":")
_localAddr := strings.Split(localAddr.String(), ":")
if s.auth == nil || (*s.auth).CheckUserPass(s.user, s.password, _userAddr[0], _localAddr[0], "") {
(*s.conn).SetDeadline(time.Now().Add(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(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(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 socks5c.CMD_BIND:
err = request.TCPReply(socks5c.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 socks5c.CMD_CONNECT:
err = request.TCPReply(socks5c.REP_SUCCESS)
if err != nil {
err = fmt.Errorf("TCPReply REP_SUCCESS to %s fail,ERR: %s", remoteAddr, err)
return
}
case socks5c.CMD_ASSOCIATE:
err = request.UDPReply(socks5c.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
}

35
core/tproxy/README.md Normal file
View File

@ -0,0 +1,35 @@
# Pass-through user IP manual
## Description:
By Linux TPROXY function,you can achieve the source Station service program can see the client's real IP, to achieve this feature requires linux operating systems and programs must meet certain conditions.
## Environmental requirements:
The source station must be a service program running on Linux, and Linux needs to meet the following conditions:
1. Linux kernel version >= 2.6.28
2. Determine whether the system supports TPROXY, execute:
grep TPROXY /boot/config-`uname -r`
If the output has the following result description is supported.
CONFIG_NETFILTER_XT_TARGET_TPROXY=m
## Deployment steps:
1. The tproxy environment setup script should be executed with root privileges every time the boot from the source Linux system: tproxy_setup.sh
2. Execute proxy proxy with root access on the source Linux system
## Parameter-tproxy is the tproxy function that turns on the proxy.
./proxy -tproxy
2. The IP address of the source station to listen to the program requires the use of: `127.0.1.1`
For example, the address of the source station before listening is: `0.0.0.0:8800`, now need to be modified to: `127.0.1.1:8800`
3. Forwarding rules inside the source address must be the corresponding, such as the above: `127.0.1.1:8800`

35
core/tproxy/README_ZH.md Normal file
View File

@ -0,0 +1,35 @@
# 透传用户IP手册
## 说明:
通过Linux的TPROXY功能,可以实现源站服务程序可以看见客户端真实IP,实现该功能需要linux操作系统和程序都要满足一定的条件.
## 环境要求:
源站必须是运行在Linux上面的服务程序,同时Linux需要满足下面条件:
1. Linux内核版本 >= 2.6.28
2. 判断系统是否支持TPROXY,执行:
grep TPROXY /boot/config-`uname -r`
如果输出有下面的结果说明支持.
CONFIG_NETFILTER_XT_TARGET_TPROXY=m
## 部署步骤:
1. 在源站的linux系统里面每次开机启动都要用root权限执行tproxy环境设置脚本:tproxy_setup.sh
2. 在源站的linux系统里面使用root权限执行代理proxy
## 参数 -tproxy 是开启代理的tproxy功能.
./proxy -tproxy
2. 源站的程序监听的地址IP需要使用:127.0.1.1
比如源站以前监听的地址是: 0.0.0.0:8800 , 现在需要修改为:127.0.1.1:8800
3. 转发规则里面源站地址必须是对应的,比如上面的:127.0.1.1:8800

249
core/tproxy/tproxy.go Normal file
View File

@ -0,0 +1,249 @@
// Package tproxy provides the TCPDial and TCPListen tproxy equivalent of the
// net package Dial and Listen with tproxy support for linux ONLY.
package tproxy
import (
"fmt"
"net"
"os"
"time"
"golang.org/x/sys/unix"
)
const big = 0xFFFFFF
const IP_ORIGADDRS = 20
// Debug outs the library in Debug mode
var Debug = false
func ipToSocksAddr(family int, ip net.IP, port int, zone string) (unix.Sockaddr, error) {
switch family {
case unix.AF_INET:
if len(ip) == 0 {
ip = net.IPv4zero
}
if ip = ip.To4(); ip == nil {
return nil, net.InvalidAddrError("non-IPv4 address")
}
sa := new(unix.SockaddrInet4)
for i := 0; i < net.IPv4len; i++ {
sa.Addr[i] = ip[i]
}
sa.Port = port
return sa, nil
case unix.AF_INET6:
if len(ip) == 0 {
ip = net.IPv6zero
}
// IPv4 callers use 0.0.0.0 to mean "announce on any available address".
// In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0",
// which it refuses to do. Rewrite to the IPv6 unspecified address.
if ip.Equal(net.IPv4zero) {
ip = net.IPv6zero
}
if ip = ip.To16(); ip == nil {
return nil, net.InvalidAddrError("non-IPv6 address")
}
sa := new(unix.SockaddrInet6)
for i := 0; i < net.IPv6len; i++ {
sa.Addr[i] = ip[i]
}
sa.Port = port
sa.ZoneId = uint32(zoneToInt(zone))
return sa, nil
}
return nil, net.InvalidAddrError("unexpected socket family")
}
func zoneToInt(zone string) int {
if zone == "" {
return 0
}
if ifi, err := net.InterfaceByName(zone); err == nil {
return ifi.Index
}
n, _, _ := dtoi(zone, 0)
return n
}
func dtoi(s string, i0 int) (n int, i int, ok bool) {
n = 0
for i = i0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
n = n*10 + int(s[i]-'0')
if n >= big {
return 0, i, false
}
}
if i == i0 {
return 0, i, false
}
return n, i, true
}
// IPTcpAddrToUnixSocksAddr returns Sockaddr for specified TCP addr.
func IPTcpAddrToUnixSocksAddr(addr string) (sa unix.Sockaddr, err error) {
if Debug {
fmt.Println("DEBUG: IPTcpAddrToUnixSocksAddr recieved address:", addr)
}
addressNet := "tcp6"
if addr[0] != '[' {
addressNet = "tcp4"
}
tcpAddr, err := net.ResolveTCPAddr(addressNet, addr)
if err != nil {
return nil, err
}
return ipToSocksAddr(ipType(addr), tcpAddr.IP, tcpAddr.Port, tcpAddr.Zone)
}
// IPv6UdpAddrToUnixSocksAddr returns Sockaddr for specified IPv6 addr.
func IPv6UdpAddrToUnixSocksAddr(addr string) (sa unix.Sockaddr, err error) {
tcpAddr, err := net.ResolveTCPAddr("udp6", addr)
if err != nil {
return nil, err
}
return ipToSocksAddr(unix.AF_INET6, tcpAddr.IP, tcpAddr.Port, tcpAddr.Zone)
}
// TCPListen is listening for incoming IP packets which are being intercepted.
// In conflict to regular Listen mehtod the socket destination and source addresses
// are of the intercepted connection.
// Else then that it works exactly like net package net.Listen.
func TCPListen(listenAddr string) (listener net.Listener, err error) {
s, err := unix.Socket(unix.AF_INET6, unix.SOCK_STREAM, 0)
if err != nil {
return nil, err
}
defer unix.Close(s)
err = unix.SetsockoptInt(s, unix.SOL_IP, unix.IP_TRANSPARENT, 1)
if err != nil {
return nil, err
}
sa, err := IPTcpAddrToUnixSocksAddr(listenAddr)
if err != nil {
return nil, err
}
err = unix.Bind(s, sa)
if err != nil {
return nil, err
}
err = unix.Listen(s, unix.SOMAXCONN)
if err != nil {
return nil, err
}
f := os.NewFile(uintptr(s), "TProxy")
defer f.Close()
return net.FileListener(f)
}
func ipType(localAddr string) int {
host, _, _ := net.SplitHostPort(localAddr)
if host != "" {
ip := net.ParseIP(host)
if ip == nil || ip.To4() != nil {
return unix.AF_INET
}
return unix.AF_INET6
}
return unix.AF_INET
}
// TCPDial is a special tcp connection which binds a non local address as the source.
// Except then the option to bind to a specific local address which the machine doesn't posses
// it is exactly like any other net.Conn connection.
// It is advised to use port numbered 0 in the localAddr and leave the kernel to choose which
// Local port to use in order to avoid errors and binding conflicts.
func TCPDial(localAddr, remoteAddr string, timeout time.Duration) (conn net.Conn, err error) {
timer := time.NewTimer(timeout)
defer timer.Stop()
if Debug {
fmt.Println("TCPDial from:", localAddr, "to:", remoteAddr)
}
s, err := unix.Socket(ipType(localAddr), unix.SOCK_STREAM, 0)
//In a case there was a need for a non-blocking socket an example
//s, err := unix.Socket(unix.AF_INET6, unix.SOCK_STREAM |unix.SOCK_NONBLOCK, 0)
if err != nil {
fmt.Println(err)
return nil, err
}
defer unix.Close(s)
err = unix.SetsockoptInt(s, unix.SOL_IP, unix.IP_TRANSPARENT, 1)
if err != nil {
if Debug {
fmt.Println("ERROR setting the socket in IP_TRANSPARENT mode", err)
}
return nil, err
}
err = unix.SetsockoptInt(s, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
if err != nil {
if Debug {
fmt.Println("ERROR setting the socket in unix.SO_REUSEADDR mode", err)
}
return nil, err
}
rhost, _, err := net.SplitHostPort(localAddr)
if err != nil {
if Debug {
// fmt.Fprintln(os.Stderr, err)
fmt.Println("ERROR", err, "running net.SplitHostPort on address:", localAddr)
}
}
sa, err := IPTcpAddrToUnixSocksAddr(rhost + ":0")
if err != nil {
if Debug {
fmt.Println("ERROR creating a hostaddres for the socker with IPTcpAddrToUnixSocksAddr", err)
}
return nil, err
}
remoteSocket, err := IPTcpAddrToUnixSocksAddr(remoteAddr)
if err != nil {
if Debug {
fmt.Println("ERROR creating a remoteSocket for the socker with IPTcpAddrToUnixSocksAddr on the remote addres", err)
}
return nil, err
}
err = unix.Bind(s, sa)
if err != nil {
fmt.Println(err)
return nil, err
}
errChn := make(chan error, 1)
func() {
err = unix.Connect(s, remoteSocket)
if err != nil {
if Debug {
fmt.Println("ERROR Connecting from", s, "to:", remoteSocket, "ERROR:", err)
}
}
errChn <- err
}()
select {
case err = <-errChn:
if err != nil {
return nil, err
}
case <-timer.C:
return nil, fmt.Errorf("ERROR connect to %s timeout", remoteAddr)
}
f := os.NewFile(uintptr(s), "TProxyTCPClient")
client, err := net.FileConn(f)
if err != nil {
if Debug {
fmt.Println("ERROR os.NewFile", err)
}
return nil, err
}
if Debug {
fmt.Println("FINISHED Creating net.coo from:", client.LocalAddr().String(), "to:", client.RemoteAddr().String())
}
return client, err
}

View File

@ -0,0 +1,30 @@
#!/bin/bash
SOURCE_BIND_IP="127.0.1.1"
echo 0 > /proc/sys/net/ipv4/conf/lo/rp_filter
echo 2 > /proc/sys/net/ipv4/conf/default/rp_filter
echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 1 > /proc/sys/net/ipv4/conf/all/send_redirects
echo 1 > /proc/sys/net/ipv4/conf/all/forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
# 本地的话,貌似这段不需要
# iptables -t mangle -N DIVERT >/dev/null 2>&1
# iptables -t mangle -F DIVERT
# iptables -t mangle -D PREROUTING -p tcp -m socket -j DIVERT >/dev/null 2>&1
# iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
# iptables -t mangle -A DIVERT -j MARK --set-mark 1
# iptables -t mangle -A DIVERT -j ACCEPT
ip rule del fwmark 1 lookup 100
ip rule add fwmark 1 lookup 100
ip route del local 0.0.0.0/0 dev lo table 100
ip route add local 0.0.0.0/0 dev lo table 100
ip rule del from ${SOURCE_BIND_IP} table 101
ip rule add from ${SOURCE_BIND_IP} table 101
ip route del default via 127.0.0.1 dev lo table 101
ip route add default via 127.0.0.1 dev lo table 101
ip route flush cache
ip ro flush cache

19
docker/Dockerfile Normal file
View File

@ -0,0 +1,19 @@
FROM golang:alpine AS builder
WORKDIR $GOPATH
ARG GOPROXY_VERSION=master
RUN apk update; apk upgrade; \
apk add --no-cache ca-certificates git; \
cd /go/src/; \
mkdir -p github.com/snail007; \
cd github.com/snail007; \
git clone --depth=1 https://github.com/snail007/goproxy.git; \
cd goproxy; \
git checkout ${GOPROXY_VERSION}; \
CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -a -installsuffix cgo -o proxy; \
chmod 0777 proxy
FROM debian:stable-slim
COPY --from=builder /go/src/github.com/snail007/goproxy/proxy /usr/local/bin/
# RUN chmod 0777 /usr/local/bin/proxy
EXPOSE 80 443
CMD /usr/local/bin/proxy http -t tcp -p :80,:443

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 19 KiB

25
docs/old-release-zh.md Normal file
View File

@ -0,0 +1,25 @@
# Old Versions of Proxy
- [v5.3手册](https://github.com/snail007/goproxy/tree/v5.3)
- [v5.2手册](https://github.com/snail007/goproxy/tree/v5.2)
- [v5.1手册](https://github.com/snail007/goproxy/tree/v5.1)
- [v5.0手册](https://github.com/snail007/goproxy/tree/v5.0)
- [v4.9手册](https://github.com/snail007/goproxy/tree/v4.9)
- [v4.8手册](https://github.com/snail007/goproxy/tree/v4.8)
- [v4.7手册](https://github.com/snail007/goproxy/tree/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)
- [v3.9手册](https://github.com/snail007/goproxy/tree/v3.9)
- [v3.8手册](https://github.com/snail007/goproxy/tree/v3.8)
- [v3.6-v3.7手册](https://github.com/snail007/goproxy/tree/v3.6)
- [v3.5手册](https://github.com/snail007/goproxy/tree/v3.5)
- [v3.4手册](https://github.com/snail007/goproxy/tree/v3.4)
- [v3.3手册](https://github.com/snail007/goproxy/tree/v3.3)
- [v3.2手册](https://github.com/snail007/goproxy/tree/v3.2)
- [v3.1手册](https://github.com/snail007/goproxy/tree/v3.1)
- [v3.0手册](https://github.com/snail007/goproxy/tree/v3.0)
- [v2.x手册](https://github.com/snail007/goproxy/tree/v2.2)

View File

@ -1,25 +1,25 @@
# Old Versions of Proxy # Old Versions of Proxy
- [v5.3手册](https://github.com/snail007/goproxy/tree/v5.3) - [v5.3 Manual](https://github.com/snail007/goproxy/tree/v5.3)
- [v5.2手册](https://github.com/snail007/goproxy/tree/v5.2) - [v5.2 Manual](https://github.com/snail007/goproxy/tree/v5.2)
- [v5.1手册](https://github.com/snail007/goproxy/tree/v5.1) - [v5.1 Manual](https://github.com/snail007/goproxy/tree/v5.1)
- [v5.0手册](https://github.com/snail007/goproxy/tree/v5.0) - [v5.0 Manual](https://github.com/snail007/goproxy/tree/v5.0)
- [v4.9手册](https://github.com/snail007/goproxy/tree/v4.9) - [v4.9 Manual](https://github.com/snail007/goproxy/tree/v4.9)
- [v4.8手册](https://github.com/snail007/goproxy/tree/v4.8) - [v4.8 Manual](https://github.com/snail007/goproxy/tree/v4.8)
- [v4.7手册](https://github.com/snail007/goproxy/tree/v4.7) - [v4.7 Manual](https://github.com/snail007/goproxy/tree/v4.7)
- [v4.6手册](https://github.com/snail007/goproxy/tree/v4.6) - [v4.6 Manual](https://github.com/snail007/goproxy/tree/v4.6)
- [v4.5手册](https://github.com/snail007/goproxy/tree/v4.5) - [v4.5 Manual](https://github.com/snail007/goproxy/tree/v4.5)
- [v4.4手册](https://github.com/snail007/goproxy/tree/v4.4) - [v4.4 Manual](https://github.com/snail007/goproxy/tree/v4.4)
- [v4.3手册](https://github.com/snail007/goproxy/tree/v4.3) - [v4.3 Manual](https://github.com/snail007/goproxy/tree/v4.3)
- [v4.2手册](https://github.com/snail007/goproxy/tree/v4.2) - [v4.2 Manual](https://github.com/snail007/goproxy/tree/v4.2)
- [v4.0-v4.1手册](https://github.com/snail007/goproxy/tree/v4.1) - [v4.0-v4.1 Manual](https://github.com/snail007/goproxy/tree/v4.1)
- [v3.9手册](https://github.com/snail007/goproxy/tree/v3.9) - [v3.9 Manual](https://github.com/snail007/goproxy/tree/v3.9)
- [v3.8手册](https://github.com/snail007/goproxy/tree/v3.8) - [v3.8 Manual](https://github.com/snail007/goproxy/tree/v3.8)
- [v3.6-v3.7手册](https://github.com/snail007/goproxy/tree/v3.6) - [v3.6-v3.7 Manual](https://github.com/snail007/goproxy/tree/v3.6)
- [v3.5手册](https://github.com/snail007/goproxy/tree/v3.5) - [v3.5 Manual](https://github.com/snail007/goproxy/tree/v3.5)
- [v3.4手册](https://github.com/snail007/goproxy/tree/v3.4) - [v3.4 Manual](https://github.com/snail007/goproxy/tree/v3.4)
- [v3.3手册](https://github.com/snail007/goproxy/tree/v3.3) - [v3.3 Manual](https://github.com/snail007/goproxy/tree/v3.3)
- [v3.2手册](https://github.com/snail007/goproxy/tree/v3.2) - [v3.2 Manual](https://github.com/snail007/goproxy/tree/v3.2)
- [v3.1手册](https://github.com/snail007/goproxy/tree/v3.1) - [v3.1 Manual](https://github.com/snail007/goproxy/tree/v3.1)
- [v3.0手册](https://github.com/snail007/goproxy/tree/v3.0) - [v3.0 Manual](https://github.com/snail007/goproxy/tree/v3.0)
- [v2.x手册](https://github.com/snail007/goproxy/tree/v2.2) - [v2.x Manual](https://github.com/snail007/goproxy/tree/v2.2)

View File

@ -1,13 +1,13 @@
# Proxy-GUI # Proxy-GUI
基于proxy的各平台SDK,作者和众多热心人士开发了各平台的GUI版本的proxy,下面分平台介绍. Based on the proxy platform SDK, the author and many enthusiasts have developed the GUI version of the proxy for each platform.
## Windows ## Windows
- 官方java版本,项目主页:[goproxy-jui](https://github.com/snail007/goproxy-jui) - Official java version, Project Homepage:[goproxy-jui](https://github.com/snail007/goproxy-jui)
## Linux ## Linux
- 官方java版本,项目主页:[goproxy-jui](https://github.com/snail007/goproxy-jui) - Official java version, Project Homepage:[goproxy-jui](https://github.com/snail007/goproxy-jui)
## MacOS ## MacOS
@ -15,13 +15,13 @@
## Android ## Android
- proxy-go,一个非官方实现版本,界面比较简陋,但是够用.下载地址:[proxy-go](https://github.com/snail007/goproxy-gui-stuff/releases/tag/proxy-go-release) - proxy-go,An unofficial implementation version, the interface is relatively simple, but enough.Download address:[proxy-go](https://github.com/snail007/goproxy-gui-stuff/releases/tag/proxy-go-release)
## IOS ## IOS
- Coming Soon ... - Coming Soon ...
## 跨平台 ## Cross-platform
- proxy-web,一个跨平台的web UI版本,项目主页:[proxy-web](https://github.com/yincongcyincong/proxy-web) - proxy-web,A cross-platform web UI version,project home page:[proxy-web](https://github.com/yincongcyincong/proxy-web)

27
gui/README_ZH.md Normal file
View File

@ -0,0 +1,27 @@
# Proxy-GUI
基于proxy的各平台SDK,作者和众多热心人士开发了各平台的GUI版本的proxy,下面分平台介绍.
## Windows
- 官方java版本,项目主页:[goproxy-jui](https://github.com/snail007/goproxy-jui)
## Linux
- 官方java版本,项目主页:[goproxy-jui](https://github.com/snail007/goproxy-jui)
## MacOS
- Coming Soon ...
## Android
- proxy-go,一个非官方实现版本,界面比较简陋,但是够用.下载地址:[proxy-go](https://github.com/snail007/goproxy-gui-stuff/releases/tag/proxy-go-release)
## IOS
- Coming Soon ...
## 跨平台
- proxy-web,一个跨平台的web UI版本,项目主页:[proxy-web](https://github.com/yincongcyincong/proxy-web)

View File

@ -5,7 +5,10 @@ if [ -e /tmp/proxy ]; then
fi fi
mkdir /tmp/proxy mkdir /tmp/proxy
cd /tmp/proxy cd /tmp/proxy
wget https://github.com/snail007/goproxy/releases/download/v5.4/proxy-linux-amd64.tar.gz
LAST_VERSION=$(curl --silent "https://api.github.com/repos/snail007/goproxy/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')
wget "https://github.com/snail007/goproxy/releases/download/${LAST_VERSION}/proxy-linux-amd64.tar.gz"
# #install proxy # #install proxy
tar zxvf proxy-linux-amd64.tar.gz tar zxvf proxy-linux-amd64.tar.gz

14
main.go
View File

@ -1,15 +1,17 @@
package main package main
import ( import (
"fmt"
"log" "log"
"os" "os"
"os/signal" "os/signal"
"runtime/debug"
"syscall" "syscall"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
) )
const APP_VERSION = "5.4" var APP_VERSION = "No Version Provided"
func main() { func main() {
err := initConfig() err := initConfig()
@ -32,7 +34,12 @@ func Clean(s *services.Service) {
syscall.SIGTERM, syscall.SIGTERM,
syscall.SIGQUIT) syscall.SIGQUIT)
go func() { go func() {
for _ = range signalChan { defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
}
}()
for range signalChan {
log.Println("Received an interrupt, stopping services...") log.Println("Received an interrupt, stopping services...")
if s != nil && *s != nil { if s != nil && *s != nil {
(*s).Clean() (*s).Clean()
@ -41,11 +48,12 @@ func Clean(s *services.Service) {
log.Printf("clean process %d", cmd.Process.Pid) log.Printf("clean process %d", cmd.Process.Pid)
cmd.Process.Kill() cmd.Process.Kill()
} }
if isDebug { if *isDebug {
saveProfiling() saveProfiling()
} }
cleanupDone <- true cleanupDone <- true
} }
}() }()
<-cleanupDone <-cleanupDone
os.Exit(0)
} }

View File

@ -1,74 +1,84 @@
#!/bin/bash #!/bin/bash
VER="5.4" VERSION=$(cat VERSION)
RELEASE="release-${VER}" VER="${VERSION}_$(date '+%Y%m%d%H%M%S')"
X="-X main.APP_VERSION=$VER"
RELEASE="release-${VERSION}"
TRIMPATH1="/Users/snail/go/src/github.com/snail007"
TRIMPATH=$(dirname ~/go/src/github.com/snail007)/snail007
if [ -d "$TRIMPATH1" ];then
TRIMPATH=$TRIMPATH1
fi
OPTS="-gcflags=-trimpath=$TRIMPATH -asmflags=-trimpath=$TRIMPATH"
rm -rf .cert rm -rf .cert
mkdir .cert mkdir .cert
go build -o proxy go build $OPTS -ldflags "$X" -o proxy
cd .cert cd .cert
../proxy keygen -C proxy ../proxy keygen -C proxy
cd .. cd ..
rm -rf ${RELEASE} rm -rf ${RELEASE}
mkdir ${RELEASE} mkdir ${RELEASE}
#linux #linux
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-386.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=386 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-amd64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm-v6.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm-v6.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=6 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm64-v6.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=6 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm64-v6.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm-v7.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm-v7.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=7 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm64-v7.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=7 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm64-v7.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm-v5.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm-v5.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=5 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm64-v5.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=5 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm64-v5.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm64-v8.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=arm go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm-v8.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips64le.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=mips go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-mipsle.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=ppc64 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-ppc64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips64le.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-ppc64le.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-mipsle.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=s390x go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-s390x.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips-softfloat.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mips64 GOMIPS=softfloat go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips64-softfloat.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mips64le GOMIPS=softfloat go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips64le-softfloat.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-mipsle-softfloat.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=ppc64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-ppc64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-ppc64le.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=s390x go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-s390x.tar.gz" proxy direct blocked
#android #android
CGO_ENABLED=0 GOOS=android GOARCH=386 go build -o proxy && tar zcfv "${RELEASE}/proxy-android-386.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=android GOARCH=386 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-android-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=android GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-android-amd64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=android GOARCH=amd64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-android-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=android GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-android-arm.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=android GOARCH=arm go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-android-arm.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=android GOARCH=arm64 go build -o proxy && tar zcfv "${RELEASE}/proxy-android-arm64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=android GOARCH=arm64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-android-arm64.tar.gz" proxy direct blocked
#darwin #darwin
CGO_ENABLED=0 GOOS=darwin GOARCH=386 go build -o proxy && tar zcfv "${RELEASE}/proxy-darwin-386.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=darwin GOARCH=386 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-darwin-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-darwin-amd64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-darwin-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=darwin GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-darwin-arm.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=darwin GOARCH=arm go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-darwin-arm.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o proxy && tar zcfv "${RELEASE}/proxy-darwin-arm64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-darwin-arm64.tar.gz" proxy direct blocked
#dragonfly #dragonfly
CGO_ENABLED=0 GOOS=dragonfly GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-dragonfly-amd64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=dragonfly GOARCH=amd64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-dragonfly-amd64.tar.gz" proxy direct blocked
#freebsd #freebsd
CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -o proxy && tar zcfv "${RELEASE}/proxy-freebsd-386.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-freebsd-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-freebsd-amd64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-freebsd-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=freebsd GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-freebsd-arm.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=freebsd GOARCH=arm go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-freebsd-arm.tar.gz" proxy direct blocked
#nacl #nacl
CGO_ENABLED=0 GOOS=nacl GOARCH=386 go build -o proxy && tar zcfv "${RELEASE}/proxy-nacl-386.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=nacl GOARCH=386 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-nacl-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=nacl GOARCH=amd64p32 go build -o proxy && tar zcfv "${RELEASE}/proxy-nacl-amd64p32.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=nacl GOARCH=amd64p32 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-nacl-amd64p32.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=nacl GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-nacl-arm.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=nacl GOARCH=arm go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-nacl-arm.tar.gz" proxy direct blocked
#netbsd #netbsd
CGO_ENABLED=0 GOOS=netbsd GOARCH=386 go build -o proxy && tar zcfv "${RELEASE}/proxy-netbsd-386.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=netbsd GOARCH=386 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-netbsd-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=netbsd GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-netbsd-amd64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=netbsd GOARCH=amd64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-netbsd-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=netbsd GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-netbsd-arm.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=netbsd GOARCH=arm go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-netbsd-arm.tar.gz" proxy direct blocked
#openbsd #openbsd
CGO_ENABLED=0 GOOS=openbsd GOARCH=386 go build -o proxy && tar zcfv "${RELEASE}/proxy-openbsd-386.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=openbsd GOARCH=386 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-openbsd-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=openbsd GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-openbsd-amd64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=openbsd GOARCH=amd64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-openbsd-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=openbsd GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-openbsd-arm.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=openbsd GOARCH=arm go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-openbsd-arm.tar.gz" proxy direct blocked
#plan9 #plan9
CGO_ENABLED=0 GOOS=plan9 GOARCH=386 go build -o proxy && tar zcfv "${RELEASE}/proxy-plan9-386.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=plan9 GOARCH=386 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-plan9-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=plan9 GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-plan9-amd64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=plan9 GOARCH=amd64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-plan9-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=plan9 GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-plan9-arm.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=plan9 GOARCH=arm go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-plan9-arm.tar.gz" proxy direct blocked
#solaris #solaris
CGO_ENABLED=0 GOOS=solaris GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-solaris-amd64.tar.gz" proxy direct blocked CGO_ENABLED=0 GOOS=solaris GOARCH=amd64 go build $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-solaris-amd64.tar.gz" proxy direct blocked
#windows #windows
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-H=windowsgui" -o proxy-noconsole.exe CGO_ENABLED=0 GOOS=windows GOARCH=386 go build $OPTS -ldflags="-H=windowsgui $X" -o proxy-noconsole.exe
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -o proxy.exe && tar zcfv "${RELEASE}/proxy-windows-386.tar.gz" proxy.exe proxy-noconsole.exe direct blocked .cert/proxy.crt .cert/proxy.key CGO_ENABLED=0 GOOS=windows GOARCH=386 go build $OPTS -ldflags "$X" -o proxy.exe && tar zcfv "${RELEASE}/proxy-windows-386.tar.gz" proxy.exe proxy-noconsole.exe direct blocked .cert/proxy.crt .cert/proxy.key
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-H=windowsgui" -o proxy-noconsole.exe CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build $OPTS -ldflags="-H=windowsgui $X" -o proxy-noconsole.exe
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o proxy.exe && tar zcfv "${RELEASE}/proxy-windows-amd64.tar.gz" proxy.exe proxy-noconsole.exe direct blocked .cert/proxy.crt .cert/proxy.key CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build $OPTS -ldflags "$X" -o proxy.exe && tar zcfv "${RELEASE}/proxy-windows-amd64.tar.gz" proxy.exe proxy-noconsole.exe direct blocked .cert/proxy.crt .cert/proxy.key
rm -rf proxy proxy.exe proxy-noconsole.exe .cert rm -rf proxy proxy.exe proxy-noconsole.exe .cert
#todo
#1.release.sh VER="xxx"
#2.main.go APP_VERSION="xxx"
#3.install_auto.sh goproxy/releases/download/vxxx
#4.README goproxy/releases/download/vxxx

View File

@ -1,21 +0,0 @@
SDK更新日志
v5.4
1.去掉了无用参数
v5.3
1.增加了支持日志输出回调的方法:
StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
2.优化了socks_client握手端口判断,避免了sstap测试UDP失败的问题..
3.修复了HTTP(S)\SPS反向代理无法正常工作的问题.
4.优化了智能判断,减少不必要的DNS解析.
5.重构了SOCKS和SPS的UDP功能,基于UDP的游戏加速嗖嗖的.
v4.9
1.修复了HTTP Basic代理返回不合适的头部,导致浏览器不会弹框,个别代理插件无法认证的问题.
2.内网穿透切换smux到yamux.
3.优化了HTTP(S)\SOCKS5代理--always的处理逻辑.
v4.8
1.修复了多个服务同时开启日志,只会输出到最后一个日志文件的bug.
2.增加了获取sdk版本的Version()方法.

View File

@ -1,259 +0,0 @@
# Proxy SDK 使用说明
支持以下平台:
- Android,`.arr`
- IOS,`.framework`
- Windows,`.dll`
- Linux,`.so`
- MacOS,`.dylib`
proxy使用gombile实现了一份go代码编译为android和ios平台下面可以直接调用的sdk类库,
另外还为linux和windows,MacOS提供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.Proxy
```
#### 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 <Proxy/Proxy.objc.h>
```
#### 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 <stdio.h>
#include<stdlib.h>
#include <string.h>
#include<pthread.h>
#include<Windows.h>
#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 <stdio.h>
#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参数.

View File

@ -13,8 +13,8 @@ import (
"github.com/miekg/dns" "github.com/miekg/dns"
gocache "github.com/pmylund/go-cache" gocache "github.com/pmylund/go-cache"
"github.com/snail007/goproxy/core/lib/kcpcfg"
services "github.com/snail007/goproxy/services" services "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
) )
type DNSArgs struct { type DNSArgs struct {
@ -58,6 +58,22 @@ func (s *DNS) InitService() (err error) {
s.cache = gocache.New(time.Second*time.Duration(*s.cfg.DNSTTL), time.Second*60) s.cache = gocache.New(time.Second*time.Duration(*s.cfg.DNSTTL), time.Second*60)
s.cache.LoadFile(*s.cfg.CacheFile) s.cache.LoadFile(*s.cfg.CacheFile)
go func() { go func() {
for {
select {
case <-s.exitSig:
return
case <-time.After(time.Second * 300):
s.cache.DeleteExpired()
s.cache.SaveFile(*s.cfg.CacheFile)
}
}
}()
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for { for {
select { select {
case <-s.exitSig: case <-s.exitSig:
@ -135,6 +151,11 @@ func (s *DNS) Start(args interface{}, log *logger.Logger) (err error) {
} }
dns.HandleFunc(".", s.callback) dns.HandleFunc(".", s.callback)
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
log.Printf("dns server on udp %s", *s.cfg.Local) log.Printf("dns server on udp %s", *s.cfg.Local)
err := dns.ListenAndServe(*s.cfg.Local, "udp", nil) err := dns.ListenAndServe(*s.cfg.Local, "udp", nil)
if err != nil { if err != nil {

View File

@ -1,5 +1,8 @@
#/bin/bash #/bin/bash
VER="v5.3" VERSION=$(cat ../../VERSION)
VER="${VERSION}_$(date '+%Y%m%d%H%M%S')"
X="-X github.com/snail007/goproxy/sdk/android-ios.SDK_VERSION=$VER -X main.APP_VERSION=$VER"
rm -rf sdk-android-*.tar.gz rm -rf sdk-android-*.tar.gz
rm -rf android rm -rf android
mkdir android mkdir android
@ -14,11 +17,11 @@ mkdir android
#go get -v golang.org/x/mobile/cmd/gomobile #go get -v golang.org/x/mobile/cmd/gomobile
#gomobile init #gomobile init
gomobile bind -v -target=android -javapkg=snail007 -ldflags="-s -w" gomobile bind -v -target=android -javapkg=snail007 -ldflags="-s -w $X"
mv proxy.aar android/snail007.goproxy.sdk.aar mv proxy.aar android/snail007.goproxy.sdk.aar
mv proxy-sources.jar android/snail007.goproxy.sdk-sources.jar mv proxy-sources.jar android/snail007.goproxy.sdk-sources.jar
cp ../README.md android cp ../README.md android
tar zcfv sdk-android-${VER}.tar.gz android tar zcfv sdk-android-${VERSION}.tar.gz android
rm -rf android rm -rf android
echo "done." echo "done."

View File

@ -1,14 +1,17 @@
#/bin/bash #/bin/bash
VER="v5.3" VERSION=$(cat ../../VERSION)
VER="${VERSION}_$(date '+%Y%m%d%H%M%S')"
X="-X github.com/snail007/goproxy/sdk/android-ios.SDK_VERSION=$VER -X main.APP_VERSION=$VER"
rm -rf sdk-ios-*.tar.gz rm -rf sdk-ios-*.tar.gz
rm -rf ios rm -rf ios
mkdir ios mkdir ios
#ios XCode required #ios XCode required
gomobile bind -v -target=ios -ldflags="-s -w" gomobile bind -v -target=ios -ldflags="-s -w $X"
mv Proxy.framework ios mv Proxy.framework ios
cp ../README.md ios cp ../README.md ios
tar zcfv sdk-ios-${VER}.tar.gz ios tar zcfv sdk-ios-${VERSION}.tar.gz ios
rm -rf ios rm -rf ios
echo "done." echo "done."

View File

@ -8,26 +8,35 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"runtime/pprof"
"strings" "strings"
"sync"
"github.com/snail007/goproxy/core/lib/kcpcfg"
encryptconn "github.com/snail007/goproxy/core/lib/transport/encrypt"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
httpx "github.com/snail007/goproxy/services/http" httpx "github.com/snail007/goproxy/services/http"
"github.com/snail007/goproxy/services/kcpcfg" keygenx "github.com/snail007/goproxy/services/keygen"
mux "github.com/snail007/goproxy/services/mux" mux "github.com/snail007/goproxy/services/mux"
socksx "github.com/snail007/goproxy/services/socks" socksx "github.com/snail007/goproxy/services/socks"
spsx "github.com/snail007/goproxy/services/sps" spsx "github.com/snail007/goproxy/services/sps"
tcpx "github.com/snail007/goproxy/services/tcp" tcpx "github.com/snail007/goproxy/services/tcp"
tunnel "github.com/snail007/goproxy/services/tunnel" tunnelx "github.com/snail007/goproxy/services/tunnel"
udpx "github.com/snail007/goproxy/services/udp" udpx "github.com/snail007/goproxy/services/udp"
kcp "github.com/xtaci/kcp-go" kcp "github.com/xtaci/kcp-go"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
kingpin "gopkg.in/alecthomas/kingpin.v2" kingpin "gopkg.in/alecthomas/kingpin.v2"
) )
const SDK_VERSION = "5.3" var SDK_VERSION = "No Version Provided"
var ( var (
app *kingpin.Application app *kingpin.Application
cpuProfilingFile, memProfilingFile, blockProfilingFile,
goroutineProfilingFile, threadcreateProfilingFile *os.File
isProfiling bool
profilingLock = &sync.Mutex{}
) )
type LogCallback interface { type LogCallback interface {
@ -62,9 +71,9 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
//define args //define args
tcpArgs := tcpx.TCPArgs{} tcpArgs := tcpx.TCPArgs{}
httpArgs := httpx.HTTPArgs{} httpArgs := httpx.HTTPArgs{}
tunnelServerArgs := tunnel.TunnelServerArgs{} tunnelServerArgs := tunnelx.TunnelServerArgs{}
tunnelClientArgs := tunnel.TunnelClientArgs{} tunnelClientArgs := tunnelx.TunnelClientArgs{}
tunnelBridgeArgs := tunnel.TunnelBridgeArgs{} tunnelBridgeArgs := tunnelx.TunnelBridgeArgs{}
muxServerArgs := mux.MuxServerArgs{} muxServerArgs := mux.MuxServerArgs{}
muxClientArgs := mux.MuxClientArgs{} muxClientArgs := mux.MuxClientArgs{}
muxBridgeArgs := mux.MuxBridgeArgs{} muxBridgeArgs := mux.MuxBridgeArgs{}
@ -72,6 +81,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
socksArgs := socksx.SocksArgs{} socksArgs := socksx.SocksArgs{}
spsArgs := spsx.SPSArgs{} spsArgs := spsx.SPSArgs{}
dnsArgs := DNSArgs{} dnsArgs := DNSArgs{}
keygenArgs := keygenx.KeygenArgs{}
kcpArgs := kcpcfg.KCPConfigArgs{} kcpArgs := kcpcfg.KCPConfigArgs{}
//build srvice args //build srvice args
app = kingpin.New("proxy", "happy with proxy") app = kingpin.New("proxy", "happy with proxy")
@ -99,7 +109,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
//########http######### //########http#########
http := app.Command("http", "proxy on http mode") 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.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').Strings()
httpArgs.CaCertFile = http.Flag("ca", "ca cert file for tls").Default("").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.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.KeyFile = http.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
@ -130,7 +140,16 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
httpArgs.ParentKey = http.Flag("parent-key", "the password for auto encrypt/decrypt parent 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.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() httpArgs.ParentCompress = http.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
httpArgs.Intelligent = http.Flag("intelligent", "settting intelligent HTTP, SOCKS5 proxy mode, can be <intelligent|direct|parent>").Default("intelligent").Enum("intelligent", "direct", "parent")
httpArgs.LoadBalanceMethod = http.Flag("lb-method", "load balance method when use multiple parent,can be <roundrobin|leastconn|leasttime|hash|weight>").Default("roundrobin").Enum("roundrobin", "weight", "leastconn", "leasttime", "hash")
httpArgs.LoadBalanceTimeout = http.Flag("lb-timeout", "tcp milliseconds timeout of connecting to parent").Default("500").Int()
httpArgs.LoadBalanceRetryTime = http.Flag("lb-retrytime", "sleep time milliseconds after checking").Default("1000").Int()
httpArgs.LoadBalanceHashTarget = http.Flag("lb-hashtarget", "use target address to choose parent for LB").Default("false").Bool()
httpArgs.LoadBalanceOnlyHA = http.Flag("lb-onlyha", "use only `high availability mode` to choose parent for LB").Default("false").Bool()
httpArgs.RateLimit = http.Flag("rate-limit", "rate limit (bytes/second) of each connection, such as: 100K 1.5M . 0 means no limitation").Short('l').Default("0").String()
httpArgs.BindListen = http.Flag("bind-listen", "using listener binding IP when connect to target").Short('B').Default("false").Bool()
httpArgs.Jumper = http.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
httpArgs.Debug = debug
//########tcp######### //########tcp#########
tcp := app.Command("tcp", "proxy on tcp mode") 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.Parent = tcp.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
@ -139,6 +158,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
tcpArgs.Timeout = tcp.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('e').Default("2000").Int() tcpArgs.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 <tls|tcp|kcp|udp>").Short('T').Enum("tls", "tcp", "udp", "kcp") tcpArgs.ParentType = tcp.Flag("parent-type", "parent protocol type <tls|tcp|kcp|udp>").Short('T').Enum("tls", "tcp", "udp", "kcp")
tcpArgs.LocalType = tcp.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") tcpArgs.LocalType = tcp.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp")
tcpArgs.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
tcpArgs.Jumper = tcp.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() tcpArgs.Jumper = tcp.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
@ -155,7 +175,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
//########mux-server######### //########mux-server#########
muxServer := app.Command("server", "proxy on mux server mode") 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.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 <tls|tcp|kcp>").Default("tls").Short('T').Enum("tls", "tcp", "kcp") muxServerArgs.ParentType = muxServer.Flag("parent-type", "parent protocol type <tls|tcp|tcps|kcp|tou>").Default("tls").Short('T').Enum("tls", "tcp", "tcps", "kcp", "tou")
muxServerArgs.CertFile = muxServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() 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.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.Timeout = muxServer.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
@ -165,11 +185,15 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp|tls 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() muxServerArgs.SessionCount = muxServer.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
muxServerArgs.Jumper = muxServer.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() muxServerArgs.Jumper = muxServer.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
muxServerArgs.TCPSMethod = muxServer.Flag("tcps-method", "method of parent tcps's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxServerArgs.TCPSPassword = muxServer.Flag("tcps-password", "password of parent tcps's encrpyt/decrypt").Default("snail007's_goproxy").String()
muxServerArgs.TOUMethod = muxServer.Flag("tou-method", "method of parent tou's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxServerArgs.TOUPassword = muxServer.Flag("tou-password", "password of parent tou's encrpyt/decrypt").Default("snail007's_goproxy").String()
//########mux-client######### //########mux-client#########
muxClient := app.Command("client", "proxy on mux client mode") 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.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 <tls|tcp|kcp>").Default("tls").Short('T').Enum("tls", "tcp", "kcp") muxClientArgs.ParentType = muxClient.Flag("parent-type", "parent protocol type <tls|tcp|tcps|kcp|tou>").Default("tls").Short('T').Enum("tls", "tcp", "tcps", "kcp", "tou")
muxClientArgs.CertFile = muxClient.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() 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.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.Timeout = muxClient.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
@ -177,6 +201,10 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp|tls 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() muxClientArgs.SessionCount = muxClient.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
muxClientArgs.Jumper = muxClient.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String() muxClientArgs.Jumper = muxClient.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
muxClientArgs.TCPSMethod = muxClient.Flag("tcps-method", "method of parent tcps's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxClientArgs.TCPSPassword = muxClient.Flag("tcps-password", "password of parent tcps's encrpyt/decrypt").Default("snail007's_goproxy").String()
muxClientArgs.TOUMethod = muxClient.Flag("tou-method", "method of parent tou's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxClientArgs.TOUPassword = muxClient.Flag("tou-password", "password of parent tou's encrpyt/decrypt").Default("snail007's_goproxy").String()
//########mux-bridge######### //########mux-bridge#########
muxBridge := app.Command("bridge", "proxy on mux bridge mode") muxBridge := app.Command("bridge", "proxy on mux bridge mode")
@ -184,7 +212,11 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
muxBridgeArgs.KeyFile = muxBridge.Flag("key", "key file for tls").Short('K').Default("proxy.key").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.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.Local = muxBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
muxBridgeArgs.LocalType = muxBridge.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tls").Short('t').Enum("tls", "tcp", "kcp") muxBridgeArgs.LocalType = muxBridge.Flag("local-type", "local protocol type <tls|tcp|tcps|kcp|tou>").Default("tls").Short('t').Enum("tls", "tcp", "tcps", "kcp", "tou")
muxBridgeArgs.TCPSMethod = muxBridge.Flag("tcps-method", "method of local tcps's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxBridgeArgs.TCPSPassword = muxBridge.Flag("tcps-password", "password of local tcps's encrpyt/decrypt").Default("snail007's_goproxy").String()
muxBridgeArgs.TOUMethod = muxBridge.Flag("tou-method", "method of local tou's encrpyt/decrypt, these below are supported :\n"+strings.Join(encryptconn.GetCipherMethods(), ",")).Default("aes-192-cfb").String()
muxBridgeArgs.TOUPassword = muxBridge.Flag("tou-password", "password of local tou's encrpyt/decrypt").Default("snail007's_goproxy").String()
//########tunnel-server######### //########tunnel-server#########
tunnelServer := app.Command("tserver", "proxy on tunnel server mode") tunnelServer := app.Command("tserver", "proxy on tunnel server mode")
@ -215,7 +247,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
//########ssh######### //########ssh#########
socks := app.Command("socks", "proxy on ssh mode") 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.Parent = socks.Flag("parent", "parent ssh address, such as: \"23.32.32.19:22\"").Default("").Short('P').Strings()
socksArgs.ParentType = socks.Flag("parent-type", "parent protocol type <tls|tcp|kcp|ssh>").Default("tcp").Short('T').Enum("tls", "tcp", "kcp", "ssh") socksArgs.ParentType = socks.Flag("parent-type", "parent protocol type <tls|tcp|kcp|ssh>").Default("tcp").Short('T').Enum("tls", "tcp", "kcp", "ssh")
socksArgs.LocalType = socks.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") socksArgs.LocalType = socks.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp")
socksArgs.Local = socks.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() socksArgs.Local = socks.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
@ -225,7 +257,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
socksArgs.SSHUser = socks.Flag("ssh-user", "user for ssh").Short('u').Default("").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.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.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.SSHPassword = socks.Flag("ssh-password", "password for ssh").Short('D').Default("").String()
socksArgs.Always = socks.Flag("always", "always use parent proxy").Default("false").Bool() 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.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.Interval = socks.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int()
@ -238,16 +270,26 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
socksArgs.AuthURLTimeout = socks.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int() 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.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.AuthURLRetry = socks.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int()
socksArgs.ParentAuth = socks.Flag("parent-auth", "parent socks auth username and password, such as: -A user1:pass1").Short('A').String()
socksArgs.DNSAddress = socks.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() 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.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.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.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.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() socksArgs.ParentCompress = socks.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
socksArgs.Intelligent = socks.Flag("intelligent", "settting intelligent HTTP, SOCKS5 proxy mode, can be <intelligent|direct|parent>").Default("intelligent").Enum("intelligent", "direct", "parent")
socksArgs.LoadBalanceMethod = socks.Flag("lb-method", "load balance method when use multiple parent,can be <roundrobin|leastconn|leasttime|hash|weight>").Default("roundrobin").Enum("roundrobin", "weight", "leastconn", "leasttime", "hash")
socksArgs.LoadBalanceTimeout = socks.Flag("lb-timeout", "tcp milliseconds timeout of connecting to parent").Default("500").Int()
socksArgs.LoadBalanceRetryTime = socks.Flag("lb-retrytime", "sleep time milliseconds after checking").Default("1000").Int()
socksArgs.LoadBalanceHashTarget = socks.Flag("lb-hashtarget", "use target address to choose parent for LB").Default("false").Bool()
socksArgs.LoadBalanceOnlyHA = socks.Flag("lb-onlyha", "use only `high availability mode` to choose parent for LB").Default("false").Bool()
socksArgs.RateLimit = socks.Flag("rate-limit", "rate limit (bytes/second) of each connection, such as: 100K 1.5M . 0 means no limitation").Short('l').Default("0").String()
socksArgs.BindListen = socks.Flag("bind-listen", "using listener binding IP when connect to target").Short('B').Default("false").Bool()
socksArgs.Debug = debug
//########socks+http(s)######### //########socks+http(s)#########
sps := app.Command("sps", "proxy on socks+http(s) mode") 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.Parent = sps.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').Strings()
spsArgs.CertFile = sps.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").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.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.CaCertFile = sps.Flag("ca", "ca cert file for tls").Default("").String()
@ -255,12 +297,12 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
spsArgs.ParentType = sps.Flag("parent-type", "parent protocol type <tls|tcp|kcp>").Short('T').Enum("tls", "tcp", "kcp") spsArgs.ParentType = sps.Flag("parent-type", "parent protocol type <tls|tcp|kcp>").Short('T').Enum("tls", "tcp", "kcp")
spsArgs.LocalType = sps.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") spsArgs.LocalType = sps.Flag("local-type", "local protocol type <tls|tcp|kcp>").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.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 <http|socks>").Short('S').Enum("http", "socks") spsArgs.ParentServiceType = sps.Flag("parent-service-type", "parent service type <http|socks|ss>").Short('S').Enum("http", "socks", "ss")
spsArgs.DNSAddress = sps.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() 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.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.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.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.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.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.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.AuthURLOkCode = sps.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
@ -270,8 +312,23 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
spsArgs.ParentKey = sps.Flag("parent-key", "the password for auto encrypt/decrypt parent 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.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() spsArgs.ParentCompress = sps.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
spsArgs.SSMethod = sps.Flag("ss-method", "the following methods are supported: aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, rc4-md5-6, chacha20, salsa20, rc4, table, des-cfb, chacha20-ietf; if you use ss client , \"-t tcp\" is required").Short('h').Default("aes-256-cfb").String()
spsArgs.SSKey = sps.Flag("ss-key", "if you use ss client , \"-t tcp\" is required").Short('j').Default("sspassword").String()
spsArgs.ParentSSMethod = sps.Flag("parent-ss-method", "the following methods are supported: aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, rc4-md5-6, chacha20, salsa20, rc4, table, des-cfb, chacha20-ietf; if you use ss server as parent, \"-T tcp\" is required").Short('H').Default("aes-256-cfb").String()
spsArgs.ParentSSKey = sps.Flag("parent-ss-key", "if you use ss server as parent, \"-T tcp\" is required").Short('J').Default("sspassword").String()
spsArgs.DisableHTTP = sps.Flag("disable-http", "disable http(s) proxy").Default("false").Bool() spsArgs.DisableHTTP = sps.Flag("disable-http", "disable http(s) proxy").Default("false").Bool()
spsArgs.DisableSocks5 = sps.Flag("disable-socks", "disable socks proxy").Default("false").Bool() spsArgs.DisableSocks5 = sps.Flag("disable-socks", "disable socks proxy").Default("false").Bool()
spsArgs.DisableSS = sps.Flag("disable-ss", "disable ss proxy").Default("false").Bool()
spsArgs.LoadBalanceMethod = sps.Flag("lb-method", "load balance method when use multiple parent,can be <roundrobin|leastconn|leasttime|hash|weight>").Default("hash").Enum("roundrobin", "weight", "leastconn", "leasttime", "hash")
spsArgs.LoadBalanceTimeout = sps.Flag("lb-timeout", "tcp milliseconds timeout of connecting to parent").Default("500").Int()
spsArgs.LoadBalanceRetryTime = sps.Flag("lb-retrytime", "sleep time milliseconds after checking").Default("1000").Int()
spsArgs.LoadBalanceHashTarget = sps.Flag("lb-hashtarget", "use target address to choose parent for LB").Default("false").Bool()
spsArgs.LoadBalanceOnlyHA = sps.Flag("lb-onlyha", "use only `high availability mode` to choose parent for LB").Default("false").Bool()
spsArgs.RateLimit = sps.Flag("rate-limit", "rate limit (bytes/second) of each connection, such as: 100K 1.5M . 0 means no limitation").Short('l').Default("0").String()
spsArgs.Jumper = sps.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Default("").String()
spsArgs.ParentTLSSingle = sps.Flag("parent-tls-single", "conntect to parent insecure skip verify").Default("false").Bool()
spsArgs.Debug = debug
//########dns######### //########dns#########
dns := app.Command("dns", "proxy on dns server mode") dns := app.Command("dns", "proxy on dns server mode")
dnsArgs.Parent = dns.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() dnsArgs.Parent = dns.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
@ -280,7 +337,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
dnsArgs.CaCertFile = dns.Flag("ca", "ca cert file for tls").Default("").String() dnsArgs.CaCertFile = dns.Flag("ca", "ca cert file for tls").Default("").String()
dnsArgs.Timeout = dns.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('i').Default("2000").Int() dnsArgs.Timeout = dns.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('i').Default("2000").Int()
dnsArgs.ParentType = dns.Flag("parent-type", "parent protocol type <tls|tcp|kcp>").Short('T').Enum("tls", "tcp", "kcp") dnsArgs.ParentType = dns.Flag("parent-type", "parent protocol type <tls|tcp|kcp>").Short('T').Enum("tls", "tcp", "kcp")
dnsArgs.Local = dns.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() dnsArgs.Local = dns.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(":53").String()
dnsArgs.ParentServiceType = dns.Flag("parent-service-type", "parent service type <http|socks>").Short('S').Enum("http", "socks") dnsArgs.ParentServiceType = dns.Flag("parent-service-type", "parent service type <http|socks>").Short('S').Enum("http", "socks")
dnsArgs.RemoteDNSAddress = dns.Flag("dns-address", "remote dns for resolve doamin").Short('q').Default("8.8.8.8:53").String() dnsArgs.RemoteDNSAddress = dns.Flag("dns-address", "remote dns for resolve doamin").Short('q').Default("8.8.8.8:53").String()
dnsArgs.DNSTTL = dns.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int() dnsArgs.DNSTTL = dns.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
@ -290,6 +347,14 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
dnsArgs.CacheFile = dns.Flag("cache-file", "dns result cached file").Short('f').Default(filepath.Join(path.Dir(os.Args[0]), "cache.dat")).String() dnsArgs.CacheFile = dns.Flag("cache-file", "dns result cached file").Short('f').Default(filepath.Join(path.Dir(os.Args[0]), "cache.dat")).String()
dnsArgs.LocalSocks5Port = dns.Flag("socks-port", "local socks5 port").Short('s').Default("65501").String() dnsArgs.LocalSocks5Port = dns.Flag("socks-port", "local socks5 port").Short('s').Default("65501").String()
//########keygen#########
keygen := app.Command("keygen", "create certificate for proxy")
keygenArgs.CommonName = keygen.Flag("cn", "common name").Short('n').Default("").String()
keygenArgs.CaName = keygen.Flag("ca", "ca name").Short('C').Default("").String()
keygenArgs.CertName = keygen.Flag("cert", "cert name of sign to create").Short('c').Default("").String()
keygenArgs.SignDays = keygen.Flag("days", "days of sign").Short('d').Default("365").Int()
keygenArgs.Sign = keygen.Flag("sign", "cert is to signin").Short('s').Default("false").Bool()
//parse args //parse args
_args := strings.Fields(strings.Trim(serviceArgsStr, " ")) _args := strings.Fields(strings.Trim(serviceArgsStr, " "))
args := []string{} args := []string{}
@ -353,7 +418,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
muxClientArgs.KCP = kcpArgs muxClientArgs.KCP = kcpArgs
dnsArgs.KCP = kcpArgs dnsArgs.KCP = kcpArgs
log := logger.New(os.Stderr, "", logger.Ldate|logger.Ltime) log := logger.New(os.Stdout, "", logger.Ldate|logger.Ltime)
flags := logger.Ldate flags := logger.Ldate
if *debug { if *debug {
flags |= logger.Lshortfile | logger.Lmicroseconds flags |= logger.Lshortfile | logger.Lmicroseconds
@ -387,11 +452,11 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
case "udp": case "udp":
services.Regist(serviceID, udpx.NewUDP(), udpArgs, log) services.Regist(serviceID, udpx.NewUDP(), udpArgs, log)
case "tserver": case "tserver":
services.Regist(serviceID, tunnel.NewTunnelServerManager(), tunnelServerArgs, log) services.Regist(serviceID, tunnelx.NewTunnelServerManager(), tunnelServerArgs, log)
case "tclient": case "tclient":
services.Regist(serviceID, tunnel.NewTunnelClient(), tunnelClientArgs, log) services.Regist(serviceID, tunnelx.NewTunnelClient(), tunnelClientArgs, log)
case "tbridge": case "tbridge":
services.Regist(serviceID, tunnel.NewTunnelBridge(), tunnelBridgeArgs, log) services.Regist(serviceID, tunnelx.NewTunnelBridge(), tunnelBridgeArgs, log)
case "server": case "server":
services.Regist(serviceID, mux.NewMuxServerManager(), muxServerArgs, log) services.Regist(serviceID, mux.NewMuxServerManager(), muxServerArgs, log)
case "client": case "client":
@ -403,7 +468,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
case "sps": case "sps":
services.Regist(serviceID, spsx.NewSPS(), spsArgs, log) services.Regist(serviceID, spsx.NewSPS(), spsArgs, log)
case "dns": case "dns":
services.Regist(serviceName, NewDNS(), dnsArgs, log) services.Regist(serviceID, NewDNS(), dnsArgs, log)
} }
_, err = services.Run(serviceID, nil) _, err = services.Run(serviceID, nil)
if err != nil { if err != nil {
@ -419,3 +484,41 @@ func Stop(serviceID string) {
func Version() string { func Version() string {
return SDK_VERSION return SDK_VERSION
} }
func StartProfiling(storePath string) {
profilingLock.Lock()
defer profilingLock.Unlock()
if !isProfiling {
isProfiling = true
if storePath == "" {
storePath = "."
}
cpuProfilingFile, _ = os.Create(filepath.Join(storePath, "cpu.prof"))
memProfilingFile, _ = os.Create(filepath.Join(storePath, "memory.prof"))
blockProfilingFile, _ = os.Create(filepath.Join(storePath, "block.prof"))
goroutineProfilingFile, _ = os.Create(filepath.Join(storePath, "goroutine.prof"))
threadcreateProfilingFile, _ = os.Create(filepath.Join(storePath, "threadcreate.prof"))
pprof.StartCPUProfile(cpuProfilingFile)
}
}
func StopProfiling() {
profilingLock.Lock()
defer profilingLock.Unlock()
if isProfiling {
isProfiling = false
pprof.StopCPUProfile()
goroutine := pprof.Lookup("goroutine")
goroutine.WriteTo(goroutineProfilingFile, 1)
heap := pprof.Lookup("heap")
heap.WriteTo(memProfilingFile, 1)
block := pprof.Lookup("block")
block.WriteTo(blockProfilingFile, 1)
threadcreate := pprof.Lookup("threadcreate")
threadcreate.WriteTo(threadcreateProfilingFile, 1)
//close
goroutineProfilingFile.Close()
memProfilingFile.Close()
blockProfilingFile.Close()
threadcreateProfilingFile.Close()
}
}

View File

@ -1,21 +1,29 @@
#/bin/bash #/bin/bash
VER="v5.3" VERSION=$(cat ../../VERSION)
VER="${VERSION}_$(date '+%Y%m%d%H%M%S')"
X="-X github.com/snail007/goproxy/sdk/android-ios.SDK_VERSION=$VER -X main.APP_VERSION=$VER"
TRIMPATH1="/Users/snail/go/src/github.com/snail007"
TRIMPATH=$(dirname ~/go/src/github.com/snail007)/snail007
if [ -d "$TRIMPATH1" ];then
TRIMPATH=$TRIMPATH1
fi
OPTS="-gcflags=-trimpath=$TRIMPATH -asmflags=-trimpath=$TRIMPATH"
rm -rf sdk-linux-*.tar.gz rm -rf sdk-linux-*.tar.gz
rm -rf README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a rm -rf README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a
#linux 32bit #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-archive $OPTS -ldflags "-s -w $X" -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 CGO_ENABLED=1 GOARCH=386 GOOS=linux go build -buildmode=c-shared $OPTS -ldflags "-s -w $X" -o libproxy-sdk.so sdk.go
cp ../README.md . cp ../README.md .
tar zcf sdk-linux-32bit-${VER}.tar.gz README.md libproxy-sdk.so libproxy-sdk.a libproxy-sdk.h tar zcf sdk-linux-32bit-${VERSION}.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 rm -rf README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a
#linux 64bit #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-archive $OPTS -ldflags "-s -w $X" -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 CGO_ENABLED=1 GOARCH=amd64 GOOS=linux go build -buildmode=c-shared $OPTS -ldflags "-s -w $X" -o libproxy-sdk.so sdk.go
cp ../README.md . cp ../README.md .
tar zcf sdk-linux-64bit-${VER}.tar.gz README.md libproxy-sdk.so libproxy-sdk.a libproxy-sdk.h tar zcf sdk-linux-64bit-${VERSION}.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 rm -rf README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a
echo "done." echo "done."

View File

@ -1,13 +1,21 @@
#/bin/bash #/bin/bash
VER="v5.3" VERSION=$(cat ../../VERSION)
VER="${VERSION}_$(date '+%Y%m%d%H%M%S')"
X="-X github.com/snail007/goproxy/sdk/android-ios.SDK_VERSION=$VER -X main.APP_VERSION=$VER"
TRIMPATH1="/Users/snail/go/src/github.com/snail007"
TRIMPATH=$(dirname ~/go/src/github.com/snail007)/snail007
if [ -d "$TRIMPATH1" ];then
TRIMPATH=$TRIMPATH1
fi
OPTS="-gcflags=-trimpath=$TRIMPATH -asmflags=-trimpath=$TRIMPATH"
rm -rf *.tar.gz rm -rf *.tar.gz
rm -rf README.md libproxy-sdk.dylib libproxy-sdk.h rm -rf README.md libproxy-sdk.dylib libproxy-sdk.h
#mac , macos required #mac , macos required
CGO_ENABLED=1 GOARCH=amd64 GOOS=darwin go build -buildmode=c-shared -ldflags "-s -w" -o libproxy-sdk.dylib sdk.go CGO_ENABLED=1 GOARCH=amd64 GOOS=darwin go build -buildmode=c-shared $OPTS -ldflags "-s -w $X" -o libproxy-sdk.dylib sdk.go
cp ../README.md . cp ../README.md .
tar zcf sdk-mac-${VER}.tar.gz README.md libproxy-sdk.dylib libproxy-sdk.h tar zcf sdk-mac-${VERSION}.tar.gz README.md libproxy-sdk.dylib libproxy-sdk.h
rm -rf README.md libproxy-sdk.dylib libproxy-sdk.h rm -rf README.md libproxy-sdk.dylib libproxy-sdk.h
echo "done." echo "done."

View File

@ -1,5 +1,13 @@
#/bin/bash #/bin/bash
VER="v5.3" VERSION=$(cat ../../VERSION)
VER="${VERSION}_$(date '+%Y%m%d%H%M%S')"
X="-X github.com/snail007/goproxy/sdk/android-ios.SDK_VERSION=$VER -X main.APP_VERSION=$VER"
TRIMPATH1="/Users/snail/go/src/github.com/snail007"
TRIMPATH=$(dirname ~/go/src/github.com/snail007)/snail007
if [ -d "$TRIMPATH1" ];then
TRIMPATH=$TRIMPATH1
fi
OPTS="-gcflags=-trimpath=$TRIMPATH -asmflags=-trimpath=$TRIMPATH"
#sudo rm /usr/local/go #sudo rm /usr/local/go
#sudo ln -s /usr/local/go1.10.1 /usr/local/go #sudo ln -s /usr/local/go1.10.1 /usr/local/go
@ -11,15 +19,15 @@ rm -rf README.md proxy-sdk.h proxy-sdk.dll
#apt-get install gcc-mingw-w64 #apt-get install gcc-mingw-w64
#windows 64bit #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 CC=x86_64-w64-mingw32-gcc GOARCH=amd64 CGO_ENABLED=1 GOOS=windows go build $OPTS -buildmode=c-shared -ldflags "-s -w $X" -o proxy-sdk.dll sdk.go
cp ../README.md . cp ../README.md .
tar zcf sdk-windows-64bit-${VER}.tar.gz README.md proxy-sdk.dll proxy-sdk.h ieshims.dll tar zcf sdk-windows-64bit-${VERSION}.tar.gz README.md proxy-sdk.dll proxy-sdk.h ieshims.dll
rm -rf README.md proxy-sdk.h proxy-sdk.dll rm -rf README.md proxy-sdk.h proxy-sdk.dll
#windows 32bit #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 CC=i686-w64-mingw32-gcc-win32 GOARCH=386 CGO_ENABLED=1 GOOS=windows go build $OPTS -buildmode=c-shared -ldflags "-s -w $X" -o proxy-sdk.dll sdk.go
cp ../README.md . cp ../README.md .
tar zcf sdk-windows-32bit-${VER}.tar.gz README.md proxy-sdk.dll proxy-sdk.h ieshims.dll tar zcf sdk-windows-32bit-${VERSION}.tar.gz README.md proxy-sdk.dll proxy-sdk.h ieshims.dll
rm -rf README.md proxy-sdk.h proxy-sdk.dll rm -rf README.md proxy-sdk.h proxy-sdk.dll
#sudo rm /usr/local/go #sudo rm /usr/local/go

View File

@ -21,5 +21,15 @@ func Version() (ver *C.char) {
return C.CString(sdk.Version()) return C.CString(sdk.Version())
} }
//export StartProfiling
func StartProfiling(storePath *C.char) {
sdk.StartProfiling(C.GoString(storePath))
}
//export StopProfiling
func StopProfiling() {
sdk.StopProfiling()
}
func main() { func main() {
} }

View File

@ -1,6 +1,7 @@
package http package http
import ( import (
"crypto/tls"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -11,8 +12,16 @@ import (
"strings" "strings"
"time" "time"
server "github.com/snail007/goproxy/core/cs/server"
"github.com/snail007/goproxy/core/lib/kcpcfg"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg" "github.com/snail007/goproxy/utils/datasize"
"github.com/snail007/goproxy/utils/dnsx"
"github.com/snail007/goproxy/utils/iolimiter"
"github.com/snail007/goproxy/utils/jumper"
"github.com/snail007/goproxy/utils/lb"
"github.com/snail007/goproxy/utils/mapx"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/conncrypt" "github.com/snail007/goproxy/utils/conncrypt"
@ -20,7 +29,7 @@ import (
) )
type HTTPArgs struct { type HTTPArgs struct {
Parent *string Parent *[]string
CertFile *string CertFile *string
KeyFile *string KeyFile *string
CaCertFile *string CaCertFile *string
@ -57,35 +66,51 @@ type HTTPArgs struct {
ParentKey *string ParentKey *string
LocalCompress *bool LocalCompress *bool
ParentCompress *bool ParentCompress *bool
Intelligent *string
LoadBalanceMethod *string
LoadBalanceTimeout *int
LoadBalanceRetryTime *int
LoadBalanceHashTarget *bool
LoadBalanceOnlyHA *bool
RateLimit *string
RateLimitBytes float64
BindListen *bool
Debug *bool
Jumper *string
} }
type HTTP struct { type HTTP struct {
outPool utils.OutConn
cfg HTTPArgs cfg HTTPArgs
checker utils.Checker checker utils.Checker
basicAuth utils.BasicAuth basicAuth utils.BasicAuth
sshClient *ssh.Client sshClient *ssh.Client
lockChn chan bool lockChn chan bool
domainResolver utils.DomainResolver domainResolver dnsx.DomainResolver
isStop bool isStop bool
serverChannels []*utils.ServerChannel serverChannels []*server.ServerChannel
userConns utils.ConcurrentMap userConns mapx.ConcurrentMap
log *logger.Logger log *logger.Logger
lb *lb.Group
jumper *jumper.Jumper
} }
func NewHTTP() services.Service { func NewHTTP() services.Service {
return &HTTP{ return &HTTP{
outPool: utils.OutConn{},
cfg: HTTPArgs{}, cfg: HTTPArgs{},
checker: utils.Checker{}, checker: utils.Checker{},
basicAuth: utils.BasicAuth{}, basicAuth: utils.BasicAuth{},
lockChn: make(chan bool, 1), lockChn: make(chan bool, 1),
isStop: false, isStop: false,
serverChannels: []*utils.ServerChannel{}, serverChannels: []*server.ServerChannel{},
userConns: utils.NewConcurrentMap(), userConns: mapx.NewConcurrentMap(),
} }
} }
func (s *HTTP) CheckArgs() (err error) { func (s *HTTP) CheckArgs() (err error) {
if *s.cfg.Parent != "" && *s.cfg.ParentType == "" {
if len(*s.cfg.Parent) == 1 && (*s.cfg.Parent)[0] == "" {
(*s.cfg.Parent) = []string{}
}
if len(*s.cfg.Parent) > 0 && *s.cfg.ParentType == "" {
err = fmt.Errorf("parent type unkown,use -T <tls|tcp|ssh|kcp>") err = fmt.Errorf("parent type unkown,use -T <tls|tcp|ssh|kcp>")
return return
} }
@ -133,15 +158,39 @@ func (s *HTTP) CheckArgs() (err error) {
s.cfg.SSHAuthMethod = ssh.PublicKeys(SSHSigner) s.cfg.SSHAuthMethod = ssh.PublicKeys(SSHSigner)
} }
} }
if *s.cfg.RateLimit != "0" && *s.cfg.RateLimit != "" {
var size uint64
size, err = datasize.Parse(*s.cfg.RateLimit)
if err != nil {
err = fmt.Errorf("parse rate limit size error,ERR:%s", err)
return
}
s.cfg.RateLimitBytes = float64(size)
}
if *s.cfg.Jumper != "" {
if *s.cfg.ParentType != "tls" && *s.cfg.ParentType != "tcp" {
err = fmt.Errorf("jumper only worked of -T is tls or tcp")
return
}
var j jumper.Jumper
j, err = jumper.New(*s.cfg.Jumper, time.Millisecond*time.Duration(*s.cfg.Timeout))
if err != nil {
err = fmt.Errorf("parse jumper fail, err %s", err)
return
}
s.jumper = &j
}
return return
} }
func (s *HTTP) InitService() (err error) { func (s *HTTP) InitService() (err error) {
s.InitBasicAuth() s.InitBasicAuth()
if *s.cfg.Parent != "" { //init lb
s.checker = utils.NewChecker(*s.cfg.HTTPTimeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct, s.log) if len(*s.cfg.Parent) > 0 {
s.checker = utils.NewChecker(*s.cfg.HTTPTimeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct, s.log, *s.cfg.Intelligent)
s.InitLB()
} }
if *s.cfg.DNSAddress != "" { if *s.cfg.DNSAddress != "" {
(*s).domainResolver = utils.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log) s.domainResolver = dnsx.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log)
} }
if *s.cfg.ParentType == "ssh" { if *s.cfg.ParentType == "ssh" {
err = s.ConnectSSH() err = s.ConnectSSH()
@ -150,13 +199,18 @@ func (s *HTTP) InitService() (err error) {
return return
} }
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
//循环检查ssh网络连通性 //循环检查ssh网络连通性
for { for {
if s.isStop { if s.isStop {
return return
} }
conn, err := utils.ConnectHost(s.Resolve(*s.cfg.Parent), *s.cfg.Timeout*2) conn, err := utils.ConnectHost(s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)), *s.cfg.Timeout*2)
if err == nil { if err == nil && conn != nil {
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write([]byte{0}) _, err = conn.Write([]byte{0})
conn.SetDeadline(time.Time{}) conn.SetDeadline(time.Time{})
@ -187,9 +241,21 @@ func (s *HTTP) StopService() {
} else { } else {
s.log.Printf("service http(s) stopped") s.log.Printf("service http(s) stopped")
} }
s.basicAuth = utils.BasicAuth{}
s.cfg = HTTPArgs{}
s.checker = utils.Checker{}
s.domainResolver = dnsx.DomainResolver{}
s.lb = nil
s.lockChn = nil
s.log = nil
s.jumper = nil
s.serverChannels = nil
s.sshClient = nil
s.userConns = nil
s = nil
}() }()
s.isStop = true s.isStop = true
if *s.cfg.Parent != "" { if len(*s.cfg.Parent) > 0 {
s.checker.Stop() s.checker.Stop()
} }
if s.sshClient != nil { if s.sshClient != nil {
@ -203,6 +269,9 @@ func (s *HTTP) StopService() {
(*sc.UDPListener).Close() (*sc.UDPListener).Close()
} }
} }
if s.lb != nil {
s.lb.Stop()
}
} }
func (s *HTTP) Start(args interface{}, log *logger.Logger) (err error) { func (s *HTTP) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log s.log = log
@ -215,20 +284,19 @@ func (s *HTTP) Start(args interface{}, log *logger.Logger) (err error) {
return return
} }
if *s.cfg.Parent != "" { if len(*s.cfg.Parent) > 0 {
s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) s.log.Printf("use %s parent %v [ %s ]", *s.cfg.ParentType, *s.cfg.Parent, strings.ToUpper(*s.cfg.LoadBalanceMethod))
s.InitOutConnPool()
} }
for _, addr := range strings.Split(*s.cfg.Local, ",") { for _, addr := range strings.Split(*s.cfg.Local, ",") {
if addr != "" { if addr != "" {
host, port, _ := net.SplitHostPort(addr) host, port, _ := net.SplitHostPort(addr)
p, _ := strconv.Atoi(port) p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p, s.log) sc := server.NewServerChannel(host, p, s.log)
if *s.cfg.LocalType == "tcp" { if *s.cfg.LocalType == "tcp" {
err = sc.ListenTCP(s.callback) err = sc.ListenTCP(s.callback)
} else if *s.cfg.LocalType == "tls" { } else if *s.cfg.LocalType == "tls" {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.callback) err = sc.ListenTLS(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.callback)
} else if *s.cfg.LocalType == "kcp" { } else if *s.cfg.LocalType == "kcp" {
err = sc.ListenKCP(s.cfg.KCP, s.callback, s.log) err = sc.ListenKCP(s.cfg.KCP, s.callback, s.log)
} }
@ -272,9 +340,9 @@ func (s *HTTP) callback(inConn net.Conn) {
address := req.Host address := req.Host
host, _, _ := net.SplitHostPort(address) host, _, _ := net.SplitHostPort(address)
useProxy := false useProxy := false
if !utils.IsIternalIP(host, *s.cfg.Always) { if !utils.IsInternalIP(host, *s.cfg.Always) {
useProxy = true useProxy = true
if *s.cfg.Parent == "" { if len(*s.cfg.Parent) == 0 {
useProxy = false useProxy = false
} else if *s.cfg.Always { } else if *s.cfg.Always {
useProxy = true useProxy = true
@ -290,17 +358,17 @@ func (s *HTTP) callback(inConn net.Conn) {
s.log.Printf("use proxy : %v, %s", useProxy, address) s.log.Printf("use proxy : %v, %s", useProxy, address)
err = s.OutToTCP(useProxy, address, &inConn, &req) lbAddr, err := s.OutToTCP(useProxy, address, &inConn, &req)
if err != nil { if err != nil {
if *s.cfg.Parent == "" { if len(*s.cfg.Parent) == 0 {
s.log.Printf("connect to %s fail, ERR:%s", address, err) s.log.Printf("connect to %s fail, ERR:%s", address, err)
} else { } else {
s.log.Printf("connect to %s parent %s fail", *s.cfg.ParentType, *s.cfg.Parent) s.log.Printf("connect to %s parent %v fail", *s.cfg.ParentType, lbAddr)
} }
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
} }
} }
func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (err interface{}) { func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (lbAddr string, err interface{}) {
inAddr := (*inConn).RemoteAddr().String() inAddr := (*inConn).RemoteAddr().String()
inLocalAddr := (*inConn).LocalAddr().String() inLocalAddr := (*inConn).LocalAddr().String()
//防止死循环 //防止死循环
@ -317,36 +385,43 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
return return
} }
if useProxy { if useProxy {
// s.log.Printf("%v", s.outPool)
if *s.cfg.ParentType == "ssh" { if *s.cfg.ParentType == "ssh" {
outConn, err = s.getSSHConn(address) outConn, err = s.getSSHConn(address)
} else { } else {
// s.log.Printf("%v", s.outPool) selectAddr := (*inConn).RemoteAddr().String()
outConn, err = s.outPool.Get() if utils.LBMethod(*s.cfg.LoadBalanceMethod) == lb.SELECT_HASH && *s.cfg.LoadBalanceHashTarget {
selectAddr = address
} }
lbAddr = s.lb.Select(selectAddr, *s.cfg.LoadBalanceOnlyHA)
outConn, err = s.GetParentConn(lbAddr)
}
} else { } else {
outConn, err = utils.ConnectHost(s.Resolve(address), *s.cfg.Timeout) outConn, err = s.GetDirectConn(s.Resolve(address), inLocalAddr)
} }
tryCount++ tryCount++
if err == nil || tryCount > maxTryCount { if err == nil || tryCount > maxTryCount {
break break
} else { } else {
s.log.Printf("connect to %s , err:%s,retrying...", *s.cfg.Parent, err) s.log.Printf("connect to %s , err:%s,retrying...", lbAddr, err)
time.Sleep(time.Second * 2) time.Sleep(time.Second * 2)
} }
} }
if err != nil { if err != nil {
s.log.Printf("connect to %s , err:%s", *s.cfg.Parent, err) s.log.Printf("connect to %s , err:%s", lbAddr, err)
utils.CloseConn(inConn) utils.CloseConn(inConn)
return return
} }
if *s.cfg.ParentCompress { if *s.cfg.ParentCompress {
outConn = utils.NewCompConn(outConn) outConn = utils.NewCompConn(outConn)
} }
if *s.cfg.ParentKey != "" { if useProxy && *s.cfg.ParentKey != "" {
outConn = conncrypt.New(outConn, &conncrypt.Config{ outConn = conncrypt.New(outConn, &conncrypt.Config{
Password: *s.cfg.ParentKey, Password: *s.cfg.ParentKey,
}) })
} }
outAddr := outConn.RemoteAddr().String() outAddr := outConn.RemoteAddr().String()
//outLocalAddr := outConn.LocalAddr().String() //outLocalAddr := outConn.LocalAddr().String()
if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") { if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") {
@ -355,7 +430,7 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
} else { } else {
//https或者http,上级是代理,proxy需要转发 //https或者http,上级是代理,proxy需要转发
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
//直连目标或上级非代理或非SNI,清理HTTP头部的代理头信息. //直连目标或上级非代理或非SNI,,清理HTTP头部的代理头信息
if (!useProxy || *s.cfg.ParentType == "ssh") && !req.IsSNI { if (!useProxy || *s.cfg.ParentType == "ssh") && !req.IsSNI {
_, err = outConn.Write(utils.RemoveProxyHeaders(req.HeadBuf)) _, err = outConn.Write(utils.RemoveProxyHeaders(req.HeadBuf))
} else { } else {
@ -363,21 +438,31 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
} }
outConn.SetDeadline(time.Time{}) outConn.SetDeadline(time.Time{})
if err != nil { if err != nil {
s.log.Printf("write to %s , err:%s", *s.cfg.Parent, err) s.log.Printf("write to %s , err:%s", lbAddr, err)
utils.CloseConn(inConn) utils.CloseConn(inConn)
return return
} }
} }
if s.cfg.RateLimitBytes > 0 {
outConn = iolimiter.NewReaderConn(outConn, s.cfg.RateLimitBytes)
}
utils.IoBind((*inConn), outConn, func(err interface{}) { utils.IoBind((*inConn), outConn, func(err interface{}) {
s.log.Printf("conn %s - %s released [%s]", inAddr, outAddr, req.Host) s.log.Printf("conn %s - %s released [%s]", inAddr, outAddr, req.Host)
s.userConns.Remove(inAddr) s.userConns.Remove(inAddr)
if len(*s.cfg.Parent) > 0 {
s.lb.DecreaseConns(lbAddr)
}
}, s.log) }, s.log)
s.log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, req.Host) s.log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, req.Host)
if c, ok := s.userConns.Get(inAddr); ok { if c, ok := s.userConns.Get(inAddr); ok {
(*c.(*net.Conn)).Close() (*c.(*net.Conn)).Close()
} }
s.userConns.Set(inAddr, inConn) s.userConns.Set(inAddr, inConn)
if len(*s.cfg.Parent) > 0 {
s.lb.IncreasConns(lbAddr)
}
return return
} }
@ -434,24 +519,14 @@ func (s *HTTP) ConnectSSH() (err error) {
if s.sshClient != nil { if s.sshClient != nil {
s.sshClient.Close() s.sshClient.Close()
} }
s.sshClient, err = ssh.Dial("tcp", s.Resolve(*s.cfg.Parent), &config) s.sshClient, err = ssh.Dial("tcp", s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)), &config)
if err != nil {
s.log.Printf("connect to ssh %s fail", s.cfg.Parent)
}
<-s.lockChn <-s.lockChn
return return
} }
func (s *HTTP) InitOutConnPool() {
if *s.cfg.ParentType == "tls" || *s.cfg.ParentType == "tcp" || *s.cfg.ParentType == "kcp" {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutConn(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType,
s.cfg.KCP,
s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes,
s.Resolve(*s.cfg.Parent),
*s.cfg.Timeout,
)
}
}
func (s *HTTP) InitBasicAuth() (err error) { func (s *HTTP) InitBasicAuth() (err error) {
if *s.cfg.DNSAddress != "" { if *s.cfg.DNSAddress != "" {
s.basicAuth = utils.NewBasicAuth(&(*s).domainResolver, s.log) s.basicAuth = utils.NewBasicAuth(&(*s).domainResolver, s.log)
@ -477,6 +552,27 @@ func (s *HTTP) InitBasicAuth() (err error) {
} }
return return
} }
func (s *HTTP) InitLB() {
configs := lb.BackendsConfig{}
for _, addr := range *s.cfg.Parent {
_addrInfo := strings.Split(addr, "@")
_addr := _addrInfo[0]
weight := 1
if len(_addrInfo) == 2 {
weight, _ = strconv.Atoi(_addrInfo[1])
}
configs = append(configs, &lb.BackendConfig{
Address: _addr,
Weight: weight,
ActiveAfter: 1,
InactiveAfter: 2,
Timeout: time.Duration(*s.cfg.LoadBalanceTimeout) * time.Millisecond,
RetryTime: time.Duration(*s.cfg.LoadBalanceRetryTime) * time.Millisecond,
})
}
LB := lb.NewGroup(utils.LBMethod(*s.cfg.LoadBalanceMethod), configs, &s.domainResolver, s.log, *s.cfg.Debug)
s.lb = &LB
}
func (s *HTTP) IsBasicAuth() bool { func (s *HTTP) IsBasicAuth() bool {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != "" return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != ""
} }
@ -494,7 +590,7 @@ func (s *HTTP) IsDeadLoop(inLocalAddr string, host string) bool {
if *s.cfg.DNSAddress != "" { if *s.cfg.DNSAddress != "" {
outIPs = []net.IP{net.ParseIP(s.Resolve(outDomain))} outIPs = []net.IP{net.ParseIP(s.Resolve(outDomain))}
} else { } else {
outIPs, err = utils.MyLookupIP(outDomain) outIPs, err = utils.LookupIP(outDomain)
} }
if err == nil { if err == nil {
for _, ip := range outIPs { for _, ip := range outIPs {
@ -530,3 +626,56 @@ func (s *HTTP) Resolve(address string) string {
} }
return ip return ip
} }
func (s *HTTP) GetParentConn(address string) (conn net.Conn, err error) {
if *s.cfg.ParentType == "tls" {
if s.jumper == nil {
var _conn tls.Conn
_conn, err = utils.TlsConnectHost(address, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes)
if err == nil {
conn = net.Conn(&_conn)
}
} else {
conf, err := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes)
if err != nil {
return nil, err
}
var _c net.Conn
_c, err = s.jumper.Dial(address, time.Millisecond*time.Duration(*s.cfg.Timeout))
if err == nil {
conn = net.Conn(tls.Client(_c, conf))
}
}
} else if *s.cfg.ParentType == "kcp" {
conn, err = utils.ConnectKCPHost(address, s.cfg.KCP)
} else if *s.cfg.ParentType == "ssh" {
var e interface{}
conn, e = s.getSSHConn(address)
if e != nil {
err = fmt.Errorf("%s", e)
}
} else {
if s.jumper == nil {
conn, err = utils.ConnectHost(address, *s.cfg.Timeout)
} else {
conn, err = s.jumper.Dial(address, time.Millisecond*time.Duration(*s.cfg.Timeout))
}
}
return
}
func (s *HTTP) GetDirectConn(address string, localAddr string) (conn net.Conn, err error) {
if !*s.cfg.BindListen {
return utils.ConnectHost(address, *s.cfg.Timeout)
}
ip, _, _ := net.SplitHostPort(localAddr)
if utils.IsInternalIP(ip, false) {
return utils.ConnectHost(address, *s.cfg.Timeout)
}
local, _ := net.ResolveTCPAddr("tcp", ip+":0")
d := net.Dialer{
Timeout: time.Millisecond * time.Duration(*s.cfg.Timeout),
LocalAddr: local,
}
conn, err = d.Dial("tcp", address)
return
}

View File

@ -7,23 +7,20 @@ import (
logger "log" logger "log"
"math/rand" "math/rand"
"net" "net"
"strconv" "runtime/debug"
"strings" "strings"
"sync" "sync"
"time" "time"
srvtransport "github.com/snail007/goproxy/core/cs/server"
"github.com/snail007/goproxy/core/lib/kcpcfg"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/mapx"
//"github.com/xtaci/smux" //"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux" smux "github.com/hashicorp/yamux"
) )
const (
CONN_SERVER = uint8(4)
CONN_CLIENT = uint8(5)
)
type MuxBridgeArgs struct { type MuxBridgeArgs struct {
CertFile *string CertFile *string
KeyFile *string KeyFile *string
@ -34,23 +31,27 @@ type MuxBridgeArgs struct {
Timeout *int Timeout *int
IsCompress *bool IsCompress *bool
KCP kcpcfg.KCPConfigArgs KCP kcpcfg.KCPConfigArgs
TCPSMethod *string
TCPSPassword *string
TOUMethod *string
TOUPassword *string
} }
type MuxBridge struct { type MuxBridge struct {
cfg MuxBridgeArgs cfg MuxBridgeArgs
clientControlConns utils.ConcurrentMap clientControlConns mapx.ConcurrentMap
serverConns utils.ConcurrentMap serverConns mapx.ConcurrentMap
router utils.ClientKeyRouter router utils.ClientKeyRouter
l *sync.Mutex l *sync.Mutex
isStop bool isStop bool
sc *utils.ServerChannel sc *srvtransport.ServerChannel
log *logger.Logger log *logger.Logger
} }
func NewMuxBridge() services.Service { func NewMuxBridge() services.Service {
b := &MuxBridge{ b := &MuxBridge{
cfg: MuxBridgeArgs{}, cfg: MuxBridgeArgs{},
clientControlConns: utils.NewConcurrentMap(), clientControlConns: mapx.NewConcurrentMap(),
serverConns: utils.NewConcurrentMap(), serverConns: mapx.NewConcurrentMap(),
l: &sync.Mutex{}, l: &sync.Mutex{},
isStop: false, isStop: false,
} }
@ -82,13 +83,21 @@ func (s *MuxBridge) StopService() {
} else { } else {
s.log.Printf("service bridge stopped") s.log.Printf("service bridge stopped")
} }
s.cfg = MuxBridgeArgs{}
s.clientControlConns = nil
s.l = nil
s.log = nil
s.router = utils.ClientKeyRouter{}
s.sc = nil
s.serverConns = nil
s = nil
}() }()
s.isStop = true s.isStop = true
if s.sc != nil && (*s.sc).Listener != nil { if s.sc != nil && (*s.sc).Listener != nil {
(*(*s.sc).Listener).Close() (*(*s.sc).Listener).Close()
} }
for _, g := range s.clientControlConns.Items() { for _, g := range s.clientControlConns.Items() {
for _, session := range g.(*utils.ConcurrentMap).Items() { for _, session := range g.(*mapx.ConcurrentMap).Items() {
(session.(*smux.Session)).Close() (session.(*smux.Session)).Close()
} }
} }
@ -106,21 +115,27 @@ func (s *MuxBridge) Start(args interface{}, log *logger.Logger) (err error) {
return return
} }
host, port, _ := net.SplitHostPort(*s.cfg.Local) sc := srvtransport.NewServerChannelHost(*s.cfg.Local, s.log)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p, s.log)
if *s.cfg.LocalType == "tcp" { if *s.cfg.LocalType == "tcp" {
err = sc.ListenTCP(s.handler) err = sc.ListenTCP(s.handler)
} else if *s.cfg.LocalType == "tls" { } else if *s.cfg.LocalType == "tls" {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.handler) err = sc.ListenTLS(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.handler)
} else if *s.cfg.LocalType == "kcp" { } else if *s.cfg.LocalType == "kcp" {
err = sc.ListenKCP(s.cfg.KCP, s.handler, s.log) err = sc.ListenKCP(s.cfg.KCP, s.handler, s.log)
} else if *s.cfg.LocalType == "tcps" {
err = sc.ListenTCPS(*s.cfg.TCPSMethod, *s.cfg.TCPSPassword, false, s.handler)
} else if *s.cfg.LocalType == "tou" {
err = sc.ListenTOU(*s.cfg.TOUMethod, *s.cfg.TOUPassword, false, s.handler)
} }
if err != nil { if err != nil {
return return
} }
s.sc = &sc s.sc = &sc
if *s.cfg.LocalType == "tou" {
s.log.Printf("%s bridge on %s", *s.cfg.LocalType, sc.UDPListener.LocalAddr())
} else {
s.log.Printf("%s bridge on %s", *s.cfg.LocalType, (*sc.Listener).Addr()) s.log.Printf("%s bridge on %s", *s.cfg.LocalType, (*sc.Listener).Addr())
}
return return
} }
func (s *MuxBridge) Clean() { func (s *MuxBridge) Clean() {
@ -200,18 +215,27 @@ func (s *MuxBridge) handler(inConn net.Conn) {
index := keyInfo[1] index := keyInfo[1]
s.l.Lock() s.l.Lock()
defer s.l.Unlock() defer s.l.Unlock()
var group *mapx.ConcurrentMap
if !s.clientControlConns.Has(groupKey) { if !s.clientControlConns.Has(groupKey) {
item := utils.NewConcurrentMap() _g := mapx.NewConcurrentMap()
s.clientControlConns.Set(groupKey, &item) group = &_g
} s.clientControlConns.Set(groupKey, group)
//s.log.Printf("init client session group %s", groupKey)
} else {
_group, _ := s.clientControlConns.Get(groupKey) _group, _ := s.clientControlConns.Get(groupKey)
group := _group.(*utils.ConcurrentMap) group = _group.(*mapx.ConcurrentMap)
}
if v, ok := group.Get(index); ok { if v, ok := group.Get(index); ok {
v.(*smux.Session).Close() v.(*smux.Session).Close()
} }
group.Set(index, session) group.Set(index, session)
// s.clientControlConns.Set(key, session) //s.log.Printf("set client session %s to group %s,grouplen:%d", index, groupKey, group.Count())
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for { for {
if s.isStop { if s.isStop {
return return
@ -221,10 +245,12 @@ func (s *MuxBridge) handler(inConn net.Conn) {
defer s.l.Unlock() defer s.l.Unlock()
if sess, ok := group.Get(index); ok && sess.(*smux.Session).IsClosed() { if sess, ok := group.Get(index); ok && sess.(*smux.Session).IsClosed() {
group.Remove(index) group.Remove(index)
//s.log.Printf("client session %s removed from group %s, grouplen:%d", key, groupKey, group.Count())
s.log.Printf("client connection %s released", key) s.log.Printf("client connection %s released", key)
} }
if group.IsEmpty() { if group.IsEmpty() {
s.clientControlConns.Remove(groupKey) s.clientControlConns.Remove(groupKey)
//s.log.Printf("client session group %s removed", groupKey)
} }
break break
} }
@ -248,15 +274,20 @@ func (s *MuxBridge) callback(inConn net.Conn, serverID, key string) {
if key == "*" { if key == "*" {
key = s.router.GetKey() key = s.router.GetKey()
} }
//s.log.Printf("server get client session %s", key)
_group, ok := s.clientControlConns.Get(key) _group, ok := s.clientControlConns.Get(key)
if !ok { if !ok {
s.log.Printf("client %s session not exists for server stream %s, retrying...", key, serverID) s.log.Printf("client %s session not exists for server stream %s, retrying...", key, serverID)
time.Sleep(time.Second * 3) time.Sleep(time.Second * 3)
continue continue
} }
group := _group.(*utils.ConcurrentMap) group := _group.(*mapx.ConcurrentMap)
keys := group.Keys() keys := []string{}
group.IterCb(func(key string, v interface{}) {
keys = append(keys, key)
})
keysLen := len(keys) keysLen := len(keys)
//s.log.Printf("client session %s , len:%d , keysLen: %d", key, group.Count(), keysLen)
i := 0 i := 0
if keysLen > 0 { if keysLen > 0 {
i = rand.Intn(keysLen) i = rand.Intn(keysLen)
@ -280,10 +311,20 @@ func (s *MuxBridge) callback(inConn net.Conn, serverID, key string) {
die1 := make(chan bool, 1) die1 := make(chan bool, 1)
die2 := make(chan bool, 1) die2 := make(chan bool, 1)
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
io.Copy(stream, inConn) io.Copy(stream, inConn)
die1 <- true die1 <- true
}() }()
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
io.Copy(inConn, stream) io.Copy(inConn, stream)
die2 <- true die2 <- true
}() }()

View File

@ -6,13 +6,19 @@ import (
"io" "io"
logger "log" logger "log"
"net" "net"
"runtime/debug"
"strings"
"sync"
"time" "time"
"github.com/golang/snappy" "github.com/golang/snappy"
clienttransport "github.com/snail007/goproxy/core/cs/client"
"github.com/snail007/goproxy/core/lib/kcpcfg"
encryptconn "github.com/snail007/goproxy/core/lib/transport/encrypt"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/jumper" "github.com/snail007/goproxy/utils/jumper"
"github.com/snail007/goproxy/utils/mapx"
//"github.com/xtaci/smux" //"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux" smux "github.com/hashicorp/yamux"
) )
@ -30,24 +36,40 @@ type MuxClientArgs struct {
SessionCount *int SessionCount *int
KCP kcpcfg.KCPConfigArgs KCP kcpcfg.KCPConfigArgs
Jumper *string Jumper *string
TCPSMethod *string
TCPSPassword *string
TOUMethod *string
TOUPassword *string
}
type ClientUDPConnItem struct {
conn *smux.Stream
isActive bool
touchtime int64
srcAddr *net.UDPAddr
localAddr *net.UDPAddr
udpConn *net.UDPConn
connid string
} }
type MuxClient struct { type MuxClient struct {
cfg MuxClientArgs cfg MuxClientArgs
isStop bool isStop bool
sessions utils.ConcurrentMap sessions mapx.ConcurrentMap
log *logger.Logger log *logger.Logger
jumper *jumper.Jumper jumper *jumper.Jumper
udpConns mapx.ConcurrentMap
} }
func NewMuxClient() services.Service { func NewMuxClient() services.Service {
return &MuxClient{ return &MuxClient{
cfg: MuxClientArgs{}, cfg: MuxClientArgs{},
isStop: false, isStop: false,
sessions: utils.NewConcurrentMap(), sessions: mapx.NewConcurrentMap(),
udpConns: mapx.NewConcurrentMap(),
} }
} }
func (s *MuxClient) InitService() (err error) { func (s *MuxClient) InitService() (err error) {
s.UDPGCDeamon()
return return
} }
@ -91,6 +113,12 @@ func (s *MuxClient) StopService() {
} else { } else {
s.log.Printf("service client stopped") s.log.Printf("service client stopped")
} }
s.cfg = MuxClientArgs{}
s.jumper = nil
s.log = nil
s.sessions = nil
s.udpConns = nil
s = nil
}() }()
s.isStop = true s.isStop = true
for _, sess := range s.sessions.Items() { for _, sess := range s.sessions.Items() {
@ -118,7 +146,7 @@ func (s *MuxClient) Start(args interface{}, log *logger.Logger) (err error) {
defer func() { defer func() {
e := recover() e := recover()
if e != nil { if e != nil {
s.log.Printf("session worker crashed: %s", e) s.log.Printf("session worker crashed: %s\nstack:%s", e, string(debug.Stack()))
} }
}() }()
for { for {
@ -132,7 +160,16 @@ func (s *MuxClient) Start(args interface{}, log *logger.Logger) (err error) {
continue continue
} }
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
g := sync.WaitGroup{}
g.Add(1)
go func() {
defer func() {
_ = recover()
g.Done()
}()
_, err = conn.Write(utils.BuildPacket(CONN_CLIENT, fmt.Sprintf("%s-%d", *s.cfg.Key, i))) _, err = conn.Write(utils.BuildPacket(CONN_CLIENT, fmt.Sprintf("%s-%d", *s.cfg.Key, i)))
}()
g.Wait()
conn.SetDeadline(time.Time{}) conn.SetDeadline(time.Time{})
if err != nil { if err != nil {
conn.Close() conn.Close()
@ -178,7 +215,7 @@ func (s *MuxClient) Start(args interface{}, log *logger.Logger) (err error) {
stream.Close() stream.Close()
return return
} }
s.log.Printf("worker[%d] signal revecived,server %s stream %s %s", i, serverID, ID, clientLocalAddr) //s.log.Printf("worker[%d] signal revecived,server %s stream %s %s", i, serverID, ID, clientLocalAddr)
protocol := clientLocalAddr[:3] protocol := clientLocalAddr[:3]
localAddr := clientLocalAddr[4:] localAddr := clientLocalAddr[4:]
if protocol == "udp" { if protocol == "udp" {
@ -200,14 +237,14 @@ func (s *MuxClient) getParentConn() (conn net.Conn, err error) {
if *s.cfg.ParentType == "tls" { if *s.cfg.ParentType == "tls" {
if s.jumper == nil { if s.jumper == nil {
var _conn tls.Conn var _conn tls.Conn
_conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) _conn, err = clienttransport.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil)
if err == nil { if err == nil {
conn = net.Conn(&_conn) conn = net.Conn(&_conn)
} }
} else { } else {
conf, err := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, nil) conf, e := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, nil)
if err != nil { if e != nil {
return nil, err return nil, e
} }
var _c net.Conn var _c net.Conn
_c, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout)) _c, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout))
@ -217,10 +254,22 @@ func (s *MuxClient) getParentConn() (conn net.Conn, err error) {
} }
} else if *s.cfg.ParentType == "kcp" { } else if *s.cfg.ParentType == "kcp" {
conn, err = utils.ConnectKCPHost(*s.cfg.Parent, s.cfg.KCP) conn, err = clienttransport.KCPConnectHost(*s.cfg.Parent, s.cfg.KCP)
} else if *s.cfg.ParentType == "tcps" {
if s.jumper == nil {
conn, err = clienttransport.TCPSConnectHost(*s.cfg.Parent, *s.cfg.TCPSMethod, *s.cfg.TCPSPassword, false, *s.cfg.Timeout)
} else {
conn, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout))
if err == nil {
conn, err = encryptconn.NewConn(conn, *s.cfg.TCPSMethod, *s.cfg.TCPSPassword)
}
}
} else if *s.cfg.ParentType == "tou" {
conn, err = clienttransport.TOUConnectHost(*s.cfg.Parent, *s.cfg.TCPSMethod, *s.cfg.TCPSPassword, false, *s.cfg.Timeout)
} else { } else {
if s.jumper == nil { if s.jumper == nil {
conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout) conn, err = clienttransport.TCPConnectHost(*s.cfg.Parent, *s.cfg.Timeout)
} else { } else {
conn, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout)) conn, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout))
} }
@ -228,76 +277,138 @@ func (s *MuxClient) getParentConn() (conn net.Conn, err error) {
return return
} }
func (s *MuxClient) ServeUDP(inConn *smux.Stream, localAddr, ID string) { func (s *MuxClient) ServeUDP(inConn *smux.Stream, localAddr, ID string) {
var item *ClientUDPConnItem
var body []byte
var err error
srcAddr := ""
defer func() {
if item != nil {
(*item).conn.Close()
(*item).udpConn.Close()
s.udpConns.Remove(srcAddr)
inConn.Close()
}
}()
for { for {
if s.isStop { if s.isStop {
return return
} }
inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) srcAddr, body, err = utils.ReadUDPPacket(inConn)
srcAddr, body, err := utils.ReadUDPPacket(inConn)
inConn.SetDeadline(time.Time{})
if err != nil { if err != nil {
s.log.Printf("udp packet revecived fail, err: %s", err) if strings.Contains(err.Error(), "n != int(") {
s.log.Printf("connection %s released", ID) continue
}
if !utils.IsNetDeadlineErr(err) && err != io.EOF {
s.log.Printf("udp packet revecived from bridge fail, err: %s", err)
}
return
}
if v, ok := s.udpConns.Get(srcAddr); !ok {
_srcAddr, _ := net.ResolveUDPAddr("udp", srcAddr)
zeroAddr, _ := net.ResolveUDPAddr("udp", ":")
_localAddr, _ := net.ResolveUDPAddr("udp", localAddr)
c, err := net.DialUDP("udp", zeroAddr, _localAddr)
if err != nil {
s.log.Printf("create local udp conn fail, err : %s", err)
inConn.Close() inConn.Close()
break return
}
item = &ClientUDPConnItem{
conn: inConn,
srcAddr: _srcAddr,
localAddr: _localAddr,
udpConn: c,
connid: ID,
}
s.udpConns.Set(srcAddr, item)
s.UDPRevecive(srcAddr, ID)
} else { } else {
//s.log.Printf("udp packet revecived:%s,%v", srcAddr, body) item = v.(*ClientUDPConnItem)
}
(*item).touchtime = time.Now().Unix()
go func() {
defer func() { _ = recover() }()
(*item).udpConn.Write(body)
}()
}
}
func (s *MuxClient) UDPRevecive(key, ID string) {
go func() { go func() {
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
s.log.Printf("client processUDPPacket crashed,err: %s", e) fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
} }
}() }()
s.processUDPPacket(inConn, srcAddr, localAddr, body) s.log.Printf("udp conn %s connected", ID)
v, ok := s.udpConns.Get(key)
if !ok {
s.log.Printf("[warn] udp conn not exists for %s, connid : %s", key, ID)
return
}
cui := v.(*ClientUDPConnItem)
buf := utils.LeakyBuffer.Get()
defer func() {
utils.LeakyBuffer.Put(buf)
cui.conn.Close()
cui.udpConn.Close()
s.udpConns.Remove(key)
s.log.Printf("udp conn %s released", ID)
}() }()
for {
n, err := cui.udpConn.Read(buf)
if err != nil {
if !utils.IsNetClosedErr(err) {
s.log.Printf("udp conn read udp packet fail , err: %s ", err)
} }
return
} }
// } cui.touchtime = time.Now().Unix()
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
cui.conn.SetWriteDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = cui.conn.Write(utils.UDPPacket(cui.srcAddr.String(), buf[:n]))
cui.conn.SetWriteDeadline(time.Time{})
if err != nil {
cui.udpConn.Close()
return
}
}()
}
}()
} }
func (s *MuxClient) processUDPPacket(inConn *smux.Stream, srcAddr, localAddr string, body []byte) { func (s *MuxClient) UDPGCDeamon() {
dstAddr, err := net.ResolveUDPAddr("udp", localAddr) gctime := int64(30)
if err != nil { go func() {
s.log.Printf("can't resolve address: %s", err) defer func() {
inConn.Close() if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
if s.isStop {
return return
} }
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} timer := time.NewTicker(time.Second)
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr) for {
if err != nil { <-timer.C
s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err) gcKeys := []string{}
return s.udpConns.IterCb(func(key string, v interface{}) {
if time.Now().Unix()-v.(*ClientUDPConnItem).touchtime > gctime {
(*(v.(*ClientUDPConnItem).conn)).Close()
(v.(*ClientUDPConnItem).udpConn).Close()
gcKeys = append(gcKeys, key)
s.log.Printf("gc udp conn %s", v.(*ClientUDPConnItem).connid)
} }
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) })
_, err = conn.Write(body) for _, k := range gcKeys {
conn.SetDeadline(time.Time{}) s.udpConns.Remove(k)
if err != nil {
s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
return
} }
//s.log.Printf("send udp packet to %s success", dstAddr.String()) gcKeys = nil
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 {
s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return
} }
respBody := buf[0:length] }()
//s.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 {
s.log.Printf("send udp response fail ,ERR:%s", err)
inConn.Close()
return
}
//s.log.Printf("send udp response success ,from:%s ,%d ,%v", dstAddr.String(), len(bs), bs)
} }
func (s *MuxClient) ServeConn(inConn *smux.Stream, localAddr, ID string) { func (s *MuxClient) ServeConn(inConn *smux.Stream, localAddr, ID string) {
var err error var err error
@ -331,10 +442,20 @@ func (s *MuxClient) ServeConn(inConn *smux.Stream, localAddr, ID string) {
die1 := make(chan bool, 1) die1 := make(chan bool, 1)
die2 := make(chan bool, 1) die2 := make(chan bool, 1)
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
io.Copy(outConn, snappy.NewReader(inConn)) io.Copy(outConn, snappy.NewReader(inConn))
die1 <- true die1 <- true
}() }()
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
io.Copy(snappy.NewWriter(inConn), outConn) io.Copy(snappy.NewWriter(inConn), outConn)
die2 <- true die2 <- true
}() }()

View File

@ -12,16 +12,26 @@ import (
"strings" "strings"
"time" "time"
clienttransport "github.com/snail007/goproxy/core/cs/client"
server "github.com/snail007/goproxy/core/cs/server"
"github.com/snail007/goproxy/core/lib/kcpcfg"
encryptconn "github.com/snail007/goproxy/core/lib/transport/encrypt"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/jumper" "github.com/snail007/goproxy/utils/jumper"
"github.com/snail007/goproxy/utils/mapx"
"github.com/golang/snappy" "github.com/golang/snappy"
//"github.com/xtaci/smux" //"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux" smux "github.com/hashicorp/yamux"
) )
const (
CONN_CLIENT_CONTROL = uint8(1)
CONN_SERVER = uint8(4)
CONN_CLIENT = uint8(5)
)
type MuxServerArgs struct { type MuxServerArgs struct {
Parent *string Parent *string
ParentType *string ParentType *string
@ -40,17 +50,24 @@ type MuxServerArgs struct {
SessionCount *int SessionCount *int
KCP kcpcfg.KCPConfigArgs KCP kcpcfg.KCPConfigArgs
Jumper *string Jumper *string
TCPSMethod *string
TCPSPassword *string
TOUMethod *string
TOUPassword *string
} }
type MuxServer struct {
type MuxUDPItem struct { cfg MuxServerArgs
packet *[]byte sc server.ServerChannel
localAddr *net.UDPAddr sessions mapx.ConcurrentMap
srcAddr *net.UDPAddr lockChn chan bool
isStop bool
log *logger.Logger
jumper *jumper.Jumper
udpConns mapx.ConcurrentMap
} }
type MuxServerManager struct { type MuxServerManager struct {
cfg MuxServerArgs cfg MuxServerArgs
udpChn chan MuxUDPItem
serverID string serverID string
servers []*services.Service servers []*services.Service
log *logger.Logger log *logger.Logger
@ -59,7 +76,6 @@ type MuxServerManager struct {
func NewMuxServerManager() services.Service { func NewMuxServerManager() services.Service {
return &MuxServerManager{ return &MuxServerManager{
cfg: MuxServerArgs{}, cfg: MuxServerArgs{},
udpChn: make(chan MuxUDPItem, 50000),
serverID: utils.Uniqueid(), serverID: utils.Uniqueid(),
servers: []*services.Service{}, servers: []*services.Service{},
} }
@ -124,6 +140,10 @@ func (s *MuxServerManager) Start(args interface{}, log *logger.Logger) (err erro
KCP: s.cfg.KCP, KCP: s.cfg.KCP,
ParentType: s.cfg.ParentType, ParentType: s.cfg.ParentType,
Jumper: s.cfg.Jumper, Jumper: s.cfg.Jumper,
TCPSMethod: s.cfg.TCPSMethod,
TCPSPassword: s.cfg.TCPSPassword,
TOUMethod: s.cfg.TOUMethod,
TOUPassword: s.cfg.TOUPassword,
}, log) }, log)
if err != nil { if err != nil {
@ -140,6 +160,11 @@ func (s *MuxServerManager) StopService() {
for _, server := range s.servers { for _, server := range s.servers {
(*server).Clean() (*server).Clean()
} }
s.cfg = MuxServerArgs{}
s.log = nil
s.serverID = ""
s.servers = nil
s = nil
} }
func (s *MuxServerManager) CheckArgs() (err error) { func (s *MuxServerManager) CheckArgs() (err error) {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
@ -158,28 +183,24 @@ func (s *MuxServerManager) InitService() (err error) {
return return
} }
type MuxServer struct {
cfg MuxServerArgs
udpChn chan MuxUDPItem
sc utils.ServerChannel
sessions utils.ConcurrentMap
lockChn chan bool
isStop bool
udpConn *net.Conn
log *logger.Logger
jumper *jumper.Jumper
}
func NewMuxServer() services.Service { func NewMuxServer() services.Service {
return &MuxServer{ return &MuxServer{
cfg: MuxServerArgs{}, cfg: MuxServerArgs{},
udpChn: make(chan MuxUDPItem, 50000),
lockChn: make(chan bool, 1), lockChn: make(chan bool, 1),
sessions: utils.NewConcurrentMap(), sessions: mapx.NewConcurrentMap(),
isStop: false, isStop: false,
udpConns: mapx.NewConcurrentMap(),
} }
} }
type MuxUDPConnItem struct {
conn *net.Conn
touchtime int64
srcAddr *net.UDPAddr
localAddr *net.UDPAddr
connid string
}
func (s *MuxServer) StopService() { func (s *MuxServer) StopService() {
defer func() { defer func() {
e := recover() e := recover()
@ -188,6 +209,14 @@ func (s *MuxServer) StopService() {
} else { } else {
s.log.Printf("service server stopped") s.log.Printf("service server stopped")
} }
s.cfg = MuxServerArgs{}
s.jumper = nil
s.lockChn = nil
s.log = nil
s.sc = server.ServerChannel{}
s.sessions = nil
s.udpConns = nil
s = nil
}() }()
s.isStop = true s.isStop = true
for _, sess := range s.sessions.Items() { for _, sess := range s.sessions.Items() {
@ -199,12 +228,9 @@ func (s *MuxServer) StopService() {
if s.sc.UDPListener != nil { if s.sc.UDPListener != nil {
(*s.sc.UDPListener).Close() (*s.sc.UDPListener).Close()
} }
if s.udpConn != nil {
(*s.udpConn).Close()
}
} }
func (s *MuxServer) InitService() (err error) { func (s *MuxServer) InitService() (err error) {
s.UDPConnDeamon() s.UDPGCDeamon()
return return
} }
func (s *MuxServer) CheckArgs() (err error) { func (s *MuxServer) CheckArgs() (err error) {
@ -239,14 +265,10 @@ func (s *MuxServer) Start(args interface{}, log *logger.Logger) (err error) {
} }
host, port, _ := net.SplitHostPort(*s.cfg.Local) host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port) p, _ := strconv.Atoi(port)
s.sc = utils.NewServerChannel(host, p, s.log) s.sc = server.NewServerChannel(host, p, s.log)
if *s.cfg.IsUDP { if *s.cfg.IsUDP {
err = s.sc.ListenUDP(func(packet []byte, localAddr, srcAddr *net.UDPAddr) { err = s.sc.ListenUDP(func(listener *net.UDPConn, packet []byte, localAddr, srcAddr *net.UDPAddr) {
s.udpChn <- MuxUDPItem{ s.UDPSend(packet, localAddr, srcAddr)
packet: &packet,
localAddr: localAddr,
srcAddr: srcAddr,
}
}) })
if err != nil { if err != nil {
return return
@ -280,10 +302,20 @@ func (s *MuxServer) Start(args interface{}, log *logger.Logger) (err error) {
die1 := make(chan bool, 1) die1 := make(chan bool, 1)
die2 := make(chan bool, 1) die2 := make(chan bool, 1)
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
io.Copy(inConn, snappy.NewReader(outConn)) io.Copy(inConn, snappy.NewReader(outConn))
die1 <- true die1 <- true
}() }()
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
io.Copy(snappy.NewWriter(outConn), inConn) io.Copy(snappy.NewWriter(outConn), inConn)
die2 <- true die2 <- true
}() }()
@ -317,7 +349,9 @@ func (s *MuxServer) GetOutConn() (outConn net.Conn, ID string, err error) {
} }
outConn, err = s.GetConn(fmt.Sprintf("%d", i)) outConn, err = s.GetConn(fmt.Sprintf("%d", i))
if err != nil { if err != nil {
if !strings.Contains(err.Error(), "can not connect at same time") {
s.log.Printf("connection err: %s", err) s.log.Printf("connection err: %s", err)
}
return return
} }
remoteAddr := "tcp:" + *s.cfg.Remote remoteAddr := "tcp:" + *s.cfg.Remote
@ -372,6 +406,11 @@ func (s *MuxServer) GetConn(index string) (conn net.Conn, err error) {
s.sessions.Set(index, session) s.sessions.Set(index, session)
s.log.Printf("session[%s] created", index) s.log.Printf("session[%s] created", index)
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for { for {
if s.isStop { if s.isStop {
return return
@ -415,6 +454,18 @@ func (s *MuxServer) getParentConn() (conn net.Conn, err error) {
} else if *s.cfg.ParentType == "kcp" { } else if *s.cfg.ParentType == "kcp" {
conn, err = utils.ConnectKCPHost(*s.cfg.Parent, s.cfg.KCP) conn, err = utils.ConnectKCPHost(*s.cfg.Parent, s.cfg.KCP)
} else if *s.cfg.ParentType == "tcps" {
if s.jumper == nil {
conn, err = clienttransport.TCPSConnectHost(*s.cfg.Parent, *s.cfg.TCPSMethod, *s.cfg.TCPSPassword, false, *s.cfg.Timeout)
} else {
conn, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout))
if err == nil {
conn, err = encryptconn.NewConn(conn, *s.cfg.TCPSMethod, *s.cfg.TCPSPassword)
}
}
} else if *s.cfg.ParentType == "tou" {
conn, err = clienttransport.TOUConnectHost(*s.cfg.Parent, *s.cfg.TCPSMethod, *s.cfg.TCPSPassword, false, *s.cfg.Timeout)
} else { } else {
if s.jumper == nil { if s.jumper == nil {
conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout) conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout)
@ -424,87 +475,128 @@ func (s *MuxServer) getParentConn() (conn net.Conn, err error) {
} }
return return
} }
func (s *MuxServer) UDPConnDeamon() { func (s *MuxServer) UDPGCDeamon() {
gctime := int64(30)
go func() { go func() {
defer func() { defer func() {
if err := recover(); err != nil { if e := recover(); e != nil {
s.log.Printf("udp conn deamon crashed with err : %s \nstack: %s", err, string(debug.Stack())) fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
} }
}() }()
var outConn net.Conn if s.isStop {
var ID string return
var err error }
timer := time.NewTicker(time.Second)
for { for {
if s.isStop { <-timer.C
return gcKeys := []string{}
s.udpConns.IterCb(func(key string, v interface{}) {
if time.Now().Unix()-v.(*MuxUDPConnItem).touchtime > gctime {
(*(v.(*MuxUDPConnItem).conn)).Close()
gcKeys = append(gcKeys, key)
s.log.Printf("gc udp conn %s", v.(*MuxUDPConnItem).connid)
} }
item := <-s.udpChn })
RETRY: for _, k := range gcKeys {
if s.isStop { s.udpConns.Remove(k)
return
} }
if outConn == nil { gcKeys = nil
for { }
if s.isStop { }()
return }
} func (s *MuxServer) UDPSend(data []byte, localAddr, srcAddr *net.UDPAddr) {
outConn, ID, err = s.GetOutConn() var (
if err != nil { uc *MuxUDPConnItem
outConn = nil key = srcAddr.String()
utils.CloseConn(&outConn) ID string
s.log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err) err error
time.Sleep(time.Second * 3) outconn net.Conn
continue )
} else { v, ok := s.udpConns.Get(key)
go func(outConn net.Conn, ID string) { if !ok {
if s.udpConn != nil { for {
(*s.udpConn).Close() outconn, ID, err = s.GetOutConn()
} if err != nil && strings.Contains(err.Error(), "can not connect at same time") {
s.udpConn = &outConn time.Sleep(time.Millisecond * 500)
for { continue
if s.isStop { } else {
return break
} }
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) }
srcAddrFromConn, body, err := utils.ReadUDPPacket(outConn) if err != nil {
outConn.SetDeadline(time.Time{}) s.log.Printf("connect to %s fail, err: %s", *s.cfg.Parent, err)
if err != nil { return
s.log.Printf("parse revecived udp packet fail, err: %s ,%v", err, body) }
s.log.Printf("UDP deamon connection %s exited", ID) uc = &MuxUDPConnItem{
break conn: &outconn,
} srcAddr: srcAddr,
//s.log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn) localAddr: localAddr,
_srcAddr := strings.Split(srcAddrFromConn, ":") connid: ID,
if len(_srcAddr) != 2 { }
s.log.Printf("parse revecived udp packet fail, addr error : %s", srcAddrFromConn) s.udpConns.Set(key, uc)
continue s.UDPRevecive(key, ID)
} } else {
port, _ := strconv.Atoi(_srcAddr[1]) uc = v.(*MuxUDPConnItem)
dstAddr := &net.UDPAddr{IP: net.ParseIP(_srcAddr[0]), Port: port} }
s.sc.UDPListener.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) go func() {
_, err = s.sc.UDPListener.WriteToUDP(body, dstAddr) defer func() {
s.sc.UDPListener.SetDeadline(time.Time{}) if e := recover(); e != nil {
if err != nil { (*uc.conn).Close()
s.log.Printf("udp response to local %s fail,ERR:%s", srcAddrFromConn, err) s.udpConns.Remove(key)
continue s.log.Printf("udp sender crashed with error : %s", e)
} }
//s.log.Printf("udp response to local %s success , %v", srcAddrFromConn, body) }()
} uc.touchtime = time.Now().Unix()
}(outConn, ID) (*uc.conn).SetWriteDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
break _, err = (*uc.conn).Write(utils.UDPPacket(srcAddr.String(), data))
} (*uc.conn).SetWriteDeadline(time.Time{})
} if err != nil {
} s.log.Printf("write udp packet to %s fail ,flush err:%s ", *s.cfg.Parent, err)
outConn.SetWriteDeadline(time.Now().Add(time.Second)) }
_, err = outConn.Write(utils.UDPPacket(item.srcAddr.String(), *item.packet)) }()
outConn.SetWriteDeadline(time.Time{}) }
if err != nil { func (s *MuxServer) UDPRevecive(key, ID string) {
utils.CloseConn(&outConn) go func() {
outConn = nil defer func() {
s.log.Printf("write udp packet to %s fail ,flush err:%s ,retrying...", *s.cfg.Parent, err) if e := recover(); e != nil {
goto RETRY fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
} }
//s.log.Printf("write packet %v", *item.packet) }()
s.log.Printf("udp conn %s connected", ID)
var uc *MuxUDPConnItem
defer func() {
if uc != nil {
(*uc.conn).Close()
}
s.udpConns.Remove(key)
s.log.Printf("udp conn %s released", ID)
}()
v, ok := s.udpConns.Get(key)
if !ok {
s.log.Printf("[warn] udp conn not exists for %s, connid : %s", key, ID)
return
}
uc = v.(*MuxUDPConnItem)
for {
_, body, err := utils.ReadUDPPacket(*uc.conn)
if err != nil {
if strings.Contains(err.Error(), "n != int(") {
continue
}
if err != io.EOF {
s.log.Printf("udp conn read udp packet fail , err: %s ", err)
}
return
}
uc.touchtime = time.Now().Unix()
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
s.sc.UDPListener.WriteToUDP(body, uc.srcAddr)
}()
} }
}() }()
} }

View File

@ -39,6 +39,10 @@ func GetService(name string) *ServiceItem {
func Stop(name string) { func Stop(name string) {
if s, ok := servicesMap.Load(name); ok && s.(*ServiceItem).S != nil { if s, ok := servicesMap.Load(name); ok && s.(*ServiceItem).S != nil {
s.(*ServiceItem).S.Clean() s.(*ServiceItem).S.Clean()
*s.(*ServiceItem) = ServiceItem{}
s = nil
servicesMap.Store(name, nil)
servicesMap.Delete(name)
} }
} }
func Run(name string, args interface{}) (service *ServiceItem, err error) { func Run(name string, args interface{}) (service *ServiceItem, err error) {

View File

@ -8,19 +8,27 @@ import (
logger "log" logger "log"
"net" "net"
"runtime/debug" "runtime/debug"
"strconv"
"strings" "strings"
"time" "time"
server "github.com/snail007/goproxy/core/cs/server"
"github.com/snail007/goproxy/core/lib/kcpcfg"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/conncrypt" "github.com/snail007/goproxy/utils/conncrypt"
"github.com/snail007/goproxy/utils/datasize"
"github.com/snail007/goproxy/utils/dnsx"
"github.com/snail007/goproxy/utils/iolimiter"
"github.com/snail007/goproxy/utils/lb"
"github.com/snail007/goproxy/utils/mapx"
"github.com/snail007/goproxy/utils/socks" "github.com/snail007/goproxy/utils/socks"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
type SocksArgs struct { type SocksArgs struct {
Parent *string Parent *[]string
ParentType *string ParentType *string
Local *string Local *string
LocalType *string LocalType *string
@ -41,6 +49,7 @@ type SocksArgs struct {
Interval *int Interval *int
Blocked *string Blocked *string
Direct *string Direct *string
ParentAuth *string
AuthFile *string AuthFile *string
Auth *[]string Auth *[]string
AuthURL *string AuthURL *string
@ -55,6 +64,17 @@ type SocksArgs struct {
ParentKey *string ParentKey *string
LocalCompress *bool LocalCompress *bool
ParentCompress *bool ParentCompress *bool
Intelligent *string
LoadBalanceMethod *string
LoadBalanceTimeout *int
LoadBalanceRetryTime *int
LoadBalanceHashTarget *bool
LoadBalanceOnlyHA *bool
RateLimit *string
RateLimitBytes float64
BindListen *bool
Debug *bool
} }
type Socks struct { type Socks struct {
cfg SocksArgs cfg SocksArgs
@ -62,13 +82,14 @@ type Socks struct {
basicAuth utils.BasicAuth basicAuth utils.BasicAuth
sshClient *ssh.Client sshClient *ssh.Client
lockChn chan bool lockChn chan bool
udpSC utils.ServerChannel udpSC server.ServerChannel
sc *utils.ServerChannel sc *server.ServerChannel
domainResolver utils.DomainResolver domainResolver dnsx.DomainResolver
isStop bool isStop bool
userConns utils.ConcurrentMap userConns mapx.ConcurrentMap
log *logger.Logger log *logger.Logger
udpRelatedPacketConns utils.ConcurrentMap lb *lb.Group
udpRelatedPacketConns mapx.ConcurrentMap
udpLocalKey []byte udpLocalKey []byte
udpParentKey []byte udpParentKey []byte
} }
@ -80,14 +101,14 @@ func NewSocks() services.Service {
basicAuth: utils.BasicAuth{}, basicAuth: utils.BasicAuth{},
lockChn: make(chan bool, 1), lockChn: make(chan bool, 1),
isStop: false, isStop: false,
userConns: utils.NewConcurrentMap(), userConns: mapx.NewConcurrentMap(),
udpRelatedPacketConns: utils.NewConcurrentMap(), udpRelatedPacketConns: mapx.NewConcurrentMap(),
} }
} }
func (s *Socks) CheckArgs() (err error) { func (s *Socks) CheckArgs() (err error) {
if *s.cfg.LocalType == "tls" || (*s.cfg.Parent != "" && *s.cfg.ParentType == "tls") { if *s.cfg.LocalType == "tls" || (len(*s.cfg.Parent) > 0 && *s.cfg.ParentType == "tls") {
s.cfg.CertBytes, s.cfg.KeyBytes, err = 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 { if err != nil {
return return
@ -100,7 +121,12 @@ func (s *Socks) CheckArgs() (err error) {
} }
} }
} }
if *s.cfg.Parent != "" {
if len(*s.cfg.Parent) == 1 && (*s.cfg.Parent)[0] == "" {
(*s.cfg.Parent) = []string{}
}
if len(*s.cfg.Parent) > 0 {
if *s.cfg.ParentType == "" { if *s.cfg.ParentType == "" {
err = fmt.Errorf("parent type unkown,use -T <tls|tcp|ssh|kcp>") err = fmt.Errorf("parent type unkown,use -T <tls|tcp|ssh|kcp>")
return return
@ -136,32 +162,46 @@ func (s *Socks) CheckArgs() (err error) {
} }
} }
} }
if *s.cfg.RateLimit != "0" && *s.cfg.RateLimit != "" {
var size uint64
size, err = datasize.Parse(*s.cfg.RateLimit)
if err != nil {
err = fmt.Errorf("parse rate limit size error,ERR:%s", err)
return
}
s.cfg.RateLimitBytes = float64(size)
}
s.udpLocalKey = s.LocalUDPKey() s.udpLocalKey = s.LocalUDPKey()
s.udpParentKey = s.ParentUDPKey() s.udpParentKey = s.ParentUDPKey()
//s.log.Printf("udpLocalKey : %v , udpParentKey : %v", s.udpLocalKey, s.udpParentKey)
return return
} }
func (s *Socks) InitService() (err error) { func (s *Socks) InitService() (err error) {
s.InitBasicAuth() s.InitBasicAuth()
if *s.cfg.DNSAddress != "" { if *s.cfg.DNSAddress != "" {
(*s).domainResolver = utils.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log) (*s).domainResolver = dnsx.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log)
} }
if *s.cfg.Parent != "" { if len(*s.cfg.Parent) > 0 {
s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct, s.log) s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct, s.log, *s.cfg.Intelligent)
s.InitLB()
} }
if *s.cfg.ParentType == "ssh" { if *s.cfg.ParentType == "ssh" {
e := s.ConnectSSH() e := s.ConnectSSH(s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)))
if e != nil { if e != nil {
err = fmt.Errorf("init service fail, ERR: %s", e) err = fmt.Errorf("init service fail, ERR: %s", e)
return return
} }
go func() { go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
}
}()
//循环检查ssh网络连通性 //循环检查ssh网络连通性
for { for {
if s.isStop { if s.isStop {
return return
} }
conn, err := utils.ConnectHost(s.Resolve(*s.cfg.Parent), *s.cfg.Timeout*2) conn, err := utils.ConnectHost(s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)), *s.cfg.Timeout*2)
if err == nil { if err == nil {
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write([]byte{0}) _, err = conn.Write([]byte{0})
@ -172,7 +212,7 @@ func (s *Socks) InitService() (err error) {
s.sshClient.Close() s.sshClient.Close()
} }
s.log.Printf("ssh offline, retrying...") s.log.Printf("ssh offline, retrying...")
s.ConnectSSH() s.ConnectSSH(s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)))
} else { } else {
conn.Close() conn.Close()
} }
@ -193,9 +233,24 @@ func (s *Socks) StopService() {
} else { } else {
s.log.Printf("service socks stopped") s.log.Printf("service socks stopped")
} }
s.basicAuth = utils.BasicAuth{}
s.cfg = SocksArgs{}
s.checker = utils.Checker{}
s.domainResolver = dnsx.DomainResolver{}
s.lb = nil
s.lockChn = nil
s.log = nil
s.sc = nil
s.sshClient = nil
s.udpLocalKey = nil
s.udpParentKey = nil
s.udpRelatedPacketConns = nil
s.udpSC = server.ServerChannel{}
s.userConns = nil
s = nil
}() }()
s.isStop = true s.isStop = true
if *s.cfg.Parent != "" { if len(*s.cfg.Parent) > 0 {
s.checker.Stop() s.checker.Stop()
} }
if s.sshClient != nil { if s.sshClient != nil {
@ -210,6 +265,9 @@ func (s *Socks) StopService() {
for _, c := range s.userConns.Items() { for _, c := range s.userConns.Items() {
(*c.(*net.Conn)).Close() (*c.(*net.Conn)).Close()
} }
if s.lb != nil {
s.lb.Stop()
}
for _, c := range s.udpRelatedPacketConns.Items() { for _, c := range s.udpRelatedPacketConns.Items() {
(*c.(*net.UDPConn)).Close() (*c.(*net.UDPConn)).Close()
} }
@ -224,14 +282,14 @@ func (s *Socks) Start(args interface{}, log *logger.Logger) (err error) {
if err = s.InitService(); err != nil { if err = s.InitService(); err != nil {
s.InitService() s.InitService()
} }
if *s.cfg.Parent != "" { if len(*s.cfg.Parent) > 0 {
s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) s.log.Printf("use %s parent %v [ %s ]", *s.cfg.ParentType, *s.cfg.Parent, strings.ToUpper(*s.cfg.LoadBalanceMethod))
} }
sc := utils.NewServerChannelHost(*s.cfg.Local, s.log) sc := server.NewServerChannelHost(*s.cfg.Local, s.log)
if *s.cfg.LocalType == "tcp" { if *s.cfg.LocalType == "tcp" {
err = sc.ListenTCP(s.socksConnCallback) err = sc.ListenTCP(s.socksConnCallback)
} else if *s.cfg.LocalType == "tls" { } else if *s.cfg.LocalType == "tls" {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.socksConnCallback) err = sc.ListenTLS(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.socksConnCallback)
} else if *s.cfg.LocalType == "kcp" { } else if *s.cfg.LocalType == "kcp" {
err = sc.ListenKCP(s.cfg.KCP, s.socksConnCallback, s.log) err = sc.ListenKCP(s.cfg.KCP, s.socksConnCallback, s.log)
} }
@ -261,116 +319,40 @@ func (s *Socks) socksConnCallback(inConn net.Conn) {
Password: *s.cfg.LocalKey, Password: *s.cfg.LocalKey,
}) })
} }
//协商开始
//method select request //socks5 server
inConn.SetReadDeadline(time.Now().Add(time.Second * 3)) var serverConn *socks.ServerConn
methodReq, err := socks.NewMethodsRequest(inConn) udpIP, _, _ := net.SplitHostPort(inConn.LocalAddr().String())
inConn.SetReadDeadline(time.Time{}) if s.IsBasicAuth() {
if err != nil { serverConn = socks.NewServerConn(&inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), &s.basicAuth, true, udpIP, nil)
methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn)
if err != io.EOF {
s.log.Printf("new methods request fail,ERR: %s", err)
}
return
}
if !s.IsBasicAuth() {
if !methodReq.Select(socks.Method_NO_AUTH) {
methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn)
s.log.Printf("none method found : Method_NO_AUTH")
return
}
//method select reply
err = methodReq.Reply(socks.Method_NO_AUTH)
if err != nil {
s.log.Printf("reply answer data fail,ERR: %s", err)
utils.CloseConn(&inConn)
return
}
// s.log.Printf("% x", methodReq.Bytes())
} else { } else {
//auth serverConn = socks.NewServerConn(&inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, true, udpIP, nil)
if !methodReq.Select(socks.Method_USER_PASS) { }
methodReq.Reply(socks.Method_NONE_ACCEPTABLE) if err := serverConn.Handshake(); err != nil {
utils.CloseConn(&inConn) if !strings.HasSuffix(err.Error(), "EOF") {
s.log.Printf("none method found : Method_USER_PASS") s.log.Printf("handshake fail, ERR: %s", err)
}
inConn.Close()
return return
} }
//method reply need auth if serverConn.IsUDP() {
err = methodReq.Reply(socks.Method_USER_PASS) s.proxyUDP(&inConn, serverConn)
if err != nil { } else if serverConn.IsTCP() {
s.log.Printf("reply answer data fail,ERR: %s", err) s.proxyTCP(&inConn, serverConn)
utils.CloseConn(&inConn)
return
} }
//read auth
buf := make([]byte, 500)
inConn.SetReadDeadline(time.Now().Add(time.Second * 3))
n, err := inConn.Read(buf)
inConn.SetReadDeadline(time.Time{})
if err != nil {
utils.CloseConn(&inConn)
return
}
r := buf[:n]
user := string(r[2 : r[1]+2])
pass := string(r[2+r[1]+1:])
//s.log.Printf("user:%s,pass:%s", user, pass)
//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
}
}
//request detail
request, err := socks.NewRequest(inConn)
if err != nil {
s.log.Printf("read request data fail,ERR: %s", err)
utils.CloseConn(&inConn)
return
}
//协商结束
switch request.CMD() {
case socks.CMD_BIND:
//bind 不支持
request.TCPReply(socks.REP_UNKNOWN)
utils.CloseConn(&inConn)
return
case socks.CMD_CONNECT:
//tcp
s.proxyTCP(&inConn, methodReq, request)
case socks.CMD_ASSOCIATE:
//udp
s.proxyUDP(&inConn, methodReq, request)
}
} }
func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) { func (s *Socks) proxyTCP(inConn *net.Conn, serverConn *socks.ServerConn) {
var outConn net.Conn var outConn net.Conn
var err interface{} var err interface{}
lbAddr := ""
useProxy := true useProxy := true
tryCount := 0 tryCount := 0
maxTryCount := 5 maxTryCount := 5
//防止死循环 //防止死循环
if s.IsDeadLoop((*inConn).LocalAddr().String(), request.Host()) { if s.IsDeadLoop((*inConn).LocalAddr().String(), serverConn.Host()) {
utils.CloseConn(inConn) utils.CloseConn(inConn)
s.log.Printf("dead loop detected , %s", request.Host()) s.log.Printf("dead loop detected , %s", serverConn.Host())
utils.CloseConn(inConn) utils.CloseConn(inConn)
return return
} }
@ -378,33 +360,70 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
if s.isStop { if s.isStop {
return return
} }
if *s.cfg.Always { if *s.cfg.Always {
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr(), true) selectAddr := (*inConn).RemoteAddr().String()
if utils.LBMethod(*s.cfg.LoadBalanceMethod) == lb.SELECT_HASH && *s.cfg.LoadBalanceHashTarget {
selectAddr = serverConn.Target()
}
lbAddr = s.lb.Select(selectAddr, *s.cfg.LoadBalanceOnlyHA)
//lbAddr = s.lb.Select((*inConn).RemoteAddr().String())
outConn, err = s.GetParentConn(lbAddr, serverConn)
if err != nil {
s.log.Printf("connect to parent fail, %s", err)
return
}
//handshake
//socks client
_, err = s.HandshakeSocksParent(&outConn, "tcp", serverConn.Target(), serverConn.AuthData(), false)
if err != nil {
if err != io.EOF {
s.log.Printf("handshake fail, %s", err)
}
return
}
} else { } else {
if *s.cfg.Parent != "" { if len(*s.cfg.Parent) > 0 {
host, _, _ := net.SplitHostPort(request.Addr()) host, _, _ := net.SplitHostPort(serverConn.Target())
useProxy := false useProxy := false
if utils.IsIternalIP(host, *s.cfg.Always) { if utils.IsInternalIP(host, *s.cfg.Always) {
useProxy = false useProxy = false
} else { } else {
var isInMap bool var isInMap bool
useProxy, isInMap, _, _ = s.checker.IsBlocked(request.Addr()) useProxy, isInMap, _, _ = s.checker.IsBlocked(serverConn.Target())
if !isInMap { if !isInMap {
s.checker.Add(request.Addr(), s.Resolve(request.Addr())) s.checker.Add(serverConn.Target(), s.Resolve(serverConn.Target()))
} }
} }
if useProxy { if useProxy {
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr(), true) selectAddr := (*inConn).RemoteAddr().String()
} else { if utils.LBMethod(*s.cfg.LoadBalanceMethod) == lb.SELECT_HASH && *s.cfg.LoadBalanceHashTarget {
outConn, err = utils.ConnectHost(s.Resolve(request.Addr()), *s.cfg.Timeout) selectAddr = serverConn.Target()
}
lbAddr = s.lb.Select(selectAddr, *s.cfg.LoadBalanceOnlyHA)
//lbAddr = s.lb.Select((*inConn).RemoteAddr().String())
outConn, err = s.GetParentConn(lbAddr, serverConn)
if err != nil {
s.log.Printf("connect to parent fail, %s", err)
return
}
//handshake
//socks client
_, err = s.HandshakeSocksParent(&outConn, "tcp", serverConn.Target(), serverConn.AuthData(), false)
if err != nil {
s.log.Printf("handshake fail, %s", err)
return
} }
} else { } else {
outConn, err = utils.ConnectHost(s.Resolve(request.Addr()), *s.cfg.Timeout) outConn, err = s.GetDirectConn(s.Resolve(serverConn.Target()), (*inConn).LocalAddr().String())
}
} else {
outConn, err = s.GetDirectConn(s.Resolve(serverConn.Target()), (*inConn).LocalAddr().String())
useProxy = false useProxy = false
} }
} }
tryCount++ tryCount++
if err == nil || tryCount > maxTryCount || *s.cfg.Parent == "" { if err == nil || tryCount > maxTryCount || len(*s.cfg.Parent) == 0 {
break break
} else { } else {
s.log.Printf("get out conn fail,%s,retrying...", err) s.log.Printf("get out conn fail,%s,retrying...", err)
@ -413,42 +432,49 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
} }
if err != nil { if err != nil {
s.log.Printf("get out conn fail,%s", err) s.log.Printf("get out conn fail,%s", err)
request.TCPReply(socks.REP_NETWOR_UNREACHABLE)
return return
} }
s.log.Printf("use proxy %v : %s", useProxy, request.Addr()) s.log.Printf("use proxy %v : %s", useProxy, serverConn.Target())
request.TCPReply(socks.REP_SUCCESS)
inAddr := (*inConn).RemoteAddr().String() inAddr := (*inConn).RemoteAddr().String()
//outRemoteAddr := outConn.RemoteAddr().String()
//inLocalAddr := (*inConn).LocalAddr().String() //inLocalAddr := (*inConn).LocalAddr().String()
s.log.Printf("conn %s - %s connected", inAddr, request.Addr()) if s.cfg.RateLimitBytes > 0 {
outConn = iolimiter.NewReaderConn(outConn, s.cfg.RateLimitBytes)
}
utils.IoBind(*inConn, outConn, func(err interface{}) { utils.IoBind(*inConn, outConn, func(err interface{}) {
s.log.Printf("conn %s - %s released", inAddr, request.Addr()) s.log.Printf("conn %s - %s released", inAddr, serverConn.Target())
s.userConns.Remove(inAddr) s.userConns.Remove(inAddr)
if len(*s.cfg.Parent) > 0 {
s.lb.DecreaseConns(lbAddr)
}
}, s.log) }, s.log)
if c, ok := s.userConns.Get(inAddr); ok { if c, ok := s.userConns.Get(inAddr); ok {
(*c.(*net.Conn)).Close() (*c.(*net.Conn)).Close()
s.userConns.Remove(inAddr) s.userConns.Remove(inAddr)
} }
s.userConns.Set(inAddr, inConn) s.userConns.Set(inAddr, inConn)
if len(*s.cfg.Parent) > 0 {
s.lb.IncreasConns(lbAddr)
}
s.log.Printf("conn %s - %s connected", inAddr, serverConn.Target())
} }
func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string, handshake bool) (outConn net.Conn, err interface{}) { func (s *Socks) GetParentConn(parentAddress string, serverConn *socks.ServerConn) (outConn net.Conn, err interface{}) {
switch *s.cfg.ParentType { switch *s.cfg.ParentType {
case "kcp": case "kcp", "tls", "tcp":
fallthrough
case "tls":
fallthrough
case "tcp":
if *s.cfg.ParentType == "tls" { if *s.cfg.ParentType == "tls" {
var _outConn tls.Conn var _conn tls.Conn
_outConn, err = utils.TlsConnectHost(s.Resolve(*s.cfg.Parent), *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) _conn, err = utils.TlsConnectHost(parentAddress, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes)
outConn = net.Conn(&_outConn) if err == nil {
outConn = net.Conn(&_conn)
}
} else if *s.cfg.ParentType == "kcp" { } else if *s.cfg.ParentType == "kcp" {
outConn, err = utils.ConnectKCPHost(s.Resolve(*s.cfg.Parent), s.cfg.KCP) outConn, err = utils.ConnectKCPHost(parentAddress, s.cfg.KCP)
} else { } else {
outConn, err = utils.ConnectHost(s.Resolve(*s.cfg.Parent), *s.cfg.Timeout) outConn, err = utils.ConnectHost(parentAddress, *s.cfg.Timeout)
} }
if err != nil { if err != nil {
err = fmt.Errorf("connect fail,%s", err) err = fmt.Errorf("connect fail,%s", err)
@ -462,44 +488,6 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string, handshake
Password: *s.cfg.ParentKey, Password: *s.cfg.ParentKey,
}) })
} }
if !handshake {
return
}
var buf = make([]byte, 1024)
//var n int
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, 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]
//s.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
}
//result := buf[:n]
//s.log.Printf("result:%v", result)
case "ssh": case "ssh":
maxTryCount := 1 maxTryCount := 1
tryCount := 0 tryCount := 0
@ -515,17 +503,17 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string, handshake
} }
wait <- true wait <- true
}() }()
outConn, err = s.sshClient.Dial("tcp", host) outConn, err = s.sshClient.Dial("tcp", serverConn.Target())
}() }()
select { select {
case <-wait: case <-wait:
case <-time.After(time.Millisecond * time.Duration(*s.cfg.Timeout) * 2): case <-time.After(time.Millisecond * time.Duration(*s.cfg.Timeout) * 2):
err = fmt.Errorf("ssh dial %s timeout", host) err = fmt.Errorf("ssh dial %s timeout", serverConn.Target())
s.sshClient.Close() s.sshClient.Close()
} }
if err != nil { if err != nil {
s.log.Printf("connect ssh fail, ERR: %s, retrying...", err) s.log.Printf("connect ssh fail, ERR: %s, retrying...", err)
e := s.ConnectSSH() e := s.ConnectSSH(parentAddress)
if e == nil { if e == nil {
tryCount++ tryCount++
time.Sleep(time.Second * 3) time.Sleep(time.Second * 3)
@ -538,7 +526,7 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string, handshake
return return
} }
func (s *Socks) ConnectSSH() (err error) { func (s *Socks) ConnectSSH(lbAddr string) (err error) {
select { select {
case s.lockChn <- true: case s.lockChn <- true:
default: default:
@ -556,7 +544,7 @@ func (s *Socks) ConnectSSH() (err error) {
if s.sshClient != nil { if s.sshClient != nil {
s.sshClient.Close() s.sshClient.Close()
} }
s.sshClient, err = ssh.Dial("tcp", s.Resolve(*s.cfg.Parent), &config) s.sshClient, err = ssh.Dial("tcp", s.Resolve(lbAddr), &config)
<-s.lockChn <-s.lockChn
return return
} }
@ -585,6 +573,27 @@ func (s *Socks) InitBasicAuth() (err error) {
} }
return return
} }
func (s *Socks) InitLB() {
configs := lb.BackendsConfig{}
for _, addr := range *s.cfg.Parent {
_addrInfo := strings.Split(addr, "@")
_addr := _addrInfo[0]
weight := 1
if len(_addrInfo) == 2 {
weight, _ = strconv.Atoi(_addrInfo[1])
}
configs = append(configs, &lb.BackendConfig{
Address: _addr,
Weight: weight,
ActiveAfter: 1,
InactiveAfter: 2,
Timeout: time.Duration(*s.cfg.LoadBalanceTimeout) * time.Millisecond,
RetryTime: time.Duration(*s.cfg.LoadBalanceRetryTime) * time.Millisecond,
})
}
LB := lb.NewGroup(utils.LBMethod(*s.cfg.LoadBalanceMethod), configs, &s.domainResolver, s.log, *s.cfg.Debug)
s.lb = &LB
}
func (s *Socks) IsBasicAuth() bool { func (s *Socks) IsBasicAuth() bool {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != "" return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != ""
} }
@ -602,7 +611,7 @@ func (s *Socks) IsDeadLoop(inLocalAddr string, host string) bool {
if *s.cfg.DNSAddress != "" { if *s.cfg.DNSAddress != "" {
outIPs = []net.IP{net.ParseIP(s.Resolve(outDomain))} outIPs = []net.IP{net.ParseIP(s.Resolve(outDomain))}
} else { } else {
outIPs, err = utils.MyLookupIP(outDomain) outIPs, err = utils.LookupIP(outDomain)
} }
if err == nil { if err == nil {
for _, ip := range outIPs { for _, ip := range outIPs {
@ -638,3 +647,37 @@ func (s *Socks) Resolve(address string) string {
} }
return ip return ip
} }
func (s *Socks) GetDirectConn(address string, localAddr string) (conn net.Conn, err error) {
if !*s.cfg.BindListen {
return utils.ConnectHost(address, *s.cfg.Timeout)
}
ip, _, _ := net.SplitHostPort(localAddr)
if utils.IsInternalIP(ip, false) {
return utils.ConnectHost(address, *s.cfg.Timeout)
}
local, _ := net.ResolveTCPAddr("tcp", ip+":0")
d := net.Dialer{
Timeout: time.Millisecond * time.Duration(*s.cfg.Timeout),
LocalAddr: local,
}
conn, err = d.Dial("tcp", address)
return
}
func (s *Socks) HandshakeSocksParent(outconn *net.Conn, network, dstAddr string, auth socks.Auth, fromSS bool) (client *socks.ClientConn, err error) {
if *s.cfg.ParentAuth != "" {
a := strings.Split(*s.cfg.ParentAuth, ":")
if len(a) != 2 {
err = fmt.Errorf("parent auth data format error")
return
}
client = socks.NewClientConn(outconn, network, dstAddr, time.Millisecond*time.Duration(*s.cfg.Timeout), &socks.Auth{User: a[0], Password: a[1]}, nil)
} else {
if !fromSS && !s.IsBasicAuth() && auth.Password != "" && auth.User != "" {
client = socks.NewClientConn(outconn, network, dstAddr, time.Millisecond*time.Duration(*s.cfg.Timeout), &auth, nil)
} else {
client = socks.NewClientConn(outconn, network, dstAddr, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil)
}
}
err = client.Handshake()
return
}

View File

@ -44,7 +44,7 @@ func (s *Socks) LocalUDPKey() (key []byte) {
} }
return return
} }
func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) { func (s *Socks) proxyUDP(inConn *net.Conn, serverConn *socks.ServerConn) {
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack())) s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack()))
@ -54,23 +54,12 @@ func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
utils.CloseConn(inConn) utils.CloseConn(inConn)
return return
} }
srcIP, _, _ := net.SplitHostPort((*inConn).RemoteAddr().String())
inconnRemoteAddr := (*inConn).RemoteAddr().String() inconnRemoteAddr := (*inConn).RemoteAddr().String()
localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
udpListener, err := net.ListenUDP("udp", localAddr) udpListener := serverConn.UDPConnListener
if err != nil { srcIP, _, _ := net.SplitHostPort((*inConn).RemoteAddr().String())
(*inConn).Close() s.log.Printf("proxy udp on %s , for %s", udpListener.LocalAddr(), inconnRemoteAddr)
udpListener.Close()
s.log.Printf("udp bind fail , %s", err)
return
}
host, _, _ := net.SplitHostPort((*inConn).LocalAddr().String())
_, port, _ := net.SplitHostPort(udpListener.LocalAddr().String())
if len(*s.cfg.LocalIPS) > 0 {
host = (*s.cfg.LocalIPS)[0]
}
s.log.Printf("proxy udp on %s , for %s", net.JoinHostPort(host, port), inconnRemoteAddr)
request.UDPReply(socks.REP_SUCCESS, net.JoinHostPort(host, port))
s.userConns.Set(inconnRemoteAddr, inConn) s.userConns.Set(inconnRemoteAddr, inConn)
var ( var (
outUDPConn *net.UDPConn outUDPConn *net.UDPConn
@ -134,15 +123,15 @@ func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
} }
}() }()
useProxy := true useProxy := true
if *s.cfg.Parent != "" { if len(*s.cfg.Parent) > 0 {
dstHost, _, _ := net.SplitHostPort(request.Addr()) dstHost, _, _ := net.SplitHostPort(serverConn.Target())
if utils.IsIternalIP(dstHost, *s.cfg.Always) { if utils.IsInternalIP(dstHost, *s.cfg.Always) {
useProxy = false useProxy = false
} else { } else {
var isInMap bool var isInMap bool
useProxy, isInMap, _, _ = s.checker.IsBlocked(request.Addr()) useProxy, isInMap, _, _ = s.checker.IsBlocked(serverConn.Target())
if !isInMap { if !isInMap {
s.checker.Add(request.Addr(), s.Resolve(request.Addr())) s.checker.Add(serverConn.Target(), s.Resolve(serverConn.Target()))
} }
} }
} else { } else {
@ -150,13 +139,17 @@ func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
} }
if useProxy { if useProxy {
//parent proxy //parent proxy
outconn, err := s.getOutConn(nil, nil, "", false) lbAddr := s.lb.Select((*inConn).RemoteAddr().String(), *s.cfg.LoadBalanceOnlyHA)
outconn, err := s.GetParentConn(lbAddr, serverConn)
//outconn, err := s.GetParentConn(nil, nil, "", false)
if err != nil { if err != nil {
clean("connnect fail", fmt.Sprintf("%s", err)) clean("connnect fail", fmt.Sprintf("%s", err))
return return
} }
client := socks.NewClientConn(&outconn, "udp", request.Addr(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil)
if err = client.Handshake(); err != nil { client, err := s.HandshakeSocksParent(&outconn, "udp", serverConn.Target(), serverConn.AuthData(), false)
if err != nil {
clean("handshake fail", fmt.Sprintf("%s", err)) clean("handshake fail", fmt.Sprintf("%s", err))
return return
} }
@ -179,11 +172,11 @@ func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
//s.log.Printf("parent udp address %s", client.UDPAddr) //s.log.Printf("parent udp address %s", client.UDPAddr)
destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr) destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr)
} }
s.log.Printf("use proxy %v : udp %s", useProxy, request.Addr()) s.log.Printf("use proxy %v : udp %s", useProxy, serverConn.Target())
//relay //relay
for {
buf := utils.LeakyBuffer.Get() buf := utils.LeakyBuffer.Get()
defer utils.LeakyBuffer.Put(buf) defer utils.LeakyBuffer.Put(buf)
for {
n, srcAddr, err := udpListener.ReadFromUDP(buf) n, srcAddr, err := udpListener.ReadFromUDP(buf)
if err != nil { if err != nil {
s.log.Printf("udp listener read fail, %s", err.Error()) s.log.Printf("udp listener read fail, %s", err.Error())
@ -245,6 +238,7 @@ func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
} }
continue continue
} }
//var dlen = n //var dlen = n
if useProxy { if useProxy {
//forward to local //forward to local

View File

@ -1,50 +1,17 @@
package sps package sps
import ( import (
"crypto/md5"
"fmt" "fmt"
"net" "net"
"runtime/debug" "runtime/debug"
"strconv"
"strings" "strings"
"time" "time"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
goaes "github.com/snail007/goproxy/utils/aes" goaes "github.com/snail007/goproxy/utils/aes"
"github.com/snail007/goproxy/utils/conncrypt"
"github.com/snail007/goproxy/utils/socks" "github.com/snail007/goproxy/utils/socks"
) )
func (s *SPS) ParentUDPKey() (key []byte) {
switch *s.cfg.ParentType {
case "tcp":
if *s.cfg.ParentKey != "" {
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey)))
return []byte(v)[:24]
}
case "tls":
return s.cfg.KeyBytes[:24]
case "kcp":
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key)))
return []byte(v)[:24]
}
return
}
func (s *SPS) LocalUDPKey() (key []byte) {
switch *s.cfg.LocalType {
case "tcp":
if *s.cfg.LocalKey != "" {
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey)))
return []byte(v)[:24]
}
case "tls":
return s.cfg.KeyBytes[:24]
case "kcp":
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key)))
return []byte(v)[:24]
}
return
}
func (s *SPS) proxyUDP(inConn *net.Conn, serverConn *socks.ServerConn) { func (s *SPS) proxyUDP(inConn *net.Conn, serverConn *socks.ServerConn) {
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
@ -123,41 +90,19 @@ func (s *SPS) proxyUDP(inConn *net.Conn, serverConn *socks.ServerConn) {
} }
}() }()
//parent proxy //parent proxy
outconn, err := s.outPool.Get() lbAddr := s.lb.Select((*inConn).RemoteAddr().String(), *s.cfg.LoadBalanceOnlyHA)
outconn, err := s.GetParentConn(lbAddr)
//outconn, err := s.GetParentConn(nil, nil, "", false) //outconn, err := s.GetParentConn(nil, nil, "", false)
if err != nil { if err != nil {
clean("connnect fail", fmt.Sprintf("%s", err)) clean("connnect fail", fmt.Sprintf("%s", err))
return return
} }
if *s.cfg.ParentCompress {
outconn = utils.NewCompConn(outconn)
}
if *s.cfg.ParentKey != "" {
outconn = conncrypt.New(outconn, &conncrypt.Config{
Password: *s.cfg.ParentKey,
})
}
s.log.Printf("connect %s for udp", serverConn.Target()) s.log.Printf("connect %s for udp", serverConn.Target())
//socks client //socks client
var client *socks.ClientConn
auth := serverConn.AuthData()
if *s.cfg.ParentAuth != "" {
a := strings.Split(*s.cfg.ParentAuth, ":")
if len(a) != 2 {
err = fmt.Errorf("parent auth data format error")
return
}
client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), &socks.Auth{User: a[0], Password: a[1]}, nil)
} else {
if !s.IsBasicAuth() && auth.Password != "" && auth.User != "" {
client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), &auth, nil)
} else {
client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil)
}
}
if err = client.Handshake(); err != nil { client, err := s.HandshakeSocksParent(s.getParentAuth(lbAddr), &outconn, "udp", serverConn.Target(), serverConn.AuthData(), false)
if err != nil {
clean("handshake fail", fmt.Sprintf("%s", err)) clean("handshake fail", fmt.Sprintf("%s", err))
return return
} }
@ -208,76 +153,42 @@ func (s *SPS) proxyUDP(inConn *net.Conn, serverConn *socks.ServerConn) {
} else { } else {
err = p.Parse(buf[:n]) err = p.Parse(buf[:n])
} }
//err = p.Parse(buf[:n])
if err != nil { if err != nil {
s.log.Printf("udp listener parse packet fail, %s", err.Error()) s.log.Printf("udp listener parse packet fail, %s", err.Error())
continue continue
} }
port, _ := strconv.Atoi(p.Port())
if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok { if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok {
if destAddr == nil {
destAddr = &net.UDPAddr{IP: net.ParseIP(p.Host()), Port: port}
}
outUDPConn, err = net.DialUDP("udp", localAddr, destAddr) outUDPConn, err = net.DialUDP("udp", localAddr, destAddr)
if err != nil { if err != nil {
s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr) s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr)
continue continue
} }
s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn) s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn)
go func() { utils.UDPCopy(udpListener, outUDPConn, srcAddr, 0, func(data []byte) []byte {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp out->local io copy crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
defer s.udpRelatedPacketConns.Remove(srcAddr.String())
//out->local io copy
buf := utils.LeakyBuffer.Get()
defer utils.LeakyBuffer.Put(buf)
for {
outUDPConn.SetReadDeadline(time.Now().Add(time.Second * 5))
n, err := outUDPConn.Read(buf)
outUDPConn.SetReadDeadline(time.Time{})
if err != nil {
s.log.Printf("read out udp data fail , %s , from : %s", err, srcAddr)
if isClosedErr(err) {
return
}
continue
}
//var dlen = n
//forward to local //forward to local
var v []byte var v []byte
//convert parent data to raw //convert parent data to raw
if len(s.udpParentKey) > 0 { if len(s.udpParentKey) > 0 {
v, err = goaes.Decrypt(s.udpParentKey, buf[:n]) v, err = goaes.Decrypt(s.udpParentKey, data)
if err != nil { if err != nil {
s.log.Printf("udp outconn parse packet fail, %s", err.Error()) s.log.Printf("udp outconn parse packet fail, %s", err.Error())
continue return []byte{}
} }
} else { } else {
v = buf[:n] v = data
} }
//now v is raw, try convert v to local //now v is raw, try convert v to local
if len(s.udpLocalKey) > 0 { if len(s.udpLocalKey) > 0 {
v, _ = goaes.Encrypt(s.udpLocalKey, v) v, _ = goaes.Encrypt(s.udpLocalKey, v)
} }
_, err = udpListener.WriteTo(v, srcAddr) return v
// _, err = udpListener.WriteTo(buf[:n], srcAddr) }, func(err interface{}) {
if err != nil {
s.udpRelatedPacketConns.Remove(srcAddr.String()) s.udpRelatedPacketConns.Remove(srcAddr.String())
s.log.Printf("write out data to local fail , %s , from : %s", err, srcAddr) if err != nil {
if isClosedErr(err) { s.log.Printf("udp out->local io copy crashed:\n%s\n%s", err, string(debug.Stack()))
return
} }
continue })
} else {
//s.log.Printf("send udp data to local success , len %d, for : %s", dlen, srcAddr)
}
}
}()
} else { } else {
outUDPConn = v.(*net.UDPConn) outUDPConn = v.(*net.UDPConn)
} }

View File

@ -2,6 +2,8 @@ package sps
import ( import (
"bytes" "bytes"
"crypto/md5"
"crypto/tls"
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
@ -11,18 +13,28 @@ import (
"runtime/debug" "runtime/debug"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/snail007/goproxy/core/cs/server"
"github.com/snail007/goproxy/core/lib/kcpcfg"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/conncrypt" "github.com/snail007/goproxy/utils/conncrypt"
cryptool "github.com/snail007/goproxy/utils/crypt"
"github.com/snail007/goproxy/utils/datasize"
"github.com/snail007/goproxy/utils/dnsx"
"github.com/snail007/goproxy/utils/iolimiter"
"github.com/snail007/goproxy/utils/jumper"
"github.com/snail007/goproxy/utils/lb"
"github.com/snail007/goproxy/utils/mapx"
"github.com/snail007/goproxy/utils/sni" "github.com/snail007/goproxy/utils/sni"
"github.com/snail007/goproxy/utils/socks" "github.com/snail007/goproxy/utils/socks"
"github.com/snail007/goproxy/utils/ss"
) )
type SPSArgs struct { type SPSArgs struct {
Parent *string Parent *[]string
CertFile *string CertFile *string
KeyFile *string KeyFile *string
CaCertFile *string CaCertFile *string
@ -49,34 +61,61 @@ type SPSArgs struct {
ParentKey *string ParentKey *string
LocalCompress *bool LocalCompress *bool
ParentCompress *bool ParentCompress *bool
SSMethod *string
SSKey *string
ParentSSMethod *string
ParentSSKey *string
DisableHTTP *bool DisableHTTP *bool
DisableSocks5 *bool DisableSocks5 *bool
DisableSS *bool
LoadBalanceMethod *string
LoadBalanceTimeout *int
LoadBalanceRetryTime *int
LoadBalanceHashTarget *bool
LoadBalanceOnlyHA *bool
ParentTLSSingle *bool
RateLimit *string
RateLimitBytes float64
Debug *bool
Jumper *string
} }
type SPS struct { type SPS struct {
outPool utils.OutConn
cfg SPSArgs cfg SPSArgs
domainResolver utils.DomainResolver domainResolver dnsx.DomainResolver
basicAuth utils.BasicAuth basicAuth utils.BasicAuth
serverChannels []*utils.ServerChannel serverChannels []*server.ServerChannel
userConns utils.ConcurrentMap userConns mapx.ConcurrentMap
log *logger.Logger log *logger.Logger
udpRelatedPacketConns utils.ConcurrentMap localCipher *ss.Cipher
parentCipher *ss.Cipher
udpRelatedPacketConns mapx.ConcurrentMap
lb *lb.Group
udpLocalKey []byte udpLocalKey []byte
udpParentKey []byte udpParentKey []byte
jumper *jumper.Jumper
parentAuthData *sync.Map
parentCipherData *sync.Map
} }
func NewSPS() services.Service { func NewSPS() services.Service {
return &SPS{ return &SPS{
outPool: utils.OutConn{},
cfg: SPSArgs{}, cfg: SPSArgs{},
basicAuth: utils.BasicAuth{}, basicAuth: utils.BasicAuth{},
serverChannels: []*utils.ServerChannel{}, serverChannels: []*server.ServerChannel{},
userConns: utils.NewConcurrentMap(), userConns: mapx.NewConcurrentMap(),
udpRelatedPacketConns: utils.NewConcurrentMap(), udpRelatedPacketConns: mapx.NewConcurrentMap(),
parentAuthData: &sync.Map{},
parentCipherData: &sync.Map{},
} }
} }
func (s *SPS) CheckArgs() (err error) { func (s *SPS) CheckArgs() (err error) {
if *s.cfg.Parent == "" {
if len(*s.cfg.Parent) == 1 && (*s.cfg.Parent)[0] == "" {
(*s.cfg.Parent) = []string{}
}
if len(*s.cfg.Parent) == 0 {
err = fmt.Errorf("parent required for %s %s", *s.cfg.LocalType, *s.cfg.Local) err = fmt.Errorf("parent required for %s %s", *s.cfg.LocalType, *s.cfg.Local)
return return
} }
@ -84,11 +123,17 @@ func (s *SPS) CheckArgs() (err error) {
err = fmt.Errorf("parent type unkown,use -T <tls|tcp|kcp>") err = fmt.Errorf("parent type unkown,use -T <tls|tcp|kcp>")
return return
} }
if *s.cfg.ParentType == "ss" && (*s.cfg.ParentSSKey == "" || *s.cfg.ParentSSMethod == "") {
err = fmt.Errorf("ss parent need a ss key, set it by : -J <sskey>")
return
}
if *s.cfg.ParentType == "tls" || *s.cfg.LocalType == "tls" { if *s.cfg.ParentType == "tls" || *s.cfg.LocalType == "tls" {
if !*s.cfg.ParentTLSSingle {
s.cfg.CertBytes, s.cfg.KeyBytes, err = 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 { if err != nil {
return return
} }
}
if *s.cfg.CaCertFile != "" { if *s.cfg.CaCertFile != "" {
s.cfg.CaCertBytes, err = ioutil.ReadFile(*s.cfg.CaCertFile) s.cfg.CaCertBytes, err = ioutil.ReadFile(*s.cfg.CaCertFile)
if err != nil { if err != nil {
@ -97,31 +142,61 @@ func (s *SPS) CheckArgs() (err error) {
} }
} }
} }
if *s.cfg.RateLimit != "0" && *s.cfg.RateLimit != "" {
var size uint64
size, err = datasize.Parse(*s.cfg.RateLimit)
if err != nil {
err = fmt.Errorf("parse rate limit size error,ERR:%s", err)
return
}
s.cfg.RateLimitBytes = float64(size)
}
s.udpLocalKey = s.LocalUDPKey() s.udpLocalKey = s.LocalUDPKey()
s.udpParentKey = s.ParentUDPKey() s.udpParentKey = s.ParentUDPKey()
if *s.cfg.Jumper != "" {
if *s.cfg.ParentType != "tls" && *s.cfg.ParentType != "tcp" {
err = fmt.Errorf("jumper only worked of -T is tls or tcp")
return
}
var j jumper.Jumper
j, err = jumper.New(*s.cfg.Jumper, time.Millisecond*time.Duration(*s.cfg.Timeout))
if err != nil {
err = fmt.Errorf("parse jumper fail, err %s", err)
return
}
s.jumper = &j
}
return return
} }
func (s *SPS) InitService() (err error) { func (s *SPS) InitService() (err error) {
if *s.cfg.DNSAddress != "" { if *s.cfg.DNSAddress != "" {
(*s).domainResolver = utils.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log) s.domainResolver = dnsx.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log)
} }
s.InitOutConnPool()
err = s.InitBasicAuth() if len(*s.cfg.Parent) > 0 {
err = s.InitLB()
if err != nil {
return return
}
func (s *SPS) InitOutConnPool() {
if *s.cfg.ParentType == "tls" || *s.cfg.ParentType == "tcp" || *s.cfg.ParentType == "kcp" {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutConn(
0,
*s.cfg.ParentType,
s.cfg.KCP,
s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes,
*s.cfg.Parent,
*s.cfg.Timeout,
)
} }
}
err = s.InitBasicAuth()
if *s.cfg.SSMethod != "" && *s.cfg.SSKey != "" {
s.localCipher, err = ss.NewCipher(*s.cfg.SSMethod, *s.cfg.SSKey)
if err != nil {
s.log.Printf("error generating cipher : %s", err)
return
}
}
if *s.cfg.ParentServiceType == "ss" {
s.parentCipher, err = ss.NewCipher(*s.cfg.ParentSSMethod, *s.cfg.ParentSSKey)
if err != nil {
s.log.Printf("error generating cipher : %s", err)
return
}
}
return
} }
func (s *SPS) StopService() { func (s *SPS) StopService() {
@ -132,6 +207,22 @@ func (s *SPS) StopService() {
} else { } else {
s.log.Printf("service sps stopped") s.log.Printf("service sps stopped")
} }
s.basicAuth = utils.BasicAuth{}
s.cfg = SPSArgs{}
s.domainResolver = dnsx.DomainResolver{}
s.lb = nil
s.localCipher = nil
s.jumper = nil
s.log = nil
s.parentCipher = nil
s.serverChannels = nil
s.udpLocalKey = nil
s.udpParentKey = nil
s.udpRelatedPacketConns = nil
s.userConns = nil
s.parentAuthData = nil
s.parentCipherData = nil
s = nil
}() }()
for _, sc := range s.serverChannels { for _, sc := range s.serverChannels {
if sc.Listener != nil && *sc.Listener != nil { if sc.Listener != nil && *sc.Listener != nil {
@ -149,6 +240,12 @@ func (s *SPS) StopService() {
(*(*c.(**net.Conn))).Close() (*(*c.(**net.Conn))).Close()
} }
} }
if s.lb != nil {
s.lb.Stop()
}
for _, c := range s.udpRelatedPacketConns.Items() {
(*c.(*net.UDPConn)).Close()
}
} }
func (s *SPS) Start(args interface{}, log *logger.Logger) (err error) { func (s *SPS) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log s.log = log
@ -159,24 +256,33 @@ func (s *SPS) Start(args interface{}, log *logger.Logger) (err error) {
if err = s.InitService(); err != nil { if err = s.InitService(); err != nil {
return return
} }
s.log.Printf("use %s %s parent %s", *s.cfg.ParentType, *s.cfg.ParentServiceType, *s.cfg.Parent)
s.log.Printf("use %s %s parent %v [ %s ]", *s.cfg.ParentType, *s.cfg.ParentServiceType, *s.cfg.Parent, strings.ToUpper(*s.cfg.LoadBalanceMethod))
for _, addr := range strings.Split(*s.cfg.Local, ",") { for _, addr := range strings.Split(*s.cfg.Local, ",") {
if addr != "" { if addr != "" {
host, port, _ := net.SplitHostPort(addr) host, port, _ := net.SplitHostPort(addr)
p, _ := strconv.Atoi(port) p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p, s.log) sc := server.NewServerChannel(host, p, s.log)
s.serverChannels = append(s.serverChannels, &sc)
if *s.cfg.LocalType == "tcp" { if *s.cfg.LocalType == "tcp" {
err = sc.ListenTCP(s.callback) err = sc.ListenTCP(s.callback)
} else if *s.cfg.LocalType == "tls" { } else if *s.cfg.LocalType == "tls" {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.callback) err = sc.ListenTLS(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.callback)
} else if *s.cfg.LocalType == "kcp" { } else if *s.cfg.LocalType == "kcp" {
err = sc.ListenKCP(s.cfg.KCP, s.callback, s.log) err = sc.ListenKCP(s.cfg.KCP, s.callback, s.log)
} }
if err != nil { if err != nil {
return return
} }
s.log.Printf("%s http(s)+socks proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr()) if *s.cfg.ParentServiceType == "socks" {
s.serverChannels = append(s.serverChannels, &sc) err = s.RunSSUDP(addr)
} else {
s.log.Println("warn : udp only for socks parent ")
}
if err != nil {
return
}
s.log.Printf("%s http(s)+socks+ss proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr())
} }
} }
return return
@ -200,22 +306,19 @@ func (s *SPS) callback(inConn net.Conn) {
}) })
} }
var err error var err error
lbAddr := ""
switch *s.cfg.ParentType { switch *s.cfg.ParentType {
case "kcp": case "kcp", "tcp", "tls":
fallthrough lbAddr, err = s.OutToTCP(&inConn)
case "tcp":
fallthrough
case "tls":
err = s.OutToTCP(&inConn)
default: default:
err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType) err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType)
} }
if err != nil { if err != nil {
s.log.Printf("connect to %s parent %s fail, ERR:%s from %s", *s.cfg.ParentType, *s.cfg.Parent, err, inConn.RemoteAddr()) s.log.Printf("connect to %s parent %s fail, ERR:%s from %s", *s.cfg.ParentType, lbAddr, err, inConn.RemoteAddr())
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
} }
} }
func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { func (s *SPS) OutToTCP(inConn *net.Conn) (lbAddr string, err error) {
enableUDP := *s.cfg.ParentServiceType == "socks" enableUDP := *s.cfg.ParentServiceType == "socks"
udpIP, _, _ := net.SplitHostPort((*inConn).LocalAddr().String()) udpIP, _, _ := net.SplitHostPort((*inConn).LocalAddr().String())
if len(*s.cfg.LocalIPS) > 0 { if len(*s.cfg.LocalIPS) > 0 {
@ -243,9 +346,9 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
isSNI, _ := sni.ServerNameFromBytes(h) isSNI, _ := sni.ServerNameFromBytes(h)
*inConn = bInConn *inConn = bInConn
address := "" address := ""
var auth socks.Auth var auth = socks.Auth{}
var forwardBytes []byte var forwardBytes []byte
//fmt.Printf("%v", h) //fmt.Printf("%v", header)
if utils.IsSocks5(h) { if utils.IsSocks5(h) {
if *s.cfg.DisableSocks5 { if *s.cfg.DisableSocks5 {
(*inConn).Close() (*inConn).Close()
@ -291,7 +394,6 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
request.HTTPSReply() request.HTTPSReply()
//s.log.Printf("https reply: %s", request.Host) //s.log.Printf("https reply: %s", request.Host)
} else { } else {
//forwardBytes = bytes.TrimRight(request.HeadBuf,"\r\n")
forwardBytes = request.HeadBuf forwardBytes = request.HeadBuf
} }
address = request.Host address = request.Host
@ -306,6 +408,25 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
auth = socks.Auth{User: userpassA[0], Password: userpassA[1]} auth = socks.Auth{User: userpassA[0], Password: userpassA[1]}
} }
} }
} else {
//ss
if *s.cfg.DisableSS {
(*inConn).Close()
return
}
(*inConn).SetDeadline(time.Now().Add(time.Second * 5))
ssConn := ss.NewConn(*inConn, s.localCipher.Copy())
address, err = ss.GetRequest(ssConn)
(*inConn).SetDeadline(time.Time{})
if err != nil {
return
}
// ensure the host does not contain some illegal characters, NUL may panic on Win32
if strings.ContainsRune(address, 0x00) {
err = errors.New("invalid domain name")
return
}
*inConn = ssConn
} }
if err != nil || address == "" { if err != nil || address == "" {
s.log.Printf("unknown request from: %s,%s", (*inConn).RemoteAddr(), string(h)) s.log.Printf("unknown request from: %s,%s", (*inConn).RemoteAddr(), string(h))
@ -316,22 +437,19 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
} }
//connect to parent //connect to parent
var outConn net.Conn var outConn net.Conn
outConn, err = s.outPool.Get() selectAddr := (*inConn).RemoteAddr().String()
if utils.LBMethod(*s.cfg.LoadBalanceMethod) == lb.SELECT_HASH && *s.cfg.LoadBalanceHashTarget {
selectAddr = address
}
lbAddr = s.lb.Select(selectAddr, *s.cfg.LoadBalanceOnlyHA)
outConn, err = s.GetParentConn(lbAddr)
if err != nil { if err != nil {
s.log.Printf("connect to %s , err:%s", *s.cfg.Parent, err) s.log.Printf("connect to %s , err:%s", lbAddr, err)
utils.CloseConn(inConn) utils.CloseConn(inConn)
return return
} }
if *s.cfg.ParentCompress { ParentAuth := s.getParentAuth(lbAddr)
outConn = utils.NewCompConn(outConn) if ParentAuth != "" || *s.cfg.ParentSSKey != "" || s.IsBasicAuth() {
}
if *s.cfg.ParentKey != "" {
outConn = conncrypt.New(outConn, &conncrypt.Config{
Password: *s.cfg.ParentKey,
})
}
if *s.cfg.ParentAuth != "" || s.IsBasicAuth() {
forwardBytes = utils.RemoveProxyHeaders(forwardBytes) forwardBytes = utils.RemoveProxyHeaders(forwardBytes)
} }
@ -345,11 +463,14 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
isHTTPS = true isHTTPS = true
pb.Write([]byte(fmt.Sprintf("CONNECT %s HTTP/1.1\r\n", address))) pb.Write([]byte(fmt.Sprintf("CONNECT %s HTTP/1.1\r\n", address)))
} }
pb.WriteString(fmt.Sprintf("Host: %s\r\n", address))
pb.WriteString(fmt.Sprintf("Proxy-Host: %s\r\n", address))
pb.WriteString("Proxy-Connection: Keep-Alive\r\n") pb.WriteString("Proxy-Connection: Keep-Alive\r\n")
pb.WriteString("Connection: Keep-Alive\r\n")
u := "" u := ""
if *s.cfg.ParentAuth != "" { if ParentAuth != "" {
a := strings.Split(*s.cfg.ParentAuth, ":") a := strings.Split(ParentAuth, ":")
if len(a) != 2 { if len(a) != 2 {
err = fmt.Errorf("parent auth data format error") err = fmt.Errorf("parent auth data format error")
return return
@ -377,7 +498,7 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
_, err = outConn.Write(pb.Bytes()) _, err = outConn.Write(pb.Bytes())
outConn.SetDeadline(time.Time{}) outConn.SetDeadline(time.Time{})
if err != nil { if err != nil {
s.log.Printf("write CONNECT to %s , err:%s", *s.cfg.Parent, err) s.log.Printf("write CONNECT to %s , err:%s", lbAddr, err)
utils.CloseConn(inConn) utils.CloseConn(inConn)
utils.CloseConn(&outConn) utils.CloseConn(&outConn)
return return
@ -389,7 +510,7 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
_, err = outConn.Read(reply) _, err = outConn.Read(reply)
outConn.SetDeadline(time.Time{}) outConn.SetDeadline(time.Time{})
if err != nil { if err != nil {
s.log.Printf("read reply from %s , err:%s", *s.cfg.Parent, err) s.log.Printf("read reply from %s , err:%s", lbAddr, err)
utils.CloseConn(inConn) utils.CloseConn(inConn)
utils.CloseConn(&outConn) utils.CloseConn(&outConn)
return return
@ -398,23 +519,24 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
} }
} else if *s.cfg.ParentServiceType == "socks" { } else if *s.cfg.ParentServiceType == "socks" {
s.log.Printf("connect %s", address) s.log.Printf("connect %s", address)
//socks client //socks client
var clientConn *socks.ClientConn _, err = s.HandshakeSocksParent(ParentAuth, &outConn, "tcp", address, auth, false)
if *s.cfg.ParentAuth != "" { if err != nil {
a := strings.Split(*s.cfg.ParentAuth, ":") s.log.Printf("handshake fail, %s", err)
if len(a) != 2 {
err = fmt.Errorf("parent auth data format error")
return return
} }
clientConn = socks.NewClientConn(&outConn, "tcp", address, time.Millisecond*time.Duration(*s.cfg.Timeout), &socks.Auth{User: a[0], Password: a[1]}, nil)
} else { } else if *s.cfg.ParentServiceType == "ss" {
if !s.IsBasicAuth() && auth.Password != "" && auth.User != "" { ra, e := ss.RawAddr(address)
clientConn = socks.NewClientConn(&outConn, "tcp", address, time.Millisecond*time.Duration(*s.cfg.Timeout), &auth, nil) if e != nil {
} else { err = fmt.Errorf("build ss raw addr fail, err: %s", e)
clientConn = socks.NewClientConn(&outConn, "tcp", address, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil) return
} }
}
if err = clientConn.Handshake(); err != nil { outConn, err = ss.DialWithRawAddr(&outConn, ra, "", s.getParentCipher(lbAddr))
if err != nil {
err = fmt.Errorf("dial ss parent fail, err : %s", err)
return return
} }
} }
@ -424,18 +546,27 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
outConn.Write(forwardBytes) outConn.Write(forwardBytes)
} }
if s.cfg.RateLimitBytes > 0 {
outConn = iolimiter.NewReaderConn(outConn, s.cfg.RateLimitBytes)
}
//bind //bind
inAddr := (*inConn).RemoteAddr().String() inAddr := (*inConn).RemoteAddr().String()
outAddr := outConn.RemoteAddr().String() outAddr := outConn.RemoteAddr().String()
utils.IoBind((*inConn), outConn, func(err interface{}) { utils.IoBind((*inConn), outConn, func(err interface{}) {
s.log.Printf("conn %s - %s released [%s]", inAddr, outAddr, address) s.log.Printf("conn %s - %s released [%s]", inAddr, outAddr, address)
s.userConns.Remove(inAddr) s.userConns.Remove(inAddr)
s.lb.DecreaseConns(lbAddr)
}, s.log) }, s.log)
s.log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, address) s.log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, address)
s.lb.IncreasConns(lbAddr)
if c, ok := s.userConns.Get(inAddr); ok { if c, ok := s.userConns.Get(inAddr); ok {
(*c.(*net.Conn)).Close() (*c.(*net.Conn)).Close()
} }
s.userConns.Set(inAddr, inConn) s.userConns.Set(inAddr, inConn)
return return
} }
func (s *SPS) InitBasicAuth() (err error) { func (s *SPS) InitBasicAuth() (err error) {
@ -463,6 +594,71 @@ func (s *SPS) InitBasicAuth() (err error) {
} }
return return
} }
func (s *SPS) InitLB() (err error) {
configs := lb.BackendsConfig{}
for _, addr := range *s.cfg.Parent {
var _addrInfo []string
if strings.Contains(addr, "#") {
_s := addr[:strings.Index(addr, "#")]
_auth, err := cryptool.CryptTools.Base64Decode(_s)
if err != nil {
s.log.Printf("decoding parent auth data [ %s ] fail , error : %s", _s, err)
return err
}
_addrInfo = strings.Split(addr[strings.Index(addr, "#")+1:], "@")
if *s.cfg.ParentServiceType == "ss" {
_s := strings.Split(_auth, ":")
m := _s[0]
k := _s[1]
if m == "" {
m = *s.cfg.ParentSSMethod
}
if k == "" {
k = *s.cfg.ParentSSKey
}
cipher, err := ss.NewCipher(m, k)
if err != nil {
s.log.Printf("error generating cipher, ssMethod: %s, ssKey: %s, error : %s", m, k, err)
return err
}
s.parentCipherData.Store(_addrInfo[0], cipher)
} else {
s.parentAuthData.Store(_addrInfo[0], _auth)
}
} else {
_addrInfo = strings.Split(addr, "@")
}
_addr := _addrInfo[0]
weight := 1
if len(_addrInfo) == 2 {
weight, _ = strconv.Atoi(_addrInfo[1])
}
configs = append(configs, &lb.BackendConfig{
Address: _addr,
Weight: weight,
ActiveAfter: 1,
InactiveAfter: 2,
Timeout: time.Duration(*s.cfg.LoadBalanceTimeout) * time.Millisecond,
RetryTime: time.Duration(*s.cfg.LoadBalanceRetryTime) * time.Millisecond,
})
}
LB := lb.NewGroup(utils.LBMethod(*s.cfg.LoadBalanceMethod), configs, &s.domainResolver, s.log, *s.cfg.Debug)
s.lb = &LB
return
}
func (s *SPS) getParentAuth(lbAddr string) string {
if v, ok := s.parentAuthData.Load(lbAddr); ok {
return v.(string)
}
return *s.cfg.ParentAuth
}
func (s *SPS) getParentCipher(lbAddr string) *ss.Cipher {
if v, ok := s.parentCipherData.Load(lbAddr); ok {
return v.(*ss.Cipher).Copy()
}
return s.parentCipher.Copy()
}
func (s *SPS) IsBasicAuth() bool { func (s *SPS) IsBasicAuth() bool {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != "" return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != ""
} }
@ -515,3 +711,103 @@ func (s *SPS) Resolve(address string) string {
} }
return ip return ip
} }
func (s *SPS) GetParentConn(address string) (conn net.Conn, err error) {
if *s.cfg.ParentType == "tls" {
if s.jumper == nil {
var _conn tls.Conn
if *s.cfg.ParentTLSSingle {
_conn, err = utils.SingleTlsConnectHost(address, *s.cfg.Timeout, s.cfg.CaCertBytes)
} else {
_conn, err = utils.TlsConnectHost(address, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes)
}
if err == nil {
conn = net.Conn(&_conn)
}
} else {
var conf *tls.Config
if *s.cfg.ParentTLSSingle {
conf, err = utils.SingleTlsConfig(s.cfg.CaCertBytes)
} else {
conf, err = utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes)
}
if err != nil {
return nil, err
}
var _c net.Conn
_c, err = s.jumper.Dial(address, time.Millisecond*time.Duration(*s.cfg.Timeout))
if err == nil {
conn = net.Conn(tls.Client(_c, conf))
}
}
} else if *s.cfg.ParentType == "kcp" {
conn, err = utils.ConnectKCPHost(address, s.cfg.KCP)
} else {
if s.jumper == nil {
conn, err = utils.ConnectHost(address, *s.cfg.Timeout)
} else {
conn, err = s.jumper.Dial(address, time.Millisecond*time.Duration(*s.cfg.Timeout))
}
}
if err == nil {
if *s.cfg.ParentCompress {
conn = utils.NewCompConn(conn)
}
if *s.cfg.ParentKey != "" {
conn = conncrypt.New(conn, &conncrypt.Config{
Password: *s.cfg.ParentKey,
})
}
}
return
}
func (s *SPS) HandshakeSocksParent(parentAuth string, outconn *net.Conn, network, dstAddr string, auth socks.Auth, fromSS bool) (client *socks.ClientConn, err error) {
if parentAuth != "" {
a := strings.Split(parentAuth, ":")
if len(a) != 2 {
err = fmt.Errorf("parent auth data format error")
return
}
client = socks.NewClientConn(outconn, network, dstAddr, time.Millisecond*time.Duration(*s.cfg.Timeout), &socks.Auth{User: a[0], Password: a[1]}, nil)
} else {
if !fromSS && !s.IsBasicAuth() && auth.Password != "" && auth.User != "" {
client = socks.NewClientConn(outconn, network, dstAddr, time.Millisecond*time.Duration(*s.cfg.Timeout), &auth, nil)
} else {
client = socks.NewClientConn(outconn, network, dstAddr, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil)
}
}
err = client.Handshake()
return
}
func (s *SPS) ParentUDPKey() (key []byte) {
switch *s.cfg.ParentType {
case "tcp":
if *s.cfg.ParentKey != "" {
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey)))
return []byte(v)[:24]
}
case "tls":
if s.cfg.KeyBytes != nil {
return s.cfg.KeyBytes[:24]
}
case "kcp":
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key)))
return []byte(v)[:24]
}
return
}
func (s *SPS) LocalUDPKey() (key []byte) {
switch *s.cfg.LocalType {
case "tcp":
if *s.cfg.LocalKey != "" {
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey)))
return []byte(v)[:24]
}
case "tls":
return s.cfg.KeyBytes[:24]
case "kcp":
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key)))
return []byte(v)[:24]
}
return
}

161
services/sps/ssudp.go Normal file
View File

@ -0,0 +1,161 @@
package sps
import (
"bytes"
"fmt"
"net"
"runtime/debug"
"time"
"github.com/snail007/goproxy/utils"
goaes "github.com/snail007/goproxy/utils/aes"
"github.com/snail007/goproxy/utils/socks"
)
func (s *SPS) RunSSUDP(addr string) (err error) {
a, _ := net.ResolveUDPAddr("udp", addr)
listener, err := net.ListenUDP("udp", a)
if err != nil {
s.log.Printf("ss udp bind error %s", err)
return
}
s.log.Printf("ss udp on %s", listener.LocalAddr())
s.udpRelatedPacketConns.Set(addr, listener)
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
buf := utils.LeakyBuffer.Get()
defer utils.LeakyBuffer.Put(buf)
for {
n, srcAddr, err := listener.ReadFrom(buf)
if err != nil {
s.log.Printf("read from client error %s", err)
if utils.IsNetClosedErr(err) {
return
}
continue
}
var (
inconnRemoteAddr = srcAddr.String()
outUDPConn *net.UDPConn
outconn net.Conn
outconnLocalAddr string
destAddr *net.UDPAddr
clean = func(msg, err string) {
raddr := ""
if outUDPConn != nil {
raddr = outUDPConn.RemoteAddr().String()
outUDPConn.Close()
}
if msg != "" {
if raddr != "" {
s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr)
} else {
s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr)
}
}
s.userConns.Remove(inconnRemoteAddr)
if outconn != nil {
outconn.Close()
}
if outconnLocalAddr != "" {
s.userConns.Remove(outconnLocalAddr)
}
}
)
defer clean("", "")
raw := new(bytes.Buffer)
raw.Write([]byte{0x00, 0x00, 0x00})
raw.Write(s.localCipher.Decrypt(buf[:n]))
socksPacket := socks.NewPacketUDP()
err = socksPacket.Parse(raw.Bytes())
raw = nil
if err != nil {
s.log.Printf("udp parse error %s", err)
return
}
if v, ok := s.udpRelatedPacketConns.Get(inconnRemoteAddr); !ok {
//socks client
lbAddr := s.lb.Select(inconnRemoteAddr, *s.cfg.LoadBalanceOnlyHA)
outconn, err := s.GetParentConn(lbAddr)
if err != nil {
clean("connnect fail", fmt.Sprintf("%s", err))
return
}
client, err := s.HandshakeSocksParent(s.getParentAuth(lbAddr), &outconn, "udp", socksPacket.Addr(), socks.Auth{}, true)
if err != nil {
clean("handshake fail", fmt.Sprintf("%s", err))
return
}
outconnLocalAddr = outconn.LocalAddr().String()
s.userConns.Set(outconnLocalAddr, &outconn)
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
buf := make([]byte, 1)
outconn.SetReadDeadline(time.Time{})
if _, err := outconn.Read(buf); err != nil {
clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err))
}
}()
destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr)
localZeroAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
outUDPConn, err = net.DialUDP("udp", localZeroAddr, destAddr)
if err != nil {
s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr)
return
}
s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn)
utils.UDPCopy(listener, outUDPConn, srcAddr, time.Second*5, func(data []byte) []byte {
//forward to local
var v []byte
//convert parent data to raw
if len(s.udpParentKey) > 0 {
v, err = goaes.Decrypt(s.udpParentKey, data)
if err != nil {
s.log.Printf("udp outconn parse packet fail, %s", err.Error())
return []byte{}
}
} else {
v = data
}
return s.localCipher.Encrypt(v[3:])
}, func(err interface{}) {
s.udpRelatedPacketConns.Remove(srcAddr.String())
if err != nil {
s.log.Printf("udp out->local io copy crashed:\n%s\n%s", err, string(debug.Stack()))
}
})
} else {
outUDPConn = v.(*net.UDPConn)
}
//forward to parent
//p is raw, now convert it to parent
var v []byte
if len(s.udpParentKey) > 0 {
v, _ = goaes.Encrypt(s.udpParentKey, socksPacket.Bytes())
} else {
v = socksPacket.Bytes()
}
_, err = outUDPConn.Write(v)
socksPacket = socks.PacketUDP{}
if err != nil {
if utils.IsNetClosedErr(err) {
return
}
s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr)
}
}
}()
return
}

View File

@ -1,19 +1,20 @@
package tcp package tcp
import ( import (
"bufio"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io"
logger "log" logger "log"
"net" "net"
"runtime/debug" "runtime/debug"
"strings"
"time" "time"
"github.com/snail007/goproxy/core/cs/server"
"github.com/snail007/goproxy/core/lib/kcpcfg"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/jumper" "github.com/snail007/goproxy/utils/jumper"
"github.com/snail007/goproxy/utils/mapx"
"strconv" "strconv"
) )
@ -28,28 +29,39 @@ type TCPArgs struct {
ParentType *string ParentType *string
LocalType *string LocalType *string
Timeout *int Timeout *int
CheckParentInterval *int
KCP kcpcfg.KCPConfigArgs KCP kcpcfg.KCPConfigArgs
Jumper *string Jumper *string
} }
type UDPConnItem struct {
conn *net.Conn
isActive bool
touchtime int64
srcAddr *net.UDPAddr
localAddr *net.UDPAddr
udpConn *net.UDPConn
connid string
}
type TCP struct { type TCP struct {
cfg TCPArgs cfg TCPArgs
sc *utils.ServerChannel sc *server.ServerChannel
isStop bool isStop bool
userConns utils.ConcurrentMap userConns mapx.ConcurrentMap
log *logger.Logger log *logger.Logger
jumper *jumper.Jumper jumper *jumper.Jumper
udpConns mapx.ConcurrentMap
} }
func NewTCP() services.Service { func NewTCP() services.Service {
return &TCP{ return &TCP{
cfg: TCPArgs{}, cfg: TCPArgs{},
isStop: false, isStop: false,
userConns: utils.NewConcurrentMap(), userConns: mapx.NewConcurrentMap(),
udpConns: mapx.NewConcurrentMap(),
} }
} }
func (s *TCP) CheckArgs() (err error) { func (s *TCP) CheckArgs() (err error) {
if *s.cfg.Parent == "" { if len(*s.cfg.Parent) == 0 {
err = fmt.Errorf("parent required for %s %s", *s.cfg.LocalType, *s.cfg.Local) err = fmt.Errorf("parent required for %s %s", *s.cfg.LocalType, *s.cfg.Local)
return return
} }
@ -79,7 +91,7 @@ func (s *TCP) CheckArgs() (err error) {
return return
} }
func (s *TCP) InitService() (err error) { func (s *TCP) InitService() (err error) {
s.UDPGCDeamon()
return return
} }
func (s *TCP) StopService() { func (s *TCP) StopService() {
@ -90,6 +102,12 @@ func (s *TCP) StopService() {
} else { } else {
s.log.Printf("service tcp stopped") s.log.Printf("service tcp stopped")
} }
s.cfg = TCPArgs{}
s.jumper = nil
s.log = nil
s.sc = nil
s.userConns = nil
s = nil
}() }()
s.isStop = true s.isStop = true
if s.sc.Listener != nil && *s.sc.Listener != nil { if s.sc.Listener != nil && *s.sc.Listener != nil {
@ -111,15 +129,15 @@ func (s *TCP) Start(args interface{}, log *logger.Logger) (err error) {
if err = s.InitService(); err != nil { if err = s.InitService(); err != nil {
return return
} }
s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) s.log.Printf("use %s parent %v", *s.cfg.ParentType, *s.cfg.Parent)
host, port, _ := net.SplitHostPort(*s.cfg.Local) host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port) p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p, s.log) sc := server.NewServerChannel(host, p, s.log)
if *s.cfg.LocalType == "tcp" { if *s.cfg.LocalType == "tcp" {
err = sc.ListenTCP(s.callback) err = sc.ListenTCP(s.callback)
} else if *s.cfg.LocalType == "tls" { } else if *s.cfg.LocalType == "tls" {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.callback) err = sc.ListenTLS(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.callback)
} else if *s.cfg.LocalType == "kcp" { } else if *s.cfg.LocalType == "kcp" {
err = sc.ListenKCP(s.cfg.KCP, s.callback, s.log) err = sc.ListenKCP(s.cfg.KCP, s.callback, s.log)
} }
@ -141,24 +159,24 @@ func (s *TCP) callback(inConn net.Conn) {
} }
}() }()
var err error var err error
lbAddr := ""
switch *s.cfg.ParentType { switch *s.cfg.ParentType {
case "kcp": case "kcp", "tcp", "tls":
fallthrough
case "tcp":
fallthrough
case "tls":
err = s.OutToTCP(&inConn) err = s.OutToTCP(&inConn)
case "udp": case "udp":
err = s.OutToUDP(&inConn) s.OutToUDP(&inConn)
default: default:
err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType) err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType)
} }
if err != nil { if err != nil {
s.log.Printf("connect to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err) if !utils.IsNetClosedErr(err) {
s.log.Printf("connect to %s parent %s fail, ERR:%s", *s.cfg.ParentType, lbAddr, err)
}
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
} }
} }
func (s *TCP) OutToTCP(inConn *net.Conn) (err error) { func (s *TCP) OutToTCP(inConn *net.Conn) (err error) {
var outConn net.Conn var outConn net.Conn
outConn, err = s.GetParentConn() outConn, err = s.GetParentConn()
if err != nil { if err != nil {
@ -182,56 +200,136 @@ func (s *TCP) OutToTCP(inConn *net.Conn) (err error) {
return return
} }
func (s *TCP) OutToUDP(inConn *net.Conn) (err error) { func (s *TCP) OutToUDP(inConn *net.Conn) (err error) {
s.log.Printf("conn created , remote : %s ", (*inConn).RemoteAddr()) var item *UDPConnItem
var body []byte
srcAddr := ""
defer func() {
if item != nil {
(*item.conn).Close()
item.udpConn.Close()
s.udpConns.Remove(srcAddr)
(*inConn).Close()
}
}()
for { for {
if s.isStop { if s.isStop {
return
}
var srcAddr string
srcAddr, body, err = utils.ReadUDPPacket(*inConn)
if err != nil {
if strings.Contains(err.Error(), "n != int(") {
continue
}
// if !utils.IsNetDeadlineErr(err) && err != io.EOF && !utils.IsNetClosedErr(err) {
// s.log.Printf("udp packet revecived from client fail, err: %s", err)
// }
return
}
localAddr := *s.cfg.Parent
if v, ok := s.udpConns.Get(srcAddr); !ok {
_srcAddr, _ := net.ResolveUDPAddr("udp", srcAddr)
zeroAddr, _ := net.ResolveUDPAddr("udp", ":")
_localAddr, _ := net.ResolveUDPAddr("udp", localAddr)
var c *net.UDPConn
c, err = net.DialUDP("udp", zeroAddr, _localAddr)
if err != nil {
s.log.Printf("create local udp conn fail, err : %s", err)
(*inConn).Close() (*inConn).Close()
return return
} }
srcAddr, body, err := utils.ReadUDPPacket(bufio.NewReader(*inConn)) item = &UDPConnItem{
if err == io.EOF || err == io.ErrUnexpectedEOF { conn: inConn,
//s.log.Printf("connection %s released", srcAddr) srcAddr: _srcAddr,
utils.CloseConn(inConn) localAddr: _localAddr,
break udpConn: c,
} }
//log.Debugf("udp packet revecived:%s,%v", srcAddr, body) s.udpConns.Set(srcAddr, item)
dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Parent) s.UDPRevecive(srcAddr)
} else {
item = v.(*UDPConnItem)
}
item.touchtime = time.Now().Unix()
go item.udpConn.Write(body)
}
}
func (s *TCP) UDPRevecive(key string) {
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
s.log.Printf("udp conn %s connected", key)
v, ok := s.udpConns.Get(key)
if !ok {
s.log.Printf("[warn] udp conn not exists for %s", key)
return
}
cui := v.(*UDPConnItem)
buf := utils.LeakyBuffer.Get()
defer func() {
utils.LeakyBuffer.Put(buf)
(*cui.conn).Close()
cui.udpConn.Close()
s.udpConns.Remove(key)
s.log.Printf("udp conn %s released", key)
}()
for {
n, err := cui.udpConn.Read(buf)
if err != nil { if err != nil {
s.log.Printf("can't resolve address: %s", err) if !utils.IsNetClosedErr(err) {
utils.CloseConn(inConn) s.log.Printf("udp conn read udp packet fail , err: %s ", err)
break
}
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr)
if err != nil {
s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
continue
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write(body)
if err != nil {
s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
continue
}
//log.Debugf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 512)
len, _, err := conn.ReadFromUDP(buf)
if err != nil {
s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
continue
}
respBody := buf[0:len]
//log.Debugf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
_, err = (*inConn).Write(utils.UDPPacket(srcAddr, respBody))
if err != nil {
s.log.Printf("send udp response fail ,ERR:%s", err)
utils.CloseConn(inConn)
break
}
//s.log.Printf("send udp response success ,from:%s", dstAddr.String())
} }
return return
}
cui.touchtime = time.Now().Unix()
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
(*cui.conn).SetWriteDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = (*cui.conn).Write(utils.UDPPacket(cui.srcAddr.String(), buf[:n]))
(*cui.conn).SetWriteDeadline(time.Time{})
if err != nil {
cui.udpConn.Close()
return
}
}()
}
}()
}
func (s *TCP) UDPGCDeamon() {
gctime := int64(30)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
if s.isStop {
return
}
timer := time.NewTicker(time.Second)
for {
<-timer.C
gcKeys := []string{}
s.udpConns.IterCb(func(key string, v interface{}) {
if time.Now().Unix()-v.(*UDPConnItem).touchtime > gctime {
(*(v.(*UDPConnItem).conn)).Close()
(v.(*UDPConnItem).udpConn).Close()
gcKeys = append(gcKeys, key)
s.log.Printf("gc udp conn %s", key)
}
})
for _, k := range gcKeys {
s.udpConns.Remove(k)
}
gcKeys = nil
}
}()
} }
func (s *TCP) GetParentConn() (conn net.Conn, err error) { func (s *TCP) GetParentConn() (conn net.Conn, err error) {
if *s.cfg.ParentType == "tls" { if *s.cfg.ParentType == "tls" {

View File

@ -7,10 +7,13 @@ import (
"net" "net"
"os" "os"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/snail007/goproxy/core/cs/server"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/mapx"
//"github.com/xtaci/smux" //"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux" smux "github.com/hashicorp/yamux"
@ -37,8 +40,8 @@ type ServerConn struct {
} }
type TunnelBridge struct { type TunnelBridge struct {
cfg TunnelBridgeArgs cfg TunnelBridgeArgs
serverConns utils.ConcurrentMap serverConns mapx.ConcurrentMap
clientControlConns utils.ConcurrentMap clientControlConns mapx.ConcurrentMap
isStop bool isStop bool
log *logger.Logger log *logger.Logger
} }
@ -46,8 +49,8 @@ type TunnelBridge struct {
func NewTunnelBridge() services.Service { func NewTunnelBridge() services.Service {
return &TunnelBridge{ return &TunnelBridge{
cfg: TunnelBridgeArgs{}, cfg: TunnelBridgeArgs{},
serverConns: utils.NewConcurrentMap(), serverConns: mapx.NewConcurrentMap(),
clientControlConns: utils.NewConcurrentMap(), clientControlConns: mapx.NewConcurrentMap(),
isStop: false, isStop: false,
} }
} }
@ -71,6 +74,11 @@ func (s *TunnelBridge) StopService() {
} else { } else {
s.log.Printf("service tbridge stopped") s.log.Printf("service tbridge stopped")
} }
s.cfg = TunnelBridgeArgs{}
s.clientControlConns = nil
s.log = nil
s.serverConns = nil
s = nil
}() }()
s.isStop = true s.isStop = true
for _, sess := range s.clientControlConns.Items() { for _, sess := range s.clientControlConns.Items() {
@ -91,9 +99,9 @@ func (s *TunnelBridge) Start(args interface{}, log *logger.Logger) (err error) {
} }
host, port, _ := net.SplitHostPort(*s.cfg.Local) host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port) p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p, s.log) sc := server.NewServerChannel(host, p, s.log)
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.callback) err = sc.ListenTLS(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.callback)
if err != nil { if err != nil {
return return
} }
@ -123,10 +131,24 @@ func (s *TunnelBridge) callback(inConn net.Conn) {
s.log.Printf("mux server conn accept error,ERR:%s", err) s.log.Printf("mux server conn accept error,ERR:%s", err)
return return
} }
go func() {
defer func() {
_ = recover()
}()
timer := time.NewTicker(time.Second * 3)
for {
<-timer.C
if sess.NumStreams() == 0 {
sess.Close()
timer.Stop()
return
}
}
}()
var buf = make([]byte, 1024) var buf = make([]byte, 1024)
n, _ := inConn.Read(buf) n, _ := inConn.Read(buf)
reader := bytes.NewReader(buf[:n]) reader := bytes.NewReader(buf[:n])
//reader := bufio.NewReader(inConn) //reader := bufio.NewReader(inConn)
var connType uint8 var connType uint8
@ -163,7 +185,7 @@ func (s *TunnelBridge) callback(inConn net.Conn) {
(*item.(*net.Conn)).SetWriteDeadline(time.Now().Add(time.Second * 3)) (*item.(*net.Conn)).SetWriteDeadline(time.Now().Add(time.Second * 3))
_, err := (*item.(*net.Conn)).Write(packet) _, err := (*item.(*net.Conn)).Write(packet)
(*item.(*net.Conn)).SetWriteDeadline(time.Time{}) (*item.(*net.Conn)).SetWriteDeadline(time.Time{})
if err != nil { if err != nil && strings.Contains(err.Error(), "stream closed") {
s.log.Printf("%s client control conn write signal fail, err: %s, retrying...", key, err) s.log.Printf("%s client control conn write signal fail, err: %s, retrying...", key, err)
time.Sleep(time.Second * 3) time.Sleep(time.Second * 3)
continue continue

View File

@ -7,20 +7,19 @@ import (
logger "log" logger "log"
"net" "net"
"os" "os"
"runtime/debug"
"strings"
"time" "time"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/jumper" "github.com/snail007/goproxy/utils/jumper"
"github.com/snail007/goproxy/utils/mapx"
//"github.com/xtaci/smux" //"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux" smux "github.com/hashicorp/yamux"
) )
const (
CONN_SERVER_MUX = uint8(6)
CONN_CLIENT_MUX = uint8(7)
)
type TunnelClientArgs struct { type TunnelClientArgs struct {
Parent *string Parent *string
CertFile *string CertFile *string
@ -31,24 +30,36 @@ type TunnelClientArgs struct {
Timeout *int Timeout *int
Jumper *string Jumper *string
} }
type ClientUDPConnItem struct {
conn *net.Conn
isActive bool
touchtime int64
srcAddr *net.UDPAddr
localAddr *net.UDPAddr
udpConn *net.UDPConn
connid string
}
type TunnelClient struct { type TunnelClient struct {
cfg TunnelClientArgs cfg TunnelClientArgs
ctrlConn net.Conn ctrlConn *net.Conn
isStop bool isStop bool
userConns utils.ConcurrentMap userConns mapx.ConcurrentMap
log *logger.Logger log *logger.Logger
jumper *jumper.Jumper jumper *jumper.Jumper
udpConns mapx.ConcurrentMap
} }
func NewTunnelClient() services.Service { func NewTunnelClient() services.Service {
return &TunnelClient{ return &TunnelClient{
cfg: TunnelClientArgs{}, cfg: TunnelClientArgs{},
userConns: utils.NewConcurrentMap(), userConns: mapx.NewConcurrentMap(),
isStop: false, isStop: false,
udpConns: mapx.NewConcurrentMap(),
} }
} }
func (s *TunnelClient) InitService() (err error) { func (s *TunnelClient) InitService() (err error) {
s.UDPGCDeamon()
return return
} }
@ -86,10 +97,17 @@ func (s *TunnelClient) StopService() {
} else { } else {
s.log.Printf("service tclient stopped") s.log.Printf("service tclient stopped")
} }
s.cfg = TunnelClientArgs{}
s.ctrlConn = nil
s.jumper = nil
s.log = nil
s.udpConns = nil
s.userConns = nil
s = nil
}() }()
s.isStop = true s.isStop = true
if s.ctrlConn != nil { if s.ctrlConn != nil {
s.ctrlConn.Close() (*s.ctrlConn).Close()
} }
for _, c := range s.userConns.Items() { for _, c := range s.userConns.Items() {
(*c.(*net.Conn)).Close() (*c.(*net.Conn)).Close()
@ -111,38 +129,50 @@ func (s *TunnelClient) Start(args interface{}, log *logger.Logger) (err error) {
return return
} }
if s.ctrlConn != nil { if s.ctrlConn != nil {
s.ctrlConn.Close() (*s.ctrlConn).Close()
} }
var c net.Conn
s.ctrlConn, err = s.GetInConn(CONN_CLIENT_CONTROL, *s.cfg.Key) c, err = s.GetInConn(CONN_CLIENT_CONTROL, *s.cfg.Key)
if err != nil { if err != nil {
s.log.Printf("control connection err: %s, retrying...", err) s.log.Printf("control connection err: %s, retrying...", err)
time.Sleep(time.Second * 3) time.Sleep(time.Second * 3)
if s.ctrlConn != nil {
s.ctrlConn.Close()
}
continue continue
} }
s.ctrlConn = &c
for { for {
if s.isStop { if s.isStop {
return return
} }
var ID, clientLocalAddr, serverID string var ID, clientLocalAddr, serverID string
err = utils.ReadPacketData(s.ctrlConn, &ID, &clientLocalAddr, &serverID) err = utils.ReadPacketData(*s.ctrlConn, &ID, &clientLocalAddr, &serverID)
if err != nil { if err != nil {
if s.ctrlConn != nil { if s.ctrlConn != nil {
s.ctrlConn.Close() (*s.ctrlConn).Close()
} }
s.log.Printf("read connection signal err: %s, retrying...", err) s.log.Printf("read connection signal err: %s, retrying...", err)
break break
} }
s.log.Printf("signal revecived:%s %s %s", serverID, ID, clientLocalAddr) //s.log.Printf("signal revecived:%s %s %s", serverID, ID, clientLocalAddr)
protocol := clientLocalAddr[:3] protocol := clientLocalAddr[:3]
localAddr := clientLocalAddr[4:] localAddr := clientLocalAddr[4:]
if protocol == "udp" { if protocol == "udp" {
go s.ServeUDP(localAddr, ID, serverID) go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
s.ServeUDP(localAddr, ID, serverID)
}()
} else { } else {
go s.ServeConn(localAddr, ID, serverID) go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
s.ServeConn(localAddr, ID, serverID)
}()
} }
} }
} }
@ -183,7 +213,7 @@ func (s *TunnelClient) GetConn() (conn net.Conn, err error) {
} }
} }
if err == nil { if err == nil {
c, e := smux.Client(conn, &smux.Config{ sess, e := smux.Client(conn, &smux.Config{
AcceptBacklog: 256, AcceptBacklog: 256,
EnableKeepAlive: true, EnableKeepAlive: true,
KeepAliveInterval: 9 * time.Second, KeepAliveInterval: 9 * time.Second,
@ -196,12 +226,26 @@ func (s *TunnelClient) GetConn() (conn net.Conn, err error) {
err = e err = e
return return
} }
conn, e = c.OpenStream() conn, e = sess.OpenStream()
if e != nil { if e != nil {
s.log.Printf("mux client conn open stream error,ERR:%s", e) s.log.Printf("mux client conn open stream error,ERR:%s", e)
err = e err = e
return return
} }
go func() {
defer func() {
_ = recover()
}()
timer := time.NewTicker(time.Second * 3)
for {
<-timer.C
if sess.NumStreams() == 0 {
sess.Close()
timer.Stop()
return
}
}
}()
} }
return return
} }
@ -229,62 +273,141 @@ func (s *TunnelClient) ServeUDP(localAddr, ID, serverID string) {
} }
// s.cm.Add(*s.cfg.Key, ID, &inConn) // s.cm.Add(*s.cfg.Key, ID, &inConn)
s.log.Printf("conn %s created", ID) s.log.Printf("conn %s created", ID)
var item *ClientUDPConnItem
var body []byte
srcAddr := ""
defer func() {
if item != nil {
(*(*item).conn).Close()
(*item).udpConn.Close()
s.udpConns.Remove(srcAddr)
inConn.Close()
}
}()
for { for {
if s.isStop { if s.isStop {
return return
} }
srcAddr, body, err := utils.ReadUDPPacket(inConn) srcAddr, body, err = utils.ReadUDPPacket(inConn)
if err == io.EOF || err == io.ErrUnexpectedEOF { if err != nil {
s.log.Printf("connection %s released", ID) if strings.Contains(err.Error(), "n != int(") {
utils.CloseConn(&inConn) continue
break }
} else if err != nil { if !utils.IsNetDeadlineErr(err) && err != io.EOF {
s.log.Printf("udp packet revecived fail, err: %s", err) s.log.Printf("udp packet revecived from bridge fail, err: %s", err)
}
return
}
if v, ok := s.udpConns.Get(srcAddr); !ok {
_srcAddr, _ := net.ResolveUDPAddr("udp", srcAddr)
zeroAddr, _ := net.ResolveUDPAddr("udp", ":")
_localAddr, _ := net.ResolveUDPAddr("udp", localAddr)
c, err := net.DialUDP("udp", zeroAddr, _localAddr)
if err != nil {
s.log.Printf("create local udp conn fail, err : %s", err)
inConn.Close()
return
}
item = &ClientUDPConnItem{
conn: &inConn,
srcAddr: _srcAddr,
localAddr: _localAddr,
udpConn: c,
connid: ID,
}
s.udpConns.Set(srcAddr, item)
s.UDPRevecive(srcAddr, ID)
} else { } else {
//s.log.Printf("udp packet revecived:%s,%v", srcAddr, body) item = v.(*ClientUDPConnItem)
go s.processUDPPacket(&inConn, srcAddr, localAddr, body)
} }
(*item).touchtime = time.Now().Unix()
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
(*item).udpConn.Write(body)
}()
} }
// }
} }
func (s *TunnelClient) processUDPPacket(inConn *net.Conn, srcAddr, localAddr string, body []byte) { func (s *TunnelClient) UDPRevecive(key, ID string) {
dstAddr, err := net.ResolveUDPAddr("udp", localAddr) go func() {
if err != nil { defer func() {
s.log.Printf("can't resolve address: %s", err) if e := recover(); e != nil {
utils.CloseConn(inConn) fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
s.log.Printf("udp conn %s connected", ID)
v, ok := s.udpConns.Get(key)
if !ok {
s.log.Printf("[warn] udp conn not exists for %s, connid : %s", key, ID)
return return
} }
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} cui := v.(*ClientUDPConnItem)
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr) buf := utils.LeakyBuffer.Get()
defer func() {
utils.LeakyBuffer.Put(buf)
(*cui.conn).Close()
cui.udpConn.Close()
s.udpConns.Remove(key)
s.log.Printf("udp conn %s released", ID)
}()
for {
n, err := cui.udpConn.Read(buf)
if err != nil { if err != nil {
s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err) if !utils.IsNetClosedErr(err) {
s.log.Printf("udp conn read udp packet fail , err: %s ", err)
}
return return
} }
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) cui.touchtime = time.Now().Unix()
_, err = conn.Write(body) go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
(*cui.conn).SetWriteDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = (*cui.conn).Write(utils.UDPPacket(cui.srcAddr.String(), buf[:n]))
(*cui.conn).SetWriteDeadline(time.Time{})
if err != nil { if err != nil {
s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) cui.udpConn.Close()
return return
} }
//s.log.Printf("send udp packet to %s success", dstAddr.String()) }()
buf := make([]byte, 1024) }
length, _, err := conn.ReadFromUDP(buf) }()
if err != nil { }
s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) func (s *TunnelClient) UDPGCDeamon() {
gctime := int64(30)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s",e, string(debug.Stack()))
}
}()
if s.isStop {
return return
} }
respBody := buf[0:length] timer := time.NewTicker(time.Second)
//s.log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody) for {
bs := utils.UDPPacket(srcAddr, respBody) <-timer.C
_, err = (*inConn).Write(bs) gcKeys := []string{}
if err != nil { s.udpConns.IterCb(func(key string, v interface{}) {
s.log.Printf("send udp response fail ,ERR:%s", err) if time.Now().Unix()-v.(*ClientUDPConnItem).touchtime > gctime {
utils.CloseConn(inConn) (*(v.(*ClientUDPConnItem).conn)).Close()
return (v.(*ClientUDPConnItem).udpConn).Close()
gcKeys = append(gcKeys, key)
s.log.Printf("gc udp conn %s", v.(*ClientUDPConnItem).connid)
} }
//s.log.Printf("send udp response success ,from:%s ,%d ,%v", dstAddr.String(), len(bs), bs) })
for _, k := range gcKeys {
s.udpConns.Remove(k)
}
gcKeys = nil
}
}()
} }
func (s *TunnelClient) ServeConn(localAddr, ID, serverID string) { func (s *TunnelClient) ServeConn(localAddr, ID, serverID string) {
var inConn, outConn net.Conn var inConn, outConn net.Conn

View File

@ -12,9 +12,11 @@ import (
"strings" "strings"
"time" "time"
"github.com/snail007/goproxy/core/cs/server"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/jumper" "github.com/snail007/goproxy/utils/jumper"
"github.com/snail007/goproxy/utils/mapx"
//"github.com/xtaci/smux" //"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux" smux "github.com/hashicorp/yamux"
@ -35,14 +37,19 @@ type TunnelServerArgs struct {
Mgr *TunnelServerManager Mgr *TunnelServerManager
Jumper *string Jumper *string
} }
type UDPItem struct { type TunnelServer struct {
packet *[]byte cfg TunnelServerArgs
localAddr *net.UDPAddr sc server.ServerChannel
srcAddr *net.UDPAddr isStop bool
udpConn *net.Conn
userConns mapx.ConcurrentMap
log *logger.Logger
jumper *jumper.Jumper
udpConns mapx.ConcurrentMap
} }
type TunnelServerManager struct { type TunnelServerManager struct {
cfg TunnelServerArgs cfg TunnelServerArgs
udpChn chan UDPItem
serverID string serverID string
servers []*services.Service servers []*services.Service
log *logger.Logger log *logger.Logger
@ -51,7 +58,6 @@ type TunnelServerManager struct {
func NewTunnelServerManager() services.Service { func NewTunnelServerManager() services.Service {
return &TunnelServerManager{ return &TunnelServerManager{
cfg: TunnelServerArgs{}, cfg: TunnelServerArgs{},
udpChn: make(chan UDPItem, 50000),
serverID: utils.Uniqueid(), serverID: utils.Uniqueid(),
servers: []*services.Service{}, servers: []*services.Service{},
} }
@ -123,6 +129,11 @@ func (s *TunnelServerManager) StopService() {
for _, server := range s.servers { for _, server := range s.servers {
(*server).Clean() (*server).Clean()
} }
s.cfg = TunnelServerArgs{}
s.log = nil
s.serverID = ""
s.servers = nil
s = nil
} }
func (s *TunnelServerManager) CheckArgs() (err error) { func (s *TunnelServerManager) CheckArgs() (err error) {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
@ -136,26 +147,29 @@ func (s *TunnelServerManager) InitService() (err error) {
return return
} }
type TunnelServer struct {
cfg TunnelServerArgs
udpChn chan UDPItem
sc utils.ServerChannel
isStop bool
udpConn *net.Conn
userConns utils.ConcurrentMap
log *logger.Logger
jumper *jumper.Jumper
}
func NewTunnelServer() services.Service { func NewTunnelServer() services.Service {
return &TunnelServer{ return &TunnelServer{
cfg: TunnelServerArgs{}, cfg: TunnelServerArgs{},
udpChn: make(chan UDPItem, 50000),
isStop: false, isStop: false,
userConns: utils.NewConcurrentMap(), userConns: mapx.NewConcurrentMap(),
udpConns: mapx.NewConcurrentMap(),
} }
} }
type TunnelUDPPacketItem struct {
packet *[]byte
localAddr *net.UDPAddr
srcAddr *net.UDPAddr
}
type TunnelUDPConnItem struct {
conn *net.Conn
isActive bool
touchtime int64
srcAddr *net.UDPAddr
localAddr *net.UDPAddr
connid string
}
func (s *TunnelServer) StopService() { func (s *TunnelServer) StopService() {
defer func() { defer func() {
e := recover() e := recover()
@ -164,6 +178,14 @@ func (s *TunnelServer) StopService() {
} else { } else {
s.log.Printf("service server stopped") s.log.Printf("service server stopped")
} }
s.cfg = TunnelServerArgs{}
s.jumper = nil
s.log = nil
s.sc = server.ServerChannel{}
s.udpConn = nil
s.udpConns = nil
s.userConns = nil
s = nil
}() }()
s.isStop = true s.isStop = true
@ -181,7 +203,7 @@ func (s *TunnelServer) StopService() {
} }
} }
func (s *TunnelServer) InitService() (err error) { func (s *TunnelServer) InitService() (err error) {
s.UDPConnDeamon() s.UDPGCDeamon()
return return
} }
func (s *TunnelServer) CheckArgs() (err error) { func (s *TunnelServer) CheckArgs() (err error) {
@ -212,14 +234,10 @@ func (s *TunnelServer) Start(args interface{}, log *logger.Logger) (err error) {
} }
host, port, _ := net.SplitHostPort(*s.cfg.Local) host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port) p, _ := strconv.Atoi(port)
s.sc = utils.NewServerChannel(host, p, s.log) s.sc = server.NewServerChannel(host, p, s.log)
if *s.cfg.IsUDP { if *s.cfg.IsUDP {
err = s.sc.ListenUDP(func(packet []byte, localAddr, srcAddr *net.UDPAddr) { err = s.sc.ListenUDP(func(listener *net.UDPConn, packet []byte, localAddr, srcAddr *net.UDPAddr) {
s.udpChn <- UDPItem{ s.UDPSend(packet, localAddr, srcAddr)
packet: &packet,
localAddr: localAddr,
srcAddr: srcAddr,
}
}) })
if err != nil { if err != nil {
return return
@ -346,89 +364,120 @@ func (s *TunnelServer) GetConn() (conn net.Conn, err error) {
} }
return return
} }
func (s *TunnelServer) UDPConnDeamon() { func (s *TunnelServer) UDPGCDeamon() {
gctime := int64(30)
go func() { go func() {
defer func() { defer func() {
if err := recover(); err != nil { if e := recover(); e != nil {
s.log.Printf("udp conn deamon crashed with err : %s \nstack: %s", err, string(debug.Stack())) fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
} }
}() }()
var outConn net.Conn if s.isStop {
// var hb utils.HeartbeatReadWriter return
var ID string }
// var cmdChn = make(chan bool, 1000) timer := time.NewTicker(time.Second)
var err error
for { for {
if s.isStop { <-timer.C
return gcKeys := []string{}
s.udpConns.IterCb(func(key string, v interface{}) {
if time.Now().Unix()-v.(*TunnelUDPConnItem).touchtime > gctime {
(*(v.(*TunnelUDPConnItem).conn)).Close()
gcKeys = append(gcKeys, key)
s.log.Printf("gc udp conn %s", v.(*TunnelUDPConnItem).connid)
} }
item := <-s.udpChn })
RETRY: for _, k := range gcKeys {
if s.isStop { s.udpConns.Remove(k)
return
} }
if outConn == nil { gcKeys = nil
for { }
if s.isStop { }()
return }
} func (s *TunnelServer) UDPSend(data []byte, localAddr, srcAddr *net.UDPAddr) {
outConn, ID, err = s.GetOutConn(CONN_SERVER) var (
if err != nil { uc *TunnelUDPConnItem
// cmdChn <- true key = srcAddr.String()
outConn = nil ID string
utils.CloseConn(&outConn) err error
s.log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err) outconn net.Conn
time.Sleep(time.Second * 3) )
continue v, ok := s.udpConns.Get(key)
} else { if !ok {
go func(outConn net.Conn, ID string) { outconn, ID, err = s.GetOutConn(CONN_SERVER)
if s.udpConn != nil { if err != nil {
(*s.udpConn).Close() s.log.Printf("connect to %s fail, err: %s", *s.cfg.Parent, err)
} return
s.udpConn = &outConn }
for { uc = &TunnelUDPConnItem{
if s.isStop { conn: &outconn,
return srcAddr: srcAddr,
} localAddr: localAddr,
srcAddrFromConn, body, err := utils.ReadUDPPacket(outConn) connid: ID,
if err == io.EOF || err == io.ErrUnexpectedEOF { }
s.log.Printf("UDP deamon connection %s exited", ID) s.udpConns.Set(key, uc)
break s.UDPRevecive(key, ID)
} } else {
if err != nil { uc = v.(*TunnelUDPConnItem)
s.log.Printf("parse revecived udp packet fail, err: %s ,%v", err, body) }
continue go func() {
} defer func() {
//s.log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn) if e := recover(); e != nil {
_srcAddr := strings.Split(srcAddrFromConn, ":") (*uc.conn).Close()
if len(_srcAddr) != 2 { s.udpConns.Remove(key)
s.log.Printf("parse revecived udp packet fail, addr error : %s", srcAddrFromConn) s.log.Printf("udp sender crashed with error : %s", e)
continue }
} }()
port, _ := strconv.Atoi(_srcAddr[1]) uc.touchtime = time.Now().Unix()
dstAddr := &net.UDPAddr{IP: net.ParseIP(_srcAddr[0]), Port: port} (*uc.conn).SetWriteDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = s.sc.UDPListener.WriteToUDP(body, dstAddr) _, err = (*uc.conn).Write(utils.UDPPacket(srcAddr.String(), data))
if err != nil { (*uc.conn).SetWriteDeadline(time.Time{})
s.log.Printf("udp response to local %s fail,ERR:%s", srcAddrFromConn, err) if err != nil {
continue s.log.Printf("write udp packet to %s fail ,flush err:%s ", *s.cfg.Parent, err)
} }
//s.log.Printf("udp response to local %s success , %v", srcAddrFromConn, body) }()
} }
}(outConn, ID) func (s *TunnelServer) UDPRevecive(key, ID string) {
break go func() {
} defer func() {
} if e := recover(); e != nil {
} fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
outConn.SetWriteDeadline(time.Now().Add(time.Second)) }
_, err = outConn.Write(utils.UDPPacket(item.srcAddr.String(), *item.packet)) }()
outConn.SetWriteDeadline(time.Time{}) s.log.Printf("udp conn %s connected", ID)
if err != nil { var uc *TunnelUDPConnItem
utils.CloseConn(&outConn) defer func() {
outConn = nil if uc != nil {
s.log.Printf("write udp packet to %s fail ,flush err:%s ,retrying...", *s.cfg.Parent, err) (*uc.conn).Close()
goto RETRY }
} s.udpConns.Remove(key)
//s.log.Printf("write packet %v", *item.packet) s.log.Printf("udp conn %s released", ID)
}()
v, ok := s.udpConns.Get(key)
if !ok {
s.log.Printf("[warn] udp conn not exists for %s, connid : %s", key, ID)
return
}
uc = v.(*TunnelUDPConnItem)
for {
_, body, err := utils.ReadUDPPacket(*uc.conn)
if err != nil {
if strings.Contains(err.Error(), "n != int(") {
continue
}
if err != io.EOF {
s.log.Printf("udp conn read udp packet fail , err: %s ", err)
}
return
}
uc.touchtime = time.Now().Unix()
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
}
}()
s.sc.UDPListener.WriteToUDP(body, uc.srcAddr)
}()
} }
}() }()
} }

View File

@ -1,9 +1,8 @@
package udp package udp
import ( import (
"bufio" "crypto/tls"
"fmt" "fmt"
"hash/crc32"
"io" "io"
logger "log" logger "log"
"net" "net"
@ -12,9 +11,10 @@ import (
"strings" "strings"
"time" "time"
"github.com/snail007/goproxy/core/cs/server"
"github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/mapx"
) )
type UDPArgs struct { type UDPArgs struct {
@ -29,28 +29,44 @@ type UDPArgs struct {
CheckParentInterval *int CheckParentInterval *int
} }
type UDP struct { type UDP struct {
p utils.ConcurrentMap p mapx.ConcurrentMap
outPool utils.OutConn
cfg UDPArgs cfg UDPArgs
sc *utils.ServerChannel sc *server.ServerChannel
isStop bool isStop bool
log *logger.Logger log *logger.Logger
outUDPConnCtxMap mapx.ConcurrentMap
udpConns mapx.ConcurrentMap
dstAddr *net.UDPAddr
}
type UDPConnItem struct {
conn *net.Conn
touchtime int64
srcAddr *net.UDPAddr
localAddr *net.UDPAddr
connid string
}
type outUDPConnCtx struct {
localAddr *net.UDPAddr
srcAddr *net.UDPAddr
udpconn *net.UDPConn
touchtime int64
} }
func NewUDP() services.Service { func NewUDP() services.Service {
return &UDP{ return &UDP{
outPool: utils.OutConn{}, p: mapx.NewConcurrentMap(),
p: utils.NewConcurrentMap(),
isStop: false, isStop: false,
outUDPConnCtxMap: mapx.NewConcurrentMap(),
udpConns: mapx.NewConcurrentMap(),
} }
} }
func (s *UDP) CheckArgs() (err error) { func (s *UDP) CheckArgs() (err error) {
if *s.cfg.Parent == "" { if len(*s.cfg.Parent) == 0 {
err = fmt.Errorf("parent required for udp %s", *s.cfg.Local) err = fmt.Errorf("parent required for udp %s", *s.cfg.Local)
return return
} }
if *s.cfg.ParentType == "" { if *s.cfg.ParentType == "" {
err = fmt.Errorf("parent type unkown,use -T <tls|tcp>") err = fmt.Errorf("parent type unkown,use -T <udp|tls|tcp>")
return return
} }
if *s.cfg.ParentType == "tls" { if *s.cfg.ParentType == "tls" {
@ -59,12 +75,17 @@ func (s *UDP) CheckArgs() (err error) {
return return
} }
} }
s.dstAddr, err = net.ResolveUDPAddr("udp", *s.cfg.Parent)
if err != nil {
s.log.Printf("resolve udp addr %s fail fail,ERR:%s", *s.cfg.Parent, err)
return
}
return return
} }
func (s *UDP) InitService() (err error) { func (s *UDP) InitService() (err error) {
if *s.cfg.ParentType != "udp" { s.OutToUDPGCDeamon()
s.InitOutConnPool() s.UDPGCDeamon()
}
return return
} }
func (s *UDP) StopService() { func (s *UDP) StopService() {
@ -75,6 +96,11 @@ func (s *UDP) StopService() {
} else { } else {
s.log.Printf("service udp stopped") s.log.Printf("service udp stopped")
} }
s.cfg = UDPArgs{}
s.log = nil
s.p = nil
s.sc = nil
s = nil
}() }()
s.isStop = true s.isStop = true
if s.sc.Listener != nil && *s.sc.Listener != nil { if s.sc.Listener != nil && *s.sc.Listener != nil {
@ -96,7 +122,7 @@ func (s *UDP) Start(args interface{}, log *logger.Logger) (err error) {
} }
host, port, _ := net.SplitHostPort(*s.cfg.Local) host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port) p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p, s.log) sc := server.NewServerChannel(host, p, s.log)
s.sc = &sc s.sc = &sc
err = sc.ListenUDP(s.callback) err = sc.ListenUDP(s.callback)
if err != nil { if err != nil {
@ -109,32 +135,26 @@ func (s *UDP) Start(args interface{}, log *logger.Logger) (err error) {
func (s *UDP) Clean() { func (s *UDP) Clean() {
s.StopService() s.StopService()
} }
func (s *UDP) callback(packet []byte, localAddr, srcAddr *net.UDPAddr) { func (s *UDP) callback(listener *net.UDPConn, packet []byte, localAddr, srcAddr *net.UDPAddr) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
s.log.Printf("udp conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack())) s.log.Printf("udp conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
} }
}() }()
var err error
switch *s.cfg.ParentType { switch *s.cfg.ParentType {
case "tcp": case "tcp", "tls":
fallthrough s.OutToTCP(packet, localAddr, srcAddr)
case "tls":
err = s.OutToTCP(packet, localAddr, srcAddr)
case "udp": case "udp":
err = s.OutToUDP(packet, localAddr, srcAddr) s.OutToUDP(packet, localAddr, srcAddr)
default: default:
err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType) s.log.Printf("unkown parent type %s", *s.cfg.ParentType)
}
if err != nil {
s.log.Printf("connect to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err)
} }
} }
func (s *UDP) GetConn(connKey string) (conn net.Conn, isNew bool, err error) { func (s *UDP) GetConn(connKey string) (conn net.Conn, isNew bool, err error) {
isNew = !s.p.Has(connKey) isNew = !s.p.Has(connKey)
var _conn interface{} var _conn interface{}
if isNew { if isNew {
_conn, err = s.outPool.Get() _conn, err = s.GetParentConn()
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
@ -145,117 +165,231 @@ func (s *UDP) GetConn(connKey string) (conn net.Conn, isNew bool, err error) {
conn = _conn.(net.Conn) conn = _conn.(net.Conn)
return return
} }
func (s *UDP) OutToTCP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err error) { func (s *UDP) OutToTCP(data []byte, localAddr, srcAddr *net.UDPAddr) (err error) {
numLocal := crc32.ChecksumIEEE([]byte(localAddr.String())) s.UDPSend(data, localAddr, srcAddr)
numSrc := crc32.ChecksumIEEE([]byte(srcAddr.String()))
mod := uint32(10)
if mod == 0 {
mod = 10
}
connKey := uint64((numLocal/10)*10 + numSrc%mod)
conn, isNew, err := s.GetConn(fmt.Sprintf("%d", connKey))
if err != nil {
s.log.Printf("upd get conn to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err)
return return
} }
if isNew { func (s *UDP) OutToUDPGCDeamon() {
gctime := int64(30)
go func() { go func() {
defer func() { defer func() {
if err := recover(); err != nil { if e := recover(); e != nil {
s.log.Printf("udp conn handler out to tcp crashed with err : %s \nstack: %s", err, string(debug.Stack())) fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
} }
}() }()
s.log.Printf("conn %d created , local: %s", connKey, srcAddr.String())
for {
if s.isStop { if s.isStop {
conn.Close()
return return
} }
srcAddrFromConn, body, err := utils.ReadUDPPacket(bufio.NewReader(conn)) timer := time.NewTicker(time.Second)
if err == io.EOF || err == io.ErrUnexpectedEOF { for {
//s.log.Printf("connection %d released", connKey) <-timer.C
s.p.Remove(fmt.Sprintf("%d", connKey)) gcKeys := []string{}
s.outUDPConnCtxMap.IterCb(func(key string, v interface{}) {
if time.Now().Unix()-v.(*outUDPConnCtx).touchtime > gctime {
(*(v.(*outUDPConnCtx).udpconn)).Close()
gcKeys = append(gcKeys, key)
s.log.Printf("gc udp conn %s <--> %s", (*v.(*outUDPConnCtx)).srcAddr, (*v.(*outUDPConnCtx)).localAddr)
}
})
for _, k := range gcKeys {
s.outUDPConnCtxMap.Remove(k)
}
gcKeys = nil
}
}()
}
func (s *UDP) OutToUDP(packet []byte, localAddr, srcAddr *net.UDPAddr) {
var ouc *outUDPConnCtx
if v, ok := s.outUDPConnCtxMap.Get(srcAddr.String()); !ok {
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
conn, err := net.DialUDP("udp", clientSrcAddr, s.dstAddr)
if err != nil {
s.log.Printf("connect to udp %s fail,ERR:%s", s.dstAddr.String(), err)
}
ouc = &outUDPConnCtx{
localAddr: localAddr,
srcAddr: srcAddr,
udpconn: conn,
}
s.outUDPConnCtxMap.Set(srcAddr.String(), ouc)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
}
}()
s.log.Printf("udp conn %s <--> %s connected", srcAddr.String(), localAddr.String())
buf := utils.LeakyBuffer.Get()
defer func() {
utils.LeakyBuffer.Put(buf)
s.outUDPConnCtxMap.Remove(srcAddr.String())
s.log.Printf("udp conn %s <--> %s released", srcAddr.String(), localAddr.String())
}()
for {
n, err := ouc.udpconn.Read(buf)
if err != nil {
if !utils.IsNetClosedErr(err) {
s.log.Printf("udp conn read udp packet fail , err: %s ", err)
}
return
}
ouc.touchtime = time.Now().Unix()
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
}
}()
(*(s.sc).UDPListener).SetWriteDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = (*(s.sc).UDPListener).WriteTo(buf[:n], srcAddr)
(*(s.sc).UDPListener).SetWriteDeadline(time.Time{})
}()
}
}()
} else {
ouc = v.(*outUDPConnCtx)
}
go func() {
ouc.touchtime = time.Now().Unix()
ouc.udpconn.SetWriteDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
ouc.udpconn.Write(packet)
ouc.udpconn.SetWriteDeadline(time.Time{})
}()
return
}
func (s *UDP) 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 {
conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout)
}
return
}
func (s *UDP) UDPGCDeamon() {
gctime := int64(30)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
}
}()
if s.isStop {
return
}
timer := time.NewTicker(time.Second)
for {
<-timer.C
gcKeys := []string{}
s.udpConns.IterCb(func(key string, v interface{}) {
if time.Now().Unix()-v.(*UDPConnItem).touchtime > gctime {
(*(v.(*UDPConnItem).conn)).Close()
gcKeys = append(gcKeys, key)
s.log.Printf("gc udp conn %s", v.(*UDPConnItem).connid)
}
})
for _, k := range gcKeys {
s.udpConns.Remove(k)
}
gcKeys = nil
}
}()
}
func (s *UDP) UDPSend(data []byte, localAddr, srcAddr *net.UDPAddr) {
var (
uc *UDPConnItem
key = srcAddr.String()
err error
outconn net.Conn
)
v, ok := s.udpConns.Get(key)
if !ok {
for {
outconn, err = s.GetParentConn()
if err != nil && strings.Contains(err.Error(), "can not connect at same time") {
time.Sleep(time.Millisecond * 500)
continue
} else {
break break
} }
}
if err != nil { if err != nil {
s.log.Printf("parse revecived udp packet fail, err: %s", err) s.log.Printf("connect to %s fail, err: %s", *s.cfg.Parent, err)
continue return
} }
//s.log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn) uc = &UDPConnItem{
_srcAddr := strings.Split(srcAddrFromConn, ":") conn: &outconn,
if len(_srcAddr) != 2 { srcAddr: srcAddr,
s.log.Printf("parse revecived udp packet fail, addr error : %s", srcAddrFromConn) localAddr: localAddr,
continue
} }
port, _ := strconv.Atoi(_srcAddr[1]) s.udpConns.Set(key, uc)
dstAddr := &net.UDPAddr{IP: net.ParseIP(_srcAddr[0]), Port: port} s.UDPRevecive(key)
_, err = s.sc.UDPListener.WriteToUDP(body, dstAddr) } else {
if err != nil { uc = v.(*UDPConnItem)
s.log.Printf("udp response to local %s fail,ERR:%s", srcAddr, err)
continue
} }
//s.log.Printf("udp response to local %s success", srcAddr) go func() {
defer func() {
if e := recover(); e != nil {
(*uc.conn).Close()
s.udpConns.Remove(key)
s.log.Printf("udp sender crashed with error : %s", e)
} }
}() }()
} uc.touchtime = time.Now().Unix()
//s.log.Printf("select conn %d , local: %s", connKey, srcAddr.String()) (*uc.conn).SetWriteDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
writer := bufio.NewWriter(conn) _, err = (*uc.conn).Write(utils.UDPPacket(fmt.Sprintf("%s", srcAddr.String()), data))
//fmt.Println(conn, writer) (*uc.conn).SetWriteDeadline(time.Time{})
writer.Write(utils.UDPPacket(srcAddr.String(), packet))
err = writer.Flush()
if err != nil { if err != nil {
s.log.Printf("write udp packet to %s fail ,flush err:%s", *s.cfg.Parent, err) s.log.Printf("write udp packet to %s fail ,flush err:%s ", *s.cfg.Parent, err)
return
} }
//s.log.Printf("write packet %v", packet) }()
return
} }
func (s *UDP) OutToUDP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err error) { func (s *UDP) UDPRevecive(key string) {
//s.log.Printf("udp packet revecived:%s,%v", srcAddr, packet) go func() {
dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Parent) defer func() {
if err != nil { if e := recover(); e != nil {
s.log.Printf("resolve udp addr %s fail fail,ERR:%s", dstAddr.String(), err) fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
}
}()
s.log.Printf("udp conn %s connected", key)
var uc *UDPConnItem
defer func() {
if uc != nil {
(*uc.conn).Close()
}
s.udpConns.Remove(key)
s.log.Printf("udp conn %s released", key)
}()
v, ok := s.udpConns.Get(key)
if !ok {
s.log.Printf("[warn] udp conn not exists for %s", key)
return return
} }
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} uc = v.(*UDPConnItem)
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr) for {
_, body, err := utils.ReadUDPPacket(*uc.conn)
if err != nil { if err != nil {
s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err) if strings.Contains(err.Error(), "n != int(") {
continue
}
if err != io.EOF && !utils.IsNetClosedErr(err) {
s.log.Printf("udp conn read udp packet fail , err: %s ", err)
}
return return
} }
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) uc.touchtime = time.Now().Unix()
_, err = conn.Write(packet) go func() {
if err != nil { defer func() {
s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) if e := recover(); e != nil {
return fmt.Printf("crashed, err: %s\nstack:\n%s", e, string(debug.Stack()))
} }
//s.log.Printf("send udp packet to %s success", dstAddr.String()) }()
buf := make([]byte, 512) s.sc.UDPListener.WriteToUDP(body, uc.srcAddr)
len, _, err := conn.ReadFromUDP(buf) }()
if err != nil {
s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return
}
//s.log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
_, err = s.sc.UDPListener.WriteToUDP(buf[0:len], srcAddr)
if err != nil {
s.log.Printf("send udp response to cluster fail ,ERR:%s", err)
return
}
//s.log.Printf("send udp response to cluster success ,from:%s", dstAddr.String())
return
}
func (s *UDP) InitOutConnPool() {
if *s.cfg.ParentType == "tls" || *s.cfg.ParentType == "tcp" {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutConn(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType,
kcpcfg.KCPConfigArgs{},
s.cfg.CertBytes, s.cfg.KeyBytes, nil,
*s.cfg.Parent,
*s.cfg.Timeout,
)
} }
}()
} }

Some files were not shown because too many files have changed in this diff Show More