36 Commits
v3.0 ... v3.2

Author SHA1 Message Date
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
27 changed files with 1270 additions and 397 deletions

View File

@ -1,8 +1,23 @@
proxy更新日志: proxy更新日志
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 v3.0
1.此次更新不兼容2.x版本,重构了全部代码,架构更合理,利于功能模块的增加与维护。 1.此次更新不兼容2.x版本,重构了全部代码,架构更合理,利于功能模块的增加与维护。
2.增加了代理死循环检查,增强了安全性。 2.增加了代理死循环检查,增强了安全性。
3.增加了反向代理模式(即:内网穿透),支持TCP和UDP两种协议,可以把任何局域网的机器A所在网络的任何端口 3.增加了反向代理模式(即:内网穿透),支持TCP和UDP两种协议,可以把任何局域网的机器A所在网络的任何端
暴露到任何局域网的机器B的本地端口或暴露到任何公网VPS上。 暴露到任何局域网的机器B的本地端口或暴露到任何公网VPS上。
4.正向代理增加了UDP模式支持。 4.正向代理增加了UDP模式支持。
@ -21,7 +36,7 @@ v2.1
v2.0 v2.0
1.增加了连接池功能,大幅提高了通过上级代理访问的速度。 1.增加了连接池功能,大幅提高了通过上级代理访问的速度。
2.HTTP代理模式,优化了请求URL的获取逻辑,可以支持:http,https,websocket 2.HTTP代理模式,优化了请求URL的获取逻辑,可以支持:http,https,websocke
3.增加了TCP代理模式,支持是否加密通讯。 3.增加了TCP代理模式,支持是否加密通讯。
4.优化了链接关闭逻辑,避免出现大量CLOSE_WAIT。 4.优化了链接关闭逻辑,避免出现大量CLOSE_WAIT。
5.增加了黑白名单机制,更自由快速的访问。 5.增加了黑白名单机制,更自由快速的访问。

185
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"/> <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代理服务器,支持正向代理和内网穿透.
--- ---
@ -11,8 +11,9 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp代理服务器,支
- 智能HTTP代理,会自动判断访问的网站是否屏蔽,如果被屏蔽那么就会使用上级代理(前提是配置了上级代理)访问网站;如果访问的网站没有被屏蔽,为了加速访问,代理会直接访问网站,不使用上级代理. - 智能HTTP代理,会自动判断访问的网站是否屏蔽,如果被屏蔽那么就会使用上级代理(前提是配置了上级代理)访问网站;如果访问的网站没有被屏蔽,为了加速访问,代理会直接访问网站,不使用上级代理.
- 域名黑白名单,更加自由的控制网站的访问方式。 - 域名黑白名单,更加自由的控制网站的访问方式。
- 跨平台性,无论你是widows,linux,还是mac,甚至是树莓派,都可以很好的运行proxy. - 跨平台性,无论你是widows,linux,还是mac,甚至是树莓派,都可以很好的运行proxy.
- 多协议支持,支持HTTP,TCP,UDP,Websocket代理. - 多协议支持,支持HTTP(S),TCP,UDP,Websocket,SOCKS5代理.
- 支持反向代理也就是"内网穿透",协议支持TCP和UDP. - 支持内网穿透,协议支持TCP和UDP.
- HTTP(S),SOCKS5代理支持SSH中转,上级Linux服务器不需要任何服务端,本地一个proxy即可开心上网.
### Why need these? ### Why need these?
- 当由于安全因素或者限制,我们不能顺畅的访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道,顺畅的访问我们的服务. - 当由于安全因素或者限制,我们不能顺畅的访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道,顺畅的访问我们的服务.
@ -20,7 +21,14 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp代理服务器,支
- 远程访问内网机器. - 远程访问内网机器.
- 和小伙伴一起玩局域网游戏. - 和小伙伴一起玩局域网游戏.
- 以前只能在局域网玩的,现在可以在任何地方玩. - 以前只能在局域网玩的,现在可以在任何地方玩.
- ... - 替代圣剑内网通显IP内网通花生壳之类的工具.
- ...  
### 手册目录
本页是最新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)
### Fast Start ### Fast Start
提示:所有操作需要root权限. 提示:所有操作需要root权限.
@ -44,7 +52,7 @@ wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_li
下载地址:https://github.com/snail007/goproxy/releases 下载地址:https://github.com/snail007/goproxy/releases
```shell ```shell
cd /root/proxy/ 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.1fix/proxy-linux-amd64.tar.gz
``` ```
**3.下载自动安装脚本** **3.下载自动安装脚本**
```shell ```shell
@ -57,11 +65,20 @@ chmod +x install.sh
## 使用教程 ## 使用教程
**提示** **提示**
本教程是v3版本,不兼容v2版本,v2版本教程请移驾:[v2教程](https://github.com/snail007/goproxy/tree/v2.2)
接下来的教程,默认系统是linux,程序是proxy所有操作需要root权限 接下来的教程,默认系统是linux,程序是proxy所有操作需要root权限
如果你的是windows,请使用windows版本的proxy.exe即可. 如果你的是windows,请使用windows版本的proxy.exe即可.
**使用配置文件**
接下来的教程都是通过命令行参数介绍使用方法,也可以通过读取配置文件获取参数.
具体格式是通过@符号指定配置文件,例如:./proxy @configfile.txt
configfile.txt里面的格式是,第一行是子命令名称,第二行开始一行一个:参数的长格式=参数值,前后不能有空格和双引号.
参数的长格式都是--开头的,短格式参数都是-开头,如果你不知道某个短格式参数对应长格式参数,查看帮助命令即可.
比如configfile.txt内容如下:
```shell
http
--local-type=tcp
--local=:33080
```
### 0.生成加密通讯需要的证书文件 ### 0.生成加密通讯需要的证书文件
http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,当然可以选择不加密通信通讯,本教程所有和上级通讯都采用加密,需要证书文件. http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,当然可以选择不加密通信通讯,本教程所有和上级通讯都采用加密,需要证书文件.
@ -76,8 +93,9 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,
**1.2.普通二级HTTP代理** **1.2.普通二级HTTP代理**
使用本地端口8090,假设上级HTTP代理是`22.22.22.22:8080` 使用本地端口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" ` `./proxy http -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" `
默认开启了连接池,如果为了网络情况很好,-L可以关闭连接池,0就是连接池大小,0为关闭. 默认关闭了连接池,如果要加快访问速度,-L可以开启连接池,10就是连接池大小,0为关闭,
`./proxy http -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -L 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,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理. 我们还可以指定网站域名的黑白名单文件,一行一个域名,怕匹配规则是最右批评匹配,比如: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` `./proxy http -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt`
@ -114,7 +132,20 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,
默认情况下,proxy会智能判断一个网站域名是否无法访问,如果无法访问才走上级HTTP代理.通过--always可以使全部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` `./proxy http --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
**1.7.查看帮助** **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.查看帮助**
`./proxy help http` `./proxy help http`
@ -202,7 +233,7 @@ VPS(IP:22.22.22.33)执行:
**3.6.查看帮助** **3.6.查看帮助**
`./proxy help udp` `./proxy help udp`
### 4.反向代理(内网穿透) ### 4.内网穿透
**4.1、原理说明** **4.1、原理说明**
内网穿透,由三部分组成:client端,server端,bridge端client和server主动连接bridge端进行桥接. 内网穿透,由三部分组成:client端,server端,bridge端client和server主动连接bridge端进行桥接.
当用户访问server端,流程是: 当用户访问server端,流程是:
@ -216,6 +247,7 @@ VPS(IP:22.22.22.33)执行:
背景: 背景:
- 公司机器A提供了web服务80端口 - 公司机器A提供了web服务80端口
- 有VPS一个,公网IP:22.22.22.22 - 有VPS一个,公网IP:22.22.22.22
- 本用法典型案例就是微信接口本地开发  
需求: 需求:
在家里能够通过访问VPS的28080端口访问到公司机器A的80端口 在家里能够通过访问VPS的28080端口访问到公司机器A的80端口
@ -223,14 +255,36 @@ VPS(IP:22.22.22.33)执行:
步骤: 步骤:
1. 在vps上执行 1. 在vps上执行
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key` `./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上面执行 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. 完成 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端口 - 公司机器A提供了DNS解析服务,UDP:53端口
- 有VPS一个,公网IP:22.22.22.22 - 有VPS一个,公网IP:22.22.22.22
@ -241,14 +295,14 @@ VPS(IP:22.22.22.33)执行:
步骤: 步骤:
1. 在vps上执行 1. 在vps上执行
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key` `./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上面执行 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. 完成 1. 完成
**4.4、高级用法一** **4.5、高级用法一**
背景: 背景:
- 公司机器A提供了web服务80端口 - 公司机器A提供了web服务80端口
- 有VPS一个,公网IP:22.22.22.22 - 有VPS一个,公网IP:22.22.22.22
@ -262,17 +316,19 @@ VPS(IP:22.22.22.33)执行:
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key` `./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key`
1. 在公司机器A上面执行 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. 在家里电脑上执行 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. 完成 1. 完成
**4.5、高级用法二** **4.6、高级用法二**
提示: 提示:
一个client和一个server是一对,如果要暴露多个端口,需要使用--k参数进行分组, 如果同时有多个client连接到同一个bridge,需要指定不同的key,可以通过--k参数设定,--k可以是任意唯一字符串,
--k可以是任意唯一字符串,只要多个端口使用的不一样即可. 只要在同一个bridge上唯一即可.
server连接到bridge的时候,如果同时有多个client连接到同一个bridge,需要使用--k参数选择client.
暴露多个端口重复-r参数即可.-r格式是:"本地IP:本地端口@clientHOST:client端口".
背景: 背景:
- 公司机器A提供了web服务80端口,ftp服务21端口 - 公司机器A提供了web服务80端口,ftp服务21端口
@ -285,23 +341,96 @@ VPS(IP:22.22.22.33)执行:
步骤: 步骤:
1. 在vps上执行 1. 在vps上执行
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key` `./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 -r ":28080@:80" -r ":29090@:21" --k test -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`
1. 在公司机器A上面执行 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 --k test -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`
1. 完成 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 tbridge`
`./proxy help tserver` `./proxy help tserver`
`./proxy help tserver` `./proxy help tserver`
### 5.SOCKS5代理
提示:SOCKS5代理,只支持TCP协议,不支持UDP协议,不支持用户名密码认证.
**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.查看帮助**
`./proxy help socks`
### TODO ### TODO
- UDP Over TCP,通过tcp代理udp协议. - SOCKS5增加用户名密码认证
### 如何使用源码?
cd进入你的go src目录,然后git clone https://github.com/snail007/goproxy.git ./proxy 即可.
编译直接:go build
运行: go run *.go
utils是工具包,service是具体的每个服务类.
### License ### License
Proxy is licensed under GPLv3 license. Proxy is licensed under GPLv3 license.
### Contact ### Contact

View File

@ -2,7 +2,6 @@ package main
import ( import (
"fmt" "fmt"
"io/ioutil"
"log" "log"
"os" "os"
"proxy/services" "proxy/services"
@ -24,7 +23,7 @@ func initConfig() (err error) {
os.Exit(0) os.Exit(0)
} }
} }
args := services.Args{}
//define args //define args
tcpArgs := services.TCPArgs{} tcpArgs := services.TCPArgs{}
httpArgs := services.HTTPArgs{} httpArgs := services.HTTPArgs{}
@ -32,19 +31,18 @@ func initConfig() (err error) {
tunnelClientArgs := services.TunnelClientArgs{} tunnelClientArgs := services.TunnelClientArgs{}
tunnelBridgeArgs := services.TunnelBridgeArgs{} tunnelBridgeArgs := services.TunnelBridgeArgs{}
udpArgs := services.UDPArgs{} udpArgs := services.UDPArgs{}
socksArgs := services.SocksArgs{}
//build srvice args //build srvice args
app = kingpin.New("proxy", "happy with proxy") app = kingpin.New("proxy", "happy with proxy")
app.Author("snail").Version(APP_VERSION) app.Author("snail").Version(APP_VERSION)
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()
//########http######### //########http#########
http := app.Command("http", "proxy on http mode") 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.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
httpArgs.ParentType = http.Flag("parent-type", "parent protocol type <tls|tcp>").Short('T').Enum("tls", "tcp") 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>").Default("tcp").Short('t').Enum("tls", "tcp")
httpArgs.ParentType = http.Flag("parent-type", "parent protocol type <tls|tcp|ssh>").Short('T').Enum("tls", "tcp", "ssh")
httpArgs.Always = http.Flag("always", "always use parent proxy").Default("false").Bool() 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.Timeout = http.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("2000").Int()
httpArgs.HTTPTimeout = http.Flag("http-timeout", "check domain if blocked , http request timeout milliseconds when connect to host").Default("3000").Int() httpArgs.HTTPTimeout = http.Flag("http-timeout", "check domain if blocked , http request timeout milliseconds when connect to host").Default("3000").Int()
@ -53,66 +51,94 @@ func initConfig() (err error) {
httpArgs.Direct = http.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String() httpArgs.Direct = http.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
httpArgs.AuthFile = http.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String() httpArgs.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.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.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()
//########tcp######### //########tcp#########
tcp := app.Command("tcp", "proxy on tcp mode") tcp := app.Command("tcp", "proxy on tcp mode")
tcpArgs.Parent = tcp.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
tcpArgs.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('t').Default("2000").Int() 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.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.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.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.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
//########udp######### //########udp#########
udp := app.Command("udp", "proxy on udp mode") 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.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.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.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######### //########tunnel-server#########
tunnelServer := app.Command("tserver", "proxy on tunnel server mode") 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.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.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()
//########tunnel-client######### //########tunnel-client#########
tunnelClient := app.Command("tclient", "proxy on tunnel client mode") 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.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.Key = tunnelClient.Flag("k", "key same with server").Default("default").String()
//########tunnel-bridge######### //########tunnel-bridge#########
tunnelBridge := app.Command("tbridge", "proxy on tunnel bridge mode") 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.Timeout = tunnelBridge.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
tunnelBridgeArgs.Local = tunnelBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
kingpin.MustParse(app.Parse(os.Args[1:])) //########ssh#########
socks := app.Command("socks", "proxy on ssh mode")
if *certTLS != "" && *keyTLS != "" { socksArgs.Parent = socks.Flag("parent", "parent ssh address, such as: \"23.32.32.19:22\"").Default("").Short('P').String()
args.CertBytes, args.KeyBytes = tlsBytes(*certTLS, *keyTLS) socksArgs.ParentType = socks.Flag("parent-type", "parent protocol type <tls|tcp|ssh>").Default("tcp").Short('T').Enum("tls", "tcp", "ssh")
} socksArgs.LocalType = socks.Flag("local-type", "local protocol type <tls|tcp>").Default("tcp").Short('t').Enum("tls", "tcp")
socksArgs.Local = socks.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
//common args socksArgs.CertFile = socks.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
httpArgs.Args = args socksArgs.KeyFile = socks.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tcpArgs.Args = args socksArgs.SSHUser = socks.Flag("ssh-user", "user for ssh").Short('u').Default("").String()
udpArgs.Args = args socksArgs.SSHKeyFile = socks.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String()
tunnelBridgeArgs.Args = args socksArgs.SSHKeyFileSalt = socks.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String()
tunnelClientArgs.Args = args socksArgs.SSHPassword = socks.Flag("ssh-password", "password for ssh").Short('A').Default("").String()
tunnelServerArgs.Args = args 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()
//parse args
serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
poster() poster()
//regist services and run service //regist services and run service
serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
services.Regist("http", services.NewHTTP(), httpArgs) services.Regist("http", services.NewHTTP(), httpArgs)
services.Regist("tcp", services.NewTCP(), tcpArgs) services.Regist("tcp", services.NewTCP(), tcpArgs)
services.Regist("udp", services.NewUDP(), udpArgs) 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("tclient", services.NewTunnelClient(), tunnelClientArgs)
services.Regist("tbridge", services.NewTunnelBridge(), tunnelBridgeArgs) services.Regist("tbridge", services.NewTunnelBridge(), tunnelBridgeArgs)
services.Regist("socks", services.NewSocks(), socksArgs)
service, err = services.Run(serviceName) service, err = services.Run(serviceName)
if err != nil { if err != nil {
log.Fatalf("run service [%s] fail, ERR:%s", service, err) log.Fatalf("run service [%s] fail, ERR:%s", serviceName, err)
} }
return return
} }
@ -129,16 +155,3 @@ func poster() {
v%s`+" by snail , blog : http://www.host900.com/\n\n", APP_VERSION) v%s`+" by snail , blog : http://www.host900.com/\n\n", APP_VERSION)
} }
func 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 #!/bin/bash
set -e set -e
if [ -e /tmp/proxy ]; then
rm -rf /tmp/proxy
fi
mkdir /tmp/proxy
cd /tmp/proxy
# install monexec # install monexec
tar zxvf monexec_0.1.1_linux_amd64.tar.gz tar zxvf monexec_0.1.1_linux_amd64.tar.gz
cd monexec_0.1.1_linux_amd64 cd monexec_0.1.1_linux_amd64

View File

@ -6,7 +6,7 @@ fi
mkdir /tmp/proxy mkdir /tmp/proxy
cd /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/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.1fix/proxy-linux-amd64.tar.gz
# install monexec # install monexec
tar zxvf monexec_0.1.1_linux_amd64.tar.gz tar zxvf monexec_0.1.1_linux_amd64.tar.gz

View File

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

View File

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
VER="3.0" VER="3.2"
RELEASE="release-${VER}" RELEASE="release-${VER}"
rm -rf .cert rm -rf .cert
mkdir .cert mkdir .cert

View File

@ -1,5 +1,7 @@
package services package services
import "golang.org/x/crypto/ssh"
// tcp := app.Command("tcp", "proxy on tcp mode") // 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() // t := tcp.Flag("tcp-timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("2000").Int()
@ -13,30 +15,44 @@ const (
CONN_CLIENT = uint8(3) CONN_CLIENT = uint8(3)
) )
type Args struct { type TunnelServerArgs struct {
Local *string
Parent *string Parent *string
CertFile *string
KeyFile *string
CertBytes []byte CertBytes []byte
KeyBytes []byte KeyBytes []byte
} Local *string
type TunnelServerArgs struct { IsUDP *bool
Args Key *string
IsUDP *bool Remote *string
Key *string Timeout *int
Timeout *int Route *[]string
} }
type TunnelClientArgs struct { type TunnelClientArgs struct {
Args Parent *string
IsUDP *bool CertFile *string
Key *string KeyFile *string
Timeout *int CertBytes []byte
KeyBytes []byte
Key *string
Timeout *int
} }
type TunnelBridgeArgs struct { type TunnelBridgeArgs struct {
Args Parent *string
Timeout *int CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
Timeout *int
} }
type TCPArgs struct { type TCPArgs struct {
Args Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
ParentType *string ParentType *string
IsTLS *bool IsTLS *bool
Timeout *int Timeout *int
@ -45,7 +61,12 @@ type TCPArgs struct {
} }
type HTTPArgs struct { type HTTPArgs struct {
Args Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
Always *bool Always *bool
HTTPTimeout *int HTTPTimeout *int
Interval *int Interval *int
@ -58,14 +79,46 @@ type HTTPArgs struct {
Timeout *int Timeout *int
PoolSize *int PoolSize *int
CheckParentInterval *int CheckParentInterval *int
SSHKeyFile *string
SSHKeyFileSalt *string
SSHPassword *string
SSHUser *string
SSHKeyBytes []byte
SSHAuthMethod ssh.AuthMethod
} }
type UDPArgs struct { type UDPArgs struct {
Args Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
ParentType *string ParentType *string
Timeout *int Timeout *int
PoolSize *int PoolSize *int
CheckParentInterval *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
}
func (a *TCPArgs) Protocol() string { func (a *TCPArgs) Protocol() string {
if *a.IsTLS { if *a.IsTLS {

View File

@ -3,11 +3,15 @@ package services
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"net" "net"
"proxy/utils" "proxy/utils"
"runtime/debug" "runtime/debug"
"strconv" "strconv"
"time"
"golang.org/x/crypto/ssh"
) )
type HTTP struct { type HTTP struct {
@ -15,6 +19,7 @@ type HTTP struct {
cfg HTTPArgs cfg HTTPArgs
checker utils.Checker checker utils.Checker
basicAuth utils.BasicAuth basicAuth utils.BasicAuth
sshClient *ssh.Client
} }
func NewHTTP() Service { func NewHTTP() Service {
@ -25,11 +30,53 @@ func NewHTTP() Service {
basicAuth: utils.BasicAuth{}, basicAuth: utils.BasicAuth{},
} }
} }
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() { func (s *HTTP) InitService() {
s.InitBasicAuth() s.InitBasicAuth()
if *s.cfg.Parent != "" { if *s.cfg.Parent != "" {
s.checker = utils.NewChecker(*s.cfg.HTTPTimeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct) 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)
}
}
} }
func (s *HTTP) StopService() { func (s *HTTP) StopService() {
if s.outPool.Pool != nil { if s.outPool.Pool != nil {
@ -38,13 +85,12 @@ func (s *HTTP) StopService() {
} }
func (s *HTTP) Start(args interface{}) (err error) { func (s *HTTP) Start(args interface{}) (err error) {
s.cfg = args.(HTTPArgs) s.cfg = args.(HTTPArgs)
s.CheckArgs()
if *s.cfg.Parent != "" { if *s.cfg.Parent != "" {
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
s.InitOutConnPool() s.InitOutConnPool()
} }
s.InitService() s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local) host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port) p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p) sc := utils.NewServerChannel(host, p)
@ -95,8 +141,9 @@ func (s *HTTP) callback(inConn net.Conn) {
//log.Printf("blocked ? : %v, %s , fail:%d ,success:%d", useProxy, address, n, m) //log.Printf("blocked ? : %v, %s , fail:%d ,success:%d", useProxy, address, n, m)
} }
log.Printf("use proxy : %v, %s", useProxy, address) log.Printf("use proxy : %v, %s", useProxy, address)
//os.Exit(0)
err = s.OutToTCP(useProxy, address, &inConn, &req) err = s.OutToTCP(useProxy, address, &inConn, &req)
if err != nil { if err != nil {
if *s.cfg.Parent == "" { if *s.cfg.Parent == "" {
log.Printf("connect to %s fail, ERR:%s", address, err) log.Printf("connect to %s fail, ERR:%s", address, err)
@ -118,9 +165,13 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
var outConn net.Conn var outConn net.Conn
var _outConn interface{} var _outConn interface{}
if useProxy { if useProxy {
_outConn, err = s.outPool.Pool.Get() if *s.cfg.ParentType == "ssh" {
if err == nil { outConn, err = s.getSSHConn(address)
outConn = _outConn.(net.Conn) } else {
_outConn, err = s.outPool.Pool.Get()
if err == nil {
outConn = _outConn.(net.Conn)
}
} }
} else { } else {
outConn, err = utils.ConnectHost(address, *s.cfg.Timeout) outConn, err = utils.ConnectHost(address, *s.cfg.Timeout)
@ -134,20 +185,61 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
outAddr := outConn.RemoteAddr().String() outAddr := outConn.RemoteAddr().String()
outLocalAddr := outConn.LocalAddr().String() outLocalAddr := outConn.LocalAddr().String()
if req.IsHTTPS() && !useProxy { if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") {
req.HTTPSReply() //https无上级或者上级非代理,proxy需要响应connect请求,并直连目标
err = req.HTTPSReply()
} else { } 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.IoBind((*inConn), outConn, func(err error) {
log.Printf("conn %s - %s - %s - %s released [%s]", inAddr, inLocalAddr, outLocalAddr, outAddr, req.Host)
utils.CloseConn(inConn) utils.CloseConn(inConn)
utils.CloseConn(&outConn) utils.CloseConn(&outConn)
}, func(n int, d bool) {}, 0) }, func(n int, d bool) {}, 0)
log.Printf("conn %s - %s - %s - %s connected [%s]", inAddr, inLocalAddr, outLocalAddr, outAddr, req.Host) log.Printf("conn %s - %s - %s - %s connected [%s]", inAddr, inLocalAddr, outLocalAddr, outAddr, req.Host)
return return
} }
func (s *HTTP) OutToUDP(inConn *net.Conn) (err error) {
func (s *HTTP) getSSHConn(host string) (outConn net.Conn, err error) {
maxTryCount := 1
tryCount := 0
RETRY:
if tryCount >= maxTryCount {
return
}
outConn, err = s.sshClient.Dial("tcp", host)
//log.Printf("s.sshClient.Dial, host:%s)", host)
if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err)
s.sshClient.Close()
e := s.ConnectSSH()
if e == nil {
tryCount++
time.Sleep(time.Second * 3)
goto RETRY
} else {
err = e
}
}
return
}
func (s *HTTP) ConnectSSH() (err error) {
config := ssh.ClientConfig{
User: *s.cfg.SSHUser,
Auth: []ssh.AuthMethod{s.cfg.SSHAuthMethod},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
s.sshClient, err = ssh.Dial("tcp", *s.cfg.Parent, &config)
return return
} }
func (s *HTTP) InitOutConnPool() { func (s *HTTP) InitOutConnPool() {

View File

@ -25,7 +25,7 @@ func Regist(name string, s Service, args interface{}) {
Name: name, Name: name,
} }
} }
func Run(name string) (service *ServiceItem, err error) { func Run(name string, args ...interface{}) (service *ServiceItem, err error) {
service, ok := servicesMap[name] service, ok := servicesMap[name]
if ok { if ok {
go func() { 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())) 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 { if err != nil {
log.Fatalf("%s servcie fail, ERR: %s", name, err) log.Fatalf("%s servcie fail, ERR: %s", name, err)
} }

304
services/socks.go Normal file
View File

@ -0,0 +1,304 @@
package services
import (
"bytes"
"crypto/tls"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"proxy/utils"
"time"
"golang.org/x/crypto/ssh"
)
type Socks struct {
cfg SocksArgs
checker utils.Checker
basicAuth utils.BasicAuth
sshClient *ssh.Client
}
func NewSocks() Service {
return &Socks{
cfg: SocksArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
}
}
func (s *Socks) CheckArgs() {
var err error
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.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 *Socks) InitService() {
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)
}
}
}
func (s *Socks) StopService() {
if s.sshClient != nil {
s.sshClient.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.callback)
} else {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback)
}
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) callback(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()))
}
utils.CloseConn(&inConn)
}()
var outConn net.Conn
defer utils.CloseConn(&outConn)
var b [1024]byte
n, err := inConn.Read(b[:])
if err != nil {
if err != io.EOF {
log.Printf("read request data fail,ERR: %s", err)
}
return
}
var reqBytes = b[:n]
//log.Printf("% x", b[:n])
//reply
n, err = inConn.Write([]byte{0x05, 0x00})
if err != nil {
log.Printf("reply answer data fail,ERR: %s", err)
return
}
//read answer
n, err = inConn.Read(b[:])
if err != nil {
log.Printf("read answer data fail,ERR: %s", err)
return
}
var headBytes = b[:n]
// log.Printf("% x", b[:n])
var addr string
switch b[3] {
case 0x01:
sip := sockIP{}
if err := binary.Read(bytes.NewReader(b[4:n]), binary.BigEndian, &sip); err != nil {
log.Printf("read ip fail,ERR: %s", err)
return
}
addr = sip.toAddr()
case 0x03:
host := string(b[5 : n-2])
var port uint16
err = binary.Read(bytes.NewReader(b[n-2:n]), binary.BigEndian, &port)
if err != nil {
log.Printf("read domain fail,ERR: %s", err)
return
}
addr = fmt.Sprintf("%s:%d", host, port)
}
useProxy := true
if *s.cfg.Always {
outConn, err = s.getOutConn(reqBytes, headBytes, addr)
} else {
if *s.cfg.Parent != "" {
s.checker.Add(addr, true, "", "", nil)
useProxy, _, _ = s.checker.IsBlocked(addr)
if useProxy {
outConn, err = s.getOutConn(reqBytes, headBytes, addr)
} else {
outConn, err = utils.ConnectHost(addr, *s.cfg.Timeout)
}
} else {
outConn, err = utils.ConnectHost(addr, *s.cfg.Timeout)
}
}
if err != nil {
log.Printf("get out conn fail,%s", err)
inConn.Write([]byte{0x05, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
return
}
log.Printf("use proxy %v : %s", useProxy, addr)
inConn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
inAddr := inConn.RemoteAddr().String()
inLocalAddr := inConn.LocalAddr().String()
log.Printf("conn %s - %s connected [%s]", inAddr, inLocalAddr, addr)
// utils.IoBind(outConn, inConn, func(err error) {
// log.Printf("conn %s - %s released [%s]", inAddr, inLocalAddr, addr)
// }, func(i int, b bool) {}, 0)
var bind = func() (err interface{}) {
defer func() {
if err == nil {
if err = recover(); err != nil {
log.Printf("bind crashed %s", err)
}
}
}()
go func() {
defer func() {
if err == nil {
if err = recover(); err != nil {
log.Printf("bind crashed %s", err)
}
}
}()
_, err = io.Copy(outConn, inConn)
}()
_, err = io.Copy(inConn, outConn)
return
}
bind()
log.Printf("conn %s - %s released [%s]", inAddr, inLocalAddr, addr)
utils.CloseConn(&inConn)
utils.CloseConn(&outConn)
}
func (s *Socks) getOutConn(reqBytes, headBytes []byte, host string) (outConn net.Conn, err error) {
switch *s.cfg.ParentType {
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 {
outConn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout)
}
if err != nil {
return
}
var buf = make([]byte, 1024)
//var n int
_, err = outConn.Write(reqBytes)
if err != nil {
return
}
_, err = outConn.Read(buf)
if err != nil {
return
}
//resp := buf[:n]
//log.Printf("resp:%v", resp)
outConn.Write(headBytes)
_, err = outConn.Read(buf)
if err != nil {
return
}
//result := buf[:n]
//log.Printf("result:%v", result)
case "ssh":
maxTryCount := 1
tryCount := 0
RETRY:
if tryCount >= maxTryCount {
return
}
outConn, err = s.sshClient.Dial("tcp", host)
if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err)
s.sshClient.Close()
e := s.ConnectSSH()
if e == nil {
tryCount++
time.Sleep(time.Second * 3)
goto RETRY
} else {
err = e
}
}
}
return
}
func (s *Socks) ConnectSSH() (err error) {
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
},
}
s.sshClient, err = ssh.Dial("tcp", *s.cfg.Parent, &config)
return
}
type sockIP struct {
A, B, C, D byte
PORT uint16
}
func (ip sockIP) toAddr() string {
return fmt.Sprintf("%d.%d.%d.%d:%d", ip.A, ip.B, ip.C, ip.D, ip.PORT)
}

View File

@ -1,6 +1,7 @@
package services package services
import ( import (
"bufio"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -23,6 +24,17 @@ func NewTCP() Service {
cfg: TCPArgs{}, 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>")
}
if *s.cfg.ParentType == "tls" || *s.cfg.IsTLS {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
}
func (s *TCP) InitService() { func (s *TCP) InitService() {
s.InitOutConnPool() s.InitOutConnPool()
} }
@ -33,12 +45,8 @@ func (s *TCP) StopService() {
} }
func (s *TCP) Start(args interface{}) (err error) { func (s *TCP) Start(args interface{}) (err error) {
s.cfg = args.(TCPArgs) s.cfg = args.(TCPArgs)
if *s.cfg.Parent != "" { s.CheckArgs()
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
} else {
log.Fatalf("parent required for %s %s", s.cfg.Protocol(), *s.cfg.Local)
}
s.InitService() s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local) host, port, _ := net.SplitHostPort(*s.cfg.Local)
@ -97,18 +105,18 @@ func (s *TCP) OutToTCP(inConn *net.Conn) (err error) {
inLocalAddr := (*inConn).LocalAddr().String() inLocalAddr := (*inConn).LocalAddr().String()
outAddr := outConn.RemoteAddr().String() outAddr := outConn.RemoteAddr().String()
outLocalAddr := outConn.LocalAddr().String() outLocalAddr := outConn.LocalAddr().String()
utils.IoBind((*inConn), outConn, func(isSrcErr bool, err error) { utils.IoBind((*inConn), outConn, func(err error) {
log.Printf("conn %s - %s - %s -%s released", inAddr, inLocalAddr, outLocalAddr, outAddr) log.Printf("conn %s - %s - %s - %s released", inAddr, inLocalAddr, outLocalAddr, outAddr)
utils.CloseConn(inConn) utils.CloseConn(inConn)
utils.CloseConn(&outConn) utils.CloseConn(&outConn)
}, func(n int, d bool) {}, 0) }, func(n int, d bool) {}, 0)
log.Printf("conn %s - %s - %s -%s connected", inAddr, inLocalAddr, outLocalAddr, outAddr) log.Printf("conn %s - %s - %s - %s connected", inAddr, inLocalAddr, outLocalAddr, outAddr)
return return
} }
func (s *TCP) OutToUDP(inConn *net.Conn) (err error) { func (s *TCP) OutToUDP(inConn *net.Conn) (err error) {
log.Printf("conn created , remote : %s ", (*inConn).RemoteAddr()) log.Printf("conn created , remote : %s ", (*inConn).RemoteAddr())
for { for {
srcAddr, body, err := utils.ReadUDPPacket(inConn) srcAddr, body, err := utils.ReadUDPPacket(bufio.NewReader(*inConn))
if err == io.EOF || err == io.ErrUnexpectedEOF { if err == io.EOF || err == io.ErrUnexpectedEOF {
//log.Printf("connection %s released", srcAddr) //log.Printf("connection %s released", srcAddr)
utils.CloseConn(inConn) utils.CloseConn(inConn)

View File

@ -3,55 +3,55 @@ package services
import ( import (
"bufio" "bufio"
"encoding/binary" "encoding/binary"
"fmt"
"log" "log"
"net" "net"
"proxy/utils" "proxy/utils"
"strconv" "strconv"
"sync"
"time" "time"
) )
type BridgeItem struct { type ServerConn struct {
ServerChn chan *net.Conn ClientLocalAddr string //tcp:2.2.22:333@ID
ClientChn chan *net.Conn Conn *net.Conn
ClientControl *net.Conn //Conn *utils.HeartbeatReadWriter
Once *sync.Once
Key string
} }
type TunnelBridge struct { type TunnelBridge struct {
cfg TunnelBridgeArgs cfg TunnelBridgeArgs
br utils.ConcurrentMap serverConns utils.ConcurrentMap
clientControlConns utils.ConcurrentMap
} }
func NewTunnelBridge() Service { func NewTunnelBridge() Service {
return &TunnelBridge{ return &TunnelBridge{
cfg: TunnelBridgeArgs{}, cfg: TunnelBridgeArgs{},
br: utils.NewConcurrentMap(), serverConns: utils.NewConcurrentMap(),
clientControlConns: utils.NewConcurrentMap(),
} }
} }
func (s *TunnelBridge) InitService() { func (s *TunnelBridge) InitService() {
} }
func (s *TunnelBridge) Check() { func (s *TunnelBridge) CheckArgs() {
if s.cfg.CertBytes == nil || s.cfg.KeyBytes == nil { if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
log.Fatalf("cert and key file required") 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) StopService() {
} }
func (s *TunnelBridge) Start(args interface{}) (err error) { func (s *TunnelBridge) Start(args interface{}) (err error) {
s.cfg = args.(TunnelBridgeArgs) s.cfg = args.(TunnelBridgeArgs)
s.Check() s.CheckArgs()
s.InitService() s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local) host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port) p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p) sc := utils.NewServerChannel(host, p)
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, func(inConn net.Conn) { err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, func(inConn net.Conn) {
//log.Printf("connection from %s ", inConn.RemoteAddr())
reader := bufio.NewReader(inConn) reader := bufio.NewReader(inConn)
var connType uint8 var connType uint8
err = binary.Read(reader, binary.LittleEndian, &connType) err = binary.Read(reader, binary.LittleEndian, &connType)
@ -59,35 +59,125 @@ func (s *TunnelBridge) Start(args interface{}) (err error) {
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
return return
} }
var key string //log.Printf("conn type %d", connType)
var key, clientLocalAddr, ID string
var connTypeStrMap = map[uint8]string{CONN_SERVER: "server", CONN_CLIENT: "client", CONN_CONTROL: "client"} 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
var keyLength uint16 err = binary.Read(reader, binary.LittleEndian, &keyLength)
err = binary.Read(reader, binary.LittleEndian, &keyLength) if err != nil {
if err != nil { return
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)
} }
_key := make([]byte, keyLength)
n, err := reader.Read(_key)
if err != nil {
return
}
if n != int(keyLength) {
return
}
key = string(_key)
//log.Printf("conn key %s", key)
if connType != CONN_CONTROL {
var IDLength uint16
err = binary.Read(reader, binary.LittleEndian, &IDLength)
if err != nil {
return
}
_id := make([]byte, IDLength)
n, err := reader.Read(_id)
if err != nil {
return
}
if n != int(IDLength) {
return
}
ID = string(_id)
if connType == CONN_SERVER {
var addrLength uint16
err = binary.Read(reader, binary.LittleEndian, &addrLength)
if err != nil {
return
}
_addr := make([]byte, addrLength)
n, err = reader.Read(_addr)
if err != nil {
return
}
if n != int(addrLength) {
return
}
clientLocalAddr = string(_addr)
}
}
log.Printf("connection from %s , key: %s , id: %s", connTypeStrMap[connType], key, ID)
switch connType { switch connType {
case CONN_SERVER: case CONN_SERVER:
s.ServerConn(&inConn, key) // hb := utils.NewHeartbeatReadWriter(&inConn, 3, func(err error, hb *utils.HeartbeatReadWriter) {
// log.Printf("%s conn %s from server released", key, ID)
// s.serverConns.Remove(ID)
// })
addr := clientLocalAddr + "@" + ID
s.serverConns.Set(ID, ServerConn{
//Conn: &hb,
Conn: &inConn,
ClientLocalAddr: addr,
})
for {
item, ok := s.clientControlConns.Get(key)
if !ok {
log.Printf("client %s control conn not exists", key)
time.Sleep(time.Second * 3)
continue
}
_, err := (*item.(*net.Conn)).Write([]byte(addr))
if err != nil {
log.Printf("%s client control conn write signal fail, err: %s, retrying...", key, err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
case CONN_CLIENT: case CONN_CLIENT:
s.ClientConn(&inConn, key) serverConnItem, ok := s.serverConns.Get(ID)
if !ok {
inConn.Close()
log.Printf("server conn %s exists", ID)
return
}
serverConn := serverConnItem.(ServerConn).Conn
// hw := utils.NewHeartbeatReadWriter(&inConn, 3, func(err error, hw *utils.HeartbeatReadWriter) {
// log.Printf("%s conn %s from client released", key, ID)
// hw.Close()
// })
utils.IoBind(*serverConn, inConn, func(err error) {
// utils.IoBind(serverConn, inConn, func(isSrcErr bool, err error) {
//serverConn.Close()
(*serverConn).Close()
utils.CloseConn(&inConn)
// hw.Close()
s.serverConns.Remove(ID)
log.Printf("conn %s released", ID)
}, func(i int, b bool) {}, 0)
log.Printf("conn %s created", ID)
case CONN_CONTROL: case CONN_CONTROL:
s.ClientControlConn(&inConn, key) if s.clientControlConns.Has(key) {
default: item, _ := s.clientControlConns.Get(key)
log.Printf("unkown conn type %d", connType) //(*item.(*utils.HeartbeatReadWriter)).Close()
utils.CloseConn(&inConn) (*item.(*net.Conn)).Close()
}
// hb := utils.NewHeartbeatReadWriter(&inConn, 3, func(err error, hb *utils.HeartbeatReadWriter) {
// log.Printf("client %s disconnected", key)
// s.clientControlConns.Remove(key)
// })
// s.clientControlConns.Set(key, &hb)
s.clientControlConns.Set(key, &inConn)
log.Printf("set client %s control conn", key)
} }
}) })
if err != nil { if err != nil {
@ -99,85 +189,3 @@ func (s *TunnelBridge) Start(args interface{}) (err error) {
func (s *TunnelBridge) Clean() { func (s *TunnelBridge) Clean() {
s.StopService() 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

@ -9,6 +9,7 @@ import (
"log" "log"
"net" "net"
"proxy/utils" "proxy/utils"
"strings"
"time" "time"
) )
@ -24,52 +25,57 @@ func NewTunnelClient() Service {
func (s *TunnelClient) InitService() { func (s *TunnelClient) InitService() {
} }
func (s *TunnelClient) Check() { func (s *TunnelClient) CheckArgs() {
if *s.cfg.Parent != "" { if *s.cfg.Parent != "" {
log.Printf("use tls parent %s", *s.cfg.Parent) log.Printf("use tls parent %s", *s.cfg.Parent)
} else { } else {
log.Fatalf("parent required") 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") 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() { func (s *TunnelClient) StopService() {
} }
func (s *TunnelClient) Start(args interface{}) (err error) { func (s *TunnelClient) Start(args interface{}) (err error) {
s.cfg = args.(TunnelClientArgs) s.cfg = args.(TunnelClientArgs)
s.Check() s.CheckArgs()
s.InitService() s.InitService()
log.Printf("proxy on tunnel client mode")
for { for {
ctrlConn, err := s.GetInConn(CONN_CONTROL) ctrlConn, err := s.GetInConn(CONN_CONTROL, "")
if err != nil { if err != nil {
log.Printf("control connection err: %s", err) log.Printf("control connection err: %s", err)
time.Sleep(time.Second * 3) time.Sleep(time.Second * 3)
utils.CloseConn(&ctrlConn) utils.CloseConn(&ctrlConn)
continue continue
} }
if *s.cfg.IsUDP { // rw := utils.NewHeartbeatReadWriter(&ctrlConn, 3, func(err error, hb *utils.HeartbeatReadWriter) {
log.Printf("proxy on udp tunnel client mode") // log.Printf("ctrlConn err %s", err)
} else { // utils.CloseConn(&ctrlConn)
log.Printf("proxy on tcp tunnel client mode") // })
}
for { for {
signal := make([]byte, 1) signal := make([]byte, 50)
if signal[0] == 1 { // n, err := rw.Read(signal)
continue n, err := ctrlConn.Read(signal)
}
_, err = ctrlConn.Read(signal)
if err != nil { if err != nil {
utils.CloseConn(&ctrlConn) utils.CloseConn(&ctrlConn)
log.Printf("read connection signal err: %s", err) log.Printf("read connection signal err: %s", err)
break break
} }
log.Printf("signal revecived:%s", signal) addr := string(signal[:n])
if *s.cfg.IsUDP { // log.Printf("n:%d addr:%s err:%s", n, addr, err)
go s.ServeUDP() // os.Exit(0)
log.Printf("signal revecived:%s", addr)
protocol := addr[:3]
atIndex := strings.Index(addr, "@")
ID := addr[atIndex+1:]
localAddr := addr[4:atIndex]
if protocol == "udp" {
go s.ServeUDP(localAddr, ID)
} else { } else {
go s.ServeConn() go s.ServeConn(localAddr, ID)
} }
} }
} }
@ -77,7 +83,7 @@ func (s *TunnelClient) Start(args interface{}) (err error) {
func (s *TunnelClient) Clean() { func (s *TunnelClient) Clean() {
s.StopService() s.StopService()
} }
func (s *TunnelClient) GetInConn(typ uint8) (outConn net.Conn, err error) { func (s *TunnelClient) GetInConn(typ uint8, ID string) (outConn net.Conn, err error) {
outConn, err = s.GetConn() outConn, err = s.GetConn()
if err != nil { if err != nil {
err = fmt.Errorf("connection err: %s", err) err = fmt.Errorf("connection err: %s", err)
@ -89,6 +95,12 @@ func (s *TunnelClient) GetInConn(typ uint8) (outConn net.Conn, err error) {
binary.Write(pkg, binary.LittleEndian, typ) binary.Write(pkg, binary.LittleEndian, typ)
binary.Write(pkg, binary.LittleEndian, keyLength) binary.Write(pkg, binary.LittleEndian, keyLength)
binary.Write(pkg, binary.LittleEndian, keyBytes) binary.Write(pkg, binary.LittleEndian, keyBytes)
if ID != "" {
IDBytes := []byte(ID)
IDLength := uint16(len(IDBytes))
binary.Write(pkg, binary.LittleEndian, IDLength)
binary.Write(pkg, binary.LittleEndian, IDBytes)
}
_, err = outConn.Write(pkg.Bytes()) _, err = outConn.Write(pkg.Bytes())
if err != nil { if err != nil {
err = fmt.Errorf("write connection data err: %s ,retrying...", err) err = fmt.Errorf("write connection data err: %s ,retrying...", err)
@ -105,36 +117,45 @@ func (s *TunnelClient) GetConn() (conn net.Conn, err error) {
} }
return return
} }
func (s *TunnelClient) ServeUDP() { func (s *TunnelClient) ServeUDP(localAddr, ID string) {
var inConn net.Conn var inConn net.Conn
var err error var err error
// for {
for { for {
for { inConn, err = s.GetInConn(CONN_CLIENT, ID)
inConn, err = s.GetInConn(CONN_CLIENT) if err != nil {
if err != nil { utils.CloseConn(&inConn)
utils.CloseConn(&inConn) log.Printf("connection err: %s, retrying...", err)
log.Printf("connection err: %s, retrying...", err) time.Sleep(time.Second * 3)
time.Sleep(time.Second * 3) continue
continue } else {
} else { break
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)
} }
} }
log.Printf("conn %s created", ID)
// hw := utils.NewHeartbeatReadWriter(&inConn, 3, func(err error, hw *utils.HeartbeatReadWriter) {
// log.Printf("hw err %s", err)
// hw.Close()
// })
for {
// srcAddr, body, err := utils.ReadUDPPacket(&hw)
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) { func (s *TunnelClient) processUDPPacket(inConn *net.Conn, srcAddr, localAddr string, body []byte) {
dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Local) dstAddr, err := net.ResolveUDPAddr("udp", localAddr)
if err != nil { if err != nil {
log.Printf("can't resolve address: %s", err) log.Printf("can't resolve address: %s", err)
utils.CloseConn(inConn) utils.CloseConn(inConn)
@ -153,27 +174,28 @@ func (s *TunnelClient) processUDPPacket(inConn *net.Conn, srcAddr string, body [
return return
} }
//log.Printf("send udp packet to %s success", dstAddr.String()) //log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 512) buf := make([]byte, 1024)
len, _, err := conn.ReadFromUDP(buf) length, _, err := conn.ReadFromUDP(buf)
if err != nil { if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return return
} }
respBody := buf[0:len] respBody := buf[0:length]
//log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody) //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 { if err != nil {
log.Printf("send udp response fail ,ERR:%s", err) log.Printf("send udp response fail ,ERR:%s", err)
utils.CloseConn(inConn) utils.CloseConn(inConn)
return 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 string) {
var inConn, outConn net.Conn var inConn, outConn net.Conn
var err error var err error
for { for {
inConn, err = s.GetInConn(CONN_CLIENT) inConn, err = s.GetInConn(CONN_CLIENT, ID)
if err != nil { if err != nil {
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
log.Printf("connection err: %s, retrying...", err) log.Printf("connection err: %s, retrying...", err)
@ -187,29 +209,27 @@ func (s *TunnelClient) ServeConn() {
i := 0 i := 0
for { for {
i++ i++
outConn, err = utils.ConnectHost(*s.cfg.Local, *s.cfg.Timeout) outConn, err = utils.ConnectHost(localAddr, *s.cfg.Timeout)
if err == nil || i == 3 { if err == nil || i == 3 {
break break
} else { } else {
if i == 3 { 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) time.Sleep(2 * time.Second)
continue continue
} }
} }
} }
if err != nil { if err != nil {
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
utils.CloseConn(&outConn) utils.CloseConn(&outConn)
log.Printf("build connection error, err: %s", err) log.Printf("build connection error, err: %s", err)
return return
} }
utils.IoBind(inConn, outConn, func(err error) {
utils.IoBind(inConn, outConn, func(isSrcErr bool, err error) { log.Printf("conn %s released", ID)
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(&inConn)
utils.CloseConn(&outConn) utils.CloseConn(&outConn)
}, func(i int, b bool) {}, 0) }, 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()) log.Printf("conn %s created", ID)
} }

View File

@ -1,10 +1,10 @@
package services package services
import ( import (
"bufio"
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"encoding/binary" "encoding/binary"
"fmt"
"io" "io"
"log" "log"
"net" "net"
@ -21,6 +21,64 @@ type TunnelServer struct {
sc utils.ServerChannel sc utils.ServerChannel
} }
type TunnelServerManager struct {
cfg TunnelServerArgs
udpChn chan UDPItem
sc utils.ServerChannel
}
func NewTunnelServerManager() Service {
return &TunnelServerManager{
cfg: TunnelServerArgs{},
udpChn: make(chan UDPItem, 50000),
}
}
func (s *TunnelServerManager) Start(args interface{}) (err error) {
s.cfg = args.(TunnelServerArgs)
if *s.cfg.Parent != "" {
log.Printf("use tls parent %s", *s.cfg.Parent)
} else {
log.Fatalf("parent required")
}
//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,
Local: &local,
IsUDP: &IsUDP,
Remote: &remote,
Key: &KEY,
Timeout: s.cfg.Timeout,
})
if err != nil {
return
}
}
return
}
func (s *TunnelServerManager) Clean() {
}
func NewTunnelServer() Service { func NewTunnelServer() Service {
return &TunnelServer{ return &TunnelServer{
cfg: TunnelServerArgs{}, cfg: TunnelServerArgs{},
@ -37,26 +95,24 @@ type UDPItem struct {
func (s *TunnelServer) InitService() { func (s *TunnelServer) InitService() {
s.UDPConnDeamon() s.UDPConnDeamon()
} }
func (s *TunnelServer) Check() { func (s *TunnelServer) CheckArgs() {
if *s.cfg.Parent != "" { if *s.cfg.Remote == "" {
log.Printf("use tls parent %s", *s.cfg.Parent) log.Fatalf("remote required")
} 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") log.Fatalf("cert and key file required")
} }
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
} }
func (s *TunnelServer) StopService() { func (s *TunnelServer) StopService() {
} }
func (s *TunnelServer) Start(args interface{}) (err error) { func (s *TunnelServer) Start(args interface{}) (err error) {
s.cfg = args.(TunnelServerArgs) s.cfg = args.(TunnelServerArgs)
s.Check() s.CheckArgs()
s.InitService() s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local) host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port) p, _ := strconv.Atoi(port)
s.sc = utils.NewServerChannel(host, p) s.sc = utils.NewServerChannel(host, p)
if *s.cfg.IsUDP { if *s.cfg.IsUDP {
err = s.sc.ListenUDP(func(packet []byte, localAddr, srcAddr *net.UDPAddr) { err = s.sc.ListenUDP(func(packet []byte, localAddr, srcAddr *net.UDPAddr) {
s.udpChn <- UDPItem{ s.udpChn <- UDPItem{
@ -77,8 +133,9 @@ func (s *TunnelServer) Start(args interface{}) (err error) {
} }
}() }()
var outConn net.Conn var outConn net.Conn
var ID string
for { for {
outConn, err = s.GetOutConn() outConn, ID, err = s.GetOutConn("")
if err != nil { if err != nil {
utils.CloseConn(&outConn) utils.CloseConn(&outConn)
log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err) log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err)
@ -88,14 +145,18 @@ func (s *TunnelServer) Start(args interface{}) (err error) {
break break
} }
} }
// hb := utils.NewHeartbeatReadWriter(&outConn, 3, func(err error, hb *utils.HeartbeatReadWriter) {
utils.IoBind(inConn, outConn, func(isSrcErr bool, err error) { // log.Printf("%s conn %s to bridge released", *s.cfg.Key, ID)
// hb.Close()
// })
// utils.IoBind(inConn, &hb, func(err error) {
utils.IoBind(inConn, outConn, func(err error) {
utils.CloseConn(&outConn) utils.CloseConn(&outConn)
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
log.Printf("%s conn %s - %s - %s - %s released", *s.cfg.Key, inConn.RemoteAddr(), inConn.LocalAddr(), outConn.LocalAddr(), outConn.RemoteAddr()) log.Printf("%s conn %s released", *s.cfg.Key, ID)
}, func(i int, b bool) {}, 0) }, 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()) log.Printf("%s conn %s created", *s.cfg.Key, ID)
}) })
if err != nil { if err != nil {
return return
@ -107,7 +168,7 @@ func (s *TunnelServer) Start(args interface{}) (err error) {
func (s *TunnelServer) Clean() { func (s *TunnelServer) Clean() {
s.StopService() s.StopService()
} }
func (s *TunnelServer) GetOutConn() (outConn net.Conn, err error) { func (s *TunnelServer) GetOutConn(id string) (outConn net.Conn, ID string, err error) {
outConn, err = s.GetConn() outConn, err = s.GetConn()
if err != nil { if err != nil {
log.Printf("connection err: %s", err) log.Printf("connection err: %s", err)
@ -115,10 +176,26 @@ func (s *TunnelServer) GetOutConn() (outConn net.Conn, err error) {
} }
keyBytes := []byte(*s.cfg.Key) keyBytes := []byte(*s.cfg.Key)
keyLength := uint16(len(keyBytes)) keyLength := uint16(len(keyBytes))
ID = utils.Uniqueid()
IDBytes := []byte(ID)
if id != "" {
ID = id
IDBytes = []byte(id)
}
IDLength := uint16(len(IDBytes))
remoteAddr := []byte("tcp:" + *s.cfg.Remote)
if *s.cfg.IsUDP {
remoteAddr = []byte("udp:" + *s.cfg.Remote)
}
remoteAddrLength := uint16(len(remoteAddr))
pkg := new(bytes.Buffer) pkg := new(bytes.Buffer)
binary.Write(pkg, binary.LittleEndian, CONN_SERVER) binary.Write(pkg, binary.LittleEndian, CONN_SERVER)
binary.Write(pkg, binary.LittleEndian, keyLength) binary.Write(pkg, binary.LittleEndian, keyLength)
binary.Write(pkg, binary.LittleEndian, keyBytes) binary.Write(pkg, binary.LittleEndian, keyBytes)
binary.Write(pkg, binary.LittleEndian, IDLength)
binary.Write(pkg, binary.LittleEndian, IDBytes)
binary.Write(pkg, binary.LittleEndian, remoteAddrLength)
binary.Write(pkg, binary.LittleEndian, remoteAddr)
_, err = outConn.Write(pkg.Bytes()) _, err = outConn.Write(pkg.Bytes())
if err != nil { if err != nil {
log.Printf("write connection data err: %s ,retrying...", err) log.Printf("write connection data err: %s ,retrying...", err)
@ -143,36 +220,37 @@ func (s *TunnelServer) UDPConnDeamon() {
} }
}() }()
var outConn net.Conn 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 var err error
for { for {
item := <-s.udpChn item := <-s.udpChn
RETRY: RETRY:
if outConn == nil { if outConn == nil {
for { for {
outConn, err = s.GetOutConn() outConn, ID, err = s.GetOutConn("")
if err != nil { if err != nil {
cmdChn <- true // cmdChn <- true
outConn = nil outConn = nil
utils.CloseConn(&outConn) utils.CloseConn(&outConn)
log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err) log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err)
time.Sleep(time.Second * 3) time.Sleep(time.Second * 3)
continue continue
} else { } else {
go func(outConn net.Conn) { go func(outConn net.Conn, ID string) {
go func() { go func() {
<-cmdChn // <-cmdChn
outConn.Close() // outConn.Close()
}() }()
for { for {
srcAddrFromConn, body, err := utils.ReadUDPPacket(&outConn) srcAddrFromConn, body, err := utils.ReadUDPPacket(outConn)
if err == io.EOF || err == io.ErrUnexpectedEOF { 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 break
} }
if err != nil { 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 continue
} }
//log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn) //log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn)
@ -188,19 +266,20 @@ func (s *TunnelServer) UDPConnDeamon() {
log.Printf("udp response to local %s fail,ERR:%s", srcAddrFromConn, err) log.Printf("udp response to local %s fail,ERR:%s", srcAddrFromConn, err)
continue 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 break
} }
} }
} }
writer := bufio.NewWriter(outConn) outConn.SetWriteDeadline(time.Now().Add(time.Second))
writer.Write(utils.UDPPacket(item.srcAddr.String(), *item.packet)) _, err = outConn.Write(utils.UDPPacket(item.srcAddr.String(), *item.packet))
err := writer.Flush() outConn.SetWriteDeadline(time.Time{})
if err != nil { if err != nil {
utils.CloseConn(&outConn)
outConn = nil 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 goto RETRY
} }
//log.Printf("write packet %v", *item.packet) //log.Printf("write packet %v", *item.packet)

View File

@ -27,6 +27,17 @@ func NewUDP() Service {
p: utils.NewConcurrentMap(), 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() { func (s *UDP) InitService() {
if *s.cfg.ParentType != TYPE_UDP { if *s.cfg.ParentType != TYPE_UDP {
s.InitOutConnPool() s.InitOutConnPool()
@ -39,12 +50,8 @@ func (s *UDP) StopService() {
} }
func (s *UDP) Start(args interface{}) (err error) { func (s *UDP) Start(args interface{}) (err error) {
s.cfg = args.(UDPArgs) s.cfg = args.(UDPArgs)
if *s.cfg.Parent != "" { s.CheckArgs()
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
} else {
log.Fatalf("parent required for udp %s", *s.cfg.Local)
}
s.InitService() s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local) 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()) log.Printf("conn %d created , local: %s", connKey, srcAddr.String())
for { for {
srcAddrFromConn, body, err := utils.ReadUDPPacket(&conn) srcAddrFromConn, body, err := utils.ReadUDPPacket(bufio.NewReader(conn))
if err == io.EOF || err == io.ErrUnexpectedEOF { if err == io.EOF || err == io.ErrUnexpectedEOF {
//log.Printf("connection %d released", connKey) //log.Printf("connection %d released", connKey)
s.p.Remove(fmt.Sprintf("%d", connKey)) s.p.Remove(fmt.Sprintf("%d", connKey))

View File

@ -9,7 +9,9 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"math/rand"
"net" "net"
"net/http" "net/http"
"os" "os"
@ -22,7 +24,7 @@ import (
"time" "time"
) )
func IoBind(dst io.ReadWriter, src io.ReadWriter, fn func(isSrcErr bool, err error), cfn func(count int, isPositive bool), bytesPreSec float64) { func IoBind(dst io.ReadWriter, src io.ReadWriter, fn func(err error), cfn func(count int, isPositive bool), bytesPreSec float64) {
var one = &sync.Once{} var one = &sync.Once{}
go func() { go func() {
defer func() { defer func() {
@ -31,22 +33,21 @@ func IoBind(dst io.ReadWriter, src io.ReadWriter, fn func(isSrcErr bool, err err
} }
}() }()
var err error var err error
var isSrcErr bool
if bytesPreSec > 0 { if bytesPreSec > 0 {
newreader := NewReader(src) newreader := NewReader(src)
newreader.SetRateLimit(bytesPreSec) newreader.SetRateLimit(bytesPreSec)
_, isSrcErr, err = ioCopy(dst, newreader, func(c int) { _, err = ioCopy(dst, newreader, func(c int) {
cfn(c, false) cfn(c, false)
}) })
} else { } else {
_, isSrcErr, err = ioCopy(dst, src, func(c int) { _, err = ioCopy(dst, src, func(c int) {
cfn(c, false) cfn(c, false)
}) })
} }
if err != nil { if err != nil {
one.Do(func() { one.Do(func() {
fn(isSrcErr, err) fn(err)
}) })
} }
}() }()
@ -57,26 +58,25 @@ func IoBind(dst io.ReadWriter, src io.ReadWriter, fn func(isSrcErr bool, err err
} }
}() }()
var err error var err error
var isSrcErr bool
if bytesPreSec > 0 { if bytesPreSec > 0 {
newReader := NewReader(dst) newReader := NewReader(dst)
newReader.SetRateLimit(bytesPreSec) newReader.SetRateLimit(bytesPreSec)
_, isSrcErr, err = ioCopy(src, newReader, func(c int) { _, err = ioCopy(src, newReader, func(c int) {
cfn(c, true) cfn(c, true)
}) })
} else { } else {
_, isSrcErr, err = ioCopy(src, dst, func(c int) { _, err = ioCopy(src, dst, func(c int) {
cfn(c, true) cfn(c, true)
}) })
} }
if err != nil { if err != nil {
one.Do(func() { one.Do(func() {
fn(isSrcErr, err) fn(err)
}) })
} }
}() }()
} }
func ioCopy(dst io.Writer, src io.Reader, fn ...func(count int)) (written int64, isSrcErr bool, err error) { func ioCopy(dst io.Writer, src io.Reader, fn ...func(count int)) (written int64, err error) {
buf := make([]byte, 32*1024) buf := make([]byte, 32*1024)
for { for {
nr, er := src.Read(buf) nr, er := src.Read(buf)
@ -99,11 +99,10 @@ func ioCopy(dst io.Writer, src io.Reader, fn ...func(count int)) (written int64,
} }
if er != nil { if er != nil {
err = er err = er
isSrcErr = true
break break
} }
} }
return written, isSrcErr, err return written, err
} }
func TlsConnectHost(host string, timeout int, certBytes, keyBytes []byte) (conn tls.Conn, err error) { func TlsConnectHost(host string, timeout int, certBytes, keyBytes []byte) (conn tls.Conn, err error) {
h := strings.Split(host, ":") h := strings.Split(host, ":")
@ -265,6 +264,7 @@ func UDPPacket(srcAddr string, packet []byte) []byte {
addrBytes := []byte(srcAddr) addrBytes := []byte(srcAddr)
addrLength := uint16(len(addrBytes)) addrLength := uint16(len(addrBytes))
bodyLength := uint16(len(packet)) bodyLength := uint16(len(packet))
//log.Printf("build packet : addr len %d, body len %d", addrLength, bodyLength)
pkg := new(bytes.Buffer) pkg := new(bytes.Buffer)
binary.Write(pkg, binary.LittleEndian, addrLength) binary.Write(pkg, binary.LittleEndian, addrLength)
binary.Write(pkg, binary.LittleEndian, addrBytes) binary.Write(pkg, binary.LittleEndian, addrBytes)
@ -272,8 +272,8 @@ func UDPPacket(srcAddr string, packet []byte) []byte {
binary.Write(pkg, binary.LittleEndian, packet) binary.Write(pkg, binary.LittleEndian, packet)
return pkg.Bytes() return pkg.Bytes()
} }
func ReadUDPPacket(conn *net.Conn) (srcAddr string, packet []byte, err error) { func ReadUDPPacket(_reader io.Reader) (srcAddr string, packet []byte, err error) {
reader := bufio.NewReader(*conn) reader := bufio.NewReader(_reader)
var addrLength uint16 var addrLength uint16
var bodyLength uint16 var bodyLength uint16
err = binary.Read(reader, binary.LittleEndian, &addrLength) err = binary.Read(reader, binary.LittleEndian, &addrLength)
@ -286,12 +286,14 @@ func ReadUDPPacket(conn *net.Conn) (srcAddr string, packet []byte, err error) {
return return
} }
if n != int(addrLength) { if n != int(addrLength) {
err = fmt.Errorf("n != int(addrLength), %d,%d", n, addrLength)
return return
} }
srcAddr = string(_srcAddr) srcAddr = string(_srcAddr)
err = binary.Read(reader, binary.LittleEndian, &bodyLength) err = binary.Read(reader, binary.LittleEndian, &bodyLength)
if err != nil { if err != nil {
return return
} }
packet = make([]byte, bodyLength) packet = make([]byte, bodyLength)
@ -300,6 +302,25 @@ func ReadUDPPacket(conn *net.Conn) (srcAddr string, packet []byte, err error) {
return return
} }
if n != int(bodyLength) { 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 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
} }
return return

View File

@ -5,6 +5,7 @@ import (
"log" "log"
"net" "net"
"runtime/debug" "runtime/debug"
"strconv"
) )
type ServerChannel struct { type ServerChannel struct {
@ -24,6 +25,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)) { func (sc *ServerChannel) SetErrAcceptHandler(fn func(err error)) {
sc.errAcceptHandler = fn sc.errAcceptHandler = fn
} }

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"encoding/base64" "encoding/base64"
"encoding/binary"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -11,6 +12,7 @@ import (
"net" "net"
"net/url" "net/url"
"strings" "strings"
"sync"
"time" "time"
) )
@ -460,3 +462,153 @@ 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
}