115 Commits
v3.0 ... v3.7

Author SHA1 Message Date
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
29 changed files with 2924 additions and 579 deletions

View File

@ -1,8 +1,51 @@
proxy更新日志:
proxy更新日志
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,11 +64,11 @@ v2.1
v2.0
1.增加了连接池功能,大幅提高了通过上级代理访问的速度。
2.HTTP代理模式,优化了请求URL的获取逻辑,可以支持:http,https,websocket
2.HTTP代理模式,优化了请求URL的获取逻辑,可以支持:http,https,websocke
3.增加了TCP代理模式,支持是否加密通讯。
4.优化了链接关闭逻辑,避免出现大量CLOSE_WAIT。
5.增加了黑白名单机制,更自由快速的访问。
6.优化了网站Block机制检测,判断更准确。
v1.0
v1.0
1.始发版本,可以代理http,https。

390
README.md
View File

@ -1,5 +1,5 @@
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/logo.jpg?raw=true" width="200"/>
Proxy是golang实现的高性能http,https,websocket,tcp,udp代理服务器,支持正向代理和反响代理(即:内网穿透).
Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务器,支持正向代理、内网穿透、SSH中转。
---
@ -8,30 +8,108 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp代理服务器,支
### Features
- 链式代理,程序本身可以作为一级代理,如果设置了上级代理那么可以作为二级代理,乃至N级代理.
- 通讯加密,如果程序不是一级代理,而且上级代理也是本程序,那么可以加密和上级代理之间的通讯,采用底层tls高强度加密,安全无特征.
- 智能HTTP代理,会自动判断访问的网站是否屏蔽,如果被屏蔽那么就会使用上级代理(前提是配置了上级代理)访问网站;如果访问的网站没有被屏蔽,为了加速访问,代理会直接访问网站,不使用上级代理.
- 智能HTTP,SOCKS5代理,会自动判断访问的网站是否屏蔽,如果被屏蔽那么就会使用上级代理(前提是配置了上级代理)访问网站;如果访问的网站没有被屏蔽,为了加速访问,代理会直接访问网站,不使用上级代理.
- 域名黑白名单,更加自由的控制网站的访问方式。
- 跨平台性,无论你是widows,linux,还是mac,甚至是树莓派,都可以很好的运行proxy.
- 多协议支持,支持HTTP,TCP,UDP,Websocket代理.
- 支持反向代理也就是"内网穿透",协议支持TCP和UDP.
- 多协议支持,支持HTTP(S),TCP,UDP,Websocket,SOCKS5代理.
- 支持内网穿透,协议支持TCP和UDP.
- SSH中转,HTTP(S),SOCKS5代理支持SSH中转,上级Linux服务器不需要任何服务端,本地一个proxy即可开心上网.
- 支持[KCP](https://github.com/xtaci/kcp-go)协议,HTTP(S),SOCKS5代理支持KCP协议传输数据,降低延迟,提升浏览体验.
### Why need these?
- 当由于安全因素或者限制,我们不能顺畅的访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道,顺畅的访问我们的服务.
- 当由于某某原因,我们不能访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道访问我们的服务.  
- 微信接口本地开发,方便调试.
- 远程访问内网机器.
- 和小伙伴一起玩局域网游戏.
- 以前只能在局域网玩的,现在可以在任何地方玩.
- ...
- 替代圣剑内网通显IP内网通花生壳之类的工具.
- ...  
本页是v3.6-v3.7手册,其他版本手册请点击下面链接查看.
- [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)
### 怎么找到组织?
[点击加入交流组织](https://gitter.im/go-proxy/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link)
### 安装
1. [快速安装](#自动安装)
1. [手动安装](#手动安装)
### 首次使用必看
- [环境](#首次使用必看-1)
- [使用配置文件](#使用配置文件)
- [后台运行](#后台运行)
- [生成通讯证书文件](#生成加密通讯需要的证书文件)
- [安全建议](#安全建议)
### 手册目录
- [1. HTTP代理](#1http代理)
- [1.1 普通HTTP代理](#11普通http代理)
- [1.2 普通二级HTTP代理](#12普通二级http代理)
- [1.3 HTTP二级代理(加密)](#13http二级代理加密)
- [1.4 HTTP三级代理(加密)](#14http三级代理加密)
- [1.5 Basic认证](#15basic认证)
- [1.6 强制走上级HTTP代理](#16http代理流量强制走上级http代理)
- [1.7 通过SSH中转](#17https通过ssh中转)
- [1.7.1 用户名和密码的方式](#171-ssh用户名和密码的方式)
- [1.7.2 用户名和密钥的方式](#172-ssh用户名和密钥的方式)
- [1.8 KCP协议传输](#18kcp协议传输)
- [1.9 查看帮助](#19查看帮助)
- [2. TCP代理](#2tcp代理)
- [2.1 普通一级TCP代理](#21普通一级tcp代理)
- [2.2 普通二级TCP代理](#22普通二级tcp代理)
- [2.3 普通三级TCP代理](#23普通三级tcp代理)
- [2.4 加密二级TCP代理](#24加密二级tcp代理)
- [2.5 加密三级TCP代理](#25加密三级tcp代理)
- [2.6 查看帮助](#26查看帮助)
- [3. UDP代理](#3udp代理)
- [3.1 普通一级TCP代理](#31普通一级udp代理)
- [3.2 普通二级TCP代理](#32普通二级udp代理)
- [3.3 普通三级TCP代理](#33普通三级udp代理)
- [3.4 加密二级TCP代理](#34加密二级udp代理)
- [3.5 加密三级TCP代理](#35加密三级udp代理)
- [3.6 查看帮助](#36查看帮助)
- [4. 内网穿透](#4内网穿透)
- [4.1 原理说明](#41原理说明)
- [4.2 TCP普通用法](#42tcp普通用法)
- [4.3 微信接口本地开发](#43微信接口本地开发)
- [4.4 UDP普通用法](#44udp普通用法)
- [4.5 高级用法一](#45高级用法一)
- [4.6 高级用法一](#46高级用法二)
- [4.7 tserver的-r参数](#47tserver的-r参数)
- [4.8 查看帮助](#48查看帮助)
- [5. SOCKS5代理](#5socks5代理)
- [5.1 普通SOCKS5代理](#51普通socks5代理)
- [5.2 普通二级SOCKS5代理](#52普通二级socks5代理)
- [5.3 SOCKS二级代理(加密)](#53socks二级代理加密)
- [5.4 SOCKS三级代理(加密)](#54socks三级代理加密)
- [5.5 流量强制走上级SOCKS代理](#55socks代理流量强制走上级socks代理)
- [5.6 通过SSH中转](#56socks通过ssh中转)
- [5.6.1 用户名和密码的方式](#561-ssh用户名和密码的方式)
- [5.6.2 用户名和密钥的方式](#562-ssh用户名和密钥的方式)
- [5.7 认证](#57认证)
- [5.8 KCP协议传输](#58kcp协议传输)
- [5.9 查看帮助](#59查看帮助)
### Fast Start
提示:所有操作需要root权限.
**0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.**
#### 自动安装
#### **0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.**
```shell
curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.sh | bash
```
安装完成,配置目录是/etc/proxy,更详细的使用方法参考下面的进一步了解.
如果安装失败或者你的vps不是linux64位系统,请按照下面的半自动步骤安装:
**1.登录你的VPS,下载守护进程monexec,选择合适你的版本,vps一般选择"linux_amd64.tar.gz"的即可.**
#### 手动安装
#### **1.登录你的VPS,下载守护进程monexec,选择合适你的版本,vps一般选择"linux_amd64.tar.gz"的即可.**
下载地址:https://github.com/reddec/monexec/releases
比如下载到/root/proxy/
执行:
@ -40,13 +118,13 @@ mkdir /root/proxy/
cd /root/proxy/
wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
```
**2.下载proxy**
#### **2.下载proxy**
下载地址:https://github.com/snail007/goproxy/releases
```shell
cd /root/proxy/
wget https://github.com/snail007/goproxy/releases/download/v2.0/proxy-linux-amd64.tar.gz
wget https://github.com/snail007/goproxy/releases/download/v3.7/proxy-linux-amd64.tar.gz
```
**3.下载自动安装脚本**
#### **3.下载自动安装脚本**
```shell
cd /root/proxy/
wget https://raw.githubusercontent.com/snail007/goproxy/master/install.sh
@ -54,34 +132,56 @@ chmod +x install.sh
./install.sh
```
## 使用教程
**提示**
本教程是v3版本,不兼容v2版本,v2版本教程请移驾:[v2教程](https://github.com/snail007/goproxy/tree/v2.2)
## 首次使用必看
#### **环境**
接下来的教程,默认系统是linux,程序是proxy所有操作需要root权限
如果你的是windows,请使用windows版本的proxy.exe即可.
### 0.生成加密通讯需要的证书文件
### **使用配置文件**
接下来的教程都是通过命令行参数介绍使用方法,也可以通过读取配置文件获取参数.
具体格式是通过@符号指定配置文件,例如:./proxy @configfile.txt
configfile.txt里面的格式是,第一行是子命令名称,第二行开始一行一个:参数的长格式=参数值,前后不能有空格和双引号.
参数的长格式都是--开头的,短格式参数都是-开头,如果你不知道某个短格式参数对应长格式参数,查看帮助命令即可.
比如configfile.txt内容如下:
```shell
http
--local-type=tcp
--local=:33080
```
### 生成加密通讯需要的证书文件
http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,当然可以选择不加密通信通讯,本教程所有和上级通讯都采用加密,需要证书文件.
在linux上并安装了openssl命令可以直接通过下面的命令生成证书和key文件.
`./proxy keygen`
默认会在当前程序目录下面生成证书文件proxy.crt和key文件proxy.key。
### 后台运行
默认执行proxy之后,如果要保持proxy运行,不能关闭命令行.
如果想在后台运行proxy,命令行可以关闭,只需要在命令最后加上--daemon参数即可.
比如:
`./proxy http -t tcp -p "0.0.0.0:38080" --daemon`
更推荐用monexec守护运行proxy比较好.
### 安全建议
当VPS在nat设备后面,vps上网卡IP都是内网IP,这个时候可以通过-g参数添加vps的外网ip防止死循环.
假设你的vps外网ip是23.23.23.23,下面命令通过-g参数设置23.23.23.23
`./proxy http -g "23.23.23.23"`
### 1.HTTP代理
**1.1.普通HTTP代理**
#### **1.1.普通HTTP代理**
`./proxy http -t tcp -p "0.0.0.0:38080"`
**1.2.普通二级HTTP代理**
#### **1.2.普通二级HTTP代理**
使用本地端口8090,假设上级HTTP代理是`22.22.22.22:8080`
`./proxy http -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" `
默认开启了连接池,如果为了网络情况很好,-L可以关闭连接池,0就是连接池大小,0为关闭.
`./proxy http -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -L 0`
默认关闭了连接池,如果要加快访问速度,-L可以开启连接池,10就是连接池大小,0为关闭,
开启连接池在网络不好的情况下,稳定不是很好.
`./proxy http -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -L 10`
我们还可以指定网站域名的黑白名单文件,一行一个域名,怕匹配规则是最右批评匹配,比如:baidu.com,匹配的是*.*.baidu.com,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理.
`./proxy http -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt`
**1.3.HTTP二级代理(加密)**
#### **1.3.HTTP二级代理(加密)**
一级HTTP代理(VPS,IP:22.22.22.22)
`./proxy http -t tls -p ":38080" -C proxy.crt -K proxy.key`
@ -93,7 +193,7 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,
`./proxy.exe http -t tcp -p ":8080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
然后设置你的windos系统中需要通过代理上网的程序的代理为http模式地址为127.0.0.1端口为8080,程序即可通过加密通道通过vps上网。
**1.4.HTTP三级代理(加密)**
#### **1.4.HTTP三级代理(加密)**
一级HTTP代理VPS_01,IP:22.22.22.22
`./proxy http -t tls -p ":38080" -C proxy.crt -K proxy.key`
二级HTTP代理VPS_02,IP:33.33.33.33
@ -102,37 +202,73 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,
`./proxy http -t tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地的8080端口就是访问一级HTTP代理上面的代理端口38080.
**1.5.Basic认证**
#### **1.5.Basic认证**
对于代理HTTP协议我们可以basic进行Basic认证,认证的用户名和密码可以在命令行指定
`./proxy http -t tcp -p ":33080" -a "user1:pass1" -a "user2:pass2"`
多个用户,重复-a参数即可.
也可以放在文件中,格式是一行一个"用户名:密码",然后用-F指定.
`./proxy http -t tcp -p ":33080" -F auth-file.txt`
如果没有-a或-F参数,就是关闭Basic认证.
`./proxy http -t tcp -p ":33080" -F auth-file.txt`
**1.6.HTTP代理流量强制走上级HTTP代理**
另外,http(s)代理还集成了外部HTTP API认证,我们可以通过--auth-url参数指定一个http url接口地址,
然后有用户连接的时候,proxy会GET方式请求这url,带上下面四个参数,如果返回HTTP状态码204,代表认证成功
其它情况认为认证失败.
比如:
`./proxy http -t tcp -p ":33080" --auth-url "http://test.com/auth.php"`
用户连接的时候,proxy会GET方式请求这url("http://test.com/auth.php"),
带上user,pass,ip,target四个参数:
http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}&target={TARGET}
user:用户名
pass:密码
ip:用户的IP,比如:192.168.1.200
target:用户访问的URL,比如:http://demo.com:80/1.html或https://www.baidu.com:80
如果没有-a或-F或--auth-url参数,就是关闭Basic认证.
#### **1.6.HTTP代理流量强制走上级HTTP代理**
默认情况下,proxy会智能判断一个网站域名是否无法访问,如果无法访问才走上级HTTP代理.通过--always可以使全部HTTP代理流量强制走上级HTTP代理.
`./proxy http --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
**1.7.查看帮助**
`./proxy help http`
#### **1.7.HTTP(S)通过SSH中转**
说明:ssh中转的原理是利用了ssh的转发功能,就是你连接上ssh之后,可以通过ssh代理访问目标地址.
假设有:vps
- IP是2.2.2.2, ssh端口是22, ssh用户名是:user, ssh用户密码是:demo
- 用户user的ssh私钥名称是user.key
##### ***1.7.1 ssh用户名和密码的方式***
本地HTTP(S)代理28080端口,执行:
`./proxy http -T ssh -P "2.2.2.2:22" -u user -A demo -t tcp -p ":28080"`
##### ***1.7.2 ssh用户名和密钥的方式***
本地HTTP(S)代理28080端口,执行:
`./proxy http -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"`
#### **1.8.KCP协议传输**
KCP协议需要-B参数设置一个密码用于加密解密数据
一级HTTP代理(VPS,IP:22.22.22.22)
`./proxy http -t kcp -p ":38080" -B mypassword`
二级HTTP代理(本地Linux)
`./proxy http -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" -B mypassword`
那么访问本地的8080端口就是访问VPS上面的代理端口38080,数据通过kcp协议传输.
#### **1.9.查看帮助**
`./proxy help http`
### 2.TCP代理
**2.1.普通一级TCP代理**
#### **2.1.普通一级TCP代理**
本地执行:
`./proxy tcp -p ":33080" -T tcp -P "192.168.22.33:22" -L 0`
那么访问本地33080端口就是访问192.168.22.33的22端口.
**2.2.普通二级TCP代理**
#### **2.2.普通二级TCP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp -p ":33080" -T tcp -P "127.0.0.1:8080" -L 0`
本地执行:
`./proxy tcp -p ":23080" -T tcp -P "22.22.22.33:33080"`
那么访问本地23080端口就是访问22.22.22.33的8080端口.
**2.3.普通三级TCP代理**
#### **2.3.普通三级TCP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp -p ":38080" -T tcp -P "66.66.66.66:8080" -L 0`
二级TCP代理VPS_02,IP:33.33.33.33
@ -141,14 +277,14 @@ VPS(IP:22.22.22.33)执行:
`./proxy tcp -p ":8080" -T tcp -P "33.33.33.33:28080"`
那么访问本地8080端口就是通过加密TCP隧道访问66.66.66.66的8080端口.
**2.4.加密二级TCP代理**
#### **2.4.加密二级TCP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp --tls -p ":33080" -T tcp -P "127.0.0.1:8080" -L 0 -C proxy.crt -K proxy.key`
本地执行:
`./proxy tcp -p ":23080" -T tls -P "22.22.22.33:33080" -C proxy.crt -K proxy.key`
那么访问本地23080端口就是通过加密TCP隧道访问22.22.22.33的8080端口.
**2.5.加密三级TCP代理**
#### **2.5.加密三级TCP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp --tls -p ":38080" -T tcp -P "66.66.66.66:8080" -C proxy.crt -K proxy.key`
二级TCP代理VPS_02,IP:33.33.33.33
@ -157,24 +293,24 @@ VPS(IP:22.22.22.33)执行:
`./proxy tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地8080端口就是通过加密TCP隧道访问66.66.66.66的8080端口.
**2.6.查看帮助**
#### **2.6.查看帮助**
`./proxy help tcp`
### 3.UDP代理
**3.1.普通一级UDP代理**
#### **3.1.普通一级UDP代理**
本地执行:
`./proxy udp -p ":5353" -T udp -P "8.8.8.8:53"`
那么访问本地UDP:5353端口就是访问8.8.8.8的UDP:53端口.
**3.2.普通二级UDP代理**
#### **3.2.普通二级UDP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp -p ":33080" -T udp -P "8.8.8.8:53"`
本地执行:
`./proxy udp -p ":5353" -T tcp -P "22.22.22.33:33080"`
那么访问本地UDP:5353端口就是通过TCP隧道,通过VPS访问8.8.8.8的UDP:53端口.
**3.3.普通三级UDP代理**
#### **3.3.普通三级UDP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp -p ":38080" -T udp -P "8.8.8.8:53"`
二级TCP代理VPS_02,IP:33.33.33.33
@ -183,14 +319,14 @@ VPS(IP:22.22.22.33)执行:
`./proxy udp -p ":5353" -T tcp -P "33.33.33.33:28080"`
那么访问本地5353端口就是通过TCP隧道,通过VPS访问8.8.8.8的53端口.
**3.4.加密二级UDP代理**
#### **3.4.加密二级UDP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp --tls -p ":33080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key`
本地执行:
`./proxy udp -p ":5353" -T tls -P "22.22.22.33:33080" -C proxy.crt -K proxy.key`
那么访问本地UDP:5353端口就是通过加密TCP隧道,通过VPS访问8.8.8.8的UDP:53端口.
**3.5.加密三级UDP代理**
#### **3.5.加密三级UDP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp --tls -p ":38080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key`
二级TCP代理VPS_02,IP:33.33.33.33
@ -199,11 +335,11 @@ VPS(IP:22.22.22.33)执行:
`./proxy udp -p ":5353" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地5353端口就是通过加密TCP隧道,通过VPS_01访问8.8.8.8的53端口.
**3.6.查看帮助**
#### **3.6.查看帮助**
`./proxy help udp`
### 4.反向代理(内网穿透)
**4.1、原理说明**
### 4.内网穿透
#### **4.1、原理说明**
内网穿透,由三部分组成:client端,server端,bridge端client和server主动连接bridge端进行桥接.
当用户访问server端,流程是:
1. server主动和bridge端建立连接
@ -212,25 +348,47 @@ VPS(IP:22.22.22.33)执行:
1. 然后bridge端把client过来的连接与server端过来的连接绑定
1. 整个通道建立完成;
**4.2、TCP普通用法**
#### **4.2、TCP普通用法**
背景:
- 公司机器A提供了web服务80端口
- 有VPS一个,公网IP:22.22.22.22
需求:
在家里能够通过访问VPS的28080端口访问到公司机器A的80端口
步骤:
1. 在vps上执行
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key`
`./proxy tserver -p ":28080" -P "127.0.0.1:33080" -C proxy.crt -K proxy.key`
`./proxy tserver -r ":28080@:80" -P "127.0.0.1:33080" -C proxy.crt -K proxy.key`
1. 在公司机器A上面执行
`./proxy tclient -p "127.0.0.1:80" -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
`./proxy tclient -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 完成
**4.3、UDP普通用法**
#### **4.3、微信接口本地开发**
背景:
- 自己的笔记本提供了nginx服务80端口
- 有VPS一个,公网IP:22.22.22.22
需求:
在微信的开发帐号的网页回调接口配置里面填写地址:http://22.22.22.22/calback.php
然后就可以访问到笔记本的80端口下面的calback.php,如果需要绑定域名,可以用自己的域名
比如:wx-dev.xxx.com解析到22.22.22.22,然后在自己笔记本的nginx里
配置域名wx-dev.xxx.com到具体的目录即可.
步骤:
1. 在vps上执行,确保vps的80端口没被其它程序占用.
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key`
`./proxy tserver -r ":80@:80" -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 在自己笔记本上面执行
`./proxy tclient -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 完成
#### **4.4、UDP普通用法**
背景:
- 公司机器A提供了DNS解析服务,UDP:53端口
- 有VPS一个,公网IP:22.22.22.22
@ -241,14 +399,14 @@ VPS(IP:22.22.22.33)执行:
步骤:
1. 在vps上执行
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key`
`./proxy tserver --udp -p ":53" -P "127.0.0.1:33080" -C proxy.crt -K proxy.key`
`./proxy tserver --udp -r ":53@:53" -P "127.0.0.1:33080" -C proxy.crt -K proxy.key`
1. 在公司机器A上面执行
`./proxy tclient --udp -p "127.0.0.1:53" -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
`./proxy tclient --udp -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 完成
**4.4、高级用法一**
#### **4.5、高级用法一**
背景:
- 公司机器A提供了web服务80端口
- 有VPS一个,公网IP:22.22.22.22
@ -262,17 +420,19 @@ VPS(IP:22.22.22.33)执行:
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key`
1. 在公司机器A上面执行
`./proxy tclient -p "127.0.0.1:80" -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
`./proxy tclient -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 在家里电脑上执行
`./proxy tserver -p ":28080" -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
`./proxy tserver -r ":28080@:80" -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 完成
**4.5、高级用法二**
#### **4.6、高级用法二**
提示:
一个client和一个server是一对,如果要暴露多个端口,需要使用--k参数进行分组,
--k可以是任意唯一字符串,只要多个端口使用的不一样即可.
如果同时有多个client连接到同一个bridge,需要指定不同的key,可以通过--k参数设定,--k可以是任意唯一字符串,
只要在同一个bridge上唯一即可.
server连接到bridge的时候,如果同时有多个client连接到同一个bridge,需要使用--k参数选择client.
暴露多个端口重复-r参数即可.-r格式是:"本地IP:本地端口@clientHOST:client端口".
背景:
- 公司机器A提供了web服务80端口,ftp服务21端口
@ -285,23 +445,129 @@ VPS(IP:22.22.22.33)执行:
步骤:
1. 在vps上执行
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key`
`./proxy tserver -p ":28080" --k web -P "127.0.0.1:33080" -C proxy.crt -K proxy.key`
`./proxy tserver -p ":29090" --k ftp -P "127.0.0.1:33080" -C proxy.crt -K proxy.key`
`./proxy tserver -r ":28080@:80" -r ":29090@:21" --k test -P "127.0.0.1:33080" -C proxy.crt -K proxy.key`
1. 在公司机器A上面执行
`./proxy tclient -p "127.0.0.1:80" --k web -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
`./proxy tclient -p "127.0.0.1:21" --k ftp -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
`./proxy tclient --k test -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 完成
**3.6.查看帮助**
#### **4.7.tserver的-r参数**
-r完整格式是:`PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT`
4.7.1.协议PROTOCOL:tcp或者udp.
比如: `-r "udp://:10053@:53" -r "tcp://:10800@:1080" -r ":8080@:80"`
如果指定了--udp参数,PROTOCOL默认为udp,那么:`-r ":8080@:80"`默认为udp;
如果没有指定--udp参数,PROTOCOL默认为tcp,那么:`-r ":8080@:80"`默认为tcp;
4.7.2.CLIENT_KEY:默认是default.
比如: -r "udp://:10053@[test1]:53" -r "tcp://:10800@[test2]:1080" -r ":8080@:80"
如果指定了--k参数,比如--k test,那么:`-r ":8080@:80"`CLIENT_KEY默认为test;
如果没有指定--k参数,那么:`-r ":8080@:80"`CLIENT_KEY默认为default;
4.7.3.LOCAL_IP为空默认是:`0.0.0.0`,CLIENT_LOCAL_HOST为空默认是:`127.0.0.1`;
#### **4.8.查看帮助**
`./proxy help tbridge`
`./proxy help tserver`
`./proxy help tserver`
### 5.SOCKS5代理
提示:SOCKS5代理,支持CONNECT,UDP协议,不支持BIND,支持用户名密码认证.
#### **5.1.普通SOCKS5代理**
`./proxy socks -t tcp -p "0.0.0.0:38080"`
#### **5.2.普通二级SOCKS5代理**
使用本地端口8090,假设上级SOCKS5代理是`22.22.22.22:8080`
`./proxy socks -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" `
我们还可以指定网站域名的黑白名单文件,一行一个域名,怕匹配规则是最右批评匹配,比如:baidu.com,匹配的是*.*.baidu.com,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理.
`./proxy socks -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt`
#### **5.3.SOCKS二级代理(加密)**
一级SOCKS代理(VPS,IP:22.22.22.22)
`./proxy socks -t tls -p ":38080" -C proxy.crt -K proxy.key`
二级SOCKS代理(本地Linux)
`./proxy socks -t tcp -p ":8080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
那么访问本地的8080端口就是访问VPS上面的代理端口38080.
二级SOCKS代理(本地windows)
`./proxy.exe socks -t tcp -p ":8080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
然后设置你的windos系统中需要通过代理上网的程序的代理为socks5模式地址为127.0.0.1端口为8080,程序即可通过加密通道通过vps上网。
#### **5.4.SOCKS三级代理(加密)**
一级SOCKS代理VPS_01,IP:22.22.22.22
`./proxy socks -t tls -p ":38080" -C proxy.crt -K proxy.key`
二级SOCKS代理VPS_02,IP:33.33.33.33
`./proxy socks -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
三级SOCKS代理(本地)
`./proxy socks -t tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地的8080端口就是访问一级SOCKS代理上面的代理端口38080.
#### **5.5.SOCKS代理流量强制走上级SOCKS代理**
默认情况下,proxy会智能判断一个网站域名是否无法访问,如果无法访问才走上级SOCKS代理.通过--always可以使全部SOCKS代理流量强制走上级SOCKS代理.
`./proxy socks --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
#### **5.6.SOCKS通过SSH中转**
说明:ssh中转的原理是利用了ssh的转发功能,就是你连接上ssh之后,可以通过ssh代理访问目标地址.
假设有:vps
- IP是2.2.2.2, ssh端口是22, ssh用户名是:user, ssh用户密码是:demo
- 用户user的ssh私钥名称是user.key
##### ***5.6.1 ssh用户名和密码的方式***
本地SOCKS5代理28080端口,执行:
`./proxy socks -T ssh -P "2.2.2.2:22" -u user -A demo -t tcp -p ":28080"`
##### ***5.6.2 ssh用户名和密钥的方式***
本地SOCKS5代理28080端口,执行:
`./proxy socks -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"`
那么访问本地的28080端口就是通过VPS访问目标地址.
#### **5.7.认证**
对于socks5代理协议我们可以进行用户名密码认证,认证的用户名和密码可以在命令行指定
`./proxy socks -t tcp -p ":33080" -a "user1:pass1" -a "user2:pass2"`
多个用户,重复-a参数即可.
也可以放在文件中,格式是一行一个"用户名:密码",然后用-F指定.
`./proxy socks -t tcp -p ":33080" -F auth-file.txt`
另外,socks5代理还集成了外部HTTP API认证,我们可以通过--auth-url参数指定一个http url接口地址,
然后有用户连接的时候,proxy会GET方式请求这url,带上下面四个参数,如果返回HTTP状态码204,代表认证成功
其它情况认为认证失败.
比如:
`./proxy socks -t tcp -p ":33080" --auth-url "http://test.com/auth.php"`
用户连接的时候,proxy会GET方式请求这url("http://test.com/auth.php"),
带上user,pass,ip,三个参数:
http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}
user:用户名
pass:密码
ip:用户的IP,比如:192.168.1.200
如果没有-a或-F或--auth-url参数,就是关闭认证.
#### **5.8.KCP协议传输**
KCP协议需要-B参数设置一个密码用于加密解密数据
一级HTTP代理(VPS,IP:22.22.22.22)
`./proxy socks -t kcp -p ":38080" -B mypassword`
二级HTTP代理(本地Linux)
`./proxy socks -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" -B mypassword`
那么访问本地的8080端口就是访问VPS上面的代理端口38080,数据通过kcp协议传输.
#### **5.9.查看帮助**
`./proxy help socks`
### TODO
- UDP Over TCP,通过tcp代理udp协议.
- http,socks代理多个上级负载均衡?
- 内网穿透增加multiplexing模式?
- 欢迎加群反馈...
### 如何使用源码?
cd进入你的go src目录,然后git clone https://github.com/snail007/goproxy.git ./proxy 即可.
编译直接:go build
运行: go run *.go
utils是工具包,service是具体的每个服务类.
### License
Proxy is licensed under GPLv3 license.
### Contact

149
config.go
View File

@ -2,9 +2,9 @@ package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"proxy/services"
"proxy/utils"
@ -24,7 +24,7 @@ func initConfig() (err error) {
os.Exit(0)
}
}
args := services.Args{}
//define args
tcpArgs := services.TCPArgs{}
httpArgs := services.HTTPArgs{}
@ -32,19 +32,19 @@ func initConfig() (err error) {
tunnelClientArgs := services.TunnelClientArgs{}
tunnelBridgeArgs := services.TunnelBridgeArgs{}
udpArgs := services.UDPArgs{}
socksArgs := services.SocksArgs{}
//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()
//########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.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 +53,136 @@ 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.PoolSize = http.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int()
httpArgs.CheckParentInterval = http.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
httpArgs.Local = http.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
httpArgs.SSHUser = http.Flag("ssh-user", "user for ssh").Short('u').Default("").String()
httpArgs.SSHKeyFile = http.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String()
httpArgs.SSHKeyFileSalt = http.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String()
httpArgs.SSHPassword = http.Flag("ssh-password", "password for ssh").Short('A').Default("").String()
httpArgs.KCPKey = http.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String()
httpArgs.KCPMethod = http.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String()
httpArgs.LocalIPS = http.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
httpArgs.AuthURL = http.Flag("auth-url", "http basic auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
httpArgs.AuthURLTimeout = http.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
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()
//########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.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.PoolSize = tcp.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int()
tcpArgs.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
tcpArgs.KCPKey = tcp.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String()
tcpArgs.KCPMethod = tcp.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String()
//########udp#########
udp := app.Command("udp", "proxy on udp mode")
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.PoolSize = udp.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int()
udpArgs.CheckParentInterval = udp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
udpArgs.Local = udp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
//########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.Mux = tunnelServer.Flag("mux", "use multiplexing mode").Default("false").Bool()
//########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.Mux = tunnelClient.Flag("mux", "use multiplexing mode").Default("false").Bool()
//########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()
tunnelBridgeArgs.Mux = tunnelBridge.Flag("mux", "use multiplexing mode").Default("false").Bool()
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.UDPParent = socks.Flag("udp-parent", "udp parent address, such as: \"23.32.32.19:33090\"").Default("").Short('X').String()
socksArgs.UDPLocal = socks.Flag("udp-local", "udp local ip:port to listen").Short('x').Default(":33090").String()
socksArgs.CertFile = socks.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
socksArgs.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.KCPKey = socks.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String()
socksArgs.KCPMethod = socks.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String()
socksArgs.LocalIPS = socks.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
socksArgs.AuthURL = socks.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
socksArgs.AuthURLTimeout = socks.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
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()
if *certTLS != "" && *keyTLS != "" {
args.CertBytes, args.KeyBytes = tlsBytes(*certTLS, *keyTLS)
//parse args
serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
flags := log.Ldate
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()
fmt.Printf("%s [PID] %d running...\n", os.Args[0], cmd.Process.Pid)
os.Exit(0)
}
//common args
httpArgs.Args = args
tcpArgs.Args = args
udpArgs.Args = args
tunnelBridgeArgs.Args = args
tunnelClientArgs.Args = args
tunnelServerArgs.Args = args
if *debug {
flags |= log.Lshortfile | log.Lmicroseconds
} else {
flags |= log.Ltime
}
log.SetFlags(flags)
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("tserver", services.NewTunnelServerManager(), tunnelServerArgs)
services.Regist("tclient", services.NewTunnelClient(), tunnelClientArgs)
services.Regist("tbridge", services.NewTunnelBridge(), tunnelBridgeArgs)
services.Regist("socks", services.NewSocks(), socksArgs)
service, err = services.Run(serviceName)
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 +199,3 @@ 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
}

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

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

View File

@ -1,10 +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

View File

@ -6,7 +6,7 @@ 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/v3.7/proxy-linux-amd64.tar.gz
# install monexec
tar zxvf monexec_0.1.1_linux_amd64.tar.gz

View File

@ -9,7 +9,7 @@ import (
"syscall"
)
const APP_VERSION = "3.0"
const APP_VERSION = "3.7"
func main() {
err := initConfig()

View File

@ -1,5 +1,5 @@
#!/bin/bash
VER="3.0"
VER="3.7"
RELEASE="release-${VER}"
rm -rf .cert
mkdir .cert
@ -9,7 +9,7 @@ cd .cert
cd ..
rm -rf ${RELEASE}
mkdir ${RELEASE}
set CGO_ENABLED=0
export 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
@ -61,3 +61,9 @@ GOOS=windows GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-windows-386.tar.g
GOOS=windows GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-windows-amd64.tar.gz" proxy.exe direct blocked .cert/proxy.crt .cert/proxy.key
rm -rf proxy proxy.exe .cert
#todo
#1.release.sh VER="xxx"
#2.main.go APP_VERSION="xxx"
#3.install_auto.sh goproxy/releases/download/vxxx
#4.README goproxy/releases/download/vxxx

View File

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

View File

@ -3,11 +3,15 @@ package services
import (
"fmt"
"io"
"io/ioutil"
"log"
"net"
"proxy/utils"
"runtime/debug"
"strconv"
"time"
"golang.org/x/crypto/ssh"
)
type HTTP struct {
@ -15,6 +19,8 @@ type HTTP struct {
cfg HTTPArgs
checker utils.Checker
basicAuth utils.BasicAuth
sshClient *ssh.Client
lockChn chan bool
}
func NewHTTP() Service {
@ -23,6 +29,43 @@ func NewHTTP() Service {
cfg: HTTPArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
lockChn: make(chan bool, 1),
}
}
func (s *HTTP) CheckArgs() {
var err error
if *s.cfg.Parent != "" && *s.cfg.ParentType == "" {
log.Fatalf("parent type unkown,use -T <tls|tcp|ssh>")
}
if *s.cfg.ParentType == "tls" || *s.cfg.LocalType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
if *s.cfg.ParentType == "ssh" {
if *s.cfg.SSHUser == "" {
log.Fatalf("ssh user required")
}
if *s.cfg.SSHKeyFile == "" && *s.cfg.SSHPassword == "" {
log.Fatalf("ssh password or key required")
}
if *s.cfg.SSHPassword != "" {
s.cfg.SSHAuthMethod = ssh.Password(*s.cfg.SSHPassword)
} else {
var SSHSigner ssh.Signer
s.cfg.SSHKeyBytes, err = ioutil.ReadFile(*s.cfg.SSHKeyFile)
if err != nil {
log.Fatalf("read key file ERR: %s", err)
}
if *s.cfg.SSHKeyFileSalt != "" {
SSHSigner, err = ssh.ParsePrivateKeyWithPassphrase(s.cfg.SSHKeyBytes, []byte(*s.cfg.SSHKeyFileSalt))
} else {
SSHSigner, err = ssh.ParsePrivateKey(s.cfg.SSHKeyBytes)
}
if err != nil {
log.Fatalf("parse ssh private key fail,ERR: %s", err)
}
s.cfg.SSHAuthMethod = ssh.PublicKeys(SSHSigner)
}
}
}
func (s *HTTP) InitService() {
@ -30,6 +73,34 @@ func (s *HTTP) InitService() {
if *s.cfg.Parent != "" {
s.checker = utils.NewChecker(*s.cfg.HTTPTimeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct)
}
if *s.cfg.ParentType == "ssh" {
err := s.ConnectSSH()
if err != nil {
log.Fatalf("init service fail, ERR: %s", err)
}
go func() {
//循环检查ssh网络连通性
for {
conn, err := utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout*2)
if err == nil {
_, err = conn.Write([]byte{0})
}
if err != nil {
if s.sshClient != nil {
s.sshClient.Close()
if s.sshClient.Conn != nil {
s.sshClient.Conn.Close()
}
}
log.Printf("ssh offline, retrying...")
s.ConnectSSH()
} else {
conn.Close()
}
time.Sleep(time.Second * 3)
}
}()
}
}
func (s *HTTP) StopService() {
if s.outPool.Pool != nil {
@ -38,20 +109,21 @@ func (s *HTTP) StopService() {
}
func (s *HTTP) Start(args interface{}) (err error) {
s.cfg = args.(HTTPArgs)
s.CheckArgs()
if *s.cfg.Parent != "" {
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
s.InitOutConnPool()
}
s.InitService()
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 {
} else if *s.cfg.LocalType == TYPE_TLS {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback)
} else if *s.cfg.LocalType == TYPE_KCP {
err = sc.ListenKCP(*s.cfg.KCPMethod, *s.cfg.KCPKey, s.callback)
}
if err != nil {
return
@ -69,10 +141,12 @@ func (s *HTTP) callback(inConn net.Conn) {
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)
var err interface{}
var req utils.HTTPRequest
req, err = utils.NewHTTPRequest(&inConn, 4096, s.IsBasicAuth(), &s.basicAuth)
if err != nil {
if err != io.EOF {
log.Printf("decoder error , form %s, ERR:%s", err, inConn.RemoteAddr())
log.Printf("decoder error , from %s, ERR:%s", inConn.RemoteAddr(), err)
}
utils.CloseConn(&inConn)
return
@ -95,8 +169,9 @@ func (s *HTTP) callback(inConn net.Conn) {
//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)
@ -106,7 +181,7 @@ func (s *HTTP) callback(inConn net.Conn) {
utils.CloseConn(&inConn)
}
}
func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (err error) {
func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (err interface{}) {
inAddr := (*inConn).RemoteAddr().String()
inLocalAddr := (*inConn).LocalAddr().String()
//防止死循环
@ -117,13 +192,29 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
}
var outConn net.Conn
var _outConn interface{}
if useProxy {
_outConn, err = s.outPool.Pool.Get()
if err == nil {
outConn = _outConn.(net.Conn)
tryCount := 0
maxTryCount := 5
for {
if useProxy {
if *s.cfg.ParentType == "ssh" {
outConn, err = s.getSSHConn(address)
} else {
//log.Printf("%v", s.outPool)
_outConn, err = s.outPool.Pool.Get()
if err == nil {
outConn = _outConn.(net.Conn)
}
}
} else {
outConn, err = utils.ConnectHost(address, *s.cfg.Timeout)
}
tryCount++
if err == nil || tryCount > maxTryCount {
break
} else {
log.Printf("connect to %s , err:%s,retrying...", *s.cfg.Parent, err)
time.Sleep(time.Second * 2)
}
} else {
outConn, err = utils.ConnectHost(address, *s.cfg.Timeout)
}
if err != nil {
log.Printf("connect to %s , err:%s", *s.cfg.Parent, err)
@ -132,31 +223,95 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
}
outAddr := outConn.RemoteAddr().String()
outLocalAddr := outConn.LocalAddr().String()
//outLocalAddr := outConn.LocalAddr().String()
if req.IsHTTPS() && !useProxy {
req.HTTPSReply()
if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") {
//https无上级或者上级非代理,proxy需要响应connect请求,并直连目标
err = req.HTTPSReply()
} else {
outConn.Write(req.HeadBuf)
//https或者http,上级是代理,proxy需要转发
_, err = outConn.Write(req.HeadBuf)
if err != nil {
log.Printf("write to %s , err:%s", *s.cfg.Parent, err)
utils.CloseConn(inConn)
return
}
}
utils.IoBind((*inConn), outConn, func(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)
utils.IoBind((*inConn), outConn, func(err interface{}) {
log.Printf("conn %s - %s released [%s]", inAddr, outAddr, req.Host)
})
log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, req.Host)
return
}
func (s *HTTP) OutToUDP(inConn *net.Conn) (err error) {
func (s *HTTP) getSSHConn(host string) (outConn net.Conn, err interface{}) {
maxTryCount := 1
tryCount := 0
RETRY:
if tryCount >= maxTryCount {
return
}
wait := make(chan bool, 1)
go func() {
defer func() {
if err == nil {
err = recover()
}
wait <- true
}()
outConn, err = s.sshClient.Dial("tcp", host)
}()
select {
case <-wait:
case <-time.After(time.Second * 5):
err = fmt.Errorf("ssh dial %s timeout", host)
}
if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err)
e := s.ConnectSSH()
if e == nil {
tryCount++
time.Sleep(time.Second * 3)
goto RETRY
} else {
err = e
}
}
return
}
func (s *HTTP) ConnectSSH() (err error) {
select {
case s.lockChn <- true:
default:
err = fmt.Errorf("can not connect at same time")
return
}
config := ssh.ClientConfig{
Timeout: time.Duration(*s.cfg.Timeout) * time.Millisecond,
User: *s.cfg.SSHUser,
Auth: []ssh.AuthMethod{s.cfg.SSHAuthMethod},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
if s.sshClient != nil {
s.sshClient.Close()
}
s.sshClient, err = ssh.Dial("tcp", *s.cfg.Parent, &config)
<-s.lockChn
return
}
func (s *HTTP) InitOutConnPool() {
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP {
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP || *s.cfg.ParentType == TYPE_KCP {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutPool(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType == TYPE_TLS,
*s.cfg.ParentType,
*s.cfg.KCPMethod,
*s.cfg.KCPKey,
s.cfg.CertBytes, s.cfg.KeyBytes,
*s.cfg.Parent,
*s.cfg.Timeout,
@ -167,6 +322,10 @@ func (s *HTTP) InitOutConnPool() {
}
func (s *HTTP) InitBasicAuth() (err error) {
s.basicAuth = utils.NewBasicAuth()
if *s.cfg.AuthURL != "" {
s.basicAuth.SetAuthURL(*s.cfg.AuthURL, *s.cfg.AuthURLOkCode, *s.cfg.AuthURLTimeout, *s.cfg.AuthURLRetry)
log.Printf("auth from %s", *s.cfg.AuthURL)
}
if *s.cfg.AuthFile != "" {
var n = 0
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
@ -183,7 +342,7 @@ func (s *HTTP) InitBasicAuth() (err error) {
return
}
func (s *HTTP) IsBasicAuth() bool {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0
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)
@ -205,6 +364,9 @@ func (s *HTTP) IsDeadLoop(inLocalAddr string, host string) bool {
}
}
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 {

View File

@ -25,7 +25,7 @@ func Regist(name string, s Service, args interface{}) {
Name: name,
}
}
func Run(name string) (service *ServiceItem, err error) {
func Run(name string, args ...interface{}) (service *ServiceItem, err error) {
service, ok := servicesMap[name]
if ok {
go func() {
@ -35,7 +35,11 @@ func Run(name string) (service *ServiceItem, err error) {
log.Fatalf("%s servcie crashed, ERR: %s\ntrace:%s", name, err, string(debug.Stack()))
}
}()
err := service.S.Start(service.Args)
if len(args) == 1 {
err = service.S.Start(args[0])
} else {
err = service.S.Start(service.Args)
}
if err != nil {
log.Fatalf("%s servcie fail, ERR: %s", name, err)
}

621
services/socks.go Normal file
View File

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

View File

@ -1,6 +1,7 @@
package services
import (
"bufio"
"fmt"
"io"
"log"
@ -23,6 +24,17 @@ func NewTCP() Service {
cfg: TCPArgs{},
}
}
func (s *TCP) CheckArgs() {
if *s.cfg.Parent == "" {
log.Fatalf("parent required for %s %s", s.cfg.Protocol(), *s.cfg.Local)
}
if *s.cfg.ParentType == "" {
log.Fatalf("parent type unkown,use -T <tls|tcp|kcp|udp>")
}
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.LocalType == TYPE_TLS {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
}
func (s *TCP) InitService() {
s.InitOutConnPool()
}
@ -33,21 +45,20 @@ func (s *TCP) StopService() {
}
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.CheckArgs()
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p)
if !*s.cfg.IsTLS {
if *s.cfg.LocalType == TYPE_TCP {
err = sc.ListenTCP(s.callback)
} else {
} else if *s.cfg.LocalType == TYPE_TLS {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback)
} else if *s.cfg.LocalType == TYPE_KCP {
err = sc.ListenKCP(*s.cfg.KCPMethod, *s.cfg.KCPKey, s.callback)
}
if err != nil {
return
@ -67,6 +78,8 @@ func (s *TCP) callback(inConn net.Conn) {
}()
var err error
switch *s.cfg.ParentType {
case TYPE_KCP:
fallthrough
case TYPE_TCP:
fallthrough
case TYPE_TLS:
@ -94,21 +107,19 @@ func (s *TCP) OutToTCP(inConn *net.Conn) (err error) {
return
}
inAddr := (*inConn).RemoteAddr().String()
inLocalAddr := (*inConn).LocalAddr().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)
//outLocalAddr := outConn.LocalAddr().String()
utils.IoBind((*inConn), outConn, func(err interface{}) {
log.Printf("conn %s - %s released", inAddr, outAddr)
})
log.Printf("conn %s - %s connected", inAddr, 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)
srcAddr, body, err := utils.ReadUDPPacket(bufio.NewReader(*inConn))
if err == io.EOF || err == io.ErrUnexpectedEOF {
//log.Printf("connection %s released", srcAddr)
utils.CloseConn(inConn)
@ -154,12 +165,14 @@ func (s *TCP) OutToUDP(inConn *net.Conn) (err error) {
}
func (s *TCP) InitOutConnPool() {
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP {
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP || *s.cfg.ParentType == TYPE_KCP {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutPool(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType == TYPE_TLS,
*s.cfg.ParentType,
*s.cfg.KCPMethod,
*s.cfg.KCPKey,
s.cfg.CertBytes, s.cfg.KeyBytes,
*s.cfg.Parent,
*s.cfg.Timeout,

View File

@ -2,92 +2,235 @@ 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 ServerConn struct {
//ClientLocalAddr string //tcp:2.2.22:333@ID
Conn *net.Conn
}
type TunnelBridge struct {
cfg TunnelBridgeArgs
br utils.ConcurrentMap
cfg TunnelBridgeArgs
serverConns utils.ConcurrentMap
clientControlConns utils.ConcurrentMap
// cmServer utils.ConnManager
// cmClient utils.ConnManager
}
func NewTunnelBridge() Service {
return &TunnelBridge{
cfg: TunnelBridgeArgs{},
br: utils.NewConcurrentMap(),
cfg: TunnelBridgeArgs{},
serverConns: utils.NewConcurrentMap(),
clientControlConns: utils.NewConcurrentMap(),
// cmServer: utils.NewConnManager(),
// cmClient: utils.NewConnManager(),
}
}
func (s *TunnelBridge) InitService() {
}
func (s *TunnelBridge) Check() {
if s.cfg.CertBytes == nil || s.cfg.KeyBytes == nil {
func (s *TunnelBridge) CheckArgs() {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
log.Fatalf("cert and key file required")
}
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
func (s *TunnelBridge) StopService() {
}
func (s *TunnelBridge) Start(args interface{}) (err error) {
s.cfg = args.(TunnelBridgeArgs)
s.Check()
s.CheckArgs()
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p)
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, func(inConn net.Conn) {
//log.Printf("connection from %s ", inConn.RemoteAddr())
reader := bufio.NewReader(inConn)
var err error
var connType uint8
err = binary.Read(reader, binary.LittleEndian, &connType)
err = utils.ReadPacket(reader, &connType)
if err != nil {
utils.CloseConn(&inConn)
log.Printf("read error,ERR:%s", err)
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)
var key, ID, clientLocalAddr, serverID string
err = utils.ReadPacketData(reader, &key, &ID, &clientLocalAddr, &serverID)
if err != nil {
log.Printf("read error,ERR:%s", err)
return
}
packet := utils.BuildPacketData(ID, clientLocalAddr, serverID)
log.Printf("server connection, key: %s , id: %s %s %s", key, ID, clientLocalAddr, serverID)
//addr := clientLocalAddr + "@" + ID
s.serverConns.Set(ID, ServerConn{
Conn: &inConn,
})
for {
item, ok := s.clientControlConns.Get(key)
if !ok {
log.Printf("client %s control conn not exists", key)
time.Sleep(time.Second * 3)
continue
}
(*item.(*net.Conn)).SetWriteDeadline(time.Now().Add(time.Second * 3))
_, err := (*item.(*net.Conn)).Write(packet)
(*item.(*net.Conn)).SetWriteDeadline(time.Time{})
if err != nil {
log.Printf("%s client control conn write signal fail, err: %s, retrying...", key, err)
time.Sleep(time.Second * 3)
continue
} else {
// s.cmServer.Add(serverID, ID, &inConn)
break
}
}
case CONN_CLIENT:
s.ClientConn(&inConn, key)
case CONN_CONTROL:
s.ClientControlConn(&inConn, key)
default:
log.Printf("unkown conn type %d", connType)
utils.CloseConn(&inConn)
var key, ID, serverID string
err = utils.ReadPacketData(reader, &key, &ID, &serverID)
if err != nil {
log.Printf("read error,ERR:%s", err)
return
}
log.Printf("client connection , key: %s , id: %s, server id:%s", key, ID, serverID)
serverConnItem, ok := s.serverConns.Get(ID)
if !ok {
inConn.Close()
log.Printf("server conn %s exists", ID)
return
}
serverConn := serverConnItem.(ServerConn).Conn
utils.IoBind(*serverConn, inConn, func(err interface{}) {
s.serverConns.Remove(ID)
// s.cmClient.RemoveOne(key, ID)
// s.cmServer.RemoveOne(serverID, ID)
log.Printf("conn %s released", ID)
})
// s.cmClient.Add(key, ID, &inConn)
log.Printf("conn %s created", ID)
case CONN_CLIENT_CONTROL:
var key string
err = utils.ReadPacketData(reader, &key)
if err != nil {
log.Printf("read error,ERR:%s", err)
return
}
log.Printf("client control connection, key: %s", key)
if s.clientControlConns.Has(key) {
item, _ := s.clientControlConns.Get(key)
(*item.(*net.Conn)).Close()
}
s.clientControlConns.Set(key, &inConn)
log.Printf("set client %s control conn", key)
// case CONN_SERVER_HEARBEAT:
// var serverID string
// err = utils.ReadPacketData(reader, &serverID)
// if err != nil {
// log.Printf("read error,ERR:%s", err)
// return
// }
// log.Printf("server heartbeat connection, id: %s", serverID)
// writeDie := make(chan bool)
// readDie := make(chan bool)
// go func() {
// for {
// inConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
// _, err = inConn.Write([]byte{0x00})
// inConn.SetWriteDeadline(time.Time{})
// if err != nil {
// log.Printf("server heartbeat connection write err %s", err)
// break
// }
// time.Sleep(time.Second * 3)
// }
// close(writeDie)
// }()
// go func() {
// for {
// signal := make([]byte, 1)
// inConn.SetReadDeadline(time.Now().Add(time.Second * 6))
// _, err := inConn.Read(signal)
// inConn.SetReadDeadline(time.Time{})
// if err != nil {
// log.Printf("server heartbeat connection read err: %s", err)
// break
// } else {
// // log.Printf("heartbeat from server ,id:%s", serverID)
// }
// }
// close(readDie)
// }()
// select {
// case <-readDie:
// case <-writeDie:
// }
// utils.CloseConn(&inConn)
// s.cmServer.Remove(serverID)
// log.Printf("server heartbeat conn %s released", serverID)
// case CONN_CLIENT_HEARBEAT:
// var clientID string
// err = utils.ReadPacketData(reader, &clientID)
// if err != nil {
// log.Printf("read error,ERR:%s", err)
// return
// }
// log.Printf("client heartbeat connection, id: %s", clientID)
// writeDie := make(chan bool)
// readDie := make(chan bool)
// go func() {
// for {
// inConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
// _, err = inConn.Write([]byte{0x00})
// inConn.SetWriteDeadline(time.Time{})
// if err != nil {
// log.Printf("client heartbeat connection write err %s", err)
// break
// }
// time.Sleep(time.Second * 3)
// }
// close(writeDie)
// }()
// go func() {
// for {
// signal := make([]byte, 1)
// inConn.SetReadDeadline(time.Now().Add(time.Second * 6))
// _, err := inConn.Read(signal)
// inConn.SetReadDeadline(time.Time{})
// if err != nil {
// log.Printf("client control connection read err: %s", err)
// break
// } else {
// // log.Printf("heartbeat from client ,id:%s", clientID)
// }
// }
// close(readDie)
// }()
// select {
// case <-readDie:
// case <-writeDie:
// }
// utils.CloseConn(&inConn)
// s.cmClient.Remove(clientID)
// if s.clientControlConns.Has(clientID) {
// item, _ := s.clientControlConns.Get(clientID)
// (*item.(*net.Conn)).Close()
// }
// s.clientControlConns.Remove(clientID)
// log.Printf("client heartbeat conn %s released", clientID)
}
})
if err != nil {
@ -99,85 +242,3 @@ func (s *TunnelBridge) Start(args interface{}) (err error) {
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,9 +1,7 @@
package services
import (
"bytes"
"crypto/tls"
"encoding/binary"
"fmt"
"io"
"log"
@ -14,62 +12,132 @@ import (
type TunnelClient struct {
cfg TunnelClientArgs
// cm utils.ConnManager
ctrlConn net.Conn
}
func NewTunnelClient() Service {
return &TunnelClient{
cfg: TunnelClientArgs{},
// cm: utils.NewConnManager(),
}
}
func (s *TunnelClient) InitService() {
// s.InitHeartbeatDeamon()
}
func (s *TunnelClient) Check() {
// func (s *TunnelClient) InitHeartbeatDeamon() {
// log.Printf("heartbeat started")
// go func() {
// var heartbeatConn net.Conn
// var ID = *s.cfg.Key
// for {
// //close all connection
// s.cm.RemoveAll()
// if s.ctrlConn != nil {
// s.ctrlConn.Close()
// }
// utils.CloseConn(&heartbeatConn)
// heartbeatConn, err := s.GetInConn(CONN_CLIENT_HEARBEAT, ID)
// if err != nil {
// log.Printf("heartbeat connection err: %s, retrying...", err)
// time.Sleep(time.Second * 3)
// utils.CloseConn(&heartbeatConn)
// continue
// }
// log.Printf("heartbeat connection created,id:%s", ID)
// writeDie := make(chan bool)
// readDie := make(chan bool)
// go func() {
// for {
// heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
// _, err = heartbeatConn.Write([]byte{0x00})
// heartbeatConn.SetWriteDeadline(time.Time{})
// if err != nil {
// log.Printf("heartbeat connection write err %s", err)
// break
// }
// time.Sleep(time.Second * 3)
// }
// close(writeDie)
// }()
// go func() {
// for {
// signal := make([]byte, 1)
// heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6))
// _, err := heartbeatConn.Read(signal)
// heartbeatConn.SetReadDeadline(time.Time{})
// if err != nil {
// log.Printf("heartbeat connection read err: %s", err)
// break
// } else {
// //log.Printf("heartbeat from bridge")
// }
// }
// close(readDie)
// }()
// select {
// case <-readDie:
// case <-writeDie:
// }
// }
// }()
// }
func (s *TunnelClient) CheckArgs() {
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 {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
log.Fatalf("cert and key file required")
}
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
func (s *TunnelClient) StopService() {
// s.cm.RemoveAll()
}
func (s *TunnelClient) Start(args interface{}) (err error) {
s.cfg = args.(TunnelClientArgs)
s.Check()
s.CheckArgs()
s.InitService()
log.Printf("proxy on tunnel client mode")
for {
ctrlConn, err := s.GetInConn(CONN_CONTROL)
//close all conn
// s.cm.Remove(*s.cfg.Key)
if s.ctrlConn != nil {
s.ctrlConn.Close()
}
s.ctrlConn, err = s.GetInConn(CONN_CLIENT_CONTROL, *s.cfg.Key)
if err != nil {
log.Printf("control connection err: %s", err)
log.Printf("control connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
utils.CloseConn(&ctrlConn)
if s.ctrlConn != nil {
s.ctrlConn.Close()
}
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)
var ID, clientLocalAddr, serverID string
err = utils.ReadPacketData(s.ctrlConn, &ID, &clientLocalAddr, &serverID)
if err != nil {
utils.CloseConn(&ctrlConn)
log.Printf("read connection signal err: %s", err)
if s.ctrlConn != nil {
s.ctrlConn.Close()
}
log.Printf("read connection signal err: %s, retrying...", err)
break
}
log.Printf("signal revecived:%s", signal)
if *s.cfg.IsUDP {
go s.ServeUDP()
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()
go s.ServeConn(localAddr, ID, serverID)
}
}
}
@ -77,19 +145,13 @@ func (s *TunnelClient) Start(args interface{}) (err error) {
func (s *TunnelClient) Clean() {
s.StopService()
}
func (s *TunnelClient) GetInConn(typ uint8) (outConn net.Conn, err error) {
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
}
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())
_, err = outConn.Write(utils.BuildPacket(typ, data...))
if err != nil {
err = fmt.Errorf("write connection data err: %s ,retrying...", err)
utils.CloseConn(&outConn)
@ -105,36 +167,43 @@ func (s *TunnelClient) GetConn() (conn net.Conn, err error) {
}
return
}
func (s *TunnelClient) ServeUDP() {
func (s *TunnelClient) ServeUDP(localAddr, ID, serverID string) {
var inConn net.Conn
var err error
// for {
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)
// s.cm.RemoveOne(*s.cfg.Key, ID)
inConn, err = s.GetInConn(CONN_CLIENT, *s.cfg.Key, ID, serverID)
if err != nil {
utils.CloseConn(&inConn)
log.Printf("connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
// s.cm.Add(*s.cfg.Key, ID, &inConn)
log.Printf("conn %s created", ID)
for {
srcAddr, body, err := utils.ReadUDPPacket(inConn)
if err == io.EOF || err == io.ErrUnexpectedEOF {
log.Printf("connection %s released", ID)
utils.CloseConn(&inConn)
break
} else if err != nil {
log.Printf("udp packet revecived fail, err: %s", err)
} else {
//log.Printf("udp packet revecived:%s,%v", srcAddr, body)
go s.processUDPPacket(&inConn, srcAddr, localAddr, body)
}
}
// }
}
func (s *TunnelClient) processUDPPacket(inConn *net.Conn, srcAddr string, body []byte) {
dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Local)
func (s *TunnelClient) processUDPPacket(inConn *net.Conn, srcAddr, localAddr string, body []byte) {
dstAddr, err := net.ResolveUDPAddr("udp", localAddr)
if err != nil {
log.Printf("can't resolve address: %s", err)
utils.CloseConn(inConn)
@ -153,27 +222,28 @@ func (s *TunnelClient) processUDPPacket(inConn *net.Conn, srcAddr string, body [
return
}
//log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 512)
len, _, err := conn.ReadFromUDP(buf)
buf := make([]byte, 1024)
length, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return
}
respBody := buf[0:len]
respBody := buf[0:length]
//log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
_, err = (*inConn).Write(utils.UDPPacket(srcAddr, respBody))
bs := utils.UDPPacket(srcAddr, respBody)
_, err = (*inConn).Write(bs)
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())
//log.Printf("send udp response success ,from:%s ,%d ,%v", dstAddr.String(), len(bs), bs)
}
func (s *TunnelClient) ServeConn() {
func (s *TunnelClient) ServeConn(localAddr, ID, serverID string) {
var inConn, outConn net.Conn
var err error
for {
inConn, err = s.GetInConn(CONN_CLIENT)
inConn, err = s.GetInConn(CONN_CLIENT, *s.cfg.Key, ID, serverID)
if err != nil {
utils.CloseConn(&inConn)
log.Printf("connection err: %s, retrying...", err)
@ -187,29 +257,27 @@ func (s *TunnelClient) ServeConn() {
i := 0
for {
i++
outConn, err = utils.ConnectHost(*s.cfg.Local, *s.cfg.Timeout)
outConn, err = utils.ConnectHost(localAddr, *s.cfg.Timeout)
if err == nil || i == 3 {
break
} else {
if i == 3 {
log.Printf("connect to %s err: %s, retrying...", *s.cfg.Local, err)
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)
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())
utils.IoBind(inConn, outConn, func(err interface{}) {
log.Printf("conn %s released", ID)
// s.cm.RemoveOne(*s.cfg.Key, ID)
})
// s.cm.Add(*s.cfg.Key, ID, &inConn)
log.Printf("conn %s created", ID)
}

View File

@ -1,10 +1,8 @@
package services
import (
"bufio"
"bytes"
"crypto/tls"
"encoding/binary"
"fmt"
"io"
"log"
"net"
@ -21,6 +19,167 @@ type TunnelServer struct {
sc utils.ServerChannel
}
type TunnelServerManager struct {
cfg TunnelServerArgs
udpChn chan UDPItem
sc utils.ServerChannel
serverID string
// cm utils.ConnManager
}
func NewTunnelServerManager() Service {
return &TunnelServerManager{
cfg: TunnelServerArgs{},
udpChn: make(chan UDPItem, 50000),
serverID: utils.Uniqueid(),
// cm: utils.NewConnManager(),
}
}
func (s *TunnelServerManager) Start(args interface{}) (err error) {
s.cfg = args.(TunnelServerArgs)
s.CheckArgs()
if *s.cfg.Parent != "" {
log.Printf("use tls parent %s", *s.cfg.Parent)
} else {
log.Fatalf("parent required")
}
s.InitService()
log.Printf("server id: %s", s.serverID)
//log.Printf("route:%v", *s.cfg.Route)
for _, _info := range *s.cfg.Route {
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,
})
if err != nil {
return
}
}
return
}
func (s *TunnelServerManager) Clean() {
s.StopService()
}
func (s *TunnelServerManager) StopService() {
// s.cm.RemoveAll()
}
func (s *TunnelServerManager) CheckArgs() {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
log.Fatalf("cert and key file required")
}
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
func (s *TunnelServerManager) InitService() {
// s.InitHeartbeatDeamon()
}
// func (s *TunnelServerManager) InitHeartbeatDeamon() {
// log.Printf("heartbeat started")
// go func() {
// var heartbeatConn net.Conn
// var ID string
// for {
// //close all connection
// s.cm.Remove(ID)
// utils.CloseConn(&heartbeatConn)
// heartbeatConn, ID, err := s.GetOutConn(CONN_SERVER_HEARBEAT)
// if err != nil {
// log.Printf("heartbeat connection err: %s, retrying...", err)
// time.Sleep(time.Second * 3)
// utils.CloseConn(&heartbeatConn)
// continue
// }
// log.Printf("heartbeat connection created,id:%s", ID)
// writeDie := make(chan bool)
// readDie := make(chan bool)
// go func() {
// for {
// heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
// _, err = heartbeatConn.Write([]byte{0x00})
// heartbeatConn.SetWriteDeadline(time.Time{})
// if err != nil {
// log.Printf("heartbeat connection write err %s", err)
// break
// }
// time.Sleep(time.Second * 3)
// }
// close(writeDie)
// }()
// go func() {
// for {
// signal := make([]byte, 1)
// heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6))
// _, err := heartbeatConn.Read(signal)
// heartbeatConn.SetReadDeadline(time.Time{})
// if err != nil {
// log.Printf("heartbeat connection read err: %s", err)
// break
// } else {
// // log.Printf("heartbeat from bridge")
// }
// }
// close(readDie)
// }()
// select {
// case <-readDie:
// case <-writeDie:
// }
// }
// }()
// }
func (s *TunnelServerManager) GetOutConn(typ uint8) (outConn net.Conn, ID string, err error) {
outConn, err = s.GetConn()
if err != nil {
log.Printf("connection err: %s", err)
return
}
ID = s.serverID
_, err = outConn.Write(utils.BuildPacket(typ, s.serverID))
if err != nil {
log.Printf("write connection data err: %s ,retrying...", err)
utils.CloseConn(&outConn)
return
}
return
}
func (s *TunnelServerManager) GetConn() (conn net.Conn, err error) {
var _conn tls.Conn
_conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes)
if err == nil {
conn = net.Conn(&_conn)
}
return
}
func NewTunnelServer() Service {
return &TunnelServer{
cfg: TunnelServerArgs{},
@ -37,26 +196,19 @@ type UDPItem struct {
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) CheckArgs() {
if *s.cfg.Remote == "" {
log.Fatalf("remote required")
}
}
func (s *TunnelServer) StopService() {
}
func (s *TunnelServer) Start(args interface{}) (err error) {
s.cfg = args.(TunnelServerArgs)
s.Check()
s.CheckArgs()
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
s.sc = utils.NewServerChannel(host, p)
if *s.cfg.IsUDP {
err = s.sc.ListenUDP(func(packet []byte, localAddr, srcAddr *net.UDPAddr) {
s.udpChn <- UDPItem{
@ -77,8 +229,9 @@ func (s *TunnelServer) Start(args interface{}) (err error) {
}
}()
var outConn net.Conn
var ID string
for {
outConn, err = s.GetOutConn()
outConn, ID, err = s.GetOutConn(CONN_SERVER)
if err != nil {
utils.CloseConn(&outConn)
log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err)
@ -88,14 +241,13 @@ func (s *TunnelServer) Start(args interface{}) (err error) {
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())
utils.IoBind(inConn, outConn, func(err interface{}) {
// s.cfg.Mgr.cm.RemoveOne(s.cfg.Mgr.serverID, ID)
log.Printf("%s conn %s released", *s.cfg.Key, ID)
})
//add conn
// s.cfg.Mgr.cm.Add(s.cfg.Mgr.serverID, ID, &inConn)
log.Printf("%s conn %s created", *s.cfg.Key, ID)
})
if err != nil {
return
@ -105,21 +257,20 @@ func (s *TunnelServer) Start(args interface{}) (err error) {
return
}
func (s *TunnelServer) Clean() {
s.StopService()
}
func (s *TunnelServer) GetOutConn() (outConn net.Conn, err error) {
func (s *TunnelServer) GetOutConn(typ uint8) (outConn net.Conn, ID string, 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())
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 {
log.Printf("write connection data err: %s ,retrying...", err)
utils.CloseConn(&outConn)
@ -143,36 +294,37 @@ func (s *TunnelServer) UDPConnDeamon() {
}
}()
var outConn net.Conn
var cmdChn = make(chan bool, 1)
// var hb utils.HeartbeatReadWriter
var ID string
// var cmdChn = make(chan bool, 1000)
var err error
for {
item := <-s.udpChn
RETRY:
if outConn == nil {
for {
outConn, err = s.GetOutConn()
outConn, ID, err = s.GetOutConn(CONN_SERVER)
if err != nil {
cmdChn <- true
// 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(outConn net.Conn, ID string) {
go func() {
<-cmdChn
outConn.Close()
// <-cmdChn
// outConn.Close()
}()
for {
srcAddrFromConn, body, err := utils.ReadUDPPacket(&outConn)
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())
log.Printf("UDP deamon connection %s exited", ID)
break
}
if err != nil {
log.Printf("parse revecived udp packet fail, err: %s", err)
log.Printf("parse revecived udp packet fail, err: %s ,%v", err, body)
continue
}
//log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn)
@ -188,19 +340,20 @@ func (s *TunnelServer) UDPConnDeamon() {
log.Printf("udp response to local %s fail,ERR:%s", srcAddrFromConn, err)
continue
}
//log.Printf("udp response to local %s success", srcAddrFromConn)
//log.Printf("udp response to local %s success , %v", srcAddrFromConn, body)
}
}(outConn)
}(outConn, ID)
break
}
}
}
writer := bufio.NewWriter(outConn)
writer.Write(utils.UDPPacket(item.srcAddr.String(), *item.packet))
err := writer.Flush()
outConn.SetWriteDeadline(time.Now().Add(time.Second))
_, err = outConn.Write(utils.UDPPacket(item.srcAddr.String(), *item.packet))
outConn.SetWriteDeadline(time.Time{})
if err != nil {
utils.CloseConn(&outConn)
outConn = nil
log.Printf("write udp packet to %s fail ,flush err:%s", *s.cfg.Parent, err)
log.Printf("write udp packet to %s fail ,flush err:%s ,retrying...", *s.cfg.Parent, err)
goto RETRY
}
//log.Printf("write packet %v", *item.packet)

View File

@ -27,6 +27,17 @@ func NewUDP() Service {
p: utils.NewConcurrentMap(),
}
}
func (s *UDP) CheckArgs() {
if *s.cfg.Parent == "" {
log.Fatalf("parent required for udp %s", *s.cfg.Local)
}
if *s.cfg.ParentType == "" {
log.Fatalf("parent type unkown,use -T <tls|tcp>")
}
if *s.cfg.ParentType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
}
func (s *UDP) InitService() {
if *s.cfg.ParentType != TYPE_UDP {
s.InitOutConnPool()
@ -39,12 +50,8 @@ func (s *UDP) StopService() {
}
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.CheckArgs()
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
@ -120,7 +127,7 @@ func (s *UDP) OutToTCP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err erro
}()
log.Printf("conn %d created , local: %s", connKey, srcAddr.String())
for {
srcAddrFromConn, body, err := utils.ReadUDPPacket(&conn)
srcAddrFromConn, body, err := utils.ReadUDPPacket(bufio.NewReader(conn))
if err == io.EOF || err == io.ErrUnexpectedEOF {
//log.Printf("connection %d released", connKey)
s.p.Remove(fmt.Sprintf("%d", connKey))
@ -200,7 +207,8 @@ func (s *UDP) InitOutConnPool() {
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutPool(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType == TYPE_TLS,
*s.cfg.ParentType,
"", "",
s.cfg.CertBytes, s.cfg.KeyBytes,
*s.cfg.Parent,
*s.cfg.Timeout,

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
}

View File

@ -3,107 +3,85 @@ package utils
import (
"bufio"
"bytes"
"crypto/sha1"
"crypto/tls"
"crypto/x509"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"net"
"net/http"
"os"
"os/exec"
"sync"
"runtime/debug"
"golang.org/x/crypto/pbkdf2"
"strconv"
"strings"
"time"
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{})) {
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)
})
}
if err != nil {
one.Do(func() {
fn(isSrcErr, err)
})
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("bind crashed %s", err)
}
}()
//_, err := io.Copy(src, dst)
err := ioCopy(src, dst)
e2 <- err
}()
var err interface{}
select {
case err = <-e1:
//log.Printf("e1")
case err = <-e2:
//log.Printf("e2")
}
src.Close()
dst.Close()
fn(err)
}()
}
func ioCopy(dst io.Writer, src io.Reader, fn ...func(count int)) (written int64, isSrcErr bool, err error) {
func ioCopy(dst io.ReadWriter, src io.ReadWriter) (err error) {
buf := make([]byte, 32*1024)
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) {
h := strings.Split(host, ":")
@ -146,6 +124,17 @@ func ConnectHost(hostAndPort string, timeout int) (conn net.Conn, err error) {
conn, err = net.DialTimeout("tcp", hostAndPort, time.Duration(timeout)*time.Millisecond)
return
}
func ConnectKCPHost(hostAndPort, method, key string) (conn net.Conn, err error) {
kcpconn, err := kcp.DialWithOptions(hostAndPort, GetKCPBlock(method, key), 10, 3)
if err != nil {
return
}
kcpconn.SetNoDelay(1, 10, 2, 1)
kcpconn.SetWindowSize(1024, 1024)
kcpconn.SetMtu(1400)
kcpconn.SetACKNoDelay(false)
return kcpconn, err
}
func ListenTls(ip string, port int, certBytes, keyBytes []byte) (ln *net.Listener, err error) {
var cert tls.Certificate
cert, err = tls.X509KeyPair(certBytes, keyBytes)
@ -195,6 +184,9 @@ 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()
@ -265,6 +257,7 @@ 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 +265,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 +279,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 +295,164 @@ 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 {
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 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) {
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 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) (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)
if err != nil {
return
}
defer resp.Body.Close()
code = resp.StatusCode
body, err = ioutil.ReadAll(resp.Body)
return
}
// type sockaddr struct {
// family uint16

View File

@ -5,6 +5,9 @@ import (
"log"
"net"
"runtime/debug"
"strconv"
kcp "github.com/xtaci/kcp-go"
)
type ServerChannel struct {
@ -24,6 +27,17 @@ func NewServerChannel(ip string, port int) ServerChannel {
},
}
}
func NewServerChannelHost(host string) ServerChannel {
h, port, _ := net.SplitHostPort(host)
p, _ := strconv.Atoi(port)
return ServerChannel{
ip: h,
port: p,
errAcceptHandler: func(err error) {
log.Printf("accept error , ERR:%s", err)
},
}
}
func (sc *ServerChannel) SetErrAcceptHandler(fn func(err error)) {
sc.errAcceptHandler = fn
}
@ -43,7 +57,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()))
log.Printf("tls connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(conn)
@ -77,7 +91,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()))
log.Printf("tcp connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(conn)
@ -124,3 +138,35 @@ func (sc *ServerChannel) ListenUDP(fn func(packet []byte, localAddr, srcAddr *ne
}
return
}
func (sc *ServerChannel) ListenKCP(method, key string, fn func(conn net.Conn)) (err error) {
var l net.Listener
l, err = kcp.ListenWithOptions(fmt.Sprintf("%s:%d", sc.ip, sc.port), GetKCPBlock(method, key), 10, 3)
if err == nil {
sc.Listener = &l
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("ListenKCP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
var conn net.Conn
conn, err = (*sc.Listener).Accept()
if err == nil {
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("kcp connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(conn)
}()
} else {
sc.errAcceptHandler(err)
break
}
}
}()
}
return
}

260
utils/socks/structs.go Normal file
View File

@ -0,0 +1,260 @@
package socks
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
"strconv"
)
const (
Method_NO_AUTH = uint8(0x00)
Method_GSSAPI = uint8(0x01)
Method_USER_PASS = uint8(0x02)
Method_IANA = uint8(0x7F)
Method_RESVERVE = uint8(0x80)
Method_NONE_ACCEPTABLE = uint8(0xFF)
VERSION_V5 = uint8(0x05)
CMD_CONNECT = uint8(0x01)
CMD_BIND = uint8(0x02)
CMD_ASSOCIATE = uint8(0x03)
ATYP_IPV4 = uint8(0x01)
ATYP_DOMAIN = uint8(0x03)
ATYP_IPV6 = uint8(0x04)
REP_SUCCESS = uint8(0x00)
REP_REQ_FAIL = uint8(0x01)
REP_RULE_FORBIDDEN = uint8(0x02)
REP_NETWOR_UNREACHABLE = uint8(0x03)
REP_HOST_UNREACHABLE = uint8(0x04)
REP_CONNECTION_REFUSED = uint8(0x05)
REP_TTL_TIMEOUT = uint8(0x06)
REP_CMD_UNSUPPORTED = uint8(0x07)
REP_ATYP_UNSUPPORTED = uint8(0x08)
REP_UNKNOWN = uint8(0x09)
RSV = uint8(0x00)
)
var (
ZERO_IP = []byte{0x00, 0x00, 0x00, 0x00}
ZERO_PORT = []byte{0x00, 0x00}
)
type Request struct {
ver uint8
cmd uint8
reserve uint8
addressType uint8
dstAddr string
dstPort string
dstHost string
bytes []byte
rw io.ReadWriter
}
func NewRequest(rw io.ReadWriter) (req Request, err interface{}) {
var b [1024]byte
var n int
req = Request{rw: rw}
n, err = rw.Read(b[:])
if err != nil {
err = fmt.Errorf("read req data fail,ERR: %s", err)
return
}
req.ver = uint8(b[0])
req.cmd = uint8(b[1])
req.reserve = uint8(b[2])
req.addressType = uint8(b[3])
if b[0] != 0x5 {
err = fmt.Errorf("sosck version supported")
req.TCPReply(REP_REQ_FAIL)
return
}
switch b[3] {
case 0x01: //IP V4
req.dstHost = net.IPv4(b[4], b[5], b[6], b[7]).String()
case 0x03: //域名
req.dstHost = string(b[5 : n-2]) //b[4]表示域名的长度
case 0x04: //IP V6
req.dstHost = net.IP{b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]}.String()
}
req.dstPort = strconv.Itoa(int(b[n-2])<<8 | int(b[n-1]))
req.dstAddr = net.JoinHostPort(req.dstHost, req.dstPort)
req.bytes = b[:n]
return
}
func (s *Request) Bytes() []byte {
return s.bytes
}
func (s *Request) Addr() string {
return s.dstAddr
}
func (s *Request) Host() string {
return s.dstHost
}
func (s *Request) Port() string {
return s.dstPort
}
func (s *Request) AType() uint8 {
return s.addressType
}
func (s *Request) CMD() uint8 {
return s.cmd
}
func (s *Request) TCPReply(rep uint8) (err error) {
_, err = s.rw.Write(s.NewReply(rep, "0.0.0.0:0"))
return
}
func (s *Request) UDPReply(rep uint8, addr string) (err error) {
_, err = s.rw.Write(s.NewReply(rep, addr))
return
}
func (s *Request) NewReply(rep uint8, addr string) []byte {
var response bytes.Buffer
host, port, _ := net.SplitHostPort(addr)
ip := net.ParseIP(host)
ipb := ip.To4()
atyp := ATYP_IPV4
ipv6 := ip.To16()
zeroiIPv6 := fmt.Sprintf("%d%d%d%d%d%d%d%d%d%d%d%d",
ipv6[0], ipv6[1], ipv6[2], ipv6[3],
ipv6[4], ipv6[5], ipv6[6], ipv6[7],
ipv6[8], ipv6[9], ipv6[10], ipv6[11],
)
if ipv6 != nil && "0000000000255255" != zeroiIPv6 {
atyp = ATYP_IPV6
ipb = ip.To16()
}
porti, _ := strconv.Atoi(port)
portb := make([]byte, 2)
binary.BigEndian.PutUint16(portb, uint16(porti))
// log.Printf("atyp : %v", atyp)
// log.Printf("ip : %v", []byte(ip))
response.WriteByte(VERSION_V5)
response.WriteByte(rep)
response.WriteByte(RSV)
response.WriteByte(atyp)
response.Write(ipb)
response.Write(portb)
return response.Bytes()
}
type MethodsRequest struct {
ver uint8
methodsCount uint8
methods []uint8
bytes []byte
rw *io.ReadWriter
}
func NewMethodsRequest(r io.ReadWriter) (s MethodsRequest, err interface{}) {
defer func() {
if err == nil {
err = recover()
}
}()
s = MethodsRequest{}
s.rw = &r
var buf = make([]byte, 300)
var n int
n, err = r.Read(buf)
if err != nil {
return
}
if buf[0] != 0x05 {
err = fmt.Errorf("socks version not supported")
return
}
if n != int(buf[1])+int(2) {
err = fmt.Errorf("socks methods data length error")
return
}
s.ver = buf[0]
s.methodsCount = buf[1]
s.methods = buf[2:n]
s.bytes = buf[:n]
return
}
func (s *MethodsRequest) Version() uint8 {
return s.ver
}
func (s *MethodsRequest) MethodsCount() uint8 {
return s.methodsCount
}
func (s *MethodsRequest) Select(method uint8) bool {
for _, m := range s.methods {
if m == method {
return true
}
}
return false
}
func (s *MethodsRequest) Reply(method uint8) (err error) {
_, err = (*s.rw).Write([]byte{byte(VERSION_V5), byte(method)})
return
}
func (s *MethodsRequest) Bytes() []byte {
return s.bytes
}
type UDPPacket struct {
rsv uint16
frag uint8
atype uint8
dstHost string
dstPort string
data []byte
header []byte
bytes []byte
}
func ParseUDPPacket(b []byte) (p UDPPacket, err error) {
p = UDPPacket{}
p.frag = uint8(b[2])
p.bytes = b
if p.frag != 0 {
err = fmt.Errorf("FRAG only support for 0 , %v ,%v", p.frag, b[:4])
return
}
portIndex := 0
p.atype = b[3]
switch p.atype {
case ATYP_IPV4: //IP V4
p.dstHost = net.IPv4(b[4], b[5], b[6], b[7]).String()
portIndex = 8
case ATYP_DOMAIN: //域名
domainLen := uint8(b[4])
p.dstHost = string(b[5 : 5+domainLen]) //b[4]表示域名的长度
portIndex = int(5 + domainLen)
case ATYP_IPV6: //IP V6
p.dstHost = net.IP{b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]}.String()
portIndex = 20
}
p.dstPort = strconv.Itoa(int(b[portIndex])<<8 | int(b[portIndex+1]))
p.data = b[portIndex+2:]
p.header = b[:portIndex+2]
return
}
func (s *UDPPacket) Header() []byte {
return s.header
}
func (s *UDPPacket) NewReply(data []byte) []byte {
var buf bytes.Buffer
buf.Write(s.header)
buf.Write(data)
return buf.Bytes()
}
func (s *UDPPacket) Host() string {
return s.dstHost
}
func (s *UDPPacket) Port() string {
return s.dstPort
}
func (s *UDPPacket) Data() []byte {
return s.data
}

View File

@ -4,6 +4,7 @@ import (
"bytes"
"crypto/tls"
"encoding/base64"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
@ -11,6 +12,7 @@ import (
"net"
"net/url"
"strings"
"sync"
"time"
)
@ -174,7 +176,11 @@ func (c *Checker) Add(address string, isHTTPS bool, method, URL string, data []b
}
type BasicAuth struct {
data ConcurrentMap
data ConcurrentMap
authURL string
authOkCode int
authTimeout int
authRetry int
}
func NewBasicAuth() BasicAuth {
@ -182,6 +188,12 @@ func NewBasicAuth() BasicAuth {
data: NewConcurrentMap(),
}
}
func (ba *BasicAuth) SetAuthURL(URL string, code, timeout, retry int) {
ba.authURL = URL
ba.authOkCode = code
ba.authTimeout = timeout
ba.authRetry = retry
}
func (ba *BasicAuth) AddFromFile(file string) (n int, err error) {
_content, err := ioutil.ReadFile(file)
if err != nil {
@ -211,16 +223,69 @@ func (ba *BasicAuth) Add(userpassArr []string) (n int) {
}
return
}
func (ba *BasicAuth) CheckUserPass(user, pass, ip, target string) (ok bool) {
func (ba *BasicAuth) Check(userpass string) (ok bool) {
return ba.Check(user+":"+pass, ip, target)
}
func (ba *BasicAuth) Check(userpass string, ip, target string) (ok bool) {
u := strings.Split(strings.Trim(userpass, " "), ":")
if len(u) == 2 {
if p, _ok := ba.data.Get(u[0]); _ok {
return p.(string) == u[1]
}
if ba.authURL != "" {
err := ba.checkFromURL(userpass, ip, target)
if err == nil {
return true
}
log.Printf("%s", err)
}
return false
}
return
}
func (ba *BasicAuth) checkFromURL(userpass, ip, target string) (err error) {
u := strings.Split(strings.Trim(userpass, " "), ":")
if len(u) != 2 {
return
}
URL := ba.authURL
if strings.Contains(URL, "?") {
URL += "&"
} else {
URL += "?"
}
URL += fmt.Sprintf("user=%s&pass=%s&ip=%s&target=%s", u[0], u[1], ip, target)
var code int
var tryCount = 0
var body []byte
for tryCount <= ba.authRetry {
body, code, err = HttpGet(URL, ba.authTimeout)
if err == nil && code == ba.authOkCode {
break
} else if err != nil {
err = fmt.Errorf("auth fail from url %s,resonse err:%s , %s", URL, err, ip)
} else {
if len(body) > 0 {
err = fmt.Errorf(string(body[0:100]))
} else {
err = fmt.Errorf("token error")
}
err = fmt.Errorf("auth fail from url %s,resonse code: %d, except: %d , %s , %s", URL, code, ba.authOkCode, ip, string(body))
}
if err != nil && tryCount < ba.authRetry {
log.Print(err)
time.Sleep(time.Second * 2)
}
tryCount++
}
if err != nil {
return
}
//log.Printf("auth success from auth url, %s", ip)
return
}
func (ba *BasicAuth) Total() (n int) {
n = ba.data.Count()
return
@ -254,13 +319,13 @@ func NewHTTPRequest(inConn *net.Conn, bufSize int, isBasicAuth bool, basicAuth *
req.HeadBuf = buf[:len]
index := bytes.IndexByte(req.HeadBuf, '\n')
if index == -1 {
err = fmt.Errorf("http decoder data line err:%s", string(req.HeadBuf)[:50])
err = fmt.Errorf("http decoder data line err:%s", SubStr(string(req.HeadBuf), 0, 50))
CloseConn(inConn)
return
}
fmt.Sscanf(string(req.HeadBuf[:index]), "%s%s", &req.Method, &req.hostOrURL)
if req.Method == "" || req.hostOrURL == "" {
err = fmt.Errorf("http decoder data err:%s", string(req.HeadBuf)[:50])
err = fmt.Errorf("http decoder data err:%s", SubStr(string(req.HeadBuf), 0, 50))
CloseConn(inConn)
return
}
@ -285,7 +350,11 @@ func (req *HTTPRequest) HTTP() (err error) {
}
req.URL, err = req.getHTTPURL()
if err == nil {
u, _ := url.Parse(req.URL)
var u *url.URL
u, err = url.Parse(req.URL)
if err != nil {
return
}
req.Host = u.Host
req.addPortIfNot()
}
@ -314,6 +383,14 @@ func (req *HTTPRequest) BasicAuth() (err error) {
CloseConn(req.conn)
return
}
if authorization == "" {
authorization, err = req.getHeader("Proxy-Authorization")
if err != nil {
fmt.Fprint((*req.conn), "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"\"\r\n\r\nUnauthorized")
CloseConn(req.conn)
return
}
}
//log.Printf("Authorization:%s", authorization)
basic := strings.Fields(authorization)
if len(basic) != 2 {
@ -327,7 +404,14 @@ func (req *HTTPRequest) BasicAuth() (err error) {
CloseConn(req.conn)
return
}
authOk := (*req.basicAuth).Check(string(user))
addr := strings.Split((*req.conn).RemoteAddr().String(), ":")
URL := ""
if req.IsHTTPS() {
URL = "https://" + req.Host
} else {
URL, _ = req.getHTTPURL()
}
authOk := (*req.basicAuth).Check(string(user), addr[0], URL)
//log.Printf("auth %s,%v", string(user), authOk)
if !authOk {
fmt.Fprint((*req.conn), "HTTP/1.1 401 Unauthorized\r\n\r\nUnauthorized")
@ -351,6 +435,7 @@ func (req *HTTPRequest) getHTTPURL() (URL string, err error) {
func (req *HTTPRequest) getHeader(key string) (val string, err error) {
key = strings.ToUpper(key)
lines := strings.Split(string(req.HeadBuf), "\r\n")
//log.Println(lines)
for _, line := range lines {
line := strings.SplitN(strings.Trim(line, "\r\n "), ":", 2)
if len(line) == 2 {
@ -362,7 +447,6 @@ func (req *HTTPRequest) getHeader(key string) (val string, err error) {
}
}
}
err = fmt.Errorf("can not find HOST header")
return
}
@ -383,19 +467,23 @@ func (req *HTTPRequest) addPortIfNot() (newHost string) {
type OutPool struct {
Pool ConnPool
dur int
isTLS bool
typ string
certBytes []byte
keyBytes []byte
kcpMethod string
kcpKey string
address string
timeout int
}
func NewOutPool(dur int, isTLS bool, certBytes, keyBytes []byte, address string, timeout int, InitialCap int, MaxCap int) (op OutPool) {
func NewOutPool(dur int, typ, kcpMethod, kcpKey string, certBytes, keyBytes []byte, address string, timeout int, InitialCap int, MaxCap int) (op OutPool) {
op = OutPool{
dur: dur,
isTLS: isTLS,
typ: typ,
certBytes: certBytes,
keyBytes: keyBytes,
kcpMethod: kcpMethod,
kcpKey: kcpKey,
address: address,
timeout: timeout,
}
@ -429,12 +517,14 @@ func NewOutPool(dur int, isTLS bool, certBytes, keyBytes []byte, address string,
return
}
func (op *OutPool) getConn() (conn interface{}, err error) {
if op.isTLS {
if op.typ == "tls" {
var _conn tls.Conn
_conn, err = TlsConnectHost(op.address, op.timeout, op.certBytes, op.keyBytes)
if err == nil {
conn = net.Conn(&_conn)
}
} else if op.typ == "kcp" {
conn, err = ConnectKCPHost(op.address, op.kcpMethod, op.kcpKey)
} else {
conn, err = ConnectHost(op.address, op.timeout)
}
@ -460,3 +550,214 @@ func (op *OutPool) initPoolDeamon() {
}
}()
}
type HeartbeatData struct {
Data []byte
N int
Error error
}
type HeartbeatReadWriter struct {
conn *net.Conn
// rchn chan HeartbeatData
l *sync.Mutex
dur int
errHandler func(err error, hb *HeartbeatReadWriter)
once *sync.Once
datachn chan byte
// rbuf bytes.Buffer
// signal chan bool
rerrchn chan error
}
func NewHeartbeatReadWriter(conn *net.Conn, dur int, fn func(err error, hb *HeartbeatReadWriter)) (hrw HeartbeatReadWriter) {
hrw = HeartbeatReadWriter{
conn: conn,
l: &sync.Mutex{},
dur: dur,
// rchn: make(chan HeartbeatData, 10000),
// signal: make(chan bool, 1),
errHandler: fn,
datachn: make(chan byte, 4*1024),
once: &sync.Once{},
rerrchn: make(chan error, 1),
// rbuf: bytes.Buffer{},
}
hrw.heartbeat()
hrw.reader()
return
}
func (rw *HeartbeatReadWriter) Close() {
CloseConn(rw.conn)
}
func (rw *HeartbeatReadWriter) reader() {
go func() {
//log.Printf("heartbeat read started")
for {
n, data, err := rw.read()
if n == -1 {
continue
}
//log.Printf("n:%d , data:%s ,err:%s", n, string(data), err)
if err == nil {
//fmt.Printf("write data %s\n", string(data))
for _, b := range data {
rw.datachn <- b
}
}
if err != nil {
//log.Printf("heartbeat reader err: %s", err)
select {
case rw.rerrchn <- err:
default:
}
rw.once.Do(func() {
rw.errHandler(err, rw)
})
break
}
}
//log.Printf("heartbeat read exited")
}()
}
func (rw *HeartbeatReadWriter) read() (n int, data []byte, err error) {
var typ uint8
err = binary.Read((*rw.conn), binary.LittleEndian, &typ)
if err != nil {
return
}
if typ == 0 {
// log.Printf("heartbeat revecived")
n = -1
return
}
var dataLength uint32
binary.Read((*rw.conn), binary.LittleEndian, &dataLength)
_data := make([]byte, dataLength)
// log.Printf("dataLength:%d , data:%s", dataLength, string(data))
n, err = (*rw.conn).Read(_data)
//log.Printf("n:%d , data:%s ,err:%s", n, string(data), err)
if err != nil {
return
}
if uint32(n) != dataLength {
err = fmt.Errorf("read short data body")
return
}
data = _data[:n]
return
}
func (rw *HeartbeatReadWriter) heartbeat() {
go func() {
//log.Printf("heartbeat started")
for {
if rw.conn == nil || *rw.conn == nil {
//log.Printf("heartbeat err: conn nil")
break
}
rw.l.Lock()
_, err := (*rw.conn).Write([]byte{0})
rw.l.Unlock()
if err != nil {
//log.Printf("heartbeat err: %s", err)
rw.once.Do(func() {
rw.errHandler(err, rw)
})
break
} else {
// log.Printf("heartbeat send ok")
}
time.Sleep(time.Second * time.Duration(rw.dur))
}
//log.Printf("heartbeat exited")
}()
}
func (rw *HeartbeatReadWriter) Read(p []byte) (n int, err error) {
data := make([]byte, cap(p))
for i := 0; i < cap(p); i++ {
data[i] = <-rw.datachn
n++
//fmt.Printf("read %d %v\n", i, data[:n])
if len(rw.datachn) == 0 {
n = i + 1
copy(p, data[:n])
return
}
}
return
}
func (rw *HeartbeatReadWriter) Write(p []byte) (n int, err error) {
defer rw.l.Unlock()
rw.l.Lock()
pkg := new(bytes.Buffer)
binary.Write(pkg, binary.LittleEndian, uint8(1))
binary.Write(pkg, binary.LittleEndian, uint32(len(p)))
binary.Write(pkg, binary.LittleEndian, p)
bs := pkg.Bytes()
n, err = (*rw.conn).Write(bs)
if err == nil {
n = len(p)
}
return
}
type ConnManager struct {
pool ConcurrentMap
l *sync.Mutex
}
func NewConnManager() ConnManager {
cm := ConnManager{
pool: NewConcurrentMap(),
l: &sync.Mutex{},
}
return cm
}
func (cm *ConnManager) Add(key, ID string, conn *net.Conn) {
cm.pool.Upsert(key, nil, func(exist bool, valueInMap interface{}, newValue interface{}) interface{} {
var conns ConcurrentMap
if !exist {
conns = NewConcurrentMap()
} else {
conns = valueInMap.(ConcurrentMap)
}
if conns.Has(ID) {
v, _ := conns.Get(ID)
(*v.(*net.Conn)).Close()
}
conns.Set(ID, conn)
log.Printf("%s conn added", key)
return conns
})
}
func (cm *ConnManager) Remove(key string) {
var conns ConcurrentMap
if v, ok := cm.pool.Get(key); ok {
conns = v.(ConcurrentMap)
conns.IterCb(func(key string, v interface{}) {
CloseConn(v.(*net.Conn))
})
log.Printf("%s conns closed", key)
}
cm.pool.Remove(key)
}
func (cm *ConnManager) RemoveOne(key string, ID string) {
defer cm.l.Unlock()
cm.l.Lock()
var conns ConcurrentMap
if v, ok := cm.pool.Get(key); ok {
conns = v.(ConcurrentMap)
if conns.Has(ID) {
v, _ := conns.Get(ID)
(*v.(*net.Conn)).Close()
conns.Remove(ID)
cm.pool.Set(key, conns)
log.Printf("%s %s conn closed", key, ID)
}
}
}
func (cm *ConnManager) RemoveAll() {
for _, k := range cm.pool.Keys() {
cm.Remove(k)
}
}