550 Commits
v3.0 ... v5.4

Author SHA1 Message Date
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
snail007
675ae276f9 Merge pull request #38 from snail007/dev
Update README.md
2018-02-20 17:41:29 +08:00
snail007
7899c45176 Update README.md 2018-02-20 17:40:53 +08:00
snail007
1dbb6feb57 Merge pull request #37 from snail007/dev
update readme
2018-02-13 12:46:38 +08:00
snail007
661685d136 Merge pull request #36 from yincongcyincong/dev
Update README.md
2018-02-13 12:44:40 +08:00
yincongcyincong
28947a0352 Update README.md 2018-02-13 08:20:18 +08:00
yincongcyincong
bd594684ce Update README.md 2018-02-13 08:18:40 +08:00
snail007
fda3609873 Merge pull request #35 from snail007/dev
Update README.md
2018-02-08 23:42:44 +08:00
snail007
d20db0c546 Update README.md 2018-02-08 23:42:04 +08:00
snail007
07fb22ae70 Merge pull request #34 from snail007/dev
add tg
2018-02-08 23:41:03 +08:00
snail007
8d5c3944ad Update README.md 2018-02-08 23:40:29 +08:00
snail007
7cf28aa9f4 Update README_ZH.md 2018-02-08 23:36:43 +08:00
snail007
731867b73c Merge pull request #33 from snail007/dev
Update README.md
2018-02-07 23:08:59 +08:00
snail007
1c6df2d9a2 Update README.md 2018-02-07 23:08:01 +08:00
snail007
ee5a248a39 Merge pull request #32 from snail007/dev
update docs
2018-02-07 23:05:53 +08:00
snail007
c174f85656 Merge pull request #31 from yincongcyincong/dev
Update README.md
2018-02-07 23:05:15 +08:00
yincongcyincong
2669aac7c9 Update README.md 2018-02-07 16:09:25 +08:00
snail007
f1aec74b11 Merge pull request #30 from snail007/dev
update en docs
2018-02-06 14:57:06 +08:00
snail007
1d382b2bf6 Merge pull request #29 from yincongcyincong/dev
Update README.md
2018-02-06 14:56:31 +08:00
yincongcyincong
55cac537b1 Update README.md 2018-02-06 14:50:31 +08:00
snail007
75258fa195 Merge pull request #28 from snail007/dev
update en_docs
2018-02-06 14:43:31 +08:00
snail007
cef2ca6d8e Merge pull request #27 from yincongcyincong/dev
Update README.md
2018-02-06 14:40:17 +08:00
yincongcyincong
4b1651bb3e Update README.md 2018-02-06 11:33:06 +08:00
snail007
78c116bca9 Merge pull request #26 from yincongcyincong/dev
en readme
2018-02-06 11:14:41 +08:00
yincongcyincong
a7c46f5582 Update README.md 2018-02-06 08:09:16 +08:00
yincongcyincong
f947d35bc3 Update README.md 2018-02-05 18:00:50 +08:00
yincongcyincong
a49e0166d4 Update README.md 2018-02-05 17:50:39 +08:00
yincongcyincong
7272b592d5 Update README.md 2018-02-05 11:35:50 +08:00
snail007
cced739d0e Update README_ZH.md 2018-02-05 10:57:07 +08:00
snail007
54ac46b3e4 Update README_ZH.md 2018-02-05 10:56:32 +08:00
snail007
8f9aa2fd64 Update README_ZH.md 2018-02-05 10:56:03 +08:00
snail007
c7b9cd5853 Update README_ZH.md 2018-02-05 10:55:28 +08:00
snail007
f9dfac55b0 Update README_ZH.md 2018-02-05 10:54:51 +08:00
snail007
b4ad1b5465 Update README_ZH.md 2018-02-05 10:54:11 +08:00
snail007
e2b2b7e255 Update README_ZH.md 2018-02-05 10:53:43 +08:00
snail007
80b691564c Update README_ZH.md 2018-02-05 10:52:25 +08:00
yincongcyincong
dfc326b771 Update README.md 2018-02-01 18:01:10 +08:00
yincongcyincong
7cfde70a9f Update README.md 2018-02-01 17:59:21 +08:00
arraykeys@gmail.com
20837ba983 Merge branch 'master' of https://github.com/snail007/goproxy.git 2018-02-01 15:26:08 +08:00
arraykeys@gmail.com
8dda32a599 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-02-01 15:07:26 +08:00
yincongcyincong
42ce2a4351 Update README.md 2018-01-31 12:57:11 +08:00
yincongcyincong
6574d5cd29 Update README.md 2018-01-31 12:41:52 +08:00
snail007
4e9ae9a8f5 Update README.md 2018-01-31 12:32:58 +08:00
arraykeys@gmail.com
a9ce3cf733 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-31 12:32:10 +08:00
snail007
65708a0f12 Update README.md 2018-01-31 12:31:29 +08:00
arraykeys@gmail.com
e2a3b5f9ee Merge branch 'x' into dev
# Conflicts:
#	README.md

Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-01-31 12:27:58 +08:00
arraykeys@gmail.com
7a752537c5 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-31 12:27:25 +08:00
arraykeys@gmail.com
abd0b63fe9 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-31 12:15:27 +08:00
arraykeys@gmail.com
7ac7cd452b Merge branch 'endoc' into dev
# Conflicts:
#	install_auto.sh
#	main.go
#	release.sh

Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2018-01-31 12:13:34 +08:00
arraykeys@gmail.com
94cecdb8c0 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-31 12:13:01 +08:00
arraykeys@gmail.com
a8b35ba971 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-31 11:54:12 +08:00
arraykeys@gmail.com
bc1ab84b75 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-31 10:39:06 +08:00
snail007
acc895d2df Update README.md 2018-01-29 23:54:43 +08:00
snail007
23dbd0a92f Update README.md 2018-01-29 23:49:42 +08:00
snail007
c069b5cd97 Update README.md 2018-01-29 21:06:10 +08:00
snail007
f1dfe50d8b Update README.md 2018-01-29 21:02:52 +08:00
snail007
7d3820175f Update README.md 2018-01-29 21:00:55 +08:00
arraykeys@gmail.com
75032fdbb7 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-27 10:03:08 +08:00
arraykeys@gmail.com
23b3ad63cf Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-26 12:09:12 +08:00
arraykeys@gmail.com
7afd0c86cd Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-26 12:06:29 +08:00
arraykeys@gmail.com
5f0a341d22 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-26 11:03:34 +08:00
arraykeys@gmail.com
2a117376b7 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-24 17:20:28 +08:00
arraykeys@gmail.com
2fc750532d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-24 17:19:54 +08:00
arraykeys@gmail.com
477be63cff Merge branch 'dev' 2018-01-24 16:55:49 +08:00
arraykeys@gmail.com
39b90357db Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-24 16:55:42 +08:00
arraykeys@gmail.com
2bd916eb73 Merge branch 'dev' 2018-01-24 16:52:27 +08:00
arraykeys@gmail.com
4d1b450b33 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-24 16:52:17 +08:00
arraykeys@gmail.com
cb70812cb7 Merge branch 'dev' 2018-01-24 16:50:43 +08:00
arraykeys@gmail.com
42e030e368 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-24 16:50:36 +08:00
arraykeys@gmail.com
f84cdc921d Merge branch 'dev' 2018-01-24 16:49:20 +08:00
arraykeys@gmail.com
02c07e7f4f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-24 16:49:10 +08:00
arraykeys@gmail.com
d6ea190688 Merge branch 'dev' 2018-01-24 15:56:41 +08:00
arraykeys@gmail.com
b20487b928 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-24 15:56:32 +08:00
arraykeys@gmail.com
004cf5693f Merge branch 'dev' 2018-01-24 11:04:31 +08:00
arraykeys@gmail.com
ef8de6feb0 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-24 11:04:19 +08:00
arraykeys@gmail.com
d791ebe634 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-24 11:02:20 +08:00
arraykeys@gmail.com
5af4a1817e Merge branch 'master' of https://github.com/snail007/goproxy.git
# Conflicts:
#	README.md
2018-01-24 10:50:52 +08:00
arraykeys@gmail.com
e97b8c55f3 Merge branch 'dev' 2018-01-24 10:50:30 +08:00
arraykeys@gmail.com
99fcb76210 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2018-01-24 10:19:13 +08:00
arraykeys@gmail.com
d4fd34165e Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-24 10:18:37 +08:00
snail007
bd4741a0a0 Update README.md 2018-01-23 21:32:37 +08:00
snail007
5945c32646 Update README.md 2018-01-23 21:31:04 +08:00
snail007
9a9dc2594d Merge pull request #20 from wujunze/patch-1
修正文档参数
2018-01-15 18:05:45 +08:00
Panda
02547e9475 修正文档参数
修正文档参数
2018-01-15 16:31:08 +08:00
arraykeys@gmail.com
d81a823da1 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2018-01-15 15:18:36 +08:00
arraykeys@gmail.com
df74bcc885 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-20 17:07:29 +08:00
arraykeys@gmail.com
094bcebfa3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-20 16:59:55 +08:00
arraykeys@gmail.com
bb2a16720b Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-18 10:52:21 +08:00
arraykeys@gmail.com
86d9a0c0f3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-18 10:49:26 +08:00
arraykeys@gmail.com
5cf9d72ed3 add vendor
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2017-12-07 16:50:24 +08:00
arraykeys@gmail.com
801605676c Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-07 12:02:06 +08:00
arraykeys@gmail.com
a9dec75e59 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-05 17:49:58 +08:00
arraykeys@gmail.com
08d9d90fe1 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-05 11:57:05 +08:00
arraykeys@gmail.com
9749db9235 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-05 11:48:32 +08:00
arraykeys@gmail.com
11073aaaa5 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-05 11:46:15 +08:00
arraykeys@gmail.com
e35ddc4d53 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-05 11:18:40 +08:00
arraykeys@gmail.com
99b06e813e Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-05 10:27:51 +08:00
arraykeys@gmail.com
7164349944 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-04 11:07:23 +08:00
arraykeys@gmail.com
bf43b3adee Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-04 10:35:44 +08:00
arraykeys
6aa4b3c8a9 no message 2017-12-02 21:14:11 +08:00
arraykeys
7a9f7ef95e no message 2017-12-02 14:25:18 +08:00
arraykeys
ee1a9d3ec7 no message 2017-12-02 14:24:30 +08:00
arraykeys
6a69e58be5 no message 2017-12-02 14:22:14 +08:00
arraykeys
6e1d788677 no message 2017-12-02 14:21:21 +08:00
arraykeys
24f8f789c5 no message 2017-12-02 14:20:15 +08:00
arraykeys
2fb779f990 no message 2017-12-02 14:18:53 +08:00
arraykeys
0a9d3cd309 no message 2017-12-02 14:16:16 +08:00
arraykeys
977b1aba1c no message 2017-12-02 14:02:17 +08:00
arraykeys
a02aeeb906 no message 2017-12-01 23:52:21 +08:00
arraykeys
7e2e63137e no message 2017-12-01 22:14:07 +08:00
arraykeys@gmail.com
4b35219c27 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-01 18:01:59 +08:00
arraykeys@gmail.com
0247c4701d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 18:43:31 +08:00
arraykeys@gmail.com
e2cd0b8e4f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 16:50:47 +08:00
arraykeys@gmail.com
ee93171c63 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 15:37:27 +08:00
arraykeys@gmail.com
ddd2302cb2 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 15:37:01 +08:00
arraykeys@gmail.com
c96d2288b3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 15:33:32 +08:00
arraykeys@gmail.com
6f5a088091 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 14:25:44 +08:00
arraykeys@gmail.com
9a07797e29 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-27 12:44:43 +08:00
arraykeys@gmail.com
055a020d33 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-27 12:41:12 +08:00
arraykeys@gmail.com
4681ff3827 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-27 12:21:59 +08:00
arraykeys@gmail.com
cff92faf06 Merge branch 'master' of https://github.com/snail007/goproxy.git into dev 2017-11-27 11:28:24 +08:00
arraykeys@gmail.com
890daf5489 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2017-11-27 11:28:17 +08:00
snail007
182bdeb766 Merge pull request #14 from snail007/dev
Update README.md
2017-11-23 19:27:53 -06:00
snail007
a4a953b167 Update README.md 2017-11-24 09:27:07 +08:00
arraykeys@gmail.com
ff37b7e18c Merge branch 'dev' 2017-11-21 14:03:24 +08:00
arraykeys@gmail.com
7aa0e78c15 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-21 14:02:28 +08:00
arraykeys@gmail.com
d798807693 Merge branch 'dev' 2017-11-14 11:58:06 +08:00
arraykeys@gmail.com
35b78c2da6 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-14 11:57:56 +08:00
arraykeys@gmail.com
66a4291c97 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-14 11:56:24 +08:00
arraykeys@gmail.com
e89a965aff Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-14 10:02:51 +08:00
arraykeys@gmail.com
85a9f10be4 Merge branch 'dev' 2017-11-09 13:11:57 +08:00
arraykeys@gmail.com
8bc6e0ffec Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-09 13:11:47 +08:00
arraykeys@gmail.com
98fc0ade4a Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-07 11:31:12 +08:00
arraykeys@gmail.com
f5626c21f7 Merge branch 'dev' 2017-11-07 10:52:31 +08:00
arraykeys@gmail.com
b5a76c7ff2 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-07 10:52:23 +08:00
arraykeys@gmail.com
612bae4c93 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-07 10:46:09 +08:00
arraykeys@gmail.com
e45bf338cb Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-03 19:16:52 +08:00
arraykeys@gmail.com
577261806c Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-03 19:05:32 +08:00
arraykeys@gmail.com
8842097bd4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-03 15:23:25 +08:00
arraykeys@gmail.com
a4b14dd0bd Merge branch 'dev' 2017-11-03 13:51:39 +08:00
arraykeys@gmail.com
94f0142c7d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-03 13:51:27 +08:00
arraykeys@gmail.com
319affa43f Merge branch 'master' of https://github.com/snail007/goproxy.git 2017-11-03 13:30:06 +08:00
arraykeys@gmail.com
14f43a5976 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-03 13:27:36 +08:00
arraykeys@gmail.com
9e9a9ac6de Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-03 12:50:50 +08:00
arraykeys@gmail.com
cc24cfc26f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-03 11:11:53 +08:00
arraykeys@gmail.com
712f7dae4a Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-02 17:52:41 +08:00
arraykeys@gmail.com
6d20908465 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-01 11:20:25 +08:00
arraykeys@gmail.com
e2f0fe71f4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-30 14:20:13 +08:00
arraykeys@gmail.com
1241b74562 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-27 18:35:39 +08:00
arraykeys@gmail.com
2d610003d5 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-27 18:34:36 +08:00
arraykeys@gmail.com
7b2952f4d6 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-27 18:33:00 +08:00
arraykeys@gmail.com
b18c5e08bb Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-27 18:31:39 +08:00
arraykeys@gmail.com
63cb67f009 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-27 18:30:32 +08:00
arraykeys@gmail.com
cd212cb978 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-27 18:29:37 +08:00
arraykeys@gmail.com
d934c1fb4a Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2017-10-27 16:53:59 +08:00
arraykeys@gmail.com
c8327b4075 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-27 16:52:54 +08:00
snail007
8410930d2d Update README.md 2017-10-27 16:29:52 +08:00
arraykeys@gmail.com
f5218a93f6 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2017-10-27 16:06:41 +08:00
arraykeys@gmail.com
ca2f367950 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-27 15:59:49 +08:00
snail007
c8559b757f Merge pull request #9 from snail007/dev
Update README.md
2017-10-26 20:01:14 +08:00
snail007
db620ebe83 Update README.md 2017-10-26 19:59:45 +08:00
arraykeys@gmail.com
abc7a4fa42 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-26 17:13:02 +08:00
arraykeys@gmail.com
b02d75eb3a Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-26 15:45:24 +08:00
arraykeys@gmail.com
2d225a6cdb Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-26 10:34:41 +08:00
arraykeys@gmail.com
ccca75ecbd Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-25 18:01:18 +08:00
arraykeys@gmail.com
2360808604 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-25 17:49:35 +08:00
arraykeys@gmail.com
b7c2b0e8fa Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-25 16:00:17 +08:00
arraykeys@gmail.com
581ff2b840 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-24 20:09:43 +08:00
arraykeys@gmail.com
8a75e202d6 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-24 10:50:38 +08:00
arraykeys@gmail.com
c23d733cfd Merge branch 'master' into dev 2017-10-23 16:41:46 +08:00
arraykeys@gmail.com
aab8b41da9 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-23 16:41:31 +08:00
arraykeys@gmail.com
e9139ee56f Merge branch 'master' into dev 2017-10-23 16:35:23 +08:00
arraykeys@gmail.com
24d3d88980 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-23 16:35:21 +08:00
arraykeys@gmail.com
078acaa0e8 完善内网穿透心跳机制
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2017-10-23 16:28:10 +08:00
arraykeys@gmail.com
96cd7a2b63 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 16:36:43 +08:00
arraykeys@gmail.com
bf095b2c76 Merge branch 'dev' 2017-10-20 12:59:31 +08:00
arraykeys@gmail.com
a80e4df6f0 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 12:59:23 +08:00
arraykeys@gmail.com
3cd0d91c22 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 12:32:50 +08:00
arraykeys@gmail.com
716aaa272d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 12:28:04 +08:00
arraykeys@gmail.com
87c13e4aec Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 12:26:05 +08:00
arraykeys@gmail.com
7005d66ed6 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 12:22:40 +08:00
arraykeys@gmail.com
ba62ce24b8 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 11:54:57 +08:00
arraykeys@gmail.com
2ec7131659 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 11:47:12 +08:00
arraykeys@gmail.com
65f441f5b5 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 11:44:09 +08:00
arraykeys@gmail.com
ee0e85a30f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 11:39:51 +08:00
arraykeys@gmail.com
8cdb5d1857 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 11:36:58 +08:00
arraykeys@gmail.com
5cb8620e82 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 11:36:12 +08:00
arraykeys@gmail.com
f0733655f8 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 11:30:33 +08:00
arraykeys@gmail.com
59a5a4a68a Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 11:27:46 +08:00
arraykeys@gmail.com
b9cd57d873 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 11:09:47 +08:00
arraykeys@gmail.com
9504061e4e Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 11:03:31 +08:00
arraykeys@gmail.com
f41f3a3b63 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 11:00:36 +08:00
arraykeys@gmail.com
8f0c80980c Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 10:40:27 +08:00
arraykeys@gmail.com
e9b46d38e3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 10:39:37 +08:00
arraykeys@gmail.com
3440af51b0 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 10:28:12 +08:00
arraykeys@gmail.com
95db78bc0b Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-20 10:20:28 +08:00
arraykeys@gmail.com
bde10ad8ef Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-19 17:35:27 +08:00
arraykeys@gmail.com
b8c2766639 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-19 15:13:14 +08:00
arraykeys@gmail.com
5bb8bf20fe Merge branch 'master' of git@github.com:snail007/goproxy.git 2017-10-19 14:59:42 +08:00
arraykeys@gmail.com
db71b77da4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-19 14:58:42 +08:00
snail007
af19092a7d Merge pull request #5 from snail007/dev
Dev
2017-10-19 14:57:32 +08:00
arraykeys@gmail.com
3020dc5c94 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-19 14:56:40 +08:00
arraykeys@gmail.com
e28d5449b5 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-18 18:01:53 +08:00
arraykeys@gmail.com
efb075c7ba Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-17 12:27:31 +08:00
arraykeys@gmail.com
31073d398e Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-17 11:12:59 +08:00
arraykeys@gmail.com
8bafb88bc4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-16 20:25:10 +08:00
arraykeys@gmail.com
dc82b94c6b Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-16 20:10:22 +08:00
arraykeys@gmail.com
cf6043b0de Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-16 20:05:16 +08:00
arraykeys@gmail.com
ce1095d6de Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-16 20:00:16 +08:00
arraykeys@gmail.com
9f08170cd3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-14 16:52:06 +08:00
arraykeys@gmail.com
5c66f5f5d2 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-12 12:35:43 +08:00
arraykeys@gmail.com
1a9c3244a3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-11 16:54:27 +08:00
arraykeys@gmail.com
00688bbf33 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-11 15:00:06 +08:00
arraykeys@gmail.com
787cc56ed4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-11 14:59:32 +08:00
arraykeys@gmail.com
3984083e23 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 13:17:55 +08:00
arraykeys@gmail.com
a91790b16d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 13:14:22 +08:00
arraykeys@gmail.com
b2549e8d48 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 13:13:31 +08:00
arraykeys@gmail.com
768e5dd6c0 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 11:07:57 +08:00
arraykeys@gmail.com
7899f1ec00 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 10:12:24 +08:00
arraykeys@gmail.com
ef93946fb1 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 10:08:53 +08:00
arraykeys@gmail.com
675061fd63 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 20:15:24 +08:00
arraykeys@gmail.com
4b212eee0d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:54:15 +08:00
arraykeys@gmail.com
0f81d5e503 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:24:35 +08:00
arraykeys@gmail.com
6616f4f860 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:23:53 +08:00
arraykeys@gmail.com
357a8745de Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:23:23 +08:00
arraykeys@gmail.com
cde72c04df Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:20:39 +08:00
arraykeys@gmail.com
a8767b0e15 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:07:05 +08:00
arraykeys@gmail.com
785762ceb9 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:59:25 +08:00
arraykeys@gmail.com
7383aa0973 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:57:49 +08:00
arraykeys@gmail.com
7d992082fa Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:47:10 +08:00
arraykeys@gmail.com
1d41eadd0b Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:46:29 +08:00
arraykeys@gmail.com
7b247384ec Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:33:09 +08:00
arraykeys@gmail.com
f270885a4d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:11:34 +08:00
arraykeys
5fa000f7e6 no message 2017-10-07 17:31:31 +08:00
arraykeys
ae56bb1edd no message 2017-10-04 12:44:51 +08:00
arraykeys
644ec6891d no message 2017-10-02 15:58:12 +08:00
arraykeys@gmail.com
db729915ad Merge branch 'master' of git@github.com:snail007/goproxy.git 2017-09-30 19:01:02 +08:00
arraykeys@gmail.com
69fcc7d12e Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-30 19:00:46 +08:00
snail007
e90852a401 Update README.md 2017-09-29 14:47:32 +08:00
snail007
71b9940916 Update README.md 2017-09-29 12:55:51 +08:00
snail007
175272744d Update README.md 2017-09-29 12:54:40 +08:00
622 changed files with 99887 additions and 2124 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

179
CHANGELOG
View File

@ -1,8 +1,181 @@
proxy更新日志:
proxy更新日志
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)透明代理.
3.增加了英文手册.
v4.1
1.优化了http(s),socks5代理中的域名智能判断,如果是内网IP,直接走本地网络,提升浏览体验,
同时优化了检查机制,判断更快.
2.http代理basic认证增加了对https协议的支持,现在basic认证可以控制所有http(s)流量了.
3.项目代码增加了依赖类库vendor目录,clone下来就能go build,再也不用担心go get依赖类库
失败导致不能编译了.
v4.0
1.内网穿透三端重构了一个multiplexing版本使用github.com/xtaci/smux实现了tcp链接的多路复用
鼎鼎大名的kcp-go底层就是使用的这个库基于kcp-go的双边加速工具kcptun的广泛使用已经很好
的验证来该库的强大与稳定。multiplexing版的内网穿透对应的子命令分别是serverclientbridge
使用方式和参数与之前的子命令tservertclienttserver完全一样另外serverclient增加了
压缩传输参数--c使用压缩传输速度更快。
v3.9
1.增加了守护运行参数--forever,比如: proxy http --forever ,
proxy会fork子进程,然后监控子进程,如果子进程异常退出,5秒后重启子进程.
该参数配合后台运行参数--daemon和日志参数--log,可以保障proxy一直在后台执行不会因为意外退出,
而且可以通过日志文件看到proxy的输出日志内容.
比如: proxy http -p ":9090" --forever --log proxy.log --daemon
v3.8
1.增加了日志输出到文件--log参数,比如: --log proxy.log,日志就会输出到proxy.log方便排除问题.
v3.7
1.修复了socks代理不能正常和上级代理通讯的问题.
v3.6
1.http(s),socks代理,集成了外部HTTP API认证,可以通过外部API对用户名和密码进行认证.
2.手册http(s),socks代理认证部分增加了集成外部HTTP API认证的使用说明.
v3.5
1.优化了kcp参数,速度有所提升.
2.修复了socks无法正常工作的问题.
3.修正了文档中的一些描述.
4.tcp代理增加了kcp协议传输数据.
5.优化了死循环检查,增加了添加本地IP参数,当VPS在nat设备后面,
vps上网卡IP都是内网IP,这个时候可以通过-g参数添加vps的外网ip防止死循环.
6.增加了--daemon参数,可以后台运行程序哟.
v3.4
1.socks5代理新增了用户名密码验证支持.
2.socks5,http(s)代理增加了kcp传输协议支持.
3.优化了内网穿透的心跳机制.
v3.3
1.修复了socks代理模式对证书文件的判断逻辑.
2.增强了http代理,socks代理的ssh中转模式的稳定性.
3.socks代理tls,tcp模式新增了CMD_ASSOCIATE(udp)支持.socks代理ssh模式不支持udp.
4.修复了http代理某些情况下会崩溃的bug.
v3.2
1.内网穿透功能server端-r参数增加了协议和key设置.
2.手册增加了对-r参数的详细说明.
3.修复了普通模式也检查证书文件的bug.
4.增加了Socks5支持,目前只支持TCP协议,不支持UDP协议.
5.Socks5上级代理支持ssh中转,linux服务器不需要任何服务端,本地一个proxy即可开心上网.
6.http(s)代理增加了ssh中转支持,linux服务器不需要任何服务端,本地一个proxy即可开心上网.
v3.1
1.优化了内网穿透功能,bridge,client和server只需要启动一个即可。
server端启动的时候可以指定client端要暴露的一个或者多个端口。
2.修复了重复解析命令行参数的问题。
3.手册增加了微信接口本地开发的示例。
4.增加了配置文件使用说明.
v3.0
1.此次更新不兼容2.x版本,重构了全部代码,架构更合理,利于功能模块的增加与维护。
2.增加了代理死循环检查,增强了安全性。
3.增加了反向代理模式(即:内网穿透),支持TCP和UDP两种协议,可以把任何局域网的机器A所在网络的任何端口
3.增加了反向代理模式(即:内网穿透),支持TCP和UDP两种协议,可以把任何局域网的机器A所在网络的任何端
暴露到任何局域网的机器B的本地端口或暴露到任何公网VPS上。
4.正向代理增加了UDP模式支持。
@ -21,7 +194,7 @@ v2.1
v2.0
1.增加了连接池功能,大幅提高了通过上级代理访问的速度。
2.HTTP代理模式,优化了请求URL的获取逻辑,可以支持:http,https,websocket
2.HTTP代理模式,优化了请求URL的获取逻辑,可以支持:http,https,websocke
3.增加了TCP代理模式,支持是否加密通讯。
4.优化了链接关闭逻辑,避免出现大量CLOSE_WAIT。
5.增加了黑白名单机制,更自由快速的访问。

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}

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

1170
README.md

File diff suppressed because it is too large Load Diff

1189
README_ZH.md Normal file

File diff suppressed because it is too large Load Diff

499
config.go
View File

@ -1,50 +1,95 @@
package main
import (
"bufio"
"crypto/sha1"
"fmt"
"io/ioutil"
"log"
logger "log"
"os"
"proxy/services"
"proxy/utils"
"os/exec"
"path"
"path/filepath"
"runtime/pprof"
"time"
sdk "github.com/snail007/goproxy/sdk/android-ios"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
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"
tunnel "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
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)
}
}
args := services.Args{}
//define args
tcpArgs := services.TCPArgs{}
httpArgs := services.HTTPArgs{}
tunnelServerArgs := services.TunnelServerArgs{}
tunnelClientArgs := services.TunnelClientArgs{}
tunnelBridgeArgs := services.TunnelBridgeArgs{}
udpArgs := services.UDPArgs{}
tcpArgs := tcpx.TCPArgs{}
httpArgs := httpx.HTTPArgs{}
tunnelServerArgs := tunnel.TunnelServerArgs{}
tunnelClientArgs := tunnel.TunnelClientArgs{}
tunnelBridgeArgs := tunnel.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)
args.Parent = app.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
args.Local = app.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
certTLS := app.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
keyTLS := app.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
debug := 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.LocalType = http.Flag("local-type", "parent protocol type <tls|tcp>").Default("tcp").Short('t').Enum("tls", "tcp")
httpArgs.ParentType = http.Flag("parent-type", "parent protocol type <tls|tcp>").Short('T').Enum("tls", "tcp")
httpArgs.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
httpArgs.CaCertFile = http.Flag("ca", "ca cert file for tls").Default("").String()
httpArgs.CertFile = http.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
httpArgs.KeyFile = http.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
httpArgs.LocalType = http.Flag("local-type", "local protocol type <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()
@ -53,66 +98,386 @@ 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("20").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.LocalIPS = http.Flag("local-bind-ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
httpArgs.AuthURL = http.Flag("auth-url", "http basic auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
httpArgs.AuthURLTimeout = http.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
httpArgs.AuthURLOkCode = http.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
httpArgs.AuthURLRetry = http.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("1").Int()
httpArgs.DNSAddress = http.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String()
httpArgs.DNSTTL = http.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
httpArgs.LocalKey = http.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String()
httpArgs.ParentKey = http.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
httpArgs.LocalCompress = http.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool()
httpArgs.ParentCompress = http.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
//########tcp#########
tcp := app.Command("tcp", "proxy on tcp mode")
tcpArgs.Timeout = tcp.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('t').Default("2000").Int()
tcpArgs.ParentType = tcp.Flag("parent-type", "parent protocol type <tls|tcp|udp>").Short('T').Enum("tls", "tcp", "udp")
tcpArgs.IsTLS = tcp.Flag("tls", "proxy on tls mode").Default("false").Bool()
tcpArgs.PoolSize = tcp.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("20").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.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.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.PoolSize = udp.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("20").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|kcp>").Default("tls").Short('T').Enum("tls", "tcp", "kcp")
muxServerArgs.CertFile = muxServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
muxServerArgs.KeyFile = muxServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxServerArgs.Timeout = muxServer.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
muxServerArgs.IsUDP = muxServer.Flag("udp", "proxy on udp mux server mode").Default("false").Bool()
muxServerArgs.Key = muxServer.Flag("k", "client key").Default("default").String()
muxServerArgs.Route = muxServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings()
muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp|tls mode").Default("false").Bool()
muxServerArgs.SessionCount = muxServer.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
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()
//########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|kcp>").Default("tls").Short('T').Enum("tls", "tcp", "kcp")
muxClientArgs.CertFile = muxClient.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
muxClientArgs.KeyFile = muxClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxClientArgs.Timeout = muxClient.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
muxClientArgs.Key = muxClient.Flag("k", "key same with server").Default("default").String()
muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp|tls mode").Default("false").Bool()
muxClientArgs.SessionCount = muxClient.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
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()
//########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|kcp>").Default("tls").Short('t').Enum("tls", "tcp", "kcp")
//########tunnel-server#########
tunnelServer := app.Command("tserver", "proxy on tunnel server mode")
tunnelServerArgs.Parent = tunnelServer.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
tunnelServerArgs.CertFile = tunnelServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
tunnelServerArgs.KeyFile = tunnelServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tunnelServerArgs.Timeout = tunnelServer.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
tunnelServerArgs.IsUDP = tunnelServer.Flag("udp", "proxy on udp tunnel server mode").Default("false").Bool()
tunnelServerArgs.Key = tunnelServer.Flag("k", "key same with client").Default("default").String()
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.IsUDP = tunnelClient.Flag("udp", "proxy on udp tunnel client mode").Default("false").Bool()
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()
kingpin.MustParse(app.Parse(os.Args[1:]))
//########ssh#########
socks := app.Command("socks", "proxy on ssh mode")
socksArgs.Parent = socks.Flag("parent", "parent ssh address, such as: \"23.32.32.19:22\"").Default("").Short('P').String()
socksArgs.ParentType = socks.Flag("parent-type", "parent protocol type <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('A').Default("").String()
socksArgs.Always = socks.Flag("always", "always use parent proxy").Default("false").Bool()
socksArgs.Timeout = socks.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("5000").Int()
socksArgs.Interval = socks.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int()
socksArgs.Blocked = socks.Flag("blocked", "blocked domain file , one domain each line").Default("blocked").Short('b').String()
socksArgs.Direct = socks.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
socksArgs.AuthFile = socks.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
socksArgs.Auth = socks.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
socksArgs.LocalIPS = socks.Flag("local-bind-ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
socksArgs.AuthURL = socks.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
socksArgs.AuthURLTimeout = socks.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
socksArgs.AuthURLOkCode = socks.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
socksArgs.AuthURLRetry = socks.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int()
socksArgs.DNSAddress = socks.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String()
socksArgs.DNSTTL = socks.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
socksArgs.LocalKey = socks.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String()
socksArgs.ParentKey = socks.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
socksArgs.LocalCompress = socks.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool()
socksArgs.ParentCompress = socks.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
if *certTLS != "" && *keyTLS != "" {
args.CertBytes, args.KeyBytes = tlsBytes(*certTLS, *keyTLS)
//########socks+http(s)#########
sps := app.Command("sps", "proxy on socks+http(s) mode")
spsArgs.Parent = sps.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
spsArgs.CertFile = sps.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
spsArgs.KeyFile = sps.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
spsArgs.CaCertFile = sps.Flag("ca", "ca cert file for tls").Default("").String()
spsArgs.Timeout = sps.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('i').Default("2000").Int()
spsArgs.ParentType = sps.Flag("parent-type", "parent protocol type <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>").Short('S').Enum("http", "socks")
spsArgs.DNSAddress = sps.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String()
spsArgs.DNSTTL = sps.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
spsArgs.AuthFile = sps.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
spsArgs.Auth = sps.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
spsArgs.LocalIPS = sps.Flag("local-bind-ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
spsArgs.AuthURL = sps.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
spsArgs.AuthURLTimeout = sps.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
spsArgs.AuthURLOkCode = sps.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
spsArgs.AuthURLRetry = sps.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int()
spsArgs.ParentAuth = sps.Flag("parent-auth", "parent socks auth username and password, such as: -A user1:pass1").Short('A').String()
spsArgs.LocalKey = sps.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String()
spsArgs.ParentKey = sps.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
spsArgs.LocalCompress = sps.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool()
spsArgs.ParentCompress = sps.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
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()
//########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:]))
isDebug = *debug
//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
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 |= logger.Ltime
}
log.SetFlags(flags)
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)
}
if *daemon {
args := []string{}
for _, arg := range os.Args[1:] {
if arg != "--daemon" {
args = append(args, arg)
}
}
cmd = exec.Command(os.Args[0], args...)
cmd.Start()
f := ""
if *forever {
f = "forever "
}
log.Printf("%s%s [PID] %d running...\n", f, os.Args[0], cmd.Process.Pid)
os.Exit(0)
}
if *forever {
args := []string{}
for _, arg := range os.Args[1:] {
if arg != "--forever" {
args = append(args, arg)
}
}
go func() {
for {
if cmd != nil {
cmd.Process.Kill()
time.Sleep(time.Second * 5)
}
cmd = exec.Command(os.Args[0], args...)
cmdReaderStderr, err := cmd.StderrPipe()
if err != nil {
log.Printf("ERR:%s,restarting...\n", err)
continue
}
cmdReader, err := cmd.StdoutPipe()
if err != nil {
log.Printf("ERR:%s,restarting...\n", err)
continue
}
scanner := bufio.NewScanner(cmdReader)
scannerStdErr := bufio.NewScanner(cmdReaderStderr)
go func() {
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}()
go func() {
for scannerStdErr.Scan() {
fmt.Println(scannerStdErr.Text())
}
}()
if err := cmd.Start(); err != nil {
log.Printf("ERR:%s,restarting...\n", err)
continue
}
pid := cmd.Process.Pid
log.Printf("worker %s [PID] %d running...\n", os.Args[0], pid)
if err := cmd.Wait(); err != nil {
log.Printf("ERR:%s,restarting...", err)
continue
}
log.Printf("worker %s [PID] %d unexpected exited, restarting...\n", os.Args[0], pid)
}
}()
return
}
if *logfile == "" {
poster()
if *debug {
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
//regist services and run service
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, tunnel.NewTunnelServerManager(), tunnelServerArgs, log)
case "tclient":
services.Regist(serviceName, tunnel.NewTunnelClient(), tunnelClientArgs, log)
case "tbridge":
services.Regist(serviceName, tunnel.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)
}
//common args
httpArgs.Args = args
tcpArgs.Args = args
udpArgs.Args = args
tunnelBridgeArgs.Args = args
tunnelClientArgs.Args = args
tunnelServerArgs.Args = args
poster()
//regist services and run service
serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
services.Regist("http", services.NewHTTP(), httpArgs)
services.Regist("tcp", services.NewTCP(), tcpArgs)
services.Regist("udp", services.NewUDP(), udpArgs)
services.Regist("tserver", services.NewTunnelServer(), tunnelServerArgs)
services.Regist("tclient", services.NewTunnelClient(), tunnelClientArgs)
services.Regist("tbridge", services.NewTunnelBridge(), tunnelBridgeArgs)
service, err = services.Run(serviceName)
service, err = services.Run(serviceName, nil)
if err != nil {
log.Fatalf("run service [%s] fail, ERR:%s", service, err)
log.Fatalf("run service [%s] fail, ERR:%s", serviceName, err)
}
return
}
@ -129,16 +494,14 @@ func poster() {
v%s`+" by snail , blog : http://www.host900.com/\n\n", APP_VERSION)
}
func tlsBytes(cert, key string) (certBytes, keyBytes []byte) {
certBytes, err := ioutil.ReadFile(cert)
if err != nil {
log.Fatalf("err : %s", err)
return
}
keyBytes, err = ioutil.ReadFile(key)
if err != nil {
log.Fatalf("err : %s", err)
return
}
return
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()
}

View File

@ -1,40 +0,0 @@
这里以vps centos 64位为例子
Linux 部分
1.Putty工具或其他工具
root登入
2.下载批量命令文件install_auto.sh64位的话直接执行这个命令即可  
#curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.sh | bash
注意
这里的install_auto.sh 源码可以下载修改proxy版本,保存后执行.
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image001.png?raw=true"/>
3.修改/etc/proxy/proxy.toml配置文件
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image002.png?raw=true"/>
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image003.png?raw=true"/>
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image004.png?raw=true"/>
#/usr/bin/proxyd status
如果未运行那么执行调试命令:/usr/bin/proxy  
如果一切正常,可以使用proxyd命令管理proxy,执行 proxyd 可以查看用法.
后台启动proxy: proxyd start
4.下载证书加密文件/etc/proxy/proxy.crt和/etc/proxy/proxy.key到windows  
Windows部分  
5.https://github.com/snail007/goproxy/releases 下载对应windows版本
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image005.jpg?raw=true"/>
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image006.png?raw=true"/>
我的是d
6.修改windows下的proxy.toml vps服务ip和上面设置的端口哦
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image007.png?raw=true"/>
然后运行proxy.exe即可.  
这时候浏览器代理服务器就是127.0.0.1:9501啦,完毕!
要隐藏windows命令用工具下载RunHiddenConsole.exe 写个bat文件都放proxy目录下就行
Start.bat
@echo off
echo Starting
RunHiddenConsole D:/proxy/proxy.exe
Stop.bat
@echo off
echo Stopping
taskkill /F /IM proxy.exe > nul
exit

BIN
docs/images/1.1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
docs/images/2.1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/images/2.2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/images/5.2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
docs/images/alipay.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 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

BIN
docs/images/wxpay.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 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

@ -1,16 +1,6 @@
#!/bin/bash
set -e
if [ -e /tmp/proxy ]; then
rm -rf /tmp/proxy
fi
mkdir /tmp/proxy
cd /tmp/proxy
# install monexec
tar zxvf monexec_0.1.1_linux_amd64.tar.gz
cd monexec_0.1.1_linux_amd64
cp monexec /usr/bin/
chmod +x /usr/bin/monexec
cd ..
# #install proxy
tar zxvf proxy-linux-amd64.tar.gz
cp proxy /usr/bin/

View File

@ -5,15 +5,8 @@ if [ -e /tmp/proxy ]; then
fi
mkdir /tmp/proxy
cd /tmp/proxy
wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
wget https://github.com/snail007/goproxy/releases/download/v3.0/proxy-linux-amd64.tar.gz
wget https://github.com/snail007/goproxy/releases/download/v5.4/proxy-linux-amd64.tar.gz
# install monexec
tar zxvf monexec_0.1.1_linux_amd64.tar.gz
cd monexec_0.1.1_linux_amd64
cp monexec /usr/bin/
chmod +x /usr/bin/monexec
cd ..
# #install proxy
tar zxvf proxy-linux-amd64.tar.gz
cp proxy /usr/bin/
@ -26,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

25
main.go
View File

@ -1,22 +1,26 @@
package main
import (
"fmt"
"log"
"os"
"os/signal"
"proxy/services"
"syscall"
"github.com/snail007/goproxy/services"
)
const APP_VERSION = "3.0"
const APP_VERSION = "5.4"
func main() {
err := initConfig()
if err != nil {
log.Fatalf("err : %s", err)
}
Clean(&service.S)
if service != nil && service.S != nil {
Clean(&service.S)
} else {
Clean(nil)
}
}
func Clean(s *services.Service) {
signalChan := make(chan os.Signal, 1)
@ -29,8 +33,17 @@ func Clean(s *services.Service) {
syscall.SIGQUIT)
go func() {
for _ = range signalChan {
fmt.Println("\nReceived an interrupt, stopping services...")
(*s).Clean()
log.Println("Received an interrupt, stopping services...")
if s != nil && *s != nil {
(*s).Clean()
}
if cmd != nil {
log.Printf("clean process %d", cmd.Process.Pid)
cmd.Process.Kill()
}
if isDebug {
saveProfiling()
}
cleanupDone <- true
}
}()

View File

@ -1,63 +1,74 @@
#!/bin/bash
VER="3.0"
VER="5.4"
RELEASE="release-${VER}"
rm -rf .cert
mkdir .cert
go build
go build -o proxy
cd .cert
../proxy keygen
../proxy keygen -C proxy
cd ..
rm -rf ${RELEASE}
mkdir ${RELEASE}
set CGO_ENABLED=0
#linux
GOOS=linux GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-linux-386.tar.gz" proxy direct blocked
GOOS=linux GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-linux-amd64.tar.gz" proxy direct blocked
GOOS=linux GOARCH=arm GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm.tar.gz" proxy direct blocked
GOOS=linux GOARCH=arm64 GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm64.tar.gz" proxy direct blocked
GOOS=linux GOARCH=mips go build && tar zcfv "${RELEASE}/proxy-linux-mips.tar.gz" proxy direct blocked
GOOS=linux GOARCH=mips64 go build && tar zcfv "${RELEASE}/proxy-linux-mips64.tar.gz" proxy direct blocked
GOOS=linux GOARCH=mips64le go build && tar zcfv "${RELEASE}/proxy-linux-mips64le.tar.gz" proxy direct blocked
GOOS=linux GOARCH=mipsle go build && tar zcfv "${RELEASE}/proxy-linux-mipsle.tar.gz" proxy direct blocked
GOOS=linux GOARCH=ppc64 go build && tar zcfv "${RELEASE}/proxy-linux-ppc64.tar.gz" proxy direct blocked
GOOS=linux GOARCH=ppc64le go build && tar zcfv "${RELEASE}/proxy-linux-ppc64le.tar.gz" proxy direct blocked
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 -o proxy && tar zcfv "${RELEASE}/proxy-linux-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm-v6.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=6 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm64-v6.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm-v7.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=7 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm64-v7.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm-v5.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=5 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-arm64-v5.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-mips64le.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-mipsle.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=ppc64 go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-ppc64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-ppc64le.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=linux GOARCH=s390x go build -o proxy && tar zcfv "${RELEASE}/proxy-linux-s390x.tar.gz" proxy direct blocked
#android
GOOS=android GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-android-386.tar.gz" proxy direct blocked
GOOS=android GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-android-amd64.tar.gz" proxy direct blocked
GOOS=android GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-android-arm.tar.gz" proxy direct blocked
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 -o proxy && tar zcfv "${RELEASE}/proxy-android-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=android GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-android-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=android GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-android-arm.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=android GOARCH=arm64 go build -o proxy && tar zcfv "${RELEASE}/proxy-android-arm64.tar.gz" proxy direct blocked
#darwin
GOOS=darwin GOARCH=386 go build go build && tar zcfv "${RELEASE}/proxy-darwin-386.tar.gz" proxy direct blocked
GOOS=darwin GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-darwin-amd64.tar.gz" proxy direct blocked
GOOS=darwin GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-darwin-arm.tar.gz" proxy direct blocked
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 -o proxy && tar zcfv "${RELEASE}/proxy-darwin-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-darwin-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=darwin GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-darwin-arm.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o proxy && tar zcfv "${RELEASE}/proxy-darwin-arm64.tar.gz" proxy direct blocked
#dragonfly
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 -o proxy && tar zcfv "${RELEASE}/proxy-dragonfly-amd64.tar.gz" proxy direct blocked
#freebsd
GOOS=freebsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-freebsd-386.tar.gz" proxy direct blocked
GOOS=freebsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-freebsd-amd64.tar.gz" proxy direct blocked
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 -o proxy && tar zcfv "${RELEASE}/proxy-freebsd-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-freebsd-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=freebsd GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-freebsd-arm.tar.gz" proxy direct blocked
#nacl
GOOS=nacl GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-nacl-386.tar.gz" proxy direct blocked
GOOS=nacl GOARCH=amd64p32 go build && tar zcfv "${RELEASE}/proxy-nacl-amd64p32.tar.gz" proxy direct blocked
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 -o proxy && tar zcfv "${RELEASE}/proxy-nacl-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=nacl GOARCH=amd64p32 go build -o proxy && tar zcfv "${RELEASE}/proxy-nacl-amd64p32.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=nacl GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-nacl-arm.tar.gz" proxy direct blocked
#netbsd
GOOS=netbsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-netbsd-386.tar.gz" proxy direct blocked
GOOS=netbsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-netbsd-amd64.tar.gz" proxy direct blocked
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 -o proxy && tar zcfv "${RELEASE}/proxy-netbsd-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=netbsd GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-netbsd-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=netbsd GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-netbsd-arm.tar.gz" proxy direct blocked
#openbsd
GOOS=openbsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-openbsd-386.tar.gz" proxy direct blocked
GOOS=openbsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-openbsd-amd64.tar.gz" proxy direct blocked
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 -o proxy && tar zcfv "${RELEASE}/proxy-openbsd-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=openbsd GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-openbsd-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=openbsd GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-openbsd-arm.tar.gz" proxy direct blocked
#plan9
GOOS=plan9 GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-plan9-386.tar.gz" proxy direct blocked
GOOS=plan9 GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-plan9-amd64.tar.gz" proxy direct blocked
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 -o proxy && tar zcfv "${RELEASE}/proxy-plan9-386.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=plan9 GOARCH=amd64 go build -o proxy && tar zcfv "${RELEASE}/proxy-plan9-amd64.tar.gz" proxy direct blocked
CGO_ENABLED=0 GOOS=plan9 GOARCH=arm go build -o proxy && tar zcfv "${RELEASE}/proxy-plan9-arm.tar.gz" proxy direct blocked
#solaris
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 -o proxy && tar zcfv "${RELEASE}/proxy-solaris-amd64.tar.gz" proxy direct blocked
#windows
GOOS=windows GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-windows-386.tar.gz" proxy.exe direct blocked .cert/proxy.crt .cert/proxy.key
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 -ldflags="-H=windowsgui" -o proxy-noconsole.exe
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -o proxy.exe && tar zcfv "${RELEASE}/proxy-windows-386.tar.gz" proxy.exe proxy-noconsole.exe direct blocked .cert/proxy.crt .cert/proxy.key
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-H=windowsgui" -o proxy-noconsole.exe
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o proxy.exe && tar zcfv "${RELEASE}/proxy-windows-amd64.tar.gz" proxy.exe proxy-noconsole.exe direct blocked .cert/proxy.crt .cert/proxy.key
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

21
sdk/CHANGELOG Normal file
View File

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

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.Proxy
```
#### 2.启动一个服务
```java
String serviceID="http01";//这里serviceID是自定义的唯一标识字符串,保证每个启动的服务不一样即可
String serviceArgs="http -p :8080";
String err=Proxy.start(serviceID,serviceArgs);
if (!err.isEmpty()){
//启动失败
System.out.println("start fail,error:"+err);
}else{
//启动成功
}
```
#### 3.停止一个服务
```java
String serviceID="http01";
Proxy.stop(serviceID);
//停止完毕
```
## IOS SDK
[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy-sdk-ios/) [![license](https://img.shields.io/github/license/snail007/goproxy-sdk-ios.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy-sdk-ios/total.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-ios/releases) [![download](https://img.shields.io/github/release/snail007/goproxy-sdk-ios.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-ios/releases)
[点击下载IOS-SDK](https://github.com/snail007/goproxy-sdk-ios/releases)
在IOS系统提供的sdk形式是一个后缀为.framework的类库文件夹,开发的时候只需要把类库文件引入项目,然后调用方法即可.
### IOS-SDK使用实例
#### 导入包
```objc
#import <Proxy/Proxy.objc.h>
```
#### 2.启动一个服务
```objc
-(IBAction)doStart:(id)sender
{
//这里serviceID是自定义的唯一标识字符串,保证每个启动的服务不一样
NSString *serviceID = @"http01";
NSString *serviceArgs = @"http -p :8080";
NSString *error = ProxyStart(serviceID,serviceArgs);
if (error != nil && error.length > 0)
{
NSLog(@"start error %@",error);
}else{
NSLog(@"启动成功");
}
}
```
#### 3.停止一个服务
```objc
-(IBAction)doStop:(id)sender
{
NSString *serviceID = @"http01";
ProxyStop(serviceID);
//停止完毕
}
```
## Windows SDK
[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy-sdk-windows/) [![license](https://img.shields.io/github/license/snail007/goproxy-sdk-windows.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy-sdk-windows/total.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-windows/releases) [![download](https://img.shields.io/github/release/snail007/goproxy-sdk-windows.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-windows/releases)
[点击下载Windows-SDK](https://github.com/snail007/goproxy-sdk-windows/releases)
在Windows系统提供的sdk形式是一个后缀为.dll的类库文件,开发的时候只需要把dll类库文件加载,然后调用方法即可.
### Windows-SDK使用实例
C++示例不需要包含头文件只需要加载proxy-sdk.dll即可ieshims.dll需要和proxy-sdk.dll在一起。
作者:[yjbdsky](https://github.com/yjbdsky)
```cpp
#include <stdio.h>
#include<stdlib.h>
#include <string.h>
#include<pthread.h>
#include<Windows.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef char *(*GOSTART)(char *s);
typedef char *(*GOSTOP)(char *s);
typedef int(*GOISRUN)(char *s);
HMODULE GODLL = LoadLibrary("proxy-sdk.dll");
char * Start(char * p0,char * p1)
{
if (GODLL != NULL)
{
GOSTART gostart = *(GOSTART)(GetProcAddress(GODLL, "Start"));
if (gostart != NULL){
printf("%s:%s\n",p0, p1);
char *ret = gostart(p0,p1);
return ret;
}
}
return "Cannot Find dll";
}
char * Stop(char * p)
{
if (GODLL != NULL)
{
GOSTOP gostop = *(GOSTOP)(GetProcAddress(GODLL, "Stop"));
if (gostop != NULL){
printf("%s\n", p);
char *ret = gostop(p);
return ret;
}
}
return "Cannot Find dll";
}
int main()
{
//这里p0是自定义的唯一标识字符串,保证每个启动的服务不一样
char *p0 = "http01";
char *p1 = "http -t tcp -p :38080";
printf("This is demo application.\n");
//启动服务,返回空字符串说明启动成功;返回非空字符串说明启动失败,返回的字符串是错误原因
printf("start result %s\n", Start(p0,p1));
//停止服务,没有返回值
Stop(p0);
return 0;
}
#ifdef __cplusplus
}
#endif
```
C++示例2请移步[GoProxyForC](https://github.com/SuperPowerLF2/GoProxyForC)
## Linux SDK
[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy-sdk-linux/) [![license](https://img.shields.io/github/license/snail007/goproxy-sdk-linux.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy-sdk-linux/total.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-linux/releases) [![download](https://img.shields.io/github/release/snail007/goproxy-sdk-linux.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-linux/releases)
[点击下载Linux-SDK](https://github.com/snail007/goproxy-sdk-linux/releases)
在Linux系统提供的sdk形式是一个后缀为.so的类库文件,开发的时候只需要把so类库加载,调用方法即可.
### Linux-SDK使用实例
Linux下面使用的sdk是so文件即libproxy-sdk.so,下面写一个简单的C程序示例,调用so库里面的方法.
`vi test-proxy.c`
```c
#include <stdio.h>
#include "libproxy-sdk.h"
int main() {
printf("This is demo application.\n");
//这里p0是自定义的唯一标识字符串,保证每个启动的服务不一样
char *p0 = "http01";
char *p1 = "http -t tcp -p :38080";
//启动服务,返回空字符串说明启动成功;返回非空字符串说明启动失败,返回的字符串是错误原因
printf("start result %s\n",Start(p0,p1));
//停止服务,没有返回值
Stop(p0);
return 0;
}
```
#### 编译test-proxy.c ####
`export LD_LIBRARY_PATH=./ && gcc -o test-proxy test.c libproxy-sdk.so`
#### 执行 ####
`./test-proxy`
## MacOS SDK
[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy-sdk-mac/) [![license](https://img.shields.io/github/license/snail007/goproxy-sdk-mac.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy-sdk-mac/total.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-mac/releases) [![download](https://img.shields.io/github/release/snail007/goproxy-sdk-mac.svg?style=plastic)](https://github.com/snail007/goproxy-sdk-mac/releases)
[点击下载MacOS-SDK](https://github.com/snail007/goproxy-sdk-mac/releases)
在MacOS系统提供的sdk形式是一个后缀为.dylib的类库文件,开发的时候只需要把so类库加载,调用方法即可.
### MacOS-SDK使用实例
MacOS下面使用的sdk是dylib文件即libproxy-sdk.dylib,下面写一个简单的Obj-C程序示例,调用dylib库里面的方法.
```objc
#import "libproxy-sdk.h"
-(IBAction)doStart:(id)sender
{
char *result = Start("http01", "http -t tcp -p :38080");
if (result)
{
printf("started");
}else{
printf("not started");
}
}
-(IBAction)doStop:(id)sender
{
Stop("http01");
}
```
### 关于服务
proxy的服务有11种,分别是:
```shell
http
socks
sps
tcp
udp
bridge
server
client
tbridge
tserver
tclient
```
服务启动时,如果存在正在运行的相同ID的服务,那么之前的服务会被停掉,后面启动的服务覆盖之前的服务.
所以要保证每次启动服务的时候,第一个ID参数唯一.
上面这些服务的具体使用方式和具体参数,可以参考[proxy手册](https://github.com/snail007/goproxy/blob/master/README_ZH.md)
sdk里面的服务不支持手册里面的--daemon和--forever参数.

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

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

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

@ -0,0 +1,256 @@
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"
services "github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
)
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() {
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: 2 * 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 stopped")
}
}()
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() {
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,24 @@
#/bin/bash
VER="v5.3"
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"
mv proxy.aar android/snail007.goproxy.sdk.aar
mv proxy-sources.jar android/snail007.goproxy.sdk-sources.jar
cp ../README.md android
tar zcfv sdk-android-${VER}.tar.gz android
rm -rf android
echo "done."

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

@ -0,0 +1,14 @@
#/bin/bash
VER="v5.3"
rm -rf sdk-ios-*.tar.gz
rm -rf ios
mkdir ios
#ios XCode required
gomobile bind -v -target=ios -ldflags="-s -w"
mv Proxy.framework ios
cp ../README.md ios
tar zcfv sdk-ios-${VER}.tar.gz ios
rm -rf ios
echo "done."

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

@ -0,0 +1,421 @@
package proxy
import (
"crypto/sha1"
"fmt"
"io/ioutil"
logger "log"
"os"
"path"
"path/filepath"
"strings"
"github.com/snail007/goproxy/services"
httpx "github.com/snail007/goproxy/services/http"
"github.com/snail007/goproxy/services/kcpcfg"
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"
tunnel "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"
)
const SDK_VERSION = "5.3"
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 := tunnel.TunnelServerArgs{}
tunnelClientArgs := tunnel.TunnelClientArgs{}
tunnelBridgeArgs := tunnel.TunnelBridgeArgs{}
muxServerArgs := mux.MuxServerArgs{}
muxClientArgs := mux.MuxClientArgs{}
muxBridgeArgs := mux.MuxBridgeArgs{}
udpArgs := udpx.UDPArgs{}
socksArgs := socksx.SocksArgs{}
spsArgs := spsx.SPSArgs{}
dnsArgs := DNSArgs{}
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("fast3").Enum("fast3", "fast2", "fast", "normal", "manual")
kcpArgs.MTU = app.Flag("kcp-mtu", "set maximum transmission unit for UDP packets").Default("1350").Int()
kcpArgs.SndWnd = app.Flag("kcp-sndwnd", "set send window size(num of packets)").Default("1024").Int()
kcpArgs.RcvWnd = app.Flag("kcp-rcvwnd", "set receive window size(num of packets)").Default("1024").Int()
kcpArgs.DataShard = app.Flag("kcp-ds", "set reed-solomon erasure coding - datashard").Default("10").Int()
kcpArgs.ParityShard = app.Flag("kcp-ps", "set reed-solomon erasure coding - parityshard").Default("3").Int()
kcpArgs.DSCP = app.Flag("kcp-dscp", "set DSCP(6bit)").Default("0").Int()
kcpArgs.NoComp = app.Flag("kcp-nocomp", "disable compression").Default("false").Bool()
kcpArgs.AckNodelay = app.Flag("kcp-acknodelay", "be carefull! flush ack immediately when a packet is received").Default("true").Bool()
kcpArgs.NoDelay = app.Flag("kcp-nodelay", "be carefull!").Default("0").Int()
kcpArgs.Interval = app.Flag("kcp-interval", "be carefull!").Default("50").Int()
kcpArgs.Resend = app.Flag("kcp-resend", "be carefull!").Default("0").Int()
kcpArgs.NoCongestion = app.Flag("kcp-nc", "be carefull! no congestion").Default("0").Int()
kcpArgs.SockBuf = app.Flag("kcp-sockbuf", "be carefull!").Default("4194304").Int()
kcpArgs.KeepAlive = app.Flag("kcp-keepalive", "be carefull!").Default("10").Int()
//########http#########
http := app.Command("http", "proxy on http mode")
httpArgs.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
httpArgs.CaCertFile = http.Flag("ca", "ca cert file for tls").Default("").String()
httpArgs.CertFile = http.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
httpArgs.KeyFile = http.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
httpArgs.LocalType = http.Flag("local-type", "local protocol type <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()
//########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.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|kcp>").Default("tls").Short('T').Enum("tls", "tcp", "kcp")
muxServerArgs.CertFile = muxServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
muxServerArgs.KeyFile = muxServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxServerArgs.Timeout = muxServer.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
muxServerArgs.IsUDP = muxServer.Flag("udp", "proxy on udp mux server mode").Default("false").Bool()
muxServerArgs.Key = muxServer.Flag("k", "client key").Default("default").String()
muxServerArgs.Route = muxServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings()
muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp|tls mode").Default("false").Bool()
muxServerArgs.SessionCount = muxServer.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
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()
//########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|kcp>").Default("tls").Short('T').Enum("tls", "tcp", "kcp")
muxClientArgs.CertFile = muxClient.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
muxClientArgs.KeyFile = muxClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxClientArgs.Timeout = muxClient.Flag("timeout", "tcp timeout with milliseconds").Short('i').Default("2000").Int()
muxClientArgs.Key = muxClient.Flag("k", "key same with server").Default("default").String()
muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp|tls mode").Default("false").Bool()
muxClientArgs.SessionCount = muxClient.Flag("session-count", "session count which connect to bridge").Short('n').Default("10").Int()
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()
//########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|kcp>").Default("tls").Short('t').Enum("tls", "tcp", "kcp")
//########tunnel-server#########
tunnelServer := app.Command("tserver", "proxy on tunnel server mode")
tunnelServerArgs.Parent = tunnelServer.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
tunnelServerArgs.CertFile = tunnelServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
tunnelServerArgs.KeyFile = tunnelServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tunnelServerArgs.Timeout = tunnelServer.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
tunnelServerArgs.IsUDP = tunnelServer.Flag("udp", "proxy on udp tunnel server mode").Default("false").Bool()
tunnelServerArgs.Key = tunnelServer.Flag("k", "client key").Default("default").String()
tunnelServerArgs.Route = tunnelServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings()
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').String()
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('A').Default("").String()
socksArgs.Always = socks.Flag("always", "always use parent proxy").Default("false").Bool()
socksArgs.Timeout = socks.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("5000").Int()
socksArgs.Interval = socks.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int()
socksArgs.Blocked = socks.Flag("blocked", "blocked domain file , one domain each line").Default("blocked").Short('b').String()
socksArgs.Direct = socks.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
socksArgs.AuthFile = socks.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
socksArgs.Auth = socks.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
socksArgs.LocalIPS = socks.Flag("local-bind-ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
socksArgs.AuthURL = socks.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
socksArgs.AuthURLTimeout = socks.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
socksArgs.AuthURLOkCode = socks.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
socksArgs.AuthURLRetry = socks.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int()
socksArgs.DNSAddress = socks.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String()
socksArgs.DNSTTL = socks.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
socksArgs.LocalKey = socks.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String()
socksArgs.ParentKey = socks.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
socksArgs.LocalCompress = socks.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool()
socksArgs.ParentCompress = socks.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
//########socks+http(s)#########
sps := app.Command("sps", "proxy on socks+http(s) mode")
spsArgs.Parent = sps.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
spsArgs.CertFile = sps.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
spsArgs.KeyFile = sps.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
spsArgs.CaCertFile = sps.Flag("ca", "ca cert file for tls").Default("").String()
spsArgs.Timeout = sps.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('i').Default("2000").Int()
spsArgs.ParentType = sps.Flag("parent-type", "parent protocol type <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>").Short('S').Enum("http", "socks")
spsArgs.DNSAddress = sps.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String()
spsArgs.DNSTTL = sps.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int()
spsArgs.AuthFile = sps.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
spsArgs.Auth = sps.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
spsArgs.LocalIPS = sps.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
spsArgs.AuthURL = sps.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
spsArgs.AuthURLTimeout = sps.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
spsArgs.AuthURLOkCode = sps.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
spsArgs.AuthURLRetry = sps.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int()
spsArgs.ParentAuth = sps.Flag("parent-auth", "parent socks auth username and password, such as: -A user1:pass1").Short('A').String()
spsArgs.LocalKey = sps.Flag("local-key", "the password for auto encrypt/decrypt local connection data").Short('z').Default("").String()
spsArgs.ParentKey = sps.Flag("parent-key", "the password for auto encrypt/decrypt parent connection data").Short('Z').Default("").String()
spsArgs.LocalCompress = sps.Flag("local-compress", "auto compress/decompress data on local connection").Short('m').Default("false").Bool()
spsArgs.ParentCompress = sps.Flag("parent-compress", "auto compress/decompress data on parent connection").Short('M').Default("false").Bool()
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()
//########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(":33080").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()
//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, tunnel.NewTunnelServerManager(), tunnelServerArgs, log)
case "tclient":
services.Regist(serviceID, tunnel.NewTunnelClient(), tunnelClientArgs, log)
case "tbridge":
services.Regist(serviceID, tunnel.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(serviceName, 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,24 @@
#/bin/bash
VER="v5.3"
rm -rf sdk-linux-*.tar.gz
rm -rf README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a
#linux 32bit
CGO_ENABLED=1 GOARCH=386 GOOS=linux go build -buildmode=c-archive -ldflags "-s -w" -o libproxy-sdk.a sdk.go
CGO_ENABLED=1 GOARCH=386 GOOS=linux go build -buildmode=c-shared -ldflags "-s -w" -o libproxy-sdk.so sdk.go
cp ../README.md .
tar zcf sdk-linux-32bit-${VER}.tar.gz README.md libproxy-sdk.so libproxy-sdk.a libproxy-sdk.h
rm -rf README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a
#linux 64bit
CGO_ENABLED=1 GOARCH=amd64 GOOS=linux go build -buildmode=c-archive -ldflags "-s -w" -o libproxy-sdk.a sdk.go
CGO_ENABLED=1 GOARCH=amd64 GOOS=linux go build -buildmode=c-shared -ldflags "-s -w" -o libproxy-sdk.so sdk.go
cp ../README.md .
tar zcf sdk-linux-64bit-${VER}.tar.gz README.md libproxy-sdk.so libproxy-sdk.a libproxy-sdk.h
rm -rf README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a
echo "done."

View File

@ -0,0 +1,13 @@
#/bin/bash
VER="v5.3"
rm -rf *.tar.gz
rm -rf README.md libproxy-sdk.dylib libproxy-sdk.h
#mac , macos required
CGO_ENABLED=1 GOARCH=amd64 GOOS=darwin go build -buildmode=c-shared -ldflags "-s -w" -o libproxy-sdk.dylib sdk.go
cp ../README.md .
tar zcf sdk-mac-${VER}.tar.gz README.md libproxy-sdk.dylib libproxy-sdk.h
rm -rf README.md libproxy-sdk.dylib libproxy-sdk.h
echo "done."

View File

@ -0,0 +1,28 @@
#/bin/bash
VER="v5.3"
#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 -buildmode=c-shared -ldflags "-s -w" -o proxy-sdk.dll sdk.go
cp ../README.md .
tar zcf sdk-windows-64bit-${VER}.tar.gz README.md proxy-sdk.dll proxy-sdk.h ieshims.dll
rm -rf README.md proxy-sdk.h proxy-sdk.dll
#windows 32bit
CC=i686-w64-mingw32-gcc-win32 GOARCH=386 CGO_ENABLED=1 GOOS=windows go build -buildmode=c-shared -ldflags "-s -w" -o proxy-sdk.dll sdk.go
cp ../README.md .
tar zcf sdk-windows-32bit-${VER}.tar.gz README.md proxy-sdk.dll proxy-sdk.h ieshims.dll
rm -rf README.md proxy-sdk.h proxy-sdk.dll
#sudo rm /usr/local/go
#sudo ln -s /usr/local/go1.8.5 /usr/local/go
echo "done."

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,75 +0,0 @@
package services
// 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"
CONN_CONTROL = uint8(1)
CONN_SERVER = uint8(2)
CONN_CLIENT = uint8(3)
)
type Args struct {
Local *string
Parent *string
CertBytes []byte
KeyBytes []byte
}
type TunnelServerArgs struct {
Args
IsUDP *bool
Key *string
Timeout *int
}
type TunnelClientArgs struct {
Args
IsUDP *bool
Key *string
Timeout *int
}
type TunnelBridgeArgs struct {
Args
Timeout *int
}
type TCPArgs struct {
Args
ParentType *string
IsTLS *bool
Timeout *int
PoolSize *int
CheckParentInterval *int
}
type HTTPArgs struct {
Args
Always *bool
HTTPTimeout *int
Interval *int
Blocked *string
Direct *string
AuthFile *string
Auth *[]string
ParentType *string
LocalType *string
Timeout *int
PoolSize *int
CheckParentInterval *int
}
type UDPArgs struct {
Args
ParentType *string
Timeout *int
PoolSize *int
CheckParentInterval *int
}
func (a *TCPArgs) Protocol() string {
if *a.IsTLS {
return "tls"
}
return "tcp"
}

View File

@ -1,219 +0,0 @@
package services
import (
"fmt"
"io"
"log"
"net"
"proxy/utils"
"runtime/debug"
"strconv"
)
type HTTP struct {
outPool utils.OutPool
cfg HTTPArgs
checker utils.Checker
basicAuth utils.BasicAuth
}
func NewHTTP() Service {
return &HTTP{
outPool: utils.OutPool{},
cfg: HTTPArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
}
}
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)
}
}
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)
if *s.cfg.Parent != "" {
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
s.InitOutConnPool()
}
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p)
if *s.cfg.LocalType == TYPE_TCP {
err = sc.ListenTCP(s.callback)
} else {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, 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()))
}
}()
req, err := utils.NewHTTPRequest(&inConn, 4096, s.IsBasicAuth(), &s.basicAuth)
if err != nil {
if err != io.EOF {
log.Printf("decoder error , form %s, ERR:%s", err, inConn.RemoteAddr())
}
utils.CloseConn(&inConn)
return
}
address := req.Host
useProxy := true
if *s.cfg.Parent == "" {
useProxy = false
} else if *s.cfg.Always {
useProxy = true
} else {
if req.IsHTTPS() {
s.checker.Add(address, true, req.Method, "", nil)
} else {
s.checker.Add(address, false, req.Method, req.URL, req.HeadBuf)
}
//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)
//os.Exit(0)
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 error) {
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{}
if useProxy {
_outConn, err = s.outPool.Pool.Get()
if err == nil {
outConn = _outConn.(net.Conn)
}
} else {
outConn, err = utils.ConnectHost(address, *s.cfg.Timeout)
}
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 {
req.HTTPSReply()
} else {
outConn.Write(req.HeadBuf)
}
utils.IoBind((*inConn), outConn, func(isSrcErr bool, err error) {
log.Printf("conn %s - %s - %s -%s released [%s]", inAddr, inLocalAddr, outLocalAddr, outAddr, req.Host)
utils.CloseConn(inConn)
utils.CloseConn(&outConn)
}, func(n int, d bool) {}, 0)
log.Printf("conn %s - %s - %s - %s connected [%s]", inAddr, inLocalAddr, outLocalAddr, outAddr, req.Host)
return
}
func (s *HTTP) OutToUDP(inConn *net.Conn) (err error) {
return
}
func (s *HTTP) InitOutConnPool() {
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutPool(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType == TYPE_TLS,
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.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
}
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()
if err == nil {
for _, localIP := range interfaceIPs {
for _, outIP := range outIPs {
if localIP.Equal(outIP) {
return true
}
}
}
}
}
return false
}

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

@ -0,0 +1,532 @@
package http
import (
"fmt"
"io"
"io/ioutil"
logger "log"
"net"
"runtime/debug"
"strconv"
"strings"
"time"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"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
}
type HTTP struct {
outPool utils.OutConn
cfg HTTPArgs
checker utils.Checker
basicAuth utils.BasicAuth
sshClient *ssh.Client
lockChn chan bool
domainResolver utils.DomainResolver
isStop bool
serverChannels []*utils.ServerChannel
userConns utils.ConcurrentMap
log *logger.Logger
}
func NewHTTP() services.Service {
return &HTTP{
outPool: utils.OutConn{},
cfg: HTTPArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
lockChn: make(chan bool, 1),
isStop: false,
serverChannels: []*utils.ServerChannel{},
userConns: utils.NewConcurrentMap(),
}
}
func (s *HTTP) CheckArgs() (err error) {
if *s.cfg.Parent != "" && *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)
}
}
return
}
func (s *HTTP) InitService() (err error) {
s.InitBasicAuth()
if *s.cfg.Parent != "" {
s.checker = utils.NewChecker(*s.cfg.HTTPTimeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct, s.log)
}
if *s.cfg.DNSAddress != "" {
(*s).domainResolver = utils.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() {
//循环检查ssh网络连通性
for {
if s.isStop {
return
}
conn, err := utils.ConnectHost(s.Resolve(*s.cfg.Parent), *s.cfg.Timeout*2)
if err == nil {
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write([]byte{0})
conn.SetDeadline(time.Time{})
}
if err != nil {
if s.sshClient != nil {
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) stopped")
}
}()
s.isStop = true
if *s.cfg.Parent != "" {
s.checker.Stop()
}
if s.sshClient != nil {
s.sshClient.Close()
}
for _, sc := range s.serverChannels {
if sc.Listener != nil && *sc.Listener != nil {
(*sc.Listener).Close()
}
if sc.UDPListener != nil {
(*sc.UDPListener).Close()
}
}
}
func (s *HTTP) Start(args interface{}, 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 *s.cfg.Parent != "" {
s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
s.InitOutConnPool()
}
for _, addr := range strings.Split(*s.cfg.Local, ",") {
if addr != "" {
host, port, _ := net.SplitHostPort(addr)
p, _ := strconv.Atoi(port)
sc := utils.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.IsIternalIP(host, *s.cfg.Always) {
useProxy = true
if *s.cfg.Parent == "" {
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)
err = s.OutToTCP(useProxy, address, &inConn, &req)
if err != nil {
if *s.cfg.Parent == "" {
s.log.Printf("connect to %s fail, ERR:%s", address, err)
} else {
s.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
tryCount := 0
maxTryCount := 5
for {
if s.isStop {
return
}
if useProxy {
if *s.cfg.ParentType == "ssh" {
outConn, err = s.getSSHConn(address)
} else {
// s.log.Printf("%v", s.outPool)
outConn, err = s.outPool.Get()
}
} else {
outConn, err = utils.ConnectHost(s.Resolve(address), *s.cfg.Timeout)
}
tryCount++
if err == nil || tryCount > maxTryCount {
break
} else {
s.log.Printf("connect to %s , err:%s,retrying...", *s.cfg.Parent, err)
time.Sleep(time.Second * 2)
}
}
if err != nil {
s.log.Printf("connect to %s , err:%s", *s.cfg.Parent, err)
utils.CloseConn(inConn)
return
}
if *s.cfg.ParentCompress {
outConn = utils.NewCompConn(outConn)
}
if *s.cfg.ParentKey != "" {
outConn = conncrypt.New(outConn, &conncrypt.Config{
Password: *s.cfg.ParentKey,
})
}
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", *s.cfg.Parent, err)
utils.CloseConn(inConn)
return
}
}
utils.IoBind((*inConn), outConn, func(err interface{}) {
s.log.Printf("conn %s - %s released [%s]", inAddr, outAddr, req.Host)
s.userConns.Remove(inAddr)
}, 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)
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.cfg.Parent), &config)
<-s.lockChn
return
}
func (s *HTTP) InitOutConnPool() {
if *s.cfg.ParentType == "tls" || *s.cfg.ParentType == "tcp" || *s.cfg.ParentType == "kcp" {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutConn(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType,
s.cfg.KCP,
s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes,
s.Resolve(*s.cfg.Parent),
*s.cfg.Timeout,
)
}
}
func (s *HTTP) InitBasicAuth() (err error) {
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) 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.MyLookupIP(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
}

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() {
}

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

@ -0,0 +1,301 @@
package mux
import (
"bufio"
"fmt"
"io"
logger "log"
"math/rand"
"net"
"strconv"
"strings"
"sync"
"time"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils"
//"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux"
)
const (
CONN_SERVER = uint8(4)
CONN_CLIENT = uint8(5)
)
type MuxBridgeArgs struct {
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
LocalType *string
Timeout *int
IsCompress *bool
KCP kcpcfg.KCPConfigArgs
}
type MuxBridge struct {
cfg MuxBridgeArgs
clientControlConns utils.ConcurrentMap
serverConns utils.ConcurrentMap
router utils.ClientKeyRouter
l *sync.Mutex
isStop bool
sc *utils.ServerChannel
log *logger.Logger
}
func NewMuxBridge() services.Service {
b := &MuxBridge{
cfg: MuxBridgeArgs{},
clientControlConns: utils.NewConcurrentMap(),
serverConns: utils.NewConcurrentMap(),
l: &sync.Mutex{},
isStop: false,
}
b.router = utils.NewClientKeyRouter(&b.clientControlConns, 50000)
return b
}
func (s *MuxBridge) InitService() (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 stopped")
}
}()
s.isStop = true
if s.sc != nil && (*s.sc).Listener != nil {
(*(*s.sc).Listener).Close()
}
for _, g := range s.clientControlConns.Items() {
for _, session := range g.(*utils.ConcurrentMap).Items() {
(session.(*smux.Session)).Close()
}
}
for _, c := range s.serverConns.Items() {
(*c.(*net.Conn)).Close()
}
}
func (s *MuxBridge) Start(args interface{}, 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
}
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p, 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)
}
if err != nil {
return
}
s.sc = &sc
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()
if !s.clientControlConns.Has(groupKey) {
item := utils.NewConcurrentMap()
s.clientControlConns.Set(groupKey, &item)
}
_group, _ := s.clientControlConns.Get(groupKey)
group := _group.(*utils.ConcurrentMap)
if v, ok := group.Get(index); ok {
v.(*smux.Session).Close()
}
group.Set(index, session)
// s.clientControlConns.Set(key, session)
go func() {
for {
if s.isStop {
return
}
if session.IsClosed() {
s.l.Lock()
defer s.l.Unlock()
if sess, ok := group.Get(index); ok && sess.(*smux.Session).IsClosed() {
group.Remove(index)
s.log.Printf("client connection %s released", key)
}
if group.IsEmpty() {
s.clientControlConns.Remove(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()
}
_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.(*utils.ConcurrentMap)
keys := group.Keys()
keysLen := len(keys)
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() {
io.Copy(stream, inConn)
die1 <- true
}()
go func() {
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
}
}
}

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

@ -0,0 +1,353 @@
package mux
import (
"crypto/tls"
"fmt"
"io"
logger "log"
"net"
"time"
"github.com/golang/snappy"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/jumper"
//"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
}
type MuxClient struct {
cfg MuxClientArgs
isStop bool
sessions utils.ConcurrentMap
log *logger.Logger
jumper *jumper.Jumper
}
func NewMuxClient() services.Service {
return &MuxClient{
cfg: MuxClientArgs{},
isStop: false,
sessions: utils.NewConcurrentMap(),
}
}
func (s *MuxClient) InitService() (err error) {
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 stopped")
}
}()
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", e)
}
}()
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)))
_, err = conn.Write(utils.BuildPacket(CONN_CLIENT, fmt.Sprintf("%s-%d", *s.cfg.Key, i)))
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 = 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.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 *MuxClient) ServeUDP(inConn *smux.Stream, localAddr, ID string) {
for {
if s.isStop {
return
}
inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
srcAddr, body, err := utils.ReadUDPPacket(inConn)
inConn.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("udp packet revecived fail, err: %s", err)
s.log.Printf("connection %s released", ID)
inConn.Close()
break
} else {
//s.log.Printf("udp packet revecived:%s,%v", srcAddr, body)
go func() {
defer func() {
if e := recover(); e != nil {
s.log.Printf("client processUDPPacket crashed,err: %s", e)
}
}()
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 {
s.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 {
s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
return
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write(body)
conn.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
return
}
//s.log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 1024)
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
length, _, err := conn.ReadFromUDP(buf)
conn.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return
}
respBody := buf[0:length]
//s.log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
bs := utils.UDPPacket(srcAddr, respBody)
(*inConn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = (*inConn).Write(bs)
(*inConn).SetDeadline(time.Time{})
if err != nil {
s.log.Printf("send udp response fail ,ERR:%s", err)
inConn.Close()
return
}
//s.log.Printf("send udp response success ,from:%s ,%d ,%v", dstAddr.String(), len(bs), bs)
}
func (s *MuxClient) ServeConn(inConn *smux.Stream, localAddr, ID string) {
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() {
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()
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)
}
}

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

@ -0,0 +1,510 @@
package mux
import (
"crypto/tls"
"fmt"
"io"
logger "log"
"math/rand"
"net"
"runtime/debug"
"strconv"
"strings"
"time"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/jumper"
"github.com/golang/snappy"
//"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux"
)
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
}
type MuxUDPItem struct {
packet *[]byte
localAddr *net.UDPAddr
srcAddr *net.UDPAddr
}
type MuxServerManager struct {
cfg MuxServerArgs
udpChn chan MuxUDPItem
serverID string
servers []*services.Service
log *logger.Logger
}
func NewMuxServerManager() services.Service {
return &MuxServerManager{
cfg: MuxServerArgs{},
udpChn: make(chan MuxUDPItem, 50000),
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,
}, 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()
}
}
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
}
type MuxServer struct {
cfg MuxServerArgs
udpChn chan MuxUDPItem
sc utils.ServerChannel
sessions utils.ConcurrentMap
lockChn chan bool
isStop bool
udpConn *net.Conn
log *logger.Logger
jumper *jumper.Jumper
}
func NewMuxServer() services.Service {
return &MuxServer{
cfg: MuxServerArgs{},
udpChn: make(chan MuxUDPItem, 50000),
lockChn: make(chan bool, 1),
sessions: utils.NewConcurrentMap(),
isStop: false,
}
}
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 stopped")
}
}()
s.isStop = true
for _, sess := range s.sessions.Items() {
sess.(*smux.Session).Close()
}
if s.sc.Listener != nil {
(*s.sc.Listener).Close()
}
if s.sc.UDPListener != nil {
(*s.sc.UDPListener).Close()
}
if s.udpConn != nil {
(*s.udpConn).Close()
}
}
func (s *MuxServer) InitService() (err error) {
s.UDPConnDeamon()
return
}
func (s *MuxServer) CheckArgs() (err error) {
if *s.cfg.Remote == "" {
err = fmt.Errorf("remote required")
return
}
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 = utils.NewServerChannel(host, p, s.log)
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
}
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() {
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()
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 {
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() {
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.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) UDPConnDeamon() {
go func() {
defer func() {
if err := recover(); err != nil {
s.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 {
if s.isStop {
return
}
item := <-s.udpChn
RETRY:
if s.isStop {
return
}
if outConn == nil {
for {
if s.isStop {
return
}
outConn, ID, err = s.GetOutConn()
if err != nil {
outConn = nil
utils.CloseConn(&outConn)
s.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) {
if s.udpConn != nil {
(*s.udpConn).Close()
}
s.udpConn = &outConn
for {
if s.isStop {
return
}
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
srcAddrFromConn, body, err := utils.ReadUDPPacket(outConn)
outConn.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("parse revecived udp packet fail, err: %s ,%v", err, body)
s.log.Printf("UDP deamon connection %s exited", ID)
break
}
//s.log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn)
_srcAddr := strings.Split(srcAddrFromConn, ":")
if len(_srcAddr) != 2 {
s.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}
s.sc.UDPListener.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = s.sc.UDPListener.WriteToUDP(body, dstAddr)
s.sc.UDPListener.SetDeadline(time.Time{})
if err != nil {
s.log.Printf("udp response to local %s fail,ERR:%s", srcAddrFromConn, err)
continue
}
//s.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
s.log.Printf("write udp packet to %s fail ,flush err:%s ,retrying...", *s.cfg.Parent, err)
goto RETRY
}
//s.log.Printf("write packet %v", *item.packet)
}
}()
}

View File

@ -2,46 +2,64 @@ 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()
}
}
func Run(name string) (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()))
}
}()
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

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

@ -0,0 +1,640 @@
package socks
import (
"crypto/tls"
"fmt"
"io"
"io/ioutil"
logger "log"
"net"
"runtime/debug"
"strings"
"time"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/conncrypt"
"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
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
}
type Socks struct {
cfg SocksArgs
checker utils.Checker
basicAuth utils.BasicAuth
sshClient *ssh.Client
lockChn chan bool
udpSC utils.ServerChannel
sc *utils.ServerChannel
domainResolver utils.DomainResolver
isStop bool
userConns utils.ConcurrentMap
log *logger.Logger
udpRelatedPacketConns utils.ConcurrentMap
udpLocalKey []byte
udpParentKey []byte
}
func NewSocks() services.Service {
return &Socks{
cfg: SocksArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
lockChn: make(chan bool, 1),
isStop: false,
userConns: utils.NewConcurrentMap(),
udpRelatedPacketConns: utils.NewConcurrentMap(),
}
}
func (s *Socks) CheckArgs() (err error) {
if *s.cfg.LocalType == "tls" || (*s.cfg.Parent != "" && *s.cfg.ParentType == "tls") {
s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
if err != nil {
return
}
if *s.cfg.CaCertFile != "" {
s.cfg.CaCertBytes, err = ioutil.ReadFile(*s.cfg.CaCertFile)
if err != nil {
err = fmt.Errorf("read ca file error,ERR:%s", err)
return
}
}
}
if *s.cfg.Parent != "" {
if *s.cfg.ParentType == "" {
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)
}
}
}
s.udpLocalKey = s.LocalUDPKey()
s.udpParentKey = s.ParentUDPKey()
//s.log.Printf("udpLocalKey : %v , udpParentKey : %v", s.udpLocalKey, s.udpParentKey)
return
}
func (s *Socks) InitService() (err error) {
s.InitBasicAuth()
if *s.cfg.DNSAddress != "" {
(*s).domainResolver = utils.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log)
}
if *s.cfg.Parent != "" {
s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct, s.log)
}
if *s.cfg.ParentType == "ssh" {
e := s.ConnectSSH()
if e != nil {
err = fmt.Errorf("init service fail, ERR: %s", e)
return
}
go func() {
//循环检查ssh网络连通性
for {
if s.isStop {
return
}
conn, err := utils.ConnectHost(s.Resolve(*s.cfg.Parent), *s.cfg.Timeout*2)
if err == nil {
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write([]byte{0})
conn.SetDeadline(time.Time{})
}
if err != nil {
if s.sshClient != nil {
s.sshClient.Close()
}
s.log.Printf("ssh offline, retrying...")
s.ConnectSSH()
} 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 stopped")
}
}()
s.isStop = true
if *s.cfg.Parent != "" {
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()
}
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 *s.cfg.Parent != "" {
s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
}
sc := utils.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, nil, 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,
})
}
//协商开始
//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)
if err != io.EOF {
s.log.Printf("new methods request fail,ERR: %s", err)
}
return
}
if !s.IsBasicAuth() {
if !methodReq.Select(socks.Method_NO_AUTH) {
methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn)
s.log.Printf("none method found : Method_NO_AUTH")
return
}
//method select reply
err = methodReq.Reply(socks.Method_NO_AUTH)
if err != nil {
s.log.Printf("reply answer data fail,ERR: %s", err)
utils.CloseConn(&inConn)
return
}
// s.log.Printf("% x", methodReq.Bytes())
} else {
//auth
if !methodReq.Select(socks.Method_USER_PASS) {
methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn)
s.log.Printf("none method found : Method_USER_PASS")
return
}
//method reply need auth
err = methodReq.Reply(socks.Method_USER_PASS)
if err != nil {
s.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:])
//s.log.Printf("user:%s,pass:%s", user, pass)
//auth
_addr := strings.Split(inConn.RemoteAddr().String(), ":")
if s.basicAuth.CheckUserPass(user, pass, _addr[0], "") {
inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
inConn.Write([]byte{0x01, 0x00})
inConn.SetDeadline(time.Time{})
} else {
inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
inConn.Write([]byte{0x01, 0x01})
inConn.SetDeadline(time.Time{})
utils.CloseConn(&inConn)
return
}
}
//request detail
request, err := socks.NewRequest(inConn)
if err != nil {
s.log.Printf("read request data fail,ERR: %s", err)
utils.CloseConn(&inConn)
return
}
//协商结束
switch request.CMD() {
case socks.CMD_BIND:
//bind 不支持
request.TCPReply(socks.REP_UNKNOWN)
utils.CloseConn(&inConn)
return
case socks.CMD_CONNECT:
//tcp
s.proxyTCP(&inConn, methodReq, request)
case socks.CMD_ASSOCIATE:
//udp
s.proxyUDP(&inConn, methodReq, request)
}
}
func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) {
var outConn net.Conn
var err interface{}
useProxy := true
tryCount := 0
maxTryCount := 5
//防止死循环
if s.IsDeadLoop((*inConn).LocalAddr().String(), request.Host()) {
utils.CloseConn(inConn)
s.log.Printf("dead loop detected , %s", request.Host())
utils.CloseConn(inConn)
return
}
for {
if s.isStop {
return
}
if *s.cfg.Always {
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr(), true)
} else {
if *s.cfg.Parent != "" {
host, _, _ := net.SplitHostPort(request.Addr())
useProxy := false
if utils.IsIternalIP(host, *s.cfg.Always) {
useProxy = false
} else {
var isInMap bool
useProxy, isInMap, _, _ = s.checker.IsBlocked(request.Addr())
if !isInMap {
s.checker.Add(request.Addr(), s.Resolve(request.Addr()))
}
}
if useProxy {
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr(), true)
} else {
outConn, err = utils.ConnectHost(s.Resolve(request.Addr()), *s.cfg.Timeout)
}
} else {
outConn, err = utils.ConnectHost(s.Resolve(request.Addr()), *s.cfg.Timeout)
useProxy = false
}
}
tryCount++
if err == nil || tryCount > maxTryCount || *s.cfg.Parent == "" {
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)
request.TCPReply(socks.REP_NETWOR_UNREACHABLE)
return
}
s.log.Printf("use proxy %v : %s", useProxy, request.Addr())
request.TCPReply(socks.REP_SUCCESS)
inAddr := (*inConn).RemoteAddr().String()
//inLocalAddr := (*inConn).LocalAddr().String()
s.log.Printf("conn %s - %s connected", inAddr, request.Addr())
utils.IoBind(*inConn, outConn, func(err interface{}) {
s.log.Printf("conn %s - %s released", inAddr, request.Addr())
s.userConns.Remove(inAddr)
}, s.log)
if c, ok := s.userConns.Get(inAddr); ok {
(*c.(*net.Conn)).Close()
s.userConns.Remove(inAddr)
}
s.userConns.Set(inAddr, inConn)
}
func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string, handshake bool) (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.Resolve(*s.cfg.Parent), *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil)
outConn = net.Conn(&_outConn)
} else if *s.cfg.ParentType == "kcp" {
outConn, err = utils.ConnectKCPHost(s.Resolve(*s.cfg.Parent), s.cfg.KCP)
} else {
outConn, err = utils.ConnectHost(s.Resolve(*s.cfg.Parent), *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,
})
}
if !handshake {
return
}
var buf = make([]byte, 1024)
//var n int
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = outConn.Write(methodBytes)
outConn.SetDeadline(time.Time{})
if err != nil {
err = fmt.Errorf("write method fail,%s", err)
return
}
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = outConn.Read(buf)
outConn.SetDeadline(time.Time{})
if err != nil {
err = fmt.Errorf("read method reply fail,%s", err)
return
}
//resp := buf[:n]
//s.log.Printf("resp:%v", resp)
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = outConn.Write(reqBytes)
outConn.SetDeadline(time.Time{})
if err != nil {
err = fmt.Errorf("write req detail fail,%s", err)
return
}
outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = outConn.Read(buf)
outConn.SetDeadline(time.Time{})
if err != nil {
err = fmt.Errorf("read req reply fail,%s", err)
return
}
//result := buf[:n]
//s.log.Printf("result:%v", result)
case "ssh":
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.Millisecond * time.Duration(*s.cfg.Timeout) * 2):
err = fmt.Errorf("ssh dial %s timeout", host)
s.sshClient.Close()
}
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 *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.Resolve(*s.cfg.Parent), &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) 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.MyLookupIP(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
}

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

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

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

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

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

@ -0,0 +1,517 @@
package sps
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
logger "log"
"net"
"runtime/debug"
"strconv"
"strings"
"time"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/conncrypt"
"github.com/snail007/goproxy/utils/sni"
"github.com/snail007/goproxy/utils/socks"
)
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
DisableHTTP *bool
DisableSocks5 *bool
}
type SPS struct {
outPool utils.OutConn
cfg SPSArgs
domainResolver utils.DomainResolver
basicAuth utils.BasicAuth
serverChannels []*utils.ServerChannel
userConns utils.ConcurrentMap
log *logger.Logger
udpRelatedPacketConns utils.ConcurrentMap
udpLocalKey []byte
udpParentKey []byte
}
func NewSPS() services.Service {
return &SPS{
outPool: utils.OutConn{},
cfg: SPSArgs{},
basicAuth: utils.BasicAuth{},
serverChannels: []*utils.ServerChannel{},
userConns: utils.NewConcurrentMap(),
udpRelatedPacketConns: utils.NewConcurrentMap(),
}
}
func (s *SPS) CheckArgs() (err error) {
if *s.cfg.Parent == "" {
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 == "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
}
}
}
s.udpLocalKey = s.LocalUDPKey()
s.udpParentKey = s.ParentUDPKey()
return
}
func (s *SPS) InitService() (err error) {
if *s.cfg.DNSAddress != "" {
(*s).domainResolver = utils.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log)
}
s.InitOutConnPool()
err = s.InitBasicAuth()
return
}
func (s *SPS) InitOutConnPool() {
if *s.cfg.ParentType == "tls" || *s.cfg.ParentType == "tcp" || *s.cfg.ParentType == "kcp" {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutConn(
0,
*s.cfg.ParentType,
s.cfg.KCP,
s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes,
*s.cfg.Parent,
*s.cfg.Timeout,
)
}
}
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 stopped")
}
}()
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()
}
}
}
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 %s", *s.cfg.ParentType, *s.cfg.ParentServiceType, *s.cfg.Parent)
for _, addr := range strings.Split(*s.cfg.Local, ",") {
if addr != "" {
host, port, _ := net.SplitHostPort(addr)
p, _ := strconv.Atoi(port)
sc := utils.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)+socks proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr())
s.serverChannels = append(s.serverChannels, &sc)
}
}
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
switch *s.cfg.ParentType {
case "kcp":
fallthrough
case "tcp":
fallthrough
case "tls":
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, *s.cfg.Parent, err, inConn.RemoteAddr())
utils.CloseConn(&inConn)
}
}
func (s *SPS) OutToTCP(inConn *net.Conn) (err error) {
enableUDP := *s.cfg.ParentServiceType == "socks"
udpIP, _, _ := net.SplitHostPort((*inConn).LocalAddr().String())
if len(*s.cfg.LocalIPS) > 0 {
udpIP = (*s.cfg.LocalIPS)[0]
}
bInConn := utils.NewBufferedConn(*inConn)
//important
//action read will regist read event to system,
//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", h)
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 = bytes.TrimRight(request.HeadBuf,"\r\n")
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]}
}
}
}
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
outConn, err = s.outPool.Get()
if err != nil {
s.log.Printf("connect to %s , err:%s", *s.cfg.Parent, err)
utils.CloseConn(inConn)
return
}
if *s.cfg.ParentCompress {
outConn = utils.NewCompConn(outConn)
}
if *s.cfg.ParentKey != "" {
outConn = conncrypt.New(outConn, &conncrypt.Config{
Password: *s.cfg.ParentKey,
})
}
if *s.cfg.ParentAuth != "" || 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("Proxy-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", *s.cfg.Parent, 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", *s.cfg.Parent, 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
var clientConn *socks.ClientConn
if *s.cfg.ParentAuth != "" {
a := strings.Split(*s.cfg.ParentAuth, ":")
if len(a) != 2 {
err = fmt.Errorf("parent auth data format error")
return
}
clientConn = socks.NewClientConn(&outConn, "tcp", address, time.Millisecond*time.Duration(*s.cfg.Timeout), &socks.Auth{User: a[0], Password: a[1]}, nil)
} else {
if !s.IsBasicAuth() && auth.Password != "" && auth.User != "" {
clientConn = socks.NewClientConn(&outConn, "tcp", address, time.Millisecond*time.Duration(*s.cfg.Timeout), &auth, nil)
} else {
clientConn = socks.NewClientConn(&outConn, "tcp", address, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil)
}
}
if err = clientConn.Handshake(); err != nil {
return
}
}
//forward client data to target,if necessary.
if len(forwardBytes) > 0 {
outConn.Write(forwardBytes)
}
//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.log)
s.log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, address)
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) 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
}

View File

@ -1,170 +0,0 @@
package services
import (
"fmt"
"io"
"log"
"net"
"proxy/utils"
"runtime/debug"
"time"
"strconv"
)
type TCP struct {
outPool utils.OutPool
cfg TCPArgs
}
func NewTCP() Service {
return &TCP{
outPool: utils.OutPool{},
cfg: TCPArgs{},
}
}
func (s *TCP) InitService() {
s.InitOutConnPool()
}
func (s *TCP) StopService() {
if s.outPool.Pool != nil {
s.outPool.Pool.ReleaseAll()
}
}
func (s *TCP) Start(args interface{}) (err error) {
s.cfg = args.(TCPArgs)
if *s.cfg.Parent != "" {
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
} else {
log.Fatalf("parent required for %s %s", s.cfg.Protocol(), *s.cfg.Local)
}
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p)
if !*s.cfg.IsTLS {
err = sc.ListenTCP(s.callback)
} else {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback)
}
if err != nil {
return
}
log.Printf("%s proxy on %s", s.cfg.Protocol(), (*sc.Listener).Addr())
return
}
func (s *TCP) Clean() {
s.StopService()
}
func (s *TCP) callback(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
log.Printf("%s conn handler crashed with err : %s \nstack: %s", s.cfg.Protocol(), err, string(debug.Stack()))
}
}()
var err error
switch *s.cfg.ParentType {
case TYPE_TCP:
fallthrough
case TYPE_TLS:
err = s.OutToTCP(&inConn)
case TYPE_UDP:
err = s.OutToUDP(&inConn)
default:
err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType)
}
if err != nil {
log.Printf("connect to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err)
utils.CloseConn(&inConn)
}
}
func (s *TCP) OutToTCP(inConn *net.Conn) (err error) {
var outConn net.Conn
var _outConn interface{}
_outConn, err = s.outPool.Pool.Get()
if err == nil {
outConn = _outConn.(net.Conn)
}
if err != nil {
log.Printf("connect to %s , err:%s", *s.cfg.Parent, err)
utils.CloseConn(inConn)
return
}
inAddr := (*inConn).RemoteAddr().String()
inLocalAddr := (*inConn).LocalAddr().String()
outAddr := outConn.RemoteAddr().String()
outLocalAddr := outConn.LocalAddr().String()
utils.IoBind((*inConn), outConn, func(isSrcErr bool, err error) {
log.Printf("conn %s - %s - %s -%s released", inAddr, inLocalAddr, outLocalAddr, outAddr)
utils.CloseConn(inConn)
utils.CloseConn(&outConn)
}, func(n int, d bool) {}, 0)
log.Printf("conn %s - %s - %s -%s connected", inAddr, inLocalAddr, outLocalAddr, outAddr)
return
}
func (s *TCP) OutToUDP(inConn *net.Conn) (err error) {
log.Printf("conn created , remote : %s ", (*inConn).RemoteAddr())
for {
srcAddr, body, err := utils.ReadUDPPacket(inConn)
if err == io.EOF || err == io.ErrUnexpectedEOF {
//log.Printf("connection %s released", srcAddr)
utils.CloseConn(inConn)
break
}
//log.Debugf("udp packet revecived:%s,%v", srcAddr, body)
dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Parent)
if err != nil {
log.Printf("can't resolve address: %s", err)
utils.CloseConn(inConn)
break
}
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)
continue
}
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)
continue
}
//log.Debugf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 512)
len, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
continue
}
respBody := buf[0:len]
//log.Debugf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
_, err = (*inConn).Write(utils.UDPPacket(srcAddr, respBody))
if err != nil {
log.Printf("send udp response fail ,ERR:%s", err)
utils.CloseConn(inConn)
break
}
//log.Printf("send udp response success ,from:%s", dstAddr.String())
}
return
}
func (s *TCP) InitOutConnPool() {
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutPool(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType == TYPE_TLS,
s.cfg.CertBytes, s.cfg.KeyBytes,
*s.cfg.Parent,
*s.cfg.Timeout,
*s.cfg.PoolSize,
*s.cfg.PoolSize*2,
)
}
}

266
services/tcp/tcp.go Normal file
View File

@ -0,0 +1,266 @@
package tcp
import (
"bufio"
"crypto/tls"
"fmt"
"io"
logger "log"
"net"
"runtime/debug"
"time"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/jumper"
"strconv"
)
type TCPArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
ParentType *string
LocalType *string
Timeout *int
KCP kcpcfg.KCPConfigArgs
Jumper *string
}
type TCP struct {
cfg TCPArgs
sc *utils.ServerChannel
isStop bool
userConns utils.ConcurrentMap
log *logger.Logger
jumper *jumper.Jumper
}
func NewTCP() services.Service {
return &TCP{
cfg: TCPArgs{},
isStop: false,
userConns: utils.NewConcurrentMap(),
}
}
func (s *TCP) CheckArgs() (err error) {
if *s.cfg.Parent == "" {
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|udp>")
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.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 *TCP) InitService() (err error) {
return
}
func (s *TCP) StopService() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop tcp service crashed,%s", e)
} else {
s.log.Printf("service tcp stopped")
}
}()
s.isStop = true
if s.sc.Listener != nil && *s.sc.Listener != nil {
(*s.sc.Listener).Close()
}
if s.sc.UDPListener != nil {
(*s.sc.UDPListener).Close()
}
for _, c := range s.userConns.Items() {
(*c.(*net.Conn)).Close()
}
}
func (s *TCP) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(TCPArgs)
if err = s.CheckArgs(); err != nil {
return
}
if err = s.InitService(); err != nil {
return
}
s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.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, nil, 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 proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr())
s.sc = &sc
return
}
func (s *TCP) Clean() {
s.StopService()
}
func (s *TCP) 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()))
}
}()
var err error
switch *s.cfg.ParentType {
case "kcp":
fallthrough
case "tcp":
fallthrough
case "tls":
err = s.OutToTCP(&inConn)
case "udp":
err = s.OutToUDP(&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", *s.cfg.ParentType, *s.cfg.Parent, err)
utils.CloseConn(&inConn)
}
}
func (s *TCP) OutToTCP(inConn *net.Conn) (err error) {
var outConn net.Conn
outConn, err = s.GetParentConn()
if err != nil {
s.log.Printf("connect to %s , err:%s", *s.cfg.Parent, err)
utils.CloseConn(inConn)
return
}
inAddr := (*inConn).RemoteAddr().String()
//inLocalAddr := (*inConn).LocalAddr().String()
outAddr := outConn.RemoteAddr().String()
//outLocalAddr := outConn.LocalAddr().String()
utils.IoBind((*inConn), outConn, func(err interface{}) {
s.log.Printf("conn %s - %s released", inAddr, outAddr)
s.userConns.Remove(inAddr)
}, s.log)
s.log.Printf("conn %s - %s connected", inAddr, outAddr)
if c, ok := s.userConns.Get(inAddr); ok {
(*c.(*net.Conn)).Close()
}
s.userConns.Set(inAddr, inConn)
return
}
func (s *TCP) OutToUDP(inConn *net.Conn) (err error) {
s.log.Printf("conn created , remote : %s ", (*inConn).RemoteAddr())
for {
if s.isStop {
(*inConn).Close()
return
}
srcAddr, body, err := utils.ReadUDPPacket(bufio.NewReader(*inConn))
if err == io.EOF || err == io.ErrUnexpectedEOF {
//s.log.Printf("connection %s released", srcAddr)
utils.CloseConn(inConn)
break
}
//log.Debugf("udp packet revecived:%s,%v", srcAddr, body)
dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Parent)
if err != nil {
s.log.Printf("can't resolve address: %s", err)
utils.CloseConn(inConn)
break
}
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr)
if err != nil {
s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
continue
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write(body)
if err != nil {
s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
continue
}
//log.Debugf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 512)
len, _, err := conn.ReadFromUDP(buf)
if err != nil {
s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
continue
}
respBody := buf[0:len]
//log.Debugf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
_, err = (*inConn).Write(utils.UDPPacket(srcAddr, respBody))
if err != nil {
s.log.Printf("send udp response fail ,ERR:%s", err)
utils.CloseConn(inConn)
break
}
//s.log.Printf("send udp response success ,from:%s", dstAddr.String())
}
return
}
func (s *TCP) 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.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
}

View File

@ -0,0 +1,215 @@
package tunnel
import (
"bytes"
"fmt"
logger "log"
"net"
"os"
"strconv"
"time"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/utils"
//"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux"
)
const (
CONN_CLIENT_CONTROL = uint8(1)
CONN_SERVER = uint8(4)
CONN_CLIENT = uint8(5)
)
type TunnelBridgeArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
Timeout *int
}
type ServerConn struct {
//ClientLocalAddr string //tcp:2.2.22:333@ID
Conn *net.Conn
}
type TunnelBridge struct {
cfg TunnelBridgeArgs
serverConns utils.ConcurrentMap
clientControlConns utils.ConcurrentMap
isStop bool
log *logger.Logger
}
func NewTunnelBridge() services.Service {
return &TunnelBridge{
cfg: TunnelBridgeArgs{},
serverConns: utils.NewConcurrentMap(),
clientControlConns: utils.NewConcurrentMap(),
isStop: false,
}
}
func (s *TunnelBridge) InitService() (err error) {
return
}
func (s *TunnelBridge) CheckArgs() (err error) {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
err = fmt.Errorf("cert and key file required")
return
}
s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
return
}
func (s *TunnelBridge) StopService() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop tbridge service crashed,%s", e)
} else {
s.log.Printf("service tbridge stopped")
}
}()
s.isStop = true
for _, sess := range s.clientControlConns.Items() {
(*sess.(*net.Conn)).Close()
}
for _, sess := range s.serverConns.Items() {
(*sess.(ServerConn).Conn).Close()
}
}
func (s *TunnelBridge) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(TunnelBridgeArgs)
if err = s.CheckArgs(); err != nil {
return
}
if err = s.InitService(); err != nil {
return
}
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p, s.log)
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.callback)
if err != nil {
return
}
s.log.Printf("proxy on tunnel bridge mode %s", (*sc.Listener).Addr())
return
}
func (s *TunnelBridge) Clean() {
s.StopService()
}
func (s *TunnelBridge) callback(inConn net.Conn) {
var err error
//s.log.Printf("connection from %s ", inConn.RemoteAddr())
sess, err := smux.Server(inConn, &smux.Config{
AcceptBacklog: 256,
EnableKeepAlive: true,
KeepAliveInterval: 9 * time.Second,
ConnectionWriteTimeout: 3 * time.Second,
MaxStreamWindowSize: 512 * 1024,
LogOutput: os.Stderr,
})
if err != nil {
s.log.Printf("new mux server conn error,ERR:%s", err)
return
}
inConn, err = sess.AcceptStream()
if err != nil {
s.log.Printf("mux server conn accept error,ERR:%s", err)
return
}
var buf = make([]byte, 1024)
n, _ := inConn.Read(buf)
reader := bytes.NewReader(buf[:n])
//reader := bufio.NewReader(inConn)
var connType uint8
err = utils.ReadPacket(reader, &connType)
if err != nil {
s.log.Printf("read error,ERR:%s", err)
return
}
switch connType {
case CONN_SERVER:
var key, ID, clientLocalAddr, serverID string
err = utils.ReadPacketData(reader, &key, &ID, &clientLocalAddr, &serverID)
if err != nil {
s.log.Printf("read error,ERR:%s", err)
return
}
packet := utils.BuildPacketData(ID, clientLocalAddr, serverID)
s.log.Printf("server connection, key: %s , id: %s %s %s", key, ID, clientLocalAddr, serverID)
//addr := clientLocalAddr + "@" + ID
s.serverConns.Set(ID, ServerConn{
Conn: &inConn,
})
for {
if s.isStop {
return
}
item, ok := s.clientControlConns.Get(key)
if !ok {
s.log.Printf("client %s control conn not exists", key)
time.Sleep(time.Second * 3)
continue
}
(*item.(*net.Conn)).SetWriteDeadline(time.Now().Add(time.Second * 3))
_, err := (*item.(*net.Conn)).Write(packet)
(*item.(*net.Conn)).SetWriteDeadline(time.Time{})
if err != nil {
s.log.Printf("%s client control conn write signal fail, err: %s, retrying...", key, err)
time.Sleep(time.Second * 3)
continue
} else {
// s.cmServer.Add(serverID, ID, &inConn)
break
}
}
case CONN_CLIENT:
var key, ID, serverID string
err = utils.ReadPacketData(reader, &key, &ID, &serverID)
if err != nil {
s.log.Printf("read error,ERR:%s", err)
return
}
s.log.Printf("client connection , key: %s , id: %s, server id:%s", key, ID, serverID)
serverConnItem, ok := s.serverConns.Get(ID)
if !ok {
inConn.Close()
s.log.Printf("server conn %s exists", ID)
return
}
serverConn := serverConnItem.(ServerConn).Conn
utils.IoBind(*serverConn, inConn, func(err interface{}) {
s.serverConns.Remove(ID)
// s.cmClient.RemoveOne(key, ID)
// s.cmServer.RemoveOne(serverID, ID)
s.log.Printf("conn %s released", ID)
}, s.log)
// s.cmClient.Add(key, ID, &inConn)
s.log.Printf("conn %s created", ID)
case CONN_CLIENT_CONTROL:
var key string
err = utils.ReadPacketData(reader, &key)
if err != nil {
s.log.Printf("read error,ERR:%s", err)
return
}
s.log.Printf("client control connection, key: %s", key)
if s.clientControlConns.Has(key) {
item, _ := s.clientControlConns.Get(key)
(*item.(*net.Conn)).Close()
}
s.clientControlConns.Set(key, &inConn)
s.log.Printf("set client %s control conn", key)
}
}

View File

@ -0,0 +1,340 @@
package tunnel
import (
"crypto/tls"
"fmt"
"io"
logger "log"
"net"
"os"
"time"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/jumper"
//"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux"
)
const (
CONN_SERVER_MUX = uint8(6)
CONN_CLIENT_MUX = uint8(7)
)
type TunnelClientArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Key *string
Timeout *int
Jumper *string
}
type TunnelClient struct {
cfg TunnelClientArgs
ctrlConn net.Conn
isStop bool
userConns utils.ConcurrentMap
log *logger.Logger
jumper *jumper.Jumper
}
func NewTunnelClient() services.Service {
return &TunnelClient{
cfg: TunnelClientArgs{},
userConns: utils.NewConcurrentMap(),
isStop: false,
}
}
func (s *TunnelClient) InitService() (err error) {
return
}
func (s *TunnelClient) 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
}
s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
if err != nil {
return
}
if *s.cfg.Jumper != "" {
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 *TunnelClient) StopService() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop tclient service crashed,%s", e)
} else {
s.log.Printf("service tclient stopped")
}
}()
s.isStop = true
if s.ctrlConn != nil {
s.ctrlConn.Close()
}
for _, c := range s.userConns.Items() {
(*c.(*net.Conn)).Close()
}
}
func (s *TunnelClient) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(TunnelClientArgs)
if err = s.CheckArgs(); err != nil {
return
}
if err = s.InitService(); err != nil {
return
}
s.log.Printf("proxy on tunnel client mode")
for {
if s.isStop {
return
}
if s.ctrlConn != nil {
s.ctrlConn.Close()
}
s.ctrlConn, err = s.GetInConn(CONN_CLIENT_CONTROL, *s.cfg.Key)
if err != nil {
s.log.Printf("control connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
if s.ctrlConn != nil {
s.ctrlConn.Close()
}
continue
}
for {
if s.isStop {
return
}
var ID, clientLocalAddr, serverID string
err = utils.ReadPacketData(s.ctrlConn, &ID, &clientLocalAddr, &serverID)
if err != nil {
if s.ctrlConn != nil {
s.ctrlConn.Close()
}
s.log.Printf("read connection signal err: %s, retrying...", err)
break
}
s.log.Printf("signal revecived:%s %s %s", serverID, ID, clientLocalAddr)
protocol := clientLocalAddr[:3]
localAddr := clientLocalAddr[4:]
if protocol == "udp" {
go s.ServeUDP(localAddr, ID, serverID)
} else {
go s.ServeConn(localAddr, ID, serverID)
}
}
}
}
func (s *TunnelClient) Clean() {
s.StopService()
}
func (s *TunnelClient) GetInConn(typ uint8, data ...string) (outConn net.Conn, err error) {
outConn, err = s.GetConn()
if err != nil {
err = fmt.Errorf("connection err: %s", err)
return
}
_, err = outConn.Write(utils.BuildPacket(typ, data...))
if err != nil {
err = fmt.Errorf("write connection data err: %s ,retrying...", err)
utils.CloseConn(&outConn)
return
}
return
}
func (s *TunnelClient) GetConn() (conn net.Conn, err error) {
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))
}
}
if err == nil {
c, e := smux.Client(conn, &smux.Config{
AcceptBacklog: 256,
EnableKeepAlive: true,
KeepAliveInterval: 9 * time.Second,
ConnectionWriteTimeout: 3 * time.Second,
MaxStreamWindowSize: 512 * 1024,
LogOutput: os.Stderr,
})
if e != nil {
s.log.Printf("new mux client conn error,ERR:%s", e)
err = e
return
}
conn, e = c.OpenStream()
if e != nil {
s.log.Printf("mux client conn open stream error,ERR:%s", e)
err = e
return
}
}
return
}
func (s *TunnelClient) ServeUDP(localAddr, ID, serverID string) {
var inConn net.Conn
var err error
// for {
for {
if s.isStop {
if inConn != nil {
inConn.Close()
}
return
}
// s.cm.RemoveOne(*s.cfg.Key, ID)
inConn, err = s.GetInConn(CONN_CLIENT, *s.cfg.Key, ID, serverID)
if err != nil {
utils.CloseConn(&inConn)
s.log.Printf("connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
// s.cm.Add(*s.cfg.Key, ID, &inConn)
s.log.Printf("conn %s created", ID)
for {
if s.isStop {
return
}
srcAddr, body, err := utils.ReadUDPPacket(inConn)
if err == io.EOF || err == io.ErrUnexpectedEOF {
s.log.Printf("connection %s released", ID)
utils.CloseConn(&inConn)
break
} else if err != nil {
s.log.Printf("udp packet revecived fail, err: %s", err)
} else {
//s.log.Printf("udp packet revecived:%s,%v", srcAddr, body)
go s.processUDPPacket(&inConn, srcAddr, localAddr, body)
}
}
// }
}
func (s *TunnelClient) processUDPPacket(inConn *net.Conn, srcAddr, localAddr string, body []byte) {
dstAddr, err := net.ResolveUDPAddr("udp", localAddr)
if err != nil {
s.log.Printf("can't resolve address: %s", err)
utils.CloseConn(inConn)
return
}
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr)
if err != nil {
s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
return
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write(body)
if err != nil {
s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
return
}
//s.log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 1024)
length, _, err := conn.ReadFromUDP(buf)
if err != nil {
s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return
}
respBody := buf[0:length]
//s.log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
bs := utils.UDPPacket(srcAddr, respBody)
_, err = (*inConn).Write(bs)
if err != nil {
s.log.Printf("send udp response fail ,ERR:%s", err)
utils.CloseConn(inConn)
return
}
//s.log.Printf("send udp response success ,from:%s ,%d ,%v", dstAddr.String(), len(bs), bs)
}
func (s *TunnelClient) ServeConn(localAddr, ID, serverID string) {
var inConn, outConn net.Conn
var err error
for {
if s.isStop {
return
}
inConn, err = s.GetInConn(CONN_CLIENT, *s.cfg.Key, ID, serverID)
if err != nil {
utils.CloseConn(&inConn)
s.log.Printf("connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
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 {
utils.CloseConn(&inConn)
utils.CloseConn(&outConn)
s.log.Printf("build connection error, err: %s", err)
return
}
inAddr := inConn.RemoteAddr().String()
utils.IoBind(inConn, outConn, func(err interface{}) {
s.log.Printf("conn %s released", ID)
s.userConns.Remove(inAddr)
}, s.log)
if c, ok := s.userConns.Get(inAddr); ok {
(*c.(*net.Conn)).Close()
}
s.userConns.Set(inAddr, &inConn)
s.log.Printf("conn %s created", ID)
}

View File

@ -0,0 +1,434 @@
package tunnel
import (
"crypto/tls"
"fmt"
"io"
logger "log"
"net"
"os"
"runtime/debug"
"strconv"
"strings"
"time"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/jumper"
//"github.com/xtaci/smux"
smux "github.com/hashicorp/yamux"
)
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
Jumper *string
}
type UDPItem struct {
packet *[]byte
localAddr *net.UDPAddr
srcAddr *net.UDPAddr
}
type TunnelServerManager struct {
cfg TunnelServerArgs
udpChn chan UDPItem
serverID string
servers []*services.Service
log *logger.Logger
}
func NewTunnelServerManager() services.Service {
return &TunnelServerManager{
cfg: TunnelServerArgs{},
udpChn: make(chan UDPItem, 50000),
serverID: utils.Uniqueid(),
servers: []*services.Service{},
}
}
func (s *TunnelServerManager) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(TunnelServerArgs)
if err = s.CheckArgs(); err != nil {
return
}
if *s.cfg.Parent != "" {
s.log.Printf("use tls parent %s", *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 {
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 := NewTunnelServer()
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(TunnelServerArgs{
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,
Jumper: s.cfg.Jumper,
}, log)
if err != nil {
return
}
s.servers = append(s.servers, &server)
}
return
}
func (s *TunnelServerManager) Clean() {
s.StopService()
}
func (s *TunnelServerManager) StopService() {
for _, server := range s.servers {
(*server).Clean()
}
}
func (s *TunnelServerManager) CheckArgs() (err error) {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
err = fmt.Errorf("cert and key file required")
return
}
s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
return
}
func (s *TunnelServerManager) InitService() (err error) {
return
}
type TunnelServer struct {
cfg TunnelServerArgs
udpChn chan UDPItem
sc utils.ServerChannel
isStop bool
udpConn *net.Conn
userConns utils.ConcurrentMap
log *logger.Logger
jumper *jumper.Jumper
}
func NewTunnelServer() services.Service {
return &TunnelServer{
cfg: TunnelServerArgs{},
udpChn: make(chan UDPItem, 50000),
isStop: false,
userConns: utils.NewConcurrentMap(),
}
}
func (s *TunnelServer) StopService() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop server service crashed,%s", e)
} else {
s.log.Printf("service server stopped")
}
}()
s.isStop = true
if s.sc.Listener != nil {
(*s.sc.Listener).Close()
}
if s.sc.UDPListener != nil {
(*s.sc.UDPListener).Close()
}
if s.udpConn != nil {
(*s.udpConn).Close()
}
for _, c := range s.userConns.Items() {
(*c.(*net.Conn)).Close()
}
}
func (s *TunnelServer) InitService() (err error) {
s.UDPConnDeamon()
return
}
func (s *TunnelServer) CheckArgs() (err error) {
if *s.cfg.Remote == "" {
err = fmt.Errorf("remote required")
return
}
if *s.cfg.Jumper != "" {
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 *TunnelServer) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(TunnelServerArgs)
if err = s.CheckArgs(); err != nil {
return
}
if err = s.InitService(); err != nil {
return
}
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
s.sc = utils.NewServerChannel(host, p, s.log)
if *s.cfg.IsUDP {
err = s.sc.ListenUDP(func(packet []byte, localAddr, srcAddr *net.UDPAddr) {
s.udpChn <- UDPItem{
packet: &packet,
localAddr: localAddr,
srcAddr: srcAddr,
}
})
if err != nil {
return
}
s.log.Printf("proxy on udp tunnel server mode %s", (*s.sc.UDPListener).LocalAddr())
} else {
err = s.sc.ListenTCP(func(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
s.log.Printf("tserver conn 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(CONN_SERVER)
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
}
}
inAddr := inConn.RemoteAddr().String()
utils.IoBind(inConn, outConn, func(err interface{}) {
s.userConns.Remove(inAddr)
s.log.Printf("%s conn %s released", *s.cfg.Key, ID)
}, s.log)
if c, ok := s.userConns.Get(inAddr); ok {
(*c.(*net.Conn)).Close()
}
s.userConns.Set(inAddr, &inConn)
s.log.Printf("%s conn %s created", *s.cfg.Key, ID)
})
if err != nil {
return
}
s.log.Printf("proxy on tunnel server mode %s", (*s.sc.Listener).Addr())
}
return
}
func (s *TunnelServer) Clean() {
}
func (s *TunnelServer) GetOutConn(typ uint8) (outConn net.Conn, ID string, err error) {
outConn, err = s.GetConn()
if err != nil {
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()
_, err = outConn.Write(utils.BuildPacket(typ, *s.cfg.Key, ID, remoteAddr, s.cfg.Mgr.serverID))
if err != nil {
s.log.Printf("write connection data err: %s ,retrying...", err)
utils.CloseConn(&outConn)
return
}
return
}
func (s *TunnelServer) GetConn() (conn net.Conn, err error) {
var dconn net.Conn
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 {
dconn = 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 {
dconn = net.Conn(tls.Client(_c, conf))
}
}
if err == nil {
sess, e := smux.Client(dconn, &smux.Config{
AcceptBacklog: 256,
EnableKeepAlive: true,
KeepAliveInterval: 9 * time.Second,
ConnectionWriteTimeout: 3 * time.Second,
MaxStreamWindowSize: 512 * 1024,
LogOutput: os.Stderr,
})
if e != nil {
s.log.Printf("new mux client conn error,ERR:%s", e)
err = e
dconn.Close()
return
}
conn, e = sess.OpenStream()
if e != nil {
s.log.Printf("mux client conn open stream error,ERR:%s", e)
err = e
dconn.Close()
return
}
go func() {
defer func() {
_ = recover()
}()
timer := time.NewTicker(time.Second * 3)
for {
<-timer.C
if sess.NumStreams() == 0 {
sess.Close()
timer.Stop()
return
}
}
}()
}
return
}
func (s *TunnelServer) UDPConnDeamon() {
go func() {
defer func() {
if err := recover(); err != nil {
s.log.Printf("udp conn deamon crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var outConn net.Conn
// var hb utils.HeartbeatReadWriter
var ID string
// var cmdChn = make(chan bool, 1000)
var err error
for {
if s.isStop {
return
}
item := <-s.udpChn
RETRY:
if s.isStop {
return
}
if outConn == nil {
for {
if s.isStop {
return
}
outConn, ID, err = s.GetOutConn(CONN_SERVER)
if err != nil {
// cmdChn <- true
outConn = 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 {
go func(outConn net.Conn, ID string) {
if s.udpConn != nil {
(*s.udpConn).Close()
}
s.udpConn = &outConn
for {
if s.isStop {
return
}
srcAddrFromConn, body, err := utils.ReadUDPPacket(outConn)
if err == io.EOF || err == io.ErrUnexpectedEOF {
s.log.Printf("UDP deamon connection %s exited", ID)
break
}
if err != nil {
s.log.Printf("parse revecived udp packet fail, err: %s ,%v", err, body)
continue
}
//s.log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn)
_srcAddr := strings.Split(srcAddrFromConn, ":")
if len(_srcAddr) != 2 {
s.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 {
s.log.Printf("udp response to local %s fail,ERR:%s", srcAddrFromConn, err)
continue
}
//s.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
s.log.Printf("write udp packet to %s fail ,flush err:%s ,retrying...", *s.cfg.Parent, err)
goto RETRY
}
//s.log.Printf("write packet %v", *item.packet)
}
}()
}

View File

@ -1,183 +0,0 @@
package services
import (
"bufio"
"encoding/binary"
"fmt"
"log"
"net"
"proxy/utils"
"strconv"
"sync"
"time"
)
type BridgeItem struct {
ServerChn chan *net.Conn
ClientChn chan *net.Conn
ClientControl *net.Conn
Once *sync.Once
Key string
}
type TunnelBridge struct {
cfg TunnelBridgeArgs
br utils.ConcurrentMap
}
func NewTunnelBridge() Service {
return &TunnelBridge{
cfg: TunnelBridgeArgs{},
br: utils.NewConcurrentMap(),
}
}
func (s *TunnelBridge) InitService() {
}
func (s *TunnelBridge) Check() {
if s.cfg.CertBytes == nil || s.cfg.KeyBytes == nil {
log.Fatalf("cert and key file required")
}
}
func (s *TunnelBridge) StopService() {
}
func (s *TunnelBridge) Start(args interface{}) (err error) {
s.cfg = args.(TunnelBridgeArgs)
s.Check()
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 connType uint8
err = binary.Read(reader, binary.LittleEndian, &connType)
if err != nil {
utils.CloseConn(&inConn)
return
}
var key string
var connTypeStrMap = map[uint8]string{CONN_SERVER: "server", CONN_CLIENT: "client", CONN_CONTROL: "client"}
if connType == CONN_SERVER || connType == CONN_CLIENT || connType == CONN_CONTROL {
var keyLength uint16
err = binary.Read(reader, binary.LittleEndian, &keyLength)
if err != nil {
return
}
_key := make([]byte, keyLength)
n, err := reader.Read(_key)
if err != nil {
return
}
if n != int(keyLength) {
return
}
key = string(_key)
log.Printf("connection from %s , key: %s", connTypeStrMap[connType], key)
}
switch connType {
case CONN_SERVER:
s.ServerConn(&inConn, key)
case CONN_CLIENT:
s.ClientConn(&inConn, key)
case CONN_CONTROL:
s.ClientControlConn(&inConn, key)
default:
log.Printf("unkown conn type %d", connType)
utils.CloseConn(&inConn)
}
})
if err != nil {
return
}
log.Printf("proxy on tunnel bridge mode %s", (*sc.Listener).Addr())
return
}
func (s *TunnelBridge) Clean() {
s.StopService()
}
func (s *TunnelBridge) ClientConn(inConn *net.Conn, key string) {
chn, _ := s.ConnChn(key, CONN_CLIENT)
chn <- inConn
}
func (s *TunnelBridge) ServerConn(inConn *net.Conn, key string) {
chn, _ := s.ConnChn(key, CONN_SERVER)
chn <- inConn
}
func (s *TunnelBridge) ClientControlConn(inConn *net.Conn, key string) {
_, item := s.ConnChn(key, CONN_CLIENT)
utils.CloseConn(item.ClientControl)
if item.ClientControl != nil {
*item.ClientControl = *inConn
} else {
item.ClientControl = inConn
}
log.Printf("set client control conn,remote: %s", (*inConn).RemoteAddr())
}
func (s *TunnelBridge) ConnChn(key string, typ uint8) (chn chan *net.Conn, item *BridgeItem) {
s.br.SetIfAbsent(key, &BridgeItem{
ServerChn: make(chan *net.Conn, 10000),
ClientChn: make(chan *net.Conn, 10000),
Once: &sync.Once{},
Key: key,
})
_item, _ := s.br.Get(key)
item = _item.(*BridgeItem)
item.Once.Do(func() {
s.ChnDeamon(item)
})
if typ == CONN_CLIENT {
chn = item.ClientChn
} else {
chn = item.ServerChn
}
return
}
func (s *TunnelBridge) ChnDeamon(item *BridgeItem) {
go func() {
log.Printf("%s conn chan deamon started", item.Key)
for {
var clientConn *net.Conn
var serverConn *net.Conn
serverConn = <-item.ServerChn
log.Printf("%s server conn picked up", item.Key)
OUT:
for {
_item, _ := s.br.Get(item.Key)
Item := _item.(*BridgeItem)
var err error
if Item.ClientControl != nil && *Item.ClientControl != nil {
_, err = (*Item.ClientControl).Write([]byte{'0'})
} else {
err = fmt.Errorf("client control conn not exists")
}
if err != nil {
log.Printf("%s client control conn write signal fail, err: %s, retrying...", item.Key, err)
utils.CloseConn(Item.ClientControl)
*Item.ClientControl = nil
Item.ClientControl = nil
time.Sleep(time.Second * 3)
continue
} else {
select {
case clientConn = <-item.ClientChn:
log.Printf("%s client conn picked up", item.Key)
break OUT
case <-time.After(time.Second * time.Duration(*s.cfg.Timeout*5)):
log.Printf("%s client conn picked timeout, retrying...", item.Key)
}
}
}
utils.IoBind(*serverConn, *clientConn, func(isSrcErr bool, err error) {
utils.CloseConn(serverConn)
utils.CloseConn(clientConn)
log.Printf("%s conn %s - %s - %s - %s released", item.Key, (*serverConn).RemoteAddr(), (*serverConn).LocalAddr(), (*clientConn).LocalAddr(), (*clientConn).RemoteAddr())
}, func(i int, b bool) {}, 0)
log.Printf("%s conn %s - %s - %s - %s created", item.Key, (*serverConn).RemoteAddr(), (*serverConn).LocalAddr(), (*clientConn).LocalAddr(), (*clientConn).RemoteAddr())
}
}()
}

View File

@ -1,215 +0,0 @@
package services
import (
"bytes"
"crypto/tls"
"encoding/binary"
"fmt"
"io"
"log"
"net"
"proxy/utils"
"time"
)
type TunnelClient struct {
cfg TunnelClientArgs
}
func NewTunnelClient() Service {
return &TunnelClient{
cfg: TunnelClientArgs{},
}
}
func (s *TunnelClient) InitService() {
}
func (s *TunnelClient) Check() {
if *s.cfg.Parent != "" {
log.Printf("use tls parent %s", *s.cfg.Parent)
} else {
log.Fatalf("parent required")
}
if s.cfg.CertBytes == nil || s.cfg.KeyBytes == nil {
log.Fatalf("cert and key file required")
}
}
func (s *TunnelClient) StopService() {
}
func (s *TunnelClient) Start(args interface{}) (err error) {
s.cfg = args.(TunnelClientArgs)
s.Check()
s.InitService()
for {
ctrlConn, err := s.GetInConn(CONN_CONTROL)
if err != nil {
log.Printf("control connection err: %s", err)
time.Sleep(time.Second * 3)
utils.CloseConn(&ctrlConn)
continue
}
if *s.cfg.IsUDP {
log.Printf("proxy on udp tunnel client mode")
} else {
log.Printf("proxy on tcp tunnel client mode")
}
for {
signal := make([]byte, 1)
if signal[0] == 1 {
continue
}
_, err = ctrlConn.Read(signal)
if err != nil {
utils.CloseConn(&ctrlConn)
log.Printf("read connection signal err: %s", err)
break
}
log.Printf("signal revecived:%s", signal)
if *s.cfg.IsUDP {
go s.ServeUDP()
} else {
go s.ServeConn()
}
}
}
}
func (s *TunnelClient) Clean() {
s.StopService()
}
func (s *TunnelClient) GetInConn(typ uint8) (outConn net.Conn, err error) {
outConn, err = s.GetConn()
if err != nil {
err = fmt.Errorf("connection err: %s", err)
return
}
keyBytes := []byte(*s.cfg.Key)
keyLength := uint16(len(keyBytes))
pkg := new(bytes.Buffer)
binary.Write(pkg, binary.LittleEndian, typ)
binary.Write(pkg, binary.LittleEndian, keyLength)
binary.Write(pkg, binary.LittleEndian, keyBytes)
_, err = outConn.Write(pkg.Bytes())
if err != nil {
err = fmt.Errorf("write connection data err: %s ,retrying...", err)
utils.CloseConn(&outConn)
return
}
return
}
func (s *TunnelClient) GetConn() (conn net.Conn, err error) {
var _conn tls.Conn
_conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes)
if err == nil {
conn = net.Conn(&_conn)
}
return
}
func (s *TunnelClient) ServeUDP() {
var inConn net.Conn
var err error
for {
for {
inConn, err = s.GetInConn(CONN_CLIENT)
if err != nil {
utils.CloseConn(&inConn)
log.Printf("connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
log.Printf("conn created , remote : %s ", inConn.RemoteAddr())
for {
srcAddr, body, err := utils.ReadUDPPacket(&inConn)
if err == io.EOF || err == io.ErrUnexpectedEOF {
log.Printf("connection %s released", srcAddr)
utils.CloseConn(&inConn)
break
}
//log.Printf("udp packet revecived:%s,%v", srcAddr, body)
go s.processUDPPacket(&inConn, srcAddr, body)
}
}
}
func (s *TunnelClient) processUDPPacket(inConn *net.Conn, srcAddr string, body []byte) {
dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Local)
if err != nil {
log.Printf("can't resolve address: %s", err)
utils.CloseConn(inConn)
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, 512)
len, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return
}
respBody := buf[0:len]
//log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
_, err = (*inConn).Write(utils.UDPPacket(srcAddr, respBody))
if err != nil {
log.Printf("send udp response fail ,ERR:%s", err)
utils.CloseConn(inConn)
return
}
//log.Printf("send udp response success ,from:%s", dstAddr.String())
}
func (s *TunnelClient) ServeConn() {
var inConn, outConn net.Conn
var err error
for {
inConn, err = s.GetInConn(CONN_CLIENT)
if err != nil {
utils.CloseConn(&inConn)
log.Printf("connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
i := 0
for {
i++
outConn, err = utils.ConnectHost(*s.cfg.Local, *s.cfg.Timeout)
if err == nil || i == 3 {
break
} else {
if i == 3 {
log.Printf("connect to %s err: %s, retrying...", *s.cfg.Local, err)
time.Sleep(2 * time.Second)
continue
}
}
}
if err != nil {
utils.CloseConn(&inConn)
utils.CloseConn(&outConn)
log.Printf("build connection error, err: %s", err)
return
}
utils.IoBind(inConn, outConn, func(isSrcErr bool, err error) {
log.Printf("%s conn %s - %s - %s - %s released", *s.cfg.Key, inConn.RemoteAddr(), inConn.LocalAddr(), outConn.LocalAddr(), outConn.RemoteAddr())
utils.CloseConn(&inConn)
utils.CloseConn(&outConn)
}, func(i int, b bool) {}, 0)
log.Printf("%s conn %s - %s - %s - %s created", *s.cfg.Key, inConn.RemoteAddr(), inConn.LocalAddr(), outConn.LocalAddr(), outConn.RemoteAddr())
}

View File

@ -1,209 +0,0 @@
package services
import (
"bufio"
"bytes"
"crypto/tls"
"encoding/binary"
"io"
"log"
"net"
"proxy/utils"
"runtime/debug"
"strconv"
"strings"
"time"
)
type TunnelServer struct {
cfg TunnelServerArgs
udpChn chan UDPItem
sc utils.ServerChannel
}
func NewTunnelServer() Service {
return &TunnelServer{
cfg: TunnelServerArgs{},
udpChn: make(chan UDPItem, 50000),
}
}
type UDPItem struct {
packet *[]byte
localAddr *net.UDPAddr
srcAddr *net.UDPAddr
}
func (s *TunnelServer) InitService() {
s.UDPConnDeamon()
}
func (s *TunnelServer) Check() {
if *s.cfg.Parent != "" {
log.Printf("use tls parent %s", *s.cfg.Parent)
} else {
log.Fatalf("parent required")
}
if s.cfg.CertBytes == nil || s.cfg.KeyBytes == nil {
log.Fatalf("cert and key file required")
}
}
func (s *TunnelServer) StopService() {
}
func (s *TunnelServer) Start(args interface{}) (err error) {
s.cfg = args.(TunnelServerArgs)
s.Check()
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 <- UDPItem{
packet: &packet,
localAddr: localAddr,
srcAddr: srcAddr,
}
})
if err != nil {
return
}
log.Printf("proxy on udp tunnel 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("tserver conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var outConn net.Conn
for {
outConn, 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
}
}
utils.IoBind(inConn, outConn, func(isSrcErr bool, err error) {
utils.CloseConn(&outConn)
utils.CloseConn(&inConn)
log.Printf("%s conn %s - %s - %s - %s released", *s.cfg.Key, inConn.RemoteAddr(), inConn.LocalAddr(), outConn.LocalAddr(), outConn.RemoteAddr())
}, func(i int, b bool) {}, 0)
log.Printf("%s conn %s - %s - %s - %s created", *s.cfg.Key, inConn.RemoteAddr(), inConn.LocalAddr(), outConn.LocalAddr(), outConn.RemoteAddr())
})
if err != nil {
return
}
log.Printf("proxy on tunnel server mode %s", (*s.sc.Listener).Addr())
}
return
}
func (s *TunnelServer) Clean() {
s.StopService()
}
func (s *TunnelServer) GetOutConn() (outConn net.Conn, err error) {
outConn, err = s.GetConn()
if err != nil {
log.Printf("connection err: %s", err)
return
}
keyBytes := []byte(*s.cfg.Key)
keyLength := uint16(len(keyBytes))
pkg := new(bytes.Buffer)
binary.Write(pkg, binary.LittleEndian, CONN_SERVER)
binary.Write(pkg, binary.LittleEndian, keyLength)
binary.Write(pkg, binary.LittleEndian, keyBytes)
_, err = outConn.Write(pkg.Bytes())
if err != nil {
log.Printf("write connection data err: %s ,retrying...", err)
utils.CloseConn(&outConn)
return
}
return
}
func (s *TunnelServer) GetConn() (conn net.Conn, err error) {
var _conn tls.Conn
_conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes)
if err == nil {
conn = net.Conn(&_conn)
}
return
}
func (s *TunnelServer) 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 cmdChn = make(chan bool, 1)
var err error
for {
item := <-s.udpChn
RETRY:
if outConn == nil {
for {
outConn, err = s.GetOutConn()
if err != nil {
cmdChn <- true
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) {
go func() {
<-cmdChn
outConn.Close()
}()
for {
srcAddrFromConn, body, err := utils.ReadUDPPacket(&outConn)
if err == io.EOF || err == io.ErrUnexpectedEOF {
log.Printf("udp connection deamon exited, %s -> %s", outConn.LocalAddr(), outConn.RemoteAddr())
break
}
if err != nil {
log.Printf("parse revecived udp packet fail, err: %s", err)
continue
}
//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", srcAddrFromConn)
}
}(outConn)
break
}
}
}
writer := bufio.NewWriter(outConn)
writer.Write(utils.UDPPacket(item.srcAddr.String(), *item.packet))
err := writer.Flush()
if err != nil {
outConn = nil
log.Printf("write udp packet to %s fail ,flush err:%s", *s.cfg.Parent, err)
goto RETRY
}
//log.Printf("write packet %v", *item.packet)
}
}()
}

View File

@ -1,211 +0,0 @@
package services
import (
"bufio"
"fmt"
"hash/crc32"
"io"
"log"
"net"
"proxy/utils"
"runtime/debug"
"strconv"
"strings"
"time"
)
type UDP struct {
p utils.ConcurrentMap
outPool utils.OutPool
cfg UDPArgs
sc *utils.ServerChannel
}
func NewUDP() Service {
return &UDP{
outPool: utils.OutPool{},
p: utils.NewConcurrentMap(),
}
}
func (s *UDP) InitService() {
if *s.cfg.ParentType != TYPE_UDP {
s.InitOutConnPool()
}
}
func (s *UDP) StopService() {
if s.outPool.Pool != nil {
s.outPool.Pool.ReleaseAll()
}
}
func (s *UDP) Start(args interface{}) (err error) {
s.cfg = args.(UDPArgs)
if *s.cfg.Parent != "" {
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
} else {
log.Fatalf("parent required for udp %s", *s.cfg.Local)
}
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p)
s.sc = &sc
err = sc.ListenUDP(s.callback)
if err != nil {
return
}
log.Printf("udp proxy on %s", (*sc.UDPListener).LocalAddr())
return
}
func (s *UDP) Clean() {
s.StopService()
}
func (s *UDP) callback(packet []byte, localAddr, srcAddr *net.UDPAddr) {
defer func() {
if err := recover(); err != nil {
log.Printf("udp conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var err error
switch *s.cfg.ParentType {
case TYPE_TCP:
fallthrough
case TYPE_TLS:
err = s.OutToTCP(packet, localAddr, srcAddr)
case TYPE_UDP:
err = s.OutToUDP(packet, localAddr, srcAddr)
default:
err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType)
}
if err != nil {
log.Printf("connect to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err)
}
}
func (s *UDP) GetConn(connKey string) (conn net.Conn, isNew bool, err error) {
isNew = !s.p.Has(connKey)
var _conn interface{}
if isNew {
_conn, err = s.outPool.Pool.Get()
if err != nil {
return nil, false, err
}
s.p.Set(connKey, _conn)
} else {
_conn, _ = s.p.Get(connKey)
}
conn = _conn.(net.Conn)
return
}
func (s *UDP) OutToTCP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err error) {
numLocal := crc32.ChecksumIEEE([]byte(localAddr.String()))
numSrc := crc32.ChecksumIEEE([]byte(srcAddr.String()))
mod := uint32(*s.cfg.PoolSize)
if mod == 0 {
mod = 10
}
connKey := uint64((numLocal/10)*10 + numSrc%mod)
conn, isNew, err := s.GetConn(fmt.Sprintf("%d", connKey))
if err != nil {
log.Printf("upd get conn to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err)
return
}
if isNew {
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("udp conn handler out to tcp crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
log.Printf("conn %d created , local: %s", connKey, srcAddr.String())
for {
srcAddrFromConn, body, err := utils.ReadUDPPacket(&conn)
if err == io.EOF || err == io.ErrUnexpectedEOF {
//log.Printf("connection %d released", connKey)
s.p.Remove(fmt.Sprintf("%d", connKey))
break
}
if err != nil {
log.Printf("parse revecived udp packet fail, err: %s", err)
continue
}
//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", srcAddr, err)
continue
}
//log.Printf("udp response to local %s success", srcAddr)
}
}()
}
//log.Printf("select conn %d , local: %s", connKey, srcAddr.String())
writer := bufio.NewWriter(conn)
//fmt.Println(conn, writer)
writer.Write(utils.UDPPacket(srcAddr.String(), packet))
err = writer.Flush()
if err != nil {
log.Printf("write udp packet to %s fail ,flush err:%s", *s.cfg.Parent, err)
return
}
//log.Printf("write packet %v", packet)
return
}
func (s *UDP) OutToUDP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err error) {
//log.Printf("udp packet revecived:%s,%v", srcAddr, packet)
dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Parent)
if err != nil {
log.Printf("resolve udp addr %s fail fail,ERR:%s", dstAddr.String(), 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)))
_, err = conn.Write(packet)
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, 512)
len, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return
}
//log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
_, err = s.sc.UDPListener.WriteToUDP(buf[0:len], srcAddr)
if err != nil {
log.Printf("send udp response to cluster fail ,ERR:%s", err)
return
}
//log.Printf("send udp response to cluster success ,from:%s", dstAddr.String())
return
}
func (s *UDP) InitOutConnPool() {
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutPool(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType == TYPE_TLS,
s.cfg.CertBytes, s.cfg.KeyBytes,
*s.cfg.Parent,
*s.cfg.Timeout,
*s.cfg.PoolSize,
*s.cfg.PoolSize*2,
)
}
}

261
services/udp/udp.go Normal file
View File

@ -0,0 +1,261 @@
package udp
import (
"bufio"
"fmt"
"hash/crc32"
"io"
logger "log"
"net"
"runtime/debug"
"strconv"
"strings"
"time"
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/services/kcpcfg"
"github.com/snail007/goproxy/utils"
)
type UDPArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
ParentType *string
Timeout *int
CheckParentInterval *int
}
type UDP struct {
p utils.ConcurrentMap
outPool utils.OutConn
cfg UDPArgs
sc *utils.ServerChannel
isStop bool
log *logger.Logger
}
func NewUDP() services.Service {
return &UDP{
outPool: utils.OutConn{},
p: utils.NewConcurrentMap(),
isStop: false,
}
}
func (s *UDP) CheckArgs() (err error) {
if *s.cfg.Parent == "" {
err = fmt.Errorf("parent required for udp %s", *s.cfg.Local)
return
}
if *s.cfg.ParentType == "" {
err = fmt.Errorf("parent type unkown,use -T <tls|tcp>")
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 *UDP) InitService() (err error) {
if *s.cfg.ParentType != "udp" {
s.InitOutConnPool()
}
return
}
func (s *UDP) StopService() {
defer func() {
e := recover()
if e != nil {
s.log.Printf("stop udp service crashed,%s", e)
} else {
s.log.Printf("service udp stopped")
}
}()
s.isStop = true
if s.sc.Listener != nil && *s.sc.Listener != nil {
(*s.sc.Listener).Close()
}
if s.sc.UDPListener != nil {
(*s.sc.UDPListener).Close()
}
}
func (s *UDP) Start(args interface{}, log *logger.Logger) (err error) {
s.log = log
s.cfg = args.(UDPArgs)
if err = s.CheckArgs(); err != nil {
return
}
s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
if err = s.InitService(); err != nil {
return
}
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p, s.log)
s.sc = &sc
err = sc.ListenUDP(s.callback)
if err != nil {
return
}
s.log.Printf("udp proxy on %s", (*sc.UDPListener).LocalAddr())
return
}
func (s *UDP) Clean() {
s.StopService()
}
func (s *UDP) callback(packet []byte, localAddr, srcAddr *net.UDPAddr) {
defer func() {
if err := recover(); err != nil {
s.log.Printf("udp conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var err error
switch *s.cfg.ParentType {
case "tcp":
fallthrough
case "tls":
err = s.OutToTCP(packet, localAddr, srcAddr)
case "udp":
err = s.OutToUDP(packet, localAddr, srcAddr)
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", *s.cfg.ParentType, *s.cfg.Parent, err)
}
}
func (s *UDP) GetConn(connKey string) (conn net.Conn, isNew bool, err error) {
isNew = !s.p.Has(connKey)
var _conn interface{}
if isNew {
_conn, err = s.outPool.Get()
if err != nil {
return nil, false, err
}
s.p.Set(connKey, _conn)
} else {
_conn, _ = s.p.Get(connKey)
}
conn = _conn.(net.Conn)
return
}
func (s *UDP) OutToTCP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err error) {
numLocal := crc32.ChecksumIEEE([]byte(localAddr.String()))
numSrc := crc32.ChecksumIEEE([]byte(srcAddr.String()))
mod := uint32(10)
if mod == 0 {
mod = 10
}
connKey := uint64((numLocal/10)*10 + numSrc%mod)
conn, isNew, err := s.GetConn(fmt.Sprintf("%d", connKey))
if err != nil {
s.log.Printf("upd get conn to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err)
return
}
if isNew {
go func() {
defer func() {
if err := recover(); err != nil {
s.log.Printf("udp conn handler out to tcp crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
s.log.Printf("conn %d created , local: %s", connKey, srcAddr.String())
for {
if s.isStop {
conn.Close()
return
}
srcAddrFromConn, body, err := utils.ReadUDPPacket(bufio.NewReader(conn))
if err == io.EOF || err == io.ErrUnexpectedEOF {
//s.log.Printf("connection %d released", connKey)
s.p.Remove(fmt.Sprintf("%d", connKey))
break
}
if err != nil {
s.log.Printf("parse revecived udp packet fail, err: %s", err)
continue
}
//s.log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn)
_srcAddr := strings.Split(srcAddrFromConn, ":")
if len(_srcAddr) != 2 {
s.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 {
s.log.Printf("udp response to local %s fail,ERR:%s", srcAddr, err)
continue
}
//s.log.Printf("udp response to local %s success", srcAddr)
}
}()
}
//s.log.Printf("select conn %d , local: %s", connKey, srcAddr.String())
writer := bufio.NewWriter(conn)
//fmt.Println(conn, writer)
writer.Write(utils.UDPPacket(srcAddr.String(), packet))
err = writer.Flush()
if err != nil {
s.log.Printf("write udp packet to %s fail ,flush err:%s", *s.cfg.Parent, err)
return
}
//s.log.Printf("write packet %v", packet)
return
}
func (s *UDP) OutToUDP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err error) {
//s.log.Printf("udp packet revecived:%s,%v", srcAddr, packet)
dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Parent)
if err != nil {
s.log.Printf("resolve udp addr %s fail fail,ERR:%s", dstAddr.String(), err)
return
}
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr)
if err != nil {
s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
return
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write(packet)
if err != nil {
s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
return
}
//s.log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 512)
len, _, err := conn.ReadFromUDP(buf)
if err != nil {
s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return
}
//s.log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
_, err = s.sc.UDPListener.WriteToUDP(buf[0:len], srcAddr)
if err != nil {
s.log.Printf("send udp response to cluster fail ,ERR:%s", err)
return
}
//s.log.Printf("send udp response to cluster success ,from:%s", dstAddr.String())
return
}
func (s *UDP) InitOutConnPool() {
if *s.cfg.ParentType == "tls" || *s.cfg.ParentType == "tcp" {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutConn(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType,
kcpcfg.KCPConfigArgs{},
s.cfg.CertBytes, s.cfg.KeyBytes, nil,
*s.cfg.Parent,
*s.cfg.Timeout,
)
}
}

View File

@ -1,4 +1,3 @@
#!/bin/bash
rm -rf /usr/bin/proxy
rm -rf /usr/bin/proxyd
echo "uninstall done"

84
utils/aes/aes.go Normal file
View File

@ -0,0 +1,84 @@
// Playbook - http://play.golang.org/p/3wFl4lacjX
package goaes
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"io"
"strings"
)
func addBase64Padding(value string) string {
m := len(value) % 4
if m != 0 {
value += strings.Repeat("=", 4-m)
}
return value
}
func removeBase64Padding(value string) string {
return strings.Replace(value, "=", "", -1)
}
func Pad(src []byte) []byte {
padding := aes.BlockSize - len(src)%aes.BlockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}
func Unpad(src []byte) ([]byte, error) {
length := len(src)
unpadding := int(src[length-1])
if unpadding > length {
return nil, errors.New("unpad error. This could happen when incorrect encryption key is used")
}
return src[:(length - unpadding)], nil
}
func Encrypt(key []byte, text []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
msg := Pad(text)
ciphertext := make([]byte, aes.BlockSize+len(msg))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
cfb := cipher.NewCFBEncrypter(block, iv)
cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(msg))
return ciphertext, nil
}
func Decrypt(key []byte, text []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if (len(text) % aes.BlockSize) != 0 {
return nil, errors.New("blocksize must be multipe of decoded message length")
}
iv := text[:aes.BlockSize]
msg := text[aes.BlockSize:]
cfb := cipher.NewCFBDecrypter(block, iv)
cfb.XORKeyStream(msg, msg)
unpadMsg, err := Unpad(msg)
if err != nil {
return nil, err
}
return unpadMsg, nil
}

199
utils/cert/cert.go Normal file
View File

@ -0,0 +1,199 @@
package cert
import (
"bytes"
cryptorand "crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"io/ioutil"
"math/big"
"math/rand"
"net"
"strconv"
"time"
)
func CreateSignCertToFile(rootCa *x509.Certificate, rootKey *rsa.PrivateKey, domainOrIP string, expireDays int, name string) (err error) {
cert, key, err := CreateSignCert(rootCa, rootKey, domainOrIP, expireDays)
if err != nil {
return
}
err = ioutil.WriteFile(name+".crt", cert, 0755)
if err != nil {
return
}
err = ioutil.WriteFile(name+".key", key, 0755)
return
}
func CreateSignCert(rootCa *x509.Certificate, rootKey *rsa.PrivateKey, domainOrIP string, expireDays int) (certBytes []byte, keyBytes []byte, err error) {
cer := &x509.Certificate{
SerialNumber: big.NewInt(rand.Int63()), //证书序列号
Subject: pkix.Name{
Country: []string{getCountry()},
Organization: []string{domainOrIP},
OrganizationalUnit: []string{domainOrIP},
CommonName: domainOrIP,
},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(time.Hour * 24 * time.Duration(expireDays)),
BasicConstraintsValid: true,
IsCA: false,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
//KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment,
EmailAddresses: []string{},
IPAddresses: []net.IP{},
}
if ip := net.ParseIP(domainOrIP); ip != nil {
cer.IPAddresses = append(cer.IPAddresses, ip)
} else {
cer.DNSNames = append(cer.DNSNames, domainOrIP)
}
// cer.IPAddresses = append(cer.IPAddresses, alternateIPs...)
// cer.DNSNames = append(cer.DNSNames, alternateDNS...)
//生成公钥私钥对
priKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
if err != nil {
return
}
certBytes, err = x509.CreateCertificate(cryptorand.Reader, cer, rootCa, &priKey.PublicKey, rootKey)
if err != nil {
return
}
//编码证书文件和私钥文件
caPem := &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certBytes = pem.EncodeToMemory(caPem)
buf := x509.MarshalPKCS1PrivateKey(priKey)
keyPem := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: buf,
}
keyBytes = pem.EncodeToMemory(keyPem)
return
}
func CreateCaToFile(name, domainOrIP string, expireDays int) (err error) {
ca, key, err := CreateCa(domainOrIP, expireDays)
if err != nil {
return
}
err = ioutil.WriteFile(name+".crt", ca, 0755)
if err != nil {
return
}
err = ioutil.WriteFile(name+".key", key, 0755)
return
}
func CreateCa(organization string, expireDays int) (certBytes []byte, keyBytes []byte, err error) {
priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
if err != nil {
return nil, nil, err
}
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: organization,
Organization: []string{organization},
OrganizationalUnit: []string{organization},
Country: []string{getCountry()},
},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(time.Hour * 24 * time.Duration(expireDays)),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
BasicConstraintsValid: true,
IsCA: true,
}
derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return nil, nil, err
}
// Generate cert
certBuffer := bytes.Buffer{}
if err := pem.Encode(&certBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
return nil, nil, err
}
// Generate key
keyBuffer := bytes.Buffer{}
if err := pem.Encode(&keyBuffer, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
return nil, nil, err
}
return certBuffer.Bytes(), keyBuffer.Bytes(), nil
}
func ParseCertAndKeyBytes(certPemFileByes, keyFileBytes []byte) (cert *x509.Certificate, privateKey *rsa.PrivateKey, err error) {
//解析根证书
cert, err = ParseCertBytes(certPemFileByes)
if err != nil {
return
}
//解析私钥
privateKey, err = ParseKeyBytes(keyFileBytes)
return
}
func ParseCertAndKey(certPemFile, keyFile string) (cert *x509.Certificate, privateKey *rsa.PrivateKey, err error) {
//解析根证书
cert, err = ParseCert(certPemFile)
if err != nil {
return
}
//解析私钥
privateKey, err = ParseKey(keyFile)
return
}
func ParseCert(certPemFile string) (cert *x509.Certificate, err error) {
//解析证书
certFile_, err := ioutil.ReadFile(certPemFile)
if err != nil {
return
}
cert, err = ParseCertBytes(certFile_)
return
}
func ParseKey(keyFile string) (key *rsa.PrivateKey, err error) {
//解析证书
keyFile_, err := ioutil.ReadFile(keyFile)
if err != nil {
return
}
key, err = ParseKeyBytes(keyFile_)
return
}
func ParseCertBytes(certPemFileBytes []byte) (cert *x509.Certificate, err error) {
caBlock, _ := pem.Decode(certPemFileBytes)
cert, err = x509.ParseCertificate(caBlock.Bytes)
return
}
func ParseKeyBytes(keyFileBytes []byte) (praKey *rsa.PrivateKey, err error) {
keyBlock, _ := pem.Decode(keyFileBytes)
praKey, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
return
}
func getCountry() string {
CList := []string{"AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AR", "AT", "AU", "AZ", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BR", "BS", "BW", "BY", "BZ", "CA", "CF", "CG", "CH", "CK", "CL", "CM", "CN", "CO", "CR", "CS", "CU", "CY", "CZ", "DE", "DJ", "DK", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FJ", "FR", "GA", "GB", "GD", "GE", "GF", "GH", "GI", "GM", "GN", "GR", "GT", "GU", "GY", "HK", "HN", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IR", "IS", "IT", "JM", "JO", "JP", "KE", "KG", "KH", "KP", "KR", "KT", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "ML", "MM", "MN", "MO", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NR", "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PR", "PT", "PY", "QA", "RO", "RU", "SA", "SB", "SC", "SD", "SE", "SG", "SI", "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", "SY", "SZ", "TD", "TG", "TH", "TJ", "TM", "TN", "TO", "TR", "TT", "TW", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "YE", "YU", "ZA", "ZM", "ZR", "ZW"}
return CList[int(randInt(4))%len(CList)]
}
func randInt(strLen int) int64 {
codes := "123456789"
codeLen := len(codes)
data := make([]byte, strLen)
rand.Seed(time.Now().UnixNano() + rand.Int63() + rand.Int63() + rand.Int63() + rand.Int63())
for i := 0; i < strLen; i++ {
idx := rand.Intn(codeLen)
data[i] = byte(codes[idx])
}
i, _ := strconv.ParseInt(string(data), 10, 64)
return i
}

71
utils/cert/cert_test.go Normal file
View File

@ -0,0 +1,71 @@
package cert
import (
"os"
"testing"
)
func TestCaGen(t *testing.T) {
err := CreateCaToFile("ca", "test", 365)
if err != nil {
t.Fatal(err)
return
}
ca, key, err := ParseCertAndKey("ca.crt", "ca.key")
if err != nil {
t.Fatal(err)
return
}
if ca.Subject.Organization[0] != "test" {
t.Fatalf("Organization %s not match test", ca.Subject.Organization[0])
return
}
err = key.Validate()
if err != nil {
t.Fatal(err)
return
}
os.Remove("ca.crt")
os.Remove("ca.key")
}
func TestSign(t *testing.T) {
err := CreateCaToFile("ca", "test", 365)
if err != nil {
t.Fatal(err)
return
}
ca, key, err := ParseCertAndKey("ca.crt", "ca.key")
if err != nil {
t.Fatal(err)
return
}
err = CreateCaToFile("ca", "test", 365)
if err != nil {
t.Fatal(err)
return
}
err = CreateSignCertToFile(ca, key, "server.com", 365, "server")
if err != nil {
t.Fatal(err)
return
}
servercrt, serverkey, err := ParseCertAndKey("server.crt", "server.key")
if err != nil {
t.Fatal(err)
return
}
if servercrt.Subject.CommonName != "server.com" {
t.Fatalf("CommonName %s not match server.com", ca.Subject.CommonName)
return
}
err = serverkey.Validate()
if err != nil {
t.Fatal(err)
return
}
os.Remove("ca.crt")
os.Remove("ca.key")
os.Remove("server.crt")
os.Remove("server.key")
}

View File

@ -0,0 +1,95 @@
package conncrypt
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"hash"
"io"
"net"
"golang.org/x/crypto/pbkdf2"
)
//Confg defaults
const DefaultIterations = 1024
const DefaultKeySize = 24 //256bits
var DefaultHashFunc = sha256.New
var DefaultSalt = []byte(`
(;QUHj.BQ?RXzYSO]ifkXp/G!kFmWyXyEV6Nt!d|@bo+N$L9+<d$|g6e26T}
Ao<:>SOd,6acYKY_ec+(x"R";\'4&fTAVu92GVA-wxBptOTM^2,iP5%)wnhW
hwk=]Snsgymt!3gbP2pe=J//}1a?lp9ej=&TB!C_V(cT2?z8wyoL_-13fd[]
`) //salt must be predefined in order to derive the same key
//Config stores the PBKDF2 key generation parameters
type Config struct {
Password string
Salt []byte
Iterations int
KeySize int
HashFunc func() hash.Hash
}
//New creates an AES encrypted net.Conn by generating
//a key using PBKDF2 with the provided configuration
func New(conn net.Conn, c *Config) net.Conn {
//set defaults
if len(c.Salt) == 0 {
c.Salt = DefaultSalt
}
if c.Iterations == 0 {
c.Iterations = DefaultIterations
}
if c.KeySize != 16 && c.KeySize != 24 && c.KeySize != 32 {
c.KeySize = DefaultKeySize
}
if c.HashFunc == nil {
c.HashFunc = DefaultHashFunc
}
//generate key
key := pbkdf2.Key([]byte(c.Password), c.Salt, c.Iterations, c.KeySize, c.HashFunc)
// could use scrypt, but it's a bit slow...
// dk, err := scrypt.Key([]byte(c.Password), c.Salt, 16384, 8, 1, 32)
//key will be always be the correct size so this will never error
conn, _ = NewFromKey(conn, key)
return conn
}
//NewFromKey creates an AES encrypted net.Conn using the provided key
func NewFromKey(conn net.Conn, key []byte) (net.Conn, error) {
block, err := aes.NewCipher([]byte(key))
if err != nil {
return nil, err
}
//hash(key) -> read IV
riv := DefaultHashFunc().Sum(key)
rstream := cipher.NewCFBDecrypter(block, riv[:aes.BlockSize])
reader := &cipher.StreamReader{S: rstream, R: conn}
//hash(read IV) -> write IV
wiv := DefaultHashFunc().Sum(riv)
wstream := cipher.NewCFBEncrypter(block, wiv[:aes.BlockSize])
writer := &cipher.StreamWriter{S: wstream, W: conn}
return &cryptoConn{
Conn: conn,
r: reader,
w: writer,
}, nil
}
type cryptoConn struct {
net.Conn
r io.Reader
w io.Writer
}
//replace read and write methods
func (c *cryptoConn) Read(p []byte) (int, error) {
return c.r.Read(p)
}
func (c *cryptoConn) Write(p []byte) (int, error) {
return c.w.Write(p)
}

View File

@ -3,116 +3,102 @@ package utils
import (
"bufio"
"bytes"
"crypto/sha1"
"crypto/tls"
"crypto/x509"
"encoding/binary"
"encoding/pem"
"errors"
"fmt"
"io"
"log"
"io/ioutil"
logger "log"
"math/rand"
"net"
"net/http"
"os"
"os/exec"
"sync"
"runtime/debug"
"github.com/snail007/goproxy/services/kcpcfg"
"golang.org/x/crypto/pbkdf2"
"context"
"strconv"
"strings"
"time"
"github.com/snail007/goproxy/utils/id"
kcp "github.com/xtaci/kcp-go"
)
func IoBind(dst io.ReadWriter, src io.ReadWriter, fn func(isSrcErr bool, err error), cfn func(count int, isPositive bool), bytesPreSec float64) {
var one = &sync.Once{}
func IoBind(dst io.ReadWriteCloser, src io.ReadWriteCloser, fn func(err interface{}), log *logger.Logger) {
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("IoBind crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
if err := recover(); err != nil {
log.Printf("bind crashed %s", err)
}
}()
var err error
var isSrcErr bool
if bytesPreSec > 0 {
newreader := NewReader(src)
newreader.SetRateLimit(bytesPreSec)
_, isSrcErr, err = ioCopy(dst, newreader, func(c int) {
cfn(c, false)
})
} else {
_, isSrcErr, err = ioCopy(dst, src, func(c int) {
cfn(c, false)
})
}
if err != nil {
one.Do(func() {
fn(isSrcErr, err)
})
}
}()
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("IoBind crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
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
}()
var err error
var isSrcErr bool
if bytesPreSec > 0 {
newReader := NewReader(dst)
newReader.SetRateLimit(bytesPreSec)
_, isSrcErr, err = ioCopy(src, newReader, func(c int) {
cfn(c, true)
})
} else {
_, isSrcErr, err = ioCopy(src, dst, func(c int) {
cfn(c, true)
})
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")
}
if err != nil {
one.Do(func() {
fn(isSrcErr, err)
})
src.Close()
dst.Close()
if fn != nil {
fn(err)
}
}()
}
func ioCopy(dst io.Writer, src io.Reader, fn ...func(count int)) (written int64, isSrcErr bool, err error) {
buf := make([]byte, 32*1024)
func ioCopy(dst io.ReadWriter, src io.ReadWriter) (err error) {
buf := LeakyBuffer.Get()
defer LeakyBuffer.Put(buf)
n := 0
for {
nr, er := src.Read(buf)
if nr > 0 {
nw, ew := dst.Write(buf[0:nr])
if nw > 0 {
written += int64(nw)
if len(fn) == 1 {
fn[0](nw)
}
}
if ew != nil {
err = ew
break
}
if nr != nw {
err = io.ErrShortWrite
break
n, err = src.Read(buf)
if n > 0 {
if _, e := dst.Write(buf[0:n]); e != nil {
return e
}
}
if er != nil {
err = er
isSrcErr = true
break
if err != nil {
return
}
}
return written, isSrcErr, err
}
func TlsConnectHost(host string, timeout int, certBytes, keyBytes []byte) (conn tls.Conn, err error) {
func TlsConnectHost(host string, timeout int, certBytes, keyBytes, caCertBytes []byte) (conn tls.Conn, err error) {
h := strings.Split(host, ":")
port, _ := strconv.Atoi(h[1])
return TlsConnect(h[0], port, timeout, certBytes, keyBytes)
return TlsConnect(h[0], port, timeout, certBytes, keyBytes, caCertBytes)
}
func TlsConnect(host string, port, timeout int, certBytes, keyBytes []byte) (conn tls.Conn, err error) {
conf, err := getRequestTlsConfig(certBytes, keyBytes)
func TlsConnect(host string, port, timeout int, certBytes, keyBytes, caCertBytes []byte) (conn tls.Conn, err error) {
conf, err := getRequestTlsConfig(certBytes, keyBytes, caCertBytes)
if err != nil {
return
}
@ -122,22 +108,51 @@ func TlsConnect(host string, port, timeout int, certBytes, keyBytes []byte) (con
}
return *tls.Client(_conn, conf), err
}
func getRequestTlsConfig(certBytes, keyBytes []byte) (conf *tls.Config, err error) {
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()
ok := serverCertPool.AppendCertsFromPEM(certBytes)
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},
ServerName: "proxy",
InsecureSkipVerify: false,
InsecureSkipVerify: true,
ServerName: x509Cert.Subject.CommonName,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
opts := x509.VerifyOptions{
Roots: serverCertPool,
}
for _, rawCert := range rawCerts {
cert, _ := x509.ParseCertificate(rawCert)
_, err := cert.Verify(opts)
if err != nil {
return err
}
}
return nil
},
}
return
}
@ -146,29 +161,23 @@ func ConnectHost(hostAndPort string, timeout int) (conn net.Conn, err error) {
conn, err = net.DialTimeout("tcp", hostAndPort, time.Duration(timeout)*time.Millisecond)
return
}
func ListenTls(ip string, port int, certBytes, keyBytes []byte) (ln *net.Listener, err error) {
var cert tls.Certificate
cert, err = tls.X509KeyPair(certBytes, keyBytes)
func ConnectKCPHost(hostAndPort string, config kcpcfg.KCPConfigArgs) (conn net.Conn, err error) {
kcpconn, err := kcp.DialWithOptions(hostAndPort, config.Block, *config.DataShard, *config.ParityShard)
if err != nil {
return
}
clientCertPool := x509.NewCertPool()
ok := clientCertPool.AppendCertsFromPEM(certBytes)
if !ok {
err = errors.New("failed to parse root certificate")
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
}
config := &tls.Config{
ClientCAs: clientCertPool,
ServerName: "proxy",
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
}
_ln, err := tls.Listen("tcp", fmt.Sprintf("%s:%d", ip, port), config)
if err == nil {
ln = &_ln
}
return
return NewCompStream(kcpconn), err
}
func PathExists(_path string) bool {
_, err := os.Stat(_path)
if err != nil && os.IsNotExist(err) {
@ -195,30 +204,21 @@ func HTTPGet(URL string, timeout int) (err error) {
}
func CloseConn(conn *net.Conn) {
defer func() {
_ = recover()
}()
if conn != nil && *conn != nil {
(*conn).SetDeadline(time.Now().Add(time.Millisecond))
(*conn).Close()
}
}
func Keygen() (err error) {
cmd := exec.Command("sh", "-c", "openssl genrsa -out proxy.key 2048")
out, err := cmd.CombinedOutput()
if err != nil {
log.Printf("err:%s", err)
return
}
fmt.Println(string(out))
cmd = exec.Command("sh", "-c", `openssl req -new -key proxy.key -x509 -days 3650 -out proxy.crt -subj /C=CN/ST=BJ/O="Localhost Ltd"/CN=proxy`)
out, err = cmd.CombinedOutput()
if err != nil {
log.Printf("err:%s", err)
return
}
fmt.Println(string(out))
return
}
func GetAllInterfaceAddr() ([]net.IP, error) {
var allInterfaceAddrCache []net.IP
func GetAllInterfaceAddr() ([]net.IP, error) {
if allInterfaceAddrCache != nil {
return allInterfaceAddrCache, nil
}
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
@ -259,12 +259,14 @@ func GetAllInterfaceAddr() ([]net.IP, error) {
return nil, fmt.Errorf("no address Found, net.InterfaceAddrs: %v", addresses)
}
//only need first
allInterfaceAddrCache = addresses
return addresses, nil
}
func UDPPacket(srcAddr string, packet []byte) []byte {
addrBytes := []byte(srcAddr)
addrLength := uint16(len(addrBytes))
bodyLength := uint16(len(packet))
//log.Printf("build packet : addr len %d, body len %d", addrLength, bodyLength)
pkg := new(bytes.Buffer)
binary.Write(pkg, binary.LittleEndian, addrLength)
binary.Write(pkg, binary.LittleEndian, addrBytes)
@ -272,8 +274,8 @@ func UDPPacket(srcAddr string, packet []byte) []byte {
binary.Write(pkg, binary.LittleEndian, packet)
return pkg.Bytes()
}
func ReadUDPPacket(conn *net.Conn) (srcAddr string, packet []byte, err error) {
reader := bufio.NewReader(*conn)
func ReadUDPPacket(_reader io.Reader) (srcAddr string, packet []byte, err error) {
reader := bufio.NewReader(_reader)
var addrLength uint16
var bodyLength uint16
err = binary.Read(reader, binary.LittleEndian, &addrLength)
@ -286,12 +288,14 @@ func ReadUDPPacket(conn *net.Conn) (srcAddr string, packet []byte, err error) {
return
}
if n != int(addrLength) {
err = fmt.Errorf("n != int(addrLength), %d,%d", n, addrLength)
return
}
srcAddr = string(_srcAddr)
err = binary.Read(reader, binary.LittleEndian, &bodyLength)
if err != nil {
return
}
packet = make([]byte, bodyLength)
@ -300,10 +304,287 @@ func ReadUDPPacket(conn *net.Conn) (srcAddr string, packet []byte, err error) {
return
}
if n != int(bodyLength) {
err = fmt.Errorf("n != int(bodyLength), %d,%d", n, bodyLength)
return
}
return
}
func Uniqueid() string {
return xid.New().String()
// var src = rand.NewSource(time.Now().UnixNano())
// s := fmt.Sprintf("%d", src.Int63())
// return s[len(s)-5:len(s)-1] + fmt.Sprintf("%d", uint64(time.Now().UnixNano()))[8:]
}
func RandString(strlen int) string {
codes := "QWERTYUIOPLKJHGFDSAZXCVBNMabcdefghijklmnopqrstuvwxyz0123456789"
codeLen := len(codes)
data := make([]byte, strlen)
rand.Seed(time.Now().UnixNano() + rand.Int63() + rand.Int63() + rand.Int63() + rand.Int63())
for i := 0; i < strlen; i++ {
idx := rand.Intn(codeLen)
data[i] = byte(codes[idx])
}
return string(data)
}
func RandInt(strLen int) int64 {
codes := "123456789"
codeLen := len(codes)
data := make([]byte, strLen)
rand.Seed(time.Now().UnixNano() + rand.Int63() + rand.Int63() + rand.Int63() + rand.Int63())
for i := 0; i < strLen; i++ {
idx := rand.Intn(codeLen)
data[i] = byte(codes[idx])
}
i, _ := strconv.ParseInt(string(data), 10, 64)
return i
}
func ReadData(r io.Reader) (data string, err error) {
var len uint16
err = binary.Read(r, binary.LittleEndian, &len)
if err != nil {
return
}
var n int
_data := make([]byte, len)
n, err = r.Read(_data)
if err != nil {
return
}
if n != int(len) {
err = fmt.Errorf("error data len")
return
}
data = string(_data)
return
}
func ReadPacketData(r io.Reader, data ...*string) (err error) {
for _, d := range data {
*d, err = ReadData(r)
if err != nil {
return
}
}
return
}
func ReadPacket(r io.Reader, typ *uint8, data ...*string) (err error) {
var connType uint8
err = binary.Read(r, binary.LittleEndian, &connType)
if err != nil {
return
}
*typ = connType
for _, d := range data {
*d, err = ReadData(r)
if err != nil {
return
}
}
return
}
func BuildPacket(typ uint8, data ...string) []byte {
pkg := new(bytes.Buffer)
binary.Write(pkg, binary.LittleEndian, typ)
for _, d := range data {
bytes := []byte(d)
binary.Write(pkg, binary.LittleEndian, uint16(len(bytes)))
binary.Write(pkg, binary.LittleEndian, bytes)
}
return pkg.Bytes()
}
func BuildPacketData(data ...string) []byte {
pkg := new(bytes.Buffer)
for _, d := range data {
bytes := []byte(d)
binary.Write(pkg, binary.LittleEndian, uint16(len(bytes)))
binary.Write(pkg, binary.LittleEndian, bytes)
}
return pkg.Bytes()
}
func SubStr(str string, start, end int) string {
if len(str) == 0 {
return ""
}
if end >= len(str) {
end = len(str) - 1
}
return str[start:end]
}
func SubBytes(bytes []byte, start, end int) []byte {
if len(bytes) == 0 {
return []byte{}
}
if end >= len(bytes) {
end = len(bytes) - 1
}
return bytes[start:end]
}
func TlsBytes(cert, key string) (certBytes, keyBytes []byte, err error) {
certBytes, err = ioutil.ReadFile(cert)
if err != nil {
err = fmt.Errorf("err : %s", err)
return
}
keyBytes, err = ioutil.ReadFile(key)
if err != nil {
err = fmt.Errorf("err : %s", err)
return
}
return
}
func GetKCPBlock(method, key string) (block kcp.BlockCrypt) {
pass := pbkdf2.Key([]byte(key), []byte(key), 4096, 32, sha1.New)
switch method {
case "sm4":
block, _ = kcp.NewSM4BlockCrypt(pass[:16])
case "tea":
block, _ = kcp.NewTEABlockCrypt(pass[:16])
case "xor":
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
case "none":
block, _ = kcp.NewNoneBlockCrypt(pass)
case "aes-128":
block, _ = kcp.NewAESBlockCrypt(pass[:16])
case "aes-192":
block, _ = kcp.NewAESBlockCrypt(pass[:24])
case "blowfish":
block, _ = kcp.NewBlowfishBlockCrypt(pass)
case "twofish":
block, _ = kcp.NewTwofishBlockCrypt(pass)
case "cast5":
block, _ = kcp.NewCast5BlockCrypt(pass[:16])
case "3des":
block, _ = kcp.NewTripleDESBlockCrypt(pass[:24])
case "xtea":
block, _ = kcp.NewXTEABlockCrypt(pass[:16])
case "salsa20":
block, _ = kcp.NewSalsa20BlockCrypt(pass)
default:
block, _ = kcp.NewAESBlockCrypt(pass)
}
return
}
func HttpGet(URL string, timeout int, host ...string) (body []byte, code int, err error) {
var tr *http.Transport
var client *http.Client
conf := &tls.Config{
InsecureSkipVerify: true,
}
if strings.Contains(URL, "https://") {
tr = &http.Transport{TLSClientConfig: conf}
client = &http.Client{Timeout: time.Millisecond * time.Duration(timeout), Transport: tr}
} else {
tr = &http.Transport{}
client = &http.Client{Timeout: time.Millisecond * time.Duration(timeout), Transport: tr}
}
defer tr.CloseIdleConnections()
//resp, err := client.Get(URL)
req, err := http.NewRequest("GET", URL, nil)
if err != nil {
return
}
if len(host) == 1 && host[0] != "" {
req.Host = host[0]
}
resp, err := client.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
code = resp.StatusCode
body, err = ioutil.ReadAll(resp.Body)
return
}
func IsIternalIP(domainOrIP string, always bool) bool {
var outIPs []net.IP
var err error
var isDomain bool
if net.ParseIP(domainOrIP) == nil {
isDomain = true
}
if always && isDomain {
return false
}
if isDomain {
outIPs, err = MyLookupIP(domainOrIP)
} else {
outIPs = []net.IP{net.ParseIP(domainOrIP)}
}
if err != nil {
return false
}
for _, ip := range outIPs {
if ip.IsLoopback() {
return true
}
if ip.To4().Mask(net.IPv4Mask(255, 0, 0, 0)).String() == "10.0.0.0" {
return true
}
if ip.To4().Mask(net.IPv4Mask(255, 0, 0, 0)).String() == "192.168.0.0" {
return true
}
if ip.To4().Mask(net.IPv4Mask(255, 0, 0, 0)).String() == "172.0.0.0" {
i, _ := strconv.Atoi(strings.Split(ip.To4().String(), ".")[1])
return i >= 16 && i <= 31
}
}
return false
}
func IsHTTP(head []byte) bool {
keys := []string{"GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"}
for _, key := range keys {
if bytes.HasPrefix(head, []byte(key)) || bytes.HasPrefix(head, []byte(strings.ToLower(key))) {
return true
}
}
return false
}
func IsSocks5(head []byte) bool {
if len(head) < 3 {
return false
}
if head[0] == uint8(0x05) && 0 < int(head[1]) && int(head[1]) < 255 {
if len(head) == 2+int(head[1]) {
return true
}
}
return false
}
func RemoveProxyHeaders(head []byte) []byte {
newLines := [][]byte{}
var keys = map[string]bool{}
lines := bytes.Split(head, []byte("\r\n"))
IsBody := false
i := -1
for _, line := range lines {
i++
if len(line) == 0 || IsBody {
newLines = append(newLines, line)
IsBody = true
} else {
hline := bytes.SplitN(line, []byte(":"), 2)
if i == 0 && IsHTTP(head) {
newLines = append(newLines, line)
continue
}
if len(hline) != 2 {
continue
}
k := strings.ToUpper(string(hline[0]))
if _, ok := keys[k]; ok || strings.HasPrefix(k, "PROXY-") {
continue
}
keys[k] = true
newLines = append(newLines, line)
}
}
return bytes.Join(newLines, []byte("\r\n"))
}
func InsertProxyHeaders(head []byte, headers string) []byte {
return bytes.Replace(head, []byte("\r\n"), []byte("\r\n"+headers), 1)
}
// type sockaddr struct {
// family uint16
@ -361,3 +642,25 @@ func ReadUDPPacket(conn *net.Conn) (srcAddr string, packet []byte, err error) {
// }
// return
// }
/*
net.LookupIP may cause deadlock in windows
https://github.com/golang/go/issues/24178
*/
func MyLookupIP(host string) ([]net.IP, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(3))
defer func() {
cancel()
//ctx.Done()
}()
addrs, err := net.DefaultResolver.LookupIPAddr(ctx, host)
if err != nil {
return nil, err
}
ips := make([]net.IP, len(addrs))
for i, ia := range addrs {
ips[i] = ia.IP
}
return ips, nil
}

264
utils/id/xid.go Normal file
View File

@ -0,0 +1,264 @@
// Package xid is a globally unique id generator suited for web scale
//
// Xid is using Mongo Object ID algorithm to generate globally unique ids:
// https://docs.mongodb.org/manual/reference/object-id/
//
// - 4-byte value representing the seconds since the Unix epoch,
// - 3-byte machine identifier,
// - 2-byte process id, and
// - 3-byte counter, starting with a random value.
//
// The binary representation of the id is compatible with Mongo 12 bytes Object IDs.
// The string representation is using base32 hex (w/o padding) for better space efficiency
// when stored in that form (20 bytes). The hex variant of base32 is used to retain the
// sortable property of the id.
//
// Xid doesn't use base64 because case sensitivity and the 2 non alphanum chars may be an
// issue when transported as a string between various systems. Base36 wasn't retained either
// because 1/ it's not standard 2/ the resulting size is not predictable (not bit aligned)
// and 3/ it would not remain sortable. To validate a base32 `xid`, expect a 20 chars long,
// all lowercase sequence of `a` to `v` letters and `0` to `9` numbers (`[0-9a-v]{20}`).
//
// UUID is 16 bytes (128 bits), snowflake is 8 bytes (64 bits), xid stands in between
// with 12 bytes with a more compact string representation ready for the web and no
// required configuration or central generation server.
//
// Features:
//
// - Size: 12 bytes (96 bits), smaller than UUID, larger than snowflake
// - Base32 hex encoded by default (16 bytes storage when transported as printable string)
// - Non configured, you don't need set a unique machine and/or data center id
// - K-ordered
// - Embedded time with 1 second precision
// - Unicity guaranted for 16,777,216 (24 bits) unique ids per second and per host/process
//
// Best used with xlog's RequestIDHandler (https://godoc.org/github.com/rs/xlog#RequestIDHandler).
//
// References:
//
// - http://www.slideshare.net/davegardnerisme/unique-id-generation-in-distributed-systems
// - https://en.wikipedia.org/wiki/Universally_unique_identifier
// - https://blog.twitter.com/2010/announcing-snowflake
package xid
import (
"crypto/md5"
"crypto/rand"
"database/sql/driver"
"encoding/binary"
"errors"
"fmt"
"os"
"sync/atomic"
"time"
)
// Code inspired from mgo/bson ObjectId
// ID represents a unique request id
type ID [rawLen]byte
const (
encodedLen = 20 // string encoded len
decodedLen = 15 // len after base32 decoding with the padded data
rawLen = 12 // binary raw len
// encoding stores a custom version of the base32 encoding with lower case
// letters.
encoding = "0123456789abcdefghijklmnopqrstuv"
)
// ErrInvalidID is returned when trying to unmarshal an invalid ID
var ErrInvalidID = errors.New("xid: invalid ID")
// objectIDCounter is atomically incremented when generating a new ObjectId
// using NewObjectId() function. It's used as a counter part of an id.
// This id is initialized with a random value.
var objectIDCounter = randInt()
// machineId stores machine id generated once and used in subsequent calls
// to NewObjectId function.
var machineID = readMachineID()
// pid stores the current process id
var pid = os.Getpid()
// dec is the decoding map for base32 encoding
var dec [256]byte
func init() {
for i := 0; i < len(dec); i++ {
dec[i] = 0xFF
}
for i := 0; i < len(encoding); i++ {
dec[encoding[i]] = byte(i)
}
}
// readMachineId generates machine id and puts it into the machineId global
// variable. If this function fails to get the hostname, it will cause
// a runtime error.
func readMachineID() []byte {
id := make([]byte, 3)
if hostname, err := os.Hostname(); err == nil {
hw := md5.New()
hw.Write([]byte(hostname))
copy(id, hw.Sum(nil))
} else {
// Fallback to rand number if machine id can't be gathered
if _, randErr := rand.Reader.Read(id); randErr != nil {
panic(fmt.Errorf("xid: cannot get hostname nor generate a random number: %v; %v", err, randErr))
}
}
return id
}
// randInt generates a random uint32
func randInt() uint32 {
b := make([]byte, 3)
if _, err := rand.Reader.Read(b); err != nil {
panic(fmt.Errorf("xid: cannot generate random number: %v;", err))
}
return uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])
}
// New generates a globaly unique ID
func New() ID {
var id ID
// Timestamp, 4 bytes, big endian
binary.BigEndian.PutUint32(id[:], uint32(time.Now().Unix()))
// Machine, first 3 bytes of md5(hostname)
id[4] = machineID[0]
id[5] = machineID[1]
id[6] = machineID[2]
// Pid, 2 bytes, specs don't specify endianness, but we use big endian.
id[7] = byte(pid >> 8)
id[8] = byte(pid)
// Increment, 3 bytes, big endian
i := atomic.AddUint32(&objectIDCounter, 1)
id[9] = byte(i >> 16)
id[10] = byte(i >> 8)
id[11] = byte(i)
return id
}
// FromString reads an ID from its string representation
func FromString(id string) (ID, error) {
i := &ID{}
err := i.UnmarshalText([]byte(id))
return *i, err
}
// String returns a base32 hex lowercased with no padding representation of the id (char set is 0-9, a-v).
func (id ID) String() string {
text := make([]byte, encodedLen)
encode(text, id[:])
return string(text)
}
// MarshalText implements encoding/text TextMarshaler interface
func (id ID) MarshalText() ([]byte, error) {
text := make([]byte, encodedLen)
encode(text, id[:])
return text, nil
}
// encode by unrolling the stdlib base32 algorithm + removing all safe checks
func encode(dst, id []byte) {
dst[0] = encoding[id[0]>>3]
dst[1] = encoding[(id[1]>>6)&0x1F|(id[0]<<2)&0x1F]
dst[2] = encoding[(id[1]>>1)&0x1F]
dst[3] = encoding[(id[2]>>4)&0x1F|(id[1]<<4)&0x1F]
dst[4] = encoding[id[3]>>7|(id[2]<<1)&0x1F]
dst[5] = encoding[(id[3]>>2)&0x1F]
dst[6] = encoding[id[4]>>5|(id[3]<<3)&0x1F]
dst[7] = encoding[id[4]&0x1F]
dst[8] = encoding[id[5]>>3]
dst[9] = encoding[(id[6]>>6)&0x1F|(id[5]<<2)&0x1F]
dst[10] = encoding[(id[6]>>1)&0x1F]
dst[11] = encoding[(id[7]>>4)&0x1F|(id[6]<<4)&0x1F]
dst[12] = encoding[id[8]>>7|(id[7]<<1)&0x1F]
dst[13] = encoding[(id[8]>>2)&0x1F]
dst[14] = encoding[(id[9]>>5)|(id[8]<<3)&0x1F]
dst[15] = encoding[id[9]&0x1F]
dst[16] = encoding[id[10]>>3]
dst[17] = encoding[(id[11]>>6)&0x1F|(id[10]<<2)&0x1F]
dst[18] = encoding[(id[11]>>1)&0x1F]
dst[19] = encoding[(id[11]<<4)&0x1F]
}
// UnmarshalText implements encoding/text TextUnmarshaler interface
func (id *ID) UnmarshalText(text []byte) error {
if len(text) != encodedLen {
return ErrInvalidID
}
for _, c := range text {
if dec[c] == 0xFF {
return ErrInvalidID
}
}
decode(id, text)
return nil
}
// decode by unrolling the stdlib base32 algorithm + removing all safe checks
func decode(id *ID, src []byte) {
id[0] = dec[src[0]]<<3 | dec[src[1]]>>2
id[1] = dec[src[1]]<<6 | dec[src[2]]<<1 | dec[src[3]]>>4
id[2] = dec[src[3]]<<4 | dec[src[4]]>>1
id[3] = dec[src[4]]<<7 | dec[src[5]]<<2 | dec[src[6]]>>3
id[4] = dec[src[6]]<<5 | dec[src[7]]
id[5] = dec[src[8]]<<3 | dec[src[9]]>>2
id[6] = dec[src[9]]<<6 | dec[src[10]]<<1 | dec[src[11]]>>4
id[7] = dec[src[11]]<<4 | dec[src[12]]>>1
id[8] = dec[src[12]]<<7 | dec[src[13]]<<2 | dec[src[14]]>>3
id[9] = dec[src[14]]<<5 | dec[src[15]]
id[10] = dec[src[16]]<<3 | dec[src[17]]>>2
id[11] = dec[src[17]]<<6 | dec[src[18]]<<1 | dec[src[19]]>>4
}
// Time returns the timestamp part of the id.
// It's a runtime error to call this method with an invalid id.
func (id ID) Time() time.Time {
// First 4 bytes of ObjectId is 32-bit big-endian seconds from epoch.
secs := int64(binary.BigEndian.Uint32(id[0:4]))
return time.Unix(secs, 0)
}
// Machine returns the 3-byte machine id part of the id.
// It's a runtime error to call this method with an invalid id.
func (id ID) Machine() []byte {
return id[4:7]
}
// Pid returns the process id part of the id.
// It's a runtime error to call this method with an invalid id.
func (id ID) Pid() uint16 {
return binary.BigEndian.Uint16(id[7:9])
}
// Counter returns the incrementing value part of the id.
// It's a runtime error to call this method with an invalid id.
func (id ID) Counter() int32 {
b := id[9:12]
// Counter is stored as big-endian 3-byte value
return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]))
}
// Value implements the driver.Valuer interface.
func (id ID) Value() (driver.Value, error) {
b, err := id.MarshalText()
return string(b), err
}
// Scan implements the sql.Scanner interface.
func (id *ID) Scan(value interface{}) (err error) {
switch val := value.(type) {
case string:
return id.UnmarshalText([]byte(val))
case []byte:
return id.UnmarshalText(val)
default:
return fmt.Errorf("xid: scanning unsupported type: %T", value)
}
}

100
utils/jumper/jumper.go Normal file
View File

@ -0,0 +1,100 @@
package jumper
import (
"bytes"
"encoding/base64"
"fmt"
"net"
"net/url"
"time"
"golang.org/x/net/proxy"
)
type Jumper struct {
proxyURL *url.URL
timeout time.Duration
}
type socks5Dialer struct {
timeout time.Duration
}
func (s socks5Dialer) Dial(network, addr string) (net.Conn, error) {
return net.DialTimeout(network, addr, s.timeout)
}
func New(proxyURL string, timeout time.Duration) (j Jumper, err error) {
u, err := url.Parse(proxyURL)
if err != nil {
return
}
j = Jumper{
proxyURL: u,
timeout: timeout,
}
return
}
func (j *Jumper) Dial(address string, timeout time.Duration) (conn net.Conn, err error) {
switch j.proxyURL.Scheme {
case "https":
return j.dialHTTPS(address, timeout)
case "socks5":
return j.dialSOCKS5(address, timeout)
default:
return nil, fmt.Errorf("unkown scheme of %s", j.proxyURL.String())
}
}
func (j *Jumper) dialHTTPS(address string, timeout time.Duration) (conn net.Conn, err error) {
conn, err = net.DialTimeout("tcp", j.proxyURL.Host, timeout)
if err != nil {
return
}
pb := new(bytes.Buffer)
pb.Write([]byte(fmt.Sprintf("CONNECT %s HTTP/1.1\r\n", address)))
pb.WriteString("Proxy-Connection: Keep-Alive\r\n")
if j.proxyURL.User != nil {
p, _ := j.proxyURL.User.Password()
u := fmt.Sprintf("%s:%s", j.proxyURL.User.Username(), p)
pb.Write([]byte(fmt.Sprintf("Proxy-Authorization: Basic %s\r\n", base64.StdEncoding.EncodeToString([]byte(u)))))
}
pb.Write([]byte("\r\n"))
_, err = conn.Write(pb.Bytes())
if err != nil {
conn.Close()
conn = nil
err = fmt.Errorf("error connecting to proxy: %s", err)
return
}
reply := make([]byte, 1024)
conn.SetDeadline(time.Now().Add(timeout))
n, err := conn.Read(reply)
conn.SetDeadline(time.Time{})
if err != nil {
err = fmt.Errorf("error read reply from proxy: %s", err)
conn.Close()
conn = nil
return
}
if bytes.Index(reply[:n], []byte("200")) == -1 {
err = fmt.Errorf("error greeting to proxy, response: %s", string(reply[:n]))
conn.Close()
conn = nil
return
}
return
}
func (j *Jumper) dialSOCKS5(address string, timeout time.Duration) (conn net.Conn, err error) {
auth := &proxy.Auth{}
if j.proxyURL.User != nil {
auth.User = j.proxyURL.User.Username()
auth.Password, _ = j.proxyURL.User.Password()
} else {
auth = nil
}
dialSocksProxy, err := proxy.SOCKS5("tcp", j.proxyURL.Host, auth, socks5Dialer{timeout: timeout})
if err != nil {
err = fmt.Errorf("error connecting to proxy: %s", err)
return
}
return dialSocksProxy.Dial("tcp", address)
}

45
utils/leakybuf.go Normal file
View File

@ -0,0 +1,45 @@
// Provides leaky buffer, based on the example in Effective Go.
package utils
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)
// 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
}

View File

@ -1,145 +0,0 @@
package utils
import (
"log"
"sync"
"time"
)
//ConnPool to use
type ConnPool interface {
Get() (conn interface{}, err error)
Put(conn interface{})
ReleaseAll()
Len() (length int)
}
type poolConfig struct {
Factory func() (interface{}, error)
IsActive func(interface{}) bool
Release func(interface{})
InitialCap int
MaxCap int
}
func NewConnPool(poolConfig poolConfig) (pool ConnPool, err error) {
p := netPool{
config: poolConfig,
conns: make(chan interface{}, poolConfig.MaxCap),
lock: &sync.Mutex{},
}
//log.Printf("pool MaxCap:%d", poolConfig.MaxCap)
if poolConfig.MaxCap > 0 {
err = p.initAutoFill(false)
if err == nil {
p.initAutoFill(true)
}
}
return &p, nil
}
type netPool struct {
conns chan interface{}
lock *sync.Mutex
config poolConfig
}
func (p *netPool) initAutoFill(async bool) (err error) {
var worker = func() (err error) {
for {
//log.Printf("pool fill: %v , len: %d", p.Len() <= p.config.InitialCap/2, p.Len())
if p.Len() <= p.config.InitialCap/2 {
p.lock.Lock()
errN := 0
for i := 0; i < p.config.InitialCap; i++ {
c, err := p.config.Factory()
if err != nil {
errN++
if async {
continue
} else {
p.lock.Unlock()
return err
}
}
select {
case p.conns <- c:
default:
p.config.Release(c)
break
}
if p.Len() >= p.config.InitialCap {
break
}
}
if errN > 0 {
log.Printf("fill conn pool fail , ERRN:%d", errN)
}
p.lock.Unlock()
}
if !async {
return
}
time.Sleep(time.Second * 2)
}
}
if async {
go worker()
} else {
err = worker()
}
return
}
func (p *netPool) Get() (conn interface{}, err error) {
// defer func() {
// log.Printf("pool len : %d", p.Len())
// }()
p.lock.Lock()
defer p.lock.Unlock()
// for {
select {
case conn = <-p.conns:
if p.config.IsActive(conn) {
return
}
p.config.Release(conn)
default:
conn, err = p.config.Factory()
if err != nil {
return nil, err
}
return conn, nil
}
// }
return
}
func (p *netPool) Put(conn interface{}) {
if conn == nil {
return
}
p.lock.Lock()
defer p.lock.Unlock()
if !p.config.IsActive(conn) {
p.config.Release(conn)
}
select {
case p.conns <- conn:
default:
p.config.Release(conn)
}
}
func (p *netPool) ReleaseAll() {
p.lock.Lock()
defer p.lock.Unlock()
close(p.conns)
for c := range p.conns {
p.config.Release(c)
}
p.conns = make(chan interface{}, p.config.InitialCap)
}
func (p *netPool) Len() (length int) {
return len(p.conns)
}

View File

@ -1,10 +1,18 @@
package utils
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"log"
logger "log"
"net"
"runtime/debug"
"strconv"
"github.com/snail007/goproxy/services/kcpcfg"
kcp "github.com/xtaci/kcp-go"
)
type ServerChannel struct {
@ -13,27 +21,41 @@ type ServerChannel struct {
Listener *net.Listener
UDPListener *net.UDPConn
errAcceptHandler func(err error)
log *logger.Logger
}
func NewServerChannel(ip string, port int) ServerChannel {
func NewServerChannel(ip string, port int, log *logger.Logger) ServerChannel {
return ServerChannel{
ip: ip,
port: port,
log: log,
errAcceptHandler: func(err error) {
fmt.Printf("accept error , ERR:%s", err)
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 (sc *ServerChannel) SetErrAcceptHandler(fn func(err error)) {
sc.errAcceptHandler = fn
}
func (sc *ServerChannel) ListenTls(certBytes, keyBytes []byte, fn func(conn net.Conn)) (err error) {
sc.Listener, err = ListenTls(sc.ip, sc.port, certBytes, keyBytes)
func (sc *ServerChannel) ListenTls(certBytes, keyBytes, caCertBytes []byte, fn func(conn net.Conn)) (err error) {
sc.Listener, err = sc.listenTls(sc.ip, sc.port, certBytes, keyBytes, caCertBytes)
if err == nil {
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("ListenTls crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
sc.log.Printf("ListenTls crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
@ -43,7 +65,7 @@ func (sc *ServerChannel) ListenTls(certBytes, keyBytes []byte, fn func(conn net.
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
sc.log.Printf("tls connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(conn)
@ -58,7 +80,33 @@ func (sc *ServerChannel) ListenTls(certBytes, keyBytes []byte, fn func(conn net.
}
return
}
func (sc *ServerChannel) listenTls(ip string, port int, certBytes, keyBytes, caCertBytes []byte) (ln *net.Listener, err error) {
var cert tls.Certificate
cert, err = tls.X509KeyPair(certBytes, keyBytes)
if err != nil {
return
}
clientCertPool := x509.NewCertPool()
caBytes := certBytes
if caCertBytes != nil {
caBytes = caCertBytes
}
ok := clientCertPool.AppendCertsFromPEM(caBytes)
if !ok {
err = errors.New("failed to parse root certificate")
}
config := &tls.Config{
ClientCAs: clientCertPool,
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
}
_ln, err := tls.Listen("tcp", fmt.Sprintf("%s:%d", ip, port), config)
if err == nil {
ln = &_ln
}
return
}
func (sc *ServerChannel) ListenTCP(fn func(conn net.Conn)) (err error) {
var l net.Listener
l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sc.ip, sc.port))
@ -67,7 +115,7 @@ func (sc *ServerChannel) ListenTCP(fn func(conn net.Conn)) (err error) {
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("ListenTCP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
sc.log.Printf("ListenTCP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
@ -77,7 +125,7 @@ func (sc *ServerChannel) ListenTCP(fn func(conn net.Conn)) (err error) {
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
sc.log.Printf("tcp connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(conn)
@ -99,7 +147,7 @@ func (sc *ServerChannel) ListenUDP(fn func(packet []byte, localAddr, srcAddr *ne
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("ListenUDP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
sc.log.Printf("ListenUDP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
@ -110,7 +158,7 @@ func (sc *ServerChannel) ListenUDP(fn func(packet []byte, localAddr, srcAddr *ne
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("udp data handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
sc.log.Printf("udp data handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(packet, addr, srcAddr)
@ -124,3 +172,58 @@ func (sc *ServerChannel) ListenUDP(fn func(packet []byte, localAddr, srcAddr *ne
}
return
}
func (sc *ServerChannel) ListenKCP(config kcpcfg.KCPConfigArgs, fn func(conn net.Conn), log *logger.Logger) (err error) {
lis, err := kcp.ListenWithOptions(fmt.Sprintf("%s:%d", sc.ip, sc.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
}
sc.Listener = new(net.Listener)
*sc.Listener = lis
go func() {
defer func() {
if e := recover(); e != nil {
sc.log.Printf("ListenKCP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
//var conn net.Conn
conn, err := lis.AcceptKCP()
if err == nil {
go func() {
defer func() {
if e := recover(); e != nil {
sc.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 := NewCompStream(conn)
fn(cconn)
}
}()
} else {
sc.errAcceptHandler(err)
break
}
}
}()
}
return
}

173
utils/sni/sni.go Normal file
View File

@ -0,0 +1,173 @@
package sni
import (
"bufio"
"bytes"
"errors"
"io"
"net"
)
func ServerNameFromBytes(data []byte) (sn string, err error) {
reader := bytes.NewReader(data)
bufferedReader := bufio.NewReader(reader)
c := bufferedConn{bufferedReader, nil, nil}
sn, _, err = ServerNameFromConn(c)
return
}
type bufferedConn struct {
r *bufio.Reader
rout io.Reader
net.Conn
}
func newBufferedConn(c net.Conn) bufferedConn {
return bufferedConn{bufio.NewReader(c), nil, c}
}
func (b bufferedConn) Peek(n int) ([]byte, error) {
return b.r.Peek(n)
}
func (b bufferedConn) Read(p []byte) (int, error) {
if b.rout != nil {
return b.rout.Read(p)
}
return b.r.Read(p)
}
var malformedError = errors.New("malformed client hello")
func getHello(b []byte) (string, error) {
rest := b[5:]
if len(rest) == 0 {
return "", malformedError
}
current := 0
handshakeType := rest[0]
current += 1
if handshakeType != 0x1 {
return "", errors.New("Not a ClientHello")
}
// Skip over another length
current += 3
// Skip over protocolversion
current += 2
// Skip over random number
current += 4 + 28
if current > len(rest) {
return "", malformedError
}
// Skip over session ID
sessionIDLength := int(rest[current])
current += 1
current += sessionIDLength
if current+1 > len(rest) {
return "", malformedError
}
cipherSuiteLength := (int(rest[current]) << 8) + int(rest[current+1])
current += 2
current += cipherSuiteLength
if current > len(rest) {
return "", malformedError
}
compressionMethodLength := int(rest[current])
current += 1
current += compressionMethodLength
if current > len(rest) {
return "", errors.New("no extensions")
}
current += 2
hostname := ""
for current+4 < len(rest) && hostname == "" {
extensionType := (int(rest[current]) << 8) + int(rest[current+1])
current += 2
extensionDataLength := (int(rest[current]) << 8) + int(rest[current+1])
current += 2
if extensionType == 0 {
// Skip over number of names as we're assuming there's just one
current += 2
if current > len(rest) {
return "", malformedError
}
nameType := rest[current]
current += 1
if nameType != 0 {
return "", errors.New("Not a hostname")
}
if current+1 > len(rest) {
return "", malformedError
}
nameLen := (int(rest[current]) << 8) + int(rest[current+1])
current += 2
if current+nameLen > len(rest) {
return "", malformedError
}
hostname = string(rest[current : current+nameLen])
}
current += extensionDataLength
}
if hostname == "" {
return "", errors.New("No hostname")
}
return hostname, nil
}
func getHelloBytes(c bufferedConn) ([]byte, error) {
b, err := c.Peek(5)
if err != nil {
return []byte{}, err
}
if b[0] != 0x16 {
return []byte{}, errors.New("not TLS")
}
restLengthBytes := b[3:]
restLength := (int(restLengthBytes[0]) << 8) + int(restLengthBytes[1])
return c.Peek(5 + restLength)
}
func getServername(c bufferedConn) (string, []byte, error) {
all, err := getHelloBytes(c)
if err != nil {
return "", nil, err
}
name, err := getHello(all)
if err != nil {
return "", nil, err
}
return name, all, err
}
// Uses SNI to get the name of the server from the connection. Returns the ServerName and a buffered connection that will not have been read off of.
func ServerNameFromConn(c net.Conn) (string, net.Conn, error) {
bufconn := newBufferedConn(c)
sn, helloBytes, err := getServername(bufconn)
if err != nil {
return "", nil, err
}
bufconn.rout = io.MultiReader(bytes.NewBuffer(helloBytes), c)
return sn, bufconn, nil
}

253
utils/socks/client.go Normal file
View File

@ -0,0 +1,253 @@
package socks
import (
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"strconv"
"time"
)
var socks5Errors = []string{
"",
"general failure",
"connection forbidden",
"network unreachable",
"host unreachable",
"connection refused",
"TTL expired",
"command not supported",
"address type not supported",
}
type Auth struct {
User, Password string
}
type ClientConn struct {
user string
password string
conn *net.Conn
header []byte
timeout time.Duration
addr string
network string
UDPAddr string
}
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
// with an optional username and password. See RFC 1928 and RFC 1929.
// target must be a canonical address with a host and port.
// network : tcp udp
func NewClientConn(conn *net.Conn, network, target string, timeout time.Duration, auth *Auth, header []byte) *ClientConn {
s := &ClientConn{
conn: conn,
network: network,
timeout: timeout,
}
if auth != nil {
s.user = auth.User
s.password = auth.Password
}
if header != nil && len(header) > 0 {
s.header = header
}
if network == "udp" && target == "" {
target = "0.0.0.0:1"
}
s.addr = target
return s
}
// connect takes an existing connection to a socks5 proxy server,
// and commands the server to extend that connection to target,
// which must be a canonical address with a host and port.
func (s *ClientConn) Handshake() error {
host, portStr, err := net.SplitHostPort(s.addr)
if err != nil {
return err
}
port, err := strconv.Atoi(portStr)
if err != nil {
return errors.New("proxy: failed to parse port number: " + portStr)
}
if s.network == "tcp" && (port < 1 || port > 0xffff) {
return errors.New("proxy: port number out of range: " + portStr)
}
if err := s.handshake(host); err != nil {
return err
}
buf := []byte{}
if s.network == "tcp" {
buf = append(buf, VERSION_V5, CMD_CONNECT, 0 /* reserved */)
} else {
buf = append(buf, VERSION_V5, CMD_ASSOCIATE, 0 /* reserved */)
}
if ip := net.ParseIP(host); ip != nil {
if ip4 := ip.To4(); ip4 != nil {
buf = append(buf, ATYP_IPV4)
ip = ip4
} else {
buf = append(buf, ATYP_IPV6)
}
buf = append(buf, ip...)
} else {
if len(host) > 255 {
return errors.New("proxy: destination host name too long: " + host)
}
buf = append(buf, ATYP_DOMAIN)
buf = append(buf, byte(len(host)))
buf = append(buf, host...)
}
buf = append(buf, byte(port>>8), byte(port))
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
if _, err := (*s.conn).Write(buf); err != nil {
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
(*s.conn).SetDeadline(time.Time{})
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
if _, err := io.ReadFull((*s.conn), buf[:4]); err != nil {
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
(*s.conn).SetDeadline(time.Time{})
failure := "unknown error"
if int(buf[1]) < len(socks5Errors) {
failure = socks5Errors[buf[1]]
}
if len(failure) > 0 {
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
}
bytesToDiscard := 0
switch buf[3] {
case ATYP_IPV4:
bytesToDiscard = net.IPv4len
case ATYP_IPV6:
bytesToDiscard = net.IPv6len
case ATYP_DOMAIN:
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
_, err := io.ReadFull((*s.conn), buf[:1])
(*s.conn).SetDeadline(time.Time{})
if err != nil {
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
bytesToDiscard = int(buf[0])
default:
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
}
if cap(buf) < bytesToDiscard {
buf = make([]byte, bytesToDiscard)
} else {
buf = buf[:bytesToDiscard]
}
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
if _, err := io.ReadFull((*s.conn), buf); err != nil {
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
(*s.conn).SetDeadline(time.Time{})
var ip net.IP
ip = buf
ipStr := ""
if bytesToDiscard == net.IPv4len || bytesToDiscard == net.IPv6len {
if ipv4 := ip.To4(); ipv4 != nil {
ipStr = ipv4.String()
} else {
ipStr = ip.To16().String()
}
}
//log.Printf("%v", ipStr)
// Also need to discard the port number
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil {
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
p := binary.BigEndian.Uint16([]byte{buf[0], buf[1]})
//log.Printf("%v", p)
s.UDPAddr = net.JoinHostPort(ipStr, fmt.Sprintf("%d", p))
//log.Printf("%v", s.udpAddr)
(*s.conn).SetDeadline(time.Time{})
return nil
}
func (s *ClientConn) SendUDP(data []byte, addr string) (respData []byte, err error) {
c, err := net.DialTimeout("udp", s.UDPAddr, s.timeout)
if err != nil {
return
}
conn := c.(*net.UDPConn)
p := NewPacketUDP()
p.Build(addr, data)
conn.SetDeadline(time.Now().Add(s.timeout))
conn.Write(p.Bytes())
conn.SetDeadline(time.Time{})
buf := make([]byte, 1024)
conn.SetDeadline(time.Now().Add(s.timeout))
n, _, err := conn.ReadFrom(buf)
conn.SetDeadline(time.Time{})
if err != nil {
return
}
respData = buf[:n]
return
}
func (s *ClientConn) handshake(host string) error {
// the size here is just an estimate
buf := make([]byte, 0, 6+len(host))
buf = append(buf, VERSION_V5)
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
buf = append(buf, 2 /* num auth methods */, Method_NO_AUTH, Method_USER_PASS)
} else {
buf = append(buf, 1 /* num auth methods */, Method_NO_AUTH)
}
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
if _, err := (*s.conn).Write(buf); err != nil {
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
(*s.conn).SetDeadline(time.Time{})
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil {
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
(*s.conn).SetDeadline(time.Time{})
if buf[0] != 5 {
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
}
if buf[1] == 0xff {
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
}
// See RFC 1929
if buf[1] == Method_USER_PASS {
buf = buf[:0]
buf = append(buf, 1 /* password protocol version */)
buf = append(buf, uint8(len(s.user)))
buf = append(buf, s.user...)
buf = append(buf, uint8(len(s.password)))
buf = append(buf, s.password...)
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
if _, err := (*s.conn).Write(buf); err != nil {
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
(*s.conn).SetDeadline(time.Time{})
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil {
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
(*s.conn).SetDeadline(time.Time{})
if buf[1] != 0 {
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
}
}
return nil
}

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