Merge branch 'dev'

This commit is contained in:
arraykeys@gmail.com
2017-10-20 12:59:31 +08:00
10 changed files with 291 additions and 95 deletions

View File

@ -1,4 +1,7 @@
proxy更新日志
v3.4
1.socks5代理新增了用户名密码验证支持.
v3.3
1.修复了socks代理模式对证书文件的判断逻辑.
2.增强了http代理,socks代理的ssh中转模式的稳定性.

162
README.md
View File

@ -24,23 +24,81 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
- 替代圣剑内网通显IP内网通花生壳之类的工具.
- ...  
### 手册目录
本页是最新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)
- [v2.x手册](https://github.com/snail007/goproxy/tree/v2.2)
### 安装
1. [快速安装](#自动安装)
1. [手动安装](#手动安装)
### 首次使用必看
- [环境](#使用教程)
- [使用配置文件](#使用配置文件)
- [生成通讯证书文件](#生成加密通讯需要的证书文件)
### 手册目录
- [1. HTTP代理](#1http代理)
- [1.1 普通HTTP代理](#11普通http代理)
- [1.2 普通二级HTTP代理](#12普通二级http代理)
- [1.3 HTTP二级代理(加密)](#13http二级代理加密)
- [1.4 HTTP三级代理(加密)](#14http三级代理加密)
- [1.5 Basic认证](#15basic认证)
- [1.6 强制走上级HTTP代理](#16http代理流量强制走上级http代理)
- [1.7 通过SSH中转](#17https通过ssh中转)
- [1.7.1 用户名和密码的方式](#171-ssh用户名和密码的方式)
- [1.7.2 用户名和密钥的方式](#172-ssh用户名和密钥的方式)
- [1.8 查看帮助](#18查看帮助)
- [2. TCP代理](#2tcp代理)
- [2.1 普通一级TCP代理](#21普通一级tcp代理)
- [2.2 普通二级TCP代理](#22普通二级tcp代理)
- [2.3 普通三级TCP代理](#23普通三级tcp代理)
- [2.4 加密二级TCP代理](#24加密二级tcp代理)
- [2.5 加密三级TCP代理](#25加密三级tcp代理)
- [2.6 查看帮助](#26查看帮助)
- [3. UDP代理](#3udp代理)
- [3.1 普通一级TCP代理](#31普通一级udp代理)
- [3.2 普通二级TCP代理](#32普通二级udp代理)
- [3.3 普通三级TCP代理](#33普通三级udp代理)
- [3.4 加密二级TCP代理](#34加密二级udp代理)
- [3.5 加密三级TCP代理](#35加密三级udp代理)
- [3.6 查看帮助](#36查看帮助)
- [4. 内网穿透](#4内网穿透)
- [4.1 原理说明](#41原理说明)
- [4.2 TCP普通用法](#42tcp普通用法)
- [4.3 微信接口本地开发](#43微信接口本地开发)
- [4.4 UDP普通用法](#44udp普通用法)
- [4.5 高级用法一](#45高级用法一)
- [4.6 高级用法一](#46高级用法二)
- [4.7 tserver的-r参数](#47tserver的-r参数)
- [4.8 查看帮助](#48查看帮助)
- [5. SOCKS5代理](#5socks5代理)
- [5.1 普通SOCKS5代理](#51普通socks5代理)
- [5.2 普通二级SOCKS5代理](#52普通二级socks5代理)
- [5.3 SOCKS二级代理(加密)](#53socks二级代理加密)
- [5.4 SOCKS三级代理(加密)](#54socks三级代理加密)
- [5.5 流量强制走上级SOCKS代理](#55socks代理流量强制走上级socks代理)
- [5.6 通过SSH中转](#56socks通过ssh中转)
- [5.6.1 用户名和密码的方式](#561-ssh用户名和密码的方式)
- [5.6.2 用户名和密钥的方式](#562-ssh用户名和密钥的方式)
- [5.7 认证](#57认证)
- [5.8 查看帮助](#58查看帮助)
### Fast Start
提示:所有操作需要root权限.
**0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.**
#### 自动安装
#### **0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.**
```shell
curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.sh | bash
```
安装完成,配置目录是/etc/proxy,更详细的使用方法参考下面的进一步了解.
如果安装失败或者你的vps不是linux64位系统,请按照下面的半自动步骤安装:
**1.登录你的VPS,下载守护进程monexec,选择合适你的版本,vps一般选择"linux_amd64.tar.gz"的即可.**
#### 手动安装
#### **1.登录你的VPS,下载守护进程monexec,选择合适你的版本,vps一般选择"linux_amd64.tar.gz"的即可.**
下载地址:https://github.com/reddec/monexec/releases
比如下载到/root/proxy/
执行:
@ -49,13 +107,13 @@ mkdir /root/proxy/
cd /root/proxy/
wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
```
**2.下载proxy**
#### **2.下载proxy**
下载地址:https://github.com/snail007/goproxy/releases
```shell
cd /root/proxy/
wget https://github.com/snail007/goproxy/releases/download/v3.1fix/proxy-linux-amd64.tar.gz
```
**3.下载自动安装脚本**
#### **3.下载自动安装脚本**
```shell
cd /root/proxy/
wget https://raw.githubusercontent.com/snail007/goproxy/master/install.sh
@ -65,11 +123,11 @@ chmod +x install.sh
## 使用教程
**提示**
#### **提示**
接下来的教程,默认系统是linux,程序是proxy所有操作需要root权限
如果你的是windows,请使用windows版本的proxy.exe即可.
**使用配置文件**
### **使用配置文件**
接下来的教程都是通过命令行参数介绍使用方法,也可以通过读取配置文件获取参数.
具体格式是通过@符号指定配置文件,例如:./proxy @configfile.txt
configfile.txt里面的格式是,第一行是子命令名称,第二行开始一行一个:参数的长格式=参数值,前后不能有空格和双引号.
@ -88,10 +146,10 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,
默认会在当前程序目录下面生成证书文件proxy.crt和key文件proxy.key。
### 1.HTTP代理
**1.1.普通HTTP代理**
#### **1.1.普通HTTP代理**
`./proxy http -t tcp -p "0.0.0.0:38080"`
**1.2.普通二级HTTP代理**
#### **1.2.普通二级HTTP代理**
使用本地端口8090,假设上级HTTP代理是`22.22.22.22:8080`
`./proxy http -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" `
默认关闭了连接池,如果要加快访问速度,-L可以开启连接池,10就是连接池大小,0为关闭,
@ -100,7 +158,7 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,
我们还可以指定网站域名的黑白名单文件,一行一个域名,怕匹配规则是最右批评匹配,比如:baidu.com,匹配的是*.*.baidu.com,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理.
`./proxy http -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt`
**1.3.HTTP二级代理(加密)**
#### **1.3.HTTP二级代理(加密)**
一级HTTP代理(VPS,IP:22.22.22.22)
`./proxy http -t tls -p ":38080" -C proxy.crt -K proxy.key`
@ -112,7 +170,7 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,
`./proxy.exe http -t tcp -p ":8080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
然后设置你的windos系统中需要通过代理上网的程序的代理为http模式地址为127.0.0.1端口为8080,程序即可通过加密通道通过vps上网。
**1.4.HTTP三级代理(加密)**
#### **1.4.HTTP三级代理(加密)**
一级HTTP代理VPS_01,IP:22.22.22.22
`./proxy http -t tls -p ":38080" -C proxy.crt -K proxy.key`
二级HTTP代理VPS_02,IP:33.33.33.33
@ -121,7 +179,7 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,
`./proxy http -t tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地的8080端口就是访问一级HTTP代理上面的代理端口38080.
**1.5.Basic认证**
#### **1.5.Basic认证**
对于代理HTTP协议我们可以basic进行Basic认证,认证的用户名和密码可以在命令行指定
`./proxy http -t tcp -p ":33080" -a "user1:pass1" -a "user2:pass2"`
多个用户,重复-a参数即可.
@ -129,42 +187,42 @@ http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,
`./proxy http -t tcp -p ":33080" -F auth-file.txt`
如果没有-a或-F参数,就是关闭Basic认证.
**1.6.HTTP代理流量强制走上级HTTP代理**
#### **1.6.HTTP代理流量强制走上级HTTP代理**
默认情况下,proxy会智能判断一个网站域名是否无法访问,如果无法访问才走上级HTTP代理.通过--always可以使全部HTTP代理流量强制走上级HTTP代理.
`./proxy http --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
**1.7.HTTP(S)通过SSH中转**
#### **1.7.HTTP(S)通过SSH中转**
说明:ssh中转的原理是利用了ssh的转发功能,就是你连接上ssh之后,可以通过ssh代理访问目标地址.
假设有:vps
- IP是2.2.2.2, ssh端口是22, ssh用户名是:user, ssh用户密码是:demo
- 用户user的ssh私钥名称是user.key
***1.7.1 ssh用户名和密码的方式***
##### ***1.7.1 ssh用户名和密码的方式***
本地HTTP(S)代理28080端口,执行:
`./proxy http -T ssh -P "2.2.2.2:22" -u user -A demo -t tcp -p ":28080"`
***1.7.2 ssh用户名和密钥的方式***
##### ***1.7.2 ssh用户名和密钥的方式***
本地HTTP(S)代理28080端口,执行:
`./proxy http -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"`
**1.8.查看帮助**
#### **1.8.查看帮助**
`./proxy help http`
### 2.TCP代理
**2.1.普通一级TCP代理**
#### **2.1.普通一级TCP代理**
本地执行:
`./proxy tcp -p ":33080" -T tcp -P "192.168.22.33:22" -L 0`
那么访问本地33080端口就是访问192.168.22.33的22端口.
**2.2.普通二级TCP代理**
#### **2.2.普通二级TCP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp -p ":33080" -T tcp -P "127.0.0.1:8080" -L 0`
本地执行:
`./proxy tcp -p ":23080" -T tcp -P "22.22.22.33:33080"`
那么访问本地23080端口就是访问22.22.22.33的8080端口.
**2.3.普通三级TCP代理**
#### **2.3.普通三级TCP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp -p ":38080" -T tcp -P "66.66.66.66:8080" -L 0`
二级TCP代理VPS_02,IP:33.33.33.33
@ -173,14 +231,14 @@ VPS(IP:22.22.22.33)执行:
`./proxy tcp -p ":8080" -T tcp -P "33.33.33.33:28080"`
那么访问本地8080端口就是通过加密TCP隧道访问66.66.66.66的8080端口.
**2.4.加密二级TCP代理**
#### **2.4.加密二级TCP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp --tls -p ":33080" -T tcp -P "127.0.0.1:8080" -L 0 -C proxy.crt -K proxy.key`
本地执行:
`./proxy tcp -p ":23080" -T tls -P "22.22.22.33:33080" -C proxy.crt -K proxy.key`
那么访问本地23080端口就是通过加密TCP隧道访问22.22.22.33的8080端口.
**2.5.加密三级TCP代理**
#### **2.5.加密三级TCP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp --tls -p ":38080" -T tcp -P "66.66.66.66:8080" -C proxy.crt -K proxy.key`
二级TCP代理VPS_02,IP:33.33.33.33
@ -189,24 +247,24 @@ VPS(IP:22.22.22.33)执行:
`./proxy tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地8080端口就是通过加密TCP隧道访问66.66.66.66的8080端口.
**2.6.查看帮助**
#### **2.6.查看帮助**
`./proxy help tcp`
### 3.UDP代理
**3.1.普通一级UDP代理**
#### **3.1.普通一级UDP代理**
本地执行:
`./proxy udp -p ":5353" -T udp -P "8.8.8.8:53"`
那么访问本地UDP:5353端口就是访问8.8.8.8的UDP:53端口.
**3.2.普通二级UDP代理**
#### **3.2.普通二级UDP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp -p ":33080" -T udp -P "8.8.8.8:53"`
本地执行:
`./proxy udp -p ":5353" -T tcp -P "22.22.22.33:33080"`
那么访问本地UDP:5353端口就是通过TCP隧道,通过VPS访问8.8.8.8的UDP:53端口.
**3.3.普通三级UDP代理**
#### **3.3.普通三级UDP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp -p ":38080" -T udp -P "8.8.8.8:53"`
二级TCP代理VPS_02,IP:33.33.33.33
@ -215,14 +273,14 @@ VPS(IP:22.22.22.33)执行:
`./proxy udp -p ":5353" -T tcp -P "33.33.33.33:28080"`
那么访问本地5353端口就是通过TCP隧道,通过VPS访问8.8.8.8的53端口.
**3.4.加密二级UDP代理**
#### **3.4.加密二级UDP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp --tls -p ":33080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key`
本地执行:
`./proxy udp -p ":5353" -T tls -P "22.22.22.33:33080" -C proxy.crt -K proxy.key`
那么访问本地UDP:5353端口就是通过加密TCP隧道,通过VPS访问8.8.8.8的UDP:53端口.
**3.5.加密三级UDP代理**
#### **3.5.加密三级UDP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp --tls -p ":38080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key`
二级TCP代理VPS_02,IP:33.33.33.33
@ -231,11 +289,11 @@ VPS(IP:22.22.22.33)执行:
`./proxy udp -p ":5353" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地5353端口就是通过加密TCP隧道,通过VPS_01访问8.8.8.8的53端口.
**3.6.查看帮助**
#### **3.6.查看帮助**
`./proxy help udp`
### 4.内网穿透
**4.1、原理说明**
#### **4.1、原理说明**
内网穿透,由三部分组成:client端,server端,bridge端client和server主动连接bridge端进行桥接.
当用户访问server端,流程是:
1. server主动和bridge端建立连接
@ -244,7 +302,7 @@ VPS(IP:22.22.22.33)执行:
1. 然后bridge端把client过来的连接与server端过来的连接绑定
1. 整个通道建立完成;
**4.2、TCP普通用法**
#### **4.2、TCP普通用法**
背景:
- 公司机器A提供了web服务80端口
- 有VPS一个,公网IP:22.22.22.22
@ -263,7 +321,7 @@ VPS(IP:22.22.22.33)执行:
1. 完成
**4.3、微信接口本地开发**
#### **4.3、微信接口本地开发**
背景:
- 自己的笔记本提供了nginx服务80端口
- 有VPS一个,公网IP:22.22.22.22
@ -285,7 +343,7 @@ VPS(IP:22.22.22.33)执行:
1. 完成
**4.4、UDP普通用法**
#### **4.4、UDP普通用法**
背景:
- 公司机器A提供了DNS解析服务,UDP:53端口
- 有VPS一个,公网IP:22.22.22.22
@ -303,7 +361,7 @@ VPS(IP:22.22.22.33)执行:
1. 完成
**4.5、高级用法一**
#### **4.5、高级用法一**
背景:
- 公司机器A提供了web服务80端口
- 有VPS一个,公网IP:22.22.22.22
@ -324,7 +382,7 @@ VPS(IP:22.22.22.33)执行:
1. 完成
**4.6、高级用法二**
#### **4.6、高级用法二**
提示:
如果同时有多个client连接到同一个bridge,需要指定不同的key,可以通过--k参数设定,--k可以是任意唯一字符串,
只要在同一个bridge上唯一即可.
@ -349,7 +407,7 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid
1. 完成
**4.7.tserver的-r参数**
#### **4.7.tserver的-r参数**
-r完整格式是:`PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT`
4.7.1.协议PROTOCOL:tcp或者udp.
@ -364,23 +422,23 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid
4.7.3.LOCAL_IP为空默认是:`0.0.0.0`,CLIENT_LOCAL_HOST为空默认是:`127.0.0.1`;
**4.8.查看帮助**
#### **4.8.查看帮助**
`./proxy help tbridge`
`./proxy help tserver`
`./proxy help tserver`
### 5.SOCKS5代理
提示:SOCKS5代理,只支持TCP协议,不支持UDP协议,不支持用户名密码认证.
**5.1.普通SOCKS5代理**
#### **5.1.普通SOCKS5代理**
`./proxy socks -t tcp -p "0.0.0.0:38080"`
**5.2.普通二级SOCKS5代理**
#### **5.2.普通二级SOCKS5代理**
使用本地端口8090,假设上级SOCKS5代理是`22.22.22.22:8080`
`./proxy socks -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" `
我们还可以指定网站域名的黑白名单文件,一行一个域名,怕匹配规则是最右批评匹配,比如:baidu.com,匹配的是*.*.baidu.com,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理.
`./proxy socks -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt`
**5.3.SOCKS二级代理(加密)**
#### **5.3.SOCKS二级代理(加密)**
一级SOCKS代理(VPS,IP:22.22.22.22)
`./proxy socks -t tls -p ":38080" -C proxy.crt -K proxy.key`
@ -392,7 +450,7 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid
`./proxy.exe socks -t tcp -p ":8080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
然后设置你的windos系统中需要通过代理上网的程序的代理为socks5模式地址为127.0.0.1端口为8080,程序即可通过加密通道通过vps上网。
**5.4.SOCKS三级代理(加密)**
#### **5.4.SOCKS三级代理(加密)**
一级SOCKS代理VPS_01,IP:22.22.22.22
`./proxy socks -t tls -p ":38080" -C proxy.crt -K proxy.key`
二级SOCKS代理VPS_02,IP:33.33.33.33
@ -401,31 +459,39 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid
`./proxy socks -t tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地的8080端口就是访问一级SOCKS代理上面的代理端口38080.
**5.5.SOCKS代理流量强制走上级SOCKS代理**
#### **5.5.SOCKS代理流量强制走上级SOCKS代理**
默认情况下,proxy会智能判断一个网站域名是否无法访问,如果无法访问才走上级SOCKS代理.通过--always可以使全部SOCKS代理流量强制走上级SOCKS代理.
`./proxy socks --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
**5.6.SOCKS通过SSH中转**
#### **5.6.SOCKS通过SSH中转**
说明:ssh中转的原理是利用了ssh的转发功能,就是你连接上ssh之后,可以通过ssh代理访问目标地址.
假设有:vps
- IP是2.2.2.2, ssh端口是22, ssh用户名是:user, ssh用户密码是:demo
- 用户user的ssh私钥名称是user.key
***5.6.1 ssh用户名和密码的方式***
##### ***5.6.1 ssh用户名和密码的方式***
本地SOCKS5代理28080端口,执行:
`./proxy socks -T ssh -P "2.2.2.2:22" -u user -A demo -t tcp -p ":28080"`
***5.6.2 ssh用户名和密钥的方式***
##### ***5.6.2 ssh用户名和密钥的方式***
本地SOCKS5代理28080端口,执行:
`./proxy socks -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"`
那么访问本地的28080端口就是通过VPS访问目标地址.
**5.7.查看帮助**
#### **5.7.认证**
对于socks5代理协议我们可以进行用户名密码认证,认证的用户名和密码可以在命令行指定
`./proxy socks -t tcp -p ":33080" -a "user1:pass1" -a "user2:pass2"`
多个用户,重复-a参数即可.
也可以放在文件中,格式是一行一个"用户名:密码",然后用-F指定.
`./proxy socks -t tcp -p ":33080" -F auth-file.txt`
如果没有-a或-F参数,就是关闭认证.
#### **5.8.查看帮助**
`./proxy help socks`
### TODO
- SOCKS5增加用户名密码认证?
- http,socks代理多个上级负载均衡?
- 内网穿透server<->bridge心跳机制?
- 欢迎加群反馈...
### 如何使用源码?

View File

@ -124,6 +124,8 @@ func initConfig() (err error) {
socksArgs.Interval = socks.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int()
socksArgs.Blocked = socks.Flag("blocked", "blocked domain file , one domain each line").Default("blocked").Short('b').String()
socksArgs.Direct = socks.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
socksArgs.AuthFile = socks.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
socksArgs.Auth = socks.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
//parse args
serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
flags := log.Ldate

View File

@ -6,7 +6,7 @@ fi
mkdir /tmp/proxy
cd /tmp/proxy
wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
wget https://github.com/snail007/goproxy/releases/download/v3.3/proxy-linux-amd64.tar.gz
wget https://github.com/snail007/goproxy/releases/download/v3.4/proxy-linux-amd64.tar.gz
# install monexec
tar zxvf monexec_0.1.1_linux_amd64.tar.gz

View File

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

View File

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

View File

@ -118,6 +118,8 @@ type SocksArgs struct {
Interval *int
Blocked *string
Direct *string
AuthFile *string
Auth *[]string
}
func (a *TCPArgs) Protocol() string {

View File

@ -78,6 +78,28 @@ func (s *HTTP) InitService() {
if err != nil {
log.Fatalf("init service fail, ERR: %s", err)
}
go func() {
//循环检查ssh网络连通性
for {
conn, err := utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout*2)
if err == nil {
_, err = conn.Write([]byte{0})
}
if err != nil {
if s.sshClient != nil {
s.sshClient.Close()
if s.sshClient.Conn != nil {
s.sshClient.Conn.Close()
}
}
log.Printf("ssh offline, retrying...")
s.ConnectSSH()
} else {
conn.Close()
}
time.Sleep(time.Second * 3)
}
}()
}
}
func (s *HTTP) StopService() {
@ -168,17 +190,28 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
}
var outConn net.Conn
var _outConn interface{}
if useProxy {
if *s.cfg.ParentType == "ssh" {
outConn, err = s.getSSHConn(address)
} else {
_outConn, err = s.outPool.Pool.Get()
if err == nil {
outConn = _outConn.(net.Conn)
tryCount := 0
maxTryCount := 5
for {
if useProxy {
if *s.cfg.ParentType == "ssh" {
outConn, err = s.getSSHConn(address)
} else {
_outConn, err = s.outPool.Pool.Get()
if err == nil {
outConn = _outConn.(net.Conn)
}
}
} else {
outConn, err = utils.ConnectHost(address, *s.cfg.Timeout)
}
tryCount++
if err == nil || tryCount > maxTryCount {
break
} else {
log.Printf("connect to %s , err:%s,retrying...", *s.cfg.Parent, err)
time.Sleep(time.Second * 2)
}
} else {
outConn, err = utils.ConnectHost(address, *s.cfg.Timeout)
}
if err != nil {
log.Printf("connect to %s , err:%s", *s.cfg.Parent, err)
@ -215,22 +248,25 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
func (s *HTTP) getSSHConn(host string) (outConn net.Conn, err interface{}) {
maxTryCount := 1
tryCount := 0
errchn := make(chan interface{}, 1)
RETRY:
if tryCount >= maxTryCount {
return
}
wait := make(chan bool, 1)
go func() {
defer func() {
if err == nil {
errchn <- recover()
} else {
errchn <- nil
err = recover()
}
wait <- true
}()
outConn, err = s.sshClient.Dial("tcp", host)
}()
err = <-errchn
select {
case <-wait:
case <-time.After(time.Second * 5):
err = fmt.Errorf("ssh dial %s timeout", host)
}
if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err)
e := s.ConnectSSH()

View File

@ -37,7 +37,6 @@ func NewSocks() Service {
func (s *Socks) CheckArgs() {
var err error
if *s.cfg.LocalType == "tls" {
//log.Println(*s.cfg.CertFile, *s.cfg.KeyFile)
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
if *s.cfg.Parent != "" {
@ -45,7 +44,6 @@ func (s *Socks) CheckArgs() {
log.Fatalf("parent type unkown,use -T <tls|tcp|ssh>")
}
if *s.cfg.ParentType == "tls" {
log.Println(*s.cfg.CertFile, *s.cfg.KeyFile)
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
if *s.cfg.ParentType == "ssh" {
@ -78,6 +76,7 @@ func (s *Socks) CheckArgs() {
}
func (s *Socks) InitService() {
s.InitBasicAuth()
s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct)
if *s.cfg.ParentType == "ssh" {
err := s.ConnectSSH()
@ -88,12 +87,12 @@ func (s *Socks) InitService() {
//循环检查ssh网络连通性
for {
conn, err := utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout*2)
if err == nil {
_, err = conn.Write([]byte{0})
}
if err != nil {
if s.sshClient != nil {
s.sshClient.Close()
if s.sshClient.Conn != nil {
s.sshClient.Conn.Close()
}
}
log.Printf("ssh offline, retrying...")
s.ConnectSSH()
@ -189,7 +188,7 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) {
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*2)))
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*5)))
_, err = conn.Write(rawB)
log.Printf("udp request:%v", len(rawB))
if err != nil {
@ -287,25 +286,72 @@ func (s *Socks) socksConnCallback(inConn net.Conn) {
}
utils.CloseConn(&inConn)
}()
//协商开始
//method select request
inConn.SetReadDeadline(time.Now().Add(time.Second * 3))
methodReq, err := socks.NewMethodsRequest(inConn)
if err != nil || !methodReq.Select(socks.Method_NO_AUTH) {
inConn.SetReadDeadline(time.Time{})
if err != nil {
methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn)
log.Printf("new methods request fail,ERR: %s", err)
return
}
//method select reply
err = methodReq.Reply(socks.Method_NO_AUTH)
if err != nil {
log.Printf("reply answer data fail,ERR: %s", err)
utils.CloseConn(&inConn)
return
if !s.IsBasicAuth() {
if !methodReq.Select(socks.Method_NO_AUTH) {
methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn)
log.Printf("none method found : Method_NO_AUTH")
return
}
//method select reply
err = methodReq.Reply(socks.Method_NO_AUTH)
if err != nil {
log.Printf("reply answer data fail,ERR: %s", err)
utils.CloseConn(&inConn)
return
}
// log.Printf("% x", methodReq.Bytes())
} else {
//auth
if !methodReq.Select(socks.Method_USER_PASS) {
methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn)
log.Printf("none method found : Method_USER_PASS")
return
}
//method reply need auth
err = methodReq.Reply(socks.Method_USER_PASS)
if err != nil {
log.Printf("reply answer data fail,ERR: %s", err)
utils.CloseConn(&inConn)
return
}
//read auth
buf := make([]byte, 500)
inConn.SetReadDeadline(time.Now().Add(time.Second * 3))
n, err := inConn.Read(buf)
inConn.SetReadDeadline(time.Time{})
if err != nil {
utils.CloseConn(&inConn)
return
}
r := buf[:n]
user := string(r[2 : r[1]+2])
pass := string(r[2+r[1]+1:])
//log.Printf("user:%s,pass:%s", user, pass)
//auth
if s.basicAuth.CheckUserPass(user, pass) {
inConn.Write([]byte{0x01, 0x00})
} else {
inConn.Write([]byte{0x01, 0x01})
utils.CloseConn(&inConn)
return
}
}
// log.Printf("% x", methodReq.Bytes())
//request detail
request, err := socks.NewRequest(inConn)
if err != nil {
@ -313,6 +359,7 @@ func (s *Socks) socksConnCallback(inConn net.Conn) {
utils.CloseConn(&inConn)
return
}
//协商结束
switch request.CMD() {
case socks.CMD_BIND:
@ -344,19 +391,30 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
defer utils.CloseConn(&outConn)
var err interface{}
useProxy := true
if *s.cfg.Always {
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr())
} else {
if *s.cfg.Parent != "" {
s.checker.Add(request.Addr(), true, "", "", nil)
useProxy, _, _ = s.checker.IsBlocked(request.Addr())
if useProxy {
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr())
tryCount := 0
maxTryCount := 5
for {
if *s.cfg.Always {
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr())
} else {
if *s.cfg.Parent != "" {
s.checker.Add(request.Addr(), true, "", "", nil)
useProxy, _, _ = s.checker.IsBlocked(request.Addr())
if useProxy {
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr())
} else {
outConn, err = utils.ConnectHost(request.Addr(), *s.cfg.Timeout)
}
} else {
outConn, err = utils.ConnectHost(request.Addr(), *s.cfg.Timeout)
}
}
tryCount++
if err == nil || tryCount > maxTryCount {
break
} else {
outConn, err = utils.ConnectHost(request.Addr(), *s.cfg.Timeout)
log.Printf("get out conn fail,%s,retrying...", err)
time.Sleep(time.Second * 2)
}
}
if err != nil {
@ -398,7 +456,6 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
utils.CloseConn(&outConn)
}
func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn net.Conn, err interface{}) {
errchn := make(chan interface{}, 1)
switch *s.cfg.ParentType {
case "tls":
fallthrough
@ -441,17 +498,22 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn n
if tryCount >= maxTryCount {
return
}
wait := make(chan bool, 1)
go func() {
defer func() {
if err == nil {
errchn <- recover()
} else {
errchn <- nil
err = recover()
}
wait <- true
}()
outConn, err = s.sshClient.Dial("tcp", host)
}()
err = <-errchn
select {
case <-wait:
case <-time.After(time.Millisecond * time.Duration(*s.cfg.Timeout) * 2):
err = fmt.Errorf("ssh dial %s timeout", host)
s.sshClient.Close()
}
if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err)
e := s.ConnectSSH()
@ -489,3 +551,23 @@ func (s *Socks) ConnectSSH() (err error) {
<-s.lockChn
return
}
func (s *Socks) InitBasicAuth() (err error) {
s.basicAuth = utils.NewBasicAuth()
if *s.cfg.AuthFile != "" {
var n = 0
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
if err != nil {
err = fmt.Errorf("auth-file ERR:%s", err)
return
}
log.Printf("auth data added from file %d , total:%d", n, s.basicAuth.Total())
}
if len(*s.cfg.Auth) > 0 {
n := s.basicAuth.Add(*s.cfg.Auth)
log.Printf("auth data added %d, total:%d", n, s.basicAuth.Total())
}
return
}
func (s *Socks) IsBasicAuth() bool {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0
}

View File

@ -213,7 +213,12 @@ 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) Check(userpass string) (ok bool) {
u := strings.Split(strings.Trim(userpass, " "), ":")
if len(u) == 2 {