35 Commits
v3.6 ... v4.0

Author SHA1 Message Date
arraykeys@gmail.com
bf43b3adee Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-04 10:35:44 +08:00
arraykeys
6aa4b3c8a9 no message 2017-12-02 21:14:11 +08:00
arraykeys
7a9f7ef95e no message 2017-12-02 14:25:18 +08:00
arraykeys
ee1a9d3ec7 no message 2017-12-02 14:24:30 +08:00
arraykeys
6a69e58be5 no message 2017-12-02 14:22:14 +08:00
arraykeys
6e1d788677 no message 2017-12-02 14:21:21 +08:00
arraykeys
24f8f789c5 no message 2017-12-02 14:20:15 +08:00
arraykeys
2fb779f990 no message 2017-12-02 14:18:53 +08:00
arraykeys
0a9d3cd309 no message 2017-12-02 14:16:16 +08:00
arraykeys
977b1aba1c no message 2017-12-02 14:02:17 +08:00
arraykeys
a02aeeb906 no message 2017-12-01 23:52:21 +08:00
arraykeys
7e2e63137e no message 2017-12-01 22:14:07 +08:00
arraykeys@gmail.com
4b35219c27 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-12-01 18:01:59 +08:00
arraykeys@gmail.com
0247c4701d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 18:43:31 +08:00
arraykeys@gmail.com
e2cd0b8e4f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 16:50:47 +08:00
arraykeys@gmail.com
ee93171c63 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 15:37:27 +08:00
arraykeys@gmail.com
ddd2302cb2 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 15:37:01 +08:00
arraykeys@gmail.com
c96d2288b3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 15:33:32 +08:00
arraykeys@gmail.com
6f5a088091 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-30 14:25:44 +08:00
arraykeys@gmail.com
9a07797e29 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-27 12:44:43 +08:00
arraykeys@gmail.com
055a020d33 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-27 12:41:12 +08:00
arraykeys@gmail.com
4681ff3827 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-27 12:21:59 +08:00
arraykeys@gmail.com
cff92faf06 Merge branch 'master' of https://github.com/snail007/goproxy.git into dev 2017-11-27 11:28:24 +08:00
arraykeys@gmail.com
890daf5489 Merge branch 'dev' of https://github.com/snail007/goproxy.git into dev 2017-11-27 11:28:17 +08:00
snail007
182bdeb766 Merge pull request #14 from snail007/dev
Update README.md
2017-11-23 19:27:53 -06:00
snail007
a4a953b167 Update README.md 2017-11-24 09:27:07 +08:00
arraykeys@gmail.com
ff37b7e18c Merge branch 'dev' 2017-11-21 14:03:24 +08:00
arraykeys@gmail.com
7aa0e78c15 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-21 14:02:28 +08:00
arraykeys@gmail.com
d798807693 Merge branch 'dev' 2017-11-14 11:58:06 +08:00
arraykeys@gmail.com
35b78c2da6 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-14 11:57:56 +08:00
arraykeys@gmail.com
66a4291c97 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-14 11:56:24 +08:00
arraykeys@gmail.com
e89a965aff Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-14 10:02:51 +08:00
arraykeys@gmail.com
85a9f10be4 Merge branch 'dev' 2017-11-09 13:11:57 +08:00
arraykeys@gmail.com
8bc6e0ffec Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-09 13:11:47 +08:00
arraykeys@gmail.com
98fc0ade4a Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-11-07 11:31:12 +08:00
18 changed files with 1448 additions and 307 deletions

View File

@ -1,4 +1,25 @@
proxy更新日志 proxy更新日志
v4.0
1.内网穿透三端重构了一个multiplexing版本使用github.com/xtaci/smux实现了tcp链接的多路复用
鼎鼎大名的kcp-go底层就是使用的这个库基于kcp-go的双边加速工具kcptun的广泛使用已经很好
的验证来该库的强大与稳定。multiplexing版的内网穿透对应的子命令分别是serverclientbridge
使用方式和参数与之前的子命令tservertclienttserver完全一样另外serverclient增加了
压缩传输参数--c使用压缩传输速度更快。
v3.9
1.增加了守护运行参数--forever,比如: proxy http --forever ,
proxy会fork子进程,然后监控子进程,如果子进程异常退出,5秒后重启子进程.
该参数配合后台运行参数--daemon和日志参数--log,可以保障proxy一直在后台执行不会因为意外退出,
而且可以通过日志文件看到proxy的输出日志内容.
比如: proxy http -p ":9090" --forever --log proxy.log --daemon
v3.8
1.增加了日志输出到文件--log参数,比如: --log proxy.log,日志就会输出到proxy.log方便排除问题.
v3.7
1.修复了socks代理不能正常和上级代理通讯的问题.
v3.6 v3.6
1.http(s),socks代理,集成了外部HTTP API认证,可以通过外部API对用户名和密码进行认证. 1.http(s),socks代理,集成了外部HTTP API认证,可以通过外部API对用户名和密码进行认证.
2.手册http(s),socks代理认证部分增加了集成外部HTTP API认证的使用说明. 2.手册http(s),socks代理认证部分增加了集成外部HTTP API认证的使用说明.

View File

@ -14,7 +14,8 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
- 多协议支持,支持HTTP(S),TCP,UDP,Websocket,SOCKS5代理. - 多协议支持,支持HTTP(S),TCP,UDP,Websocket,SOCKS5代理.
- 支持内网穿透,协议支持TCP和UDP. - 支持内网穿透,协议支持TCP和UDP.
- SSH中转,HTTP(S),SOCKS5代理支持SSH中转,上级Linux服务器不需要任何服务端,本地一个proxy即可开心上网. - SSH中转,HTTP(S),SOCKS5代理支持SSH中转,上级Linux服务器不需要任何服务端,本地一个proxy即可开心上网.
- 支持[KCP](https://github.com/xtaci/kcp-go)协议,HTTP(S),SOCKS5代理支持KCP协议传输数据,降低延迟,提升浏览体验. - [KCP](https://github.com/xtaci/kcp-go)协议支持,HTTP(S),SOCKS5代理支持KCP协议传输数据,降低延迟,提升浏览体验.
- 集成外部APIHTTP(S),SOCKS5代理认证功能可以与外部HTTP API集成可以方便的通过外部系统控制代理用户
### Why need these? ### Why need these?
- 当由于某某原因,我们不能访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道访问我们的服务.   - 当由于某某原因,我们不能访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道访问我们的服务.  
@ -26,7 +27,10 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
- ...   - ...  
本页是v3.6手册,其他版本手册请点击下面链接查看. 本页是v4.0手册,其他版本手册请点击下面链接查看.
- [v3.9手册](https://github.com/snail007/goproxy/tree/v3.9)
- [v3.8手册](https://github.com/snail007/goproxy/tree/v3.8)
- [v3.6-v3.7手册](https://github.com/snail007/goproxy/tree/v3.6)
- [v3.5手册](https://github.com/snail007/goproxy/tree/v3.5) - [v3.5手册](https://github.com/snail007/goproxy/tree/v3.5)
- [v3.4手册](https://github.com/snail007/goproxy/tree/v3.4) - [v3.4手册](https://github.com/snail007/goproxy/tree/v3.4)
- [v3.3手册](https://github.com/snail007/goproxy/tree/v3.3) - [v3.3手册](https://github.com/snail007/goproxy/tree/v3.3)
@ -35,6 +39,9 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
- [v3.0手册](https://github.com/snail007/goproxy/tree/v3.0) - [v3.0手册](https://github.com/snail007/goproxy/tree/v3.0)
- [v2.x手册](https://github.com/snail007/goproxy/tree/v2.2) - [v2.x手册](https://github.com/snail007/goproxy/tree/v2.2)
### 怎么找到组织?
[点击加入交流组织](https://gitter.im/go-proxy/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link)
### 安装 ### 安装
1. [快速安装](#自动安装) 1. [快速安装](#自动安装)
1. [手动安装](#手动安装) 1. [手动安装](#手动安装)
@ -42,7 +49,10 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
### 首次使用必看 ### 首次使用必看
- [环境](#首次使用必看-1) - [环境](#首次使用必看-1)
- [使用配置文件](#使用配置文件) - [使用配置文件](#使用配置文件)
- [调试输出](#调试输出)
- [使用日志文件](#使用日志文件)
- [后台运行](#后台运行) - [后台运行](#后台运行)
- [守护运行](#守护运行)
- [生成通讯证书文件](#生成加密通讯需要的证书文件) - [生成通讯证书文件](#生成加密通讯需要的证书文件)
- [安全建议](#安全建议) - [安全建议](#安全建议)
@ -106,22 +116,14 @@ curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.s
如果安装失败或者你的vps不是linux64位系统,请按照下面的半自动步骤安装: 如果安装失败或者你的vps不是linux64位系统,请按照下面的半自动步骤安装:
#### 手动安装 #### 手动安装
#### **1.登录你的VPS,下载守护进程monexec,选择合适你的版本,vps一般选择"linux_amd64.tar.gz"的即可.**
下载地址:https://github.com/reddec/monexec/releases #### **1.下载proxy**
比如下载到/root/proxy/
执行:
```shell
mkdir /root/proxy/
cd /root/proxy/
wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
```
#### **2.下载proxy**
下载地址: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/v3.6/proxy-linux-amd64.tar.gz wget https://github.com/snail007/goproxy/releases/download/v4.0/proxy-linux-amd64.tar.gz
``` ```
#### **3.下载自动安装脚本** #### **2.下载自动安装脚本**
```shell ```shell
cd /root/proxy/ cd /root/proxy/
wget https://raw.githubusercontent.com/snail007/goproxy/master/install.sh wget https://raw.githubusercontent.com/snail007/goproxy/master/install.sh
@ -129,7 +131,7 @@ chmod +x install.sh
./install.sh ./install.sh
``` ```
## 首次使用必看 ## **首次使用必看**
#### **环境** #### **环境**
接下来的教程,默认系统是linux,程序是proxy所有操作需要root权限 接下来的教程,默认系统是linux,程序是proxy所有操作需要root权限
@ -146,26 +148,40 @@ http
--local-type=tcp --local-type=tcp
--local=:33080 --local=:33080
``` ```
### 生成加密通讯需要的证书文件 ### **调试输出**
默认情况下,日志输出的信息不包含文件行数,某些情况下为了排除程序问题,快速定位问题,
可以使用--debug参数,输出代码行数和毫秒时间.
### **使用日志文件**
默认情况下,日志是直接在控制台显示出来的,如果要保存到文件,可以使用--log参数,
比如: --log proxy.log,日志就会输出到proxy.log方便排除问题.
### **生成加密通讯需要的证书文件**
http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,当然可以选择不加密通信通讯,本教程所有和上级通讯都采用加密,需要证书文件. http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,当然可以选择不加密通信通讯,本教程所有和上级通讯都采用加密,需要证书文件.
在linux上并安装了openssl命令可以直接通过下面的命令生成证书和key文件. 在linux上并安装了openssl命令可以直接通过下面的命令生成证书和key文件.
`./proxy keygen` `./proxy keygen`
默认会在当前程序目录下面生成证书文件proxy.crt和key文件proxy.key。 默认会在当前程序目录下面生成证书文件proxy.crt和key文件proxy.key。
### 后台运行 ### **后台运行**
默认执行proxy之后,如果要保持proxy运行,不能关闭命令行. 默认执行proxy之后,如果要保持proxy运行,不能关闭命令行.
如果想在后台运行proxy,命令行可以关闭,只需要在命令最后加上--daemon参数即可. 如果想在后台运行proxy,命令行可以关闭,只需要在命令最后加上--daemon参数即可.
比如: 比如:
`./proxy http -t tcp -p "0.0.0.0:38080" --daemon` `./proxy http -t tcp -p "0.0.0.0:38080" --daemon`
更推荐用monexec守护运行proxy比较好.
### 安全建议 ### **守护运行**
守护运行参数--forever,比如: `proxy http --forever` ,
proxy会fork子进程,然后监控子进程,如果子进程异常退出,5秒后重启子进程.
该参数配合后台运行参数--daemon和日志参数--log,可以保障proxy一直在后台执行不会因为意外退出,
而且可以通过日志文件看到proxy的输出日志内容.
比如: `proxy http -p ":9090" --forever --log proxy.log --daemon`
### **安全建议**
当VPS在nat设备后面,vps上网卡IP都是内网IP,这个时候可以通过-g参数添加vps的外网ip防止死循环. 当VPS在nat设备后面,vps上网卡IP都是内网IP,这个时候可以通过-g参数添加vps的外网ip防止死循环.
假设你的vps外网ip是23.23.23.23,下面命令通过-g参数设置23.23.23.23 假设你的vps外网ip是23.23.23.23,下面命令通过-g参数设置23.23.23.23
`./proxy http -g "23.23.23.23"` `./proxy http -g "23.23.23.23"`
### 1.HTTP代理 ### **1.HTTP代理**
#### **1.1.普通HTTP代理** #### **1.1.普通HTTP代理**
`./proxy http -t tcp -p "0.0.0.0:38080"` `./proxy http -t tcp -p "0.0.0.0:38080"`
@ -251,7 +267,7 @@ KCP协议需要-B参数设置一个密码用于加密解密数据
#### **1.9.查看帮助** #### **1.9.查看帮助**
`./proxy help http` `./proxy help http`
### 2.TCP代理 ### **2.TCP代理**
#### **2.1.普通一级TCP代理** #### **2.1.普通一级TCP代理**
本地执行: 本地执行:
@ -293,7 +309,7 @@ VPS(IP:22.22.22.33)执行:
#### **2.6.查看帮助** #### **2.6.查看帮助**
`./proxy help tcp` `./proxy help tcp`
### 3.UDP代理 ### **3.UDP代理**
#### **3.1.普通一级UDP代理** #### **3.1.普通一级UDP代理**
本地执行: 本地执行:
@ -335,14 +351,22 @@ 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端进行桥接. 内网穿透,分为两个版本“多链接版本”和“多路复用版本”一般像web服务这种不是长时间连接的服务建议用“多链接版本”如果是要保持长时间连接建议使用“多路复用版本”。
当用户访问server端,流程是: 1. 多链接版本对应的子命令是tservertclienttbridge。
1. server主动和bridge端建立连接 1. 多路复用版本对应的子命令是serverclientbridge。
1. 然后bridge端通知client端连接bridge端,并连接内网目标端口; 1. 多链接版本和多路复用版本的参数和使用方式完全一样。
1. 然后绑定client端到bridge端和client端到内网端口的连接 1. **多路复用版本的serverclient可以开启压缩传输参数是--c。**
1. 然后bridge端把client过来的连接与server端过来的连接绑定 1. **serverclient要么都开启压缩要么都不开启不能只开一个。**
下面的教程以“多链接版本”为例子,说明使用方法。
内网穿透由三部分组成:tclient端,tserver端,tbridge端tclient和tserver主动连接tbridge端进行桥接.
当用户访问tserver端,流程是:
1. tserver主动和tbridge端建立连接
1. 然后tbridge端通知tclient端连接tbridge端,并连接内网目标端口;
1. 然后绑定tclient端到tbridge端和tclient端到内网端口的连接
1. 然后tbridge端把tclient过来的连接与tserver端过来的连接绑定
1. 整个通道建立完成; 1. 整个通道建立完成;
#### **4.2、TCP普通用法** #### **4.2、TCP普通用法**
@ -399,7 +423,7 @@ VPS(IP:22.22.22.33)执行:
`./proxy tserver --udp -r ":53@: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 "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. 完成
@ -469,7 +493,7 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid
`./proxy help tserver` `./proxy help tserver`
`./proxy help tserver` `./proxy help tserver`
### 5.SOCKS5代理 ### **5.SOCKS5代理**
提示:SOCKS5代理,支持CONNECT,UDP协议,不支持BIND,支持用户名密码认证. 提示:SOCKS5代理,支持CONNECT,UDP协议,不支持BIND,支持用户名密码认证.
#### **5.1.普通SOCKS5代理** #### **5.1.普通SOCKS5代理**
`./proxy socks -t tcp -p "0.0.0.0:38080"` `./proxy socks -t tcp -p "0.0.0.0:38080"`
@ -556,7 +580,6 @@ KCP协议需要-B参数设置一个密码用于加密解密数据
### TODO ### TODO
- http,socks代理多个上级负载均衡? - http,socks代理多个上级负载均衡?
- 内网穿透server<->bridge心跳机制?
- 欢迎加群反馈... - 欢迎加群反馈...
### 如何使用源码? ### 如何使用源码?

122
config.go
View File

@ -1,12 +1,14 @@
package main package main
import ( import (
"bufio"
"fmt" "fmt"
"log" "log"
"os" "os"
"os/exec" "os/exec"
"proxy/services" "proxy/services"
"proxy/utils" "proxy/utils"
"time"
kingpin "gopkg.in/alecthomas/kingpin.v2" kingpin "gopkg.in/alecthomas/kingpin.v2"
) )
@ -14,6 +16,7 @@ import (
var ( var (
app *kingpin.Application app *kingpin.Application
service *services.ServiceItem service *services.ServiceItem
cmd *exec.Cmd
) )
func initConfig() (err error) { func initConfig() (err error) {
@ -31,6 +34,9 @@ func initConfig() (err error) {
tunnelServerArgs := services.TunnelServerArgs{} tunnelServerArgs := services.TunnelServerArgs{}
tunnelClientArgs := services.TunnelClientArgs{} tunnelClientArgs := services.TunnelClientArgs{}
tunnelBridgeArgs := services.TunnelBridgeArgs{} tunnelBridgeArgs := services.TunnelBridgeArgs{}
muxServerArgs := services.MuxServerArgs{}
muxClientArgs := services.MuxClientArgs{}
muxBridgeArgs := services.MuxBridgeArgs{}
udpArgs := services.UDPArgs{} udpArgs := services.UDPArgs{}
socksArgs := services.SocksArgs{} socksArgs := services.SocksArgs{}
//build srvice args //build srvice args
@ -38,6 +44,9 @@ func initConfig() (err error) {
app.Author("snail").Version(APP_VERSION) app.Author("snail").Version(APP_VERSION)
debug := app.Flag("debug", "debug log output").Default("false").Bool() debug := app.Flag("debug", "debug log output").Default("false").Bool()
daemon := app.Flag("daemon", "run proxy in background").Default("false").Bool() daemon := app.Flag("daemon", "run proxy in background").Default("false").Bool()
forever := app.Flag("forever", "run proxy in forever,fail and retry").Default("false").Bool()
logfile := app.Flag("log", "log file path").Default("").String()
//########http######### //########http#########
http := app.Command("http", "proxy on http mode") http := app.Command("http", "proxy on http mode")
httpArgs.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String() httpArgs.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
@ -93,6 +102,33 @@ func initConfig() (err error) {
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() udpArgs.Local = udp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
//########mux-server#########
muxServer := app.Command("server", "proxy on mux server mode")
muxServerArgs.Parent = muxServer.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
muxServerArgs.CertFile = muxServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
muxServerArgs.KeyFile = muxServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxServerArgs.Timeout = muxServer.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
muxServerArgs.IsUDP = muxServer.Flag("udp", "proxy on udp mux server mode").Default("false").Bool()
muxServerArgs.Key = muxServer.Flag("k", "client key").Default("default").String()
muxServerArgs.Route = muxServer.Flag("route", "local route to client's network, such as: PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings()
muxServerArgs.IsCompress = muxServer.Flag("c", "compress data when tcp mode").Default("false").Bool()
//########mux-client#########
muxClient := app.Command("client", "proxy on mux client mode")
muxClientArgs.Parent = muxClient.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
muxClientArgs.CertFile = muxClient.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
muxClientArgs.KeyFile = muxClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxClientArgs.Timeout = muxClient.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
muxClientArgs.Key = muxClient.Flag("k", "key same with server").Default("default").String()
muxClientArgs.IsCompress = muxClient.Flag("c", "compress data when tcp mode").Default("false").Bool()
//########mux-bridge#########
muxBridge := app.Command("bridge", "proxy on mux bridge mode")
muxBridgeArgs.CertFile = muxBridge.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
muxBridgeArgs.KeyFile = muxBridge.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
muxBridgeArgs.Timeout = muxBridge.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
muxBridgeArgs.Local = muxBridge.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.Parent = tunnelServer.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
@ -101,7 +137,7 @@ func initConfig() (err error) {
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", "client key").Default("default").String() tunnelServerArgs.Key = tunnelServer.Flag("k", "client key").Default("default").String()
tunnelServerArgs.Route = tunnelServer.Flag("route", "local route to client's network, such as :PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings() tunnelServerArgs.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")
@ -150,6 +186,20 @@ func initConfig() (err error) {
//parse args //parse args
serviceName := kingpin.MustParse(app.Parse(os.Args[1:])) serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
flags := log.Ldate flags := log.Ldate
if *debug {
flags |= log.Lshortfile | log.Lmicroseconds
} else {
flags |= log.Ltime
}
log.SetFlags(flags)
if *logfile != "" {
f, e := os.OpenFile(*logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if e != nil {
log.Fatal(e)
}
log.SetOutput(f)
}
if *daemon { if *daemon {
args := []string{} args := []string{}
for _, arg := range os.Args[1:] { for _, arg := range os.Args[1:] {
@ -157,18 +207,69 @@ func initConfig() (err error) {
args = append(args, arg) args = append(args, arg)
} }
} }
cmd := exec.Command(os.Args[0], args...) cmd = exec.Command(os.Args[0], args...)
cmd.Start() cmd.Start()
fmt.Printf("%s [PID] %d running...\n", os.Args[0], cmd.Process.Pid) f := ""
if *forever {
f = "forever "
}
log.Printf("%s%s [PID] %d running...\n", f, os.Args[0], cmd.Process.Pid)
os.Exit(0) os.Exit(0)
} }
if *debug { if *forever {
flags |= log.Lshortfile | log.Lmicroseconds args := []string{}
} else { for _, arg := range os.Args[1:] {
flags |= log.Ltime if arg != "--forever" {
args = append(args, arg)
}
}
go func() {
for {
if cmd != nil {
cmd.Process.Kill()
}
cmd = exec.Command(os.Args[0], args...)
cmdReaderStderr, err := cmd.StderrPipe()
if err != nil {
log.Printf("ERR:%s,restarting...\n", err)
continue
}
cmdReader, err := cmd.StdoutPipe()
if err != nil {
log.Printf("ERR:%s,restarting...\n", err)
continue
}
scanner := bufio.NewScanner(cmdReader)
scannerStdErr := bufio.NewScanner(cmdReaderStderr)
go func() {
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}()
go func() {
for scannerStdErr.Scan() {
fmt.Println(scannerStdErr.Text())
}
}()
if err := cmd.Start(); err != nil {
log.Printf("ERR:%s,restarting...\n", err)
continue
}
pid := cmd.Process.Pid
log.Printf("worker %s [PID] %d running...\n", os.Args[0], pid)
if err := cmd.Wait(); err != nil {
log.Printf("ERR:%s,restarting...", err)
continue
}
log.Printf("worker %s [PID] %d unexpected exited, restarting...\n", os.Args[0], pid)
time.Sleep(time.Second * 5)
}
}()
return
}
if *logfile == "" {
poster()
} }
log.SetFlags(flags)
poster()
//regist services and run service //regist services and run service
services.Regist("http", services.NewHTTP(), httpArgs) services.Regist("http", services.NewHTTP(), httpArgs)
services.Regist("tcp", services.NewTCP(), tcpArgs) services.Regist("tcp", services.NewTCP(), tcpArgs)
@ -176,6 +277,9 @@ func initConfig() (err error) {
services.Regist("tserver", services.NewTunnelServerManager(), 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("server", services.NewMuxServerManager(), muxServerArgs)
services.Regist("client", services.NewMuxClient(), muxClientArgs)
services.Regist("bridge", services.NewMuxBridge(), muxBridgeArgs)
services.Regist("socks", services.NewSocks(), socksArgs) services.Regist("socks", services.NewSocks(), socksArgs)
service, err = services.Run(serviceName) service, err = services.Run(serviceName)
if err != nil { if err != nil {

View File

@ -1,12 +1,6 @@
#!/bin/bash #!/bin/bash
set -e set -e
# install monexec
tar zxvf monexec_0.1.1_linux_amd64.tar.gz
cd monexec_0.1.1_linux_amd64
cp monexec /usr/bin/
chmod +x /usr/bin/monexec
cd ..
# #install proxy # #install proxy
tar zxvf proxy-linux-amd64.tar.gz tar zxvf proxy-linux-amd64.tar.gz
cp proxy /usr/bin/ cp proxy /usr/bin/

View File

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

19
main.go
View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"fmt"
"log" "log"
"os" "os"
"os/signal" "os/signal"
@ -9,14 +8,18 @@ import (
"syscall" "syscall"
) )
const APP_VERSION = "3.6" const APP_VERSION = "4.0"
func main() { func main() {
err := initConfig() err := initConfig()
if err != nil { if err != nil {
log.Fatalf("err : %s", err) log.Fatalf("err : %s", err)
} }
Clean(&service.S) if service != nil && service.S != nil {
Clean(&service.S)
} else {
Clean(nil)
}
} }
func Clean(s *services.Service) { func Clean(s *services.Service) {
signalChan := make(chan os.Signal, 1) signalChan := make(chan os.Signal, 1)
@ -29,8 +32,14 @@ func Clean(s *services.Service) {
syscall.SIGQUIT) syscall.SIGQUIT)
go func() { go func() {
for _ = range signalChan { for _ = range signalChan {
fmt.Println("\nReceived an interrupt, stopping services...") log.Println("Received an interrupt, stopping services...")
(*s).Clean() if s != nil && *s != nil {
(*s).Clean()
}
if cmd != nil {
log.Printf("clean process %d", cmd.Process.Pid)
cmd.Process.Kill()
}
cleanupDone <- true cleanupDone <- true
} }
}() }()

View File

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
VER="3.6" VER="4.0"
RELEASE="release-${VER}" RELEASE="release-${VER}"
rm -rf .cert rm -rf .cert
mkdir .cert mkdir .cert
@ -13,8 +13,12 @@ export CGO_ENABLED=0
#linux #linux
GOOS=linux GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-linux-386.tar.gz" proxy direct blocked GOOS=linux GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-linux-386.tar.gz" proxy direct blocked
GOOS=linux GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-linux-amd64.tar.gz" proxy direct blocked GOOS=linux GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-linux-amd64.tar.gz" proxy direct blocked
GOOS=linux GOARCH=arm GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm.tar.gz" proxy direct blocked GOOS=linux GOARCH=arm GOARM=6 go build && tar zcfv "${RELEASE}/proxy-linux-arm-v6.tar.gz" proxy direct blocked
GOOS=linux GOARCH=arm64 GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm64.tar.gz" proxy direct blocked GOOS=linux GOARCH=arm64 GOARM=6 go build && tar zcfv "${RELEASE}/proxy-linux-arm64-v6.tar.gz" proxy direct blocked
GOOS=linux GOARCH=arm GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm-v7.tar.gz" proxy direct blocked
GOOS=linux GOARCH=arm64 GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm64-v7.tar.gz" proxy direct blocked
GOOS=linux GOARCH=arm GOARM=8 go build && tar zcfv "${RELEASE}/proxy-linux-arm-v8.tar.gz" proxy direct blocked
GOOS=linux GOARCH=arm64 GOARM=8 go build && tar zcfv "${RELEASE}/proxy-linux-arm64-v8.tar.gz" proxy direct blocked
GOOS=linux GOARCH=mips go build && tar zcfv "${RELEASE}/proxy-linux-mips.tar.gz" proxy direct blocked GOOS=linux GOARCH=mips go build && tar zcfv "${RELEASE}/proxy-linux-mips.tar.gz" proxy direct blocked
GOOS=linux GOARCH=mips64 go build && tar zcfv "${RELEASE}/proxy-linux-mips64.tar.gz" proxy direct blocked GOOS=linux GOARCH=mips64 go build && tar zcfv "${RELEASE}/proxy-linux-mips64.tar.gz" proxy direct blocked
GOOS=linux GOARCH=mips64le go build && tar zcfv "${RELEASE}/proxy-linux-mips64le.tar.gz" proxy direct blocked GOOS=linux GOARCH=mips64le go build && tar zcfv "${RELEASE}/proxy-linux-mips64le.tar.gz" proxy direct blocked

View File

@ -16,8 +16,45 @@ const (
CONN_SERVER_HEARBEAT = uint8(3) CONN_SERVER_HEARBEAT = uint8(3)
CONN_SERVER = uint8(4) CONN_SERVER = uint8(4)
CONN_CLIENT = uint8(5) CONN_CLIENT = uint8(5)
CONN_SERVER_MUX = uint8(6)
CONN_CLIENT_MUX = uint8(7)
) )
type MuxServerArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
IsUDP *bool
Key *string
Remote *string
Timeout *int
Route *[]string
Mgr *MuxServerManager
IsCompress *bool
}
type MuxClientArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Key *string
Timeout *int
IsCompress *bool
}
type MuxBridgeArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
Timeout *int
IsCompress *bool
}
type TunnelServerArgs struct { type TunnelServerArgs struct {
Parent *string Parent *string
CertFile *string CertFile *string
@ -31,6 +68,7 @@ type TunnelServerArgs struct {
Timeout *int Timeout *int
Route *[]string Route *[]string
Mgr *TunnelServerManager Mgr *TunnelServerManager
Mux *bool
} }
type TunnelClientArgs struct { type TunnelClientArgs struct {
Parent *string Parent *string
@ -40,6 +78,7 @@ type TunnelClientArgs struct {
KeyBytes []byte KeyBytes []byte
Key *string Key *string
Timeout *int Timeout *int
Mux *bool
} }
type TunnelBridgeArgs struct { type TunnelBridgeArgs struct {
Parent *string Parent *string
@ -49,6 +88,7 @@ type TunnelBridgeArgs struct {
KeyBytes []byte KeyBytes []byte
Local *string Local *string
Timeout *int Timeout *int
Mux *bool
} }
type TCPArgs struct { type TCPArgs struct {
Parent *string Parent *string

146
services/mux_bridge.go Normal file
View File

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

206
services/mux_client.go Normal file
View File

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

333
services/mux_server.go Normal file
View File

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

View File

@ -492,11 +492,11 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn n
err = fmt.Errorf("write req detail fail,%s", err) err = fmt.Errorf("write req detail fail,%s", err)
return return
} }
// _, err = outConn.Read(buf) _, err = outConn.Read(buf)
// if err != nil { if err != nil {
// err = fmt.Errorf("read req reply fail,%s", err) err = fmt.Errorf("read req reply fail,%s", err)
// return return
// } }
//result := buf[:n] //result := buf[:n]
//log.Printf("result:%v", result) //log.Printf("result:%v", result)

View File

@ -17,8 +17,8 @@ type TunnelBridge struct {
cfg TunnelBridgeArgs cfg TunnelBridgeArgs
serverConns utils.ConcurrentMap serverConns utils.ConcurrentMap
clientControlConns utils.ConcurrentMap clientControlConns utils.ConcurrentMap
cmServer utils.ConnManager // cmServer utils.ConnManager
cmClient utils.ConnManager // cmClient utils.ConnManager
} }
func NewTunnelBridge() Service { func NewTunnelBridge() Service {
@ -26,8 +26,8 @@ func NewTunnelBridge() Service {
cfg: TunnelBridgeArgs{}, cfg: TunnelBridgeArgs{},
serverConns: utils.NewConcurrentMap(), serverConns: utils.NewConcurrentMap(),
clientControlConns: utils.NewConcurrentMap(), clientControlConns: utils.NewConcurrentMap(),
cmServer: utils.NewConnManager(), // cmServer: utils.NewConnManager(),
cmClient: utils.NewConnManager(), // cmClient: utils.NewConnManager(),
} }
} }
@ -92,7 +92,7 @@ func (s *TunnelBridge) Start(args interface{}) (err error) {
time.Sleep(time.Second * 3) time.Sleep(time.Second * 3)
continue continue
} else { } else {
s.cmServer.Add(serverID, ID, &inConn) // s.cmServer.Add(serverID, ID, &inConn)
break break
} }
} }
@ -114,11 +114,11 @@ func (s *TunnelBridge) Start(args interface{}) (err error) {
serverConn := serverConnItem.(ServerConn).Conn serverConn := serverConnItem.(ServerConn).Conn
utils.IoBind(*serverConn, inConn, func(err interface{}) { utils.IoBind(*serverConn, inConn, func(err interface{}) {
s.serverConns.Remove(ID) s.serverConns.Remove(ID)
s.cmClient.RemoveOne(key, ID) // s.cmClient.RemoveOne(key, ID)
s.cmServer.RemoveOne(serverID, ID) // s.cmServer.RemoveOne(serverID, ID)
log.Printf("conn %s released", ID) log.Printf("conn %s released", ID)
}) })
s.cmClient.Add(key, ID, &inConn) // s.cmClient.Add(key, ID, &inConn)
log.Printf("conn %s created", ID) log.Printf("conn %s created", ID)
case CONN_CLIENT_CONTROL: case CONN_CLIENT_CONTROL:
@ -136,101 +136,101 @@ func (s *TunnelBridge) Start(args interface{}) (err error) {
s.clientControlConns.Set(key, &inConn) s.clientControlConns.Set(key, &inConn)
log.Printf("set client %s control conn", key) log.Printf("set client %s control conn", key)
case CONN_SERVER_HEARBEAT: // case CONN_SERVER_HEARBEAT:
var serverID string // var serverID string
err = utils.ReadPacketData(reader, &serverID) // err = utils.ReadPacketData(reader, &serverID)
if err != nil { // if err != nil {
log.Printf("read error,ERR:%s", err) // log.Printf("read error,ERR:%s", err)
return // return
} // }
log.Printf("server heartbeat connection, id: %s", serverID) // log.Printf("server heartbeat connection, id: %s", serverID)
writeDie := make(chan bool) // writeDie := make(chan bool)
readDie := make(chan bool) // readDie := make(chan bool)
go func() { // go func() {
for { // for {
inConn.SetWriteDeadline(time.Now().Add(time.Second * 3)) // inConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
_, err = inConn.Write([]byte{0x00}) // _, err = inConn.Write([]byte{0x00})
inConn.SetWriteDeadline(time.Time{}) // inConn.SetWriteDeadline(time.Time{})
if err != nil { // if err != nil {
log.Printf("server heartbeat connection write err %s", err) // log.Printf("server heartbeat connection write err %s", err)
break // break
} // }
time.Sleep(time.Second * 3) // time.Sleep(time.Second * 3)
} // }
close(writeDie) // close(writeDie)
}() // }()
go func() { // go func() {
for { // for {
signal := make([]byte, 1) // signal := make([]byte, 1)
inConn.SetReadDeadline(time.Now().Add(time.Second * 6)) // inConn.SetReadDeadline(time.Now().Add(time.Second * 6))
_, err := inConn.Read(signal) // _, err := inConn.Read(signal)
inConn.SetReadDeadline(time.Time{}) // inConn.SetReadDeadline(time.Time{})
if err != nil { // if err != nil {
log.Printf("server heartbeat connection read err: %s", err) // log.Printf("server heartbeat connection read err: %s", err)
break // break
} else { // } else {
// log.Printf("heartbeat from server ,id:%s", serverID) // // log.Printf("heartbeat from server ,id:%s", serverID)
} // }
} // }
close(readDie) // close(readDie)
}() // }()
select { // select {
case <-readDie: // case <-readDie:
case <-writeDie: // case <-writeDie:
} // }
utils.CloseConn(&inConn) // utils.CloseConn(&inConn)
s.cmServer.Remove(serverID) // s.cmServer.Remove(serverID)
log.Printf("server heartbeat conn %s released", serverID) // log.Printf("server heartbeat conn %s released", serverID)
case CONN_CLIENT_HEARBEAT: // case CONN_CLIENT_HEARBEAT:
var clientID string // var clientID string
err = utils.ReadPacketData(reader, &clientID) // err = utils.ReadPacketData(reader, &clientID)
if err != nil { // if err != nil {
log.Printf("read error,ERR:%s", err) // log.Printf("read error,ERR:%s", err)
return // return
} // }
log.Printf("client heartbeat connection, id: %s", clientID) // log.Printf("client heartbeat connection, id: %s", clientID)
writeDie := make(chan bool) // writeDie := make(chan bool)
readDie := make(chan bool) // readDie := make(chan bool)
go func() { // go func() {
for { // for {
inConn.SetWriteDeadline(time.Now().Add(time.Second * 3)) // inConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
_, err = inConn.Write([]byte{0x00}) // _, err = inConn.Write([]byte{0x00})
inConn.SetWriteDeadline(time.Time{}) // inConn.SetWriteDeadline(time.Time{})
if err != nil { // if err != nil {
log.Printf("client heartbeat connection write err %s", err) // log.Printf("client heartbeat connection write err %s", err)
break // break
} // }
time.Sleep(time.Second * 3) // time.Sleep(time.Second * 3)
} // }
close(writeDie) // close(writeDie)
}() // }()
go func() { // go func() {
for { // for {
signal := make([]byte, 1) // signal := make([]byte, 1)
inConn.SetReadDeadline(time.Now().Add(time.Second * 6)) // inConn.SetReadDeadline(time.Now().Add(time.Second * 6))
_, err := inConn.Read(signal) // _, err := inConn.Read(signal)
inConn.SetReadDeadline(time.Time{}) // inConn.SetReadDeadline(time.Time{})
if err != nil { // if err != nil {
log.Printf("client control connection read err: %s", err) // log.Printf("client control connection read err: %s", err)
break // break
} else { // } else {
// log.Printf("heartbeat from client ,id:%s", clientID) // // log.Printf("heartbeat from client ,id:%s", clientID)
} // }
} // }
close(readDie) // close(readDie)
}() // }()
select { // select {
case <-readDie: // case <-readDie:
case <-writeDie: // case <-writeDie:
} // }
utils.CloseConn(&inConn) // utils.CloseConn(&inConn)
s.cmClient.Remove(clientID) // s.cmClient.Remove(clientID)
if s.clientControlConns.Has(clientID) { // if s.clientControlConns.Has(clientID) {
item, _ := s.clientControlConns.Get(clientID) // item, _ := s.clientControlConns.Get(clientID)
(*item.(*net.Conn)).Close() // (*item.(*net.Conn)).Close()
} // }
s.clientControlConns.Remove(clientID) // s.clientControlConns.Remove(clientID)
log.Printf("client heartbeat conn %s released", clientID) // log.Printf("client heartbeat conn %s released", clientID)
} }
}) })
if err != nil { if err != nil {

View File

@ -11,79 +11,80 @@ import (
) )
type TunnelClient struct { type TunnelClient struct {
cfg TunnelClientArgs cfg TunnelClientArgs
cm utils.ConnManager // cm utils.ConnManager
ctrlConn net.Conn ctrlConn net.Conn
} }
func NewTunnelClient() Service { func NewTunnelClient() Service {
return &TunnelClient{ return &TunnelClient{
cfg: TunnelClientArgs{}, cfg: TunnelClientArgs{},
cm: utils.NewConnManager(), // cm: utils.NewConnManager(),
} }
} }
func (s *TunnelClient) InitService() { func (s *TunnelClient) InitService() {
s.InitHeartbeatDeamon() // s.InitHeartbeatDeamon()
} }
func (s *TunnelClient) InitHeartbeatDeamon() {
log.Printf("heartbeat started")
go func() {
var heartbeatConn net.Conn
var ID = *s.cfg.Key
for {
//close all connection // func (s *TunnelClient) InitHeartbeatDeamon() {
s.cm.RemoveAll() // log.Printf("heartbeat started")
if s.ctrlConn != nil { // go func() {
s.ctrlConn.Close() // var heartbeatConn net.Conn
} // var ID = *s.cfg.Key
utils.CloseConn(&heartbeatConn) // for {
heartbeatConn, err := s.GetInConn(CONN_CLIENT_HEARBEAT, ID)
if err != nil { // //close all connection
log.Printf("heartbeat connection err: %s, retrying...", err) // s.cm.RemoveAll()
time.Sleep(time.Second * 3) // if s.ctrlConn != nil {
utils.CloseConn(&heartbeatConn) // s.ctrlConn.Close()
continue // }
} // utils.CloseConn(&heartbeatConn)
log.Printf("heartbeat connection created,id:%s", ID) // heartbeatConn, err := s.GetInConn(CONN_CLIENT_HEARBEAT, ID)
writeDie := make(chan bool) // if err != nil {
readDie := make(chan bool) // log.Printf("heartbeat connection err: %s, retrying...", err)
go func() { // time.Sleep(time.Second * 3)
for { // utils.CloseConn(&heartbeatConn)
heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3)) // continue
_, err = heartbeatConn.Write([]byte{0x00}) // }
heartbeatConn.SetWriteDeadline(time.Time{}) // log.Printf("heartbeat connection created,id:%s", ID)
if err != nil { // writeDie := make(chan bool)
log.Printf("heartbeat connection write err %s", err) // readDie := make(chan bool)
break // go func() {
} // for {
time.Sleep(time.Second * 3) // heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
} // _, err = heartbeatConn.Write([]byte{0x00})
close(writeDie) // heartbeatConn.SetWriteDeadline(time.Time{})
}() // if err != nil {
go func() { // log.Printf("heartbeat connection write err %s", err)
for { // break
signal := make([]byte, 1) // }
heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6)) // time.Sleep(time.Second * 3)
_, err := heartbeatConn.Read(signal) // }
heartbeatConn.SetReadDeadline(time.Time{}) // close(writeDie)
if err != nil { // }()
log.Printf("heartbeat connection read err: %s", err) // go func() {
break // for {
} else { // signal := make([]byte, 1)
//log.Printf("heartbeat from bridge") // heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6))
} // _, err := heartbeatConn.Read(signal)
} // heartbeatConn.SetReadDeadline(time.Time{})
close(readDie) // if err != nil {
}() // log.Printf("heartbeat connection read err: %s", err)
select { // break
case <-readDie: // } else {
case <-writeDie: // //log.Printf("heartbeat from bridge")
} // }
} // }
}() // close(readDie)
} // }()
// select {
// case <-readDie:
// case <-writeDie:
// }
// }
// }()
// }
func (s *TunnelClient) CheckArgs() { 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)
@ -96,7 +97,7 @@ func (s *TunnelClient) CheckArgs() {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
} }
func (s *TunnelClient) StopService() { func (s *TunnelClient) StopService() {
s.cm.RemoveAll() // s.cm.RemoveAll()
} }
func (s *TunnelClient) Start(args interface{}) (err error) { func (s *TunnelClient) Start(args interface{}) (err error) {
s.cfg = args.(TunnelClientArgs) s.cfg = args.(TunnelClientArgs)
@ -106,7 +107,7 @@ func (s *TunnelClient) Start(args interface{}) (err error) {
for { for {
//close all conn //close all conn
s.cm.Remove(*s.cfg.Key) // s.cm.Remove(*s.cfg.Key)
if s.ctrlConn != nil { if s.ctrlConn != nil {
s.ctrlConn.Close() s.ctrlConn.Close()
} }
@ -171,7 +172,7 @@ func (s *TunnelClient) ServeUDP(localAddr, ID, serverID string) {
var err error var err error
// for { // for {
for { for {
s.cm.RemoveOne(*s.cfg.Key, ID) // s.cm.RemoveOne(*s.cfg.Key, ID)
inConn, err = s.GetInConn(CONN_CLIENT, *s.cfg.Key, ID, serverID) inConn, err = s.GetInConn(CONN_CLIENT, *s.cfg.Key, ID, serverID)
if err != nil { if err != nil {
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
@ -182,7 +183,7 @@ func (s *TunnelClient) ServeUDP(localAddr, ID, serverID string) {
break break
} }
} }
s.cm.Add(*s.cfg.Key, ID, &inConn) // s.cm.Add(*s.cfg.Key, ID, &inConn)
log.Printf("conn %s created", ID) log.Printf("conn %s created", ID)
for { for {
@ -275,8 +276,8 @@ func (s *TunnelClient) ServeConn(localAddr, ID, serverID string) {
} }
utils.IoBind(inConn, outConn, func(err interface{}) { utils.IoBind(inConn, outConn, func(err interface{}) {
log.Printf("conn %s released", ID) log.Printf("conn %s released", ID)
s.cm.RemoveOne(*s.cfg.Key, ID) // s.cm.RemoveOne(*s.cfg.Key, ID)
}) })
s.cm.Add(*s.cfg.Key, ID, &inConn) // s.cm.Add(*s.cfg.Key, ID, &inConn)
log.Printf("conn %s created", ID) log.Printf("conn %s created", ID)
} }

View File

@ -24,7 +24,7 @@ type TunnelServerManager struct {
udpChn chan UDPItem udpChn chan UDPItem
sc utils.ServerChannel sc utils.ServerChannel
serverID string serverID string
cm utils.ConnManager // cm utils.ConnManager
} }
func NewTunnelServerManager() Service { func NewTunnelServerManager() Service {
@ -32,7 +32,7 @@ func NewTunnelServerManager() Service {
cfg: TunnelServerArgs{}, cfg: TunnelServerArgs{},
udpChn: make(chan UDPItem, 50000), udpChn: make(chan UDPItem, 50000),
serverID: utils.Uniqueid(), serverID: utils.Uniqueid(),
cm: utils.NewConnManager(), // cm: utils.NewConnManager(),
} }
} }
func (s *TunnelServerManager) Start(args interface{}) (err error) { func (s *TunnelServerManager) Start(args interface{}) (err error) {
@ -91,7 +91,7 @@ func (s *TunnelServerManager) Clean() {
s.StopService() s.StopService()
} }
func (s *TunnelServerManager) StopService() { func (s *TunnelServerManager) StopService() {
s.cm.RemoveAll() // s.cm.RemoveAll()
} }
func (s *TunnelServerManager) CheckArgs() { func (s *TunnelServerManager) CheckArgs() {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
@ -100,62 +100,63 @@ func (s *TunnelServerManager) CheckArgs() {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
} }
func (s *TunnelServerManager) InitService() { func (s *TunnelServerManager) InitService() {
s.InitHeartbeatDeamon() // s.InitHeartbeatDeamon()
}
func (s *TunnelServerManager) InitHeartbeatDeamon() {
log.Printf("heartbeat started")
go func() {
var heartbeatConn net.Conn
var ID string
for {
//close all connection
s.cm.Remove(ID)
utils.CloseConn(&heartbeatConn)
heartbeatConn, ID, err := s.GetOutConn(CONN_SERVER_HEARBEAT)
if err != nil {
log.Printf("heartbeat connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
utils.CloseConn(&heartbeatConn)
continue
}
log.Printf("heartbeat connection created,id:%s", ID)
writeDie := make(chan bool)
readDie := make(chan bool)
go func() {
for {
heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
_, err = heartbeatConn.Write([]byte{0x00})
heartbeatConn.SetWriteDeadline(time.Time{})
if err != nil {
log.Printf("heartbeat connection write err %s", err)
break
}
time.Sleep(time.Second * 3)
}
close(writeDie)
}()
go func() {
for {
signal := make([]byte, 1)
heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6))
_, err := heartbeatConn.Read(signal)
heartbeatConn.SetReadDeadline(time.Time{})
if err != nil {
log.Printf("heartbeat connection read err: %s", err)
break
} else {
// log.Printf("heartbeat from bridge")
}
}
close(readDie)
}()
select {
case <-readDie:
case <-writeDie:
}
}
}()
} }
// func (s *TunnelServerManager) InitHeartbeatDeamon() {
// log.Printf("heartbeat started")
// go func() {
// var heartbeatConn net.Conn
// var ID string
// for {
// //close all connection
// s.cm.Remove(ID)
// utils.CloseConn(&heartbeatConn)
// heartbeatConn, ID, err := s.GetOutConn(CONN_SERVER_HEARBEAT)
// if err != nil {
// log.Printf("heartbeat connection err: %s, retrying...", err)
// time.Sleep(time.Second * 3)
// utils.CloseConn(&heartbeatConn)
// continue
// }
// log.Printf("heartbeat connection created,id:%s", ID)
// writeDie := make(chan bool)
// readDie := make(chan bool)
// go func() {
// for {
// heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
// _, err = heartbeatConn.Write([]byte{0x00})
// heartbeatConn.SetWriteDeadline(time.Time{})
// if err != nil {
// log.Printf("heartbeat connection write err %s", err)
// break
// }
// time.Sleep(time.Second * 3)
// }
// close(writeDie)
// }()
// go func() {
// for {
// signal := make([]byte, 1)
// heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6))
// _, err := heartbeatConn.Read(signal)
// heartbeatConn.SetReadDeadline(time.Time{})
// if err != nil {
// log.Printf("heartbeat connection read err: %s", err)
// break
// } else {
// // log.Printf("heartbeat from bridge")
// }
// }
// close(readDie)
// }()
// select {
// case <-readDie:
// case <-writeDie:
// }
// }
// }()
// }
func (s *TunnelServerManager) GetOutConn(typ uint8) (outConn net.Conn, ID string, err error) { func (s *TunnelServerManager) GetOutConn(typ uint8) (outConn net.Conn, ID string, err error) {
outConn, err = s.GetConn() outConn, err = s.GetConn()
if err != nil { if err != nil {
@ -241,11 +242,11 @@ func (s *TunnelServer) Start(args interface{}) (err error) {
} }
} }
utils.IoBind(inConn, outConn, func(err interface{}) { utils.IoBind(inConn, outConn, func(err interface{}) {
s.cfg.Mgr.cm.RemoveOne(s.cfg.Mgr.serverID, ID) // s.cfg.Mgr.cm.RemoveOne(s.cfg.Mgr.serverID, ID)
log.Printf("%s conn %s released", *s.cfg.Key, ID) log.Printf("%s conn %s released", *s.cfg.Key, ID)
}) })
//add conn //add conn
s.cfg.Mgr.cm.Add(s.cfg.Mgr.serverID, ID, &inConn) // s.cfg.Mgr.cm.Add(s.cfg.Mgr.serverID, ID, &inConn)
log.Printf("%s conn %s created", *s.cfg.Key, ID) log.Printf("%s conn %s created", *s.cfg.Key, ID)
}) })
if err != nil { if err != nil {

View File

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

View File

@ -12,7 +12,6 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"math/rand"
"net" "net"
"net/http" "net/http"
"os" "os"
@ -20,6 +19,7 @@ import (
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
"proxy/utils/id"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -42,7 +42,8 @@ func IoBind(dst io.ReadWriteCloser, src io.ReadWriteCloser, fn func(err interfac
log.Printf("bind crashed %s", err) log.Printf("bind crashed %s", err)
} }
}() }()
_, err := io.Copy(dst, src) //_, err := io.Copy(dst, src)
err := ioCopy(dst, src)
e1 <- err e1 <- err
}() }()
go func() { go func() {
@ -51,7 +52,8 @@ func IoBind(dst io.ReadWriteCloser, src io.ReadWriteCloser, fn func(err interfac
log.Printf("bind crashed %s", err) log.Printf("bind crashed %s", err)
} }
}() }()
_, err := io.Copy(src, dst) //_, err := io.Copy(src, dst)
err := ioCopy(src, dst)
e2 <- err e2 <- err
}() }()
var err interface{} var err interface{}
@ -299,9 +301,10 @@ func ReadUDPPacket(_reader io.Reader) (srcAddr string, packet []byte, err error)
return return
} }
func Uniqueid() string { func Uniqueid() string {
var src = rand.NewSource(time.Now().UnixNano()) return xid.New().String()
s := fmt.Sprintf("%d", src.Int63()) // var src = rand.NewSource(time.Now().UnixNano())
return s[len(s)-5:len(s)-1] + fmt.Sprintf("%d", uint64(time.Now().UnixNano()))[8:] // s := fmt.Sprintf("%d", src.Int63())
// return s[len(s)-5:len(s)-1] + fmt.Sprintf("%d", uint64(time.Now().UnixNano()))[8:]
} }
func ReadData(r io.Reader) (data string, err error) { func ReadData(r io.Reader) (data string, err error) {
var len uint16 var len uint16

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

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