385 Commits
v4.3 ... v6.1

Author SHA1 Message Date
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
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
arraykeys@gmail.com
a3eb0bfde9 Merge branch 'dev' 2018-08-30 16:10:28 +08:00
arraykeys@gmail.com
33e665631d v5.4 2018-08-30 16:09:58 +08:00
arraykeys@gmail.com
375eb9aa66 v5.4 2018-08-30 15:34:13 +08:00
arraykeys@gmail.com
889ce76676 优化sdk,支持并发启动/关闭操作 2018-08-24 11:41:15 +08:00
arraykeys@gmail.com
abdcae31c9 优化sdk,支持并发启动/关闭操作 2018-08-24 11:40:36 +08:00
arraykeys@gmail.com
c9f8f6c6fb fix GetAllInterfaceAddr cpu high 2018-08-21 10:52:18 +08:00
arraykeys@gmail.com
aab899397e fix GetAllInterfaceAddr cpu high 2018-08-21 10:29:51 +08:00
arraykeys@gmail.com
a933322ef7 Merge branch 'dev' 2018-08-06 16:07:24 +08:00
arraykeys@gmail.com
b9f054c495 v5.3 2018-08-06 16:06:43 +08:00
arraykeys@gmail.com
5c2e6fd109 a 2018-07-24 17:25:04 +08:00
arraykeys@gmail.com
e427263ccf a 2018-07-24 12:10:24 +08:00
arraykeys@gmail.com
a1a2c3f985 sdk5.3 2018-07-24 11:31:16 +08:00
arraykeys@gmail.com
84458a10e4 Merge remote-tracking branch 'origin/dev' into dev 2018-07-18 11:01:46 +08:00
snail007
9dd7bf80c5 Merge pull request #114 from schmich/fix-typo
Fix typo
2018-07-18 09:09:59 +08:00
Chris Schmich
317d0113fb Fix typo: 'stoped' -> 'stopped'. 2018-07-17 14:02:38 -07:00
arraykeys@gmail.com
53153da2e6 a 2018-07-17 17:55:54 +08:00
arraykeys@gmail.com
747a94a1c8 fix #113 2018-07-17 16:00:48 +08:00
arraykeys@gmail.com
df94ca5601 a 2018-07-16 18:32:53 +08:00
arraykeys@gmail.com
3cf7788f34 a 2018-07-16 18:10:21 +08:00
arraykeys@gmail.com
af91921070 a 2018-07-16 16:18:34 +08:00
arraykeys@gmail.com
7fc5c30f60 fix docker file 2018-07-16 15:54:55 +08:00
arraykeys@gmail.com
c4a7f0fbb0 Merge remote-tracking branch 'origin/dev' into dev 2018-07-12 13:34:18 +08:00
arraykeys@gmail.com
f656773858 优化智能检查的初始化时机 2018-07-12 13:32:46 +08:00
snail007
e208ccee18 Merge pull request #110 from boboanvip/dev
net.LookupIP may cause  deadlock in windows
2018-07-12 12:38:20 +08:00
boboan
5980fee788 net.LookupIP may cause deadlock in windows 2018-07-12 10:31:54 +08:00
boboanvip
61f791e77d Merge branch 'dev' into dev 2018-07-12 10:28:32 +08:00
boboan
907dec42e0 net.LookupIP may cause deadlock in windows
https://github.com/golang/go/issues/24178
2018-07-12 10:21:28 +08:00
arraykeys
8dce99fec6 a 2018-07-11 21:35:02 +08:00
snail007
db13b83f44 Merge pull request #109 from boboanvip/master
fix net.LookupIP may cause  deadlock in windows
2018-07-11 20:50:58 +08:00
boboan
dd8d7dd0d4 net.LookupIP may cause deadlock in windows
https://github.com/golang/go/issues/24178
2018-07-11 17:45:35 +08:00
arraykeys@gmail.com
dd355b5d98 fix socks client check port range 2018-07-10 16:26:26 +08:00
arraykeys@gmail.com
1e1999dede a 2018-07-10 13:37:00 +08:00
snail007
bb933cfb6e Merge pull request #108 from snail007/dev
docs
2018-07-09 20:33:31 +08:00
arraykeys
2603802dd9 no message 2018-07-09 20:18:30 +08:00
snail007
68ad904772 Merge pull request #106 from snail007/dev
docs
2018-07-09 17:27:49 +08:00
arraykeys@gmail.com
af19e5d25b docs 2018-07-09 17:25:58 +08:00
snail007
62907efe0c Merge pull request #105 from snail007/dev
v5.2
2018-07-09 17:24:16 +08:00
arraykeys@gmail.com
29e4cdf4e2 v5.2 2018-07-09 17:22:10 +08:00
arraykeys@gmail.com
3a4a9a3a27 a 2018-07-09 16:41:48 +08:00
arraykeys@gmail.com
04ef338807 add secure IP check for socks udp 2018-07-09 16:32:04 +08:00
arraykeys@gmail.com
3c070a7da3 Merge remote-tracking branch 'github_https/dev' into dev 2018-07-09 12:47:56 +08:00
snail007
c34f7db239 Merge pull request #104 from admpub/master
修复bridge服务bug
2018-07-09 00:21:36 +08:00
Wenhui Shen
1f655808c6 bugfixed 2018-07-08 19:52:46 +08:00
arraykeys
0226d2cde3 add sps : socks->udp support 2018-07-07 23:56:56 +08:00
arraykeys@gmail.com
61bb2d8ca0 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-07-06 18:24:55 +08:00
arraykeys@gmail.com
f5d09b878b fix socks udp reply wrong dst addr
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-07-06 18:24:12 +08:00
arraykeys
6b4ee97f05 no message 2018-07-05 20:18:12 +08:00
arraykeys@gmail.com
12dd591c58 add fully socks5 UDP support
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-07-05 15:56:26 +08:00
arraykeys
3b49f17e01 no message 2018-07-05 01:56:18 +08:00
arraykeys
cb8d0c0b42 no message 2018-07-05 01:03:37 +08:00
arraykeys
e92375f6a9 no message 2018-07-05 00:03:46 +08:00
arraykeys
c20e19d74f no message 2018-07-04 23:58:09 +08:00
arraykeys
50886bd69a no message 2018-07-04 21:51:59 +08:00
arraykeys
eaf836eff3 no message 2018-07-04 21:43:30 +08:00
arraykeys@gmail.com
bf72325fc0 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-07-04 17:44:24 +08:00
arraykeys
846956a9fe no message 2018-07-03 21:38:35 +08:00
arraykeys@gmail.com
20c31b0c68 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-07-03 17:37:17 +08:00
arraykeys@gmail.com
d84a4bec86 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-07-03 17:35:05 +08:00
arraykeys@gmail.com
05d2f16777 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-29 19:21:04 +08:00
arraykeys@gmail.com
9fa1c4932b Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-29 18:55:18 +08:00
arraykeys@gmail.com
0932aecff3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-29 18:54:24 +08:00
arraykeys@gmail.com
a9f8acd98d Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-06-29 17:56:04 +08:00
arraykeys@gmail.com
2336e77ac4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-29 17:55:19 +08:00
snail007
1ef8d7c82a Merge pull request #101 from snail007/dev
update docs
2018-06-29 09:13:28 +08:00
snail007
24d54725df Merge pull request #100 from yincongcyincong/dev
Update README.md
2018-06-29 09:12:23 +08:00
arraykeys
9b430b5ba4 fix create root certificate 2018-06-28 23:05:48 +08:00
yincongcyincong
0ee7abbd6a Update README.md 2018-06-28 18:07:48 +08:00
arraykeys@gmail.com
361215fb6f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-28 17:28:42 +08:00
arraykeys@gmail.com
7c0daad40a Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-28 16:10:04 +08:00
arraykeys@gmail.com
726240e669 v5.1
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-06-28 16:02:40 +08:00
arraykeys@gmail.com
1449b94dd4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-27 17:59:41 +08:00
arraykeys@gmail.com
ee9cd7b181 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-27 17:58:54 +08:00
arraykeys@gmail.com
ec0492b4b6 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-06-27 11:30:44 +08:00
arraykeys@gmail.com
7b4cb4df04 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-27 11:30:28 +08:00
arraykeys
7a31291c4c no message 2018-06-26 00:12:13 +08:00
arraykeys
16f1d80588 no message 2018-06-25 20:43:35 +08:00
arraykeys
7101530b22 no message 2018-06-22 21:19:09 +08:00
arraykeys@gmail.com
7f0bea952d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-21 14:24:53 +08:00
arraykeys@gmail.com
380c203222 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-21 14:09:42 +08:00
arraykeys@gmail.com
c4ac760603 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-19 16:12:51 +08:00
arraykeys@gmail.com
09158ebaa2 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-19 14:09:18 +08:00
arraykeys@gmail.com
4709ccfca4 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-06-19 13:35:33 +08:00
arraykeys@gmail.com
bea9ca25cf Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-19 13:34:38 +08:00
snail007
8ca1bfe09c Merge pull request #96 from yincongcyincong/dev
Update README.md
2018-06-18 11:50:15 +08:00
arraykeys
878b5e8c8a no message 2018-06-16 19:40:36 +08:00
arraykeys
1e9cfccd5a no message 2018-06-16 19:18:22 +08:00
arraykeys
995aa73bf2 no message 2018-06-16 19:06:24 +08:00
yincongcyincong
97012a0a19 Update README.md 2018-06-16 18:35:45 +08:00
arraykeys
006f2f9510 no message 2018-06-16 17:41:09 +08:00
arraykeys
e849cbfcd8 add secure dns proxy support 2018-06-16 17:14:43 +08:00
arraykeys
dff1a6daaf no message 2018-06-15 22:01:22 +08:00
arraykeys@gmail.com
250dc26514 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-14 14:55:12 +08:00
arraykeys@gmail.com
1b327d6abf Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-12 16:00:17 +08:00
arraykeys@gmail.com
fa11146246 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-12 13:21:02 +08:00
arraykeys@gmail.com
da7bfcf3be Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-12 13:19:52 +08:00
arraykeys@gmail.com
4214ec4239 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-06 15:06:56 +08:00
arraykeys@gmail.com
504de47999 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-06-06 14:44:04 +08:00
arraykeys@gmail.com
e185d734d0 mux内网穿透切换smux到yamux
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-05-31 16:26:40 +08:00
arraykeys@gmail.com
9b1ef52649 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-05-31 11:39:50 +08:00
arraykeys@gmail.com
5c9fc850d8 fix #84
fix #81

Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-05-31 11:39:29 +08:00
snail007
ff96e52a33 Merge pull request #89 from moonfruit/dev
HTTP Basic 认证失败返回的 WWW-Authenticate 更正为 Proxy-Authenticate
2018-05-24 11:22:35 +08:00
MoonFruit
78004bcd39 HTTP Basic 认证失败返回的 WWW-Authenticate 更正为 Proxy-Authenticate 2018-05-24 09:58:04 +08:00
arraykeys@gmail.com
b16decf976 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-23 14:38:19 +08:00
arraykeys@gmail.com
828636553d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-23 14:23:43 +08:00
arraykeys@gmail.com
bfcc27e70f fix #85
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-05-22 12:34:18 +08:00
arraykeys@gmail.com
7cb7d34d42 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-22 12:01:55 +08:00
arraykeys@gmail.com
5276154401 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-22 11:32:31 +08:00
arraykeys@gmail.com
dad091441e Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-22 11:30:19 +08:00
arraykeys@gmail.com
81ff3dadd5 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-22 11:26:34 +08:00
arraykeys@gmail.com
f559fb1cae fix #58
fix #80

Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-05-22 11:21:58 +08:00
arraykeys@gmail.com
8649bbc191 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-21 16:04:40 +08:00
snail007
d775339948 Merge pull request #83 from yelongyu/dev
Update README_ZH.md
2018-05-20 22:46:51 +08:00
yelongyu
69a5b906e0 Update README_ZH.md
删除多余的"域名"
2018-05-20 17:33:49 +08:00
yelongyu
2d66cc6215 Update README_ZH.md
添加多级加密HTTP代理设置注意事项
2018-05-20 17:26:46 +08:00
arraykeys@gmail.com
8d74baf48c Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-16 15:59:53 +08:00
arraykeys@gmail.com
d7641c4483 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-16 11:23:54 +08:00
arraykeys@gmail.com
ffe54c3af7 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-05-15 10:58:17 +08:00
arraykeys@gmail.com
53df3b5578 add docker support
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-05-15 10:56:57 +08:00
snail007
54c22f1410 Merge pull request #77 from FarhadF/dev
Added Dockerfile
2018-05-15 08:58:30 +08:00
FarhadF
366b7e04f3 Added Dockerfile 2018-05-14 17:33:00 +04:30
snail007
a33a4d2bd3 Merge pull request #74 from whgfu/dev
Dev-更新说明文件中socks5 http api认证中的 url参数为三个
2018-05-14 20:53:01 +08:00
粥冰涅槃
eb00d570a8 更新说明文件中socks5 http api认证中的 url参数为三个 2018-05-14 18:42:01 +08:00
whgfu
500142f4c8 Merge pull request #1 from snail007/dev
Dev
2018-05-14 18:31:11 +08:00
arraykeys
bffd5891cc no message 2018-05-08 08:45:29 +08:00
arraykeys
cf2e6f9ff0 no message 2018-05-08 07:28:05 +08:00
arraykeys
ed4b8d11e3 no message 2018-05-07 20:58:59 +08:00
arraykeys
905c1eac63 no message 2018-05-07 20:48:32 +08:00
arraykeys@gmail.com
61872133b1 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-07 17:46:23 +08:00
arraykeys@gmail.com
e18f53a5bb Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-07 17:32:38 +08:00
arraykeys@gmail.com
947fb51963 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-07 17:25:47 +08:00
arraykeys@gmail.com
7aeef3f8ba Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-07 17:14:20 +08:00
arraykeys@gmail.com
b42f6a6364 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-07 16:23:47 +08:00
arraykeys@gmail.com
92f4d31dfc Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-05-07 16:23:01 +08:00
arraykeys@gmail.com
4f11593f26 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-05-07 16:21:58 +08:00
arraykeys
795d63879f no message 2018-05-04 18:24:52 +08:00
arraykeys
1c46eeaf43 no message 2018-05-04 18:10:09 +08:00
arraykeys
6f47d12498 no message 2018-05-04 17:49:56 +08:00
arraykeys
e6c56675ca no message 2018-05-04 17:42:59 +08:00
snail007
ff92c96d8d Merge pull request #70 from yincongcyincong/dev
Update README.md
2018-05-03 23:15:19 +08:00
yincongcyincong
fed2afb964 Update README.md 2018-05-03 18:42:42 +08:00
arraykeys@gmail.com
b3feff7843 1.修复了多个服务同时开启日志,只会输出到最后一个日志文件的bug.
2.增加了获取sdk版本的Version()方法.

Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-05-03 17:59:06 +08:00
arraykeys@gmail.com
edb2fb3458 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-28 14:18:03 +08:00
arraykeys@gmail.com
dc51a0bd9d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-28 14:15:47 +08:00
arraykeys@gmail.com
34b30ac8c9 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-04-28 14:13:50 +08:00
arraykeys@gmail.com
8122af9096 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-28 14:13:20 +08:00
snail007
2b267fe4bb Merge pull request #66 from shoaly/patch-4
good job
2018-04-28 13:54:32 +08:00
shoaly
90bf483976 Update README_ZH.md 2018-04-28 13:45:41 +08:00
arraykeys@gmail.com
b109f273a5 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-27 16:28:14 +08:00
arraykeys@gmail.com
482977a4ac Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-27 15:27:13 +08:00
arraykeys@gmail.com
515dcdbf1f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-27 14:59:02 +08:00
arraykeys@gmail.com
0e033c1d85 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-27 12:22:37 +08:00
arraykeys@gmail.com
ad2441de3b Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-27 12:08:31 +08:00
arraykeys@gmail.com
7a881b3625 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-04-27 11:58:50 +08:00
arraykeys@gmail.com
faf61fdd60 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-27 11:58:27 +08:00
snail007
2b3d23f77c Merge pull request #63 from yincongcyincong/dev
Update README.md
2018-04-26 21:42:48 +08:00
yincongcyincong
06e1247706 Update README.md 2018-04-26 18:56:57 +08:00
arraykeys@gmail.com
6b5fdef8c7 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-26 17:43:53 +08:00
arraykeys@gmail.com
48b1621ee4 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-04-26 17:37:56 +08:00
snail007
c2ce973f61 Merge pull request #61 from onetwotrip/dev
Fix imports path
2018-04-26 17:37:35 +08:00
arraykeys@gmail.com
cac648cc32 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-26 17:37:03 +08:00
Maxim Pogozhiy
b9bffcdf4b Fix imports path 2018-04-25 11:28:20 +03:00
snail007
7a1e5c5de7 Merge pull request #62 from yincongcyincong/dev
en doc
2018-04-25 15:54:41 +08:00
yc
e24dfc856a Merge branch 'snail007-dev' into dev 2018-04-24 19:53:43 +08:00
yc
66a115c764 update readme 2018-04-24 19:53:27 +08:00
yincongcyincong
a6e80a30dc Update README.md 2018-04-24 19:36:30 +08:00
arraykeys@gmail.com
52a2771382 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-24 18:25:04 +08:00
arraykeys@gmail.com
6b2b75bc50 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-24 18:18:49 +08:00
arraykeys@gmail.com
893baff6c6 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev
# Conflicts:
#	sdk/windows-linux/release_linux.sh

Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-04-24 18:17:25 +08:00
arraykeys@gmail.com
de62d956dd Merge branch 'master' of https://github.com/snail007/goproxy.git into dev 2018-04-24 18:13:28 +08:00
arraykeys@gmail.com
b59cf1f144 增加了大量插图,优化了多链接版本内网穿透,实现了心跳机制.
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-04-24 18:09:33 +08:00
arraykeys
8b5cc3fb89 no message 2018-04-21 11:21:04 +08:00
arraykeys@gmail.com
fbd8c67649 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-20 13:35:05 +08:00
arraykeys
c6f6266592 no message 2018-04-20 13:31:58 +08:00
arraykeys
ab72640ffd Merge branch 'dev' of github.com:snail007/goproxy into dev
* 'dev' of github.com:snail007/goproxy:
  fix #56
  Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-04-20 13:23:31 +08:00
arraykeys
905bfff92b no message 2018-04-20 13:23:08 +08:00
arraykeys@gmail.com
4ef33d0ffd fix #56
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-04-19 14:55:56 +08:00
arraykeys@gmail.com
1597363dd1 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-18 15:27:32 +08:00
arraykeys
27896a0563 no message 2018-04-18 13:22:44 +08:00
arraykeys
bef385cfd1 no message 2018-04-18 13:11:43 +08:00
arraykeys@gmail.com
98e0154baa Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-18 11:19:51 +08:00
arraykeys@gmail.com
01961a798a Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-04-18 10:57:06 +08:00
arraykeys
d5a460bd09 no message 2018-04-18 10:55:54 +08:00
arraykeys@gmail.com
e94605644f Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-04-18 10:53:39 +08:00
arraykeys@gmail.com
8dc206e2d6 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-18 10:53:14 +08:00
arraykeys
e8acbbfabf mac sdk 2018-04-18 10:52:57 +08:00
arraykeys
17335eb92b no message 2018-04-17 22:25:10 +08:00
arraykeys
d2051e6e37 no message 2018-04-17 18:24:01 +08:00
arraykeys
0aa0e7c550 add windows sdk demo 2018-04-17 17:44:16 +08:00
arraykeys
57935c2296 no message 2018-04-17 13:57:52 +08:00
arraykeys@gmail.com
9d4930b29d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-17 13:45:31 +08:00
arraykeys@gmail.com
66206e63b3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-17 13:36:19 +08:00
arraykeys@gmail.com
19baccb91a add linux .so and windows .dll sdk support,
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-04-16 18:33:10 +08:00
yincongcyincong
98bbd68448 Update README.md 2018-04-14 11:37:09 +08:00
yincongcyincong
45dee58203 Update README.md 2018-04-14 11:30:29 +08:00
yincongcyincong
e2557c44c4 Update README.md 2018-04-14 10:29:21 +08:00
arraykeys@gmail.com
f0ed6d73e4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-13 18:31:12 +08:00
arraykeys@gmail.com
47790d1d58 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-13 18:23:26 +08:00
arraykeys@gmail.com
9aff5eda38 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-13 18:21:26 +08:00
arraykeys@gmail.com
eb2f055e07 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-13 18:14:32 +08:00
arraykeys@gmail.com
0998c06195 Add compress and encryt support on (tcp|tls|kcp) transport layer
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-04-13 17:23:42 +08:00
arraykeys@gmail.com
cf22866b2a Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-13 14:12:05 +08:00
arraykeys@gmail.com
16bb452640 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-12 17:03:16 +08:00
arraykeys@gmail.com
6f1d826ef5 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-12 16:45:39 +08:00
arraykeys@gmail.com
5a68fb3c3d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-12 16:42:28 +08:00
arraykeys@gmail.com
350eb5b6ed Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-12 16:41:03 +08:00
arraykeys@gmail.com
d48f3b3323 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-12 16:36:36 +08:00
arraykeys@gmail.com
7a1491c7b3 android sdk
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-04-12 16:32:56 +08:00
arraykeys@gmail.com
68deae6bf8 优化服务stop方法,方便sdk开发.
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-04-10 18:32:17 +08:00
arraykeys@gmail.com
2086966a89 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-09 16:21:27 +08:00
arraykeys@gmail.com
3aba428b76 增加gomobile sdk对android/ios进行支持.
优化错误控制

Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-04-09 16:16:55 +08:00
arraykeys@gmail.com
a11ce38747 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-04-03 17:34:33 +08:00
arraykeys@gmail.com
f7b363ec73 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-29 17:31:38 +08:00
arraykeys@gmail.com
4c33a1e9b2 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-29 17:25:32 +08:00
arraykeys@gmail.com
c4d9382ac7 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-29 16:59:52 +08:00
arraykeys@gmail.com
7df2d990e7 Merge branch 'dev' 2018-03-28 14:10:10 +08:00
snail007
900b75ddcd Merge pull request #48 from yincongcyincong/dev
Dev
2018-03-28 14:08:28 +08:00
yincongcyincong
a5d199fb1c Update README.md 2018-03-28 14:06:19 +08:00
yincongcyincong
2d8190873f Update README.md 2018-03-28 13:56:02 +08:00
yincongcyincong
3eff793ac2 Update README.md 2018-03-28 13:53:09 +08:00
arraykeys@gmail.com
27ce6e1bd2 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-28 13:34:08 +08:00
arraykeys@gmail.com
b11c7e632b fix #47
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-28 13:30:09 +08:00
arraykeys@gmail.com
d911be7d80 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-28 13:27:59 +08:00
arraykeys@gmail.com
bd056d74cc Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-28 13:25:47 +08:00
arraykeys@gmail.com
69b65a37ca Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-28 13:25:17 +08:00
arraykeys@gmail.com
dd52ad8a8a Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-28 13:22:37 +08:00
arraykeys@gmail.com
59b5ef2df4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-28 13:21:02 +08:00
arraykeys@gmail.com
4a34566f08 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-28 13:14:06 +08:00
arraykeys@gmail.com
d81d5ffe06 socks5 client server done
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-26 18:42:44 +08:00
arraykeys@gmail.com
59c9148875 optimize timeout of http(s)\socks\nat
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-21 18:17:20 +08:00
arraykeys@gmail.com
40bce3e736 optimize http basic auth
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-21 16:57:46 +08:00
arraykeys@gmail.com
d4c0775b4a Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-16 16:16:11 +08:00
arraykeys@gmail.com
7f983152b7 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-14 09:37:57 +08:00
arraykeys@gmail.com
bab4325414 server add safe close user conn
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-13 18:36:34 +08:00
arraykeys@gmail.com
0d85c7dd7d bridge add timeout
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-13 17:31:51 +08:00
arraykeys@gmail.com
f756d62b19 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-13 17:14:25 +08:00
arraykeys@gmail.com
2d1c1449aa Merge branch 'master' of https://github.com/snail007/goproxy.git 2018-03-13 10:29:50 +08:00
arraykeys@gmail.com
f348298acd v4.5 2018-03-13 10:29:26 +08:00
arraykeys@gmail.com
ad47104fc7 v4.5 2018-03-13 10:04:18 +08:00
snail007
4d4fb64b59 Update mux_bridge.go 2018-03-12 19:27:37 +08:00
arraykeys@gmail.com
9ce3a6e468 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-12 18:15:14 +08:00
arraykeys@gmail.com
34e9e362b9 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-12 17:31:35 +08:00
arraykeys@gmail.com
f87cbf73e8 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-03-12 13:59:47 +08:00
arraykeys@gmail.com
362ada2ebb v4.5
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-12 13:59:25 +08:00
snail007
b4b7221dab Merge pull request #44 from snail007/dev
Update README_ZH.md
2018-03-10 17:00:50 +08:00
snail007
2d8dc56f4e Update README_ZH.md 2018-03-10 17:00:03 +08:00
arraykeys@gmail.com
1cf4313d12 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-09 17:16:21 +08:00
arraykeys@gmail.com
7bb8f19b90 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-09 16:02:05 +08:00
arraykeys@gmail.com
1263a4e751 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-03-09 09:50:38 +08:00
arraykeys@gmail.com
1a432a9b79 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-09 09:50:21 +08:00
snail007
8951fdbd59 Merge pull request #42 from yincongcyincong/dev
Update README.md
2018-03-09 08:36:10 +08:00
yincongcyincong
77129367fe Update README.md 2018-03-08 20:50:36 +08:00
arraykeys@gmail.com
ae2e1e0933 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-03-08 18:43:07 +08:00
arraykeys@gmail.com
4143f14fbd optimise nat forwarding in different lan
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-08 18:42:50 +08:00
snail007
8e3e262c2f Merge pull request #41 from yincongcyincong/dev
Dev
2018-03-08 13:46:47 +08:00
arraykeys@gmail.com
6f11deab96 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-08 13:45:54 +08:00
arraykeys@gmail.com
a17acd7351 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-08 12:08:25 +08:00
arraykeys@gmail.com
1cbb4195e4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-08 12:08:02 +08:00
arraykeys@gmail.com
c471dd8297 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-08 12:07:44 +08:00
arraykeys@gmail.com
25deffb7d6 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-08 12:07:19 +08:00
arraykeys@gmail.com
7eb0e0040e Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-08 12:06:42 +08:00
arraykeys@gmail.com
15994988be Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-08 12:04:26 +08:00
arraykeys@gmail.com
ae293a6102 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-03-08 11:52:06 +08:00
arraykeys@gmail.com
5ed4702b62 add kcp config args
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-08 11:51:34 +08:00
yc
287ddc3424 Merge branch 'snail007-dev' into dev 2018-03-08 09:56:52 +08:00
yc
57a4227007 fix conflick 2018-03-08 09:56:21 +08:00
yc
c9eacd1bf2 Merge branch 'dev' of https://github.com/yincongcyincong/goproxy into dev 2018-03-08 09:50:59 +08:00
yincongcyincong
5f38162fbb Update README.md 2018-03-07 23:30:05 +08:00
snail007
c755f75a11 Update README_ZH.md 2018-03-07 23:09:08 +08:00
yincongcyincong
ac9eb64501 Update README.md 2018-03-07 13:34:16 +08:00
yincongcyincong
3dd013c13c Update README.md 2018-03-07 08:01:32 +08:00
yincongcyincong
ab0205587a Update README.md 2018-03-06 18:28:54 +08:00
arraykeys@gmail.com
70955878c9 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-06 18:11:13 +08:00
arraykeys@gmail.com
86f017d92f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-06 17:38:37 +08:00
arraykeys@gmail.com
446cc3f9a7 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-06 17:37:23 +08:00
arraykeys@gmail.com
6529921d71 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-06 17:24:28 +08:00
arraykeys@gmail.com
52e441c111 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-06 17:21:44 +08:00
arraykeys@gmail.com
a6b169d336 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-06 17:20:33 +08:00
arraykeys@gmail.com
5514cfee6c Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-06 17:19:34 +08:00
arraykeys@gmail.com
b1de184bda Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-06 17:17:09 +08:00
arraykeys@gmail.com
af2405ba48 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-06 17:15:34 +08:00
arraykeys@gmail.com
bee80330b0 sps done
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-06 17:13:43 +08:00
arraykeys@gmail.com
f1de8659b7 prepare for sps
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-05 18:46:19 +08:00
arraykeys@gmail.com
bfc5835d82 Merge branch 'master' of https://github.com/snail007/goproxy.git into dev 2018-03-05 11:22:33 +08:00
arraykeys@gmail.com
82bc3e27d6 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-05 11:22:15 +08:00
arraykeys@gmail.com
5436a95430 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-05 11:15:30 +08:00
arraykeys@gmail.com
2c675f2cbe Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-03-05 09:46:07 +08:00
arraykeys@gmail.com
8e9427b0c0 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-05 09:46:00 +08:00
arraykeys@gmail.com
edcf78f77c Merge branch 'master' into dev 2018-03-05 09:45:42 +08:00
snail007
8f88d14c07 Update README.md 2018-03-01 19:40:33 +08:00
snail007
1372801b6f Update README_ZH.md 2018-03-01 19:39:08 +08:00
arraykeys@gmail.com
2af904f442 fix dns Resolve
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-03-01 15:01:08 +08:00
arraykeys@gmail.com
885b27e0d1 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-03-01 13:55:53 +08:00
arraykeys@gmail.com
0207e4731f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-03-01 13:55:25 +08:00
arraykeys@gmail.com
038b6749a3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-02-28 17:55:18 +08:00
snail007
ead577cbfb Merge pull request #40 from yincongcyincong/dev
Dev
2018-02-28 16:28:10 +08:00
yincongcyincong
26e9231e48 Update README.md 2018-02-28 16:23:30 +08:00
yincongcyincong
938ddd1141 Update README.md 2018-02-28 16:15:29 +08:00
yincongcyincong
68080539f7 Update README.md 2018-02-28 16:14:16 +08:00
yincongcyincong
5583b303be Update README.md 2018-02-28 16:11:43 +08:00
yincongcyincong
9301c9b49b Update README.md 2018-02-28 16:07:40 +08:00
yincongcyincong
8d2e210522 Update README.md 2018-02-28 16:05:47 +08:00
yincongcyincong
32661552ff Update README.md 2018-02-28 14:09:23 +08:00
arraykeys@gmail.com
9a111a59bf Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-02-28 13:40:14 +08:00
arraykeys@gmail.com
f5e472ea9f 4.3
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-02-28 13:27:13 +08:00
arraykeys@gmail.com
7599e2c793 add socks dns support 2018-02-27 18:33:22 +08:00
arraykeys@gmail.com
3726f5b9c3 update vendor
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-02-27 10:33:46 +08:00
arraykeys@gmail.com
dee517217e add dns support
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-02-26 18:44:14 +08:00
arraykeys@gmail.com
983912e44e Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-02-26 10:33:12 +08:00
arraykeys@gmail.com
982390f4b2 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-02-26 10:28:01 +08:00
arraykeys@gmail.com
5e3f51a8b0 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-02-26 10:26:58 +08:00
yincongcyincong
bd594684ce Update README.md 2018-02-13 08:18:40 +08:00
586 changed files with 58126 additions and 7883 deletions

9
.gitignore vendored
View File

@ -1,7 +1,10 @@
proxy
/proxy
/goproxy
*.exe
*.exe~
.*
*.prof
!.gitignore
release-*
proxy.crt
proxy.key
/proxy.crt
/proxy.key

13
AUTHORIZATION.md Normal file
View File

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

137
CHANGELOG
View File

@ -1,4 +1,141 @@
proxy更新日志
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
1.优化了获取本地IP信息导致CPU过高的问题.
2.所有服务都增加了--nolog参数,可以关闭日志输出,节省CPU.
3.优化sdk,支持并发启动/关闭操作.
4.修复了多连接版本的内网穿透,tserver连接不能正确释放的bug.
5.内网穿透增加了client/tclient和server/tserver使用代理连接bridge/tbridge的功能,详细内容参考手册.
6.TCP端口映射(TCP代理)增加了使用代理连接上级的功能,详细内容参考手册.
v5.3
1.优化了socks_client握手端口判断,避免了sstap测试UDP失败的问题.
v5.2
1.修复了HTTP(S)\SPS反向代理无法正常工作的问题.
2.优化了智能判断,减少不必要的DNS解析.
3.重构了SOCKS和SPS的UDP功能,基于UDP的游戏加速嗖嗖的.
v5.1
1.优化了kcp默认mtu配置,调整为450.
2.优化了HTTP(S)\SOCKS5代理智能判断更加精确。
3.fix #97 , 修复了RemoveProxyHeaders方法忽略了第一行的bug。
4.修复了-g参数长格式没有连接符号的bug.
5.重构了证书生成功能,不再有任何外部依赖,任何平台都可以独立生成证书.
v5.0
1.修复了SPS多端口无效的bug.
2.增加了DNS代理功能提供安全无污染的DNS解析.
v4.9
1.修复了HTTP Basic代理返回不合适的头部,导致浏览器不会弹框,个别代理插件无法认证的问题.
2.内网穿透切换smux到yamux.
3.优化了HTTP(S)\SOCKS5代理--always的处理逻辑.
v4.8
1.优化了SPS连接HTTP上级的指令,避免了某些代理不响应的问题.
2.SPS功能增加了参数:
--disable-http:禁用http(s)代理
--disable-socks:禁用socks代理
默认都是false(开启).
3.重构了部分代码的日志部分,保证了日志按着预期输出.
4.修复了sps\http代理初始化服务的时机不正确,导致nil异常的bug.
5.优化了sps日志输出.
6.--debug参数增加了Profiling功能,可以保存cpu,内存等多种调试数据到文件.
7.优化了服务注册,避免了不必要的内存开销.
8.增加了Dockerfile和docker安装手册.
9.优化了ioCopy避免了内存泄漏,大大提升了内存占用的稳定性.
v4.7
1.增加了基于gomobile的sdk,对android/ios/windows/linux/mac提供SDK支持.
2.优化了bridge的日志,增加了client和server的掉线日志.
3.优化了sps读取http(s)代理响应的缓冲大小,同时优化了CONNECT请求,
避免了某些代理服务器返回过多数据导致不能正常通讯的问题.
4.去除了鸡肋连接池功能.
5.优化了所有服务代码,方便对sdk提供支持.
6.增加了SDK手册.
7.增加了GUI客户端(windows/web/android/ios)介绍主页.
8.SPS\HTTP(s)\Socks代理增加了自定义加密传输,只需要通过参数-z和-Z设置一个密码即可.
9.SPS\HTTP(s)\Socks代理增加了压缩传输,只需要通过参数-m和-M设置即可.
10.手册增加了SPS\HTTP(s)\Socks自定义加密的使用示例.
11.手册增加了SPS\HTTP(s)\Socks压缩传输的使用示例.
12.优化了多链接版本的内网穿透,融合了多链接和smux的优点,即能够拥有大的吞吐量,
同时又具备mux的心跳机制保证了链接的稳定性.
13.手册增加了大量配图.
14.优化了socks代理udp上级的设置逻辑,智能判断parent上级填充udp parent.
15.优化了项目文件夹结构,使用源码可以直接go get.
v4.6
1.sps,http(s),socks5,内网穿透都做了大量的超时优化处理,更加稳定.
2.sps增加了强大的树形级联认证支持,可以轻松构建你的认证代理网络.
3.手册增加了6.6对sps认证功能的介绍.
v4.5
1.优化了mux内网穿透连接管理逻辑,增强了稳定性.
2.mux内网穿透增加了tcp和kcp协议支持,之前是tls,现在支持三种协议tcp,tls,kcp.
3.keygen参数增加了用法: proxy keygen usage.
4.http(s)/socks5代理,tls增加了自签名证书支持.
5.建议升级.
v4.4
1.增加了协议转换sps功能代理协议转换使用的是sps子命令(socks+https的缩写)
sps本身不提供代理功能只是接受代理请求"转换并转发"给已经存在的http(s)代理
或者socks5代理sps可以把已经存在的http(s)代理或者socks5代理转换为一个端口
同时支持http(s)和socks5代理而且http(s)代理支持正向代理和反向代理(SNI),转
换后的SOCKS5代理不支持UDP功能另外对于已经存在的http(s)代理或者socks5代理
支持tls、tcp、kcp三种模式支持链式连接也就是可以多个sps结点层级连接构建
加密通道。
2.增加了对KCP传输参数的配置多达17个参数可以自由的配置对kcp传输效率调优。
3.内网穿透功能server和client增加了--session-count参数可以设置server每个
监听端口到bridge打开的session数量可以设置client到bridge打开的session数量
之前都是1个现在性能提升N倍N就是你自己设置的--session-count这个参数很大
程度上解决了多路复用的拥塞问题v4.4开始默认10个。
v4.3
1.优化了参数keygen生成证书逻辑避免证书出现特征。
2.http(s)和socks代理增加了--dns-address和--dns-ttl参数。
用于自己指定proxy访问域名的时候使用的dns--dns-address以及解析结果缓存时间--dns-ttl秒数
避免系统dns对proxy的干扰另外缓存功能还能减少dns解析时间提高访问速度。
3.优化了http代理的basic认证逻辑。
提示:
v4.3生成的证书不适用于v4.2及以下版本。
v4.2
1.优化了内网穿透,避免了client意外下线,导致链接信息残留的问题.
2.http代理增加了SNI支持,现在http(s)代理模式支持反向代理,支持http(s)透明代理.

82
Dockerfile Normal file
View File

@ -0,0 +1,82 @@
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}

133
Godeps/Godeps.json generated
View File

@ -1,133 +0,0 @@
{
"ImportPath": "proxy",
"GoVersion": "go1.8",
"GodepVersion": "v79",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "github.com/alecthomas/template",
"Rev": "a0175ee3bccc567396460bf5acd36800cb10c49c"
},
{
"ImportPath": "github.com/alecthomas/template/parse",
"Rev": "a0175ee3bccc567396460bf5acd36800cb10c49c"
},
{
"ImportPath": "github.com/alecthomas/units",
"Rev": "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
},
{
"ImportPath": "github.com/golang/snappy",
"Rev": "553a641470496b2327abcac10b36396bd98e45c9"
},
{
"ImportPath": "github.com/pkg/errors",
"Comment": "v0.8.0-6-g602255c",
"Rev": "602255cdb6deaf1523ea53ac30eae5554ba7bee9"
},
{
"ImportPath": "github.com/templexxx/cpufeat",
"Rev": "3794dfbfb04749f896b521032f69383f24c3687e"
},
{
"ImportPath": "github.com/templexxx/reedsolomon",
"Comment": "0.1.1-4-g7092926",
"Rev": "7092926d7d05c415fabb892b1464a03f8228ab80"
},
{
"ImportPath": "github.com/templexxx/xor",
"Comment": "0.1.2",
"Rev": "0af8e873c554da75f37f2049cdffda804533d44c"
},
{
"ImportPath": "github.com/tjfoc/gmsm/sm4",
"Comment": "v1.0.1-3-g9d99fac",
"Rev": "9d99face20b0dd300b7db50b3f69758de41c096a"
},
{
"ImportPath": "github.com/xtaci/kcp-go",
"Comment": "v3.19-6-g21da33a",
"Rev": "21da33a6696d67c1bffb3c954366499d613097a6"
},
{
"ImportPath": "github.com/xtaci/smux",
"Comment": "v1.0.6",
"Rev": "ebec7ef2574b42a7088cd7751176483e0a27d458"
},
{
"ImportPath": "golang.org/x/crypto/blowfish",
"Rev": "f899cbd3df85058aa20d1cf129473b18f2a2b49f"
},
{
"ImportPath": "golang.org/x/crypto/cast5",
"Rev": "86e16787bfd59cb4db9e278c51a95488c141a5d6"
},
{
"ImportPath": "golang.org/x/crypto/curve25519",
"Rev": "1843fabd21d7180cf65e36759986d00c13dbb0fd"
},
{
"ImportPath": "golang.org/x/crypto/ed25519",
"Rev": "1843fabd21d7180cf65e36759986d00c13dbb0fd"
},
{
"ImportPath": "golang.org/x/crypto/ed25519/internal/edwards25519",
"Rev": "1843fabd21d7180cf65e36759986d00c13dbb0fd"
},
{
"ImportPath": "golang.org/x/crypto/pbkdf2",
"Rev": "1843fabd21d7180cf65e36759986d00c13dbb0fd"
},
{
"ImportPath": "golang.org/x/crypto/salsa20",
"Rev": "1843fabd21d7180cf65e36759986d00c13dbb0fd"
},
{
"ImportPath": "golang.org/x/crypto/salsa20/salsa",
"Rev": "1843fabd21d7180cf65e36759986d00c13dbb0fd"
},
{
"ImportPath": "golang.org/x/crypto/ssh",
"Rev": "1843fabd21d7180cf65e36759986d00c13dbb0fd"
},
{
"ImportPath": "golang.org/x/crypto/tea",
"Rev": "1843fabd21d7180cf65e36759986d00c13dbb0fd"
},
{
"ImportPath": "golang.org/x/crypto/twofish",
"Rev": "1843fabd21d7180cf65e36759986d00c13dbb0fd"
},
{
"ImportPath": "golang.org/x/crypto/xtea",
"Rev": "1843fabd21d7180cf65e36759986d00c13dbb0fd"
},
{
"ImportPath": "golang.org/x/net/bpf",
"Rev": "114479435b31b5077a087cc5303a45cb5d355dc4"
},
{
"ImportPath": "golang.org/x/net/internal/iana",
"Rev": "114479435b31b5077a087cc5303a45cb5d355dc4"
},
{
"ImportPath": "golang.org/x/net/internal/socket",
"Rev": "114479435b31b5077a087cc5303a45cb5d355dc4"
},
{
"ImportPath": "golang.org/x/net/ipv4",
"Rev": "114479435b31b5077a087cc5303a45cb5d355dc4"
},
{
"ImportPath": "golang.org/x/time/rate",
"Rev": "8be79e1e0910c292df4e79c241bb7e8f7e725959"
},
{
"ImportPath": "gopkg.in/alecthomas/kingpin.v2",
"Comment": "v2.2.5",
"Rev": "1087e65c9441605df944fb12c33f0fe7072d18ca"
}
]
}

5
Godeps/Readme generated
View File

@ -1,5 +0,0 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

139
Gopkg.lock generated Normal file
View File

@ -0,0 +1,139 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/alecthomas/template"
packages = [
".",
"parse"
]
revision = "a0175ee3bccc567396460bf5acd36800cb10c49c"
[[projects]]
branch = "master"
name = "github.com/alecthomas/units"
packages = ["."]
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
[[projects]]
branch = "master"
name = "github.com/golang/snappy"
packages = ["."]
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
[[projects]]
branch = "master"
name = "github.com/hashicorp/yamux"
packages = ["."]
revision = "3520598351bb3500a49ae9563f5539666ae0a27c"
[[projects]]
name = "github.com/klauspost/cpuid"
packages = ["."]
revision = "ae7887de9fa5d2db4eaa8174a7eff2c1ac00f2da"
version = "v1.1"
[[projects]]
name = "github.com/klauspost/reedsolomon"
packages = ["."]
revision = "6bb6130ff6a76a904c1841707d65603aec9cc288"
version = "v1.6"
[[projects]]
name = "github.com/miekg/dns"
packages = ["."]
revision = "5a2b9fab83ff0f8bfc99684bd5f43a37abe560f1"
version = "v1.0.8"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/pmylund/go-cache"
packages = ["."]
revision = "a3647f8e31d79543b2d0f0ae2fe5c379d72cedc0"
version = "v2.1.0"
[[projects]]
branch = "master"
name = "github.com/templexxx/cpufeat"
packages = ["."]
revision = "3794dfbfb04749f896b521032f69383f24c3687e"
[[projects]]
name = "github.com/templexxx/xor"
packages = ["."]
revision = "0af8e873c554da75f37f2049cdffda804533d44c"
version = "0.1.2"
[[projects]]
name = "github.com/tjfoc/gmsm"
packages = ["sm4"]
revision = "98aa888b79d8de04afe0fccf45ed10594efc858b"
version = "v1.1"
[[projects]]
name = "github.com/xtaci/kcp-go"
packages = ["."]
revision = "42bc1dfefff592fdb3affa793980c4f6ab4213e5"
version = "v3.25"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = [
"blowfish",
"cast5",
"curve25519",
"ed25519",
"ed25519/internal/edwards25519",
"internal/chacha20",
"internal/subtle",
"pbkdf2",
"poly1305",
"salsa20",
"salsa20/salsa",
"ssh",
"tea",
"twofish",
"xtea"
]
revision = "a8fb68e7206f8c78be19b432c58eb52a6aa34462"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"bpf",
"context",
"internal/iana",
"internal/socket",
"internal/socks",
"ipv4",
"ipv6",
"proxy"
]
revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196"
[[projects]]
branch = "master"
name = "golang.org/x/time"
packages = ["rate"]
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
[[projects]]
name = "gopkg.in/alecthomas/kingpin.v2"
packages = ["."]
revision = "947dcec5ba9c011838740e680966fd7087a71d0d"
version = "v2.2.6"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "eab0ef29432489696d8bfd524757dcb03a83f91c329f2d05c36da70df850360d"
solver-name = "gps-cdcl"
solver-version = 1

66
Gopkg.toml Normal file
View File

@ -0,0 +1,66 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/golang/snappy"
[[constraint]]
branch = "master"
name = "github.com/hashicorp/yamux"
[[constraint]]
name = "github.com/miekg/dns"
version = "1.0.8"
[[constraint]]
name = "github.com/pmylund/go-cache"
version = "2.1.0"
[[constraint]]
name = "github.com/xtaci/kcp-go"
version = "3.25.0"
[[constraint]]
branch = "master"
name = "golang.org/x/crypto"
[[constraint]]
branch = "master"
name = "golang.org/x/net"
[[constraint]]
branch = "master"
name = "golang.org/x/time"
[[constraint]]
name = "gopkg.in/alecthomas/kingpin.v2"
version = "2.2.6"
[prune]
go-tests = true
unused-packages = true

676
README.md
View File

@ -1,11 +1,25 @@
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/logo.jpg?raw=true" width="200"/>
Proxy is a high performance HTTP(S), websocket, TCP, UDP, Socks5 proxy server implemented by golang. It supports parent proxy,nat forward,TCP/UDP port forwarding, SSH transfer. you can expose a local server behind a NAT or firewall to the internet.  
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.  
---
[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy/) [![license](https://img.shields.io/github/license/snail007/goproxy.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy/total.svg?style=plastic)](https://github.com/snail007/goproxy/releases) [![download](https://img.shields.io/github/release/snail007/goproxy.svg?style=plastic)](https://github.com/snail007/goproxy/releases)
[中文手册](/README_ZH.md)
**[中文手册](/README_ZH.md)**
**[全平台图形界面版本](/gui/README.md)**
**[全平台SDK](/sdk/README.md)**
**[GoProxy特殊授权](/AUTHORIZATION.md)**
### How to contribute to the code (Pull Request)?
Pull Request is welcomed.
First, you need to clone the project to your account, and then modify the code on the dev branch.
Finally, Pull Request to dev branch of goproxy project, and contribute code for efficiency.
PR needs to explain what changes have been made and why you change them.
### Features
- chain-style proxy: the program itself can be a primary proxy, and if a parent proxy is set, it can be used as a second level proxy or even a N level proxy.
@ -21,6 +35,15 @@ Proxy is a high performance HTTP(S), websocket, TCP, UDP, Socks5 proxy server im
- 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.
- 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 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.
- 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.
- 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?
- 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.  
@ -32,26 +55,18 @@ Proxy is a high performance HTTP(S), websocket, TCP, UDP, Socks5 proxy server im
- ...  
This page is the v4.2 manual, and the other version of the manual can be checked by the following link.
- [v4.0-4.1 manual](https://github.com/snail007/goproxy/tree/v4.1)
- [v3.9 manual](https://github.com/snail007/goproxy/tree/v3.9)
- [v3.8 manual](https://github.com/snail007/goproxy/tree/v3.8)
- [v3.6-v3.7 manual](https://github.com/snail007/goproxy/tree/v3.6)
- [v3.5 manual](https://github.com/snail007/goproxy/tree/v3.5)
- [v3.4 manual](https://github.com/snail007/goproxy/tree/v3.4)
- [v3.3 manual](https://github.com/snail007/goproxy/tree/v3.3)
- [v3.2 manual](https://github.com/snail007/goproxy/tree/v3.2)
- [v3.1 manual](https://github.com/snail007/goproxy/tree/v3.1)
- [v3.0 manual](https://github.com/snail007/goproxy/tree/v3.0)
- [v2.x manual](https://github.com/snail007/goproxy/tree/v2.2)
This page is the 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?
[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 telegram](https://t.me/joinchat/GYHXghCDSBmkKZrvu4wIdQ)  
### Installation
- [Quick installation](#quick-installation)
- [Manual installation](#manual-installation)
- [Docker installation](#docker-installation)
### First use must read
- [Environmental Science](#environmental-science)
@ -64,6 +79,7 @@ This page is the v4.2 manual, and the other version of the manual can be checked
- [Safety advice](#safety-advice)
### Manual catalogues
- [Load balance and high available](#load-balance-and-high-available)
- [1.HTTP proxy](#1http-proxy)
- [1.1 Common HTTP proxy](#11common-http-proxy)
- [1.2 Common HTTP second level proxy](#12common-http-second-level-proxy)
@ -77,14 +93,22 @@ This page is the v4.2 manual, and the other version of the manual can be checked
- [1.8 KCP protocol transmission](#18kcp-protocol-transmission)
- [1.9 HTTP(S) reverse proxy](#19http-reverse-proxy)
- [1.10 HTTP(S) transparent proxy](#110http-transparent-proxy)
- [1.11 View help](#111view-help)
- [1.11 Custom DNS](#111custom-dns)
- [1.12 Custom encryption](#112-custom-encryption)
- [1.13 Compressed transmission](#113-compressed-transmission)
- [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 View help](#118view-help)
- [2.TCP proxy](#2tcp-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.3 Common TCP third level proxy](#23common-tcp-third-level-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.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.1 Common UDP first level proxy](#31common-udp-first-level-proxy)
- [3.2 Common UDP second level proxy](#32common-udp-second-level-proxy)
@ -100,7 +124,8 @@ This page is the v4.2 manual, and the other version of the manual can be checked
- [4.5 Advanced usage 1](#45advanced-usage-1)
- [4.6 Advanced usage 2](#46advanced-usage-2)
- [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.1 Common SOCKS5 proxy](#51common-socks5-proxy)
- [5.2 Common SOCKS5 second level proxy](#52common-socks5-second-level-proxy)
@ -112,7 +137,38 @@ This page is the v4.2 manual, and the other version of the manual can be checked
- [5.6.2 The way of username and key](#562the-way-of-username-and-key)
- [5.7 Authentication](#57authentication)
- [5.8 KCP protocol transmission](#58kcp-protocol-transmission)
- [5.9 View help](#59view-help)
- [5.9 Custom DNS](#59custom-dns)
- [5.10 Custom encryption](#510custom-encryption)
- [5.11 Compressed transmission](#511compressed-transmission)
- [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 View help](#517view-help)
- [6.Proxy protocol conversion](#6proxy-protocol-conversion)
- [6.1 Functional introduction](#61functional-introduction)
- [6.2 HTTP(S) to HTTP(S) + SOCKS5](#62http-to-http-socks5)
- [6.3 SOCKS5 to HTTP(S) + SOCKS5](#63socks5-to-http-socks5)
- [6.4 SS to HTTP(S)+SOCKS5+SS](#64-ss-to-httpssocks5ss)
- [6.5 Chain style connection](#65chain-style-connection)
- [6.6 Listening on multiple ports](#66listening-on-multiple-ports)
- [6.7 Authentication](#67authentication)
- [6.8 Custom encryption](#68-custom-encryption)
- [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.1 Configuration introduction](#71configuration-introduction)
- [7.2 Configuration details](#72configuration-details)
- [8.DNS anti pollution server](#8dns-anti-pollution-server)
- [8.1 Introduction](#81introduction)
- [8.2 Use examples](#82use-examples)
### Fast Start
tips:all operations require root permissions.
@ -121,7 +177,7 @@ tips:all operations require root permissions.
```shell
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:
#### Manual installation
@ -130,7 +186,8 @@ If the installation fails or your VPS is not a linux64 system, please follow the
Download address: https://github.com/snail007/goproxy/releases
```shell
cd /root/proxy/
wget https://github.com/snail007/goproxy/releases/download/v4.0/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**
```shell
@ -140,6 +197,36 @@ chmod +x install.sh
./install.sh
```
#### Docker installation
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 6.0:
```
ARG GOPROXY_VERSION=v6.0
```
To Run:
1. Clone the repository and cd into it.
```
sudo docker build .
```
2. Tag the image:
```
sudo docker tag <id from previous step> snail007/goproxy:latest
```
3. Run!
Just put your arguments to proxy binary in the OPTS environmental variable (this is just a sample http proxy):
```
sudo docker run -d --restart=always --name goproxy -e OPTS="http -p :33080" -p 33080:33080 snail007/goproxy:latest
```
4. View logs:
```
sudo docker logs -f goproxy
```
## **First use must be read**  
### **Environmental Science**
@ -166,9 +253,18 @@ for example, --log proxy.log, The log will be exported to proxy.log file which i
### **Generating a communication certificate file**
HTTP, TCP, UDP proxy process will communicate with parent proxy. In order to secure, we use encrypted communication. Of course, we can choose not to encrypted communication. All communication with parent proxy in this tutorial is encrypted, requiring certificate files.
The OpenSSL command is installed on the Linux and encrypted certificate can be generated directly through the following command.
`./proxy keygen`
By default, the certificate file proxy.crt and the key file proxy.key are generated under the current program directory.
1.Generate signed certificates and key files through the following commands.
`./proxy keygen -C proxy`
The certificate file proxy.crt and key file proxy.key will be generated under the current directory.
2.Through the following commands, use the signed certificate proxy.crt and key file proxy.key to issue new certificates: goproxy.crt and goproxy.key.
`./proxy keygen -s -C proxy -c goproxy`
The certificate file goproxy.crt and key file goproxy.key will be generated under the current program directory.
3.By default, the domain name in the certificate is a random domain and can be specified using the `-n test.com` parameter.
4.More usage:`proxy keygen --help`
### **Daemon mode**
After the default execution of proxy, if you want to keep proxy running, you can't close the command line.
@ -187,12 +283,27 @@ 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.
`./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.1.common HTTP proxy**
![1.1](/docs/images/1.1.jpg)
![1.1](/docs/images/http-1.png)
`./proxy http -t tcp -p "0.0.0.0:38080"`
#### **1.2.Common HTTP second level proxy**
![1.2](/docs/images/http-2.png)
Using local port 8090, assume the parent HTTP proxy is: `22.22.22.22:8080`
`./proxy http -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" `
The connection pool is closed by default. If you want to speed up access speed, -L can open the connection pool, the 10 is the size of the connection pool, and the 0 is closed.
@ -202,6 +313,7 @@ We can also specify the black and white list files of the domain name, one line
`./proxy http -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt`
#### **1.3.HTTP second level encrypted proxy**
![1.3](/docs/images/http-tls-2.png)
HTTP first level proxy(VPS,IP:22.22.22.22)
`./proxy http -t tls -p ":38080" -C proxy.crt -K proxy.key`
@ -214,6 +326,7 @@ HTTP second level proxy(local windows)
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.
#### **1.4.HTTP third level encrypted proxy**
![1.4](/docs/images/http-tls-3.png)
HTTP first level proxy VPS_01,IP:22.22.22.22
`./proxy http -t tls -p ":38080" -C proxy.crt -K proxy.key`
HTTP second level proxy VPS_02,IP:33.33.33.33
@ -250,6 +363,7 @@ Through --always, all HTTP proxy traffic can be coercion to the parent HTTP prox
`./proxy http --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
#### **1.7.Transfer through SSH**
![1.7](/docs/images/http-ssh-1.png)
Explanation: the principle of SSH transfer is to take advantage of SSH's forwarding function, which is, after you connect to SSH, you can access to the target address through the SSH proxy.
Suppose there is a vps
- IP is 2.2.2.2, ssh port is 22, ssh username is user, ssh password is demo
@ -263,15 +377,17 @@ Local HTTP (S) proxy use 28080 port,excute:
`./proxy http -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"`
#### **1.8.KCP protocol transmission**
The KCP protocol requires a -B parameter to set a password which can encrypt and decrypt data.
![1.8](/docs/images/http-kcp.png)
The KCP protocol requires a --kcp-key parameter to set a password which can encrypt and decrypt data.
Http first level proxy(VPS,IP:22.22.22.22)
`./proxy http -t kcp -p ":38080" -B mypassword`
`./proxy http -t kcp -p ":38080" --kcp-key mypassword`
Http second level proxy(os is Linux)
`./proxy http -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" -B mypassword`
`./proxy http -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" --kcp-key mypassword`
Then access to the local 8080 port is access to the proxy's port 38080 on the VPS, and the data is transmitted through the KCP protocol.
#### **1.9.HTTP reverse proxy**
![1.9](/docs/images/fxdl.png)
Proxy supports not only set up a proxy through in other software, to provide services for other software, but support the request directly to the website domain to proxy monitor IP when proxy monitors 80 and 443 ports, then proxy will automatically access to the HTTP proxy access website for you.  
How to use:  
@ -331,19 +447,92 @@ iptables -t nat -A OUTPUT -p tcp -j PROXY
- Deleting the specified chain that user defined command is iptables -X chain name, such as iptables -t NAT -X PROXY
- Deleting the rules of the chain command is iptables -D chain name from the selected chain, such as iptables -t nat -D PROXY -d 223.223.192.0/255.255.240.0 -j RETURN
#### **1.11.view help**
#### **1.11.Custom DNS**
--dns-address and --dns-ttl parameters can be used to specify DNS--dns-address when you use proxy to access to a domain.
they also can specify dns result cache time (--dns-ttl) which unit is second. they can avoid the interference of system DNS to proxy. cache can reduce DNS resolution time and increase access speed.
for example:
`./proxy http -p ":33080" --dns-address "8.8.8.8:53" --dns-ttl 300`
#### **1.12 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.
Custom encryption requires both ends are proxy. Next, we use two level example and three level example as examples:
**two level example**
First level VPS (ip:2.2.2.2) execution:
`proxy http -t tcp -z demo_password -p :7777`
Local second level execution:
`proxy http -T tcp -P 2.2.2.2:777 -Z demo_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.
**three level example**
First level VPS (ip:2.2.2.2) execution:
`proxy http -t tcp -z demo_password -p :7777`
Second level VPS (ip:2.2.2.2) execution:
`proxy http -T tcp -P 2.2.2.2:7777 -Z demo_password -t tcp -z other_password -p :8888`
Local third level execution:
`proxy http -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.
#### **1.13 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.
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:
**two level example**
First level VPS (ip:2.2.2.2) execution:
`proxy http -t tcp -m -p :7777`
Local second level execution:
`proxy http -T tcp -P 2.2.2.2:777 -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.  
**three level example**
First level VPS (ip:2.2.2.2) execution:
`proxy http -t tcp -m -p :7777`
Second level VPS (ip:3.3.3.3) execution:
`proxy http -T tcp -P 2.2.2.2:7777 -M -t tcp -m -p :8888`
Local third level execution:
`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.
### **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 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 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.view help**
`./proxy help http`
### **2.TCP proxy**
#### **2.1.Common TCP first level proxy**
![2.1](/docs/images/2.1.png)
![2.1](/docs/images/tcp-1.png)
Local execution:
`./proxy tcp -p ":33080" -T tcp -P "192.168.22.33:22" -L 0`
Then access to the local 33080 port is the 22 port of access to 192.168.22.33.
#### **2.2.Common TCP second level proxy**
![2.2](/docs/images/2.2.png)
![2.2](/docs/images/tcp-2.png)
VPS(IP:22.22.22.33) execute:  
`./proxy tcp -p ":33080" -T tcp -P "127.0.0.1:8080" -L 0`  
local execution:  
@ -351,6 +540,7 @@ local execution:  
Then access to the local 23080 port is the 8080 port of access to 22.22.22.33.
#### **2.3.Common TCP third level proxy**
![2.3](/docs/images/tcp-3.png)
TCP first level proxy VPS_01,IP:22.22.22.22
`./proxy tcp -p ":38080" -T tcp -P "66.66.66.66:8080" -L 0`
TCP second level proxy VPS_02,IP:33.33.33.33
@ -360,6 +550,7 @@ TCP third level proxy (local)
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.4.TCP second level encrypted proxy**
![2.4](/docs/images/tcp-tls-2.png)
VPS(IP:22.22.22.33) execute:
`./proxy tcp --tls -p ":33080" -T tcp -P "127.0.0.1:8080" -L 0 -C proxy.crt -K proxy.key`
local execution:
@ -367,6 +558,7 @@ local execution:
Then access to the local 23080 port is to access the 8080 port of the 22.22.22.33 by encrypting the TCP tunnel.
#### **2.5.TCP third level encrypted proxy**
![2.5](/docs/images/tcp-tls-3.png)
TCP first level proxy VPS_01,IP:22.22.22.22
`./proxy tcp --tls -p ":38080" -T tcp -P "66.66.66.66:8080" -C proxy.crt -K proxy.key`
TCP second level proxy VPS_02,IP:33.33.33.33
@ -375,17 +567,38 @@ TCP third level proxy (local)
`./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.
#### **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`
### **3.UDP proxy**
#### **3.1.Common UDP first level proxy**
![3.1](/docs/images/udp-1.png)
local execution:
`./proxy udp -p ":5353" -T udp -P "8.8.8.8:53"`
Then access to the local UDP:5353 port is access to the UDP:53 port of the 8.8.8.8.
#### **3.2.Common UDP second level proxy**
![3.2](/docs/images/udp-2.png)
VPS(IP:22.22.22.33) execute:
`./proxy tcp -p ":33080" -T udp -P "8.8.8.8:53"`
local execution:
@ -393,6 +606,7 @@ local execution:
Then access to the local UDP:5353 port is access to the UDP:53 port of the 8.8.8.8 through the TCP tunnel.
#### **3.3.Common UDP third level proxy**
![3.3](/docs/images/udp-3.png)
TCP first level proxy VPS_01,IP:22.22.22.22
`./proxy tcp -p ":38080" -T udp -P "8.8.8.8:53"`
TCP second level proxy VPS_02,IP:33.33.33.33
@ -402,6 +616,7 @@ TCP third level proxy (local)
Then access to the local 5353 port is access to the 53 port of the 8.8.8.8 through the TCP tunnel.
#### **3.4.UDP second level encrypted proxy**
![3.4](/docs/images/udp-tls-2.png)
VPS(IP:22.22.22.33) execute:
`./proxy tcp --tls -p ":33080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key`
local execution:
@ -409,6 +624,7 @@ local execution:
Then access to the local UDP:5353 port is access to the UDP:53 port of the 8.8.8.8 by the encrypting TCP tunnel.
#### **3.5.UDP third level encrypted proxy**
![3.5](/docs/images/udp-tls-3.png)
TCP first level proxy VPS_01,IP:22.22.22.22
`./proxy tcp --tls -p ":38080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key`
TCP second level proxy VPS_02,IP:33.33.33.33
@ -554,10 +770,29 @@ 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.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 server`
`./proxy help server`
`./proxy help client`
### **5.SOCKS5 proxy**
Tips: SOCKS5 proxy, support CONNECT, UDP protocol and don't support BIND and support username password authentication.
@ -565,6 +800,7 @@ Tips: SOCKS5 proxy, support CONNECT, UDP protocol and don't support BIND and sup
`./proxy socks -t tcp -p "0.0.0.0:38080"`
#### **5.2.Common SOCKS5 second level proxy**
![5.2](/docs/images/socks-2.png)
![5.2](/docs/images/5.2.png)
Using local port 8090, assume that the parent SOCKS5 proxy is `22.22.22.22:8080`
`./proxy socks -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" `
@ -572,6 +808,7 @@ We can also specify the black and white list files of the domain name, one line
`./proxy socks -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt`
#### **5.3.SOCKS second level encrypted proxy**
![5.3](/docs/images/socks-tls-2.png)
SOCKS5 first level proxy(VPS,IP:22.22.22.22)
`./proxy socks -t tls -p ":38080" -C proxy.crt -K proxy.key`
@ -584,6 +821,7 @@ SOCKS5 second level proxy(local windows)
Then set up your windows system, the proxy that needs to surf the Internet by proxy is Socks5 mode, the address is: 127.0.0.1, the port is: 8080. the program can surf the Internet through the encrypted channel which is running on VPS.  
#### **5.4.SOCKS third level encrypted proxy**
![5.4](/docs/images/socks-tls-3.png)
SOCKS5 first level proxy VPS_01,IP:22.22.22.22
`./proxy socks -t tls -p ":38080" -C proxy.crt -K proxy.key`
SOCKS5 second level proxy VPS_02,IP:33.33.33.33
@ -597,6 +835,7 @@ By default, proxy will intelligently judge whether a domain name can be accessed
`./proxy socks --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
#### **5.6.Transfer through SSH**
![5.6](/docs/images/socks-ssh.png)
Explanation: the principle of SSH transfer is to take advantage of SSH's forwarding function, which is, after you connect to SSH, you can access the target address by the SSH.  
Suppose there is a vps
- IP is 2.2.2.2, SSH port is 22, SSH username is user, SSH password is Demo
@ -619,7 +858,7 @@ You can also be placed in a file, which is a line, a username: password, a
`./proxy socks -t tcp -p ":33080" -F auth-file.txt`
In addition, socks5 proxy also integrates external HTTP API authentication, we can specify a http url interface address through the --auth-url parameter,
Then when the user is connected, the proxy request this url by get way, with the following four parameters, if the return HTTP status code 204, on behalf of the authentication is successful.
Then when the user is connected, the proxy request this url by get way, with the following three parameters, if the return HTTP status code 204, on behalf of the authentication is successful.
In other cases, the authentication fails.
for example:
`./proxy socks -t tcp -p ":33080" --auth-url "http://test.com/auth.php"`
@ -633,25 +872,380 @@ ip: user's IP, for example: 192.168.1.200
If there is no -a or -F or --auth-url parameters, it means to turn off the authentication.
#### **5.8.KCP protocol transmission**
The KCP protocol requires a -B parameter which can set a password to encrypt and decrypt data.  
The KCP protocol requires a --kcp-key parameter which can set a password to encrypt and decrypt data.  
HTTP first level proxy(VPS,IP:22.22.22.22)
`./proxy socks -t kcp -p ":38080" -B mypassword`
`./proxy socks -t kcp -p ":38080" --kcp-key mypassword`
HTTP two level proxy(local os is Linux)
`./proxy socks -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" -B mypassword`
`./proxy socks -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" --kcp-key mypassword`
Then access to the local 8080 port is access to the proxy port 38080 on the VPS, and the data is transmitted through the KCP protocol.
#### **5.9.view help**
#### **5.9.Custom DNS**
--dns-address and --dns-ttl parameters can be used to specify DNS--dns-address when you use proxy to access to a domain.
they also can specify dns result cache time (--dns-ttl) which unit is second. they can avoid the interference of system DNS to proxy. cache can reduce DNS resolution time and increase access speed.
for example:
`./proxy socks -p ":33080" --dns-address "8.8.8.8:53" --dns-ttl 300`
#### **5.10.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.
Custom encryption requires both ends are proxy. Next, we use two level example and three level example as examples:
**two level example**
First level VPS (ip:2.2.2.2) execution:
`proxy socks -t tcp -z demo_password -p :7777`
Local second level execution:
`proxy socks -T tcp -P 2.2.2.2:777 -Z demo_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.
**three level example**
First level VPS (ip:2.2.2.2) execution:
`proxy socks -t tcp -z demo_password -p :7777`
Second level VPS (ip:2.2.2.2) execution:
`proxy socks -T tcp -P 2.2.2.2:7777 -Z demo_password -t tcp -z other_password -p :8888`
Local third level execution:
`proxy socks -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.
#### **5.11.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.
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:
**two level example**
First level VPS (ip:2.2.2.2) execution:
`proxy socks -t tcp -m -p :7777`
Local second level execution:
`proxy socks -T tcp -P 2.2.2.2:777 -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.
**three level example**
First level VPS (ip:2.2.2.2) execution:
`proxy socks -t tcp -m -p :7777`
Second level VPS (ip:3.3.3.3) execution:
`proxy socks -T tcp -P 2.2.2.2:7777 -M -t tcp -m -p :8888`
Local third level execution:
`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.
#### **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.view help**
`./proxy help socks`
### **6.Proxy protocol conversion**
#### **6.1.Functional introduction**
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**
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
`./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), 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
`./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), 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
`./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**
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
`./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 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
`./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 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
`./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 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)
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
vps023.3.3.3
Now we want to use PC and vps01 and vps02 to build an encrypted channel. In this example, TLS is used. KCP also supports encryption in addition to TLS. and accessing to local 18080 port on PC is accessing to the local 8080 ports of vps01.
First, on vps01 (2.2.2.2), we run a HTTP (s) proxy that only can be accessed locally,excute
`./proxy -t tcp -p 127.0.0.1:8080`
Then run a SPS node on vps01 (2.2.2.2)excute
`./proxy -S http -T tcp -P 127.0.0.1:8080 -t tls -p :8081 -C proxy.crt -K proxy.key`
Then run a SPS node on vps02 (3.3.3.3)excute
`./proxy -S http -T tls -P 2.2.2.2:8081 -t tls -p :8082 -C proxy.crt -K proxy.key`
Then run a SPS node on the PCexcute
`./proxy -S http -T tls -P 3.3.3.3:8082 -t tcp -p :18080 -C proxy.crt -K proxy.key`
finish。
#### **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.
The format is`-p 0.0.0.0:80,0.0.0.0:443`, Multiple bindings are separated by a comma.
#### **6.7.Authentication**
SPS supports HTTP(s)\socks5 proxy authentication, which can concatenate authentication, there are four important information:
1:Users send authentication information`user-auth`
2:Local authentication information set up`local-auth`
3:Set the authentication information accessing to the father proxy`parent-auth`
4:The final authentication information sent to the father proxy`auth-info-to-parent`
The relationship between them is as follows:
| user-auth | local-auth | parent-auth | auth-info-to-paren
| ------ | ------ | ------ | ------
| yes/no | yes    | yes   | come from parent-auth  
| yes/no | no | yes | come from parent-auth
| yes/no | yes | no | no
| no | no | no | no
| yes | no | no | come from user-auth
For SPS proxy we can have username and password to authenticate, and the authentication username and password can be specified on the command line
`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p ":33080" -a "user1:pass1" -a "user2:pass2"`
if there are multiple users, repeat the -a parameters.
It can also be placed in a file, which is a line to a username: password, and then specified in -F parameter.
`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p ":33080" -F auth-file.txt`
If the father proxy is authenticated, the lower level can set the authentication information through the -A parameters, such as:
father proxy:`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p ":33080" -a "user1:pass1" -a "user2:pass2"`
local proxy:`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -A "user1:pass1" -t tcp -p ":33080" `
In addition, SPS proxy, local authentication is integrated with external HTTP API authentication, and we can specify a HTTP URL interface address through the --auth-url parameter,
Then, when there is a user connection, proxy will request this URL by GET way, with the following four parameters, and if the HTTP state code 204 is returned, the authentication is successful.
Other cases consider authentication failure.
for example:
`./proxy sps -S http -T tcp -P 127.0.0.1:8080 -t tcp -p ":33080" --auth-url "http://test.com/auth.php"`
When the user is connected, proxy will request this URL by GET way("http://test.com/auth.php"),
Four parameters with user, pass, IP, and target:
http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}&target={TARGET}
user:username
pass:password
ip:user's ip,for example:192.168.1.200
target: if the client is the HTTP (s) proxy request, this represents the complete URL of the request, and the other cases are empty.
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.
#### **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.
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`
**two level example**
First level VPS (ip:2.2.2.2) execution:
`proxy sps -S http -T tcp -P 6.6.6.6:6666 -t tcp -z demo_password -p :7777`
Local second level execution:
`proxy sps -T tcp -P 2.2.2.2:777 -Z demo_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.
**three level example**
First level VPS (ip:2.2.2.2) execution:
`proxy sps -S http -T tcp -P 6.6.6.6:6666 -t tcp -z demo_password -p :7777`
Second level VPS (ip:2.2.2.2) execution:
`proxy sps -T tcp -P 2.2.2.2:7777 -Z demo_password -t tcp -z other_password -p :8888`
Local third level execution:
`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.
#### **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.
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:
**two level example**
First level VPS (ip:2.2.2.2) execution:
`proxy sps -t tcp -m -p :7777`
Local second level execution:
`proxy sps -T tcp -P 2.2.2.2:777 -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.
**three level example**
First level VPS (ip:2.2.2.2) execution:
`proxy sps -t tcp -m -p :7777`
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`
Local third level execution:
`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.
#### **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`
### **7.KCP Configuration**
#### **7.1.Configuration introduction**
Many functions of the proxy support the KCP protocol, and all the functions that can use the KCP protocol support the configuration parameters introduced here.
So here is a unified introduction to the KCP configuration parameters.
#### **7.2.Configuration details**
The number of KCP configuration parameters is 17, you don't have to set up them. they all have the default value, if for the best effect,  
You need to configure the parameters according to your own network conditions. Due to the complexity of KCP configuration, a certain network basic knowledge is required,
If you want to get a more detailed configuration and explanation of the KCP parameters, search for yourself. The command line name for each parameter, as well as the default and simple functions, are described as follows
```
--kcp-key="secrect" pre-shared secret between client and server
--kcp-method="aes" encrypt/decrypt method, can be: aes, aes-128, aes-192, salsa20, blowfish,
twofish, cast5, 3des, tea, xtea, xor, sm4, none
--kcp-mode="secrect" profiles: fast3, fast2, fast, normal, manual
--kcp-mtu=1350 set maximum transmission unit for UDP packets
--kcp-sndwnd=1024 set send window size(num of packets)
--kcp-rcvwnd=1024 set receive window size(num of packets)
--kcp-ds=10 set reed-solomon erasure coding - datashard
--kcp-ps=3 set reed-solomon erasure coding - parityshard
--kcp-dscp=0 set DSCP(6bit)
--kcp-nocomp disable compression
--kcp-acknodelay be carefull! flush ack immediately when a packet is received
--kcp-nodelay=0 be carefull!
--kcp-interval=50 be carefull!
--kcp-resend=0 be carefull!
--kcp-nc=0 be carefull! no congestion
--kcp-sockbuf=4194304 be carefull!
--kcp-keepalive=10 be carefull!
```
### **8.DNS anti pollution server**
#### **8.1.Introduction**
It is well known that DNS is a service which use UDP protocol and 53 portBut with the development of network, some well-known DNS servers also support TCP protocol's DNS querysuch as google's 8.8.8.8Proxy's DNS anti pollution server theory is starting a local DNS proxy serverIt uses TCP to conduct DNS queries through father proxy. If it encrypted communicate with father proxyThen you can make a safe and pollution-free DNS analysis.
#### **8.2.Use examples**
***8.2.1 common HTTP(S) father proxy***
Suppose there is a father proxy2.2.2.2:33080
local execution
`proxy dns -S http -T tcp -P 2.2.2.2:33080 -p :53`
Then the local UDP port 53 provides the DNS analysis.
***8.2.2 common SOCKS5 father proxy***
Suppose there is a father proxy2.2.2.2:33080
local execution
`proxy dns -S socks -T tcp -P 2.2.2.2:33080 -p :53`
Then the local UDP port 53 provides the DNS analysis.
***8.2.3 TLS encrypted HTTP(S) father proxy***
Suppose there is a father proxy2.2.2.2:33080
The orders executed by father proxy
`proxy http -t tls -C proxy.crt -K proxy.key -p :33080`
local execution
`proxy dns -S http -T tls -P 2.2.2.2:33080 -C proxy.crt -K proxy.key -p :53`
Then the local UDP port 53 provides a security and anti pollution DNS analysis.
***8.2.4 TLS encrypted SOCKS5 father proxy***
Suppose there is a father proxy2.2.2.2:33080
The orders executed by father proxy
`proxy socks -t tls -C proxy.crt -K proxy.key -p :33080`
local execution
`proxy dns -S socks -T tls -P 2.2.2.2:33080 -C proxy.crt -K proxy.key -p :53`
Then the local UDP port 53 provides a security and anti pollution DNS analysis.
***8.2.5 KCP encrypted HTTP(S) father proxy***
Suppose there is a father proxy2.2.2.2:33080
The orders executed by father proxy
`proxy http -t kcp -p :33080`
local execution
`proxy dns -S http -T kcp -P 2.2.2.2:33080 -p :53`
Then the local UDP port 53 provides a security and anti pollution DNS analysis.
***8.2.6 KCP encrypted SOCKS5 father proxy***
Suppose there is a father proxy2.2.2.2:33080
The orders executed by father proxy
`proxy socks -t kcp -p :33080`
local execution
`proxy dns -S socks -T kcp -P 2.2.2.2:33080 -p :53`
Then the local UDP port 53 provides a security and anti pollution DNS analysis.
***8.2.7 Custom encrypted HTTP(S) father proxy***
Suppose there is a father proxy2.2.2.2:33080
The orders executed by father proxy
`proxy http -t tcp -p :33080 -z password`
local execution
`proxy dns -S http -T tcp -Z password -P 2.2.2.2:33080 -p :53`
Then the local UDP port 53 provides a security and anti pollution DNS analysis.
***8.2.8 Custom encrypted SOCKS5 father proxy***
Suppose there is a father proxy2.2.2.2:33080
The orders executed by father proxy
`proxy socks -t kcp -p :33080 -z password`
local execution
`proxy dns -S socks -T tcp -Z password -P 2.2.2.2:33080 -p :53`
Then the local UDP port 53 provides a security and anti pollution DNS analysis.
### TODO
- HTTP, socks proxy which has multi parents proxy load balancing?
- HTTP (s) proxy support PAC?
- Welcome joining group feedback...
### How to use the source code?
use command cd to enter your go SRC directory and then
execute `git clone https://github.com/snail007/goproxy.git ./proxy`
Direct compilation: `go build`
Recommend go1.10.1.
`go get github.com/snail007/goproxy`
use command cd to enter your go SRC directory
then cd to enter `github.com/snail007/goproxy`.
Direct compilation:`go build -o proxy`
execution: `go run *.go`
`utils` is a toolkit, and `service` is a specific service class.

File diff suppressed because it is too large Load Diff

1
VERSION Normal file
View File

@ -0,0 +1 @@
6.1

399
config.go
View File

@ -2,54 +2,91 @@ package main
import (
"bufio"
"crypto/sha1"
"fmt"
"log"
"io/ioutil"
logger "log"
"os"
"os/exec"
"proxy/services"
"proxy/utils"
"path"
"path/filepath"
"runtime/debug"
"runtime/pprof"
"strings"
"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"
services "github.com/snail007/goproxy/services"
httpx "github.com/snail007/goproxy/services/http"
keygenx "github.com/snail007/goproxy/services/keygen"
mux "github.com/snail007/goproxy/services/mux"
socksx "github.com/snail007/goproxy/services/socks"
spsx "github.com/snail007/goproxy/services/sps"
tcpx "github.com/snail007/goproxy/services/tcp"
tunnelx "github.com/snail007/goproxy/services/tunnel"
udpx "github.com/snail007/goproxy/services/udp"
kcp "github.com/xtaci/kcp-go"
"golang.org/x/crypto/pbkdf2"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
var (
app *kingpin.Application
service *services.ServiceItem
cmd *exec.Cmd
app *kingpin.Application
service *services.ServiceItem
cmd *exec.Cmd
cpuProfilingFile, memProfilingFile, blockProfilingFile, goroutineProfilingFile, threadcreateProfilingFile *os.File
isDebug bool
)
func initConfig() (err error) {
//keygen
if len(os.Args) > 1 {
if os.Args[1] == "keygen" {
utils.Keygen()
os.Exit(0)
}
}
//define args
tcpArgs := services.TCPArgs{}
httpArgs := services.HTTPArgs{}
tunnelServerArgs := services.TunnelServerArgs{}
tunnelClientArgs := services.TunnelClientArgs{}
tunnelBridgeArgs := services.TunnelBridgeArgs{}
muxServerArgs := services.MuxServerArgs{}
muxClientArgs := services.MuxClientArgs{}
muxBridgeArgs := services.MuxBridgeArgs{}
udpArgs := services.UDPArgs{}
socksArgs := services.SocksArgs{}
tcpArgs := tcpx.TCPArgs{}
httpArgs := httpx.HTTPArgs{}
tunnelServerArgs := tunnelx.TunnelServerArgs{}
tunnelClientArgs := tunnelx.TunnelClientArgs{}
tunnelBridgeArgs := tunnelx.TunnelBridgeArgs{}
muxServerArgs := mux.MuxServerArgs{}
muxClientArgs := mux.MuxClientArgs{}
muxBridgeArgs := mux.MuxBridgeArgs{}
udpArgs := udpx.UDPArgs{}
socksArgs := socksx.SocksArgs{}
spsArgs := spsx.SPSArgs{}
dnsArgs := sdk.DNSArgs{}
keygenArgs := keygenx.KeygenArgs{}
kcpArgs := kcpcfg.KCPConfigArgs{}
//build srvice args
app = kingpin.New("proxy", "happy with proxy")
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()
forever := app.Flag("forever", "run proxy in forever,fail and retry").Default("false").Bool()
logfile := app.Flag("log", "log file path").Default("").String()
nolog := app.Flag("nolog", "turn off logging").Default("false").Bool()
kcpArgs.Key = app.Flag("kcp-key", "pre-shared secret between client and server").Default("secrect").String()
kcpArgs.Crypt = app.Flag("kcp-method", "encrypt/decrypt method, can be: aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none").Default("aes").Enum("aes", "aes-128", "aes-192", "salsa20", "blowfish", "twofish", "cast5", "3des", "tea", "xtea", "xor", "sm4", "none")
kcpArgs.Mode = app.Flag("kcp-mode", "profiles: fast3, fast2, fast, normal, manual").Default("fast").Enum("fast3", "fast2", "fast", "normal", "manual")
kcpArgs.MTU = app.Flag("kcp-mtu", "set maximum transmission unit for UDP packets").Default("450").Int()
kcpArgs.SndWnd = app.Flag("kcp-sndwnd", "set send window size(num of packets)").Default("1024").Int()
kcpArgs.RcvWnd = app.Flag("kcp-rcvwnd", "set receive window size(num of packets)").Default("1024").Int()
kcpArgs.DataShard = app.Flag("kcp-ds", "set reed-solomon erasure coding - datashard").Default("10").Int()
kcpArgs.ParityShard = app.Flag("kcp-ps", "set reed-solomon erasure coding - parityshard").Default("3").Int()
kcpArgs.DSCP = app.Flag("kcp-dscp", "set DSCP(6bit)").Default("0").Int()
kcpArgs.NoComp = app.Flag("kcp-nocomp", "disable compression").Default("false").Bool()
kcpArgs.AckNodelay = app.Flag("kcp-acknodelay", "be carefull! flush ack immediately when a packet is received").Default("true").Bool()
kcpArgs.NoDelay = app.Flag("kcp-nodelay", "be carefull!").Default("0").Int()
kcpArgs.Interval = app.Flag("kcp-interval", "be carefull!").Default("50").Int()
kcpArgs.Resend = app.Flag("kcp-resend", "be carefull!").Default("0").Int()
kcpArgs.NoCongestion = app.Flag("kcp-nc", "be carefull! no congestion").Default("0").Int()
kcpArgs.SockBuf = app.Flag("kcp-sockbuf", "be carefull!").Default("4194304").Int()
kcpArgs.KeepAlive = app.Flag("kcp-keepalive", "be carefull!").Default("10").Int()
//########http#########
http := app.Command("http", "proxy on http mode")
httpArgs.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
httpArgs.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.CertFile = http.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
httpArgs.KeyFile = http.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
httpArgs.LocalType = http.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp")
@ -62,21 +99,31 @@ func initConfig() (err error) {
httpArgs.Direct = http.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
httpArgs.AuthFile = http.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
httpArgs.Auth = http.Flag("auth", "http basic auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
httpArgs.PoolSize = http.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int()
httpArgs.CheckParentInterval = http.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
httpArgs.Local = http.Flag("local", "local ip:port to listen,multiple address use comma split,such as: 0.0.0.0:80,0.0.0.0:443").Short('p').Default(":33080").String()
httpArgs.SSHUser = http.Flag("ssh-user", "user for ssh").Short('u').Default("").String()
httpArgs.SSHKeyFile = http.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String()
httpArgs.SSHKeyFileSalt = http.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String()
httpArgs.SSHPassword = http.Flag("ssh-password", "password for ssh").Short('A').Default("").String()
httpArgs.KCPKey = http.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String()
httpArgs.KCPMethod = http.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String()
httpArgs.LocalIPS = http.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
httpArgs.LocalIPS = http.Flag("local-bind-ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
httpArgs.AuthURL = http.Flag("auth-url", "http basic auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
httpArgs.AuthURLTimeout = http.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
httpArgs.AuthURLOkCode = http.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
httpArgs.AuthURLRetry = http.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("1").Int()
httpArgs.DNSAddress = http.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String()
httpArgs.DNSTTL = http.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
httpArgs.LocalKey = http.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String()
httpArgs.ParentKey = http.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
httpArgs.LocalCompress = http.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool()
httpArgs.ParentCompress = http.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
httpArgs.LoadBalanceMethod = http.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")
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.Debug = isDebug
//########tcp#########
tcp := app.Command("tcp", "proxy on tcp mode")
tcpArgs.Parent = tcp.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
@ -85,11 +132,9 @@ func initConfig() (err error) {
tcpArgs.Timeout = tcp.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('e').Default("2000").Int()
tcpArgs.ParentType = tcp.Flag("parent-type", "parent protocol type <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.PoolSize = tcp.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int()
tcpArgs.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
tcpArgs.KCPKey = tcp.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String()
tcpArgs.KCPMethod = tcp.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String()
tcpArgs.Jumper = tcp.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
//########udp#########
udp := app.Command("udp", "proxy on udp mode")
@ -98,36 +143,54 @@ func initConfig() (err error) {
udpArgs.KeyFile = udp.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
udpArgs.Timeout = udp.Flag("timeout", "tcp timeout milliseconds when connect to parent proxy").Short('t').Default("2000").Int()
udpArgs.ParentType = udp.Flag("parent-type", "parent protocol type <tls|tcp|udp>").Short('T').Enum("tls", "tcp", "udp")
udpArgs.PoolSize = udp.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int()
udpArgs.CheckParentInterval = udp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
udpArgs.Local = udp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
//########mux-server#########
muxServer := app.Command("server", "proxy on mux server mode")
muxServerArgs.Parent = muxServer.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
muxServerArgs.ParentType = muxServer.Flag("parent-type", "parent protocol type <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.KeyFile = muxServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxServerArgs.Timeout = muxServer.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
muxServerArgs.Timeout = muxServer.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
muxServerArgs.IsUDP = muxServer.Flag("udp", "proxy on udp mux server mode").Default("false").Bool()
muxServerArgs.Key = muxServer.Flag("k", "client key").Default("default").String()
muxServerArgs.Route = muxServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings()
muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp mode").Default("false").Bool()
muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp|tls mode").Default("false").Bool()
muxServerArgs.SessionCount = muxServer.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
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#########
muxClient := app.Command("client", "proxy on mux client mode")
muxClientArgs.Parent = muxClient.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
muxClientArgs.ParentType = muxClient.Flag("parent-type", "parent protocol type <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.KeyFile = muxClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxClientArgs.Timeout = muxClient.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
muxClientArgs.Timeout = muxClient.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
muxClientArgs.Key = muxClient.Flag("k", "key same with server").Default("default").String()
muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp mode").Default("false").Bool()
muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp|tls mode").Default("false").Bool()
muxClientArgs.SessionCount = muxClient.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
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#########
muxBridge := app.Command("bridge", "proxy on mux bridge mode")
muxBridgeArgs.CertFile = muxBridge.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
muxBridgeArgs.KeyFile = muxBridge.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxBridgeArgs.Timeout = muxBridge.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
muxBridgeArgs.Timeout = muxBridge.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
muxBridgeArgs.Local = muxBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
muxBridgeArgs.LocalType = muxBridge.Flag("local-type", "local protocol type <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#########
tunnelServer := app.Command("tserver", "proxy on tunnel server mode")
@ -138,6 +201,7 @@ func initConfig() (err error) {
tunnelServerArgs.IsUDP = tunnelServer.Flag("udp", "proxy on udp tunnel server mode").Default("false").Bool()
tunnelServerArgs.Key = tunnelServer.Flag("k", "client key").Default("default").String()
tunnelServerArgs.Route = tunnelServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings()
tunnelServerArgs.Jumper = tunnelServer.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
//########tunnel-client#########
tunnelClient := app.Command("tclient", "proxy on tunnel client mode")
@ -146,6 +210,7 @@ func initConfig() (err error) {
tunnelClientArgs.KeyFile = tunnelClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tunnelClientArgs.Timeout = tunnelClient.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
tunnelClientArgs.Key = tunnelClient.Flag("k", "key same with server").Default("default").String()
tunnelClientArgs.Jumper = tunnelClient.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
//########tunnel-bridge#########
tunnelBridge := app.Command("tbridge", "proxy on tunnel bridge mode")
@ -156,18 +221,17 @@ func initConfig() (err error) {
//########ssh#########
socks := app.Command("socks", "proxy on ssh mode")
socksArgs.Parent = socks.Flag("parent", "parent ssh address, such as: \"23.32.32.19:22\"").Default("").Short('P').String()
socksArgs.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.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.UDPParent = socks.Flag("udp-parent", "udp parent address, such as: \"23.32.32.19:33090\"").Default("").Short('X').String()
socksArgs.UDPLocal = socks.Flag("udp-local", "udp local ip:port to listen").Short('x').Default(":33090").String()
socksArgs.CertFile = socks.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
socksArgs.CaCertFile = socks.Flag("ca", "ca cert file for tls").Default("").String()
socksArgs.KeyFile = socks.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
socksArgs.SSHUser = socks.Flag("ssh-user", "user for ssh").Short('u').Default("").String()
socksArgs.SSHKeyFile = socks.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String()
socksArgs.SSHKeyFileSalt = socks.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String()
socksArgs.SSHPassword = socks.Flag("ssh-password", "password for ssh").Short('A').Default("").String()
socksArgs.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.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()
@ -175,25 +239,167 @@ func initConfig() (err error) {
socksArgs.Direct = socks.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
socksArgs.AuthFile = socks.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
socksArgs.Auth = socks.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
socksArgs.KCPKey = socks.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String()
socksArgs.KCPMethod = socks.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String()
socksArgs.LocalIPS = socks.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
socksArgs.LocalIPS = socks.Flag("local-bind-ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
socksArgs.AuthURL = socks.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
socksArgs.AuthURLTimeout = socks.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
socksArgs.AuthURLOkCode = socks.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
socksArgs.AuthURLRetry = socks.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int()
socksArgs.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.DNSTTL = socks.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
socksArgs.LocalKey = socks.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String()
socksArgs.ParentKey = socks.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
socksArgs.LocalCompress = socks.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool()
socksArgs.ParentCompress = socks.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
socksArgs.LoadBalanceMethod = socks.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")
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 := 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').Strings()
spsArgs.CertFile = sps.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
spsArgs.KeyFile = sps.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
spsArgs.CaCertFile = sps.Flag("ca", "ca cert file for tls").Default("").String()
spsArgs.Timeout = sps.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('i').Default("2000").Int()
spsArgs.ParentType = sps.Flag("parent-type", "parent protocol type <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.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|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.DNSTTL = sps.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
spsArgs.AuthFile = sps.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
spsArgs.Auth = sps.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
spsArgs.LocalIPS = sps.Flag("local-bind-ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
spsArgs.AuthURL = sps.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
spsArgs.AuthURLTimeout = sps.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
spsArgs.AuthURLOkCode = sps.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
spsArgs.AuthURLRetry = sps.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int()
spsArgs.ParentAuth = sps.Flag("parent-auth", "parent socks auth username and password, such as: -A user1:pass1").Short('A').String()
spsArgs.LocalKey = sps.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String()
spsArgs.ParentKey = sps.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
spsArgs.LocalCompress = sps.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool()
spsArgs.ParentCompress = sps.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
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.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.Debug = isDebug
//########dns#########
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.CertFile = dns.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
dnsArgs.KeyFile = dns.Flag("key", "key file for tls").Short('K').Default("proxy.key").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.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(":53").String()
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.DNSTTL = dns.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
dnsArgs.ParentAuth = dns.Flag("parent-auth", "parent socks auth username and password, such as: -A user1:pass1").Short('A').String()
dnsArgs.ParentKey = dns.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
dnsArgs.ParentCompress = dns.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
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()
//########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
serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
flags := log.Ldate
if *debug {
flags |= log.Lshortfile | log.Lmicroseconds
//set kcp config
switch *kcpArgs.Mode {
case "normal":
*kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 0, 40, 2, 1
case "fast":
*kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 0, 30, 2, 1
case "fast2":
*kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 1, 20, 2, 1
case "fast3":
*kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 1, 10, 2, 1
}
pass := pbkdf2.Key([]byte(*kcpArgs.Key), []byte("snail007-goproxy"), 4096, 32, sha1.New)
switch *kcpArgs.Crypt {
case "sm4":
kcpArgs.Block, _ = kcp.NewSM4BlockCrypt(pass[:16])
case "tea":
kcpArgs.Block, _ = kcp.NewTEABlockCrypt(pass[:16])
case "xor":
kcpArgs.Block, _ = kcp.NewSimpleXORBlockCrypt(pass)
case "none":
kcpArgs.Block, _ = kcp.NewNoneBlockCrypt(pass)
case "aes-128":
kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass[:16])
case "aes-192":
kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass[:24])
case "blowfish":
kcpArgs.Block, _ = kcp.NewBlowfishBlockCrypt(pass)
case "twofish":
kcpArgs.Block, _ = kcp.NewTwofishBlockCrypt(pass)
case "cast5":
kcpArgs.Block, _ = kcp.NewCast5BlockCrypt(pass[:16])
case "3des":
kcpArgs.Block, _ = kcp.NewTripleDESBlockCrypt(pass[:24])
case "xtea":
kcpArgs.Block, _ = kcp.NewXTEABlockCrypt(pass[:16])
case "salsa20":
kcpArgs.Block, _ = kcp.NewSalsa20BlockCrypt(pass)
default:
*kcpArgs.Crypt = "aes"
kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass)
}
//attach kcp config
tcpArgs.KCP = kcpArgs
httpArgs.KCP = kcpArgs
socksArgs.KCP = kcpArgs
spsArgs.KCP = kcpArgs
muxBridgeArgs.KCP = kcpArgs
muxServerArgs.KCP = kcpArgs
muxClientArgs.KCP = kcpArgs
dnsArgs.KCP = kcpArgs
log := logger.New(os.Stderr, "", logger.Ldate|logger.Ltime)
flags := logger.Ldate
if *isDebug {
flags |= logger.Lshortfile | logger.Lmicroseconds
cpuProfilingFile, _ = os.Create("cpu.prof")
memProfilingFile, _ = os.Create("memory.prof")
blockProfilingFile, _ = os.Create("block.prof")
goroutineProfilingFile, _ = os.Create("goroutine.prof")
threadcreateProfilingFile, _ = os.Create("threadcreate.prof")
pprof.StartCPUProfile(cpuProfilingFile)
} else {
flags |= log.Ltime
flags |= logger.Ltime
}
log.SetFlags(flags)
if *logfile != "" {
if *nolog {
log.SetOutput(ioutil.Discard)
} else if *logfile != "" {
f, e := os.OpenFile(*logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if e != nil {
log.Fatal(e)
@ -224,9 +430,15 @@ func initConfig() (err error) {
}
}
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for {
if cmd != nil {
cmd.Process.Kill()
time.Sleep(time.Second * 5)
}
cmd = exec.Command(os.Args[0], args...)
cmdReaderStderr, err := cmd.StderrPipe()
@ -242,11 +454,21 @@ func initConfig() (err error) {
scanner := bufio.NewScanner(cmdReader)
scannerStdErr := bufio.NewScanner(cmdReaderStderr)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}()
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for scannerStdErr.Scan() {
fmt.Println(scannerStdErr.Text())
}
@ -262,26 +484,51 @@ func initConfig() (err error) {
continue
}
log.Printf("worker %s [PID] %d unexpected exited, restarting...\n", os.Args[0], pid)
time.Sleep(time.Second * 5)
}
}()
return
}
if *logfile == "" {
poster()
if *isDebug {
log.Println("[profiling] cpu profiling save to file : cpu.prof")
log.Println("[profiling] memory profiling save to file : memory.prof")
log.Println("[profiling] block profiling save to file : block.prof")
log.Println("[profiling] goroutine profiling save to file : goroutine.prof")
log.Println("[profiling] threadcreate profiling save to file : threadcreate.prof")
}
}
//regist services and run service
services.Regist("http", services.NewHTTP(), httpArgs)
services.Regist("tcp", services.NewTCP(), tcpArgs)
services.Regist("udp", services.NewUDP(), udpArgs)
services.Regist("tserver", services.NewTunnelServerManager(), tunnelServerArgs)
services.Regist("tclient", services.NewTunnelClient(), tunnelClientArgs)
services.Regist("tbridge", services.NewTunnelBridge(), tunnelBridgeArgs)
services.Regist("server", services.NewMuxServerManager(), muxServerArgs)
services.Regist("client", services.NewMuxClient(), muxClientArgs)
services.Regist("bridge", services.NewMuxBridge(), muxBridgeArgs)
services.Regist("socks", services.NewSocks(), socksArgs)
service, err = services.Run(serviceName)
switch serviceName {
case "http":
services.Regist(serviceName, httpx.NewHTTP(), httpArgs, log)
case "tcp":
services.Regist(serviceName, tcpx.NewTCP(), tcpArgs, log)
case "udp":
services.Regist(serviceName, udpx.NewUDP(), udpArgs, log)
case "tserver":
services.Regist(serviceName, tunnelx.NewTunnelServerManager(), tunnelServerArgs, log)
case "tclient":
services.Regist(serviceName, tunnelx.NewTunnelClient(), tunnelClientArgs, log)
case "tbridge":
services.Regist(serviceName, tunnelx.NewTunnelBridge(), tunnelBridgeArgs, log)
case "server":
services.Regist(serviceName, mux.NewMuxServerManager(), muxServerArgs, log)
case "client":
services.Regist(serviceName, mux.NewMuxClient(), muxClientArgs, log)
case "bridge":
services.Regist(serviceName, mux.NewMuxBridge(), muxBridgeArgs, log)
case "socks":
services.Regist(serviceName, socksx.NewSocks(), socksArgs, log)
case "sps":
services.Regist(serviceName, spsx.NewSPS(), spsArgs, log)
case "dns":
services.Regist(serviceName, sdk.NewDNS(), dnsArgs, log)
case "keygen":
services.Regist(serviceName, keygenx.NewKeygen(), keygenArgs, log)
}
service, err = services.Run(serviceName, nil)
if err != nil {
log.Fatalf("run service [%s] fail, ERR:%s", serviceName, err)
}
@ -289,14 +536,16 @@ func initConfig() (err error) {
}
func poster() {
fmt.Printf(`
######## ######## ####### ## ## ## ##
## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ####
######## ######## ## ## ### ##
## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ##
## ## ## ####### ## ## ##
v%s`+" by snail , blog : http://www.host900.com/\n\n", APP_VERSION)
fmt.Printf(`Proxy Enterprise Version v%s`+" by snail , blog : http://www.host900.com/\n\n", APP_VERSION)
}
func saveProfiling() {
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)
pprof.StopCPUProfile()
}

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:", 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:",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:",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:",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:",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:",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:",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,47 @@
package encrypt
import (
"crypto/cipher"
"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) {
return s.r.Read(b)
}
func (s *Conn) Write(b []byte) (n int, err error) {
return s.w.Write(b)
}
func (s *Conn) Close() (err error) {
err = s.Conn.Close()
s.Cipher = nil
s.r = nil
s.w = nil
return err
}

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:",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:",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:",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:",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,373 @@
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, fromIP, 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()
//协商开始
//method select request
var methodReq MethodsRequest
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
methodReq, e := NewMethodsRequest((*s.conn), s.header)
(*s.conn).SetReadDeadline(time.Time{})
if e != nil {
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
methodReq.Reply(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(time.Second * 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(time.Second * 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(time.Second * 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(time.Second * s.timeout))
n, err = (*s.conn).Read(buf)
(*s.conn).SetReadDeadline(time.Time{})
if err != nil {
err = fmt.Errorf("read auth info fail,ERR: %s", err)
return
}
r := buf[:n]
s.user = string(r[2 : r[1]+2])
s.password = string(r[2+r[1]+1:])
//err = fmt.Errorf("user:%s,pass:%s", user, pass)
//auth
_addr := strings.Split(remoteAddr.String(), ":")
if s.auth == nil || (*s.auth).CheckUserPass(s.user, s.password, _addr[0], "") {
(*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout)))
_, err = (*s.conn).Write([]byte{0x01, 0x00})
(*s.conn).SetDeadline(time.Time{})
if err != nil {
err = fmt.Errorf("answer auth success to %s fail,ERR: %s", remoteAddr, err)
return
}
} else {
(*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout)))
_, err = (*s.conn).Write([]byte{0x01, 0x01})
(*s.conn).SetDeadline(time.Time{})
if err != nil {
err = fmt.Errorf("answer auth fail to %s fail,ERR: %s", remoteAddr, err)
return
}
err = fmt.Errorf("auth fail from %s", remoteAddr)
return
}
}
//request detail
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
request, e := NewRequest(*s.conn)
(*s.conn).SetReadDeadline(time.Time{})
if e != nil {
err = fmt.Errorf("read request data fail,ERR: %s", e)
return
}
//协商结束
switch request.CMD() {
case 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 @@
# 透传用户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

BIN
docs/images/fxdl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/images/http-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/images/http-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/images/http-kcp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/images/http-ssh-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/images/http-tls-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/images/http-tls-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/images/socks-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/images/socks-ssh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/images/socks-tls-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/images/socks-tls-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/images/sps-tls.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/images/tcp-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/images/tcp-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
docs/images/tcp-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/images/tcp-tls-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/images/tcp-tls-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/images/udp-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/images/udp-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/images/udp-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
docs/images/udp-tls-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/images/udp-tls-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

25
docs/old-release.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)

27
gui/README.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,7 @@ if [ -e /tmp/proxy ]; then
fi
mkdir /tmp/proxy
cd /tmp/proxy
wget https://github.com/snail007/goproxy/releases/download/v4.2/proxy-linux-amd64.tar.gz
wget https://github.com/snail007/goproxy/releases/download/v6.1/proxy-linux-amd64.tar.gz
# #install proxy
tar zxvf proxy-linux-amd64.tar.gz
@ -19,7 +19,7 @@ fi
if [ ! -e /etc/proxy/proxy.crt ]; then
cd /etc/proxy/
proxy keygen >/dev/null 2>&1
proxy keygen -C proxy >/dev/null 2>&1
fi
rm -rf /tmp/proxy
echo "install done"

View File

@ -1,3 +0,0 @@
#!/bin/bash
openssl genrsa -out proxy.key 2048
openssl req -new -key proxy.key -x509 -days 3650 -out proxy.crt -subj /C=CN/ST=BJ/O="Localhost Ltd"/CN=proxy

15
main.go
View File

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

View File

@ -1,72 +1,87 @@
#!/bin/bash
VER="4.2"
RELEASE="release-${VER}"
VERSION=$(cat VERSION)
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
mkdir .cert
go build
go build $OPTS -ldflags "$X" -o proxy
cd .cert
../proxy keygen
../proxy keygen -C proxy
cd ..
rm -rf ${RELEASE}
mkdir ${RELEASE}
#linux
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-linux-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-linux-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build && tar zcfv "${RELEASE}/proxy-linux-arm-v6.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=6 go build && tar zcfv "${RELEASE}/proxy-linux-arm64-v6.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm-v7.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm64-v7.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build && tar zcfv "${RELEASE}/proxy-linux-arm-v5.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=5 go build && tar zcfv "${RELEASE}/proxy-linux-arm64-v5.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mips go build && tar zcfv "${RELEASE}/proxy-linux-mips.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build && tar zcfv "${RELEASE}/proxy-linux-mips64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build && tar zcfv "${RELEASE}/proxy-linux-mips64le.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build && tar zcfv "${RELEASE}/proxy-linux-mipsle.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=ppc64 go build && tar zcfv "${RELEASE}/proxy-linux-ppc64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le go build && tar zcfv "${RELEASE}/proxy-linux-ppc64le.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=s390x go build && tar zcfv "${RELEASE}/proxy-linux-s390x.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 $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 $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 $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 $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 $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 $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 $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm64-v5.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=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=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=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=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=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=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
CGO_ENABLED=0 GOOS=android GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-android-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=android GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-android-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=android GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-android-arm.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=android GOARCH=arm64 go build && tar zcfv "${RELEASE}/proxy-android-arm64.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 $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 $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 $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-android-arm64.tar.gz" proxy direct blocked
#darwin
CGO_ENABLED=0 GOOS=darwin GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-darwin-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-darwin-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=darwin GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-darwin-arm.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build && tar zcfv "${RELEASE}/proxy-darwin-arm64.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 $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 $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 $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-darwin-arm64.tar.gz" proxy direct blocked
#dragonfly
CGO_ENABLED=0 GOOS=dragonfly GOARCH=amd64 go build && 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
CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-freebsd-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-freebsd-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=freebsd GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-freebsd-arm.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 $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 $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-freebsd-arm.tar.gz" proxy direct blocked
#nacl
CGO_ENABLED=0 GOOS=nacl GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-nacl-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=nacl GOARCH=amd64p32 go build && tar zcfv "${RELEASE}/proxy-nacl-amd64p32.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=nacl GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-nacl-arm.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 $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 $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-nacl-arm.tar.gz" proxy direct blocked
#netbsd
CGO_ENABLED=0 GOOS=netbsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-netbsd-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=netbsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-netbsd-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=netbsd GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-netbsd-arm.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 $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 $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-netbsd-arm.tar.gz" proxy direct blocked
#openbsd
CGO_ENABLED=0 GOOS=openbsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-openbsd-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=openbsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-openbsd-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=openbsd GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-openbsd-arm.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 $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 $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-openbsd-arm.tar.gz" proxy direct blocked
#plan9
CGO_ENABLED=0 GOOS=plan9 GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-plan9-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=plan9 GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-plan9-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=plan9 GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-plan9-arm.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 $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 $OPTS -ldflags "$X" -o proxy && tar zcfv "${RELEASE}/proxy-plan9-arm.tar.gz" proxy direct blocked
#solaris
CGO_ENABLED=0 GOOS=solaris GOARCH=amd64 go build && 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
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-windows-386.tar.gz" proxy.exe direct blocked .cert/proxy.crt .cert/proxy.key
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-windows-amd64.tar.gz" proxy.exe direct blocked .cert/proxy.crt .cert/proxy.key
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 $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 $OPTS -ldflags="-H=windowsgui $X" -o proxy-noconsole.exe
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 .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
#1.install_auto.sh goproxy/releases/download/vxxx
#2.README goproxy/releases/download/vxxx

3
sdk/CHANGELOG Normal file
View File

@ -0,0 +1,3 @@
v4.8
1.修复了多个服务同时开启日志,只会输出到最后一个日志文件的bug.
2.增加了获取sdk版本的Version()方法.

259
sdk/README.md Normal file
View File

@ -0,0 +1,259 @@
# 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.Porxy
```
#### 2.启动一个服务
```java
String serviceID="http01";//这里serviceID是自定义的唯一标识字符串,保证每个启动的服务不一样即可
String serviceArgs="http -p :8080";
String err=Proxy.start(serviceID,serviceArgs);
if (!err.isEmpty()){
//启动失败
System.out.println("start fail,error:"+err);
}else{
//启动成功
}
```
#### 3.停止一个服务
```java
String serviceID="http01";
Proxy.stop(serviceID);
//停止完毕
```
## IOS SDK
[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy-sdk-ios/) [![license](https://img.shields.io/github/license/snail007/goproxy-sdk-ios.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy-sdk-ios/total.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-ios/releases) [![download](https://img.shields.io/github/release/snail007/goproxy-sdk-ios.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-ios/releases)
[点击下载IOS-SDK](https://github.com/snail007/goproxy-sdk-ios/releases)
在IOS系统提供的sdk形式是一个后缀为.framework的类库文件夹,开发的时候只需要把类库文件引入项目,然后调用方法即可.
### IOS-SDK使用实例
#### 导入包
```objc
#import <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-proxy.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参数.

7
sdk/android-ios/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*.jar
*.aar
*.tar.gz
ios
android
Proxy.framework

266
sdk/android-ios/dns.go Normal file
View File

@ -0,0 +1,266 @@
package proxy
import (
"crypto/md5"
"encoding/hex"
"fmt"
logger "log"
"net"
"runtime/debug"
"time"
"golang.org/x/net/proxy"
"github.com/miekg/dns"
gocache "github.com/pmylund/go-cache"
"github.com/snail007/goproxy/core/lib/kcpcfg"
services "github.com/snail007/goproxy/services"
)
type DNSArgs struct {
ParentServiceType *string
ParentType *string
Parent *string
ParentAuth *string
ParentKey *string
ParentCompress *bool
KCP kcpcfg.KCPConfigArgs
CertFile *string
KeyFile *string
CaCertFile *string
Local *string
Timeout *int
RemoteDNSAddress *string
DNSTTL *int
CacheFile *string
LocalSocks5Port *string
}
type DNS struct {
cfg DNSArgs
log *logger.Logger
cache *gocache.Cache
exitSig chan bool
serviceKey string
dialer proxy.Dialer
}
func NewDNS() services.Service {
return &DNS{
cfg: DNSArgs{},
exitSig: make(chan bool, 1),
serviceKey: "dns-service-" + fmt.Sprintf("%d", time.Now().UnixNano()),
}
}
func (s *DNS) CheckArgs() (err error) {
return
}
func (s *DNS) InitService() (err error) {
s.cache = gocache.New(time.Second*time.Duration(*s.cfg.DNSTTL), time.Second*60)
s.cache.LoadFile(*s.cfg.CacheFile)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for {
select {
case <-s.exitSig:
return
case <-time.After(time.Second * 60):
err := s.cache.SaveFile(*s.cfg.CacheFile)
if err == nil {
//s.log.Printf("cache saved: %s", *s.cfg.CacheFile)
} else {
s.log.Printf("cache save failed: %s, %s", *s.cfg.CacheFile, err)
}
}
}
}()
s.dialer, err = proxy.SOCKS5("tcp", *s.cfg.Parent,
nil,
&net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 5 * time.Second,
},
)
if err != nil {
return
}
sdkArgs := fmt.Sprintf("sps -S %s -T %s -P %s -C %s -K %s -i %d -p 127.0.0.1:%s --disable-http",
*s.cfg.ParentServiceType,
*s.cfg.ParentType,
*s.cfg.Parent,
*s.cfg.CertFile,
*s.cfg.KeyFile,
*s.cfg.Timeout,
*s.cfg.LocalSocks5Port,
)
if *s.cfg.ParentKey != "" {
sdkArgs += " -Z " + *s.cfg.ParentKey
}
if *s.cfg.ParentAuth != "" {
sdkArgs += " -A " + *s.cfg.ParentAuth
}
if *s.cfg.CaCertFile != "" {
sdkArgs += " --ca " + *s.cfg.CaCertFile
}
if *s.cfg.ParentCompress {
sdkArgs += " -M"
}
s.log.Printf("start sps with : %s", sdkArgs)
errStr := Start(s.serviceKey, sdkArgs)
if errStr != "" {
err = fmt.Errorf("start sps service fail,%s", errStr)
}
return
}
func (s *DNS) StopService() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop dns service crashed,%s", e)
} else {
s.log.Printf("service dns stoped")
}
}()
Stop(s.serviceKey)
s.cache.Flush()
s.exitSig <- true
}
func (s *DNS) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(DNSArgs)
if err = s.CheckArgs(); err != nil {
return
}
if err = s.InitService(); err != nil {
return
}
dns.HandleFunc(".", s.callback)
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)
err := dns.ListenAndServe(*s.cfg.Local, "udp", nil)
if err != nil {
log.Printf("dns listen error: %s", err)
}
}()
return
}
func (s *DNS) Clean() {
s.StopService()
}
func (s *DNS) callback(w dns.ResponseWriter, req *dns.Msg) {
defer func() {
if err := recover(); err != nil {
s.log.Printf("dns handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var (
key string
m *dns.Msg
err error
data []byte
id uint16
query []string
questions []dns.Question
)
if req.MsgHdr.Response == true {
return
}
query = make([]string, len(req.Question))
for i, q := range req.Question {
if q.Qtype != dns.TypeAAAA {
questions = append(questions, q)
}
query[i] = fmt.Sprintf("(%s %s %s)", q.Name, dns.ClassToString[q.Qclass], dns.TypeToString[q.Qtype])
}
if len(questions) == 0 {
return
}
req.Question = questions
id = req.Id
req.Id = 0
key = s.toMd5(req.String())
req.Id = id
if reply, ok := s.cache.Get(key); ok {
data, _ = reply.([]byte)
}
if data != nil && len(data) > 0 {
m = &dns.Msg{}
m.Unpack(data)
m.Id = id
err = w.WriteMsg(m)
s.log.Printf("id: %5d cache: HIT %v", id, query)
return
} else {
s.log.Printf("id: %5d cache: MISS %v", id, query)
}
s.log.Printf("id: %5d resolve: %v %s", id, query, *s.cfg.RemoteDNSAddress)
rawConn, err := s.dialer.Dial("tcp", *s.cfg.RemoteDNSAddress)
if err != nil {
s.log.Printf("dail to %s fail,%s", *s.cfg.RemoteDNSAddress, err)
return
}
defer rawConn.Close()
co := new(dns.Conn)
co.Conn = rawConn
defer co.Close()
if err = co.WriteMsg(req); err != nil {
s.log.Printf("write dns query fail,%s", err)
return
}
m, err = co.ReadMsg()
if err == nil && m.Id != req.Id {
s.log.Printf("id: %5d mismath", id)
return
}
if err != nil || len(m.Answer) == 0 {
s.log.Printf("dns query fail,%s", err)
return
}
data, err = m.Pack()
if err != nil {
s.log.Printf("dns query fail,%s", err)
return
}
_, err = w.Write(data)
if err != nil {
s.log.Printf("dns query fail,%s", err)
return
}
m.Id = 0
data, _ = m.Pack()
ttl := 0
if len(m.Answer) > 0 {
if *s.cfg.DNSTTL > 0 {
ttl = *s.cfg.DNSTTL
} else {
ttl = int(m.Answer[0].Header().Ttl)
if ttl < 0 {
ttl = *s.cfg.DNSTTL
}
}
}
s.cache.Set(key, data, time.Second*time.Duration(ttl))
m.Id = id
s.log.Printf("id: %5d cache: CACHED %v TTL %v", id, query, ttl)
}
func (s *DNS) toMd5(data string) string {
m := md5.New()
m.Write([]byte(data))
return hex.EncodeToString(m.Sum(nil))
}

View File

@ -0,0 +1,27 @@
#/bin/bash
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 android
mkdir android
#android ; jdk,android ndk & android sdk required, install gomobile go1.10 required
#export GOPATH="$HOME/go"
#export GOROOT="/usr/local/go"
#export PATH="$GOROOT/bin:$GOPATH/bin:$PATH"
#export ANDROID_HOME="$HOME/Android/Sdk"
#export NDK_ROOT="$HOME/Android/Sdk/ndk-bundle"
#export PATH="$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$NDK_ROOT:$PATH"
#go get -v golang.org/x/mobile/cmd/gomobile
#gomobile init
gomobile bind -v -target=android -javapkg=snail007 -ldflags="-s -w $X"
mv proxy.aar android/snail007.goproxy.sdk.aar
mv proxy-sources.jar android/snail007.goproxy.sdk-sources.jar
cp ../README.md android
tar zcfv sdk-android-${VERSION}.tar.gz android
rm -rf android
echo "done."

17
sdk/android-ios/release_ios.sh Executable file
View File

@ -0,0 +1,17 @@
#/bin/bash
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 ios
mkdir ios
#ios XCode required
gomobile bind -v -target=ios -ldflags="-s -w $X"
mv Proxy.framework ios
cp ../README.md ios
tar zcfv sdk-ios-${VERSION}.tar.gz ios
rm -rf ios
echo "done."

475
sdk/android-ios/sdk.go Normal file
View File

@ -0,0 +1,475 @@
package proxy
import (
"crypto/sha1"
"fmt"
"io/ioutil"
logger "log"
"os"
"path"
"path/filepath"
"strings"
"github.com/snail007/goproxy/core/lib/kcpcfg"
encryptconn "github.com/snail007/goproxy/core/lib/transport/encrypt"
"github.com/snail007/goproxy/services"
httpx "github.com/snail007/goproxy/services/http"
keygenx "github.com/snail007/goproxy/services/keygen"
mux "github.com/snail007/goproxy/services/mux"
socksx "github.com/snail007/goproxy/services/socks"
spsx "github.com/snail007/goproxy/services/sps"
tcpx "github.com/snail007/goproxy/services/tcp"
tunnelx "github.com/snail007/goproxy/services/tunnel"
udpx "github.com/snail007/goproxy/services/udp"
kcp "github.com/xtaci/kcp-go"
"golang.org/x/crypto/pbkdf2"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
var SDK_VERSION = "No Version Provided"
var (
app *kingpin.Application
)
type LogCallback interface {
Write(line string)
}
type logCallback interface {
Write(line string)
}
type logWriter struct {
callback LogCallback
}
func (s *logWriter) Write(p []byte) (n int, err error) {
s.callback.Write(string(p))
return
}
func Start(serviceID, serviceArgsStr string) (errStr string) {
return StartWithLog(serviceID, serviceArgsStr, nil)
}
//Start
//serviceID : is service identify id,different service's id should be difference
//serviceArgsStr: is the whole command line args string
//such as :
//1."http -t tcp -p :8989"
//2."socks -t tcp -p :8989"
//and so on.
//if an error occured , errStr will be the error reason
//if start success, errStr is empty.
func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback) (errStr string) {
//define args
tcpArgs := tcpx.TCPArgs{}
httpArgs := httpx.HTTPArgs{}
tunnelServerArgs := tunnelx.TunnelServerArgs{}
tunnelClientArgs := tunnelx.TunnelClientArgs{}
tunnelBridgeArgs := tunnelx.TunnelBridgeArgs{}
muxServerArgs := mux.MuxServerArgs{}
muxClientArgs := mux.MuxClientArgs{}
muxBridgeArgs := mux.MuxBridgeArgs{}
udpArgs := udpx.UDPArgs{}
socksArgs := socksx.SocksArgs{}
spsArgs := spsx.SPSArgs{}
dnsArgs := DNSArgs{}
keygenArgs := keygenx.KeygenArgs{}
kcpArgs := kcpcfg.KCPConfigArgs{}
//build srvice args
app = kingpin.New("proxy", "happy with proxy")
app.Author("snail").Version(SDK_VERSION)
debug := app.Flag("debug", "debug log output").Default("false").Bool()
logfile := app.Flag("log", "log file path").Default("").String()
nolog := app.Flag("nolog", "turn off logging").Default("false").Bool()
kcpArgs.Key = app.Flag("kcp-key", "pre-shared secret between client and server").Default("secrect").String()
kcpArgs.Crypt = app.Flag("kcp-method", "encrypt/decrypt method, can be: aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none").Default("aes").Enum("aes", "aes-128", "aes-192", "salsa20", "blowfish", "twofish", "cast5", "3des", "tea", "xtea", "xor", "sm4", "none")
kcpArgs.Mode = app.Flag("kcp-mode", "profiles: fast3, fast2, fast, normal, manual").Default("fast").Enum("fast3", "fast2", "fast", "normal", "manual")
kcpArgs.MTU = app.Flag("kcp-mtu", "set maximum transmission unit for UDP packets").Default("450").Int()
kcpArgs.SndWnd = app.Flag("kcp-sndwnd", "set send window size(num of packets)").Default("1024").Int()
kcpArgs.RcvWnd = app.Flag("kcp-rcvwnd", "set receive window size(num of packets)").Default("1024").Int()
kcpArgs.DataShard = app.Flag("kcp-ds", "set reed-solomon erasure coding - datashard").Default("10").Int()
kcpArgs.ParityShard = app.Flag("kcp-ps", "set reed-solomon erasure coding - parityshard").Default("3").Int()
kcpArgs.DSCP = app.Flag("kcp-dscp", "set DSCP(6bit)").Default("0").Int()
kcpArgs.NoComp = app.Flag("kcp-nocomp", "disable compression").Default("false").Bool()
kcpArgs.AckNodelay = app.Flag("kcp-acknodelay", "be carefull! flush ack immediately when a packet is received").Default("true").Bool()
kcpArgs.NoDelay = app.Flag("kcp-nodelay", "be carefull!").Default("0").Int()
kcpArgs.Interval = app.Flag("kcp-interval", "be carefull!").Default("50").Int()
kcpArgs.Resend = app.Flag("kcp-resend", "be carefull!").Default("0").Int()
kcpArgs.NoCongestion = app.Flag("kcp-nc", "be carefull! no congestion").Default("0").Int()
kcpArgs.SockBuf = app.Flag("kcp-sockbuf", "be carefull!").Default("4194304").Int()
kcpArgs.KeepAlive = app.Flag("kcp-keepalive", "be carefull!").Default("10").Int()
//########http#########
http := app.Command("http", "proxy on http mode")
httpArgs.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').Strings()
httpArgs.CaCertFile = http.Flag("ca", "ca cert file for tls").Default("").String()
httpArgs.CertFile = http.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
httpArgs.KeyFile = http.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
httpArgs.LocalType = http.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp")
httpArgs.ParentType = http.Flag("parent-type", "parent protocol type <tls|tcp|ssh|kcp>").Short('T').Enum("tls", "tcp", "ssh", "kcp")
httpArgs.Always = http.Flag("always", "always use parent proxy").Default("false").Bool()
httpArgs.Timeout = http.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("2000").Int()
httpArgs.HTTPTimeout = http.Flag("http-timeout", "check domain if blocked , http request timeout milliseconds when connect to host").Default("3000").Int()
httpArgs.Interval = http.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int()
httpArgs.Blocked = http.Flag("blocked", "blocked domain file , one domain each line").Default("blocked").Short('b').String()
httpArgs.Direct = http.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
httpArgs.AuthFile = http.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
httpArgs.Auth = http.Flag("auth", "http basic auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
httpArgs.CheckParentInterval = http.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
httpArgs.Local = http.Flag("local", "local ip:port to listen,multiple address use comma split,such as: 0.0.0.0:80,0.0.0.0:443").Short('p').Default(":33080").String()
httpArgs.SSHUser = http.Flag("ssh-user", "user for ssh").Short('u').Default("").String()
httpArgs.SSHKeyFile = http.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String()
httpArgs.SSHKeyFileSalt = http.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String()
httpArgs.SSHPassword = http.Flag("ssh-password", "password for ssh").Short('A').Default("").String()
httpArgs.LocalIPS = http.Flag("local-bind-ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
httpArgs.AuthURL = http.Flag("auth-url", "http basic auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
httpArgs.AuthURLTimeout = http.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
httpArgs.AuthURLOkCode = http.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
httpArgs.AuthURLRetry = http.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("1").Int()
httpArgs.DNSAddress = http.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String()
httpArgs.DNSTTL = http.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
httpArgs.LocalKey = http.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String()
httpArgs.ParentKey = http.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
httpArgs.LocalCompress = http.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool()
httpArgs.ParentCompress = http.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
httpArgs.LoadBalanceMethod = http.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")
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.Debug = debug
//########tcp#########
tcp := app.Command("tcp", "proxy on tcp mode")
tcpArgs.Parent = tcp.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
tcpArgs.CertFile = tcp.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
tcpArgs.KeyFile = tcp.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tcpArgs.Timeout = tcp.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('e').Default("2000").Int()
tcpArgs.ParentType = tcp.Flag("parent-type", "parent protocol type <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.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
tcpArgs.Jumper = tcp.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
//########udp#########
udp := app.Command("udp", "proxy on udp mode")
udpArgs.Parent = udp.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
udpArgs.CertFile = udp.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
udpArgs.KeyFile = udp.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
udpArgs.Timeout = udp.Flag("timeout", "tcp timeout milliseconds when connect to parent proxy").Short('t').Default("2000").Int()
udpArgs.ParentType = udp.Flag("parent-type", "parent protocol type <tls|tcp|udp>").Short('T').Enum("tls", "tcp", "udp")
udpArgs.CheckParentInterval = udp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
udpArgs.Local = udp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
//########mux-server#########
muxServer := app.Command("server", "proxy on mux server mode")
muxServerArgs.Parent = muxServer.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
muxServerArgs.ParentType = muxServer.Flag("parent-type", "parent protocol type <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.KeyFile = muxServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxServerArgs.Timeout = muxServer.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
muxServerArgs.IsUDP = muxServer.Flag("udp", "proxy on udp mux server mode").Default("false").Bool()
muxServerArgs.Key = muxServer.Flag("k", "client key").Default("default").String()
muxServerArgs.Route = muxServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings()
muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp|tls mode").Default("false").Bool()
muxServerArgs.SessionCount = muxServer.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
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#########
muxClient := app.Command("client", "proxy on mux client mode")
muxClientArgs.Parent = muxClient.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
muxClientArgs.ParentType = muxClient.Flag("parent-type", "parent protocol type <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.KeyFile = muxClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxClientArgs.Timeout = muxClient.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
muxClientArgs.Key = muxClient.Flag("k", "key same with server").Default("default").String()
muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp|tls mode").Default("false").Bool()
muxClientArgs.SessionCount = muxClient.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
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#########
muxBridge := app.Command("bridge", "proxy on mux bridge mode")
muxBridgeArgs.CertFile = muxBridge.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
muxBridgeArgs.KeyFile = muxBridge.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxBridgeArgs.Timeout = muxBridge.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
muxBridgeArgs.Local = muxBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
muxBridgeArgs.LocalType = muxBridge.Flag("local-type", "local protocol type <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#########
tunnelServer := app.Command("tserver", "proxy on tunnel server mode")
tunnelServerArgs.Parent = tunnelServer.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
tunnelServerArgs.CertFile = tunnelServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
tunnelServerArgs.KeyFile = tunnelServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tunnelServerArgs.Timeout = tunnelServer.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
tunnelServerArgs.IsUDP = tunnelServer.Flag("udp", "proxy on udp tunnel server mode").Default("false").Bool()
tunnelServerArgs.Key = tunnelServer.Flag("k", "client key").Default("default").String()
tunnelServerArgs.Route = tunnelServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings()
tunnelServerArgs.Jumper = tunnelServer.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
//########tunnel-client#########
tunnelClient := app.Command("tclient", "proxy on tunnel client mode")
tunnelClientArgs.Parent = tunnelClient.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
tunnelClientArgs.CertFile = tunnelClient.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
tunnelClientArgs.KeyFile = tunnelClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tunnelClientArgs.Timeout = tunnelClient.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
tunnelClientArgs.Key = tunnelClient.Flag("k", "key same with server").Default("default").String()
tunnelClientArgs.Jumper = tunnelClient.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Short('J').Default("").String()
//########tunnel-bridge#########
tunnelBridge := app.Command("tbridge", "proxy on tunnel bridge mode")
tunnelBridgeArgs.CertFile = tunnelBridge.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
tunnelBridgeArgs.KeyFile = tunnelBridge.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tunnelBridgeArgs.Timeout = tunnelBridge.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
tunnelBridgeArgs.Local = tunnelBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
//########ssh#########
socks := app.Command("socks", "proxy on ssh mode")
socksArgs.Parent = socks.Flag("parent", "parent ssh address, such as: \"23.32.32.19:22\"").Default("").Short('P').Strings()
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.Local = socks.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
socksArgs.CertFile = socks.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
socksArgs.CaCertFile = socks.Flag("ca", "ca cert file for tls").Default("").String()
socksArgs.KeyFile = socks.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
socksArgs.SSHUser = socks.Flag("ssh-user", "user for ssh").Short('u').Default("").String()
socksArgs.SSHKeyFile = socks.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String()
socksArgs.SSHKeyFileSalt = socks.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String()
socksArgs.SSHPassword = socks.Flag("ssh-password", "password for ssh").Short('D').Default("").String()
socksArgs.Always = socks.Flag("always", "always use parent proxy").Default("false").Bool()
socksArgs.Timeout = socks.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("5000").Int()
socksArgs.Interval = socks.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int()
socksArgs.Blocked = socks.Flag("blocked", "blocked domain file , one domain each line").Default("blocked").Short('b').String()
socksArgs.Direct = socks.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
socksArgs.AuthFile = socks.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
socksArgs.Auth = socks.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
socksArgs.LocalIPS = socks.Flag("local-bind-ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
socksArgs.AuthURL = socks.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
socksArgs.AuthURLTimeout = socks.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
socksArgs.AuthURLOkCode = socks.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
socksArgs.AuthURLRetry = socks.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int()
socksArgs.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.DNSTTL = socks.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
socksArgs.LocalKey = socks.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String()
socksArgs.ParentKey = socks.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
socksArgs.LocalCompress = socks.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool()
socksArgs.ParentCompress = socks.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
socksArgs.LoadBalanceMethod = socks.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")
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)#########
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').Strings()
spsArgs.CertFile = sps.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
spsArgs.KeyFile = sps.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
spsArgs.CaCertFile = sps.Flag("ca", "ca cert file for tls").Default("").String()
spsArgs.Timeout = sps.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('i').Default("2000").Int()
spsArgs.ParentType = sps.Flag("parent-type", "parent protocol type <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.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|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.DNSTTL = sps.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
spsArgs.AuthFile = sps.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
spsArgs.Auth = sps.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
spsArgs.LocalIPS = sps.Flag("local-bind-ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
spsArgs.AuthURL = sps.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
spsArgs.AuthURLTimeout = sps.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
spsArgs.AuthURLOkCode = sps.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
spsArgs.AuthURLRetry = sps.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int()
spsArgs.ParentAuth = sps.Flag("parent-auth", "parent socks auth username and password, such as: -A user1:pass1").Short('A').String()
spsArgs.LocalKey = sps.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String()
spsArgs.ParentKey = sps.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
spsArgs.LocalCompress = sps.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool()
spsArgs.ParentCompress = sps.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
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.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.Debug = debug
//########dns#########
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.CertFile = dns.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
dnsArgs.KeyFile = dns.Flag("key", "key file for tls").Short('K').Default("proxy.key").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.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(":53").String()
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.DNSTTL = dns.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
dnsArgs.ParentAuth = dns.Flag("parent-auth", "parent socks auth username and password, such as: -A user1:pass1").Short('A').String()
dnsArgs.ParentKey = dns.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
dnsArgs.ParentCompress = dns.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
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()
//########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
_args := strings.Fields(strings.Trim(serviceArgsStr, " "))
args := []string{}
for _, a := range _args {
args = append(args, strings.Trim(a, "\""))
}
serviceName, err := app.Parse(args)
if err != nil {
return fmt.Sprintf("parse args fail,err: %s", err)
}
//set kcp config
switch *kcpArgs.Mode {
case "normal":
*kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 0, 40, 2, 1
case "fast":
*kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 0, 30, 2, 1
case "fast2":
*kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 1, 20, 2, 1
case "fast3":
*kcpArgs.NoDelay, *kcpArgs.Interval, *kcpArgs.Resend, *kcpArgs.NoCongestion = 1, 10, 2, 1
}
pass := pbkdf2.Key([]byte(*kcpArgs.Key), []byte("snail007-goproxy"), 4096, 32, sha1.New)
switch *kcpArgs.Crypt {
case "sm4":
kcpArgs.Block, _ = kcp.NewSM4BlockCrypt(pass[:16])
case "tea":
kcpArgs.Block, _ = kcp.NewTEABlockCrypt(pass[:16])
case "xor":
kcpArgs.Block, _ = kcp.NewSimpleXORBlockCrypt(pass)
case "none":
kcpArgs.Block, _ = kcp.NewNoneBlockCrypt(pass)
case "aes-128":
kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass[:16])
case "aes-192":
kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass[:24])
case "blowfish":
kcpArgs.Block, _ = kcp.NewBlowfishBlockCrypt(pass)
case "twofish":
kcpArgs.Block, _ = kcp.NewTwofishBlockCrypt(pass)
case "cast5":
kcpArgs.Block, _ = kcp.NewCast5BlockCrypt(pass[:16])
case "3des":
kcpArgs.Block, _ = kcp.NewTripleDESBlockCrypt(pass[:24])
case "xtea":
kcpArgs.Block, _ = kcp.NewXTEABlockCrypt(pass[:16])
case "salsa20":
kcpArgs.Block, _ = kcp.NewSalsa20BlockCrypt(pass)
default:
*kcpArgs.Crypt = "aes"
kcpArgs.Block, _ = kcp.NewAESBlockCrypt(pass)
}
//attach kcp config
tcpArgs.KCP = kcpArgs
httpArgs.KCP = kcpArgs
socksArgs.KCP = kcpArgs
spsArgs.KCP = kcpArgs
muxBridgeArgs.KCP = kcpArgs
muxServerArgs.KCP = kcpArgs
muxClientArgs.KCP = kcpArgs
dnsArgs.KCP = kcpArgs
log := logger.New(os.Stderr, "", logger.Ldate|logger.Ltime)
flags := logger.Ldate
if *debug {
flags |= logger.Lshortfile | logger.Lmicroseconds
} else {
flags |= logger.Ltime
}
log.SetFlags(flags)
if loggerCallback == nil {
if *nolog {
log.SetOutput(ioutil.Discard)
} else if *logfile != "" {
f, e := os.OpenFile(*logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if e != nil {
log.Fatal(e)
}
log.SetOutput(f)
}
} else {
log.SetOutput(&logWriter{
callback: loggerCallback,
})
}
//regist services and run service
switch serviceName {
case "http":
services.Regist(serviceID, httpx.NewHTTP(), httpArgs, log)
case "tcp":
services.Regist(serviceID, tcpx.NewTCP(), tcpArgs, log)
case "udp":
services.Regist(serviceID, udpx.NewUDP(), udpArgs, log)
case "tserver":
services.Regist(serviceID, tunnelx.NewTunnelServerManager(), tunnelServerArgs, log)
case "tclient":
services.Regist(serviceID, tunnelx.NewTunnelClient(), tunnelClientArgs, log)
case "tbridge":
services.Regist(serviceID, tunnelx.NewTunnelBridge(), tunnelBridgeArgs, log)
case "server":
services.Regist(serviceID, mux.NewMuxServerManager(), muxServerArgs, log)
case "client":
services.Regist(serviceID, mux.NewMuxClient(), muxClientArgs, log)
case "bridge":
services.Regist(serviceID, mux.NewMuxBridge(), muxBridgeArgs, log)
case "socks":
services.Regist(serviceID, socksx.NewSocks(), socksArgs, log)
case "sps":
services.Regist(serviceID, spsx.NewSPS(), spsArgs, log)
case "dns":
services.Regist(serviceID, NewDNS(), dnsArgs, log)
}
_, err = services.Run(serviceID, nil)
if err != nil {
return fmt.Sprintf("run service [%s:%s] fail, ERR:%s", serviceID, serviceName, err)
}
return
}
func Stop(serviceID string) {
services.Stop(serviceID)
}
func Version() string {
return SDK_VERSION
}

6
sdk/windows-linux/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
proxy-sdk.dll
proxy-sdk.h
proxy-sdk.so
proxy-sdk.a
*.tar.gz
test.c

Binary file not shown.

View File

@ -0,0 +1,32 @@
#/bin/bash
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 README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a
#linux 32bit
CGO_ENABLED=1 GOARCH=386 GOOS=linux go build -buildmode=c-archive $OPTS -ldflags "-s -w $X" -o libproxy-sdk.a 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 .
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
#linux 64bit
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 $OPTS -ldflags "-s -w $X" -o libproxy-sdk.so sdk.go
cp ../README.md .
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
echo "done."

View File

@ -0,0 +1,21 @@
#/bin/bash
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 README.md libproxy-sdk.dylib libproxy-sdk.h
#mac , macos required
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 .
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
echo "done."

View File

@ -0,0 +1,36 @@
#/bin/bash
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 ln -s /usr/local/go1.10.1 /usr/local/go
rm -rf sdk-windows-*.tar.gz
rm -rf README.md proxy-sdk.h proxy-sdk.dll
#apt-get install gcc-multilib
#apt-get install gcc-mingw-w64
#windows 64bit
CC=x86_64-w64-mingw32-gcc GOARCH=amd64 CGO_ENABLED=1 GOOS=windows go build $OPTS -buildmode=c-shared -ldflags "-s -w $X" -o proxy-sdk.dll sdk.go
cp ../README.md .
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
#windows 32bit
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 .
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
#sudo rm /usr/local/go
#sudo ln -s /usr/local/go1.8.5 /usr/local/go
echo "done."

25
sdk/windows-linux/sdk.go Normal file
View File

@ -0,0 +1,25 @@
package main
import (
"C"
sdk "github.com/snail007/goproxy/sdk/android-ios"
)
//export Start
func Start(serviceID *C.char, serviceArgsStr *C.char) (errStr *C.char) {
return C.CString(sdk.Start(C.GoString(serviceID), C.GoString(serviceArgsStr)))
}
//export Stop
func Stop(serviceID *C.char) {
sdk.Stop(C.GoString(serviceID))
}
//export Version
func Version() (ver *C.char) {
return C.CString(sdk.Version())
}
func main() {
}

View File

@ -1,197 +0,0 @@
package services
import "golang.org/x/crypto/ssh"
// tcp := app.Command("tcp", "proxy on tcp mode")
// t := tcp.Flag("tcp-timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("2000").Int()
const (
TYPE_TCP = "tcp"
TYPE_UDP = "udp"
TYPE_HTTP = "http"
TYPE_TLS = "tls"
TYPE_KCP = "kcp"
CONN_CLIENT_CONTROL = uint8(1)
CONN_CLIENT_HEARBEAT = uint8(2)
CONN_SERVER_HEARBEAT = uint8(3)
CONN_SERVER = uint8(4)
CONN_CLIENT = uint8(5)
CONN_SERVER_MUX = uint8(6)
CONN_CLIENT_MUX = uint8(7)
)
type MuxServerArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
IsUDP *bool
Key *string
Remote *string
Timeout *int
Route *[]string
Mgr *MuxServerManager
IsCompress *bool
}
type MuxClientArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Key *string
Timeout *int
IsCompress *bool
}
type MuxBridgeArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
Timeout *int
IsCompress *bool
}
type TunnelServerArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
IsUDP *bool
Key *string
Remote *string
Timeout *int
Route *[]string
Mgr *TunnelServerManager
Mux *bool
}
type TunnelClientArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Key *string
Timeout *int
Mux *bool
}
type TunnelBridgeArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
Timeout *int
Mux *bool
}
type TCPArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
ParentType *string
LocalType *string
Timeout *int
PoolSize *int
CheckParentInterval *int
KCPMethod *string
KCPKey *string
}
type HTTPArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
Always *bool
HTTPTimeout *int
Interval *int
Blocked *string
Direct *string
AuthFile *string
Auth *[]string
AuthURL *string
AuthURLOkCode *int
AuthURLTimeout *int
AuthURLRetry *int
ParentType *string
LocalType *string
Timeout *int
PoolSize *int
CheckParentInterval *int
SSHKeyFile *string
SSHKeyFileSalt *string
SSHPassword *string
SSHUser *string
SSHKeyBytes []byte
SSHAuthMethod ssh.AuthMethod
KCPMethod *string
KCPKey *string
LocalIPS *[]string
}
type UDPArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
ParentType *string
Timeout *int
PoolSize *int
CheckParentInterval *int
}
type SocksArgs struct {
Parent *string
ParentType *string
Local *string
LocalType *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
SSHKeyFile *string
SSHKeyFileSalt *string
SSHPassword *string
SSHUser *string
SSHKeyBytes []byte
SSHAuthMethod ssh.AuthMethod
Timeout *int
Always *bool
Interval *int
Blocked *string
Direct *string
AuthFile *string
Auth *[]string
AuthURL *string
AuthURLOkCode *int
AuthURLTimeout *int
AuthURLRetry *int
KCPMethod *string
KCPKey *string
UDPParent *string
UDPLocal *string
LocalIPS *[]string
}
func (a *TCPArgs) Protocol() string {
switch *a.LocalType {
case TYPE_TLS:
return TYPE_TLS
case TYPE_TCP:
return TYPE_TCP
case TYPE_KCP:
return TYPE_KCP
}
return "unknown"
}

View File

@ -1,386 +0,0 @@
package services
import (
"fmt"
"io"
"io/ioutil"
"log"
"net"
"proxy/utils"
"runtime/debug"
"strconv"
"strings"
"time"
"golang.org/x/crypto/ssh"
)
type HTTP struct {
outPool utils.OutPool
cfg HTTPArgs
checker utils.Checker
basicAuth utils.BasicAuth
sshClient *ssh.Client
lockChn chan bool
}
func NewHTTP() Service {
return &HTTP{
outPool: utils.OutPool{},
cfg: HTTPArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
lockChn: make(chan bool, 1),
}
}
func (s *HTTP) CheckArgs() {
var err error
if *s.cfg.Parent != "" && *s.cfg.ParentType == "" {
log.Fatalf("parent type unkown,use -T <tls|tcp|ssh>")
}
if *s.cfg.ParentType == "tls" || *s.cfg.LocalType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
if *s.cfg.ParentType == "ssh" {
if *s.cfg.SSHUser == "" {
log.Fatalf("ssh user required")
}
if *s.cfg.SSHKeyFile == "" && *s.cfg.SSHPassword == "" {
log.Fatalf("ssh password or key required")
}
if *s.cfg.SSHPassword != "" {
s.cfg.SSHAuthMethod = ssh.Password(*s.cfg.SSHPassword)
} else {
var SSHSigner ssh.Signer
s.cfg.SSHKeyBytes, err = ioutil.ReadFile(*s.cfg.SSHKeyFile)
if err != nil {
log.Fatalf("read key file ERR: %s", err)
}
if *s.cfg.SSHKeyFileSalt != "" {
SSHSigner, err = ssh.ParsePrivateKeyWithPassphrase(s.cfg.SSHKeyBytes, []byte(*s.cfg.SSHKeyFileSalt))
} else {
SSHSigner, err = ssh.ParsePrivateKey(s.cfg.SSHKeyBytes)
}
if err != nil {
log.Fatalf("parse ssh private key fail,ERR: %s", err)
}
s.cfg.SSHAuthMethod = ssh.PublicKeys(SSHSigner)
}
}
}
func (s *HTTP) InitService() {
s.InitBasicAuth()
if *s.cfg.Parent != "" {
s.checker = utils.NewChecker(*s.cfg.HTTPTimeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct)
}
if *s.cfg.ParentType == "ssh" {
err := s.ConnectSSH()
if err != nil {
log.Fatalf("init service fail, ERR: %s", err)
}
go func() {
//循环检查ssh网络连通性
for {
conn, err := utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout*2)
if err == nil {
_, err = conn.Write([]byte{0})
}
if err != nil {
if s.sshClient != nil {
s.sshClient.Close()
if s.sshClient.Conn != nil {
s.sshClient.Conn.Close()
}
}
log.Printf("ssh offline, retrying...")
s.ConnectSSH()
} else {
conn.Close()
}
time.Sleep(time.Second * 3)
}
}()
}
}
func (s *HTTP) StopService() {
if s.outPool.Pool != nil {
s.outPool.Pool.ReleaseAll()
}
}
func (s *HTTP) Start(args interface{}) (err error) {
s.cfg = args.(HTTPArgs)
s.CheckArgs()
if *s.cfg.Parent != "" {
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
s.InitOutConnPool()
}
s.InitService()
for _, addr := range strings.Split(*s.cfg.Local, ",") {
if addr != "" {
host, port, _ := net.SplitHostPort(addr)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p)
if *s.cfg.LocalType == TYPE_TCP {
err = sc.ListenTCP(s.callback)
} else if *s.cfg.LocalType == TYPE_TLS {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback)
} else if *s.cfg.LocalType == TYPE_KCP {
err = sc.ListenKCP(*s.cfg.KCPMethod, *s.cfg.KCPKey, s.callback)
}
if err != nil {
return
}
log.Printf("%s http(s) proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr())
}
}
return
}
func (s *HTTP) Clean() {
s.StopService()
}
func (s *HTTP) callback(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
log.Printf("http(s) conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var err interface{}
var req utils.HTTPRequest
req, err = utils.NewHTTPRequest(&inConn, 4096, s.IsBasicAuth(), &s.basicAuth)
if err != nil {
if err != io.EOF {
log.Printf("decoder error , from %s, ERR:%s", inConn.RemoteAddr(), err)
}
utils.CloseConn(&inConn)
return
}
address := req.Host
host, _, _ := net.SplitHostPort(address)
useProxy := false
if !utils.IsIternalIP(host) {
useProxy = true
if *s.cfg.Parent == "" {
useProxy = false
} else if *s.cfg.Always {
useProxy = true
} else {
s.checker.Add(address)
//var n, m uint
useProxy, _, _ = s.checker.IsBlocked(req.Host)
//log.Printf("blocked ? : %v, %s , fail:%d ,success:%d", useProxy, address, n, m)
}
}
log.Printf("use proxy : %v, %s", useProxy, address)
err = s.OutToTCP(useProxy, address, &inConn, &req)
if err != nil {
if *s.cfg.Parent == "" {
log.Printf("connect to %s fail, ERR:%s", address, err)
} else {
log.Printf("connect to %s parent %s fail", *s.cfg.ParentType, *s.cfg.Parent)
}
utils.CloseConn(&inConn)
}
}
func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (err interface{}) {
inAddr := (*inConn).RemoteAddr().String()
inLocalAddr := (*inConn).LocalAddr().String()
//防止死循环
if s.IsDeadLoop(inLocalAddr, req.Host) {
utils.CloseConn(inConn)
err = fmt.Errorf("dead loop detected , %s", req.Host)
return
}
var outConn net.Conn
var _outConn interface{}
tryCount := 0
maxTryCount := 5
for {
if useProxy {
if *s.cfg.ParentType == "ssh" {
outConn, err = s.getSSHConn(address)
} else {
//log.Printf("%v", s.outPool)
_outConn, err = s.outPool.Pool.Get()
if err == nil {
outConn = _outConn.(net.Conn)
}
}
} else {
outConn, err = utils.ConnectHost(address, *s.cfg.Timeout)
}
tryCount++
if err == nil || tryCount > maxTryCount {
break
} else {
log.Printf("connect to %s , err:%s,retrying...", *s.cfg.Parent, err)
time.Sleep(time.Second * 2)
}
}
if err != nil {
log.Printf("connect to %s , err:%s", *s.cfg.Parent, err)
utils.CloseConn(inConn)
return
}
outAddr := outConn.RemoteAddr().String()
//outLocalAddr := outConn.LocalAddr().String()
if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") {
//https无上级或者上级非代理,proxy需要响应connect请求,并直连目标
err = req.HTTPSReply()
} else {
//https或者http,上级是代理,proxy需要转发
_, err = outConn.Write(req.HeadBuf)
if err != nil {
log.Printf("write to %s , err:%s", *s.cfg.Parent, err)
utils.CloseConn(inConn)
return
}
}
utils.IoBind((*inConn), outConn, func(err interface{}) {
log.Printf("conn %s - %s released [%s]", inAddr, outAddr, req.Host)
})
log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, req.Host)
return
}
func (s *HTTP) getSSHConn(host string) (outConn net.Conn, err interface{}) {
maxTryCount := 1
tryCount := 0
RETRY:
if tryCount >= maxTryCount {
return
}
wait := make(chan bool, 1)
go func() {
defer func() {
if err == nil {
err = recover()
}
wait <- true
}()
outConn, err = s.sshClient.Dial("tcp", host)
}()
select {
case <-wait:
case <-time.After(time.Second * 5):
err = fmt.Errorf("ssh dial %s timeout", host)
}
if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err)
e := s.ConnectSSH()
if e == nil {
tryCount++
time.Sleep(time.Second * 3)
goto RETRY
} else {
err = e
}
}
return
}
func (s *HTTP) ConnectSSH() (err error) {
select {
case s.lockChn <- true:
default:
err = fmt.Errorf("can not connect at same time")
return
}
config := ssh.ClientConfig{
Timeout: time.Duration(*s.cfg.Timeout) * time.Millisecond,
User: *s.cfg.SSHUser,
Auth: []ssh.AuthMethod{s.cfg.SSHAuthMethod},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
if s.sshClient != nil {
s.sshClient.Close()
}
s.sshClient, err = ssh.Dial("tcp", *s.cfg.Parent, &config)
<-s.lockChn
return
}
func (s *HTTP) InitOutConnPool() {
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP || *s.cfg.ParentType == TYPE_KCP {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutPool(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType,
*s.cfg.KCPMethod,
*s.cfg.KCPKey,
s.cfg.CertBytes, s.cfg.KeyBytes,
*s.cfg.Parent,
*s.cfg.Timeout,
*s.cfg.PoolSize,
*s.cfg.PoolSize*2,
)
}
}
func (s *HTTP) InitBasicAuth() (err error) {
s.basicAuth = utils.NewBasicAuth()
if *s.cfg.AuthURL != "" {
s.basicAuth.SetAuthURL(*s.cfg.AuthURL, *s.cfg.AuthURLOkCode, *s.cfg.AuthURLTimeout, *s.cfg.AuthURLRetry)
log.Printf("auth from %s", *s.cfg.AuthURL)
}
if *s.cfg.AuthFile != "" {
var n = 0
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
if err != nil {
err = fmt.Errorf("auth-file ERR:%s", err)
return
}
log.Printf("auth data added from file %d , total:%d", n, s.basicAuth.Total())
}
if len(*s.cfg.Auth) > 0 {
n := s.basicAuth.Add(*s.cfg.Auth)
log.Printf("auth data added %d, total:%d", n, s.basicAuth.Total())
}
return
}
func (s *HTTP) IsBasicAuth() bool {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != ""
}
func (s *HTTP) IsDeadLoop(inLocalAddr string, host string) bool {
inIP, inPort, err := net.SplitHostPort(inLocalAddr)
if err != nil {
return false
}
outDomain, outPort, err := net.SplitHostPort(host)
if err != nil {
return false
}
if inPort == outPort {
var outIPs []net.IP
outIPs, err = net.LookupIP(outDomain)
if err == nil {
for _, ip := range outIPs {
if ip.String() == inIP {
return true
}
}
}
interfaceIPs, err := utils.GetAllInterfaceAddr()
for _, ip := range *s.cfg.LocalIPS {
interfaceIPs = append(interfaceIPs, net.ParseIP(ip).To4())
}
if err == nil {
for _, localIP := range interfaceIPs {
for _, outIP := range outIPs {
if localIP.Equal(outIP) {
return true
}
}
}
}
}
return false
}

638
services/http/http.go Normal file
View File

@ -0,0 +1,638 @@
package http
import (
"crypto/tls"
"fmt"
"io"
"io/ioutil"
logger "log"
"net"
"runtime/debug"
"strconv"
"strings"
"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/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"
"github.com/snail007/goproxy/utils/conncrypt"
"golang.org/x/crypto/ssh"
)
type HTTPArgs struct {
Parent *[]string
CertFile *string
KeyFile *string
CaCertFile *string
CaCertBytes []byte
CertBytes []byte
KeyBytes []byte
Local *string
Always *bool
HTTPTimeout *int
Interval *int
Blocked *string
Direct *string
AuthFile *string
Auth *[]string
AuthURL *string
AuthURLOkCode *int
AuthURLTimeout *int
AuthURLRetry *int
ParentType *string
LocalType *string
Timeout *int
CheckParentInterval *int
SSHKeyFile *string
SSHKeyFileSalt *string
SSHPassword *string
SSHUser *string
SSHKeyBytes []byte
SSHAuthMethod ssh.AuthMethod
KCP kcpcfg.KCPConfigArgs
LocalIPS *[]string
DNSAddress *string
DNSTTL *int
LocalKey *string
ParentKey *string
LocalCompress *bool
ParentCompress *bool
LoadBalanceMethod *string
LoadBalanceTimeout *int
LoadBalanceRetryTime *int
LoadBalanceHashTarget *bool
LoadBalanceOnlyHA *bool
RateLimit *string
RateLimitBytes float64
BindListen *bool
Debug *bool
}
type HTTP struct {
cfg HTTPArgs
checker utils.Checker
basicAuth utils.BasicAuth
sshClient *ssh.Client
lockChn chan bool
domainResolver dnsx.DomainResolver
isStop bool
serverChannels []*server.ServerChannel
userConns mapx.ConcurrentMap
log *logger.Logger
lb *lb.Group
}
func NewHTTP() services.Service {
return &HTTP{
cfg: HTTPArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
lockChn: make(chan bool, 1),
isStop: false,
serverChannels: []*server.ServerChannel{},
userConns: mapx.NewConcurrentMap(),
}
}
func (s *HTTP) CheckArgs() (err error) {
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>")
return
}
if *s.cfg.ParentType == "tls" || *s.cfg.LocalType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
if err != nil {
return
}
if *s.cfg.CaCertFile != "" {
s.cfg.CaCertBytes, err = ioutil.ReadFile(*s.cfg.CaCertFile)
if err != nil {
err = fmt.Errorf("read ca file error,ERR:%s", err)
return
}
}
}
if *s.cfg.ParentType == "ssh" {
if *s.cfg.SSHUser == "" {
err = fmt.Errorf("ssh user required")
return
}
if *s.cfg.SSHKeyFile == "" && *s.cfg.SSHPassword == "" {
err = fmt.Errorf("ssh password or key required")
return
}
if *s.cfg.SSHPassword != "" {
s.cfg.SSHAuthMethod = ssh.Password(*s.cfg.SSHPassword)
} else {
var SSHSigner ssh.Signer
s.cfg.SSHKeyBytes, err = ioutil.ReadFile(*s.cfg.SSHKeyFile)
if err != nil {
err = fmt.Errorf("read key file ERR: %s", err)
return
}
if *s.cfg.SSHKeyFileSalt != "" {
SSHSigner, err = ssh.ParsePrivateKeyWithPassphrase(s.cfg.SSHKeyBytes, []byte(*s.cfg.SSHKeyFileSalt))
} else {
SSHSigner, err = ssh.ParsePrivateKey(s.cfg.SSHKeyBytes)
}
if err != nil {
err = fmt.Errorf("parse ssh private key fail,ERR: %s", err)
return
}
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)
}
return
}
func (s *HTTP) InitService() (err error) {
s.InitBasicAuth()
//init lb
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.InitLB()
}
if *s.cfg.DNSAddress != "" {
s.domainResolver = dnsx.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log)
}
if *s.cfg.ParentType == "ssh" {
err = s.ConnectSSH()
if err != nil {
err = fmt.Errorf("init service fail, ERR: %s", err)
return
}
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
//循环检查ssh网络连通性
for {
if s.isStop {
return
}
conn, err := utils.ConnectHost(s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)), *s.cfg.Timeout*2)
if err == nil {
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write([]byte{0})
conn.SetDeadline(time.Time{})
}
if err != nil {
if s.sshClient != nil {
s.sshClient.Close()
if s.sshClient.Conn != nil {
s.sshClient.Conn.Close()
}
}
s.log.Printf("ssh offline, retrying...")
s.ConnectSSH()
} else {
conn.Close()
}
time.Sleep(time.Second * 3)
}
}()
}
return
}
func (s *HTTP) StopService() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop http(s) service crashed,%s", e)
} else {
s.log.Printf("service http(s) stoped")
}
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.serverChannels = nil
s.sshClient = nil
s.userConns = nil
s = nil
}()
s.isStop = true
if len(*s.cfg.Parent) > 0 {
s.checker.Stop()
}
if s.sshClient != nil {
s.sshClient.Close()
}
for _, sc := range s.serverChannels {
if sc.Listener != nil && *sc.Listener != nil {
(*sc.Listener).Close()
}
if sc.UDPListener != nil {
(*sc.UDPListener).Close()
}
}
if s.lb != nil {
s.lb.Stop()
}
}
func (s *HTTP) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(HTTPArgs)
if err = s.CheckArgs(); err != nil {
return
}
if err = s.InitService(); err != nil {
return
}
if len(*s.cfg.Parent) > 0 {
s.log.Printf("use %s parent %v [ %s ]", *s.cfg.ParentType, *s.cfg.Parent, strings.ToUpper(*s.cfg.LoadBalanceMethod))
}
for _, addr := range strings.Split(*s.cfg.Local, ",") {
if addr != "" {
host, port, _ := net.SplitHostPort(addr)
p, _ := strconv.Atoi(port)
sc := server.NewServerChannel(host, p, s.log)
if *s.cfg.LocalType == "tcp" {
err = sc.ListenTCP(s.callback)
} else if *s.cfg.LocalType == "tls" {
err = sc.ListenTLS(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.callback)
} else if *s.cfg.LocalType == "kcp" {
err = sc.ListenKCP(s.cfg.KCP, s.callback, s.log)
}
if err != nil {
return
}
s.log.Printf("%s http(s) proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr())
s.serverChannels = append(s.serverChannels, &sc)
}
}
return
}
func (s *HTTP) Clean() {
s.StopService()
}
func (s *HTTP) callback(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
s.log.Printf("http(s) conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
if *s.cfg.LocalCompress {
inConn = utils.NewCompConn(inConn)
}
if *s.cfg.LocalKey != "" {
inConn = conncrypt.New(inConn, &conncrypt.Config{
Password: *s.cfg.LocalKey,
})
}
var err interface{}
var req utils.HTTPRequest
req, err = utils.NewHTTPRequest(&inConn, 4096, s.IsBasicAuth(), &s.basicAuth, s.log)
if err != nil {
if err != io.EOF {
s.log.Printf("decoder error , from %s, ERR:%s", inConn.RemoteAddr(), err)
}
utils.CloseConn(&inConn)
return
}
address := req.Host
host, _, _ := net.SplitHostPort(address)
useProxy := false
if !utils.IsInternalIP(host, *s.cfg.Always) {
useProxy = true
if len(*s.cfg.Parent) == 0 {
useProxy = false
} else if *s.cfg.Always {
useProxy = true
} else {
var isInMap bool
useProxy, isInMap, _, _ = s.checker.IsBlocked(address)
if !isInMap {
s.checker.Add(address, s.Resolve(address))
}
//s.log.Printf("blocked ? : %v, %s , fail:%d ,success:%d", useProxy, address, n, m)
}
}
s.log.Printf("use proxy : %v, %s", useProxy, address)
lbAddr, err := s.OutToTCP(useProxy, address, &inConn, &req)
if err != nil {
if len(*s.cfg.Parent) == 0 {
s.log.Printf("connect to %s fail, ERR:%s", address, err)
} else {
s.log.Printf("connect to %s parent %v fail", *s.cfg.ParentType, lbAddr)
}
utils.CloseConn(&inConn)
}
}
func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (lbAddr string, err interface{}) {
inAddr := (*inConn).RemoteAddr().String()
inLocalAddr := (*inConn).LocalAddr().String()
//防止死循环
if s.IsDeadLoop(inLocalAddr, req.Host) {
utils.CloseConn(inConn)
err = fmt.Errorf("dead loop detected , %s", req.Host)
return
}
var outConn net.Conn
tryCount := 0
maxTryCount := 5
for {
if s.isStop {
return
}
if useProxy {
// s.log.Printf("%v", s.outPool)
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)
} else {
outConn, err = s.GetDirectConn(s.Resolve(address), inLocalAddr)
}
tryCount++
if err == nil || tryCount > maxTryCount {
break
} else {
s.log.Printf("connect to %s , err:%s,retrying...", lbAddr, err)
time.Sleep(time.Second * 2)
}
}
if err != nil {
s.log.Printf("connect to %s , err:%s", lbAddr, err)
utils.CloseConn(inConn)
return
}
if *s.cfg.ParentCompress {
outConn = utils.NewCompConn(outConn)
}
if *s.cfg.ParentKey != "" {
outConn = conncrypt.New(outConn, &conncrypt.Config{
Password: *s.cfg.ParentKey,
})
}
outAddr := outConn.RemoteAddr().String()
//outLocalAddr := outConn.LocalAddr().String()
if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") {
//https无上级或者上级非代理,proxy需要响应connect请求,并直连目标
err = req.HTTPSReply()
} else {
//https或者http,上级是代理,proxy需要转发
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
//直连目标或上级非代理或非SNI,,清理HTTP头部的代理头信息
if !useProxy || *s.cfg.ParentType == "ssh" && !req.IsSNI {
_, err = outConn.Write(utils.RemoveProxyHeaders(req.HeadBuf))
} else {
_, err = outConn.Write(req.HeadBuf)
}
outConn.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("write to %s , err:%s", lbAddr, err)
utils.CloseConn(inConn)
return
}
}
if s.cfg.RateLimitBytes > 0 {
outConn = iolimiter.NewReaderConn(outConn, s.cfg.RateLimitBytes)
}
utils.IoBind((*inConn), outConn, func(err interface{}) {
s.log.Printf("conn %s - %s released [%s]", inAddr, outAddr, req.Host)
s.userConns.Remove(inAddr)
if len(*s.cfg.Parent) > 0 {
s.lb.DecreaseConns(lbAddr)
}
}, s.log)
s.log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, req.Host)
if c, ok := s.userConns.Get(inAddr); ok {
(*c.(*net.Conn)).Close()
}
s.userConns.Set(inAddr, inConn)
if len(*s.cfg.Parent) > 0 {
s.lb.IncreasConns(lbAddr)
}
return
}
func (s *HTTP) getSSHConn(host string) (outConn net.Conn, err interface{}) {
maxTryCount := 1
tryCount := 0
RETRY:
if tryCount >= maxTryCount || s.isStop {
return
}
wait := make(chan bool, 1)
go func() {
defer func() {
if err == nil {
err = recover()
}
wait <- true
}()
outConn, err = s.sshClient.Dial("tcp", host)
}()
select {
case <-wait:
case <-time.After(time.Second * 5):
err = fmt.Errorf("ssh dial %s timeout", host)
}
if err != nil {
s.log.Printf("connect ssh fail, ERR: %s, retrying...", err)
e := s.ConnectSSH()
if e == nil {
tryCount++
time.Sleep(time.Second * 3)
goto RETRY
} else {
err = e
}
}
return
}
func (s *HTTP) ConnectSSH() (err error) {
select {
case s.lockChn <- true:
default:
err = fmt.Errorf("can not connect at same time")
return
}
config := ssh.ClientConfig{
Timeout: time.Duration(*s.cfg.Timeout) * time.Millisecond,
User: *s.cfg.SSHUser,
Auth: []ssh.AuthMethod{s.cfg.SSHAuthMethod},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
if s.sshClient != nil {
s.sshClient.Close()
}
s.sshClient, err = ssh.Dial("tcp", s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)), &config)
<-s.lockChn
return
}
func (s *HTTP) InitBasicAuth() (err error) {
if *s.cfg.DNSAddress != "" {
s.basicAuth = utils.NewBasicAuth(&(*s).domainResolver, s.log)
} else {
s.basicAuth = utils.NewBasicAuth(nil, s.log)
}
if *s.cfg.AuthURL != "" {
s.basicAuth.SetAuthURL(*s.cfg.AuthURL, *s.cfg.AuthURLOkCode, *s.cfg.AuthURLTimeout, *s.cfg.AuthURLRetry)
s.log.Printf("auth from %s", *s.cfg.AuthURL)
}
if *s.cfg.AuthFile != "" {
var n = 0
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
if err != nil {
err = fmt.Errorf("auth-file ERR:%s", err)
return
}
s.log.Printf("auth data added from file %d , total:%d", n, s.basicAuth.Total())
}
if len(*s.cfg.Auth) > 0 {
n := s.basicAuth.Add(*s.cfg.Auth)
s.log.Printf("auth data added %d, total:%d", n, s.basicAuth.Total())
}
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 {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != ""
}
func (s *HTTP) IsDeadLoop(inLocalAddr string, host string) bool {
inIP, inPort, err := net.SplitHostPort(inLocalAddr)
if err != nil {
return false
}
outDomain, outPort, err := net.SplitHostPort(host)
if err != nil {
return false
}
if inPort == outPort {
var outIPs []net.IP
if *s.cfg.DNSAddress != "" {
outIPs = []net.IP{net.ParseIP(s.Resolve(outDomain))}
} else {
outIPs, err = utils.LookupIP(outDomain)
}
if err == nil {
for _, ip := range outIPs {
if ip.String() == inIP {
return true
}
}
}
interfaceIPs, err := utils.GetAllInterfaceAddr()
for _, ip := range *s.cfg.LocalIPS {
interfaceIPs = append(interfaceIPs, net.ParseIP(ip).To4())
}
if err == nil {
for _, localIP := range interfaceIPs {
for _, outIP := range outIPs {
if localIP.Equal(outIP) {
return true
}
}
}
}
}
return false
}
func (s *HTTP) Resolve(address string) string {
if *s.cfg.DNSAddress == "" {
return address
}
ip, err := s.domainResolver.Resolve(address)
if err != nil {
s.log.Printf("dns error %s , ERR:%s", address, err)
return address
}
return ip
}
func (s *HTTP) GetParentConn(address string) (conn net.Conn, err error) {
if *s.cfg.ParentType == "tls" {
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 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 {
conn, err = utils.ConnectHost(address, *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
}

24
services/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
}

71
services/keygen/keygen.go Normal file
View File

@ -0,0 +1,71 @@
package keygen
import (
"fmt"
logger "log"
"os"
"strings"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/cert"
)
type KeygenArgs struct {
CaName *string
CertName *string
Sign *bool
SignDays *int
CommonName *string
}
type Keygen struct {
cfg KeygenArgs
log *logger.Logger
}
func NewKeygen() services.Service {
return &Keygen{}
}
func (s *Keygen) CheckArgs() (err error) {
if *s.cfg.Sign && (*s.cfg.CertName == "" || *s.cfg.CaName == "") {
err = fmt.Errorf("ca name and cert name required for signin")
return
}
if !*s.cfg.Sign && *s.cfg.CaName == "" {
err = fmt.Errorf("ca name required")
return
}
if *s.cfg.CommonName == "" {
domainSubfixList := []string{".com", ".edu", ".gov", ".int", ".mil", ".net", ".org", ".biz", ".info", ".pro", ".name", ".museum", ".coop", ".aero", ".xxx", ".idv", ".ac", ".ad", ".ae", ".af", ".ag", ".ai", ".al", ".am", ".an", ".ao", ".aq", ".ar", ".as", ".at", ".au", ".aw", ".az", ".ba", ".bb", ".bd", ".be", ".bf", ".bg", ".bh", ".bi", ".bj", ".bm", ".bn", ".bo", ".br", ".bs", ".bt", ".bv", ".bw", ".by", ".bz", ".ca", ".cc", ".cd", ".cf", ".cg", ".ch", ".ci", ".ck", ".cl", ".cm", ".cn", ".co", ".cr", ".cu", ".cv", ".cx", ".cy", ".cz", ".de", ".dj", ".dk", ".dm", ".do", ".dz", ".ec", ".ee", ".eg", ".eh", ".er", ".es", ".et", ".eu", ".fi", ".fj", ".fk", ".fm", ".fo", ".fr", ".ga", ".gd", ".ge", ".gf", ".gg", ".gh", ".gi", ".gl", ".gm", ".gn", ".gp", ".gq", ".gr", ".gs", ".gt", ".gu", ".gw", ".gy", ".hk", ".hm", ".hn", ".hr", ".ht", ".hu", ".id", ".ie", ".il", ".im", ".in", ".io", ".iq", ".ir", ".is", ".it", ".je", ".jm", ".jo", ".jp", ".ke", ".kg", ".kh", ".ki", ".km", ".kn", ".kp", ".kr", ".kw", ".ky", ".kz", ".la", ".lb", ".lc", ".li", ".lk", ".lr", ".ls", ".lt", ".lu", ".lv", ".ly", ".ma", ".mc", ".md", ".mg", ".mh", ".mk", ".ml", ".mm", ".mn", ".mo", ".mp", ".mq", ".mr", ".ms", ".mt", ".mu", ".mv", ".mw", ".mx", ".my", ".mz", ".na", ".nc", ".ne", ".nf", ".ng", ".ni", ".nl", ".no", ".np", ".nr", ".nu", ".nz", ".om", ".pa", ".pe", ".pf", ".pg", ".ph", ".pk", ".pl", ".pm", ".pn", ".pr", ".ps", ".pt", ".pw", ".py", ".qa", ".re", ".ro", ".ru", ".rw", ".sa", ".sb", ".sc", ".sd", ".se", ".sg", ".sh", ".si", ".sj", ".sk", ".sl", ".sm", ".sn", ".so", ".sr", ".st", ".sv", ".sy", ".sz", ".tc", ".td", ".tf", ".tg", ".th", ".tj", ".tk", ".tl", ".tm", ".tn", ".to", ".tp", ".tr", ".tt", ".tv", ".tw", ".tz", ".ua", ".ug", ".uk", ".um", ".us", ".uy", ".uz", ".va", ".vc", ".ve", ".vg", ".vi", ".vn", ".vu", ".wf", ".ws", ".ye", ".yt", ".yu", ".yr", ".za", ".zm", ".zw"}
CN := strings.ToLower(utils.RandString(int(utils.RandInt(4)%10)) + domainSubfixList[int(utils.RandInt(4))%len(domainSubfixList)])
*s.cfg.CommonName = CN
}
return
}
func (s *Keygen) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(KeygenArgs)
if err = s.CheckArgs(); err != nil {
return
}
if *s.cfg.Sign {
caCert, caKey, err := cert.ParseCertAndKey(*s.cfg.CaName+".crt", *s.cfg.CaName+".key")
if err != nil {
return err
}
err = cert.CreateSignCertToFile(caCert, caKey, *s.cfg.CommonName, *s.cfg.SignDays, *s.cfg.CertName)
} else {
err = cert.CreateCaToFile(*s.cfg.CaName, *s.cfg.CommonName, *s.cfg.SignDays)
}
if err != nil {
return
}
s.log.Println("success")
os.Exit(0)
return
}
func (s *Keygen) Clean() {
}

342
services/mux/mux_bridge.go Normal file
View File

@ -0,0 +1,342 @@
package mux
import (
"bufio"
"fmt"
"io"
logger "log"
"math/rand"
"net"
"runtime/debug"
"strings"
"sync"
"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/utils"
"github.com/snail007/goproxy/utils/mapx"
//"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux"
)
type MuxBridgeArgs struct {
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
LocalType *string
Timeout *int
IsCompress *bool
KCP kcpcfg.KCPConfigArgs
TCPSMethod *string
TCPSPassword *string
TOUMethod *string
TOUPassword *string
}
type MuxBridge struct {
cfg MuxBridgeArgs
clientControlConns mapx.ConcurrentMap
serverConns mapx.ConcurrentMap
router utils.ClientKeyRouter
l *sync.Mutex
isStop bool
sc *srvtransport.ServerChannel
log *logger.Logger
}
func NewMuxBridge() services.Service {
b := &MuxBridge{
cfg: MuxBridgeArgs{},
clientControlConns: mapx.NewConcurrentMap(),
serverConns: mapx.NewConcurrentMap(),
l: &sync.Mutex{},
isStop: false,
}
b.router = utils.NewClientKeyRouter(&b.clientControlConns, 50000)
return b
}
func (s *MuxBridge) InitService() (err error) {
return
}
func (s *MuxBridge) CheckArgs() (err error) {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
err = fmt.Errorf("cert and key file required")
return
}
if *s.cfg.LocalType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
if err != nil {
return
}
}
return
}
func (s *MuxBridge) StopService() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop bridge service crashed,%s", e)
} else {
s.log.Printf("service bridge stoped")
}
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
if s.sc != nil && (*s.sc).Listener != nil {
(*(*s.sc).Listener).Close()
}
for _, g := range s.clientControlConns.Items() {
for _, session := range g.(*mapx.ConcurrentMap).Items() {
(session.(*smux.Session)).Close()
}
}
for _, c := range s.serverConns.Items() {
(*c.(*net.Conn)).Close()
}
}
func (s *MuxBridge) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(MuxBridgeArgs)
if err = s.CheckArgs(); err != nil {
return
}
if err = s.InitService(); err != nil {
return
}
sc := srvtransport.NewServerChannelHost(*s.cfg.Local, s.log)
if *s.cfg.LocalType == "tcp" {
err = sc.ListenTCP(s.handler)
} else if *s.cfg.LocalType == "tls" {
err = sc.ListenTLS(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.handler)
} else if *s.cfg.LocalType == "kcp" {
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 {
return
}
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())
}
return
}
func (s *MuxBridge) Clean() {
s.StopService()
}
func (s *MuxBridge) handler(inConn net.Conn) {
reader := bufio.NewReader(inConn)
var err error
var connType uint8
var key string
inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
err = utils.ReadPacket(reader, &connType, &key)
inConn.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("read error,ERR:%s", err)
return
}
switch connType {
case CONN_SERVER:
var serverID string
inAddr := inConn.RemoteAddr().String()
inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
err = utils.ReadPacketData(reader, &serverID)
inConn.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("read error,ERR:%s", err)
return
}
s.log.Printf("server connection %s %s connected", serverID, key)
if c, ok := s.serverConns.Get(inAddr); ok {
(*c.(*net.Conn)).Close()
}
s.serverConns.Set(inAddr, &inConn)
session, err := smux.Server(inConn, nil)
if err != nil {
utils.CloseConn(&inConn)
s.log.Printf("server session error,ERR:%s", err)
return
}
for {
if s.isStop {
return
}
stream, err := session.AcceptStream()
if err != nil {
session.Close()
utils.CloseConn(&inConn)
s.serverConns.Remove(inAddr)
s.log.Printf("server connection %s %s released", serverID, key)
return
}
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("bridge callback crashed,err: %s", e)
}
}()
s.callback(stream, serverID, key)
}()
}
case CONN_CLIENT:
s.log.Printf("client connection %s connected", key)
session, err := smux.Client(inConn, nil)
if err != nil {
utils.CloseConn(&inConn)
s.log.Printf("client session error,ERR:%s", err)
return
}
keyInfo := strings.Split(key, "-")
if len(keyInfo) != 2 {
utils.CloseConn(&inConn)
s.log.Printf("client key format error,key:%s", key)
return
}
groupKey := keyInfo[0]
index := keyInfo[1]
s.l.Lock()
defer s.l.Unlock()
var group *mapx.ConcurrentMap
if !s.clientControlConns.Has(groupKey) {
_g := mapx.NewConcurrentMap()
group = &_g
s.clientControlConns.Set(groupKey, group)
//s.log.Printf("init client session group %s", groupKey)
} else {
_group, _ := s.clientControlConns.Get(groupKey)
group = _group.(*mapx.ConcurrentMap)
}
if v, ok := group.Get(index); ok {
v.(*smux.Session).Close()
}
group.Set(index, session)
//s.log.Printf("set client session %s to group %s,grouplen:%d", index, groupKey, group.Count())
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for {
if s.isStop {
return
}
if session.IsClosed() {
s.l.Lock()
defer s.l.Unlock()
if sess, ok := group.Get(index); ok && sess.(*smux.Session).IsClosed() {
group.Remove(index)
//s.log.Printf("client session %s removed from group %s, grouplen:%d", key, groupKey, group.Count())
s.log.Printf("client connection %s released", key)
}
if group.IsEmpty() {
s.clientControlConns.Remove(groupKey)
//s.log.Printf("client session group %s removed", groupKey)
}
break
}
time.Sleep(time.Second * 5)
}
}()
//s.log.Printf("set client session,key: %s", key)
}
}
func (s *MuxBridge) callback(inConn net.Conn, serverID, key string) {
try := 20
for {
if s.isStop {
return
}
try--
if try == 0 {
break
}
if key == "*" {
key = s.router.GetKey()
}
//s.log.Printf("server get client session %s", key)
_group, ok := s.clientControlConns.Get(key)
if !ok {
s.log.Printf("client %s session not exists for server stream %s, retrying...", key, serverID)
time.Sleep(time.Second * 3)
continue
}
group := _group.(*mapx.ConcurrentMap)
keys := []string{}
group.IterCb(func(key string, v interface{}) {
keys = append(keys, key)
})
keysLen := len(keys)
//s.log.Printf("client session %s , len:%d , keysLen: %d", key, group.Count(), keysLen)
i := 0
if keysLen > 0 {
i = rand.Intn(keysLen)
} else {
s.log.Printf("client %s session empty for server stream %s, retrying...", key, serverID)
time.Sleep(time.Second * 3)
continue
}
index := keys[i]
s.log.Printf("select client : %s-%s", key, index)
session, _ := group.Get(index)
//session.(*smux.Session).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
stream, err := session.(*smux.Session).OpenStream()
//session.(*smux.Session).SetDeadline(time.Time{})
if err != nil {
s.log.Printf("%s client session open stream %s fail, err: %s, retrying...", key, serverID, err)
time.Sleep(time.Second * 3)
continue
} else {
s.log.Printf("stream %s -> %s created", serverID, key)
die1 := make(chan bool, 1)
die2 := make(chan bool, 1)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
io.Copy(stream, inConn)
die1 <- true
}()
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
io.Copy(inConn, stream)
die2 <- true
}()
select {
case <-die1:
case <-die2:
}
stream.Close()
inConn.Close()
s.log.Printf("%s server %s stream released", key, serverID)
break
}
}
}

474
services/mux/mux_client.go Normal file
View File

@ -0,0 +1,474 @@
package mux
import (
"crypto/tls"
"fmt"
"io"
logger "log"
"net"
"runtime/debug"
"strings"
"sync"
"time"
"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/utils"
"github.com/snail007/goproxy/utils/jumper"
"github.com/snail007/goproxy/utils/mapx"
//"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux"
)
type MuxClientArgs struct {
Parent *string
ParentType *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Key *string
Timeout *int
IsCompress *bool
SessionCount *int
KCP kcpcfg.KCPConfigArgs
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 {
cfg MuxClientArgs
isStop bool
sessions mapx.ConcurrentMap
log *logger.Logger
jumper *jumper.Jumper
udpConns mapx.ConcurrentMap
}
func NewMuxClient() services.Service {
return &MuxClient{
cfg: MuxClientArgs{},
isStop: false,
sessions: mapx.NewConcurrentMap(),
udpConns: mapx.NewConcurrentMap(),
}
}
func (s *MuxClient) InitService() (err error) {
s.UDPGCDeamon()
return
}
func (s *MuxClient) CheckArgs() (err error) {
if *s.cfg.Parent != "" {
s.log.Printf("use tls parent %s", *s.cfg.Parent)
} else {
err = fmt.Errorf("parent required")
return
}
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
err = fmt.Errorf("cert and key file required")
return
}
if *s.cfg.ParentType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
if err != nil {
return
}
}
if *s.cfg.Jumper != "" {
if *s.cfg.ParentType != "tls" && *s.cfg.ParentType != "tcp" {
err = fmt.Errorf("jumper only worked of -T is tls or tcp")
return
}
var j jumper.Jumper
j, err = jumper.New(*s.cfg.Jumper, time.Millisecond*time.Duration(*s.cfg.Timeout))
if err != nil {
err = fmt.Errorf("parse jumper fail, err %s", err)
return
}
s.jumper = &j
}
return
}
func (s *MuxClient) StopService() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop client service crashed,%s", e)
} else {
s.log.Printf("service client stoped")
}
s.cfg = MuxClientArgs{}
s.jumper = nil
s.log = nil
s.sessions = nil
s.udpConns = nil
s = nil
}()
s.isStop = true
for _, sess := range s.sessions.Items() {
sess.(*smux.Session).Close()
}
}
func (s *MuxClient) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(MuxClientArgs)
if err = s.CheckArgs(); err != nil {
return
}
if err = s.InitService(); err != nil {
return
}
s.log.Printf("client started")
count := 1
if *s.cfg.SessionCount > 0 {
count = *s.cfg.SessionCount
}
for i := 1; i <= count; i++ {
key := fmt.Sprintf("worker[%d]", i)
s.log.Printf("session %s started", key)
go func(i int) {
defer func() {
e := recover()
if e != nil {
s.log.Printf("session worker crashed: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for {
if s.isStop {
return
}
conn, err := s.getParentConn()
if err != nil {
s.log.Printf("connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
continue
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
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)))
}()
g.Wait()
conn.SetDeadline(time.Time{})
if err != nil {
conn.Close()
s.log.Printf("connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
continue
}
session, err := smux.Server(conn, nil)
if err != nil {
s.log.Printf("session err: %s, retrying...", err)
conn.Close()
time.Sleep(time.Second * 3)
continue
}
if _sess, ok := s.sessions.Get(key); ok {
_sess.(*smux.Session).Close()
}
s.sessions.Set(key, session)
for {
if s.isStop {
return
}
stream, err := session.AcceptStream()
if err != nil {
s.log.Printf("accept stream err: %s, retrying...", err)
session.Close()
time.Sleep(time.Second * 3)
break
}
go func() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stream handler crashed: %s", e)
}
}()
var ID, clientLocalAddr, serverID string
stream.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
err = utils.ReadPacketData(stream, &ID, &clientLocalAddr, &serverID)
stream.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("read stream signal err: %s", err)
stream.Close()
return
}
//s.log.Printf("worker[%d] signal revecived,server %s stream %s %s", i, serverID, ID, clientLocalAddr)
protocol := clientLocalAddr[:3]
localAddr := clientLocalAddr[4:]
if protocol == "udp" {
s.ServeUDP(stream, localAddr, ID)
} else {
s.ServeConn(stream, localAddr, ID)
}
}()
}
}
}(i)
}
return
}
func (s *MuxClient) Clean() {
s.StopService()
}
func (s *MuxClient) getParentConn() (conn net.Conn, err error) {
if *s.cfg.ParentType == "tls" {
if s.jumper == nil {
var _conn tls.Conn
_conn, err = clienttransport.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil)
if err == nil {
conn = net.Conn(&_conn)
}
} else {
conf, err := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, nil)
if err != nil {
return nil, err
}
var _c net.Conn
_c, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout))
if err == nil {
conn = net.Conn(tls.Client(_c, conf))
}
}
} else if *s.cfg.ParentType == "kcp" {
conn, err = 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 {
if s.jumper == nil {
conn, err = clienttransport.TCPConnectHost(*s.cfg.Parent, *s.cfg.Timeout)
} else {
conn, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout))
}
}
return
}
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 {
if s.isStop {
return
}
srcAddr, body, err = utils.ReadUDPPacket(inConn)
if err != nil {
if strings.Contains(err.Error(), "n != int(") {
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()
return
}
item = &ClientUDPConnItem{
conn: inConn,
srcAddr: _srcAddr,
localAddr: _localAddr,
udpConn: c,
connid: ID,
}
s.udpConns.Set(srcAddr, item)
s.UDPRevecive(srcAddr, ID)
} else {
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() {
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", 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) 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.(*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)
}
})
for _, k := range gcKeys {
s.udpConns.Remove(k)
}
gcKeys = nil
}
}()
}
func (s *MuxClient) ServeConn(inConn *smux.Stream, localAddr, ID string) {
var err error
var outConn net.Conn
i := 0
for {
if s.isStop {
return
}
i++
outConn, err = utils.ConnectHost(localAddr, *s.cfg.Timeout)
if err == nil || i == 3 {
break
} else {
if i == 3 {
s.log.Printf("connect to %s err: %s, retrying...", localAddr, err)
time.Sleep(2 * time.Second)
continue
}
}
}
if err != nil {
inConn.Close()
utils.CloseConn(&outConn)
s.log.Printf("build connection error, err: %s", err)
return
}
s.log.Printf("stream %s created", ID)
if *s.cfg.IsCompress {
die1 := make(chan bool, 1)
die2 := make(chan bool, 1)
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))
die1 <- true
}()
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)
die2 <- true
}()
select {
case <-die1:
case <-die2:
}
outConn.Close()
inConn.Close()
s.log.Printf("%s stream %s released", *s.cfg.Key, ID)
} else {
utils.IoBind(inConn, outConn, func(err interface{}) {
s.log.Printf("stream %s released", ID)
}, s.log)
}
}

602
services/mux/mux_server.go Normal file
View File

@ -0,0 +1,602 @@
package mux
import (
"crypto/tls"
"fmt"
"io"
logger "log"
"math/rand"
"net"
"runtime/debug"
"strconv"
"strings"
"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/utils"
"github.com/snail007/goproxy/utils/jumper"
"github.com/snail007/goproxy/utils/mapx"
"github.com/golang/snappy"
//"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux"
)
const (
CONN_CLIENT_CONTROL = uint8(1)
CONN_SERVER = uint8(4)
CONN_CLIENT = uint8(5)
)
type MuxServerArgs struct {
Parent *string
ParentType *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
IsUDP *bool
Key *string
Remote *string
Timeout *int
Route *[]string
Mgr *MuxServerManager
IsCompress *bool
SessionCount *int
KCP kcpcfg.KCPConfigArgs
Jumper *string
TCPSMethod *string
TCPSPassword *string
TOUMethod *string
TOUPassword *string
}
type MuxServer struct {
cfg MuxServerArgs
sc server.ServerChannel
sessions mapx.ConcurrentMap
lockChn chan bool
isStop bool
log *logger.Logger
jumper *jumper.Jumper
udpConns mapx.ConcurrentMap
}
type MuxServerManager struct {
cfg MuxServerArgs
serverID string
servers []*services.Service
log *logger.Logger
}
func NewMuxServerManager() services.Service {
return &MuxServerManager{
cfg: MuxServerArgs{},
serverID: utils.Uniqueid(),
servers: []*services.Service{},
}
}
func (s *MuxServerManager) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(MuxServerArgs)
if err = s.CheckArgs(); err != nil {
return
}
if *s.cfg.Parent != "" {
s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
} else {
err = fmt.Errorf("parent required")
return
}
if err = s.InitService(); err != nil {
return
}
s.log.Printf("server id: %s", s.serverID)
//s.log.Printf("route:%v", *s.cfg.Route)
for _, _info := range *s.cfg.Route {
if _info == "" {
continue
}
IsUDP := *s.cfg.IsUDP
if strings.HasPrefix(_info, "udp://") {
IsUDP = true
}
info := strings.TrimPrefix(_info, "udp://")
info = strings.TrimPrefix(info, "tcp://")
_routeInfo := strings.Split(info, "@")
server := NewMuxServer()
local := _routeInfo[0]
remote := _routeInfo[1]
KEY := *s.cfg.Key
if strings.HasPrefix(remote, "[") {
KEY = remote[1:strings.LastIndex(remote, "]")]
remote = remote[strings.LastIndex(remote, "]")+1:]
}
if strings.HasPrefix(remote, ":") {
remote = fmt.Sprintf("127.0.0.1%s", remote)
}
err = server.Start(MuxServerArgs{
CertBytes: s.cfg.CertBytes,
KeyBytes: s.cfg.KeyBytes,
Parent: s.cfg.Parent,
CertFile: s.cfg.CertFile,
KeyFile: s.cfg.KeyFile,
Local: &local,
IsUDP: &IsUDP,
Remote: &remote,
Key: &KEY,
Timeout: s.cfg.Timeout,
Mgr: s,
IsCompress: s.cfg.IsCompress,
SessionCount: s.cfg.SessionCount,
KCP: s.cfg.KCP,
ParentType: s.cfg.ParentType,
Jumper: s.cfg.Jumper,
TCPSMethod: s.cfg.TCPSMethod,
TCPSPassword: s.cfg.TCPSPassword,
TOUMethod: s.cfg.TOUMethod,
TOUPassword: s.cfg.TOUPassword,
}, log)
if err != nil {
return
}
s.servers = append(s.servers, &server)
}
return
}
func (s *MuxServerManager) Clean() {
s.StopService()
}
func (s *MuxServerManager) StopService() {
for _, server := range s.servers {
(*server).Clean()
}
s.cfg = MuxServerArgs{}
s.log = nil
s.serverID = ""
s.servers = nil
s = nil
}
func (s *MuxServerManager) CheckArgs() (err error) {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
err = fmt.Errorf("cert and key file required")
return
}
if *s.cfg.ParentType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
if err != nil {
return
}
}
return
}
func (s *MuxServerManager) InitService() (err error) {
return
}
func NewMuxServer() services.Service {
return &MuxServer{
cfg: MuxServerArgs{},
lockChn: make(chan bool, 1),
sessions: mapx.NewConcurrentMap(),
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() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop server service crashed,%s", e)
} else {
s.log.Printf("service server stoped")
}
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
for _, sess := range s.sessions.Items() {
sess.(*smux.Session).Close()
}
if s.sc.Listener != nil {
(*s.sc.Listener).Close()
}
if s.sc.UDPListener != nil {
(*s.sc.UDPListener).Close()
}
}
func (s *MuxServer) InitService() (err error) {
s.UDPGCDeamon()
return
}
func (s *MuxServer) CheckArgs() (err error) {
if *s.cfg.Remote == "" {
err = fmt.Errorf("remote required")
return
}
if *s.cfg.Jumper != "" {
if *s.cfg.ParentType != "tls" && *s.cfg.ParentType != "tcp" {
err = fmt.Errorf("jumper only worked of -T is tls or tcp")
return
}
var j jumper.Jumper
j, err = jumper.New(*s.cfg.Jumper, time.Millisecond*time.Duration(*s.cfg.Timeout))
if err != nil {
err = fmt.Errorf("parse jumper fail, err %s", err)
return
}
s.jumper = &j
}
return
}
func (s *MuxServer) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(MuxServerArgs)
if err = s.CheckArgs(); err != nil {
return
}
if err = s.InitService(); err != nil {
return
}
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
s.sc = server.NewServerChannel(host, p, s.log)
if *s.cfg.IsUDP {
err = s.sc.ListenUDP(func(listener *net.UDPConn, packet []byte, localAddr, srcAddr *net.UDPAddr) {
s.UDPSend(packet, localAddr, srcAddr)
})
if err != nil {
return
}
s.log.Printf("server on %s", (*s.sc.UDPListener).LocalAddr())
} else {
err = s.sc.ListenTCP(func(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
s.log.Printf("connection handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var outConn net.Conn
var ID string
for {
if s.isStop {
return
}
outConn, ID, err = s.GetOutConn()
if err != nil {
utils.CloseConn(&outConn)
s.log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
s.log.Printf("%s stream %s created", *s.cfg.Key, ID)
if *s.cfg.IsCompress {
die1 := make(chan bool, 1)
die2 := make(chan bool, 1)
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))
die1 <- true
}()
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)
die2 <- true
}()
select {
case <-die1:
case <-die2:
}
outConn.Close()
inConn.Close()
s.log.Printf("%s stream %s released", *s.cfg.Key, ID)
} else {
utils.IoBind(inConn, outConn, func(err interface{}) {
s.log.Printf("%s stream %s released", *s.cfg.Key, ID)
}, s.log)
}
})
if err != nil {
return
}
s.log.Printf("server on %s", (*s.sc.Listener).Addr())
}
return
}
func (s *MuxServer) Clean() {
s.StopService()
}
func (s *MuxServer) GetOutConn() (outConn net.Conn, ID string, err error) {
i := 1
if *s.cfg.SessionCount > 0 {
i = rand.Intn(*s.cfg.SessionCount)
}
outConn, err = s.GetConn(fmt.Sprintf("%d", i))
if err != nil {
if !strings.Contains(err.Error(), "can not connect at same time") {
s.log.Printf("connection err: %s", err)
}
return
}
remoteAddr := "tcp:" + *s.cfg.Remote
if *s.cfg.IsUDP {
remoteAddr = "udp:" + *s.cfg.Remote
}
ID = utils.Uniqueid()
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = outConn.Write(utils.BuildPacketData(ID, remoteAddr, s.cfg.Mgr.serverID))
outConn.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("write stream data err: %s ,retrying...", err)
utils.CloseConn(&outConn)
return
}
return
}
func (s *MuxServer) GetConn(index string) (conn net.Conn, err error) {
select {
case s.lockChn <- true:
default:
err = fmt.Errorf("can not connect at same time")
return
}
defer func() {
<-s.lockChn
}()
var session *smux.Session
_session, ok := s.sessions.Get(index)
if !ok {
var c net.Conn
c, err = s.getParentConn()
if err != nil {
return
}
c.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = c.Write(utils.BuildPacket(CONN_SERVER, *s.cfg.Key, s.cfg.Mgr.serverID))
c.SetDeadline(time.Time{})
if err != nil {
c.Close()
return
}
if err == nil {
session, err = smux.Client(c, nil)
if err != nil {
return
}
}
if _sess, ok := s.sessions.Get(index); ok {
_sess.(*smux.Session).Close()
}
s.sessions.Set(index, session)
s.log.Printf("session[%s] created", index)
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack()))
}
}()
for {
if s.isStop {
return
}
if session.IsClosed() {
s.sessions.Remove(index)
break
}
time.Sleep(time.Second * 5)
}
}()
} else {
session = _session.(*smux.Session)
}
conn, err = session.OpenStream()
if err != nil {
session.Close()
s.sessions.Remove(index)
}
return
}
func (s *MuxServer) getParentConn() (conn net.Conn, err error) {
if *s.cfg.ParentType == "tls" {
if s.jumper == nil {
var _conn tls.Conn
_conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil)
if err == nil {
conn = net.Conn(&_conn)
}
} else {
conf, err := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, nil)
if err != nil {
return nil, err
}
var _c net.Conn
_c, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout))
if err == nil {
conn = net.Conn(tls.Client(_c, conf))
}
}
} else if *s.cfg.ParentType == "kcp" {
conn, err = utils.ConnectKCPHost(*s.cfg.Parent, s.cfg.KCP)
} else if *s.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 {
if s.jumper == nil {
conn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout)
} else {
conn, err = s.jumper.Dial(*s.cfg.Parent, time.Millisecond*time.Duration(*s.cfg.Timeout))
}
}
return
}
func (s *MuxServer) 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.(*MuxUDPConnItem).touchtime > gctime {
(*(v.(*MuxUDPConnItem).conn)).Close()
gcKeys = append(gcKeys, key)
s.log.Printf("gc udp conn %s", v.(*MuxUDPConnItem).connid)
}
})
for _, k := range gcKeys {
s.udpConns.Remove(k)
}
gcKeys = nil
}
}()
}
func (s *MuxServer) UDPSend(data []byte, localAddr, srcAddr *net.UDPAddr) {
var (
uc *MuxUDPConnItem
key = srcAddr.String()
ID string
err error
outconn net.Conn
)
v, ok := s.udpConns.Get(key)
if !ok {
for {
outconn, ID, err = s.GetOutConn()
if err != nil && strings.Contains(err.Error(), "can not connect at same time") {
time.Sleep(time.Millisecond * 500)
continue
} else {
break
}
}
if err != nil {
s.log.Printf("connect to %s fail, err: %s", *s.cfg.Parent, err)
return
}
uc = &MuxUDPConnItem{
conn: &outconn,
srcAddr: srcAddr,
localAddr: localAddr,
connid: ID,
}
s.udpConns.Set(key, uc)
s.UDPRevecive(key, ID)
} else {
uc = v.(*MuxUDPConnItem)
}
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()
(*uc.conn).SetWriteDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, 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)
}
}()
}
func (s *MuxServer) UDPRevecive(key, ID 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", 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

@ -1,155 +0,0 @@
package services
import (
"bufio"
"io"
"log"
"net"
"proxy/utils"
"strconv"
"time"
"github.com/xtaci/smux"
)
type MuxBridge struct {
cfg MuxBridgeArgs
clientControlConns utils.ConcurrentMap
}
func NewMuxBridge() Service {
return &MuxBridge{
cfg: MuxBridgeArgs{},
clientControlConns: utils.NewConcurrentMap(),
}
}
func (s *MuxBridge) InitService() {
}
func (s *MuxBridge) CheckArgs() {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
log.Fatalf("cert and key file required")
}
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
func (s *MuxBridge) StopService() {
}
func (s *MuxBridge) Start(args interface{}) (err error) {
s.cfg = args.(MuxBridgeArgs)
s.CheckArgs()
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p)
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, func(inConn net.Conn) {
reader := bufio.NewReader(inConn)
var err error
var connType uint8
var key string
err = utils.ReadPacket(reader, &connType, &key)
if err != nil {
log.Printf("read error,ERR:%s", err)
return
}
switch connType {
case CONN_SERVER:
var serverID string
err = utils.ReadPacketData(reader, &serverID)
if err != nil {
log.Printf("read error,ERR:%s", err)
return
}
log.Printf("server connection %s %s connected", serverID, key)
session, err := smux.Server(inConn, nil)
if err != nil {
utils.CloseConn(&inConn)
log.Printf("server session error,ERR:%s", err)
return
}
for {
stream, err := session.AcceptStream()
if err != nil {
session.Close()
utils.CloseConn(&inConn)
return
}
go s.callback(stream, serverID, key)
}
case CONN_CLIENT:
log.Printf("client connection %s connected", key)
session, err := smux.Client(inConn, nil)
if err != nil {
utils.CloseConn(&inConn)
log.Printf("client session error,ERR:%s", err)
return
}
s.clientControlConns.Set(key, session)
go func() {
for {
if session.IsClosed() {
s.clientControlConns.Remove(key)
break
}
time.Sleep(time.Second * 5)
}
}()
//log.Printf("set client session,key: %s", key)
}
})
if err != nil {
return
}
log.Printf("proxy on mux bridge mode %s", (*sc.Listener).Addr())
return
}
func (s *MuxBridge) Clean() {
s.StopService()
}
func (s *MuxBridge) callback(inConn net.Conn, serverID, key string) {
try := 20
for {
try--
if try == 0 {
break
}
session, ok := s.clientControlConns.Get(key)
if !ok {
log.Printf("client %s session not exists for server stream %s", key, serverID)
time.Sleep(time.Second * 3)
continue
}
stream, err := session.(*smux.Session).OpenStream()
if err != nil {
log.Printf("%s client session open stream %s fail, err: %s, retrying...", key, serverID, err)
time.Sleep(time.Second * 3)
continue
} else {
log.Printf("%s server %s stream created", key, serverID)
die1 := make(chan bool, 1)
die2 := make(chan bool, 1)
go func() {
io.Copy(stream, inConn)
die1 <- true
}()
go func() {
io.Copy(inConn, stream)
die2 <- true
}()
select {
case <-die1:
case <-die2:
}
stream.Close()
inConn.Close()
log.Printf("%s server %s stream released", key, serverID)
break
}
}
}

View File

@ -1,206 +0,0 @@
package services
import (
"crypto/tls"
"io"
"log"
"net"
"proxy/utils"
"time"
"github.com/golang/snappy"
"github.com/xtaci/smux"
)
type MuxClient struct {
cfg MuxClientArgs
}
func NewMuxClient() Service {
return &MuxClient{
cfg: MuxClientArgs{},
}
}
func (s *MuxClient) InitService() {
}
func (s *MuxClient) CheckArgs() {
if *s.cfg.Parent != "" {
log.Printf("use tls parent %s", *s.cfg.Parent)
} else {
log.Fatalf("parent required")
}
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
log.Fatalf("cert and key file required")
}
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
func (s *MuxClient) StopService() {
}
func (s *MuxClient) Start(args interface{}) (err error) {
s.cfg = args.(MuxClientArgs)
s.CheckArgs()
s.InitService()
log.Printf("proxy on mux client mode, compress %v", *s.cfg.IsCompress)
for {
var _conn tls.Conn
_conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes)
if err != nil {
log.Printf("connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
continue
}
conn := net.Conn(&_conn)
_, err = conn.Write(utils.BuildPacket(CONN_CLIENT, *s.cfg.Key))
if err != nil {
conn.Close()
log.Printf("connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
continue
}
session, err := smux.Server(conn, nil)
if err != nil {
log.Printf("session err: %s, retrying...", err)
conn.Close()
time.Sleep(time.Second * 3)
continue
}
for {
stream, err := session.AcceptStream()
if err != nil {
log.Printf("accept stream err: %s, retrying...", err)
session.Close()
time.Sleep(time.Second * 3)
break
}
go func() {
var ID, clientLocalAddr, serverID string
err = utils.ReadPacketData(stream, &ID, &clientLocalAddr, &serverID)
if err != nil {
log.Printf("read stream signal err: %s", err)
stream.Close()
return
}
log.Printf("signal revecived,server %s stream %s %s", serverID, ID, clientLocalAddr)
protocol := clientLocalAddr[:3]
localAddr := clientLocalAddr[4:]
if protocol == "udp" {
s.ServeUDP(stream, localAddr, ID)
} else {
s.ServeConn(stream, localAddr, ID)
}
}()
}
}
}
func (s *MuxClient) Clean() {
s.StopService()
}
func (s *MuxClient) ServeUDP(inConn *smux.Stream, localAddr, ID string) {
for {
srcAddr, body, err := utils.ReadUDPPacket(inConn)
if err != nil {
log.Printf("udp packet revecived fail, err: %s", err)
log.Printf("connection %s released", ID)
inConn.Close()
break
} else {
//log.Printf("udp packet revecived:%s,%v", srcAddr, body)
go s.processUDPPacket(inConn, srcAddr, localAddr, body)
}
}
// }
}
func (s *MuxClient) processUDPPacket(inConn *smux.Stream, srcAddr, localAddr string, body []byte) {
dstAddr, err := net.ResolveUDPAddr("udp", localAddr)
if err != nil {
log.Printf("can't resolve address: %s", err)
inConn.Close()
return
}
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr)
if err != nil {
log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
return
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write(body)
if err != nil {
log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
return
}
//log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 1024)
length, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return
}
respBody := buf[0:length]
//log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
bs := utils.UDPPacket(srcAddr, respBody)
_, err = (*inConn).Write(bs)
if err != nil {
log.Printf("send udp response fail ,ERR:%s", err)
inConn.Close()
return
}
//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) {
var err error
var outConn net.Conn
i := 0
for {
i++
outConn, err = utils.ConnectHost(localAddr, *s.cfg.Timeout)
if err == nil || i == 3 {
break
} else {
if i == 3 {
log.Printf("connect to %s err: %s, retrying...", localAddr, err)
time.Sleep(2 * time.Second)
continue
}
}
}
if err != nil {
inConn.Close()
utils.CloseConn(&outConn)
log.Printf("build connection error, err: %s", err)
return
}
log.Printf("stream %s created", ID)
if *s.cfg.IsCompress {
die1 := make(chan bool, 1)
die2 := make(chan bool, 1)
go func() {
io.Copy(outConn, snappy.NewReader(inConn))
die1 <- true
}()
go func() {
io.Copy(snappy.NewWriter(inConn), outConn)
die2 <- true
}()
select {
case <-die1:
case <-die2:
}
outConn.Close()
inConn.Close()
log.Printf("%s stream %s released", *s.cfg.Key, ID)
} else {
utils.IoBind(inConn, outConn, func(err interface{}) {
log.Printf("stream %s released", ID)
})
}
}

View File

@ -1,333 +0,0 @@
package services
import (
"crypto/tls"
"fmt"
"io"
"log"
"net"
"proxy/utils"
"runtime/debug"
"strconv"
"strings"
"time"
"github.com/golang/snappy"
"github.com/xtaci/smux"
)
type MuxServer struct {
cfg MuxServerArgs
udpChn chan MuxUDPItem
sc utils.ServerChannel
session *smux.Session
lockChn chan bool
}
type MuxServerManager struct {
cfg MuxServerArgs
udpChn chan MuxUDPItem
sc utils.ServerChannel
serverID string
}
func NewMuxServerManager() Service {
return &MuxServerManager{
cfg: MuxServerArgs{},
udpChn: make(chan MuxUDPItem, 50000),
serverID: utils.Uniqueid(),
}
}
func (s *MuxServerManager) Start(args interface{}) (err error) {
s.cfg = args.(MuxServerArgs)
s.CheckArgs()
if *s.cfg.Parent != "" {
log.Printf("use tls parent %s", *s.cfg.Parent)
} else {
log.Fatalf("parent required")
}
s.InitService()
log.Printf("server id: %s", s.serverID)
//log.Printf("route:%v", *s.cfg.Route)
for _, _info := range *s.cfg.Route {
if _info == "" {
continue
}
IsUDP := *s.cfg.IsUDP
if strings.HasPrefix(_info, "udp://") {
IsUDP = true
}
info := strings.TrimPrefix(_info, "udp://")
info = strings.TrimPrefix(info, "tcp://")
_routeInfo := strings.Split(info, "@")
server := NewMuxServer()
local := _routeInfo[0]
remote := _routeInfo[1]
KEY := *s.cfg.Key
if strings.HasPrefix(remote, "[") {
KEY = remote[1:strings.LastIndex(remote, "]")]
remote = remote[strings.LastIndex(remote, "]")+1:]
}
if strings.HasPrefix(remote, ":") {
remote = fmt.Sprintf("127.0.0.1%s", remote)
}
err = server.Start(MuxServerArgs{
CertBytes: s.cfg.CertBytes,
KeyBytes: s.cfg.KeyBytes,
Parent: s.cfg.Parent,
CertFile: s.cfg.CertFile,
KeyFile: s.cfg.KeyFile,
Local: &local,
IsUDP: &IsUDP,
Remote: &remote,
Key: &KEY,
Timeout: s.cfg.Timeout,
Mgr: s,
IsCompress: s.cfg.IsCompress,
})
if err != nil {
return
}
}
return
}
func (s *MuxServerManager) Clean() {
s.StopService()
}
func (s *MuxServerManager) StopService() {
}
func (s *MuxServerManager) CheckArgs() {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
log.Fatalf("cert and key file required")
}
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
func (s *MuxServerManager) InitService() {
}
func NewMuxServer() Service {
return &MuxServer{
cfg: MuxServerArgs{},
udpChn: make(chan MuxUDPItem, 50000),
lockChn: make(chan bool, 1),
}
}
type MuxUDPItem struct {
packet *[]byte
localAddr *net.UDPAddr
srcAddr *net.UDPAddr
}
func (s *MuxServer) InitService() {
s.UDPConnDeamon()
}
func (s *MuxServer) CheckArgs() {
if *s.cfg.Remote == "" {
log.Fatalf("remote required")
}
}
func (s *MuxServer) Start(args interface{}) (err error) {
s.cfg = args.(MuxServerArgs)
s.CheckArgs()
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
s.sc = utils.NewServerChannel(host, p)
if *s.cfg.IsUDP {
err = s.sc.ListenUDP(func(packet []byte, localAddr, srcAddr *net.UDPAddr) {
s.udpChn <- MuxUDPItem{
packet: &packet,
localAddr: localAddr,
srcAddr: srcAddr,
}
})
if err != nil {
return
}
log.Printf("proxy on udp mux server mode %s", (*s.sc.UDPListener).LocalAddr())
} else {
err = s.sc.ListenTCP(func(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
log.Printf("connection handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var outConn net.Conn
var ID string
for {
outConn, ID, err = s.GetOutConn()
if err != nil {
utils.CloseConn(&outConn)
log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
log.Printf("%s stream %s created", *s.cfg.Key, ID)
if *s.cfg.IsCompress {
die1 := make(chan bool, 1)
die2 := make(chan bool, 1)
go func() {
io.Copy(inConn, snappy.NewReader(outConn))
die1 <- true
}()
go func() {
io.Copy(snappy.NewWriter(outConn), inConn)
die2 <- true
}()
select {
case <-die1:
case <-die2:
}
outConn.Close()
inConn.Close()
log.Printf("%s stream %s released", *s.cfg.Key, ID)
} else {
utils.IoBind(inConn, outConn, func(err interface{}) {
log.Printf("%s stream %s released", *s.cfg.Key, ID)
})
}
})
if err != nil {
return
}
log.Printf("proxy on mux server mode %s, compress %v", (*s.sc.Listener).Addr(), *s.cfg.IsCompress)
}
return
}
func (s *MuxServer) Clean() {
}
func (s *MuxServer) GetOutConn() (outConn net.Conn, ID string, err error) {
outConn, err = s.GetConn()
if err != nil {
log.Printf("connection err: %s", err)
return
}
remoteAddr := "tcp:" + *s.cfg.Remote
if *s.cfg.IsUDP {
remoteAddr = "udp:" + *s.cfg.Remote
}
ID = utils.Uniqueid()
_, err = outConn.Write(utils.BuildPacketData(ID, remoteAddr, s.cfg.Mgr.serverID))
if err != nil {
log.Printf("write stream data err: %s ,retrying...", err)
utils.CloseConn(&outConn)
return
}
return
}
func (s *MuxServer) GetConn() (conn net.Conn, err error) {
select {
case s.lockChn <- true:
default:
err = fmt.Errorf("can not connect at same time")
return
}
defer func() {
<-s.lockChn
}()
if s.session == nil {
var _conn tls.Conn
_conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes)
if err != nil {
s.session = nil
return
}
c := net.Conn(&_conn)
_, err = c.Write(utils.BuildPacket(CONN_SERVER, *s.cfg.Key, s.cfg.Mgr.serverID))
if err != nil {
c.Close()
s.session = nil
return
}
if err == nil {
s.session, err = smux.Client(c, nil)
if err != nil {
s.session = nil
return
}
}
}
conn, err = s.session.OpenStream()
if err != nil {
s.session.Close()
s.session = nil
}
return
}
func (s *MuxServer) UDPConnDeamon() {
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("udp conn deamon crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var outConn net.Conn
var ID string
var err error
for {
item := <-s.udpChn
RETRY:
if outConn == nil {
for {
outConn, ID, err = s.GetOutConn()
if err != nil {
outConn = nil
utils.CloseConn(&outConn)
log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err)
time.Sleep(time.Second * 3)
continue
} else {
go func(outConn net.Conn, ID string) {
go func() {
// outConn.Close()
}()
for {
srcAddrFromConn, body, err := utils.ReadUDPPacket(outConn)
if err != nil {
log.Printf("parse revecived udp packet fail, err: %s ,%v", err, body)
log.Printf("UDP deamon connection %s exited", ID)
break
}
//log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn)
_srcAddr := strings.Split(srcAddrFromConn, ":")
if len(_srcAddr) != 2 {
log.Printf("parse revecived udp packet fail, addr error : %s", srcAddrFromConn)
continue
}
port, _ := strconv.Atoi(_srcAddr[1])
dstAddr := &net.UDPAddr{IP: net.ParseIP(_srcAddr[0]), Port: port}
_, err = s.sc.UDPListener.WriteToUDP(body, dstAddr)
if err != nil {
log.Printf("udp response to local %s fail,ERR:%s", srcAddrFromConn, err)
continue
}
//log.Printf("udp response to local %s success , %v", srcAddrFromConn, body)
}
}(outConn, ID)
break
}
}
}
outConn.SetWriteDeadline(time.Now().Add(time.Second))
_, err = outConn.Write(utils.UDPPacket(item.srcAddr.String(), *item.packet))
outConn.SetWriteDeadline(time.Time{})
if err != nil {
utils.CloseConn(&outConn)
outConn = nil
log.Printf("write udp packet to %s fail ,flush err:%s ,retrying...", *s.cfg.Parent, err)
goto RETRY
}
//log.Printf("write packet %v", *item.packet)
}
}()
}

View File

@ -2,50 +2,65 @@ package services
import (
"fmt"
"log"
logger "log"
"runtime/debug"
"sync"
)
type Service interface {
Start(args interface{}) (err error)
Start(args interface{}, log *logger.Logger) (err error)
Clean()
}
type ServiceItem struct {
S Service
Args interface{}
Name string
Log *logger.Logger
}
var servicesMap = map[string]*ServiceItem{}
var servicesMap = sync.Map{}
func Regist(name string, s Service, args interface{}) {
servicesMap[name] = &ServiceItem{
func Regist(name string, s Service, args interface{}, log *logger.Logger) {
Stop(name)
servicesMap.Store(name, &ServiceItem{
S: s,
Args: args,
Name: name,
Log: log,
})
}
func GetService(name string) *ServiceItem {
if s, ok := servicesMap.Load(name); ok && s.(*ServiceItem).S != nil {
return s.(*ServiceItem)
}
return nil
}
func Stop(name string) {
if s, ok := servicesMap.Load(name); ok && s.(*ServiceItem).S != nil {
s.(*ServiceItem).S.Clean()
servicesMap.Delete(name)
}
}
func Run(name string, args ...interface{}) (service *ServiceItem, err error) {
service, ok := servicesMap[name]
func Run(name string, args interface{}) (service *ServiceItem, err error) {
_service, ok := servicesMap.Load(name)
if ok {
go func() {
defer func() {
err := recover()
if err != nil {
log.Fatalf("%s servcie crashed, ERR: %s\ntrace:%s", name, err, string(debug.Stack()))
}
}()
if len(args) == 1 {
err = service.S.Start(args[0])
} else {
err = service.S.Start(service.Args)
}
if err != nil {
log.Fatalf("%s servcie fail, ERR: %s", name, err)
defer func() {
e := recover()
if e != nil {
err = fmt.Errorf("%s servcie crashed, ERR: %s\ntrace:%s", name, e, string(debug.Stack()))
}
}()
}
if !ok {
service = _service.(*ServiceItem)
if args != nil {
err = service.S.Start(args, service.Log)
} else {
err = service.S.Start(service.Args, service.Log)
}
if err != nil {
err = fmt.Errorf("%s servcie fail, ERR: %s", name, err)
}
} else {
err = fmt.Errorf("service %s not found", name)
}
return

View File

@ -1,627 +0,0 @@
package services
import (
"crypto/tls"
"fmt"
"io/ioutil"
"log"
"net"
"proxy/utils"
"proxy/utils/aes"
"proxy/utils/socks"
"runtime/debug"
"strings"
"time"
"golang.org/x/crypto/ssh"
)
type Socks struct {
cfg SocksArgs
checker utils.Checker
basicAuth utils.BasicAuth
sshClient *ssh.Client
lockChn chan bool
udpSC utils.ServerChannel
}
func NewSocks() Service {
return &Socks{
cfg: SocksArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
lockChn: make(chan bool, 1),
}
}
func (s *Socks) CheckArgs() {
var err error
if *s.cfg.LocalType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
if *s.cfg.Parent != "" {
if *s.cfg.ParentType == "" {
log.Fatalf("parent type unkown,use -T <tls|tcp|ssh>")
}
if *s.cfg.ParentType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
if *s.cfg.ParentType == "ssh" {
if *s.cfg.SSHUser == "" {
log.Fatalf("ssh user required")
}
if *s.cfg.SSHKeyFile == "" && *s.cfg.SSHPassword == "" {
log.Fatalf("ssh password or key required")
}
if *s.cfg.SSHPassword != "" {
s.cfg.SSHAuthMethod = ssh.Password(*s.cfg.SSHPassword)
} else {
var SSHSigner ssh.Signer
s.cfg.SSHKeyBytes, err = ioutil.ReadFile(*s.cfg.SSHKeyFile)
if err != nil {
log.Fatalf("read key file ERR: %s", err)
}
if *s.cfg.SSHKeyFileSalt != "" {
SSHSigner, err = ssh.ParsePrivateKeyWithPassphrase(s.cfg.SSHKeyBytes, []byte(*s.cfg.SSHKeyFileSalt))
} else {
SSHSigner, err = ssh.ParsePrivateKey(s.cfg.SSHKeyBytes)
}
if err != nil {
log.Fatalf("parse ssh private key fail,ERR: %s", err)
}
s.cfg.SSHAuthMethod = ssh.PublicKeys(SSHSigner)
}
}
}
}
func (s *Socks) InitService() {
s.InitBasicAuth()
s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct)
if *s.cfg.ParentType == "ssh" {
err := s.ConnectSSH()
if err != nil {
log.Fatalf("init service fail, ERR: %s", err)
}
go func() {
//循环检查ssh网络连通性
for {
conn, err := utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout*2)
if err == nil {
_, err = conn.Write([]byte{0})
}
if err != nil {
if s.sshClient != nil {
s.sshClient.Close()
}
log.Printf("ssh offline, retrying...")
s.ConnectSSH()
} else {
conn.Close()
}
time.Sleep(time.Second * 3)
}
}()
}
if *s.cfg.ParentType == "ssh" {
log.Println("warn: socks udp not suppored for ssh")
} else {
s.udpSC = utils.NewServerChannelHost(*s.cfg.UDPLocal)
err := s.udpSC.ListenUDP(s.udpCallback)
if err != nil {
log.Fatalf("init udp service fail, ERR: %s", err)
}
log.Printf("udp socks proxy on %s", s.udpSC.UDPListener.LocalAddr())
}
}
func (s *Socks) StopService() {
if s.sshClient != nil {
s.sshClient.Close()
}
if s.udpSC.UDPListener != nil {
s.udpSC.UDPListener.Close()
}
}
func (s *Socks) Start(args interface{}) (err error) {
//start()
s.cfg = args.(SocksArgs)
s.CheckArgs()
s.InitService()
if *s.cfg.Parent != "" {
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
}
sc := utils.NewServerChannelHost(*s.cfg.Local)
if *s.cfg.LocalType == TYPE_TCP {
err = sc.ListenTCP(s.socksConnCallback)
} else if *s.cfg.LocalType == TYPE_TLS {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.socksConnCallback)
} else if *s.cfg.LocalType == TYPE_KCP {
err = sc.ListenKCP(*s.cfg.KCPMethod, *s.cfg.KCPKey, s.socksConnCallback)
}
if err != nil {
return
}
log.Printf("%s socks proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr())
return
}
func (s *Socks) Clean() {
s.StopService()
}
func (s *Socks) UDPKey() []byte {
return s.cfg.KeyBytes[:32]
}
func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) {
rawB := b
var err error
if *s.cfg.LocalType == "tls" {
//decode b
rawB, err = goaes.Decrypt(s.UDPKey(), b)
if err != nil {
log.Printf("decrypt udp packet fail from %s", srcAddr.String())
return
}
}
p, err := socks.ParseUDPPacket(rawB)
log.Printf("udp revecived:%v", len(p.Data()))
if err != nil {
log.Printf("parse udp packet fail, ERR:%s", err)
return
}
//防止死循环
if s.IsDeadLoop((*localAddr).String(), p.Host()) {
log.Printf("dead loop detected , %s", p.Host())
return
}
//log.Printf("##########udp to -> %s:%s###########", p.Host(), p.Port())
if *s.cfg.Parent != "" {
//有上级代理,转发给上级
if *s.cfg.ParentType == "tls" {
//encode b
rawB, err = goaes.Encrypt(s.UDPKey(), rawB)
if err != nil {
log.Printf("encrypt udp data fail to %s", *s.cfg.Parent)
return
}
}
parent := *s.cfg.UDPParent
if parent == "" {
parent = *s.cfg.Parent
}
dstAddr, err := net.ResolveUDPAddr("udp", parent)
if err != nil {
log.Printf("can't resolve address: %s", err)
return
}
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr)
if err != nil {
log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
return
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*5)))
_, err = conn.Write(rawB)
log.Printf("udp request:%v", len(rawB))
if err != nil {
log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
conn.Close()
return
}
//log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 10*1024)
length, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
conn.Close()
return
}
respBody := buf[0:length]
log.Printf("udp response:%v", len(respBody))
//log.Printf("revecived udp packet from %s", dstAddr.String())
if *s.cfg.ParentType == "tls" {
//decode b
respBody, err = goaes.Decrypt(s.UDPKey(), respBody)
if err != nil {
log.Printf("encrypt udp data fail to %s", *s.cfg.Parent)
conn.Close()
return
}
}
if *s.cfg.LocalType == "tls" {
d, err := goaes.Encrypt(s.UDPKey(), respBody)
if err != nil {
log.Printf("encrypt udp data fail from %s", dstAddr.String())
conn.Close()
return
}
s.udpSC.UDPListener.WriteToUDP(d, srcAddr)
log.Printf("udp reply:%v", len(d))
} else {
s.udpSC.UDPListener.WriteToUDP(respBody, srcAddr)
log.Printf("udp reply:%v", len(respBody))
}
} else {
//本地代理
dstAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(p.Host(), p.Port()))
if err != nil {
log.Printf("can't resolve address: %s", err)
return
}
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr)
if err != nil {
log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
return
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*3)))
_, err = conn.Write(p.Data())
log.Printf("udp send:%v", len(p.Data()))
if err != nil {
log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
conn.Close()
return
}
//log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 10*1024)
length, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
conn.Close()
return
}
respBody := buf[0:length]
//封装来自真实服务器的数据,返回给访问者
respPacket := p.NewReply(respBody)
//log.Printf("revecived udp packet from %s", dstAddr.String())
if *s.cfg.LocalType == "tls" {
d, err := goaes.Encrypt(s.UDPKey(), respPacket)
if err != nil {
log.Printf("encrypt udp data fail from %s", dstAddr.String())
conn.Close()
return
}
s.udpSC.UDPListener.WriteToUDP(d, srcAddr)
} else {
s.udpSC.UDPListener.WriteToUDP(respPacket, srcAddr)
}
log.Printf("udp reply:%v", len(respPacket))
}
}
func (s *Socks) socksConnCallback(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
log.Printf("socks conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
inConn.Close()
}
}()
//协商开始
//method select request
inConn.SetReadDeadline(time.Now().Add(time.Second * 3))
methodReq, err := socks.NewMethodsRequest(inConn)
inConn.SetReadDeadline(time.Time{})
if err != nil {
methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn)
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)
log.Printf("none method found : Method_NO_AUTH")
return
}
//method select reply
err = methodReq.Reply(socks.Method_NO_AUTH)
if err != nil {
log.Printf("reply answer data fail,ERR: %s", err)
utils.CloseConn(&inConn)
return
}
// log.Printf("% x", methodReq.Bytes())
} else {
//auth
if !methodReq.Select(socks.Method_USER_PASS) {
methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn)
log.Printf("none method found : Method_USER_PASS")
return
}
//method reply need auth
err = methodReq.Reply(socks.Method_USER_PASS)
if err != nil {
log.Printf("reply answer data fail,ERR: %s", err)
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:])
//log.Printf("user:%s,pass:%s", user, pass)
//auth
_addr := strings.Split(inConn.RemoteAddr().String(), ":")
if s.basicAuth.CheckUserPass(user, pass, _addr[0], "") {
inConn.Write([]byte{0x01, 0x00})
} else {
inConn.Write([]byte{0x01, 0x01})
utils.CloseConn(&inConn)
return
}
}
//request detail
request, err := socks.NewRequest(inConn)
if err != nil {
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) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) {
if *s.cfg.ParentType == "ssh" {
utils.CloseConn(inConn)
return
}
host, _, _ := net.SplitHostPort((*inConn).LocalAddr().String())
_, port, _ := net.SplitHostPort(s.udpSC.UDPListener.LocalAddr().String())
log.Printf("proxy udp on %s", net.JoinHostPort(host, port))
request.UDPReply(socks.REP_SUCCESS, net.JoinHostPort(host, port))
}
func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) {
var outConn net.Conn
var err interface{}
useProxy := true
tryCount := 0
maxTryCount := 5
//防止死循环
if s.IsDeadLoop((*inConn).LocalAddr().String(), request.Host()) {
utils.CloseConn(inConn)
log.Printf("dead loop detected , %s", request.Host())
utils.CloseConn(inConn)
return
}
for {
if *s.cfg.Always {
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr())
} else {
if *s.cfg.Parent != "" {
host, _, _ := net.SplitHostPort(request.Addr())
useProxy := false
if utils.IsIternalIP(host) {
useProxy = false
} else {
s.checker.Add(request.Addr())
useProxy, _, _ = s.checker.IsBlocked(request.Addr())
}
if useProxy {
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr())
} else {
outConn, err = utils.ConnectHost(request.Addr(), *s.cfg.Timeout)
}
} else {
outConn, err = utils.ConnectHost(request.Addr(), *s.cfg.Timeout)
useProxy = false
}
}
tryCount++
if err == nil || tryCount > maxTryCount || *s.cfg.Parent == "" {
break
} else {
log.Printf("get out conn fail,%s,retrying...", err)
time.Sleep(time.Second * 2)
}
}
if err != nil {
log.Printf("get out conn fail,%s", err)
request.TCPReply(socks.REP_NETWOR_UNREACHABLE)
return
}
log.Printf("use proxy %v : %s", useProxy, request.Addr())
request.TCPReply(socks.REP_SUCCESS)
inAddr := (*inConn).RemoteAddr().String()
//inLocalAddr := (*inConn).LocalAddr().String()
log.Printf("conn %s - %s connected", inAddr, request.Addr())
utils.IoBind(*inConn, outConn, func(err interface{}) {
log.Printf("conn %s - %s released", inAddr, request.Addr())
})
}
func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn net.Conn, err interface{}) {
switch *s.cfg.ParentType {
case "kcp":
fallthrough
case "tls":
fallthrough
case "tcp":
if *s.cfg.ParentType == "tls" {
var _outConn tls.Conn
_outConn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes)
outConn = net.Conn(&_outConn)
} else if *s.cfg.ParentType == "kcp" {
outConn, err = utils.ConnectKCPHost(*s.cfg.Parent, *s.cfg.KCPMethod, *s.cfg.KCPKey)
} else {
outConn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout)
}
if err != nil {
err = fmt.Errorf("connect fail,%s", err)
return
}
var buf = make([]byte, 1024)
//var n int
_, err = outConn.Write(methodBytes)
if err != nil {
err = fmt.Errorf("write method fail,%s", err)
return
}
_, err = outConn.Read(buf)
if err != nil {
err = fmt.Errorf("read method reply fail,%s", err)
return
}
//resp := buf[:n]
//log.Printf("resp:%v", resp)
_, err = outConn.Write(reqBytes)
if err != nil {
err = fmt.Errorf("write req detail fail,%s", err)
return
}
_, err = outConn.Read(buf)
if err != nil {
err = fmt.Errorf("read req reply fail,%s", err)
return
}
//result := buf[:n]
//log.Printf("result:%v", result)
case "ssh":
maxTryCount := 1
tryCount := 0
RETRY:
if tryCount >= maxTryCount {
return
}
wait := make(chan bool, 1)
go func() {
defer func() {
if err == nil {
err = recover()
}
wait <- true
}()
outConn, err = s.sshClient.Dial("tcp", host)
}()
select {
case <-wait:
case <-time.After(time.Millisecond * time.Duration(*s.cfg.Timeout) * 2):
err = fmt.Errorf("ssh dial %s timeout", host)
s.sshClient.Close()
}
if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err)
e := s.ConnectSSH()
if e == nil {
tryCount++
time.Sleep(time.Second * 3)
goto RETRY
} else {
err = e
}
}
}
return
}
func (s *Socks) ConnectSSH() (err error) {
select {
case s.lockChn <- true:
default:
err = fmt.Errorf("can not connect at same time")
return
}
config := ssh.ClientConfig{
Timeout: time.Duration(*s.cfg.Timeout) * time.Millisecond,
User: *s.cfg.SSHUser,
Auth: []ssh.AuthMethod{s.cfg.SSHAuthMethod},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
if s.sshClient != nil {
s.sshClient.Close()
}
s.sshClient, err = ssh.Dial("tcp", *s.cfg.Parent, &config)
<-s.lockChn
return
}
func (s *Socks) InitBasicAuth() (err error) {
s.basicAuth = utils.NewBasicAuth()
if *s.cfg.AuthURL != "" {
s.basicAuth.SetAuthURL(*s.cfg.AuthURL, *s.cfg.AuthURLOkCode, *s.cfg.AuthURLTimeout, *s.cfg.AuthURLRetry)
log.Printf("auth from %s", *s.cfg.AuthURL)
}
if *s.cfg.AuthFile != "" {
var n = 0
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
if err != nil {
err = fmt.Errorf("auth-file ERR:%s", err)
return
}
log.Printf("auth data added from file %d , total:%d", n, s.basicAuth.Total())
}
if len(*s.cfg.Auth) > 0 {
n := s.basicAuth.Add(*s.cfg.Auth)
log.Printf("auth data added %d, total:%d", n, s.basicAuth.Total())
}
return
}
func (s *Socks) IsBasicAuth() bool {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != ""
}
func (s *Socks) IsDeadLoop(inLocalAddr string, host string) bool {
inIP, inPort, err := net.SplitHostPort(inLocalAddr)
if err != nil {
return false
}
outDomain, outPort, err := net.SplitHostPort(host)
if err != nil {
return false
}
if inPort == outPort {
var outIPs []net.IP
outIPs, err = net.LookupIP(outDomain)
if err == nil {
for _, ip := range outIPs {
if ip.String() == inIP {
return true
}
}
}
interfaceIPs, err := utils.GetAllInterfaceAddr()
for _, ip := range *s.cfg.LocalIPS {
interfaceIPs = append(interfaceIPs, net.ParseIP(ip).To4())
}
if err == nil {
for _, localIP := range interfaceIPs {
for _, outIP := range outIPs {
if localIP.Equal(outIP) {
return true
}
}
}
}
}
return false
}

682
services/socks/socks.go Normal file
View File

@ -0,0 +1,682 @@
package socks
import (
"crypto/tls"
"fmt"
"io"
"io/ioutil"
logger "log"
"net"
"runtime/debug"
"strconv"
"strings"
"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/utils"
"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"
"golang.org/x/crypto/ssh"
)
type SocksArgs struct {
Parent *[]string
ParentType *string
Local *string
LocalType *string
CertFile *string
KeyFile *string
CaCertFile *string
CaCertBytes []byte
CertBytes []byte
KeyBytes []byte
SSHKeyFile *string
SSHKeyFileSalt *string
SSHPassword *string
SSHUser *string
SSHKeyBytes []byte
SSHAuthMethod ssh.AuthMethod
Timeout *int
Always *bool
Interval *int
Blocked *string
Direct *string
ParentAuth *string
AuthFile *string
Auth *[]string
AuthURL *string
AuthURLOkCode *int
AuthURLTimeout *int
AuthURLRetry *int
KCP kcpcfg.KCPConfigArgs
LocalIPS *[]string
DNSAddress *string
DNSTTL *int
LocalKey *string
ParentKey *string
LocalCompress *bool
ParentCompress *bool
LoadBalanceMethod *string
LoadBalanceTimeout *int
LoadBalanceRetryTime *int
LoadBalanceHashTarget *bool
LoadBalanceOnlyHA *bool
RateLimit *string
RateLimitBytes float64
BindListen *bool
Debug *bool
}
type Socks struct {
cfg SocksArgs
checker utils.Checker
basicAuth utils.BasicAuth
sshClient *ssh.Client
lockChn chan bool
udpSC server.ServerChannel
sc *server.ServerChannel
domainResolver dnsx.DomainResolver
isStop bool
userConns mapx.ConcurrentMap
log *logger.Logger
lb *lb.Group
udpRelatedPacketConns mapx.ConcurrentMap
udpLocalKey []byte
udpParentKey []byte
}
func NewSocks() services.Service {
return &Socks{
cfg: SocksArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
lockChn: make(chan bool, 1),
isStop: false,
userConns: mapx.NewConcurrentMap(),
udpRelatedPacketConns: mapx.NewConcurrentMap(),
}
}
func (s *Socks) CheckArgs() (err error) {
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)
if err != nil {
return
}
if *s.cfg.CaCertFile != "" {
s.cfg.CaCertBytes, err = ioutil.ReadFile(*s.cfg.CaCertFile)
if err != nil {
err = fmt.Errorf("read ca file error,ERR:%s", err)
return
}
}
}
if len(*s.cfg.Parent) == 1 && (*s.cfg.Parent)[0] == "" {
(*s.cfg.Parent) = []string{}
}
if len(*s.cfg.Parent) > 0 {
if *s.cfg.ParentType == "" {
err = fmt.Errorf("parent type unkown,use -T <tls|tcp|ssh|kcp>")
return
}
if *s.cfg.ParentType == "ssh" {
if *s.cfg.SSHUser == "" {
err = fmt.Errorf("ssh user required")
return
}
if *s.cfg.SSHKeyFile == "" && *s.cfg.SSHPassword == "" {
err = fmt.Errorf("ssh password or key required")
return
}
if *s.cfg.SSHPassword != "" {
s.cfg.SSHAuthMethod = ssh.Password(*s.cfg.SSHPassword)
} else {
var SSHSigner ssh.Signer
s.cfg.SSHKeyBytes, err = ioutil.ReadFile(*s.cfg.SSHKeyFile)
if err != nil {
err = fmt.Errorf("read key file ERR: %s", err)
return
}
if *s.cfg.SSHKeyFileSalt != "" {
SSHSigner, err = ssh.ParsePrivateKeyWithPassphrase(s.cfg.SSHKeyBytes, []byte(*s.cfg.SSHKeyFileSalt))
} else {
SSHSigner, err = ssh.ParsePrivateKey(s.cfg.SSHKeyBytes)
}
if err != nil {
err = fmt.Errorf("parse ssh private key fail,ERR: %s", err)
return
}
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)
}
s.udpLocalKey = s.LocalUDPKey()
s.udpParentKey = s.ParentUDPKey()
return
}
func (s *Socks) InitService() (err error) {
s.InitBasicAuth()
if *s.cfg.DNSAddress != "" {
(*s).domainResolver = dnsx.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log)
}
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.InitLB()
}
if *s.cfg.ParentType == "ssh" {
e := s.ConnectSSH(s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)))
if e != nil {
err = fmt.Errorf("init service fail, ERR: %s", e)
return
}
go func() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("crashed, err: %s\nstack:", e, string(debug.Stack()))
}
}()
//循环检查ssh网络连通性
for {
if s.isStop {
return
}
conn, err := utils.ConnectHost(s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)), *s.cfg.Timeout*2)
if err == nil {
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write([]byte{0})
conn.SetDeadline(time.Time{})
}
if err != nil {
if s.sshClient != nil {
s.sshClient.Close()
}
s.log.Printf("ssh offline, retrying...")
s.ConnectSSH(s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)))
} else {
conn.Close()
}
time.Sleep(time.Second * 3)
}
}()
}
if *s.cfg.ParentType == "ssh" {
s.log.Printf("warn: socks udp not suppored for ssh")
}
return
}
func (s *Socks) StopService() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop socks service crashed,%s", e)
} else {
s.log.Printf("service socks stoped")
}
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
if len(*s.cfg.Parent) > 0 {
s.checker.Stop()
}
if s.sshClient != nil {
s.sshClient.Close()
}
if s.udpSC.UDPListener != nil {
s.udpSC.UDPListener.Close()
}
if s.sc != nil && (*s.sc).Listener != nil {
(*(*s.sc).Listener).Close()
}
for _, c := range s.userConns.Items() {
(*c.(*net.Conn)).Close()
}
if s.lb != nil {
s.lb.Stop()
}
for _, c := range s.udpRelatedPacketConns.Items() {
(*c.(*net.UDPConn)).Close()
}
}
func (s *Socks) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
//start()
s.cfg = args.(SocksArgs)
if err = s.CheckArgs(); err != nil {
return
}
if err = s.InitService(); err != nil {
s.InitService()
}
if len(*s.cfg.Parent) > 0 {
s.log.Printf("use %s parent %v [ %s ]", *s.cfg.ParentType, *s.cfg.Parent, strings.ToUpper(*s.cfg.LoadBalanceMethod))
}
sc := server.NewServerChannelHost(*s.cfg.Local, s.log)
if *s.cfg.LocalType == "tcp" {
err = sc.ListenTCP(s.socksConnCallback)
} else if *s.cfg.LocalType == "tls" {
err = sc.ListenTLS(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.socksConnCallback)
} else if *s.cfg.LocalType == "kcp" {
err = sc.ListenKCP(s.cfg.KCP, s.socksConnCallback, s.log)
}
if err != nil {
return
}
s.sc = &sc
s.log.Printf("%s socks proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr())
return
}
func (s *Socks) Clean() {
s.StopService()
}
func (s *Socks) socksConnCallback(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
s.log.Printf("socks conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
inConn.Close()
}
}()
if *s.cfg.LocalCompress {
inConn = utils.NewCompConn(inConn)
}
if *s.cfg.LocalKey != "" {
inConn = conncrypt.New(inConn, &conncrypt.Config{
Password: *s.cfg.LocalKey,
})
}
//socks5 server
var serverConn *socks.ServerConn
udpIP, _, _ := net.SplitHostPort(inConn.LocalAddr().String())
if s.IsBasicAuth() {
serverConn = socks.NewServerConn(&inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), &s.basicAuth, true, udpIP, nil)
} else {
serverConn = socks.NewServerConn(&inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, true, udpIP, nil)
}
if err := serverConn.Handshake(); err != nil {
if !strings.HasSuffix(err.Error(), "EOF") {
s.log.Printf("handshake fail, ERR: %s", err)
}
inConn.Close()
return
}
if serverConn.IsUDP() {
s.proxyUDP(&inConn, serverConn)
} else if serverConn.IsTCP() {
s.proxyTCP(&inConn, serverConn)
}
}
func (s *Socks) proxyTCP(inConn *net.Conn, serverConn *socks.ServerConn) {
var outConn net.Conn
var err interface{}
lbAddr := ""
useProxy := true
tryCount := 0
maxTryCount := 5
//防止死循环
if s.IsDeadLoop((*inConn).LocalAddr().String(), serverConn.Host()) {
utils.CloseConn(inConn)
s.log.Printf("dead loop detected , %s", serverConn.Host())
utils.CloseConn(inConn)
return
}
for {
if s.isStop {
return
}
if *s.cfg.Always {
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 {
if len(*s.cfg.Parent) > 0 {
host, _, _ := net.SplitHostPort(serverConn.Target())
useProxy := false
if utils.IsInternalIP(host, *s.cfg.Always) {
useProxy = false
} else {
var isInMap bool
useProxy, isInMap, _, _ = s.checker.IsBlocked(serverConn.Target())
if !isInMap {
s.checker.Add(serverConn.Target(), s.Resolve(serverConn.Target()))
}
}
if useProxy {
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 {
s.log.Printf("handshake fail, %s", err)
return
}
} else {
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
}
}
tryCount++
if err == nil || tryCount > maxTryCount || len(*s.cfg.Parent) == 0 {
break
} else {
s.log.Printf("get out conn fail,%s,retrying...", err)
time.Sleep(time.Second * 2)
}
}
if err != nil {
s.log.Printf("get out conn fail,%s", err)
return
}
s.log.Printf("use proxy %v : %s", useProxy, serverConn.Target())
inAddr := (*inConn).RemoteAddr().String()
//outRemoteAddr := outConn.RemoteAddr().String()
//inLocalAddr := (*inConn).LocalAddr().String()
if s.cfg.RateLimitBytes > 0 {
outConn = iolimiter.NewReaderConn(outConn, s.cfg.RateLimitBytes)
}
utils.IoBind(*inConn, outConn, func(err interface{}) {
s.log.Printf("conn %s - %s released", inAddr, serverConn.Target())
s.userConns.Remove(inAddr)
if len(*s.cfg.Parent) > 0 {
s.lb.DecreaseConns(lbAddr)
}
}, s.log)
if c, ok := s.userConns.Get(inAddr); ok {
(*c.(*net.Conn)).Close()
s.userConns.Remove(inAddr)
}
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) GetParentConn(parentAddress string, serverConn *socks.ServerConn) (outConn net.Conn, err interface{}) {
switch *s.cfg.ParentType {
case "kcp", "tls", "tcp":
if *s.cfg.ParentType == "tls" {
var _conn tls.Conn
_conn, err = utils.TlsConnectHost(parentAddress, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes)
if err == nil {
outConn = net.Conn(&_conn)
}
} else if *s.cfg.ParentType == "kcp" {
outConn, err = utils.ConnectKCPHost(parentAddress, s.cfg.KCP)
} else {
outConn, err = utils.ConnectHost(parentAddress, *s.cfg.Timeout)
}
if err != nil {
err = fmt.Errorf("connect fail,%s", err)
return
}
if *s.cfg.ParentCompress {
outConn = utils.NewCompConn(outConn)
}
if *s.cfg.ParentKey != "" {
outConn = conncrypt.New(outConn, &conncrypt.Config{
Password: *s.cfg.ParentKey,
})
}
case "ssh":
maxTryCount := 1
tryCount := 0
RETRY:
if tryCount >= maxTryCount || s.isStop {
return
}
wait := make(chan bool, 1)
go func() {
defer func() {
if err == nil {
err = recover()
}
wait <- true
}()
outConn, err = s.sshClient.Dial("tcp", serverConn.Target())
}()
select {
case <-wait:
case <-time.After(time.Millisecond * time.Duration(*s.cfg.Timeout) * 2):
err = fmt.Errorf("ssh dial %s timeout", serverConn.Target())
s.sshClient.Close()
}
if err != nil {
s.log.Printf("connect ssh fail, ERR: %s, retrying...", err)
e := s.ConnectSSH(parentAddress)
if e == nil {
tryCount++
time.Sleep(time.Second * 3)
goto RETRY
} else {
err = e
}
}
}
return
}
func (s *Socks) ConnectSSH(lbAddr string) (err error) {
select {
case s.lockChn <- true:
default:
err = fmt.Errorf("can not connect at same time")
return
}
config := ssh.ClientConfig{
Timeout: time.Duration(*s.cfg.Timeout) * time.Millisecond,
User: *s.cfg.SSHUser,
Auth: []ssh.AuthMethod{s.cfg.SSHAuthMethod},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
if s.sshClient != nil {
s.sshClient.Close()
}
s.sshClient, err = ssh.Dial("tcp", s.Resolve(lbAddr), &config)
<-s.lockChn
return
}
func (s *Socks) InitBasicAuth() (err error) {
if *s.cfg.DNSAddress != "" {
s.basicAuth = utils.NewBasicAuth(&(*s).domainResolver, s.log)
} else {
s.basicAuth = utils.NewBasicAuth(nil, s.log)
}
if *s.cfg.AuthURL != "" {
s.basicAuth.SetAuthURL(*s.cfg.AuthURL, *s.cfg.AuthURLOkCode, *s.cfg.AuthURLTimeout, *s.cfg.AuthURLRetry)
s.log.Printf("auth from %s", *s.cfg.AuthURL)
}
if *s.cfg.AuthFile != "" {
var n = 0
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
if err != nil {
err = fmt.Errorf("auth-file ERR:%s", err)
return
}
s.log.Printf("auth data added from file %d , total:%d", n, s.basicAuth.Total())
}
if len(*s.cfg.Auth) > 0 {
n := s.basicAuth.Add(*s.cfg.Auth)
s.log.Printf("auth data added %d, total:%d", n, s.basicAuth.Total())
}
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 {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != ""
}
func (s *Socks) IsDeadLoop(inLocalAddr string, host string) bool {
inIP, inPort, err := net.SplitHostPort(inLocalAddr)
if err != nil {
return false
}
outDomain, outPort, err := net.SplitHostPort(host)
if err != nil {
return false
}
if inPort == outPort {
var outIPs []net.IP
if *s.cfg.DNSAddress != "" {
outIPs = []net.IP{net.ParseIP(s.Resolve(outDomain))}
} else {
outIPs, err = utils.LookupIP(outDomain)
}
if err == nil {
for _, ip := range outIPs {
if ip.String() == inIP {
return true
}
}
}
interfaceIPs, err := utils.GetAllInterfaceAddr()
for _, ip := range *s.cfg.LocalIPS {
interfaceIPs = append(interfaceIPs, net.ParseIP(ip).To4())
}
if err == nil {
for _, localIP := range interfaceIPs {
for _, outIP := range outIPs {
if localIP.Equal(outIP) {
return true
}
}
}
}
}
return false
}
func (s *Socks) Resolve(address string) string {
if *s.cfg.DNSAddress == "" {
return address
}
ip, err := s.domainResolver.Resolve(address)
if err != nil {
s.log.Printf("dns error %s , ERR:%s", address, err)
return address
}
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
}

315
services/socks/udp.go Normal file
View File

@ -0,0 +1,315 @@
package socks
import (
"crypto/md5"
"fmt"
"net"
"runtime/debug"
"strconv"
"strings"
"time"
"github.com/snail007/goproxy/utils"
goaes "github.com/snail007/goproxy/utils/aes"
"github.com/snail007/goproxy/utils/socks"
)
func (s *Socks) ParentUDPKey() (key []byte) {
switch *s.cfg.ParentType {
case "tcp":
if *s.cfg.ParentKey != "" {
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey)))
return []byte(v)[:24]
}
case "tls":
return s.cfg.KeyBytes[:24]
case "kcp":
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key)))
return []byte(v)[:24]
}
return
}
func (s *Socks) LocalUDPKey() (key []byte) {
switch *s.cfg.LocalType {
case "tcp":
if *s.cfg.LocalKey != "" {
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey)))
return []byte(v)[:24]
}
case "tls":
return s.cfg.KeyBytes[:24]
case "kcp":
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key)))
return []byte(v)[:24]
}
return
}
func (s *Socks) proxyUDP(inConn *net.Conn, serverConn *socks.ServerConn) {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
if *s.cfg.ParentType == "ssh" {
utils.CloseConn(inConn)
return
}
inconnRemoteAddr := (*inConn).RemoteAddr().String()
localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
udpListener := serverConn.UDPConnListener
srcIP, _, _ := net.SplitHostPort((*inConn).RemoteAddr().String())
s.log.Printf("proxy udp on %s , for %s", udpListener.LocalAddr(), inconnRemoteAddr)
s.userConns.Set(inconnRemoteAddr, inConn)
var (
outUDPConn *net.UDPConn
outconn net.Conn
outconnLocalAddr string
isClosedErr = func(err error) bool {
return err != nil && strings.Contains(err.Error(), "use of closed network connection")
}
destAddr *net.UDPAddr
)
var clean = func(msg, err string) {
raddr := ""
if outUDPConn != nil {
raddr = outUDPConn.RemoteAddr().String()
outUDPConn.Close()
}
if msg != "" {
if raddr != "" {
s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr)
} else {
s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr)
}
}
(*inConn).Close()
udpListener.Close()
s.userConns.Remove(inconnRemoteAddr)
if outconn != nil {
outconn.Close()
}
if outconnLocalAddr != "" {
s.userConns.Remove(outconnLocalAddr)
}
}
defer clean("", "")
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp related client tcp conn read crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
buf := make([]byte, 1)
(*inConn).SetReadDeadline(time.Time{})
if _, err := (*inConn).Read(buf); err != nil {
clean("udp related tcp conn disconnected with read", err.Error())
}
}()
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp related client tcp conn write crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
for {
(*inConn).SetWriteDeadline(time.Now().Add(time.Second * 5))
if _, err := (*inConn).Write([]byte{0x00}); err != nil {
clean("udp related tcp conn disconnected with write", err.Error())
return
}
(*inConn).SetWriteDeadline(time.Time{})
time.Sleep(time.Second * 5)
}
}()
useProxy := true
if len(*s.cfg.Parent) > 0 {
dstHost, _, _ := net.SplitHostPort(serverConn.Target())
if utils.IsInternalIP(dstHost, *s.cfg.Always) {
useProxy = false
} else {
var isInMap bool
useProxy, isInMap, _, _ = s.checker.IsBlocked(serverConn.Target())
if !isInMap {
s.checker.Add(serverConn.Target(), s.Resolve(serverConn.Target()))
}
}
} else {
useProxy = false
}
if useProxy {
//parent proxy
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 {
clean("connnect fail", fmt.Sprintf("%s", err))
return
}
client, err := s.HandshakeSocksParent(&outconn, "udp", serverConn.Target(), serverConn.AuthData(), false)
if err != nil {
clean("handshake fail", fmt.Sprintf("%s", err))
return
}
//outconnRemoteAddr := outconn.RemoteAddr().String()
outconnLocalAddr = outconn.LocalAddr().String()
s.userConns.Set(outconnLocalAddr, &outconn)
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
buf := make([]byte, 1)
outconn.SetReadDeadline(time.Time{})
if _, err := outconn.Read(buf); err != nil {
clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err))
}
}()
//forward to parent udp
//s.log.Printf("parent udp address %s", client.UDPAddr)
destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr)
}
s.log.Printf("use proxy %v : udp %s", useProxy, serverConn.Target())
//relay
for {
buf := utils.LeakyBuffer.Get()
defer utils.LeakyBuffer.Put(buf)
n, srcAddr, err := udpListener.ReadFromUDP(buf)
if err != nil {
s.log.Printf("udp listener read fail, %s", err.Error())
if isClosedErr(err) {
return
}
continue
}
srcIP0, _, _ := net.SplitHostPort(srcAddr.String())
//IP not match drop it
if srcIP != srcIP0 {
continue
}
p := socks.NewPacketUDP()
//convert data to raw
if len(s.udpLocalKey) > 0 {
var v []byte
v, err = goaes.Decrypt(s.udpLocalKey, buf[:n])
if err == nil {
err = p.Parse(v)
}
} else {
err = p.Parse(buf[:n])
}
//err = p.Parse(buf[:n])
if err != nil {
s.log.Printf("udp listener parse packet fail, %s", err.Error())
continue
}
port, _ := strconv.Atoi(p.Port())
if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok {
if destAddr == nil {
destAddr = &net.UDPAddr{IP: net.ParseIP(p.Host()), Port: port}
}
outUDPConn, err = net.DialUDP("udp", localAddr, destAddr)
if err != nil {
s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr)
continue
}
s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn)
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp out->local io copy crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
defer s.udpRelatedPacketConns.Remove(srcAddr.String())
//out->local io copy
buf := utils.LeakyBuffer.Get()
defer utils.LeakyBuffer.Put(buf)
for {
n, err := outUDPConn.Read(buf)
if err != nil {
s.log.Printf("read out udp data fail , %s , from : %s", err, srcAddr)
if isClosedErr(err) {
return
}
continue
}
//var dlen = n
if useProxy {
//forward to local
var v []byte
//convert parent data to raw
if len(s.udpParentKey) > 0 {
v, err = goaes.Decrypt(s.udpParentKey, buf[:n])
if err != nil {
s.log.Printf("udp outconn parse packet fail, %s", err.Error())
continue
}
} else {
v = buf[:n]
}
//now v is raw, try convert v to local
if len(s.udpLocalKey) > 0 {
v, _ = goaes.Encrypt(s.udpLocalKey, v)
}
_, err = udpListener.WriteTo(v, srcAddr)
// _, err = udpListener.WriteTo(buf[:n], srcAddr)
} else {
rp := socks.NewPacketUDP()
rp.Build(destAddr.String(), buf[:n])
v := rp.Bytes()
//dlen = len(v)
//rp.Bytes() v is raw, try convert to local
if len(s.udpLocalKey) > 0 {
v, _ = goaes.Encrypt(s.udpLocalKey, v)
}
_, err = udpListener.WriteTo(v, srcAddr)
}
if err != nil {
s.udpRelatedPacketConns.Remove(srcAddr.String())
s.log.Printf("write out data to local fail , %s , from : %s", err, srcAddr)
if isClosedErr(err) {
return
}
continue
} else {
//s.log.Printf("send udp data to local success , len %d, for : %s", dlen, srcAddr)
}
}
}()
} else {
outUDPConn = v.(*net.UDPConn)
}
//local->out io copy
if useProxy {
//forward to parent
//p is raw, now convert it to parent
var v []byte
if len(s.udpParentKey) > 0 {
v, _ = goaes.Encrypt(s.udpParentKey, p.Bytes())
} else {
v = p.Bytes()
}
_, err = outUDPConn.Write(v)
// _, err = outUDPConn.Write(p.Bytes())
} else {
_, err = outUDPConn.Write(p.Data())
}
if err != nil {
if isClosedErr(err) {
return
}
s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr)
continue
} else {
//s.log.Printf("send udp data to remote success , len %d, for : %s", len(p.Data()), srcAddr)
}
}
}

217
services/sps/socksudp.go Normal file
View File

@ -0,0 +1,217 @@
package sps
import (
"fmt"
"net"
"runtime/debug"
"strings"
"time"
"github.com/snail007/goproxy/utils"
goaes "github.com/snail007/goproxy/utils/aes"
"github.com/snail007/goproxy/utils/socks"
)
func (s *SPS) proxyUDP(inConn *net.Conn, serverConn *socks.ServerConn) {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
if *s.cfg.ParentType == "ssh" {
utils.CloseConn(inConn)
return
}
srcIP, _, _ := net.SplitHostPort((*inConn).RemoteAddr().String())
inconnRemoteAddr := (*inConn).RemoteAddr().String()
localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
udpListener := serverConn.UDPConnListener
s.log.Printf("proxy udp on %s , for %s", udpListener.LocalAddr(), inconnRemoteAddr)
s.userConns.Set(inconnRemoteAddr, inConn)
var (
outUDPConn *net.UDPConn
outconn net.Conn
outconnLocalAddr string
isClosedErr = func(err error) bool {
return err != nil && strings.Contains(err.Error(), "use of closed network connection")
}
destAddr *net.UDPAddr
)
var clean = func(msg, err string) {
raddr := ""
if outUDPConn != nil {
raddr = outUDPConn.RemoteAddr().String()
outUDPConn.Close()
}
if msg != "" {
if raddr != "" {
s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr)
} else {
s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr)
}
}
(*inConn).Close()
udpListener.Close()
s.userConns.Remove(inconnRemoteAddr)
if outconn != nil {
outconn.Close()
}
if outconnLocalAddr != "" {
s.userConns.Remove(outconnLocalAddr)
}
}
defer clean("", "")
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp related client tcp conn read crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
buf := make([]byte, 1)
(*inConn).SetReadDeadline(time.Time{})
if _, err := (*inConn).Read(buf); err != nil {
clean("udp related tcp conn disconnected with read", err.Error())
}
}()
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp related client tcp conn write crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
for {
(*inConn).SetWriteDeadline(time.Now().Add(time.Second * 5))
if _, err := (*inConn).Write([]byte{0x00}); err != nil {
clean("udp related tcp conn disconnected with write", err.Error())
return
}
(*inConn).SetWriteDeadline(time.Time{})
time.Sleep(time.Second * 5)
}
}()
//parent proxy
lbAddr := s.lb.Select((*inConn).RemoteAddr().String(), *s.cfg.LoadBalanceOnlyHA)
outconn, err := s.GetParentConn(lbAddr)
//outconn, err := s.GetParentConn(nil, nil, "", false)
if err != nil {
clean("connnect fail", fmt.Sprintf("%s", err))
return
}
s.log.Printf("connect %s for udp", serverConn.Target())
//socks client
client, err := s.HandshakeSocksParent(&outconn, "udp", serverConn.Target(), serverConn.AuthData(), false)
if err != nil {
clean("handshake fail", fmt.Sprintf("%s", err))
return
}
//outconnRemoteAddr := outconn.RemoteAddr().String()
outconnLocalAddr = outconn.LocalAddr().String()
s.userConns.Set(outconnLocalAddr, &outconn)
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack()))
}
}()
buf := make([]byte, 1)
outconn.SetReadDeadline(time.Time{})
if _, err := outconn.Read(buf); err != nil {
clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err))
}
}()
//forward to parent udp
//s.log.Printf("parent udp address %s", client.UDPAddr)
destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr)
//relay
for {
buf := utils.LeakyBuffer.Get()
defer utils.LeakyBuffer.Put(buf)
n, srcAddr, err := udpListener.ReadFromUDP(buf)
if err != nil {
s.log.Printf("udp listener read fail, %s", err.Error())
if isClosedErr(err) {
return
}
continue
}
srcIP0, _, _ := net.SplitHostPort(srcAddr.String())
//IP not match drop it
if srcIP != srcIP0 {
continue
}
p := socks.NewPacketUDP()
//convert data to raw
if len(s.udpLocalKey) > 0 {
var v []byte
v, err = goaes.Decrypt(s.udpLocalKey, buf[:n])
if err == nil {
err = p.Parse(v)
}
} else {
err = p.Parse(buf[:n])
}
//err = p.Parse(buf[:n])
if err != nil {
s.log.Printf("udp listener parse packet fail, %s", err.Error())
continue
}
if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok {
outUDPConn, err = net.DialUDP("udp", localAddr, destAddr)
if err != nil {
s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr)
continue
}
s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn)
utils.UDPCopy(udpListener, outUDPConn, srcAddr, 0, 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
}
//now v is raw, try convert v to local
if len(s.udpLocalKey) > 0 {
v, _ = goaes.Encrypt(s.udpLocalKey, v)
}
return v
}, 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)
}
//local->out io copy
//forward to parent
//p is raw, now convert it to parent
var v []byte
if len(s.udpParentKey) > 0 {
v, _ = goaes.Encrypt(s.udpParentKey, p.Bytes())
} else {
v = p.Bytes()
}
_, err = outUDPConn.Write(v)
// _, err = outUDPConn.Write(p.Bytes())
if err != nil {
if isClosedErr(err) {
return
}
s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr)
continue
} else {
//s.log.Printf("send udp data to remote success , len %d, for : %s", len(p.Data()), srcAddr)
}
}
}

707
services/sps/sps.go Normal file
View File

@ -0,0 +1,707 @@
package sps
import (
"bytes"
"crypto/md5"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
logger "log"
"net"
"runtime/debug"
"strconv"
"strings"
"time"
"github.com/snail007/goproxy/core/cs/server"
"github.com/snail007/goproxy/core/lib/kcpcfg"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/utils"
"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/sni"
"github.com/snail007/goproxy/utils/socks"
"github.com/snail007/goproxy/utils/ss"
)
type SPSArgs struct {
Parent *[]string
CertFile *string
KeyFile *string
CaCertFile *string
CaCertBytes []byte
CertBytes []byte
KeyBytes []byte
Local *string
ParentType *string
LocalType *string
Timeout *int
KCP kcpcfg.KCPConfigArgs
ParentServiceType *string
DNSAddress *string
DNSTTL *int
AuthFile *string
Auth *[]string
AuthURL *string
AuthURLOkCode *int
AuthURLTimeout *int
AuthURLRetry *int
LocalIPS *[]string
ParentAuth *string
LocalKey *string
ParentKey *string
LocalCompress *bool
ParentCompress *bool
SSMethod *string
SSKey *string
ParentSSMethod *string
ParentSSKey *string
DisableHTTP *bool
DisableSocks5 *bool
DisableSS *bool
LoadBalanceMethod *string
LoadBalanceTimeout *int
LoadBalanceRetryTime *int
LoadBalanceHashTarget *bool
LoadBalanceOnlyHA *bool
RateLimit *string
RateLimitBytes float64
Debug *bool
}
type SPS struct {
cfg SPSArgs
domainResolver dnsx.DomainResolver
basicAuth utils.BasicAuth
serverChannels []*server.ServerChannel
userConns mapx.ConcurrentMap
log *logger.Logger
localCipher *ss.Cipher
parentCipher *ss.Cipher
udpRelatedPacketConns mapx.ConcurrentMap
lb *lb.Group
udpLocalKey []byte
udpParentKey []byte
}
func NewSPS() services.Service {
return &SPS{
cfg: SPSArgs{},
basicAuth: utils.BasicAuth{},
serverChannels: []*server.ServerChannel{},
userConns: mapx.NewConcurrentMap(),
udpRelatedPacketConns: mapx.NewConcurrentMap(),
}
}
func (s *SPS) CheckArgs() (err error) {
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)
return
}
if *s.cfg.ParentType == "" {
err = fmt.Errorf("parent type unkown,use -T <tls|tcp|kcp>")
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" {
s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
if err != nil {
return
}
if *s.cfg.CaCertFile != "" {
s.cfg.CaCertBytes, err = ioutil.ReadFile(*s.cfg.CaCertFile)
if err != nil {
err = fmt.Errorf("read ca file error,ERR:%s", err)
return
}
}
}
if *s.cfg.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.udpParentKey = s.ParentUDPKey()
return
}
func (s *SPS) InitService() (err error) {
if *s.cfg.DNSAddress != "" {
s.domainResolver = dnsx.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log)
}
if len(*s.cfg.Parent) > 0 {
s.InitLB()
}
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() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop sps service crashed,%s", e)
} else {
s.log.Printf("service sps stoped")
}
s.basicAuth = utils.BasicAuth{}
s.cfg = SPSArgs{}
s.domainResolver = dnsx.DomainResolver{}
s.lb = nil
s.localCipher = nil
s.log = nil
s.parentCipher = nil
s.serverChannels = nil
s.udpLocalKey = nil
s.udpParentKey = nil
s.udpRelatedPacketConns = nil
s.userConns = nil
s = nil
}()
for _, sc := range s.serverChannels {
if sc.Listener != nil && *sc.Listener != nil {
(*sc.Listener).Close()
}
if sc.UDPListener != nil {
(*sc.UDPListener).Close()
}
}
for _, c := range s.userConns.Items() {
if _, ok := c.(*net.Conn); ok {
(*c.(*net.Conn)).Close()
}
if _, ok := c.(**net.Conn); ok {
(*(*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) {
s.log = log
s.cfg = args.(SPSArgs)
if err = s.CheckArgs(); err != nil {
return
}
if err = s.InitService(); err != nil {
return
}
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, ",") {
if addr != "" {
host, port, _ := net.SplitHostPort(addr)
p, _ := strconv.Atoi(port)
sc := server.NewServerChannel(host, p, s.log)
s.serverChannels = append(s.serverChannels, &sc)
if *s.cfg.LocalType == "tcp" {
err = sc.ListenTCP(s.callback)
} else if *s.cfg.LocalType == "tls" {
err = sc.ListenTLS(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.callback)
} else if *s.cfg.LocalType == "tcp" {
err = sc.ListenKCP(s.cfg.KCP, s.callback, s.log)
}
if *s.cfg.ParentServiceType == "socks" {
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
}
func (s *SPS) Clean() {
s.StopService()
}
func (s *SPS) callback(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
s.log.Printf("%s conn handler crashed with err : %s \nstack: %s", *s.cfg.LocalType, err, string(debug.Stack()))
}
}()
if *s.cfg.LocalCompress {
inConn = utils.NewCompConn(inConn)
}
if *s.cfg.LocalKey != "" {
inConn = conncrypt.New(inConn, &conncrypt.Config{
Password: *s.cfg.LocalKey,
})
}
var err error
lbAddr := ""
switch *s.cfg.ParentType {
case "kcp", "tcp", "tls":
lbAddr, err = s.OutToTCP(&inConn)
default:
err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType)
}
if err != nil {
s.log.Printf("connect to %s parent %s fail, ERR:%s from %s", *s.cfg.ParentType, lbAddr, err, inConn.RemoteAddr())
utils.CloseConn(&inConn)
}
}
func (s *SPS) OutToTCP(inConn *net.Conn) (lbAddr string, err error) {
enableUDP := *s.cfg.ParentServiceType == "socks"
udpIP, _, _ := net.SplitHostPort((*inConn).LocalAddr().String())
if len(*s.cfg.LocalIPS) > 0 {
udpIP = (*s.cfg.LocalIPS)[0]
}
bInConn := utils.NewBufferedConn(*inConn)
//important
//action read will regist read event to system,
//when data arrived , system call process
//so that we can get buffered bytes count
//otherwise Buffered() always return 0
bInConn.ReadByte()
bInConn.UnreadByte()
n := 2048
if n > bInConn.Buffered() {
n = bInConn.Buffered()
}
h, err := bInConn.Peek(n)
if err != nil {
s.log.Printf("peek error %s ", err)
(*inConn).Close()
return
}
isSNI, _ := sni.ServerNameFromBytes(h)
*inConn = bInConn
address := ""
var auth = socks.Auth{}
var forwardBytes []byte
//fmt.Printf("%v", header)
if utils.IsSocks5(h) {
if *s.cfg.DisableSocks5 {
(*inConn).Close()
return
}
//socks5 server
var serverConn *socks.ServerConn
if s.IsBasicAuth() {
serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), &s.basicAuth, enableUDP, udpIP, nil)
} else {
serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, enableUDP, udpIP, nil)
}
if err = serverConn.Handshake(); err != nil {
return
}
address = serverConn.Target()
auth = serverConn.AuthData()
if serverConn.IsUDP() {
s.proxyUDP(inConn, serverConn)
return
}
} else if utils.IsHTTP(h) || isSNI != "" {
if *s.cfg.DisableHTTP {
(*inConn).Close()
return
}
//http
var request utils.HTTPRequest
(*inConn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
if s.IsBasicAuth() {
request, err = utils.NewHTTPRequest(inConn, 1024, true, &s.basicAuth, s.log)
} else {
request, err = utils.NewHTTPRequest(inConn, 1024, false, nil, s.log)
}
(*inConn).SetDeadline(time.Time{})
if err != nil {
s.log.Printf("new http request fail,ERR: %s", err)
utils.CloseConn(inConn)
return
}
if len(h) >= 7 && strings.ToLower(string(h[:7])) == "connect" {
//https
request.HTTPSReply()
//s.log.Printf("https reply: %s", request.Host)
} else {
forwardBytes = request.HeadBuf
}
address = request.Host
var userpass string
if s.IsBasicAuth() {
userpass, err = request.GetAuthDataStr()
if err != nil {
return
}
userpassA := strings.Split(userpass, ":")
if len(userpassA) == 2 {
auth = socks.Auth{User: userpassA[0], Password: userpassA[1]}
}
}
} else {
//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 == "" {
s.log.Printf("unknown request from: %s,%s", (*inConn).RemoteAddr(), string(h))
(*inConn).Close()
utils.CloseConn(inConn)
err = errors.New("unknown request")
return
}
//connect to parent
var outConn net.Conn
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 {
s.log.Printf("connect to %s , err:%s", lbAddr, err)
utils.CloseConn(inConn)
return
}
if *s.cfg.ParentAuth != "" || *s.cfg.ParentSSKey != "" || s.IsBasicAuth() {
forwardBytes = utils.RemoveProxyHeaders(forwardBytes)
}
//ask parent for connect to target address
if *s.cfg.ParentServiceType == "http" {
//http parent
isHTTPS := false
pb := new(bytes.Buffer)
if len(forwardBytes) == 0 {
isHTTPS = true
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("Connection: Keep-Alive\r\n")
u := ""
if *s.cfg.ParentAuth != "" {
a := strings.Split(*s.cfg.ParentAuth, ":")
if len(a) != 2 {
err = fmt.Errorf("parent auth data format error")
return
}
u = fmt.Sprintf("%s:%s", a[0], a[1])
} else {
if !s.IsBasicAuth() && auth.Password != "" && auth.User != "" {
u = fmt.Sprintf("%s:%s", auth.User, auth.Password)
}
}
if u != "" {
pb.Write([]byte(fmt.Sprintf("Proxy-Authorization: Basic %s\r\n", base64.StdEncoding.EncodeToString([]byte(u)))))
}
if isHTTPS {
pb.Write([]byte("\r\n"))
} else {
forwardBytes = utils.InsertProxyHeaders(forwardBytes, string(pb.Bytes()))
pb.Reset()
pb.Write(forwardBytes)
forwardBytes = nil
}
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = outConn.Write(pb.Bytes())
outConn.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("write CONNECT to %s , err:%s", lbAddr, err)
utils.CloseConn(inConn)
utils.CloseConn(&outConn)
return
}
if isHTTPS {
reply := make([]byte, 1024)
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = outConn.Read(reply)
outConn.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("read reply from %s , err:%s", lbAddr, err)
utils.CloseConn(inConn)
utils.CloseConn(&outConn)
return
}
//s.log.Printf("reply: %s", string(reply[:n]))
}
} else if *s.cfg.ParentServiceType == "socks" {
s.log.Printf("connect %s", address)
//socks client
_, err = s.HandshakeSocksParent(&outConn, "tcp", address, auth, false)
if err != nil {
s.log.Printf("handshake fail, %s", err)
return
}
} else if *s.cfg.ParentServiceType == "ss" {
ra, e := ss.RawAddr(address)
if e != nil {
err = fmt.Errorf("build ss raw addr fail, err: %s", e)
return
}
outConn, err = ss.DialWithRawAddr(&outConn, ra, "", s.parentCipher.Copy())
if err != nil {
err = fmt.Errorf("dial ss parent fail, err : %s", err)
return
}
}
//forward client data to target,if necessary.
if len(forwardBytes) > 0 {
outConn.Write(forwardBytes)
}
if s.cfg.RateLimitBytes > 0 {
outConn = iolimiter.NewReaderConn(outConn, s.cfg.RateLimitBytes)
}
//bind
inAddr := (*inConn).RemoteAddr().String()
outAddr := outConn.RemoteAddr().String()
utils.IoBind((*inConn), outConn, func(err interface{}) {
s.log.Printf("conn %s - %s released [%s]", inAddr, outAddr, address)
s.userConns.Remove(inAddr)
s.lb.DecreaseConns(lbAddr)
}, s.log)
s.log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, address)
s.lb.IncreasConns(lbAddr)
if c, ok := s.userConns.Get(inAddr); ok {
(*c.(*net.Conn)).Close()
}
s.userConns.Set(inAddr, inConn)
return
}
func (s *SPS) InitBasicAuth() (err error) {
if *s.cfg.DNSAddress != "" {
s.basicAuth = utils.NewBasicAuth(&(*s).domainResolver, s.log)
} else {
s.basicAuth = utils.NewBasicAuth(nil, s.log)
}
if *s.cfg.AuthURL != "" {
s.basicAuth.SetAuthURL(*s.cfg.AuthURL, *s.cfg.AuthURLOkCode, *s.cfg.AuthURLTimeout, *s.cfg.AuthURLRetry)
s.log.Printf("auth from %s", *s.cfg.AuthURL)
}
if *s.cfg.AuthFile != "" {
var n = 0
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
if err != nil {
err = fmt.Errorf("auth-file ERR:%s", err)
return
}
s.log.Printf("auth data added from file %d , total:%d", n, s.basicAuth.Total())
}
if len(*s.cfg.Auth) > 0 {
n := s.basicAuth.Add(*s.cfg.Auth)
s.log.Printf("auth data added %d, total:%d", n, s.basicAuth.Total())
}
return
}
func (s *SPS) 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 *SPS) IsBasicAuth() bool {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != ""
}
func (s *SPS) buildRequest(address string) (buf []byte, err error) {
host, portStr, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
port, err := strconv.Atoi(portStr)
if err != nil {
err = errors.New("proxy: failed to parse port number: " + portStr)
return
}
if port < 1 || port > 0xffff {
err = errors.New("proxy: port number out of range: " + portStr)
return
}
buf = buf[:0]
buf = append(buf, 0x05, 0x01, 0 /* reserved */)
if ip := net.ParseIP(host); ip != nil {
if ip4 := ip.To4(); ip4 != nil {
buf = append(buf, 0x01)
ip = ip4
} else {
buf = append(buf, 0x04)
}
buf = append(buf, ip...)
} else {
if len(host) > 255 {
err = errors.New("proxy: destination host name too long: " + host)
return
}
buf = append(buf, 0x03)
buf = append(buf, byte(len(host)))
buf = append(buf, host...)
}
buf = append(buf, byte(port>>8), byte(port))
return
}
func (s *SPS) Resolve(address string) string {
if *s.cfg.DNSAddress == "" {
return address
}
ip, err := s.domainResolver.Resolve(address)
if err != nil {
s.log.Printf("dns error %s , ERR:%s", address, err)
return address
}
return ip
}
func (s *SPS) GetParentConn(address string) (conn net.Conn, err error) {
if *s.cfg.ParentType == "tls" {
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 if *s.cfg.ParentType == "kcp" {
conn, err = utils.ConnectKCPHost(address, s.cfg.KCP)
} else {
conn, err = utils.ConnectHost(address, *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(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
}
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
}

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