Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee93171c63 | ||
|
|
ddd2302cb2 | ||
|
|
c96d2288b3 | ||
|
|
6f5a088091 | ||
|
|
9a07797e29 | ||
|
|
055a020d33 | ||
|
|
4681ff3827 | ||
|
|
cff92faf06 | ||
|
|
890daf5489 | ||
|
|
182bdeb766 | ||
|
|
a4a953b167 | ||
|
|
ff37b7e18c | ||
|
|
7aa0e78c15 | ||
|
|
d798807693 | ||
|
|
35b78c2da6 | ||
|
|
66a4291c97 | ||
|
|
e89a965aff | ||
|
|
85a9f10be4 | ||
|
|
8bc6e0ffec | ||
|
|
98fc0ade4a | ||
|
|
f5626c21f7 | ||
|
|
b5a76c7ff2 | ||
|
|
612bae4c93 | ||
|
|
e45bf338cb | ||
|
|
577261806c | ||
|
|
8842097bd4 | ||
|
|
a4b14dd0bd | ||
|
|
94f0142c7d | ||
|
|
319affa43f | ||
|
|
14f43a5976 | ||
|
|
9e9a9ac6de | ||
|
|
cc24cfc26f | ||
|
|
712f7dae4a | ||
|
|
6d20908465 | ||
|
|
e2f0fe71f4 | ||
|
|
1241b74562 | ||
|
|
2d610003d5 | ||
|
|
7b2952f4d6 | ||
|
|
b18c5e08bb | ||
|
|
63cb67f009 | ||
|
|
cd212cb978 | ||
|
|
d934c1fb4a | ||
|
|
c8327b4075 | ||
|
|
8410930d2d | ||
|
|
f5218a93f6 | ||
|
|
ca2f367950 | ||
|
|
c8559b757f | ||
|
|
db620ebe83 | ||
|
|
abc7a4fa42 | ||
|
|
b02d75eb3a |
27
CHANGELOG
27
CHANGELOG
@ -1,4 +1,31 @@
|
||||
proxy更新日志
|
||||
v3.9
|
||||
1.增加了守护运行参数--forever,比如: proxy http --forever ,
|
||||
proxy会fork子进程,然后监控子进程,如果子进程异常退出,5秒后重启子进程.
|
||||
该参数配合后台运行参数--daemon和日志参数--log,可以保障proxy一直在后台执行不会因为意外退出,
|
||||
而且可以通过日志文件看到proxy的输出日志内容.
|
||||
比如: proxy http -p ":9090" --forever --log proxy.log --daemon
|
||||
|
||||
v3.8
|
||||
1.增加了日志输出到文件--log参数,比如: --log proxy.log,日志就会输出到proxy.log方便排除问题.
|
||||
|
||||
v3.7
|
||||
1.修复了socks代理不能正常和上级代理通讯的问题.
|
||||
|
||||
|
||||
v3.6
|
||||
1.http(s),socks代理,集成了外部HTTP API认证,可以通过外部API对用户名和密码进行认证.
|
||||
2.手册http(s),socks代理认证部分增加了集成外部HTTP API认证的使用说明.
|
||||
|
||||
v3.5
|
||||
1.优化了kcp参数,速度有所提升.
|
||||
2.修复了socks无法正常工作的问题.
|
||||
3.修正了文档中的一些描述.
|
||||
4.tcp代理增加了kcp协议传输数据.
|
||||
5.优化了死循环检查,增加了添加本地IP参数,当VPS在nat设备后面,
|
||||
vps上网卡IP都是内网IP,这个时候可以通过-g参数添加vps的外网ip防止死循环.
|
||||
6.增加了--daemon参数,可以后台运行程序哟.
|
||||
|
||||
v3.4
|
||||
1.socks5代理新增了用户名密码验证支持.
|
||||
2.socks5,http(s)代理增加了kcp传输协议支持.
|
||||
|
||||
131
README.md
131
README.md
@ -13,10 +13,12 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
|
||||
- 跨平台性,无论你是widows,linux,还是mac,甚至是树莓派,都可以很好的运行proxy.
|
||||
- 多协议支持,支持HTTP(S),TCP,UDP,Websocket,SOCKS5代理.
|
||||
- 支持内网穿透,协议支持TCP和UDP.
|
||||
- HTTP(S),SOCKS5代理支持SSH中转,上级Linux服务器不需要任何服务端,本地一个proxy即可开心上网.
|
||||
- SSH中转,HTTP(S),SOCKS5代理支持SSH中转,上级Linux服务器不需要任何服务端,本地一个proxy即可开心上网.
|
||||
- [KCP](https://github.com/xtaci/kcp-go)协议支持,HTTP(S),SOCKS5代理支持KCP协议传输数据,降低延迟,提升浏览体验.
|
||||
- 集成外部API,HTTP(S),SOCKS5代理认证功能可以与外部HTTP API集成,可以方便的通过外部系统控制代理用户.
|
||||
|
||||
### Why need these?
|
||||
- 当由于安全因素或者限制,我们不能顺畅的访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道,顺畅的访问我们的服务.
|
||||
- 当由于某某原因,我们不能访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道访问我们的服务.
|
||||
- 微信接口本地开发,方便调试.
|
||||
- 远程访问内网机器.
|
||||
- 和小伙伴一起玩局域网游戏.
|
||||
@ -25,21 +27,33 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
|
||||
- ...
|
||||
|
||||
|
||||
本页是v3.4手册,其他版本手册请点击下面链接查看.
|
||||
本页是v3.9手册,其他版本手册请点击下面链接查看.
|
||||
- [v3.8手册](https://github.com/snail007/goproxy/tree/v3.8)
|
||||
- [v3.6-v3.7手册](https://github.com/snail007/goproxy/tree/v3.6)
|
||||
- [v3.5手册](https://github.com/snail007/goproxy/tree/v3.5)
|
||||
- [v3.4手册](https://github.com/snail007/goproxy/tree/v3.4)
|
||||
- [v3.3手册](https://github.com/snail007/goproxy/tree/v3.3)
|
||||
- [v3.2手册](https://github.com/snail007/goproxy/tree/v3.2)
|
||||
- [v3.1手册](https://github.com/snail007/goproxy/tree/v3.1)
|
||||
- [v3.0手册](https://github.com/snail007/goproxy/tree/v3.0)
|
||||
- [v2.x手册](https://github.com/snail007/goproxy/tree/v2.2)
|
||||
|
||||
### 怎么找到组织?
|
||||
[点击加入交流组织](https://gitter.im/go-proxy/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link)
|
||||
|
||||
### 安装
|
||||
1. [快速安装](#自动安装)
|
||||
1. [手动安装](#手动安装)
|
||||
|
||||
### 首次使用必看
|
||||
- [环境](#使用教程)
|
||||
- [环境](#首次使用必看-1)
|
||||
- [使用配置文件](#使用配置文件)
|
||||
- [调试输出](#调试输出)
|
||||
- [使用日志文件](#使用日志文件)
|
||||
- [后台运行](#后台运行)
|
||||
- [守护运行](#守护运行)
|
||||
- [生成通讯证书文件](#生成加密通讯需要的证书文件)
|
||||
- [安全建议](#安全建议)
|
||||
|
||||
### 手册目录
|
||||
- [1. HTTP代理](#1http代理)
|
||||
@ -52,7 +66,7 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
|
||||
- [1.7 通过SSH中转](#17https通过ssh中转)
|
||||
- [1.7.1 用户名和密码的方式](#171-ssh用户名和密码的方式)
|
||||
- [1.7.2 用户名和密钥的方式](#172-ssh用户名和密钥的方式)
|
||||
- [1.8 KCP协议传输](#18KCP协议传输)
|
||||
- [1.8 KCP协议传输](#18kcp协议传输)
|
||||
- [1.9 查看帮助](#19查看帮助)
|
||||
- [2. TCP代理](#2tcp代理)
|
||||
- [2.1 普通一级TCP代理](#21普通一级tcp代理)
|
||||
@ -87,7 +101,7 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
|
||||
- [5.6.1 用户名和密码的方式](#561-ssh用户名和密码的方式)
|
||||
- [5.6.2 用户名和密钥的方式](#562-ssh用户名和密钥的方式)
|
||||
- [5.7 认证](#57认证)
|
||||
- [5.8 KCP协议传输](#58KCP协议传输)
|
||||
- [5.8 KCP协议传输](#58kcp协议传输)
|
||||
- [5.9 查看帮助](#59查看帮助)
|
||||
|
||||
### Fast Start
|
||||
@ -100,23 +114,15 @@ curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.s
|
||||
安装完成,配置目录是/etc/proxy,更详细的使用方法参考下面的进一步了解.
|
||||
如果安装失败或者你的vps不是linux64位系统,请按照下面的半自动步骤安装:
|
||||
|
||||
#### 手动安装
|
||||
#### **1.登录你的VPS,下载守护进程monexec,选择合适你的版本,vps一般选择"linux_amd64.tar.gz"的即可.**
|
||||
下载地址:https://github.com/reddec/monexec/releases
|
||||
比如下载到/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**
|
||||
#### 手动安装
|
||||
|
||||
#### **1.下载proxy**
|
||||
下载地址:https://github.com/snail007/goproxy/releases
|
||||
```shell
|
||||
cd /root/proxy/
|
||||
wget https://github.com/snail007/goproxy/releases/download/v3.1fix/proxy-linux-amd64.tar.gz
|
||||
wget https://github.com/snail007/goproxy/releases/download/v3.9/proxy-linux-amd64.tar.gz
|
||||
```
|
||||
#### **3.下载自动安装脚本**
|
||||
#### **2.下载自动安装脚本**
|
||||
```shell
|
||||
cd /root/proxy/
|
||||
wget https://raw.githubusercontent.com/snail007/goproxy/master/install.sh
|
||||
@ -124,9 +130,9 @@ chmod +x install.sh
|
||||
./install.sh
|
||||
```
|
||||
|
||||
## 使用教程
|
||||
## **首次使用必看**
|
||||
|
||||
#### **提示**
|
||||
#### **环境**
|
||||
接下来的教程,默认系统是linux,程序是proxy;所有操作需要root权限;
|
||||
如果你的是windows,请使用windows版本的proxy.exe即可.
|
||||
|
||||
@ -141,14 +147,40 @@ http
|
||||
--local-type=tcp
|
||||
--local=:33080
|
||||
```
|
||||
### 0.生成加密通讯需要的证书文件
|
||||
|
||||
### **调试输出**
|
||||
默认情况下,日志输出的信息不包含文件行数,某些情况下为了排除程序问题,快速定位问题,
|
||||
可以使用--debug参数,输出代码行数和毫秒时间.
|
||||
|
||||
### **使用日志文件**
|
||||
默认情况下,日志是直接在控制台显示出来的,如果要保存到文件,可以使用--log参数,
|
||||
比如: --log proxy.log,日志就会输出到proxy.log方便排除问题.
|
||||
|
||||
|
||||
### **生成加密通讯需要的证书文件**
|
||||
http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,当然可以选择不加密通信通讯,本教程所有和上级通讯都采用加密,需要证书文件.
|
||||
在linux上并安装了openssl命令,可以直接通过下面的命令生成证书和key文件.
|
||||
`./proxy keygen`
|
||||
默认会在当前程序目录下面生成证书文件proxy.crt和key文件proxy.key。
|
||||
|
||||
### 1.HTTP代理
|
||||
### **后台运行**
|
||||
默认执行proxy之后,如果要保持proxy运行,不能关闭命令行.
|
||||
如果想在后台运行proxy,命令行可以关闭,只需要在命令最后加上--daemon参数即可.
|
||||
比如:
|
||||
`./proxy http -t tcp -p "0.0.0.0:38080" --daemon`
|
||||
|
||||
### **守护运行**
|
||||
守护运行参数--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外网ip是23.23.23.23,下面命令通过-g参数设置23.23.23.23
|
||||
`./proxy http -g "23.23.23.23"`
|
||||
|
||||
### **1.HTTP代理**
|
||||
#### **1.1.普通HTTP代理**
|
||||
`./proxy http -t tcp -p "0.0.0.0:38080"`
|
||||
|
||||
@ -187,9 +219,23 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,
|
||||
`./proxy http -t tcp -p ":33080" -a "user1:pass1" -a "user2:pass2"`
|
||||
多个用户,重复-a参数即可.
|
||||
也可以放在文件中,格式是一行一个"用户名:密码",然后用-F指定.
|
||||
`./proxy http -t tcp -p ":33080" -F auth-file.txt`
|
||||
如果没有-a或-F参数,就是关闭Basic认证.
|
||||
`./proxy http -t tcp -p ":33080" -F auth-file.txt`
|
||||
|
||||
另外,http(s)代理还集成了外部HTTP API认证,我们可以通过--auth-url参数指定一个http url接口地址,
|
||||
然后有用户连接的时候,proxy会GET方式请求这url,带上下面四个参数,如果返回HTTP状态码204,代表认证成功
|
||||
其它情况认为认证失败.
|
||||
比如:
|
||||
`./proxy http -t tcp -p ":33080" --auth-url "http://test.com/auth.php"`
|
||||
用户连接的时候,proxy会GET方式请求这url("http://test.com/auth.php"),
|
||||
带上user,pass,ip,target四个参数:
|
||||
http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}&target={TARGET}
|
||||
user:用户名
|
||||
pass:密码
|
||||
ip:用户的IP,比如:192.168.1.200
|
||||
target:用户访问的URL,比如:http://demo.com:80/1.html或https://www.baidu.com:80
|
||||
|
||||
如果没有-a或-F或--auth-url参数,就是关闭Basic认证.
|
||||
|
||||
#### **1.6.HTTP代理流量强制走上级HTTP代理**
|
||||
默认情况下,proxy会智能判断一个网站域名是否无法访问,如果无法访问才走上级HTTP代理.通过--always可以使全部HTTP代理流量强制走上级HTTP代理.
|
||||
`./proxy http --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
|
||||
@ -211,7 +257,7 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,
|
||||
KCP协议需要-B参数设置一个密码用于加密解密数据
|
||||
|
||||
一级HTTP代理(VPS,IP:22.22.22.22)
|
||||
`./proxy http -t kcp -p ":38080" -B mypassword
|
||||
`./proxy http -t kcp -p ":38080" -B mypassword`
|
||||
|
||||
二级HTTP代理(本地Linux)
|
||||
`./proxy http -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" -B mypassword`
|
||||
@ -220,7 +266,7 @@ KCP协议需要-B参数设置一个密码用于加密解密数据
|
||||
#### **1.9.查看帮助**
|
||||
`./proxy help http`
|
||||
|
||||
### 2.TCP代理
|
||||
### **2.TCP代理**
|
||||
|
||||
#### **2.1.普通一级TCP代理**
|
||||
本地执行:
|
||||
@ -262,7 +308,7 @@ VPS(IP:22.22.22.33)执行:
|
||||
#### **2.6.查看帮助**
|
||||
`./proxy help tcp`
|
||||
|
||||
### 3.UDP代理
|
||||
### **3.UDP代理**
|
||||
|
||||
#### **3.1.普通一级UDP代理**
|
||||
本地执行:
|
||||
@ -304,7 +350,7 @@ VPS(IP:22.22.22.33)执行:
|
||||
#### **3.6.查看帮助**
|
||||
`./proxy help udp`
|
||||
|
||||
### 4.内网穿透
|
||||
### **4.内网穿透**
|
||||
#### **4.1、原理说明**
|
||||
内网穿透,由三部分组成:client端,server端,bridge端;client和server主动连接bridge端进行桥接.
|
||||
当用户访问server端,流程是:
|
||||
@ -368,7 +414,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`
|
||||
|
||||
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. 完成
|
||||
|
||||
@ -438,8 +484,8 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid
|
||||
`./proxy help tserver`
|
||||
`./proxy help tserver`
|
||||
|
||||
### 5.SOCKS5代理
|
||||
提示:SOCKS5代理,只支持TCP协议,不支持UDP协议,不支持用户名密码认证.
|
||||
### **5.SOCKS5代理**
|
||||
提示:SOCKS5代理,支持CONNECT,UDP协议,不支持BIND,支持用户名密码认证.
|
||||
#### **5.1.普通SOCKS5代理**
|
||||
`./proxy socks -t tcp -p "0.0.0.0:38080"`
|
||||
|
||||
@ -495,13 +541,26 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid
|
||||
多个用户,重复-a参数即可.
|
||||
也可以放在文件中,格式是一行一个"用户名:密码",然后用-F指定.
|
||||
`./proxy socks -t tcp -p ":33080" -F auth-file.txt`
|
||||
如果没有-a或-F参数,就是关闭认证.
|
||||
|
||||
另外,socks5代理还集成了外部HTTP API认证,我们可以通过--auth-url参数指定一个http url接口地址,
|
||||
然后有用户连接的时候,proxy会GET方式请求这url,带上下面四个参数,如果返回HTTP状态码204,代表认证成功
|
||||
其它情况认为认证失败.
|
||||
比如:
|
||||
`./proxy socks -t tcp -p ":33080" --auth-url "http://test.com/auth.php"`
|
||||
用户连接的时候,proxy会GET方式请求这url("http://test.com/auth.php"),
|
||||
带上user,pass,ip,三个参数:
|
||||
http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}
|
||||
user:用户名
|
||||
pass:密码
|
||||
ip:用户的IP,比如:192.168.1.200
|
||||
|
||||
如果没有-a或-F或--auth-url参数,就是关闭认证.
|
||||
|
||||
#### **5.8.KCP协议传输**
|
||||
KCP协议需要-B参数设置一个密码用于加密解密数据
|
||||
|
||||
一级HTTP代理(VPS,IP:22.22.22.22)
|
||||
`./proxy socks -t kcp -p ":38080" -B mypassword
|
||||
`./proxy socks -t kcp -p ":38080" -B mypassword`
|
||||
|
||||
二级HTTP代理(本地Linux)
|
||||
`./proxy socks -t tcp -p ":8080" -T kcp -P "22.22.22.22:38080" -B mypassword`
|
||||
@ -512,7 +571,7 @@ KCP协议需要-B参数设置一个密码用于加密解密数据
|
||||
|
||||
### TODO
|
||||
- http,socks代理多个上级负载均衡?
|
||||
- 内网穿透server<->bridge心跳机制?
|
||||
- 内网穿透增加multiplexing模式?
|
||||
- 欢迎加群反馈...
|
||||
|
||||
### 如何使用源码?
|
||||
|
||||
108
config.go
108
config.go
@ -1,11 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"proxy/services"
|
||||
"proxy/utils"
|
||||
"time"
|
||||
|
||||
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
@ -13,6 +16,7 @@ import (
|
||||
var (
|
||||
app *kingpin.Application
|
||||
service *services.ServiceItem
|
||||
cmd *exec.Cmd
|
||||
)
|
||||
|
||||
func initConfig() (err error) {
|
||||
@ -36,6 +40,10 @@ func initConfig() (err error) {
|
||||
app = kingpin.New("proxy", "happy with proxy")
|
||||
app.Author("snail").Version(APP_VERSION)
|
||||
debug := app.Flag("debug", "debug log output").Default("false").Bool()
|
||||
daemon := app.Flag("daemon", "run proxy in background").Default("false").Bool()
|
||||
forever := app.Flag("forever", "run proxy in forever,fail and retry").Default("false").Bool()
|
||||
logfile := app.Flag("log", "log file path").Default("").String()
|
||||
|
||||
//########http#########
|
||||
http := app.Command("http", "proxy on http mode")
|
||||
httpArgs.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
|
||||
@ -60,18 +68,25 @@ func initConfig() (err error) {
|
||||
httpArgs.SSHPassword = http.Flag("ssh-password", "password for ssh").Short('A').Default("").String()
|
||||
httpArgs.KCPKey = http.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String()
|
||||
httpArgs.KCPMethod = http.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String()
|
||||
httpArgs.LocalIPS = http.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
|
||||
httpArgs.AuthURL = http.Flag("auth-url", "http basic auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
|
||||
httpArgs.AuthURLTimeout = http.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
|
||||
httpArgs.AuthURLOkCode = http.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
|
||||
httpArgs.AuthURLRetry = http.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("1").Int()
|
||||
|
||||
//########tcp#########
|
||||
tcp := app.Command("tcp", "proxy on tcp mode")
|
||||
tcpArgs.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.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.Timeout = tcp.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('e').Default("2000").Int()
|
||||
tcpArgs.ParentType = tcp.Flag("parent-type", "parent protocol type <tls|tcp|kcp|udp>").Short('T').Enum("tls", "tcp", "udp", "kcp")
|
||||
tcpArgs.LocalType = tcp.Flag("local-type", "local protocol type <tls|tcp|kcp>").Default("tcp").Short('t').Enum("tls", "tcp", "kcp")
|
||||
tcpArgs.PoolSize = tcp.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int()
|
||||
tcpArgs.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
|
||||
tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
|
||||
tcpArgs.KCPKey = tcp.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String()
|
||||
tcpArgs.KCPMethod = tcp.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String()
|
||||
|
||||
//########udp#########
|
||||
udp := app.Command("udp", "proxy on udp mode")
|
||||
@ -93,6 +108,7 @@ func initConfig() (err error) {
|
||||
tunnelServerArgs.IsUDP = tunnelServer.Flag("udp", "proxy on udp tunnel server mode").Default("false").Bool()
|
||||
tunnelServerArgs.Key = tunnelServer.Flag("k", "client key").Default("default").String()
|
||||
tunnelServerArgs.Route = tunnelServer.Flag("route", "local route to client's network, such as :PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings()
|
||||
tunnelServerArgs.Mux = tunnelServer.Flag("mux", "use multiplexing mode").Default("false").Bool()
|
||||
|
||||
//########tunnel-client#########
|
||||
tunnelClient := app.Command("tclient", "proxy on tunnel client mode")
|
||||
@ -101,6 +117,7 @@ func initConfig() (err error) {
|
||||
tunnelClientArgs.KeyFile = tunnelClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
|
||||
tunnelClientArgs.Timeout = tunnelClient.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
|
||||
tunnelClientArgs.Key = tunnelClient.Flag("k", "key same with server").Default("default").String()
|
||||
tunnelClientArgs.Mux = tunnelClient.Flag("mux", "use multiplexing mode").Default("false").Bool()
|
||||
|
||||
//########tunnel-bridge#########
|
||||
tunnelBridge := app.Command("tbridge", "proxy on tunnel bridge mode")
|
||||
@ -108,6 +125,7 @@ func initConfig() (err error) {
|
||||
tunnelBridgeArgs.KeyFile = tunnelBridge.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
|
||||
tunnelBridgeArgs.Timeout = tunnelBridge.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
|
||||
tunnelBridgeArgs.Local = tunnelBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
|
||||
tunnelBridgeArgs.Mux = tunnelBridge.Flag("mux", "use multiplexing mode").Default("false").Bool()
|
||||
|
||||
//########ssh#########
|
||||
socks := app.Command("socks", "proxy on ssh mode")
|
||||
@ -132,6 +150,11 @@ func initConfig() (err error) {
|
||||
socksArgs.Auth = socks.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
|
||||
socksArgs.KCPKey = socks.Flag("kcp-key", "key for kcp encrypt/decrypt data").Short('B').Default("encrypt").String()
|
||||
socksArgs.KCPMethod = socks.Flag("kcp-method", "kcp encrypt/decrypt method").Short('M').Default("3des").String()
|
||||
socksArgs.LocalIPS = socks.Flag("local bind ips", "if your host behind a nat,set your public ip here avoid dead loop").Short('g').Strings()
|
||||
socksArgs.AuthURL = socks.Flag("auth-url", "auth username and password will send to this url,response http code equal to 'auth-code' means ok,others means fail.").Default("").String()
|
||||
socksArgs.AuthURLTimeout = socks.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int()
|
||||
socksArgs.AuthURLOkCode = socks.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int()
|
||||
socksArgs.AuthURLRetry = socks.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int()
|
||||
|
||||
//parse args
|
||||
serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
|
||||
@ -142,7 +165,84 @@ func initConfig() (err error) {
|
||||
flags |= log.Ltime
|
||||
}
|
||||
log.SetFlags(flags)
|
||||
poster()
|
||||
|
||||
if *logfile != "" {
|
||||
f, e := os.OpenFile(*logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
|
||||
if e != nil {
|
||||
log.Fatal(e)
|
||||
}
|
||||
log.SetOutput(f)
|
||||
}
|
||||
if *daemon {
|
||||
args := []string{}
|
||||
for _, arg := range os.Args[1:] {
|
||||
if arg != "--daemon" {
|
||||
args = append(args, arg)
|
||||
}
|
||||
}
|
||||
cmd = exec.Command(os.Args[0], args...)
|
||||
cmd.Start()
|
||||
f := ""
|
||||
if *forever {
|
||||
f = "forever "
|
||||
}
|
||||
log.Printf("%s%s [PID] %d running...\n", f, os.Args[0], cmd.Process.Pid)
|
||||
os.Exit(0)
|
||||
}
|
||||
if *forever {
|
||||
args := []string{}
|
||||
for _, arg := range os.Args[1:] {
|
||||
if arg != "--forever" {
|
||||
args = append(args, arg)
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
if cmd != nil {
|
||||
cmd.Process.Kill()
|
||||
}
|
||||
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("%s [PID] %d unexpected exited, restarting...\n", os.Args[0], pid)
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
if *logfile == "" {
|
||||
poster()
|
||||
}
|
||||
//regist services and run service
|
||||
services.Regist("http", services.NewHTTP(), httpArgs)
|
||||
services.Regist("tcp", services.NewTCP(), tcpArgs)
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
#!/bin/bash
|
||||
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
|
||||
tar zxvf proxy-linux-amd64.tar.gz
|
||||
cp proxy /usr/bin/
|
||||
|
||||
@ -5,15 +5,8 @@ if [ -e /tmp/proxy ]; then
|
||||
fi
|
||||
mkdir /tmp/proxy
|
||||
cd /tmp/proxy
|
||||
wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
|
||||
wget https://github.com/snail007/goproxy/releases/download/v3.4/proxy-linux-amd64.tar.gz
|
||||
wget https://github.com/snail007/goproxy/releases/download/v3.9/proxy-linux-amd64.tar.gz
|
||||
|
||||
# install monexec
|
||||
tar zxvf monexec_0.1.1_linux_amd64.tar.gz
|
||||
cd monexec_0.1.1_linux_amd64
|
||||
cp monexec /usr/bin/
|
||||
chmod +x /usr/bin/monexec
|
||||
cd ..
|
||||
# #install proxy
|
||||
tar zxvf proxy-linux-amd64.tar.gz
|
||||
cp proxy /usr/bin/
|
||||
|
||||
19
main.go
19
main.go
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
@ -9,14 +8,18 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const APP_VERSION = "3.4"
|
||||
const APP_VERSION = "3.9"
|
||||
|
||||
func main() {
|
||||
err := initConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("err : %s", err)
|
||||
}
|
||||
Clean(&service.S)
|
||||
if service != nil && service.S != nil {
|
||||
Clean(&service.S)
|
||||
} else {
|
||||
Clean(nil)
|
||||
}
|
||||
}
|
||||
func Clean(s *services.Service) {
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
@ -29,8 +32,14 @@ func Clean(s *services.Service) {
|
||||
syscall.SIGQUIT)
|
||||
go func() {
|
||||
for _ = range signalChan {
|
||||
fmt.Println("\nReceived an interrupt, stopping services...")
|
||||
(*s).Clean()
|
||||
log.Println("Received an interrupt, stopping services...")
|
||||
if s != nil && *s != nil {
|
||||
(*s).Clean()
|
||||
}
|
||||
if cmd != nil {
|
||||
log.Printf("clean process %d", cmd.Process.Pid)
|
||||
cmd.Process.Kill()
|
||||
}
|
||||
cleanupDone <- true
|
||||
}
|
||||
}()
|
||||
|
||||
10
release.sh
10
release.sh
@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
VER="3.4"
|
||||
VER="3.9"
|
||||
RELEASE="release-${VER}"
|
||||
rm -rf .cert
|
||||
mkdir .cert
|
||||
@ -9,7 +9,7 @@ cd .cert
|
||||
cd ..
|
||||
rm -rf ${RELEASE}
|
||||
mkdir ${RELEASE}
|
||||
set CGO_ENABLED=0
|
||||
export CGO_ENABLED=0
|
||||
#linux
|
||||
GOOS=linux GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-linux-386.tar.gz" proxy direct blocked
|
||||
GOOS=linux GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-linux-amd64.tar.gz" proxy direct blocked
|
||||
@ -61,3 +61,9 @@ GOOS=windows GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-windows-386.tar.g
|
||||
GOOS=windows GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-windows-amd64.tar.gz" proxy.exe direct blocked .cert/proxy.crt .cert/proxy.key
|
||||
|
||||
rm -rf proxy proxy.exe .cert
|
||||
|
||||
#todo
|
||||
#1.release.sh VER="xxx"
|
||||
#2.main.go APP_VERSION="xxx"
|
||||
#3.install_auto.sh goproxy/releases/download/vxxx
|
||||
#4.README goproxy/releases/download/vxxx
|
||||
@ -16,6 +16,8 @@ const (
|
||||
CONN_SERVER_HEARBEAT = uint8(3)
|
||||
CONN_SERVER = uint8(4)
|
||||
CONN_CLIENT = uint8(5)
|
||||
CONN_SERVER_MUX = uint8(6)
|
||||
CONN_CLIENT_MUX = uint8(7)
|
||||
)
|
||||
|
||||
type TunnelServerArgs struct {
|
||||
@ -31,6 +33,7 @@ type TunnelServerArgs struct {
|
||||
Timeout *int
|
||||
Route *[]string
|
||||
Mgr *TunnelServerManager
|
||||
Mux *bool
|
||||
}
|
||||
type TunnelClientArgs struct {
|
||||
Parent *string
|
||||
@ -40,6 +43,7 @@ type TunnelClientArgs struct {
|
||||
KeyBytes []byte
|
||||
Key *string
|
||||
Timeout *int
|
||||
Mux *bool
|
||||
}
|
||||
type TunnelBridgeArgs struct {
|
||||
Parent *string
|
||||
@ -49,6 +53,7 @@ type TunnelBridgeArgs struct {
|
||||
KeyBytes []byte
|
||||
Local *string
|
||||
Timeout *int
|
||||
Mux *bool
|
||||
}
|
||||
type TCPArgs struct {
|
||||
Parent *string
|
||||
@ -58,10 +63,12 @@ type TCPArgs struct {
|
||||
KeyBytes []byte
|
||||
Local *string
|
||||
ParentType *string
|
||||
IsTLS *bool
|
||||
LocalType *string
|
||||
Timeout *int
|
||||
PoolSize *int
|
||||
CheckParentInterval *int
|
||||
KCPMethod *string
|
||||
KCPKey *string
|
||||
}
|
||||
|
||||
type HTTPArgs struct {
|
||||
@ -78,6 +85,10 @@ type HTTPArgs struct {
|
||||
Direct *string
|
||||
AuthFile *string
|
||||
Auth *[]string
|
||||
AuthURL *string
|
||||
AuthURLOkCode *int
|
||||
AuthURLTimeout *int
|
||||
AuthURLRetry *int
|
||||
ParentType *string
|
||||
LocalType *string
|
||||
Timeout *int
|
||||
@ -91,6 +102,7 @@ type HTTPArgs struct {
|
||||
SSHAuthMethod ssh.AuthMethod
|
||||
KCPMethod *string
|
||||
KCPKey *string
|
||||
LocalIPS *[]string
|
||||
}
|
||||
type UDPArgs struct {
|
||||
Parent *string
|
||||
@ -126,15 +138,25 @@ type SocksArgs struct {
|
||||
Direct *string
|
||||
AuthFile *string
|
||||
Auth *[]string
|
||||
AuthURL *string
|
||||
AuthURLOkCode *int
|
||||
AuthURLTimeout *int
|
||||
AuthURLRetry *int
|
||||
KCPMethod *string
|
||||
KCPKey *string
|
||||
UDPParent *string
|
||||
UDPLocal *string
|
||||
LocalIPS *[]string
|
||||
}
|
||||
|
||||
func (a *TCPArgs) Protocol() string {
|
||||
if *a.IsTLS {
|
||||
return "tls"
|
||||
switch *a.LocalType {
|
||||
case TYPE_TLS:
|
||||
return TYPE_TLS
|
||||
case TYPE_TCP:
|
||||
return TYPE_TCP
|
||||
case TYPE_KCP:
|
||||
return TYPE_KCP
|
||||
}
|
||||
return "tcp"
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@ -146,7 +146,7 @@ func (s *HTTP) callback(inConn net.Conn) {
|
||||
req, err = utils.NewHTTPRequest(&inConn, 4096, s.IsBasicAuth(), &s.basicAuth)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Printf("decoder error , form %s, ERR:%s", err, inConn.RemoteAddr())
|
||||
log.Printf("decoder error , from %s, ERR:%s", inConn.RemoteAddr(), err)
|
||||
}
|
||||
utils.CloseConn(&inConn)
|
||||
return
|
||||
@ -223,7 +223,7 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
|
||||
}
|
||||
|
||||
outAddr := outConn.RemoteAddr().String()
|
||||
outLocalAddr := outConn.LocalAddr().String()
|
||||
//outLocalAddr := outConn.LocalAddr().String()
|
||||
|
||||
if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") {
|
||||
//https无上级或者上级非代理,proxy需要响应connect请求,并直连目标
|
||||
@ -238,12 +238,10 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
|
||||
}
|
||||
}
|
||||
|
||||
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(&outConn)
|
||||
utils.IoBind((*inConn), outConn, func(err interface{}) {
|
||||
log.Printf("conn %s - %s released [%s]", inAddr, outAddr, req.Host)
|
||||
})
|
||||
log.Printf("conn %s - %s - %s - %s connected [%s]", inAddr, inLocalAddr, outLocalAddr, outAddr, req.Host)
|
||||
log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, req.Host)
|
||||
|
||||
return
|
||||
}
|
||||
@ -324,6 +322,10 @@ func (s *HTTP) InitOutConnPool() {
|
||||
}
|
||||
func (s *HTTP) InitBasicAuth() (err error) {
|
||||
s.basicAuth = utils.NewBasicAuth()
|
||||
if *s.cfg.AuthURL != "" {
|
||||
s.basicAuth.SetAuthURL(*s.cfg.AuthURL, *s.cfg.AuthURLOkCode, *s.cfg.AuthURLTimeout, *s.cfg.AuthURLRetry)
|
||||
log.Printf("auth from %s", *s.cfg.AuthURL)
|
||||
}
|
||||
if *s.cfg.AuthFile != "" {
|
||||
var n = 0
|
||||
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
|
||||
@ -340,7 +342,7 @@ func (s *HTTP) InitBasicAuth() (err error) {
|
||||
return
|
||||
}
|
||||
func (s *HTTP) IsBasicAuth() bool {
|
||||
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0
|
||||
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != ""
|
||||
}
|
||||
func (s *HTTP) IsDeadLoop(inLocalAddr string, host string) bool {
|
||||
inIP, inPort, err := net.SplitHostPort(inLocalAddr)
|
||||
@ -362,6 +364,9 @@ func (s *HTTP) IsDeadLoop(inLocalAddr string, host string) bool {
|
||||
}
|
||||
}
|
||||
interfaceIPs, err := utils.GetAllInterfaceAddr()
|
||||
for _, ip := range *s.cfg.LocalIPS {
|
||||
interfaceIPs = append(interfaceIPs, net.ParseIP(ip).To4())
|
||||
}
|
||||
if err == nil {
|
||||
for _, localIP := range interfaceIPs {
|
||||
for _, outIP := range outIPs {
|
||||
|
||||
1
services/mux_bridge.go
Normal file
1
services/mux_bridge.go
Normal file
@ -0,0 +1 @@
|
||||
package services
|
||||
1
services/mux_client.go
Normal file
1
services/mux_client.go
Normal file
@ -0,0 +1 @@
|
||||
package services
|
||||
1
services/mux_server.go
Normal file
1
services/mux_server.go
Normal file
@ -0,0 +1 @@
|
||||
package services
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"proxy/utils/aes"
|
||||
"proxy/utils/socks"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
@ -167,6 +168,11 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) {
|
||||
log.Printf("parse udp packet fail, ERR:%s", err)
|
||||
return
|
||||
}
|
||||
//防止死循环
|
||||
if s.IsDeadLoop((*localAddr).String(), p.Host()) {
|
||||
log.Printf("dead loop detected , %s", p.Host())
|
||||
return
|
||||
}
|
||||
//log.Printf("##########udp to -> %s:%s###########", p.Host(), p.Port())
|
||||
if *s.cfg.Parent != "" {
|
||||
//有上级代理,转发给上级
|
||||
@ -288,8 +294,8 @@ func (s *Socks) socksConnCallback(inConn net.Conn) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Printf("socks conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
|
||||
inConn.Close()
|
||||
}
|
||||
utils.CloseConn(&inConn)
|
||||
}()
|
||||
//协商开始
|
||||
|
||||
@ -348,7 +354,8 @@ func (s *Socks) socksConnCallback(inConn net.Conn) {
|
||||
pass := string(r[2+r[1]+1:])
|
||||
//log.Printf("user:%s,pass:%s", user, pass)
|
||||
//auth
|
||||
if s.basicAuth.CheckUserPass(user, pass) {
|
||||
_addr := strings.Split(inConn.RemoteAddr().String(), ":")
|
||||
if s.basicAuth.CheckUserPass(user, pass, _addr[0], "") {
|
||||
inConn.Write([]byte{0x01, 0x00})
|
||||
} else {
|
||||
inConn.Write([]byte{0x01, 0x01})
|
||||
@ -393,11 +400,17 @@ func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
|
||||
}
|
||||
func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) {
|
||||
var outConn net.Conn
|
||||
defer utils.CloseConn(&outConn)
|
||||
var err interface{}
|
||||
useProxy := true
|
||||
tryCount := 0
|
||||
maxTryCount := 5
|
||||
//防止死循环
|
||||
if s.IsDeadLoop((*inConn).LocalAddr().String(), request.Host()) {
|
||||
utils.CloseConn(inConn)
|
||||
log.Printf("dead loop detected , %s", request.Host())
|
||||
utils.CloseConn(inConn)
|
||||
return
|
||||
}
|
||||
for {
|
||||
if *s.cfg.Always {
|
||||
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr())
|
||||
@ -412,10 +425,11 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
|
||||
}
|
||||
} else {
|
||||
outConn, err = utils.ConnectHost(request.Addr(), *s.cfg.Timeout)
|
||||
useProxy = false
|
||||
}
|
||||
}
|
||||
tryCount++
|
||||
if err == nil || tryCount > maxTryCount {
|
||||
if err == nil || tryCount > maxTryCount || *s.cfg.Parent == "" {
|
||||
break
|
||||
} else {
|
||||
log.Printf("get out conn fail,%s,retrying...", err)
|
||||
@ -431,13 +445,11 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
|
||||
|
||||
request.TCPReply(socks.REP_SUCCESS)
|
||||
inAddr := (*inConn).RemoteAddr().String()
|
||||
inLocalAddr := (*inConn).LocalAddr().String()
|
||||
//inLocalAddr := (*inConn).LocalAddr().String()
|
||||
|
||||
log.Printf("conn %s - %s connected [%s]", inAddr, inLocalAddr, request.Addr())
|
||||
utils.IoBind(*inConn, outConn, func(err error) {
|
||||
log.Printf("conn %s - %s released [%s]", inAddr, inLocalAddr, request.Addr())
|
||||
utils.CloseConn(inConn)
|
||||
utils.CloseConn(&outConn)
|
||||
log.Printf("conn %s - %s connected", inAddr, request.Addr())
|
||||
utils.IoBind(*inConn, outConn, func(err interface{}) {
|
||||
log.Printf("conn %s - %s released", inAddr, request.Addr())
|
||||
})
|
||||
}
|
||||
func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn net.Conn, err interface{}) {
|
||||
@ -480,11 +492,11 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn n
|
||||
err = fmt.Errorf("write req detail fail,%s", err)
|
||||
return
|
||||
}
|
||||
// _, err = outConn.Read(buf)
|
||||
// if err != nil {
|
||||
// err = fmt.Errorf("read req reply fail,%s", err)
|
||||
// return
|
||||
// }
|
||||
_, err = outConn.Read(buf)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("read req reply fail,%s", err)
|
||||
return
|
||||
}
|
||||
//result := buf[:n]
|
||||
//log.Printf("result:%v", result)
|
||||
|
||||
@ -550,6 +562,10 @@ func (s *Socks) ConnectSSH() (err error) {
|
||||
}
|
||||
func (s *Socks) InitBasicAuth() (err error) {
|
||||
s.basicAuth = utils.NewBasicAuth()
|
||||
if *s.cfg.AuthURL != "" {
|
||||
s.basicAuth.SetAuthURL(*s.cfg.AuthURL, *s.cfg.AuthURLOkCode, *s.cfg.AuthURLTimeout, *s.cfg.AuthURLRetry)
|
||||
log.Printf("auth from %s", *s.cfg.AuthURL)
|
||||
}
|
||||
if *s.cfg.AuthFile != "" {
|
||||
var n = 0
|
||||
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
|
||||
@ -566,5 +582,40 @@ func (s *Socks) InitBasicAuth() (err error) {
|
||||
return
|
||||
}
|
||||
func (s *Socks) IsBasicAuth() bool {
|
||||
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0
|
||||
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != ""
|
||||
}
|
||||
func (s *Socks) IsDeadLoop(inLocalAddr string, host string) bool {
|
||||
inIP, inPort, err := net.SplitHostPort(inLocalAddr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
outDomain, outPort, err := net.SplitHostPort(host)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if inPort == outPort {
|
||||
var outIPs []net.IP
|
||||
outIPs, err = net.LookupIP(outDomain)
|
||||
if err == nil {
|
||||
for _, ip := range outIPs {
|
||||
if ip.String() == inIP {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
interfaceIPs, err := utils.GetAllInterfaceAddr()
|
||||
for _, ip := range *s.cfg.LocalIPS {
|
||||
interfaceIPs = append(interfaceIPs, net.ParseIP(ip).To4())
|
||||
}
|
||||
if err == nil {
|
||||
for _, localIP := range interfaceIPs {
|
||||
for _, outIP := range outIPs {
|
||||
if localIP.Equal(outIP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -29,9 +29,9 @@ func (s *TCP) CheckArgs() {
|
||||
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>")
|
||||
log.Fatalf("parent type unkown,use -T <tls|tcp|kcp|udp>")
|
||||
}
|
||||
if *s.cfg.ParentType == "tls" || *s.cfg.IsTLS {
|
||||
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.LocalType == TYPE_TLS {
|
||||
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
|
||||
}
|
||||
}
|
||||
@ -52,10 +52,13 @@ func (s *TCP) Start(args interface{}) (err error) {
|
||||
host, port, _ := net.SplitHostPort(*s.cfg.Local)
|
||||
p, _ := strconv.Atoi(port)
|
||||
sc := utils.NewServerChannel(host, p)
|
||||
if !*s.cfg.IsTLS {
|
||||
|
||||
if *s.cfg.LocalType == TYPE_TCP {
|
||||
err = sc.ListenTCP(s.callback)
|
||||
} else {
|
||||
} else if *s.cfg.LocalType == TYPE_TLS {
|
||||
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback)
|
||||
} else if *s.cfg.LocalType == TYPE_KCP {
|
||||
err = sc.ListenKCP(*s.cfg.KCPMethod, *s.cfg.KCPKey, s.callback)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
@ -75,6 +78,8 @@ func (s *TCP) callback(inConn net.Conn) {
|
||||
}()
|
||||
var err error
|
||||
switch *s.cfg.ParentType {
|
||||
case TYPE_KCP:
|
||||
fallthrough
|
||||
case TYPE_TCP:
|
||||
fallthrough
|
||||
case TYPE_TLS:
|
||||
@ -102,15 +107,13 @@ func (s *TCP) OutToTCP(inConn *net.Conn) (err error) {
|
||||
return
|
||||
}
|
||||
inAddr := (*inConn).RemoteAddr().String()
|
||||
inLocalAddr := (*inConn).LocalAddr().String()
|
||||
//inLocalAddr := (*inConn).LocalAddr().String()
|
||||
outAddr := outConn.RemoteAddr().String()
|
||||
outLocalAddr := outConn.LocalAddr().String()
|
||||
utils.IoBind((*inConn), outConn, func(err error) {
|
||||
log.Printf("conn %s - %s - %s - %s released", inAddr, inLocalAddr, outLocalAddr, outAddr)
|
||||
utils.CloseConn(inConn)
|
||||
utils.CloseConn(&outConn)
|
||||
//outLocalAddr := outConn.LocalAddr().String()
|
||||
utils.IoBind((*inConn), outConn, func(err interface{}) {
|
||||
log.Printf("conn %s - %s released", inAddr, outAddr)
|
||||
})
|
||||
log.Printf("conn %s - %s - %s - %s connected", inAddr, inLocalAddr, outLocalAddr, outAddr)
|
||||
log.Printf("conn %s - %s connected", inAddr, outAddr)
|
||||
return
|
||||
}
|
||||
func (s *TCP) OutToUDP(inConn *net.Conn) (err error) {
|
||||
@ -162,13 +165,14 @@ func (s *TCP) OutToUDP(inConn *net.Conn) (err error) {
|
||||
|
||||
}
|
||||
func (s *TCP) InitOutConnPool() {
|
||||
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP {
|
||||
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP || *s.cfg.ParentType == TYPE_KCP {
|
||||
//dur int, isTLS bool, certBytes, keyBytes []byte,
|
||||
//parent string, timeout int, InitialCap int, MaxCap int
|
||||
s.outPool = utils.NewOutPool(
|
||||
*s.cfg.CheckParentInterval,
|
||||
*s.cfg.ParentType,
|
||||
"", "",
|
||||
*s.cfg.KCPMethod,
|
||||
*s.cfg.KCPKey,
|
||||
s.cfg.CertBytes, s.cfg.KeyBytes,
|
||||
*s.cfg.Parent,
|
||||
*s.cfg.Timeout,
|
||||
|
||||
@ -17,8 +17,8 @@ type TunnelBridge struct {
|
||||
cfg TunnelBridgeArgs
|
||||
serverConns utils.ConcurrentMap
|
||||
clientControlConns utils.ConcurrentMap
|
||||
cmServer utils.ConnManager
|
||||
cmClient utils.ConnManager
|
||||
// cmServer utils.ConnManager
|
||||
// cmClient utils.ConnManager
|
||||
}
|
||||
|
||||
func NewTunnelBridge() Service {
|
||||
@ -26,8 +26,8 @@ func NewTunnelBridge() Service {
|
||||
cfg: TunnelBridgeArgs{},
|
||||
serverConns: utils.NewConcurrentMap(),
|
||||
clientControlConns: utils.NewConcurrentMap(),
|
||||
cmServer: utils.NewConnManager(),
|
||||
cmClient: utils.NewConnManager(),
|
||||
// cmServer: utils.NewConnManager(),
|
||||
// cmClient: utils.NewConnManager(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ func (s *TunnelBridge) Start(args interface{}) (err error) {
|
||||
time.Sleep(time.Second * 3)
|
||||
continue
|
||||
} else {
|
||||
s.cmServer.Add(serverID, ID, &inConn)
|
||||
// s.cmServer.Add(serverID, ID, &inConn)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -112,15 +112,13 @@ func (s *TunnelBridge) Start(args interface{}) (err error) {
|
||||
return
|
||||
}
|
||||
serverConn := serverConnItem.(ServerConn).Conn
|
||||
utils.IoBind(*serverConn, inConn, func(err error) {
|
||||
(*serverConn).Close()
|
||||
utils.CloseConn(&inConn)
|
||||
utils.IoBind(*serverConn, inConn, func(err interface{}) {
|
||||
s.serverConns.Remove(ID)
|
||||
s.cmClient.RemoveOne(key, ID)
|
||||
s.cmServer.RemoveOne(serverID, ID)
|
||||
// s.cmClient.RemoveOne(key, ID)
|
||||
// s.cmServer.RemoveOne(serverID, 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)
|
||||
|
||||
case CONN_CLIENT_CONTROL:
|
||||
@ -138,101 +136,101 @@ func (s *TunnelBridge) Start(args interface{}) (err error) {
|
||||
s.clientControlConns.Set(key, &inConn)
|
||||
log.Printf("set client %s control conn", key)
|
||||
|
||||
case CONN_SERVER_HEARBEAT:
|
||||
var serverID string
|
||||
err = utils.ReadPacketData(reader, &serverID)
|
||||
if err != nil {
|
||||
log.Printf("read error,ERR:%s", err)
|
||||
return
|
||||
}
|
||||
log.Printf("server heartbeat connection, id: %s", serverID)
|
||||
writeDie := make(chan bool)
|
||||
readDie := make(chan bool)
|
||||
go func() {
|
||||
for {
|
||||
inConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
|
||||
_, err = inConn.Write([]byte{0x00})
|
||||
inConn.SetWriteDeadline(time.Time{})
|
||||
if err != nil {
|
||||
log.Printf("server heartbeat connection write err %s", err)
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
close(writeDie)
|
||||
}()
|
||||
go func() {
|
||||
for {
|
||||
signal := make([]byte, 1)
|
||||
inConn.SetReadDeadline(time.Now().Add(time.Second * 6))
|
||||
_, err := inConn.Read(signal)
|
||||
inConn.SetReadDeadline(time.Time{})
|
||||
if err != nil {
|
||||
log.Printf("server heartbeat connection read err: %s", err)
|
||||
break
|
||||
} else {
|
||||
// log.Printf("heartbeat from server ,id:%s", serverID)
|
||||
}
|
||||
}
|
||||
close(readDie)
|
||||
}()
|
||||
select {
|
||||
case <-readDie:
|
||||
case <-writeDie:
|
||||
}
|
||||
utils.CloseConn(&inConn)
|
||||
s.cmServer.Remove(serverID)
|
||||
log.Printf("server heartbeat conn %s released", serverID)
|
||||
case CONN_CLIENT_HEARBEAT:
|
||||
var clientID string
|
||||
err = utils.ReadPacketData(reader, &clientID)
|
||||
if err != nil {
|
||||
log.Printf("read error,ERR:%s", err)
|
||||
return
|
||||
}
|
||||
log.Printf("client heartbeat connection, id: %s", clientID)
|
||||
writeDie := make(chan bool)
|
||||
readDie := make(chan bool)
|
||||
go func() {
|
||||
for {
|
||||
inConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
|
||||
_, err = inConn.Write([]byte{0x00})
|
||||
inConn.SetWriteDeadline(time.Time{})
|
||||
if err != nil {
|
||||
log.Printf("client heartbeat connection write err %s", err)
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
close(writeDie)
|
||||
}()
|
||||
go func() {
|
||||
for {
|
||||
signal := make([]byte, 1)
|
||||
inConn.SetReadDeadline(time.Now().Add(time.Second * 6))
|
||||
_, err := inConn.Read(signal)
|
||||
inConn.SetReadDeadline(time.Time{})
|
||||
if err != nil {
|
||||
log.Printf("client control connection read err: %s", err)
|
||||
break
|
||||
} else {
|
||||
// log.Printf("heartbeat from client ,id:%s", clientID)
|
||||
}
|
||||
}
|
||||
close(readDie)
|
||||
}()
|
||||
select {
|
||||
case <-readDie:
|
||||
case <-writeDie:
|
||||
}
|
||||
utils.CloseConn(&inConn)
|
||||
s.cmClient.Remove(clientID)
|
||||
if s.clientControlConns.Has(clientID) {
|
||||
item, _ := s.clientControlConns.Get(clientID)
|
||||
(*item.(*net.Conn)).Close()
|
||||
}
|
||||
s.clientControlConns.Remove(clientID)
|
||||
log.Printf("client heartbeat conn %s released", clientID)
|
||||
// case CONN_SERVER_HEARBEAT:
|
||||
// var serverID string
|
||||
// err = utils.ReadPacketData(reader, &serverID)
|
||||
// if err != nil {
|
||||
// log.Printf("read error,ERR:%s", err)
|
||||
// return
|
||||
// }
|
||||
// log.Printf("server heartbeat connection, id: %s", serverID)
|
||||
// writeDie := make(chan bool)
|
||||
// readDie := make(chan bool)
|
||||
// go func() {
|
||||
// for {
|
||||
// inConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
|
||||
// _, err = inConn.Write([]byte{0x00})
|
||||
// inConn.SetWriteDeadline(time.Time{})
|
||||
// if err != nil {
|
||||
// log.Printf("server heartbeat connection write err %s", err)
|
||||
// break
|
||||
// }
|
||||
// time.Sleep(time.Second * 3)
|
||||
// }
|
||||
// close(writeDie)
|
||||
// }()
|
||||
// go func() {
|
||||
// for {
|
||||
// signal := make([]byte, 1)
|
||||
// inConn.SetReadDeadline(time.Now().Add(time.Second * 6))
|
||||
// _, err := inConn.Read(signal)
|
||||
// inConn.SetReadDeadline(time.Time{})
|
||||
// if err != nil {
|
||||
// log.Printf("server heartbeat connection read err: %s", err)
|
||||
// break
|
||||
// } else {
|
||||
// // log.Printf("heartbeat from server ,id:%s", serverID)
|
||||
// }
|
||||
// }
|
||||
// close(readDie)
|
||||
// }()
|
||||
// select {
|
||||
// case <-readDie:
|
||||
// case <-writeDie:
|
||||
// }
|
||||
// utils.CloseConn(&inConn)
|
||||
// s.cmServer.Remove(serverID)
|
||||
// log.Printf("server heartbeat conn %s released", serverID)
|
||||
// case CONN_CLIENT_HEARBEAT:
|
||||
// var clientID string
|
||||
// err = utils.ReadPacketData(reader, &clientID)
|
||||
// if err != nil {
|
||||
// log.Printf("read error,ERR:%s", err)
|
||||
// return
|
||||
// }
|
||||
// log.Printf("client heartbeat connection, id: %s", clientID)
|
||||
// writeDie := make(chan bool)
|
||||
// readDie := make(chan bool)
|
||||
// go func() {
|
||||
// for {
|
||||
// inConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
|
||||
// _, err = inConn.Write([]byte{0x00})
|
||||
// inConn.SetWriteDeadline(time.Time{})
|
||||
// if err != nil {
|
||||
// log.Printf("client heartbeat connection write err %s", err)
|
||||
// break
|
||||
// }
|
||||
// time.Sleep(time.Second * 3)
|
||||
// }
|
||||
// close(writeDie)
|
||||
// }()
|
||||
// go func() {
|
||||
// for {
|
||||
// signal := make([]byte, 1)
|
||||
// inConn.SetReadDeadline(time.Now().Add(time.Second * 6))
|
||||
// _, err := inConn.Read(signal)
|
||||
// inConn.SetReadDeadline(time.Time{})
|
||||
// if err != nil {
|
||||
// log.Printf("client control connection read err: %s", err)
|
||||
// break
|
||||
// } else {
|
||||
// // log.Printf("heartbeat from client ,id:%s", clientID)
|
||||
// }
|
||||
// }
|
||||
// close(readDie)
|
||||
// }()
|
||||
// select {
|
||||
// case <-readDie:
|
||||
// case <-writeDie:
|
||||
// }
|
||||
// utils.CloseConn(&inConn)
|
||||
// s.cmClient.Remove(clientID)
|
||||
// if s.clientControlConns.Has(clientID) {
|
||||
// item, _ := s.clientControlConns.Get(clientID)
|
||||
// (*item.(*net.Conn)).Close()
|
||||
// }
|
||||
// s.clientControlConns.Remove(clientID)
|
||||
// log.Printf("client heartbeat conn %s released", clientID)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@ -11,79 +11,80 @@ import (
|
||||
)
|
||||
|
||||
type TunnelClient struct {
|
||||
cfg TunnelClientArgs
|
||||
cm utils.ConnManager
|
||||
cfg TunnelClientArgs
|
||||
// cm utils.ConnManager
|
||||
ctrlConn net.Conn
|
||||
}
|
||||
|
||||
func NewTunnelClient() Service {
|
||||
return &TunnelClient{
|
||||
cfg: TunnelClientArgs{},
|
||||
cm: utils.NewConnManager(),
|
||||
// cm: utils.NewConnManager(),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
s.cm.RemoveAll()
|
||||
if s.ctrlConn != nil {
|
||||
s.ctrlConn.Close()
|
||||
}
|
||||
utils.CloseConn(&heartbeatConn)
|
||||
heartbeatConn, err := s.GetInConn(CONN_CLIENT_HEARBEAT, ID)
|
||||
if err != nil {
|
||||
log.Printf("heartbeat connection err: %s, retrying...", err)
|
||||
time.Sleep(time.Second * 3)
|
||||
utils.CloseConn(&heartbeatConn)
|
||||
continue
|
||||
}
|
||||
log.Printf("heartbeat connection created,id:%s", ID)
|
||||
writeDie := make(chan bool)
|
||||
readDie := make(chan bool)
|
||||
go func() {
|
||||
for {
|
||||
heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
|
||||
_, err = heartbeatConn.Write([]byte{0x00})
|
||||
heartbeatConn.SetWriteDeadline(time.Time{})
|
||||
if err != nil {
|
||||
log.Printf("heartbeat connection write err %s", err)
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
close(writeDie)
|
||||
}()
|
||||
go func() {
|
||||
for {
|
||||
signal := make([]byte, 1)
|
||||
heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6))
|
||||
_, err := heartbeatConn.Read(signal)
|
||||
heartbeatConn.SetReadDeadline(time.Time{})
|
||||
if err != nil {
|
||||
log.Printf("heartbeat connection read err: %s", err)
|
||||
break
|
||||
} else {
|
||||
log.Printf("heartbeat from bridge")
|
||||
}
|
||||
}
|
||||
close(readDie)
|
||||
}()
|
||||
select {
|
||||
case <-readDie:
|
||||
case <-writeDie:
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
// func (s *TunnelClient) InitHeartbeatDeamon() {
|
||||
// log.Printf("heartbeat started")
|
||||
// go func() {
|
||||
// var heartbeatConn net.Conn
|
||||
// var ID = *s.cfg.Key
|
||||
// for {
|
||||
|
||||
// //close all connection
|
||||
// s.cm.RemoveAll()
|
||||
// if s.ctrlConn != nil {
|
||||
// s.ctrlConn.Close()
|
||||
// }
|
||||
// utils.CloseConn(&heartbeatConn)
|
||||
// heartbeatConn, err := s.GetInConn(CONN_CLIENT_HEARBEAT, ID)
|
||||
// if err != nil {
|
||||
// log.Printf("heartbeat connection err: %s, retrying...", err)
|
||||
// time.Sleep(time.Second * 3)
|
||||
// utils.CloseConn(&heartbeatConn)
|
||||
// continue
|
||||
// }
|
||||
// log.Printf("heartbeat connection created,id:%s", ID)
|
||||
// writeDie := make(chan bool)
|
||||
// readDie := make(chan bool)
|
||||
// go func() {
|
||||
// for {
|
||||
// heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
|
||||
// _, err = heartbeatConn.Write([]byte{0x00})
|
||||
// heartbeatConn.SetWriteDeadline(time.Time{})
|
||||
// if err != nil {
|
||||
// log.Printf("heartbeat connection write err %s", err)
|
||||
// break
|
||||
// }
|
||||
// time.Sleep(time.Second * 3)
|
||||
// }
|
||||
// close(writeDie)
|
||||
// }()
|
||||
// go func() {
|
||||
// for {
|
||||
// signal := make([]byte, 1)
|
||||
// heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6))
|
||||
// _, err := heartbeatConn.Read(signal)
|
||||
// heartbeatConn.SetReadDeadline(time.Time{})
|
||||
// if err != nil {
|
||||
// log.Printf("heartbeat connection read err: %s", err)
|
||||
// break
|
||||
// } else {
|
||||
// //log.Printf("heartbeat from bridge")
|
||||
// }
|
||||
// }
|
||||
// close(readDie)
|
||||
// }()
|
||||
// select {
|
||||
// case <-readDie:
|
||||
// case <-writeDie:
|
||||
// }
|
||||
// }
|
||||
// }()
|
||||
// }
|
||||
func (s *TunnelClient) CheckArgs() {
|
||||
if *s.cfg.Parent != "" {
|
||||
log.Printf("use tls parent %s", *s.cfg.Parent)
|
||||
@ -96,7 +97,7 @@ func (s *TunnelClient) CheckArgs() {
|
||||
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
|
||||
}
|
||||
func (s *TunnelClient) StopService() {
|
||||
s.cm.RemoveAll()
|
||||
// s.cm.RemoveAll()
|
||||
}
|
||||
func (s *TunnelClient) Start(args interface{}) (err error) {
|
||||
s.cfg = args.(TunnelClientArgs)
|
||||
@ -106,7 +107,7 @@ func (s *TunnelClient) Start(args interface{}) (err error) {
|
||||
|
||||
for {
|
||||
//close all conn
|
||||
s.cm.Remove(*s.cfg.Key)
|
||||
// s.cm.Remove(*s.cfg.Key)
|
||||
if s.ctrlConn != nil {
|
||||
s.ctrlConn.Close()
|
||||
}
|
||||
@ -171,7 +172,7 @@ func (s *TunnelClient) ServeUDP(localAddr, ID, serverID string) {
|
||||
var err error
|
||||
// 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)
|
||||
if err != nil {
|
||||
utils.CloseConn(&inConn)
|
||||
@ -182,7 +183,7 @@ func (s *TunnelClient) ServeUDP(localAddr, ID, serverID string) {
|
||||
break
|
||||
}
|
||||
}
|
||||
s.cm.Add(*s.cfg.Key, ID, &inConn)
|
||||
// s.cm.Add(*s.cfg.Key, ID, &inConn)
|
||||
log.Printf("conn %s created", ID)
|
||||
|
||||
for {
|
||||
@ -273,12 +274,10 @@ func (s *TunnelClient) ServeConn(localAddr, ID, serverID string) {
|
||||
log.Printf("build connection error, err: %s", err)
|
||||
return
|
||||
}
|
||||
utils.IoBind(inConn, outConn, func(err error) {
|
||||
utils.IoBind(inConn, outConn, func(err interface{}) {
|
||||
log.Printf("conn %s released", ID)
|
||||
utils.CloseConn(&inConn)
|
||||
utils.CloseConn(&outConn)
|
||||
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)
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ type TunnelServerManager struct {
|
||||
udpChn chan UDPItem
|
||||
sc utils.ServerChannel
|
||||
serverID string
|
||||
cm utils.ConnManager
|
||||
// cm utils.ConnManager
|
||||
}
|
||||
|
||||
func NewTunnelServerManager() Service {
|
||||
@ -32,7 +32,7 @@ func NewTunnelServerManager() Service {
|
||||
cfg: TunnelServerArgs{},
|
||||
udpChn: make(chan UDPItem, 50000),
|
||||
serverID: utils.Uniqueid(),
|
||||
cm: utils.NewConnManager(),
|
||||
// cm: utils.NewConnManager(),
|
||||
}
|
||||
}
|
||||
func (s *TunnelServerManager) Start(args interface{}) (err error) {
|
||||
@ -91,7 +91,7 @@ func (s *TunnelServerManager) Clean() {
|
||||
s.StopService()
|
||||
}
|
||||
func (s *TunnelServerManager) StopService() {
|
||||
s.cm.RemoveAll()
|
||||
// s.cm.RemoveAll()
|
||||
}
|
||||
func (s *TunnelServerManager) CheckArgs() {
|
||||
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)
|
||||
}
|
||||
func (s *TunnelServerManager) InitService() {
|
||||
s.InitHeartbeatDeamon()
|
||||
}
|
||||
func (s *TunnelServerManager) InitHeartbeatDeamon() {
|
||||
log.Printf("heartbeat started")
|
||||
go func() {
|
||||
var heartbeatConn net.Conn
|
||||
var ID string
|
||||
for {
|
||||
//close all connection
|
||||
s.cm.Remove(ID)
|
||||
utils.CloseConn(&heartbeatConn)
|
||||
heartbeatConn, ID, err := s.GetOutConn(CONN_SERVER_HEARBEAT)
|
||||
if err != nil {
|
||||
log.Printf("heartbeat connection err: %s, retrying...", err)
|
||||
time.Sleep(time.Second * 3)
|
||||
utils.CloseConn(&heartbeatConn)
|
||||
continue
|
||||
}
|
||||
log.Printf("heartbeat connection created,id:%s", ID)
|
||||
writeDie := make(chan bool)
|
||||
readDie := make(chan bool)
|
||||
go func() {
|
||||
for {
|
||||
heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
|
||||
_, err = heartbeatConn.Write([]byte{0x00})
|
||||
heartbeatConn.SetWriteDeadline(time.Time{})
|
||||
if err != nil {
|
||||
log.Printf("heartbeat connection write err %s", err)
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
close(writeDie)
|
||||
}()
|
||||
go func() {
|
||||
for {
|
||||
signal := make([]byte, 1)
|
||||
heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6))
|
||||
_, err := heartbeatConn.Read(signal)
|
||||
heartbeatConn.SetReadDeadline(time.Time{})
|
||||
if err != nil {
|
||||
log.Printf("heartbeat connection read err: %s", err)
|
||||
break
|
||||
} else {
|
||||
// log.Printf("heartbeat from bridge")
|
||||
}
|
||||
}
|
||||
close(readDie)
|
||||
}()
|
||||
select {
|
||||
case <-readDie:
|
||||
case <-writeDie:
|
||||
}
|
||||
}
|
||||
}()
|
||||
// s.InitHeartbeatDeamon()
|
||||
}
|
||||
|
||||
// func (s *TunnelServerManager) InitHeartbeatDeamon() {
|
||||
// log.Printf("heartbeat started")
|
||||
// go func() {
|
||||
// var heartbeatConn net.Conn
|
||||
// var ID string
|
||||
// for {
|
||||
// //close all connection
|
||||
// s.cm.Remove(ID)
|
||||
// utils.CloseConn(&heartbeatConn)
|
||||
// heartbeatConn, ID, err := s.GetOutConn(CONN_SERVER_HEARBEAT)
|
||||
// if err != nil {
|
||||
// log.Printf("heartbeat connection err: %s, retrying...", err)
|
||||
// time.Sleep(time.Second * 3)
|
||||
// utils.CloseConn(&heartbeatConn)
|
||||
// continue
|
||||
// }
|
||||
// log.Printf("heartbeat connection created,id:%s", ID)
|
||||
// writeDie := make(chan bool)
|
||||
// readDie := make(chan bool)
|
||||
// go func() {
|
||||
// for {
|
||||
// heartbeatConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
|
||||
// _, err = heartbeatConn.Write([]byte{0x00})
|
||||
// heartbeatConn.SetWriteDeadline(time.Time{})
|
||||
// if err != nil {
|
||||
// log.Printf("heartbeat connection write err %s", err)
|
||||
// break
|
||||
// }
|
||||
// time.Sleep(time.Second * 3)
|
||||
// }
|
||||
// close(writeDie)
|
||||
// }()
|
||||
// go func() {
|
||||
// for {
|
||||
// signal := make([]byte, 1)
|
||||
// heartbeatConn.SetReadDeadline(time.Now().Add(time.Second * 6))
|
||||
// _, err := heartbeatConn.Read(signal)
|
||||
// heartbeatConn.SetReadDeadline(time.Time{})
|
||||
// if err != nil {
|
||||
// log.Printf("heartbeat connection read err: %s", err)
|
||||
// break
|
||||
// } else {
|
||||
// // log.Printf("heartbeat from bridge")
|
||||
// }
|
||||
// }
|
||||
// close(readDie)
|
||||
// }()
|
||||
// select {
|
||||
// case <-readDie:
|
||||
// case <-writeDie:
|
||||
// }
|
||||
// }
|
||||
// }()
|
||||
// }
|
||||
func (s *TunnelServerManager) GetOutConn(typ uint8) (outConn net.Conn, ID string, err error) {
|
||||
outConn, err = s.GetConn()
|
||||
if err != nil {
|
||||
@ -240,14 +241,12 @@ func (s *TunnelServer) Start(args interface{}) (err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
utils.IoBind(inConn, outConn, func(err error) {
|
||||
utils.CloseConn(&outConn)
|
||||
utils.CloseConn(&inConn)
|
||||
s.cfg.Mgr.cm.RemoveOne(s.cfg.Mgr.serverID, ID)
|
||||
utils.IoBind(inConn, outConn, func(err interface{}) {
|
||||
// s.cfg.Mgr.cm.RemoveOne(s.cfg.Mgr.serverID, ID)
|
||||
log.Printf("%s conn %s released", *s.cfg.Key, ID)
|
||||
})
|
||||
//add conn
|
||||
s.cfg.Mgr.cm.Add(s.cfg.Mgr.serverID, ID, &inConn)
|
||||
// s.cfg.Mgr.cm.Add(s.cfg.Mgr.serverID, ID, &inConn)
|
||||
log.Printf("%s conn %s created", *s.cfg.Key, ID)
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@ -20,7 +20,6 @@ import (
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -28,39 +27,62 @@ import (
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
func IoBind(dst io.ReadWriter, src io.ReadWriter, fn func(err error)) {
|
||||
func IoBind(dst io.ReadWriteCloser, src io.ReadWriteCloser, fn func(err interface{})) {
|
||||
go func() {
|
||||
e1 := make(chan error, 1)
|
||||
e2 := make(chan error, 1)
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Printf("bind crashed %s", err)
|
||||
}
|
||||
}()
|
||||
e1 := make(chan interface{}, 1)
|
||||
e2 := make(chan interface{}, 1)
|
||||
go func() {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
log.Printf("IoBind crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
|
||||
if err := recover(); err != nil {
|
||||
log.Printf("bind crashed %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
_, e := io.Copy(dst, src)
|
||||
e1 <- e
|
||||
//_, err := io.Copy(dst, src)
|
||||
err := ioCopy(dst, src)
|
||||
e1 <- err
|
||||
}()
|
||||
go func() {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
log.Printf("IoBind crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
|
||||
if err := recover(); err != nil {
|
||||
log.Printf("bind crashed %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
_, e := io.Copy(src, dst)
|
||||
e2 <- e
|
||||
//_, err := io.Copy(src, dst)
|
||||
err := ioCopy(src, dst)
|
||||
e2 <- err
|
||||
}()
|
||||
var err error
|
||||
var err interface{}
|
||||
select {
|
||||
case err = <-e1:
|
||||
//log.Printf("e1")
|
||||
case err = <-e2:
|
||||
//log.Printf("e2")
|
||||
}
|
||||
src.Close()
|
||||
dst.Close()
|
||||
fn(err)
|
||||
}()
|
||||
}
|
||||
|
||||
func ioCopy(dst io.ReadWriter, src io.ReadWriter) (err error) {
|
||||
buf := make([]byte, 32*1024)
|
||||
n := 0
|
||||
for {
|
||||
n, err = src.Read(buf)
|
||||
if n > 0 {
|
||||
if _, e := dst.Write(buf[0:n]); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
func TlsConnectHost(host string, timeout int, certBytes, keyBytes []byte) (conn tls.Conn, err error) {
|
||||
h := strings.Split(host, ":")
|
||||
port, _ := strconv.Atoi(h[1])
|
||||
@ -103,8 +125,15 @@ func ConnectHost(hostAndPort string, timeout int) (conn net.Conn, err error) {
|
||||
return
|
||||
}
|
||||
func ConnectKCPHost(hostAndPort, method, key string) (conn net.Conn, err error) {
|
||||
conn, err = kcp.DialWithOptions(hostAndPort, GetKCPBlock(method, key), 10, 3)
|
||||
return
|
||||
kcpconn, err := kcp.DialWithOptions(hostAndPort, GetKCPBlock(method, key), 10, 3)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
kcpconn.SetNoDelay(1, 10, 2, 1)
|
||||
kcpconn.SetWindowSize(1024, 1024)
|
||||
kcpconn.SetMtu(1400)
|
||||
kcpconn.SetACKNoDelay(false)
|
||||
return kcpconn, err
|
||||
}
|
||||
func ListenTls(ip string, port int, certBytes, keyBytes []byte) (ln *net.Listener, err error) {
|
||||
var cert tls.Certificate
|
||||
@ -401,6 +430,29 @@ func GetKCPBlock(method, key string) (block kcp.BlockCrypt) {
|
||||
}
|
||||
return
|
||||
}
|
||||
func HttpGet(URL string, timeout int) (body []byte, code int, err error) {
|
||||
var tr *http.Transport
|
||||
var client *http.Client
|
||||
conf := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
if strings.Contains(URL, "https://") {
|
||||
tr = &http.Transport{TLSClientConfig: conf}
|
||||
client = &http.Client{Timeout: time.Millisecond * time.Duration(timeout), Transport: tr}
|
||||
} else {
|
||||
tr = &http.Transport{}
|
||||
client = &http.Client{Timeout: time.Millisecond * time.Duration(timeout), Transport: tr}
|
||||
}
|
||||
defer tr.CloseIdleConnections()
|
||||
resp, err := client.Get(URL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
code = resp.StatusCode
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
return
|
||||
}
|
||||
|
||||
// type sockaddr struct {
|
||||
// family uint16
|
||||
|
||||
@ -176,7 +176,11 @@ func (c *Checker) Add(address string, isHTTPS bool, method, URL string, data []b
|
||||
}
|
||||
|
||||
type BasicAuth struct {
|
||||
data ConcurrentMap
|
||||
data ConcurrentMap
|
||||
authURL string
|
||||
authOkCode int
|
||||
authTimeout int
|
||||
authRetry int
|
||||
}
|
||||
|
||||
func NewBasicAuth() BasicAuth {
|
||||
@ -184,6 +188,12 @@ func NewBasicAuth() BasicAuth {
|
||||
data: NewConcurrentMap(),
|
||||
}
|
||||
}
|
||||
func (ba *BasicAuth) SetAuthURL(URL string, code, timeout, retry int) {
|
||||
ba.authURL = URL
|
||||
ba.authOkCode = code
|
||||
ba.authTimeout = timeout
|
||||
ba.authRetry = retry
|
||||
}
|
||||
func (ba *BasicAuth) AddFromFile(file string) (n int, err error) {
|
||||
_content, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
@ -213,21 +223,69 @@ func (ba *BasicAuth) Add(userpassArr []string) (n int) {
|
||||
}
|
||||
return
|
||||
}
|
||||
func (ba *BasicAuth) CheckUserPass(user, pass string) (ok bool) {
|
||||
if p, _ok := ba.data.Get(user); _ok {
|
||||
return p.(string) == pass
|
||||
}
|
||||
return
|
||||
func (ba *BasicAuth) CheckUserPass(user, pass, ip, target string) (ok bool) {
|
||||
|
||||
return ba.Check(user+":"+pass, ip, target)
|
||||
}
|
||||
func (ba *BasicAuth) Check(userpass string) (ok bool) {
|
||||
func (ba *BasicAuth) Check(userpass string, ip, target string) (ok bool) {
|
||||
u := strings.Split(strings.Trim(userpass, " "), ":")
|
||||
if len(u) == 2 {
|
||||
if p, _ok := ba.data.Get(u[0]); _ok {
|
||||
return p.(string) == u[1]
|
||||
}
|
||||
if ba.authURL != "" {
|
||||
err := ba.checkFromURL(userpass, ip, target)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
log.Printf("%s", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return
|
||||
}
|
||||
func (ba *BasicAuth) checkFromURL(userpass, ip, target string) (err error) {
|
||||
u := strings.Split(strings.Trim(userpass, " "), ":")
|
||||
if len(u) != 2 {
|
||||
return
|
||||
}
|
||||
URL := ba.authURL
|
||||
if strings.Contains(URL, "?") {
|
||||
URL += "&"
|
||||
} else {
|
||||
URL += "?"
|
||||
}
|
||||
URL += fmt.Sprintf("user=%s&pass=%s&ip=%s&target=%s", u[0], u[1], ip, target)
|
||||
var code int
|
||||
var tryCount = 0
|
||||
var body []byte
|
||||
for tryCount <= ba.authRetry {
|
||||
body, code, err = HttpGet(URL, ba.authTimeout)
|
||||
if err == nil && code == ba.authOkCode {
|
||||
break
|
||||
} else if err != nil {
|
||||
err = fmt.Errorf("auth fail from url %s,resonse err:%s , %s", URL, err, ip)
|
||||
} else {
|
||||
if len(body) > 0 {
|
||||
err = fmt.Errorf(string(body[0:100]))
|
||||
} else {
|
||||
err = fmt.Errorf("token error")
|
||||
}
|
||||
err = fmt.Errorf("auth fail from url %s,resonse code: %d, except: %d , %s , %s", URL, code, ba.authOkCode, ip, string(body))
|
||||
}
|
||||
if err != nil && tryCount < ba.authRetry {
|
||||
log.Print(err)
|
||||
time.Sleep(time.Second * 2)
|
||||
}
|
||||
tryCount++
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//log.Printf("auth success from auth url, %s", ip)
|
||||
return
|
||||
}
|
||||
|
||||
func (ba *BasicAuth) Total() (n int) {
|
||||
n = ba.data.Count()
|
||||
return
|
||||
@ -292,7 +350,11 @@ func (req *HTTPRequest) HTTP() (err error) {
|
||||
}
|
||||
req.URL, err = req.getHTTPURL()
|
||||
if err == nil {
|
||||
u, _ := url.Parse(req.URL)
|
||||
var u *url.URL
|
||||
u, err = url.Parse(req.URL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Host = u.Host
|
||||
req.addPortIfNot()
|
||||
}
|
||||
@ -321,6 +383,14 @@ func (req *HTTPRequest) BasicAuth() (err error) {
|
||||
CloseConn(req.conn)
|
||||
return
|
||||
}
|
||||
if authorization == "" {
|
||||
authorization, err = req.getHeader("Proxy-Authorization")
|
||||
if err != nil {
|
||||
fmt.Fprint((*req.conn), "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"\"\r\n\r\nUnauthorized")
|
||||
CloseConn(req.conn)
|
||||
return
|
||||
}
|
||||
}
|
||||
//log.Printf("Authorization:%s", authorization)
|
||||
basic := strings.Fields(authorization)
|
||||
if len(basic) != 2 {
|
||||
@ -334,7 +404,14 @@ func (req *HTTPRequest) BasicAuth() (err error) {
|
||||
CloseConn(req.conn)
|
||||
return
|
||||
}
|
||||
authOk := (*req.basicAuth).Check(string(user))
|
||||
addr := strings.Split((*req.conn).RemoteAddr().String(), ":")
|
||||
URL := ""
|
||||
if req.IsHTTPS() {
|
||||
URL = "https://" + req.Host
|
||||
} else {
|
||||
URL, _ = req.getHTTPURL()
|
||||
}
|
||||
authOk := (*req.basicAuth).Check(string(user), addr[0], URL)
|
||||
//log.Printf("auth %s,%v", string(user), authOk)
|
||||
if !authOk {
|
||||
fmt.Fprint((*req.conn), "HTTP/1.1 401 Unauthorized\r\n\r\nUnauthorized")
|
||||
@ -358,6 +435,7 @@ func (req *HTTPRequest) getHTTPURL() (URL string, err error) {
|
||||
func (req *HTTPRequest) getHeader(key string) (val string, err error) {
|
||||
key = strings.ToUpper(key)
|
||||
lines := strings.Split(string(req.HeadBuf), "\r\n")
|
||||
//log.Println(lines)
|
||||
for _, line := range lines {
|
||||
line := strings.SplitN(strings.Trim(line, "\r\n "), ":", 2)
|
||||
if len(line) == 2 {
|
||||
@ -369,7 +447,6 @@ func (req *HTTPRequest) getHeader(key string) (val string, err error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("can not find HOST header")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user