mirror of https://github.com/ehang-io/nps
https 、客户端与服务端连接优化
parent
717028e5f1
commit
eccc221e67
389
README.md
389
README.md
|
@ -6,7 +6,7 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
|
|||
目前市面上提供类似服务的有花生壳、TeamView、GoToMyCloud等等,但要使用第三方的公网服务器就必须为第三方付费,并且这些服务都有各种各样的限制,此外,由于数据包会流经第三方,因此对数据安全也是一大隐患。
|
||||
|
||||
|
||||
支持客户端与服务端连接中断自动重连,多路传输,大大的提高请求处理速度,go语言编写,无第三方依赖,各个平台都已经编译在release中,普通个人场景下,内存使用量在10M以下。
|
||||
go语言编写,无第三方依赖,各个平台都已经编译在release中。
|
||||
|
||||
## 背景
|
||||
![image](https://github.com/cnlh/easyProxy/blob/master/image/web.png?raw=true)
|
||||
|
@ -30,6 +30,12 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
|
|||
* [web管理](#web管理模式)(多隧道时推荐)
|
||||
* [启动](#启动)
|
||||
* [配置文件说明](#服务端配置文件)
|
||||
* [详细使用说明](#详细说明)
|
||||
* [http|https域名解析](#域名解析)
|
||||
* [tcp隧道](#tcp隧道)
|
||||
* [udp隧道](#udp隧道)
|
||||
* [sock5代理](#sock5代理)
|
||||
* [http正向代理](#http正向代理)
|
||||
* 单隧道模式及介绍
|
||||
* [tcp隧道模式](#tcp隧道模式)
|
||||
* [udp隧道模式](#udp隧道模式)
|
||||
|
@ -40,7 +46,6 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
|
|||
* [数据压缩支持](#数据压缩支持)
|
||||
* [站点密码保护](#站点保护)
|
||||
* [加密传输](#加密传输)
|
||||
* [TCP多路复用](#多路复用)
|
||||
* [host修改](#host修改)
|
||||
* [自定义header](#自定义header)
|
||||
* [自定义404页面](#404页面配置)
|
||||
|
@ -48,10 +53,14 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
|
|||
* [带宽限制](#带宽限制)
|
||||
* [相关说明](#相关说明)
|
||||
* [流量统计](#流量统计)
|
||||
* [连接池](#连接池)
|
||||
* [热更新支持](#热更新支持)
|
||||
* [获取用户真实ip](#获取用户真实ip)
|
||||
* [客户端地址显示](#客户端地址显示)
|
||||
* [简单的性能测试](#简单的性能测试)
|
||||
* [qps](#qps)
|
||||
* [速度测试](#速度测试)
|
||||
* [内存和cpu](#内存和cpu)
|
||||
* [额外消耗连接数](#额外消耗连接数)
|
||||
* [webAPI](#webAPI)
|
||||
* [客户端](#客户端)
|
||||
* [域名代理](#域名代理)
|
||||
|
@ -78,16 +87,8 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
|
|||
![image](https://github.com/cnlh/easyProxy/blob/master/image/web2.png?raw=true)
|
||||
### 介绍
|
||||
|
||||
可在网页上配置和管理各个tcp、udp隧道、内网站点代理等等,功能极为强大,操作也非常方便。
|
||||
### 服务端配置文件
|
||||
- /conf/app.conf
|
||||
可在网页上配置和管理各个tcp、udp隧道、内网站点代理,http、https解析等,功能极为强大,操作也非常方便。
|
||||
|
||||
名称 | 含义
|
||||
---|---
|
||||
httpport | web管理端口
|
||||
password | web界面管理密码
|
||||
hostPort | 域名代理模式监听端口
|
||||
tcpport | 服务端客户端通信端口
|
||||
|
||||
**提示:使用web模式时,服务端执行文件必须在项目根目录,否则无法正确加载配置文件**
|
||||
|
||||
|
@ -102,15 +103,121 @@ tcpport | 服务端客户端通信端口
|
|||
|
||||
- 客户端
|
||||
```
|
||||
./proxy_server -server=ip:port -vkey=web界面中显示的
|
||||
./proxy_server -server=ip:port -vkey=web界面中显示的密钥
|
||||
```
|
||||
|
||||
进入web管理界面,有详细的命令
|
||||
|
||||
- 配置
|
||||
|
||||
进入web界面,公网ip:web界面端口(默认8080),密码默认为123
|
||||
|
||||
进入web管理界面,有详细的命令
|
||||
|
||||
### 服务端配置文件
|
||||
- /conf/app.conf
|
||||
|
||||
名称 | 含义
|
||||
---|---
|
||||
httpport | web管理端口
|
||||
password | web界面管理密码
|
||||
hostPort | 域名代理模式监听端口
|
||||
tcpport | 服务端客户端通信端口
|
||||
pemPath | ssl certFile绝对路径
|
||||
keyPath | ssl keyFile绝对路径
|
||||
httpsProxyPort | https代理监听端口
|
||||
httpProxyPort | http代理监听端口
|
||||
|
||||
### 详细说明
|
||||
|
||||
#### 域名解析
|
||||
|
||||
**适用范围:** 小程序开发、微信公众号开发、产品演示
|
||||
|
||||
**假设场景:**
|
||||
- 有一个域名proxy.com,有一台公网机器ip为1.1.1.1
|
||||
- 两个内网开发站点127.0.0.1:81,127.0.0.1:82
|
||||
- 想通过(http|https://)a.proxy.com访问127.0.0.1:81,通过(http|https://)b.proxy.com访问127.0.0.1:82
|
||||
|
||||
**使用步骤**
|
||||
- 将*.proxy.com解析到公网服务器1.1.1.1
|
||||
- 在客户端管理中创建一个客户端,记录下验证密钥
|
||||
- 点击该客户端的域名管理,添加两条规则规则:1、域名:a.proxy.com,内网目标:127.0.0.1:81,2、域名:b.proxy.com,内网目标:127.0.0.1:82
|
||||
-内网客户端运行
|
||||
|
||||
```
|
||||
./proxy_client server=1.1.1.1:8284 -vkey=客户端的密钥
|
||||
```
|
||||
现在访问(http|https://)a.proxy.com,b.proxy.com即可成功
|
||||
|
||||
**https:** 如需使用https请在配置文件中将https端口设置为443,和将对应的证书文件路径添加到配置文件中
|
||||
|
||||
#### tcp隧道
|
||||
|
||||
|
||||
**适用范围:** ssh、远程桌面等tcp连接场景
|
||||
|
||||
**假设场景:**
|
||||
想通过访问公网服务器1.1.1.1的8001端口,连接内网机器10.1.50.101的22端口,实现ssh连接
|
||||
|
||||
**使用步骤**
|
||||
- 在客户端管理中创建一个客户端,记录下验证密钥
|
||||
- -内网客户端运行
|
||||
```
|
||||
./proxy_client server=1.1.1.1:8284 -vkey=客户端的密钥
|
||||
```
|
||||
- 在该客户端隧道管理中添加一条tcp隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),选择压缩方式,保存。
|
||||
- 访问公网服务器ip(127.0.0.1),填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:ssh -p 8001 root@127.0.0.1
|
||||
|
||||
#### udp隧道
|
||||
|
||||
|
||||
|
||||
**适用范围:** 内网dns解析等udp连接场景
|
||||
|
||||
**假设场景:**
|
||||
内网有一台dns(10.1.50.102:53),在非内网环境下想使用该dns,公网服务器为1.1.1.1
|
||||
|
||||
**使用步骤**
|
||||
- 在客户端管理中创建一个客户端,记录下验证密钥
|
||||
- -内网客户端运行
|
||||
```
|
||||
./proxy_client server=1.1.1.1:8284 -vkey=客户端的密钥
|
||||
```
|
||||
- 在该客户端的隧道管理中添加一条udp隧道,填写监听的端口(53)、内网目标ip和目标端口(10.1.50.102:53),选择压缩方式,保存。
|
||||
- 修改本机dns为127.0.0.1,则相当于使用10.1.50.202作为dns服务器
|
||||
|
||||
#### sock5代理
|
||||
|
||||
|
||||
**适用范围:** 在外网环境下如同使用vpn一样访问内网设备或者资源
|
||||
|
||||
**假设场景:**
|
||||
想将公网服务器1.1.1.1的8003端口作为socks5代理,达到访问内网任意设备或者资源的效果
|
||||
|
||||
**使用步骤**
|
||||
- 在客户端管理中创建一个客户端,记录下验证密钥
|
||||
- -内网客户端运行
|
||||
```
|
||||
./proxy_client server=1.1.1.1:8284 -vkey=客户端的密钥
|
||||
```
|
||||
- 在该客户端隧道管理中添加一条socks5代理,填写监听的端口(8003),验证用户名和密码自行选择(建议先不填,部分客户端不支持,proxifer支持),选择压缩方式,保存。
|
||||
- 在外网环境的本机配置socks5代理,ip为公网服务器ip(127.0.0.1),端口为填写的监听端口(8003),即可畅享内网了
|
||||
|
||||
#### http正向代理
|
||||
|
||||
**适用范围:** 在外网环境下使用http代理访问内网站点
|
||||
|
||||
**假设场景:**
|
||||
想将公网服务器1.1.1.1的8004端口作为http代理,访问内网网站
|
||||
|
||||
**使用步骤**
|
||||
- 在客户端管理中创建一个客户端,记录下验证密钥
|
||||
- -内网客户端运行
|
||||
```
|
||||
./proxy_client server=1.1.1.1:8284 -vkey=客户端的密钥
|
||||
```
|
||||
- 在该客户端隧道管理中添加一条http代理,填写监听的端口(8004),选择压缩方式,保存。
|
||||
- 在外网环境的本机配置http代理,ip为公网服务器ip(127.0.0.1),端口为填写的监听端口(8004),即可访问了
|
||||
|
||||
|
||||
|
||||
## tcp隧道模式
|
||||
|
@ -316,16 +423,6 @@ authip | 免验证ip,适用于web api
|
|||
-crypt=true
|
||||
```
|
||||
|
||||
### 多路复用
|
||||
|
||||
客户端和服务器端之间的连接支持多路复用,不再需要为每一个用户请求创建一个连接,使连接建立的延迟降低,并且避免了大量文件描述符的占用。
|
||||
|
||||
|
||||
- 在server端加上参数 -mux=true(或在web管理中设置)
|
||||
|
||||
```
|
||||
-mux=true
|
||||
```
|
||||
|
||||
|
||||
### 站点保护
|
||||
|
@ -373,227 +470,27 @@ authip | 免验证ip,适用于web api
|
|||
### 流量统计
|
||||
可统计显示每个代理使用的流量,由于压缩和加密等原因,会和实际环境中的略有差异
|
||||
|
||||
### 连接池
|
||||
easyProxy会预先和后端服务建立起指定数量的连接,每次接收到用户请求后,会从连接池中取出一个连接和用户连接关联起来,避免了等待与后端服务建立连接时间。
|
||||
## 简单性能测试
|
||||
|
||||
### qps
|
||||
![image](https://github.com/cnlh/easyProxy/blob/master/image/qps.png?raw=true)
|
||||
### 速度测试
|
||||
**测试环境:** 1M带宽云服务器,理论125kb/s,带宽与代理无关,与服务器关系较大。
|
||||
![image](https://github.com/cnlh/easyProxy/blob/master/image/speed.png?raw=true)
|
||||
|
||||
|
||||
### 内存和cpu
|
||||
|
||||
**1000次性能测试后**
|
||||
![image](https://github.com/cnlh/easyProxy/blob/master/image/cpu1.png?raw=true)
|
||||
|
||||
**启动时**
|
||||
![image](https://github.com/cnlh/easyProxy/blob/master/image/cpu2.png?raw=true)
|
||||
|
||||
### 额外消耗连接数
|
||||
为了最大化的提升效率和并发,客户端与服务端之间仅两条tcp连接,减少建立连接的时间消耗和多余tcp连接对机器性能的影响。
|
||||
|
||||
## webAPI
|
||||
|
||||
### 客户端
|
||||
|
||||
#### 添加客户端
|
||||
```
|
||||
POST /client/add/
|
||||
```
|
||||
参数 | 含义
|
||||
---|---
|
||||
remark | 备注
|
||||
u | 用户名
|
||||
p | 密码
|
||||
compress | 压缩(snappy或空)
|
||||
crypt | 是否加密(1或者0)
|
||||
mux | 是否TCP复用(1或者0)
|
||||
rate_limit|带宽限制
|
||||
flow_limit|流量限制
|
||||
|
||||
#### 添加客户端
|
||||
```
|
||||
POST /client/edit/
|
||||
```
|
||||
参数 | 含义
|
||||
---|---
|
||||
id | id
|
||||
remark | 备注
|
||||
u | 用户名
|
||||
p | 密码
|
||||
compress | 压缩(snappy或空)
|
||||
crypt | 是否加密(1或者0)
|
||||
mux | 是否TCP复用(1或者0)
|
||||
rate_limit|带宽限制
|
||||
flow_limit|流量限制
|
||||
|
||||
#### 更改状态
|
||||
```
|
||||
POST /client/changestatus/
|
||||
```
|
||||
参数 | 含义
|
||||
---|---
|
||||
id | id
|
||||
status|1或0
|
||||
|
||||
#### 删除客户端
|
||||
```
|
||||
POST /client/del/
|
||||
```
|
||||
参数 | 含义
|
||||
---|---
|
||||
id | id
|
||||
#### 获取单个客户端
|
||||
```
|
||||
POST /client/getclient/
|
||||
```
|
||||
参数 | 含义
|
||||
---|---
|
||||
id | id
|
||||
#### 获取客户端列表
|
||||
```
|
||||
POST /client/list/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
start | 开始
|
||||
length | 长度
|
||||
|
||||
### 域名代理
|
||||
|
||||
#### 添加域名代理
|
||||
|
||||
```
|
||||
POST /index/addhost/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
host | 域名
|
||||
target | 内网目标地址
|
||||
header | header修改
|
||||
hostchange | host修改
|
||||
remark | 备注
|
||||
client_id | 客户端id
|
||||
#### 删除域名代理
|
||||
```
|
||||
POST /index/delhost/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
host | 域名
|
||||
|
||||
#### 修改域名代理
|
||||
```
|
||||
POST /index/edithost/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
nhost | 修改后的域名
|
||||
host | 修改之前的域名
|
||||
target | 内网目标地址
|
||||
header | header修改
|
||||
hostchange | host修改
|
||||
remark | 备注
|
||||
client_id | 客户端id
|
||||
|
||||
#### 获取域名代理列表
|
||||
```
|
||||
POST /index/hostlist/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
start | 开始
|
||||
length | 长度
|
||||
client_id | 客户端id(为空获取所有)
|
||||
#### 获取单个host
|
||||
```
|
||||
POST /index/gethost/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
host|域名
|
||||
|
||||
|
||||
### 其他代理
|
||||
|
||||
#### 获取隧道列表
|
||||
|
||||
```
|
||||
POST /index/gettunnel/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
client_id|客户端id(为空则忽略客户端限制)
|
||||
type|类型(udpServer、tunnelServer、socks5Server、httpProxyServer,为空则忽略类型限制)
|
||||
start|开始
|
||||
length|长度
|
||||
|
||||
#### 添加
|
||||
|
||||
```
|
||||
POST /index/add/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
port|监听端口
|
||||
type|类型(udpServer、tunnelServer、socks5Server、httpProxyServer)
|
||||
start|开始
|
||||
u|验证用户名
|
||||
p|验证密码
|
||||
compress|压缩(空或snappy)
|
||||
crypt|是否加密(1或0)
|
||||
mux|是否tcp复用(1或0)
|
||||
use_client|是否使用客户端配置(1或0)
|
||||
remark|备注
|
||||
client_id|客户端id
|
||||
|
||||
#### 修改
|
||||
|
||||
```
|
||||
POST /index/edit/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
id|id
|
||||
port|监听端口
|
||||
type|类型(udpServer、tunnelServer、socks5Server、httpProxyServer)
|
||||
start|开始
|
||||
u|验证用户名
|
||||
p|验证密码
|
||||
compress|压缩(空或snappy)
|
||||
crypt|是否加密(1或0)
|
||||
mux|是否tcp复用(1或0)
|
||||
use_client|是否使用客户端配置(1或0)
|
||||
remark|备注
|
||||
client_id|客户端id
|
||||
|
||||
#### 停止隧道
|
||||
|
||||
```
|
||||
POST /index/stop/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
id|id
|
||||
|
||||
#### 删除隧道
|
||||
|
||||
```
|
||||
POST /index/del/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
id|id
|
||||
#### 开始隧道
|
||||
|
||||
```
|
||||
POST /index/start/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
id|id
|
||||
#### 获取单条隧道详细
|
||||
|
||||
```
|
||||
POST /index/getonetunnel/
|
||||
```
|
||||
|
||||
参数 | 含义
|
||||
---|---
|
||||
id|id
|
||||
为方便第三方扩展,在web模式下可利用webAPI进行相关操作,详情见
|
||||
[webAPI文档](https://github.com/cnlh/easyProxy/wiki/webAPI%E6%96%87%E6%A1%A3)
|
286
bridge/bridge.go
286
bridge/bridge.go
|
@ -5,47 +5,33 @@ import (
|
|||
"github.com/cnlh/easyProxy/utils"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type list struct {
|
||||
connList chan *utils.Conn
|
||||
}
|
||||
|
||||
func (l *list) Add(c *utils.Conn) {
|
||||
l.connList <- c
|
||||
}
|
||||
|
||||
func (l *list) Pop() *utils.Conn {
|
||||
return <-l.connList
|
||||
}
|
||||
func (l *list) Len() int {
|
||||
return len(l.connList)
|
||||
}
|
||||
|
||||
func newList() *list {
|
||||
l := new(list)
|
||||
l.connList = make(chan *utils.Conn, 1000)
|
||||
return l
|
||||
type Client struct {
|
||||
tunnel *utils.Conn
|
||||
signal *utils.Conn
|
||||
linkMap map[int]*utils.Link
|
||||
linkStatusMap map[int]bool
|
||||
stop chan bool
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type Bridge struct {
|
||||
TunnelPort int //通信隧道端口
|
||||
listener *net.TCPListener //server端监听
|
||||
SignalList map[int]*list //通信
|
||||
TunnelList map[int]*list //隧道
|
||||
Client map[int]*Client
|
||||
RunList map[int]interface{} //运行中的任务
|
||||
lock sync.Mutex
|
||||
tunnelLock sync.Mutex
|
||||
clientLock sync.Mutex
|
||||
}
|
||||
|
||||
func NewTunnel(tunnelPort int, runList map[int]interface{}) *Bridge {
|
||||
t := new(Bridge)
|
||||
t.TunnelPort = tunnelPort
|
||||
t.SignalList = make(map[int]*list)
|
||||
t.TunnelList = make(map[int]*list)
|
||||
t.Client = make(map[int]*Client)
|
||||
t.RunList = runList
|
||||
return t
|
||||
}
|
||||
|
@ -103,11 +89,12 @@ func (s *Bridge) cliProcess(c *utils.Conn) {
|
|||
}
|
||||
|
||||
func (s *Bridge) closeClient(id int) {
|
||||
if len(s.SignalList) > 0 {
|
||||
s.SignalList[id].Pop().WriteClose()
|
||||
s.clientLock.Lock()
|
||||
defer s.clientLock.Unlock()
|
||||
if v, ok := s.Client[id]; ok {
|
||||
v.signal.WriteClose()
|
||||
delete(s.Client, id)
|
||||
}
|
||||
s.DelClientSignal(id)
|
||||
s.DelClientTunnel(id)
|
||||
}
|
||||
|
||||
//tcp连接类型区分
|
||||
|
@ -115,113 +102,123 @@ func (s *Bridge) typeDeal(typeVal string, c *utils.Conn, id int) {
|
|||
switch typeVal {
|
||||
case utils.WORK_MAIN:
|
||||
//客户端已经存在,下线
|
||||
if _, ok := s.SignalList[id]; ok {
|
||||
s.clientLock.Lock()
|
||||
if _, ok := s.Client[id]; ok {
|
||||
s.clientLock.Unlock()
|
||||
s.closeClient(id)
|
||||
} else {
|
||||
s.clientLock.Unlock()
|
||||
}
|
||||
log.Println("客户端连接成功", c.Conn.RemoteAddr())
|
||||
s.addList(s.SignalList, c, id)
|
||||
s.clientLock.Lock()
|
||||
|
||||
s.Client[id] = &Client{
|
||||
linkMap: make(map[int]*utils.Link),
|
||||
stop: make(chan bool),
|
||||
linkStatusMap: make(map[int]bool),
|
||||
}
|
||||
log.Printf("客户端%d连接成功,地址为:%s", id, c.Conn.RemoteAddr())
|
||||
s.Client[id].signal = c
|
||||
s.clientLock.Unlock()
|
||||
go s.GetStatus(id)
|
||||
case utils.WORK_CHAN:
|
||||
s.addList(s.TunnelList, c, id)
|
||||
s.clientLock.Lock()
|
||||
if v, ok := s.Client[id]; ok {
|
||||
s.clientLock.Unlock()
|
||||
v.tunnel = c
|
||||
} else {
|
||||
s.clientLock.Unlock()
|
||||
return
|
||||
}
|
||||
go s.clientCopy(id)
|
||||
}
|
||||
c.SetAlive()
|
||||
return
|
||||
}
|
||||
|
||||
//加到对应的list中
|
||||
func (s *Bridge) addList(m map[int]*list, c *utils.Conn, id int) {
|
||||
s.lock.Lock()
|
||||
if v, ok := m[id]; ok {
|
||||
v.Add(c)
|
||||
} else {
|
||||
l := newList()
|
||||
l.Add(c)
|
||||
m[id] = l
|
||||
//等待
|
||||
func (s *Bridge) waitStatus(clientId, id int) (bool) {
|
||||
ticker := time.NewTicker(time.Millisecond * 100)
|
||||
stop := time.After(time.Second * 10)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
s.clientLock.Lock()
|
||||
if v, ok := s.Client[clientId]; ok {
|
||||
s.clientLock.Unlock()
|
||||
v.Lock()
|
||||
if vv, ok := v.linkStatusMap[id]; ok {
|
||||
ticker.Stop()
|
||||
v.Unlock()
|
||||
return vv
|
||||
}
|
||||
s.lock.Unlock()
|
||||
v.Unlock()
|
||||
} else {
|
||||
s.clientLock.Unlock()
|
||||
}
|
||||
case <-stop:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Bridge) SendLinkInfo(clientId int, link *utils.Link) (tunnel *utils.Conn, err error) {
|
||||
s.clientLock.Lock()
|
||||
if v, ok := s.Client[clientId]; ok {
|
||||
s.clientLock.Unlock()
|
||||
v.signal.SendLinkInfo(link)
|
||||
if err != nil {
|
||||
log.Println("send error:", err, link.Id)
|
||||
s.DelClient(clientId)
|
||||
return
|
||||
}
|
||||
if v.tunnel == nil {
|
||||
err = errors.New("tunnel获取错误")
|
||||
return
|
||||
} else {
|
||||
tunnel = v.tunnel
|
||||
}
|
||||
v.Lock()
|
||||
v.linkMap[link.Id] = link
|
||||
v.Unlock()
|
||||
if !s.waitStatus(clientId, link.Id) {
|
||||
err = errors.New("连接失败")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
s.clientLock.Unlock()
|
||||
err = errors.New("客户端未连接")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//得到一个tcp隧道
|
||||
func (s *Bridge) GetTunnel(id int, en, de int, crypt, mux bool) (c *utils.Conn, err error) {
|
||||
retry:
|
||||
if c, err = s.waitAndPop(s.TunnelList, id); err != nil {
|
||||
return
|
||||
func (s *Bridge) GetTunnel(id int, en, de int, crypt, mux bool) (conn *utils.Conn, err error) {
|
||||
s.clientLock.Lock()
|
||||
defer s.clientLock.Unlock()
|
||||
if v, ok := s.Client[id]; !ok {
|
||||
err = errors.New("客户端未连接")
|
||||
} else {
|
||||
conn = v.tunnel
|
||||
}
|
||||
if _, err = c.WriteTest(); err != nil {
|
||||
c.Close()
|
||||
goto retry
|
||||
}
|
||||
c.WriteConnInfo(en, de, crypt, mux)
|
||||
return
|
||||
}
|
||||
|
||||
//得到一个通信通道
|
||||
func (s *Bridge) GetSignal(id int) (err error, conn *utils.Conn) {
|
||||
if v, ok := s.SignalList[id]; !ok || v.Len() == 0 {
|
||||
func (s *Bridge) GetSignal(id int) (conn *utils.Conn, err error) {
|
||||
s.clientLock.Lock()
|
||||
defer s.clientLock.Unlock()
|
||||
if v, ok := s.Client[id]; !ok {
|
||||
err = errors.New("客户端未连接")
|
||||
} else {
|
||||
conn = v.signal
|
||||
}
|
||||
return
|
||||
}
|
||||
conn = s.SignalList[id].Pop()
|
||||
return
|
||||
}
|
||||
|
||||
//重回slice 复用
|
||||
func (s *Bridge) ReturnSignal(conn *utils.Conn, id int) {
|
||||
if v, ok := s.SignalList[id]; ok {
|
||||
v.Add(conn)
|
||||
}
|
||||
}
|
||||
|
||||
//重回slice 复用
|
||||
func (s *Bridge) ReturnTunnel(conn *utils.Conn, id int) {
|
||||
if v, ok := s.TunnelList[id]; ok {
|
||||
utils.FlushConn(conn.Conn)
|
||||
v.Add(conn)
|
||||
}
|
||||
}
|
||||
|
||||
//删除通信通道
|
||||
func (s *Bridge) DelClientSignal(id int) {
|
||||
s.delClient(id, s.SignalList)
|
||||
}
|
||||
|
||||
//删除隧道
|
||||
func (s *Bridge) DelClientTunnel(id int) {
|
||||
s.delClient(id, s.TunnelList)
|
||||
}
|
||||
|
||||
func (s *Bridge) delClient(id int, l map[int]*list) {
|
||||
if t := l[id]; t != nil {
|
||||
for {
|
||||
if t.Len() <= 0 {
|
||||
break
|
||||
}
|
||||
t.Pop().Close()
|
||||
}
|
||||
delete(l, id)
|
||||
}
|
||||
}
|
||||
|
||||
//等待
|
||||
func (s *Bridge) waitAndPop(m map[int]*list, id int) (c *utils.Conn, err error) {
|
||||
ticker := time.NewTicker(time.Millisecond * 100)
|
||||
stop := time.After(time.Second * 3)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
s.lock.Lock()
|
||||
if v, ok := m[id]; ok && v.Len() > 0 {
|
||||
c = v.Pop()
|
||||
ticker.Stop()
|
||||
s.lock.Unlock()
|
||||
return
|
||||
}
|
||||
s.lock.Unlock()
|
||||
case <-stop:
|
||||
err = errors.New("client id: " + strconv.Itoa(id) + ",err: get client conn timeout")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
func (s *Bridge) DelClient(id int) {
|
||||
s.closeClient(id)
|
||||
}
|
||||
|
||||
func (s *Bridge) verify(id int) bool {
|
||||
|
@ -232,3 +229,66 @@ func (s *Bridge) verify(id int) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
func (s *Bridge) GetStatus(clientId int) {
|
||||
s.clientLock.Lock()
|
||||
client := s.Client[clientId]
|
||||
s.clientLock.Unlock()
|
||||
|
||||
if client == nil {
|
||||
return
|
||||
}
|
||||
for {
|
||||
if id, status, err := client.signal.GetConnStatus(); err != nil {
|
||||
s.closeClient(clientId)
|
||||
return
|
||||
} else {
|
||||
client.Lock()
|
||||
client.linkStatusMap[id] = status
|
||||
client.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Bridge) clientCopy(clientId int) {
|
||||
|
||||
s.clientLock.Lock()
|
||||
client := s.Client[clientId]
|
||||
s.clientLock.Unlock()
|
||||
|
||||
for {
|
||||
if id, err := client.tunnel.GetLen(); err != nil {
|
||||
s.closeClient(clientId)
|
||||
log.Println("读取msg id 错误", err, id)
|
||||
break
|
||||
} else {
|
||||
client.Lock()
|
||||
if link, ok := client.linkMap[id]; ok {
|
||||
client.Unlock()
|
||||
if content, err := client.tunnel.GetMsgContent(link); err != nil {
|
||||
utils.PutBufPoolCopy(content)
|
||||
s.closeClient(clientId)
|
||||
log.Println("read msg content error", err, "close client")
|
||||
break
|
||||
} else {
|
||||
if len(content) == len(utils.IO_EOF) && string(content) == utils.IO_EOF {
|
||||
if link.Conn != nil {
|
||||
link.Conn.Close()
|
||||
}
|
||||
} else {
|
||||
if link.UdpListener != nil && link.UdpRemoteAddr != nil {
|
||||
link.UdpListener.WriteToUDP(content, link.UdpRemoteAddr)
|
||||
} else {
|
||||
link.Conn.Write(content)
|
||||
}
|
||||
link.Flow.Add(0, len(content))
|
||||
}
|
||||
utils.PutBufPoolCopy(content)
|
||||
}
|
||||
} else {
|
||||
client.Unlock()
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
189
client/client.go
189
client/client.go
|
@ -5,87 +5,129 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TRPClient struct {
|
||||
svrAddr string
|
||||
tcpNum int
|
||||
connPoolSize int
|
||||
tunnelNum int64
|
||||
tunnel chan bool
|
||||
serverStatus bool
|
||||
linkMap map[int]*utils.Link
|
||||
stop chan bool
|
||||
tunnel *utils.Conn
|
||||
sync.Mutex
|
||||
vKey string
|
||||
}
|
||||
|
||||
//new client
|
||||
func NewRPClient(svraddr string, tcpNum int, vKey string) *TRPClient {
|
||||
c := new(TRPClient)
|
||||
c.svrAddr = svraddr
|
||||
c.tcpNum = tcpNum
|
||||
c.vKey = vKey
|
||||
c.tunnel = make(chan bool)
|
||||
c.connPoolSize = 5
|
||||
return c
|
||||
func NewRPClient(svraddr string, vKey string) *TRPClient {
|
||||
return &TRPClient{
|
||||
svrAddr: svraddr,
|
||||
linkMap: make(map[int]*utils.Link),
|
||||
stop: make(chan bool),
|
||||
tunnel: nil,
|
||||
Mutex: sync.Mutex{},
|
||||
vKey: vKey,
|
||||
}
|
||||
}
|
||||
|
||||
//start
|
||||
func (s *TRPClient) Start() error {
|
||||
for i := 0; i < s.tcpNum; i++ {
|
||||
go s.NewConn()
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
go s.dealChan()
|
||||
}
|
||||
go s.session()
|
||||
s.NewConn()
|
||||
return nil
|
||||
}
|
||||
|
||||
//新建
|
||||
func (s *TRPClient) NewConn() error {
|
||||
s.Lock()
|
||||
s.serverStatus = false
|
||||
func (s *TRPClient) NewConn() {
|
||||
retry:
|
||||
conn, err := net.Dial("tcp", s.svrAddr)
|
||||
if err != nil {
|
||||
log.Println("连接服务端失败,五秒后将重连")
|
||||
time.Sleep(time.Second * 5)
|
||||
s.Unlock()
|
||||
go s.NewConn()
|
||||
return err
|
||||
goto retry
|
||||
return
|
||||
}
|
||||
s.Unlock()
|
||||
return s.processor(utils.NewConn(conn))
|
||||
s.processor(utils.NewConn(conn))
|
||||
}
|
||||
|
||||
//处理
|
||||
func (s *TRPClient) processor(c *utils.Conn) error {
|
||||
s.serverStatus = true
|
||||
func (s *TRPClient) processor(c *utils.Conn) {
|
||||
c.SetAlive()
|
||||
if _, err := c.Write([]byte(utils.Getverifyval(s.vKey))); err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
c.WriteMain()
|
||||
|
||||
go s.dealChan()
|
||||
|
||||
for {
|
||||
flags, err := c.ReadFlag()
|
||||
if err != nil {
|
||||
log.Println("服务端断开,五秒后将重连", err)
|
||||
go s.NewConn()
|
||||
log.Println("服务端断开,正在重新连接")
|
||||
break
|
||||
}
|
||||
switch flags {
|
||||
case utils.VERIFY_EER:
|
||||
log.Fatalln("vkey:", s.vKey, "不正确,服务端拒绝连接,请检查")
|
||||
case utils.WORK_CHAN: //隧道模式,每次开启10个,加快连接速度
|
||||
log.Fatalf("vKey:%s不正确,服务端拒绝连接,请检查", s.vKey)
|
||||
case utils.NEW_CONN:
|
||||
if link, err := c.GetLinkInfo(); err != nil {
|
||||
break
|
||||
} else {
|
||||
log.Println(link)
|
||||
s.Lock()
|
||||
s.linkMap[link.Id] = link
|
||||
s.Unlock()
|
||||
go s.linkProcess(link, c)
|
||||
}
|
||||
case utils.RES_CLOSE:
|
||||
log.Fatal("该vkey被另一客户连接")
|
||||
case utils.RES_MSG:
|
||||
log.Println("服务端返回错误。")
|
||||
log.Println("服务端返回错误,重新连接")
|
||||
break
|
||||
default:
|
||||
log.Println("无法解析该错误。", flags)
|
||||
log.Println("无法解析该错误,重新连接")
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
s.stop <- true
|
||||
s.linkMap = make(map[int]*utils.Link)
|
||||
go s.NewConn()
|
||||
}
|
||||
func (s *TRPClient) linkProcess(link *utils.Link, c *utils.Conn) {
|
||||
//与目标建立连接
|
||||
server, err := net.DialTimeout(link.ConnType, link.Host, time.Second*3)
|
||||
|
||||
if err != nil {
|
||||
c.WriteFail(link.Id)
|
||||
log.Println("connect to ", link.Host, "error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.WriteSuccess(link.Id)
|
||||
|
||||
link.Conn = utils.NewConn(server)
|
||||
|
||||
for {
|
||||
buf := utils.BufPoolCopy.Get().([]byte)
|
||||
if n, err := server.Read(buf); err != nil {
|
||||
utils.PutBufPoolCopy(buf)
|
||||
s.tunnel.SendMsg([]byte(utils.IO_EOF), link)
|
||||
break
|
||||
} else {
|
||||
if _, err := s.tunnel.SendMsg(buf[:n], link); err != nil {
|
||||
utils.PutBufPoolCopy(buf)
|
||||
c.Close()
|
||||
break
|
||||
}
|
||||
utils.PutBufPoolCopy(buf)
|
||||
if link.ConnType == utils.CONN_UDP {
|
||||
c.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
delete(s.linkMap, link.Id)
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
//隧道模式处理
|
||||
|
@ -103,50 +145,37 @@ func (s *TRPClient) dealChan() {
|
|||
return
|
||||
}
|
||||
//默认长连接保持
|
||||
c := utils.NewConn(conn)
|
||||
c.SetAlive()
|
||||
s.tunnel = utils.NewConn(conn)
|
||||
s.tunnel.SetAlive()
|
||||
//写标志
|
||||
c.WriteChan()
|
||||
re:
|
||||
atomic.AddInt64(&s.tunnelNum, 1)
|
||||
//获取连接的host type(tcp or udp)
|
||||
typeStr, host, en, de, crypt, mux, err := c.GetHostFromConn()
|
||||
s.tunnel <- true
|
||||
atomic.AddInt64(&s.tunnelNum, -1)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
s.ConnectAndCopy(c, typeStr, host, en, de, crypt, mux)
|
||||
if mux {
|
||||
utils.FlushConn(conn)
|
||||
goto re
|
||||
} else {
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
s.tunnel.WriteChan()
|
||||
|
||||
func (s *TRPClient) session() {
|
||||
t := time.NewTicker(time.Millisecond * 1000)
|
||||
go func() {
|
||||
for {
|
||||
if id, err := s.tunnel.GetLen(); err != nil {
|
||||
log.Println("get msg id error")
|
||||
break
|
||||
} else {
|
||||
s.Lock()
|
||||
if v, ok := s.linkMap[id]; ok {
|
||||
s.Unlock()
|
||||
if content, err := s.tunnel.GetMsgContent(v); err != nil {
|
||||
log.Println("get msg content error:", err, id)
|
||||
break
|
||||
} else {
|
||||
if len(content) == len(utils.IO_EOF) && string(content) == utils.IO_EOF {
|
||||
v.Conn.Close()
|
||||
} else if v.Conn != nil {
|
||||
v.Conn.Write(content)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-s.tunnel:
|
||||
case <-t.C:
|
||||
}
|
||||
if s.serverStatus && s.tunnelNum < 5 {
|
||||
go s.dealChan()
|
||||
case <-s.stop:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TRPClient) ConnectAndCopy(c *utils.Conn, typeStr, host string, en, de int, crypt, mux bool) {
|
||||
//与目标建立连接,超时时间为3
|
||||
server, err := net.DialTimeout(typeStr, host, time.Second*3)
|
||||
if err != nil {
|
||||
log.Println("connect to ", host, "error:", err, mux)
|
||||
c.WriteFail()
|
||||
return
|
||||
}
|
||||
c.WriteSuccess()
|
||||
utils.ReplayWaitGroup(c.Conn, server, en, de, crypt, mux, nil)
|
||||
}
|
||||
|
|
|
@ -13,11 +13,12 @@ var (
|
|||
)
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.Lshortfile)
|
||||
flag.Parse()
|
||||
stop := make(chan int)
|
||||
for _, v := range strings.Split(*verifyKey, ",") {
|
||||
log.Println("客户端启动,连接:", *serverAddr, " 验证令牌:", v)
|
||||
go client.NewRPClient(*serverAddr, 1, v).Start()
|
||||
go client.NewRPClient(*serverAddr, v).Start()
|
||||
}
|
||||
<-stop
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@ var (
|
|||
p = flag.String("p", "", "验证密码(socks5和web)")
|
||||
compress = flag.String("compress", "", "数据压缩方式(snappy)")
|
||||
crypt = flag.String("crypt", "false", "是否加密(true|false)")
|
||||
mux = flag.String("mux", "false", "是否TCP多路复用(true|false)")
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.Lshortfile)
|
||||
flag.Parse()
|
||||
task := &utils.Tunnel{
|
||||
TcpPort: *httpPort,
|
||||
|
@ -33,7 +33,6 @@ func main() {
|
|||
P: *p,
|
||||
Compress: *compress,
|
||||
Crypt: utils.GetBoolByStr(*crypt),
|
||||
Mux: utils.GetBoolByStr(*mux),
|
||||
},
|
||||
Flow: &utils.Flow{},
|
||||
UseClientCnf: false,
|
||||
|
|
|
@ -9,11 +9,20 @@ runmode = dev
|
|||
#web管理密码
|
||||
password=123
|
||||
|
||||
#http监听端口
|
||||
hostPort=80
|
||||
|
||||
##客户端与服务端通信端口
|
||||
tcpport=8284
|
||||
|
||||
#web api免验证IP地址
|
||||
authip=127.0.0.1
|
||||
|
||||
##http代理端口
|
||||
httpProxyPort=80
|
||||
|
||||
##https代理端口
|
||||
httpsProxyPort=443
|
||||
|
||||
##certFile绝对路径
|
||||
pemPath=/etc/nginx/certificate.crt
|
||||
|
||||
##keyFile绝对路径
|
||||
keyPath=/etc/nginx/private.key
|
||||
|
|
|
@ -1 +1 @@
|
|||
1,wuz1nozs9dhtxic6,,true,,,0,0,,0,1
|
||||
1,wajajb6bl086s2aq,111,true,1,2,1,,20000,1000
|
||||
|
|
|
|
@ -1 +1 @@
|
|||
a.o.com,127.0.0.1:8082,1,Connection:close,,
|
||||
a.o.com,127.0.0.1:8082,1,,www.baidu.com,测试
|
||||
|
|
|
|
@ -1 +1,4 @@
|
|||
9001,tunnelServer,127.0.0.1:8082,,,,1,0,0,0,0,1,1,true,
|
||||
9002,tunnelServer,127.0.0.1:8082,2,2,,1,1,0,0,1,1,false,123
|
||||
53,udpServer,114.114.114.114:53,,,,1,0,0,0,2,1,true,udp测试
|
||||
9003,httpProxyServer,,,,,1,0,0,0,3,1,false,
|
||||
8024,socks5Server,,,,,1,0,0,0,4,1,true,
|
||||
|
|
|
|
@ -1,29 +1,24 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/cnlh/easyProxy/bridge"
|
||||
"github.com/cnlh/easyProxy/utils"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//server base struct
|
||||
type server struct {
|
||||
id int
|
||||
bridge *bridge.Bridge
|
||||
task *utils.Tunnel
|
||||
config *utils.Config
|
||||
errorContent []byte
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (s *server) GetTunnelAndWriteHost(connType string, clientId int, cnf *utils.Config, addr string) (link *utils.Conn, err error) {
|
||||
if link, err = s.bridge.GetTunnel(clientId, cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, cnf.Mux); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = link.WriteHost(connType, addr); err != nil {
|
||||
link.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *server) FlowAdd(in, out int64) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
@ -56,7 +51,6 @@ func (s *server) ResetConfig() bool {
|
|||
s.config.U = client.Cnf.U
|
||||
s.config.P = client.Cnf.P
|
||||
s.config.Compress = client.Cnf.Compress
|
||||
s.config.Mux = client.Cnf.Mux
|
||||
s.config.Crypt = client.Cnf.Crypt
|
||||
}
|
||||
} else {
|
||||
|
@ -64,7 +58,6 @@ func (s *server) ResetConfig() bool {
|
|||
s.config.U = task.Config.U
|
||||
s.config.P = task.Config.P
|
||||
s.config.Compress = task.Config.Compress
|
||||
s.config.Mux = task.Config.Mux
|
||||
s.config.Crypt = task.Config.Crypt
|
||||
}
|
||||
}
|
||||
|
@ -72,3 +65,43 @@ func (s *server) ResetConfig() bool {
|
|||
s.config.CompressDecode, s.config.CompressEncode = utils.GetCompressType(s.config.Compress)
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *server) linkCopy(link *utils.Link, c *utils.Conn, rb []byte, tunnel *utils.Conn, flow *utils.Flow) {
|
||||
if rb != nil {
|
||||
if _, err := tunnel.SendMsg(rb, link); err != nil {
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
flow.Add(len(rb), 0)
|
||||
}
|
||||
for {
|
||||
buf := utils.BufPoolCopy.Get().([]byte)
|
||||
if n, err := c.Read(buf); err != nil {
|
||||
tunnel.SendMsg([]byte(utils.IO_EOF), link)
|
||||
break
|
||||
} else {
|
||||
if _, err := tunnel.SendMsg(buf[:n], link); err != nil {
|
||||
utils.PutBufPoolCopy(buf)
|
||||
c.Close()
|
||||
break
|
||||
}
|
||||
utils.PutBufPoolCopy(buf)
|
||||
flow.Add(n, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) writeConnFail(c net.Conn) {
|
||||
c.Write([]byte(utils.ConnectionFailBytes))
|
||||
c.Write(s.errorContent)
|
||||
}
|
||||
|
||||
//权限认证
|
||||
func (s *server) auth(r *http.Request, c *utils.Conn, u, p string) error {
|
||||
if u != "" && p != "" && !utils.CheckAuth(r, u, p) {
|
||||
c.Write([]byte(utils.UnauthorizedBytes))
|
||||
c.Close()
|
||||
return errors.New("401 Unauthorized")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/easyProxy/bridge"
|
||||
"github.com/cnlh/easyProxy/utils"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type httpServer struct {
|
||||
server
|
||||
httpPort int //http端口
|
||||
httpsPort int //https监听端口
|
||||
pemPath string
|
||||
keyPath string
|
||||
stop chan bool
|
||||
}
|
||||
|
||||
func NewHttp(bridge *bridge.Bridge, c *utils.Tunnel) *httpServer {
|
||||
httpPort, _ := beego.AppConfig.Int("httpProxyPort")
|
||||
httpsPort, _ := beego.AppConfig.Int("httpsProxyPort")
|
||||
pemPath := beego.AppConfig.String("pemPath")
|
||||
keyPath := beego.AppConfig.String("keyPath")
|
||||
return &httpServer{
|
||||
server: server{
|
||||
task: c,
|
||||
bridge: bridge,
|
||||
Mutex: sync.Mutex{},
|
||||
},
|
||||
httpPort: httpPort,
|
||||
httpsPort: httpsPort,
|
||||
pemPath: pemPath,
|
||||
keyPath: keyPath,
|
||||
stop: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *httpServer) Start() error {
|
||||
var err error
|
||||
var http, https *http.Server
|
||||
if s.errorContent, err = utils.ReadAllFromFile(beego.AppPath + "/web/static/page/error.html"); err != nil {
|
||||
s.errorContent = []byte("easyProxy 404")
|
||||
}
|
||||
|
||||
if s.httpPort > 0 {
|
||||
http = s.NewServer(s.httpPort)
|
||||
go func() {
|
||||
log.Println("启动http监听,端口为", s.httpPort)
|
||||
err := http.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
if s.httpsPort > 0 {
|
||||
https = s.NewServer(s.httpsPort)
|
||||
go func() {
|
||||
log.Println("启动https监听,端口为", s.httpsPort)
|
||||
err := https.ListenAndServeTLS(s.pemPath, s.keyPath)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
select {
|
||||
case <-s.stop:
|
||||
if http != nil {
|
||||
http.Close()
|
||||
}
|
||||
if https != nil {
|
||||
https.Close()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *httpServer) Close() {
|
||||
s.stop <- true
|
||||
}
|
||||
|
||||
func (s *httpServer) handleTunneling(w http.ResponseWriter, r *http.Request) {
|
||||
hijacker, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
http.Error(w, "Hijacking not supported", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
conn, _, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusServiceUnavailable)
|
||||
}
|
||||
s.process(utils.NewConn(conn), r)
|
||||
}
|
||||
|
||||
func (s *httpServer) process(c *utils.Conn, r *http.Request) {
|
||||
//多客户端域名代理
|
||||
var (
|
||||
isConn = true
|
||||
link *utils.Link
|
||||
host *utils.Host
|
||||
tunnel *utils.Conn
|
||||
err error
|
||||
)
|
||||
for {
|
||||
//首次获取conn
|
||||
if isConn {
|
||||
if host, err = GetInfoByHost(r.Host); err != nil {
|
||||
log.Printf("the host %s is not found !", r.Host)
|
||||
break
|
||||
}
|
||||
//流量限制
|
||||
if host.Client.Flow.FlowLimit > 0 && (host.Client.Flow.FlowLimit<<20) < (host.Client.Flow.ExportFlow+host.Client.Flow.InletFlow) {
|
||||
break
|
||||
}
|
||||
host.Client.Cnf.CompressDecode, host.Client.Cnf.CompressEncode = utils.GetCompressType(host.Client.Cnf.Compress)
|
||||
//权限控制
|
||||
if err = s.auth(r, c, host.Client.Cnf.U, host.Client.Cnf.P); err != nil {
|
||||
break
|
||||
}
|
||||
link = utils.NewLink(host.Client.GetId(), utils.CONN_TCP, host.Target, host.Client.Cnf.CompressEncode, host.Client.Cnf.CompressDecode, host.Client.Cnf.Crypt, c, host.Flow, nil, host.Client.Rate, nil)
|
||||
if tunnel, err = s.bridge.SendLinkInfo(host.Client.Id, link); err != nil {
|
||||
break
|
||||
}
|
||||
isConn = false
|
||||
} else {
|
||||
r, err = http.ReadRequest(bufio.NewReader(c))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
break
|
||||
}
|
||||
}
|
||||
//根据设定,修改header和host
|
||||
utils.ChangeHostAndHeader(r, host.HostChange, host.HeaderChange, c.Conn.RemoteAddr().String())
|
||||
b, err := httputil.DumpRequest(r, true)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
host.Flow.Add(len(b), 0)
|
||||
if _, err := tunnel.SendMsg(b, link); err != nil {
|
||||
c.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isConn {
|
||||
s.writeConnFail(c.Conn)
|
||||
} else {
|
||||
tunnel.SendMsg([]byte(utils.IO_EOF), link)
|
||||
}
|
||||
|
||||
c.Close()
|
||||
|
||||
}
|
||||
|
||||
func (s *httpServer) NewServer(port int) *http.Server {
|
||||
return &http.Server{
|
||||
Addr: ":" + strconv.Itoa(port),
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
s.handleTunneling(w, r)
|
||||
}),
|
||||
// Disable HTTP/2.
|
||||
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"github.com/cnlh/easyProxy/utils"
|
||||
"github.com/pkg/errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type process func(c *utils.Conn, s *TunnelModeServer) error
|
||||
|
||||
//tcp隧道模式
|
||||
func ProcessTunnel(c *utils.Conn, s *TunnelModeServer) error {
|
||||
if !s.ResetConfig() {
|
||||
c.Close()
|
||||
return errors.New("流量超出")
|
||||
}
|
||||
return s.dealClient(c, s.config, s.task.Target, "", nil)
|
||||
}
|
||||
|
||||
//http代理模式
|
||||
func ProcessHttp(c *utils.Conn, s *TunnelModeServer) error {
|
||||
if !s.ResetConfig() {
|
||||
c.Close()
|
||||
return errors.New("流量超出")
|
||||
}
|
||||
method, addr, rb, err, r := c.GetHost()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
c.Close()
|
||||
return err
|
||||
}
|
||||
if err := s.auth(r, c, s.config.U, s.config.P); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.dealClient(c, s.config, addr, method, rb)
|
||||
}
|
||||
|
||||
//多客户端域名代理
|
||||
func ProcessHost(c *utils.Conn, s *TunnelModeServer) error {
|
||||
var (
|
||||
isConn = true
|
||||
link *utils.Conn
|
||||
host *utils.Host
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
for {
|
||||
r, err := http.ReadRequest(bufio.NewReader(c))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
//首次获取conn
|
||||
if isConn {
|
||||
if host, err = GetInfoByHost(r.Host); err != nil {
|
||||
log.Printf("the host %s is not found !", r.Host)
|
||||
break
|
||||
}
|
||||
//流量限制
|
||||
if host.Client.Flow.FlowLimit > 0 && (host.Client.Flow.FlowLimit<<20) < (host.Client.Flow.ExportFlow+host.Client.Flow.InletFlow) {
|
||||
break
|
||||
}
|
||||
host.Client.Cnf.CompressDecode, host.Client.Cnf.CompressEncode = utils.GetCompressType(host.Client.Cnf.Compress)
|
||||
//权限控制
|
||||
if err = s.auth(r, c, host.Client.Cnf.U, host.Client.Cnf.P); err != nil {
|
||||
break
|
||||
}
|
||||
if link, err = s.GetTunnelAndWriteHost(utils.CONN_TCP, host.Client.Id, host.Client.Cnf, host.Target); err != nil {
|
||||
log.Println("get bridge tunnel error: ", err)
|
||||
break
|
||||
}
|
||||
if flag, err := link.ReadFlag(); err != nil || flag == utils.CONN_ERROR {
|
||||
log.Printf("the host %s connection to %s error", r.Host, host.Target)
|
||||
break
|
||||
} else {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
out, _ := utils.Relay(c.Conn, link.Conn, host.Client.Cnf.CompressDecode, host.Client.Cnf.Crypt, host.Client.Cnf.Mux, host.Client.Rate)
|
||||
wg.Done()
|
||||
s.FlowAddHost(host, 0, out)
|
||||
}()
|
||||
}
|
||||
isConn = false
|
||||
}
|
||||
//根据设定,修改header和host
|
||||
utils.ChangeHostAndHeader(r, host.HostChange, host.HeaderChange, c.Conn.RemoteAddr().String())
|
||||
b, err := httputil.DumpRequest(r, true)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
s.FlowAddHost(host, int64(len(b)), 0)
|
||||
if _, err := link.WriteTo(b, host.Client.Cnf.CompressEncode, host.Client.Cnf.Crypt, host.Client.Rate); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
if host != nil && host.Client.Cnf != nil && host.Client.Cnf.Mux && link != nil {
|
||||
link.WriteTo([]byte(utils.IO_EOF), host.Client.Cnf.CompressEncode, host.Client.Cnf.Crypt, host.Client.Rate)
|
||||
s.bridge.ReturnTunnel(link, host.Client.Id)
|
||||
} else if link != nil {
|
||||
link.Close()
|
||||
}
|
||||
|
||||
if isConn {
|
||||
s.writeConnFail(c.Conn)
|
||||
}
|
||||
c.Close()
|
||||
return nil
|
||||
}
|
|
@ -2,23 +2,13 @@ package server
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/easyProxy/bridge"
|
||||
"github.com/cnlh/easyProxy/utils"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type RunServer struct {
|
||||
flag int //标志
|
||||
ExportFlow int64 //出口流量
|
||||
InletFlow int64 //入口流量
|
||||
service interface{}
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
var (
|
||||
Bridge *bridge.Bridge
|
||||
RunList map[int]interface{} //运行中的任务
|
||||
|
@ -69,9 +59,8 @@ func NewMode(Bridge *bridge.Bridge, c *utils.Tunnel) interface{} {
|
|||
return NewUdpModeServer(Bridge, c)
|
||||
case "webServer":
|
||||
InitFromCsv()
|
||||
p, _ := beego.AppConfig.Int("hostPort")
|
||||
t := &utils.Tunnel{
|
||||
TcpPort: p,
|
||||
TcpPort: 0,
|
||||
Mode: "httpHostServer",
|
||||
Target: "",
|
||||
Config: &utils.Config{},
|
||||
|
@ -82,7 +71,7 @@ func NewMode(Bridge *bridge.Bridge, c *utils.Tunnel) interface{} {
|
|||
case "hostServer":
|
||||
return NewHostServer(c)
|
||||
case "httpHostServer":
|
||||
return NewTunnelModeServer(ProcessHost, Bridge, c)
|
||||
return NewHttp(Bridge, c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -161,7 +150,7 @@ func GetTunnel(start, length int, typeVal string, clientId int) ([]*utils.Tunnel
|
|||
continue
|
||||
}
|
||||
cnt++
|
||||
if _, ok := Bridge.SignalList[v.Client.Id]; ok {
|
||||
if _, ok := Bridge.Client[v.Client.Id]; ok {
|
||||
v.Client.IsConnect = true
|
||||
} else {
|
||||
v.Client.IsConnect = false
|
||||
|
@ -189,7 +178,7 @@ func GetClientList(start, length int) (list []*utils.Client, cnt int) {
|
|||
|
||||
func dealClientData(list []*utils.Client) {
|
||||
for _, v := range list {
|
||||
if _, ok := Bridge.SignalList[v.Id]; ok {
|
||||
if _, ok := Bridge.Client[v.Id]; ok {
|
||||
v.IsConnect = true
|
||||
} else {
|
||||
v.IsConnect = false
|
||||
|
@ -228,8 +217,7 @@ func DelTunnelAndHostByClientId(clientId int) {
|
|||
|
||||
//关闭客户端连接
|
||||
func DelClientConnect(clientId int) {
|
||||
Bridge.DelClientTunnel(clientId)
|
||||
Bridge.DelClientSignal(clientId)
|
||||
Bridge.DelClient(clientId)
|
||||
}
|
||||
|
||||
func GetDashboardData() map[string]int {
|
||||
|
|
|
@ -106,7 +106,7 @@ func (s *Sock5ModeServer) sendReply(c net.Conn, rep uint8) {
|
|||
}
|
||||
|
||||
//do conn
|
||||
func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *utils.Conn, err error) {
|
||||
func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) {
|
||||
addrType := make([]byte, 1)
|
||||
c.Read(addrType)
|
||||
var host string
|
||||
|
@ -127,8 +127,7 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *utils
|
|||
host = string(domain)
|
||||
default:
|
||||
s.sendReply(c, addrTypeNotSupported)
|
||||
err = errors.New("Address type not supported")
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
|
||||
var port uint16
|
||||
|
@ -141,34 +140,22 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *utils
|
|||
} else {
|
||||
ltype = utils.CONN_TCP
|
||||
}
|
||||
if proxyConn, err = s.GetTunnelAndWriteHost(ltype, s.task.Client.Id, s.config, addr); err != nil {
|
||||
log.Println("get bridge tunnel error: ", err)
|
||||
link := utils.NewLink(s.task.Client.GetId(), ltype, addr, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, utils.NewConn(c), s.task.Flow, nil, s.task.Client.Rate, nil)
|
||||
|
||||
if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link); err != nil {
|
||||
log.Println("error", err, link)
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
s.sendReply(c, succeeded)
|
||||
var flag string
|
||||
if flag, err = proxyConn.ReadFlag(); err == nil {
|
||||
if flag != utils.CONN_SUCCESS {
|
||||
err = errors.New("conn failed")
|
||||
}
|
||||
s.linkCopy(link, utils.NewConn(c), nil, tunnel, s.task.Flow)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//conn
|
||||
func (s *Sock5ModeServer) handleConnect(c net.Conn) {
|
||||
proxyConn, err := s.doConnect(c, connectMethod)
|
||||
defer func() {
|
||||
if s.config.Mux && proxyConn != nil {
|
||||
s.bridge.ReturnTunnel(proxyConn, s.task.Client.Id)
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
c.Close()
|
||||
} else {
|
||||
out, in := utils.ReplayWaitGroup(proxyConn.Conn, c, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, s.config.Mux, s.task.Client.Rate)
|
||||
s.FlowAdd(in, out)
|
||||
}
|
||||
s.doConnect(c, connectMethod)
|
||||
}
|
||||
|
||||
// passive mode
|
||||
|
@ -195,18 +182,7 @@ func (s *Sock5ModeServer) handleUDP(c net.Conn) {
|
|||
c.Read(dummy)
|
||||
}
|
||||
|
||||
proxyConn, err := s.doConnect(c, associateMethod)
|
||||
defer func() {
|
||||
if s.config.Mux && proxyConn != nil {
|
||||
s.bridge.ReturnTunnel(proxyConn, s.task.Client.Id)
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
c.Close()
|
||||
} else {
|
||||
out, in := utils.ReplayWaitGroup(proxyConn.Conn, c, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, s.config.Mux, s.task.Client.Rate)
|
||||
s.FlowAdd(in, out)
|
||||
}
|
||||
s.doConnect(c, associateMethod)
|
||||
}
|
||||
|
||||
//new conn
|
||||
|
|
|
@ -2,19 +2,16 @@ package server
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/easyProxy/bridge"
|
||||
"github.com/cnlh/easyProxy/utils"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TunnelModeServer struct {
|
||||
server
|
||||
errorContent []byte
|
||||
process process
|
||||
listener *net.TCPListener
|
||||
}
|
||||
|
@ -32,9 +29,6 @@ func NewTunnelModeServer(process process, bridge *bridge.Bridge, task *utils.Tun
|
|||
//开始
|
||||
func (s *TunnelModeServer) Start() error {
|
||||
var err error
|
||||
if s.errorContent, err = utils.ReadAllFromFile(beego.AppPath + "/web/static/page/error.html"); err != nil {
|
||||
s.errorContent = []byte("easyProxy 404")
|
||||
}
|
||||
s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.task.TcpPort, ""})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -53,44 +47,15 @@ func (s *TunnelModeServer) Start() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
//权限认证
|
||||
func (s *TunnelModeServer) auth(r *http.Request, c *utils.Conn, u, p string) error {
|
||||
if u != "" && p != "" && !utils.CheckAuth(r, u, p) {
|
||||
c.Write([]byte(utils.UnauthorizedBytes))
|
||||
c.Close()
|
||||
return errors.New("401 Unauthorized")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TunnelModeServer) writeConnFail(c net.Conn) {
|
||||
c.Write([]byte(utils.ConnectionFailBytes))
|
||||
c.Write(s.errorContent)
|
||||
}
|
||||
|
||||
//与客户端建立通道
|
||||
func (s *TunnelModeServer) dealClient(c *utils.Conn, cnf *utils.Config, addr string, method string, rb []byte) error {
|
||||
var link *utils.Conn
|
||||
var err error
|
||||
defer func() {
|
||||
if cnf.Mux && link != nil {
|
||||
s.bridge.ReturnTunnel(link, s.task.Client.Id)
|
||||
}
|
||||
}()
|
||||
if link, err = s.GetTunnelAndWriteHost(utils.CONN_TCP, s.task.Client.Id, cnf, addr); err != nil {
|
||||
log.Println("get bridge tunnel error: ", err)
|
||||
link := utils.NewLink(s.task.Client.GetId(), utils.CONN_TCP, addr, cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, c, s.task.Flow, nil, s.task.Client.Rate, nil)
|
||||
|
||||
if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link); err != nil {
|
||||
c.Close()
|
||||
return err
|
||||
}
|
||||
if flag, err := link.ReadFlag(); err == nil {
|
||||
if flag == utils.CONN_SUCCESS {
|
||||
if method == "CONNECT" {
|
||||
fmt.Fprint(c, "HTTP/1.1 200 Connection established\r\n")
|
||||
} else if rb != nil {
|
||||
link.WriteTo(rb, cnf.CompressEncode, cnf.Crypt, s.task.Client.Rate)
|
||||
}
|
||||
out, in := utils.ReplayWaitGroup(link.Conn, c.Conn, cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, cnf.Mux, s.task.Client.Rate)
|
||||
s.FlowAdd(in, out)
|
||||
}
|
||||
} else {
|
||||
s.linkCopy(link, c, rb, tunnel, s.task.Flow)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -142,3 +107,32 @@ func NewHostServer(task *utils.Tunnel) *HostServer {
|
|||
func (s *HostServer) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type process func(c *utils.Conn, s *TunnelModeServer) error
|
||||
|
||||
//tcp隧道模式
|
||||
func ProcessTunnel(c *utils.Conn, s *TunnelModeServer) error {
|
||||
if !s.ResetConfig() {
|
||||
c.Close()
|
||||
return errors.New("流量超出")
|
||||
}
|
||||
return s.dealClient(c, s.config, s.task.Target, "", nil)
|
||||
}
|
||||
|
||||
//http代理模式
|
||||
func ProcessHttp(c *utils.Conn, s *TunnelModeServer) error {
|
||||
if !s.ResetConfig() {
|
||||
c.Close()
|
||||
return errors.New("流量超出")
|
||||
}
|
||||
method, addr, rb, err, r := c.GetHost()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
c.Close()
|
||||
return err
|
||||
}
|
||||
if err := s.auth(r, c, s.config.U, s.config.P); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.dealClient(c, s.config, addr, method, rb)
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@ package server
|
|||
import (
|
||||
"github.com/cnlh/easyProxy/bridge"
|
||||
"github.com/cnlh/easyProxy/utils"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
@ -31,9 +29,9 @@ func (s *UdpModeServer) Start() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data := make([]byte, 1472) //udp数据包大小
|
||||
buf := utils.BufPoolUdp.Get().([]byte)
|
||||
for {
|
||||
n, addr, err := s.listener.ReadFromUDP(data)
|
||||
n, addr, err := s.listener.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "use of closed network connection") {
|
||||
break
|
||||
|
@ -43,42 +41,19 @@ func (s *UdpModeServer) Start() error {
|
|||
if !s.ResetConfig() {
|
||||
continue
|
||||
}
|
||||
go s.process(addr, data[:n])
|
||||
go s.process(addr, buf[:n])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//TODO:效率问题有待解决--->建立稳定通道,重复利用,提高效率,下个版本
|
||||
func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) {
|
||||
conn, err := s.bridge.GetTunnel(s.task.Client.Id, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, s.config.Mux)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
link := utils.NewLink(s.task.Client.GetId(), utils.CONN_UDP, s.task.Target, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, nil, s.task.Flow, s.listener, s.task.Client.Rate, addr)
|
||||
|
||||
if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err := conn.WriteHost(utils.CONN_UDP, s.task.Target); err != nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
if flag, err := conn.ReadFlag(); err == nil {
|
||||
defer func() {
|
||||
if conn != nil && s.config.Mux {
|
||||
conn.WriteTo([]byte(utils.IO_EOF), s.config.CompressEncode, s.config.Crypt, s.task.Client.Rate)
|
||||
s.bridge.ReturnTunnel(conn, s.task.Client.Id)
|
||||
} else {
|
||||
conn.Close()
|
||||
}
|
||||
}()
|
||||
if flag == utils.CONN_SUCCESS {
|
||||
in, _ := conn.WriteTo(data, s.config.CompressEncode, s.config.Crypt, s.task.Client.Rate)
|
||||
buf := utils.BufPoolUdp.Get().([]byte)
|
||||
out, err := conn.ReadFrom(buf, s.config.CompressDecode, s.config.Crypt, s.task.Client.Rate)
|
||||
if err != nil || err == io.EOF {
|
||||
return
|
||||
}
|
||||
s.listener.WriteToUDP(buf[:out], addr)
|
||||
s.FlowAdd(int64(in), int64(out))
|
||||
utils.BufPoolUdp.Put(buf)
|
||||
}
|
||||
s.task.Flow.Add(len(data), 0)
|
||||
tunnel.SendMsg(data, link)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
308
utils/conn.go
308
utils/conn.go
|
@ -13,6 +13,7 @@ import (
|
|||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -52,12 +53,6 @@ func (s *CryptConn) Write(b []byte) (n int, err error) {
|
|||
|
||||
//解密读
|
||||
func (s *CryptConn) Read(b []byte) (n int, err error) {
|
||||
defer func() {
|
||||
if err == nil && n == len(IO_EOF) && string(b[:n]) == IO_EOF {
|
||||
err = io.EOF
|
||||
n = 0
|
||||
}
|
||||
}()
|
||||
var lens int
|
||||
var buf []byte
|
||||
var rb []byte
|
||||
|
@ -122,14 +117,8 @@ func (s *SnappyConn) Write(b []byte) (n int, err error) {
|
|||
|
||||
//snappy压缩读 包含解密
|
||||
func (s *SnappyConn) Read(b []byte) (n int, err error) {
|
||||
buf := bufPool.Get().([]byte)
|
||||
defer func() {
|
||||
if err == nil && n == len(IO_EOF) && string(b[:n]) == IO_EOF {
|
||||
err = io.EOF
|
||||
n = 0
|
||||
}
|
||||
bufPool.Put(buf)
|
||||
}()
|
||||
buf := BufPool.Get().([]byte)
|
||||
defer BufPool.Put(buf)
|
||||
if n, err = s.r.Read(buf); err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -152,6 +141,7 @@ func (s *SnappyConn) Read(b []byte) (n int, err error) {
|
|||
|
||||
type Conn struct {
|
||||
Conn net.Conn
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
//new conn
|
||||
|
@ -161,101 +151,7 @@ func NewConn(conn net.Conn) *Conn {
|
|||
return c
|
||||
}
|
||||
|
||||
//读取指定长度内容
|
||||
func (s *Conn) ReadLen(cLen int) ([]byte, error) {
|
||||
if cLen > poolSize {
|
||||
return nil, errors.New("长度错误" + strconv.Itoa(cLen))
|
||||
}
|
||||
var buf []byte
|
||||
if cLen <= poolSizeSmall {
|
||||
buf = bufPoolSmall.Get().([]byte)[:cLen]
|
||||
defer bufPoolSmall.Put(buf)
|
||||
} else {
|
||||
buf = bufPoolMax.Get().([]byte)[:cLen]
|
||||
defer bufPoolMax.Put(buf)
|
||||
}
|
||||
if n, err := io.ReadFull(s, buf); err != nil || n != cLen {
|
||||
return buf, errors.New("读取指定长度错误" + err.Error())
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
//获取长度
|
||||
func (s *Conn) GetLen() (int, error) {
|
||||
val, err := s.ReadLen(4)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return GetLenByBytes(val)
|
||||
}
|
||||
|
||||
//写入长度+内容 粘包
|
||||
func (s *Conn) WriteLen(buf []byte) (int, error) {
|
||||
var b []byte
|
||||
var err error
|
||||
if b, err = GetLenBytes(buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return s.Write(b)
|
||||
}
|
||||
|
||||
//读取flag
|
||||
func (s *Conn) ReadFlag() (string, error) {
|
||||
val, err := s.ReadLen(4)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(val), err
|
||||
}
|
||||
|
||||
//读取host 连接地址 压缩类型
|
||||
func (s *Conn) GetHostFromConn() (typeStr string, host string, en, de int, crypt, mux bool, err error) {
|
||||
retry:
|
||||
lType, err := s.ReadLen(3)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if typeStr = string(lType); typeStr == TEST_FLAG {
|
||||
en, de, crypt, mux = s.GetConnInfoFromConn()
|
||||
goto retry
|
||||
} else if typeStr != CONN_TCP && typeStr != CONN_UDP {
|
||||
err = errors.New("unknown conn type")
|
||||
return
|
||||
}
|
||||
cLen, err := s.GetLen()
|
||||
if err != nil || cLen > poolSize {
|
||||
return
|
||||
}
|
||||
hostByte, err := s.ReadLen(cLen)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
host = string(hostByte)
|
||||
return
|
||||
}
|
||||
|
||||
//写连接类型 和 host地址
|
||||
func (s *Conn) WriteHost(ltype string, host string) (int, error) {
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
binary.Write(raw, binary.LittleEndian, []byte(ltype))
|
||||
binary.Write(raw, binary.LittleEndian, int32(len([]byte(host))))
|
||||
binary.Write(raw, binary.LittleEndian, []byte(host))
|
||||
return s.Write(raw.Bytes())
|
||||
}
|
||||
|
||||
//设置连接为长连接
|
||||
func (s *Conn) SetAlive() {
|
||||
conn := s.Conn.(*net.TCPConn)
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
conn.SetKeepAlive(true)
|
||||
conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
|
||||
}
|
||||
|
||||
func (s *Conn) SetReadDeadline(t time.Duration) {
|
||||
s.Conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
|
||||
}
|
||||
|
||||
//从tcp报文中解析出host,连接类型等 TODO 多种情况
|
||||
//从tcp报文中解析出host,连接类型等
|
||||
func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.Request) {
|
||||
var b [32 * 1024]byte
|
||||
var n int
|
||||
|
@ -285,6 +181,71 @@ func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.
|
|||
return
|
||||
}
|
||||
|
||||
//读取指定长度内容
|
||||
func (s *Conn) ReadLen(cLen int) ([]byte, error) {
|
||||
if cLen > poolSize {
|
||||
return nil, errors.New("长度错误" + strconv.Itoa(cLen))
|
||||
}
|
||||
var buf []byte
|
||||
if cLen <= poolSizeSmall {
|
||||
buf = BufPoolSmall.Get().([]byte)[:cLen]
|
||||
defer BufPoolSmall.Put(buf)
|
||||
} else {
|
||||
buf = BufPoolMax.Get().([]byte)[:cLen]
|
||||
defer BufPoolMax.Put(buf)
|
||||
}
|
||||
if n, err := io.ReadFull(s, buf); err != nil || n != cLen {
|
||||
return buf, errors.New("读取指定长度错误" + err.Error())
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
//read length or id (content length=4)
|
||||
func (s *Conn) GetLen() (int, error) {
|
||||
val, err := s.ReadLen(4)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return GetLenByBytes(val)
|
||||
}
|
||||
|
||||
//read flag
|
||||
func (s *Conn) ReadFlag() (string, error) {
|
||||
val, err := s.ReadLen(4)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(val), err
|
||||
}
|
||||
|
||||
//read connect status
|
||||
func (s *Conn) GetConnStatus() (id int, status bool, err error) {
|
||||
id, err = s.GetLen()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var b []byte
|
||||
if b, err = s.ReadLen(1); err != nil {
|
||||
return
|
||||
} else {
|
||||
status = GetBoolByStr(string(b[0]))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//设置连接为长连接
|
||||
func (s *Conn) SetAlive() {
|
||||
conn := s.Conn.(*net.TCPConn)
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
conn.SetKeepAlive(true)
|
||||
conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
|
||||
}
|
||||
|
||||
//set read dead time
|
||||
func (s *Conn) SetReadDeadline(t time.Duration) {
|
||||
s.Conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
|
||||
}
|
||||
|
||||
//单独读(加密|压缩)
|
||||
func (s *Conn) ReadFrom(b []byte, compress int, crypt bool, rate *Rate) (int, error) {
|
||||
if COMPRESS_SNAPY_DECODE == compress {
|
||||
|
@ -301,23 +262,111 @@ func (s *Conn) WriteTo(b []byte, compress int, crypt bool, rate *Rate) (n int, e
|
|||
return NewCryptConn(s.Conn, crypt, rate).Write(b)
|
||||
}
|
||||
|
||||
//写压缩方式,加密
|
||||
func (s *Conn) WriteConnInfo(en, de int, crypt, mux bool) {
|
||||
s.Write([]byte(strconv.Itoa(en) + strconv.Itoa(de) + GetStrByBool(crypt) + GetStrByBool(mux)))
|
||||
//send msg
|
||||
func (s *Conn) SendMsg(content []byte, link *Link) (n int, err error) {
|
||||
/*
|
||||
The msg info is formed as follows:
|
||||
+----+--------+
|
||||
|id | content |
|
||||
+----+--------+
|
||||
| 4 | ... |
|
||||
+----+--------+
|
||||
*/
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
binary.Write(raw, binary.LittleEndian, int32(link.Id))
|
||||
if n, err = s.Write(raw.Bytes()); err != nil {
|
||||
return
|
||||
}
|
||||
raw.Reset()
|
||||
binary.Write(raw, binary.LittleEndian, content)
|
||||
n, err = s.WriteTo(raw.Bytes(), link.En, link.Crypt, link.Rate)
|
||||
return
|
||||
}
|
||||
|
||||
//获取压缩方式,是否加密
|
||||
func (s *Conn) GetConnInfoFromConn() (en, de int, crypt, mux bool) {
|
||||
buf, err := s.ReadLen(4)
|
||||
if err != nil {
|
||||
//get msg content from conn
|
||||
func (s *Conn) GetMsgContent(link *Link) (content []byte, err error) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
buf := BufPoolCopy.Get().([]byte)
|
||||
if n, err := s.ReadFrom(buf, link.De, link.Crypt, link.Rate); err == nil && n > 4 {
|
||||
content = buf[:n]
|
||||
}
|
||||
return
|
||||
}
|
||||
en, _ = strconv.Atoi(string(buf[0]))
|
||||
de, _ = strconv.Atoi(string(buf[1]))
|
||||
crypt = GetBoolByStr(string(buf[2]))
|
||||
mux = GetBoolByStr(string(buf[3]))
|
||||
|
||||
//send info for link
|
||||
func (s *Conn) SendLinkInfo(link *Link) (int, error) {
|
||||
/*
|
||||
The link info is formed as follows:
|
||||
+----------+------+----------+------+----------+-----+
|
||||
| id | len | type | hostlen | host | en | de |crypt |
|
||||
+----------+------+----------+------+---------+------+
|
||||
| 4 | 4 | 3 | 4 | host | 1 | 1 | 1 |
|
||||
+----------+------+----------+------+----+----+------+
|
||||
*/
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
binary.Write(raw, binary.LittleEndian, []byte(NEW_CONN))
|
||||
binary.Write(raw, binary.LittleEndian, int32(14+len(link.Host)))
|
||||
binary.Write(raw, binary.LittleEndian, int32(link.Id))
|
||||
binary.Write(raw, binary.LittleEndian, []byte(link.ConnType))
|
||||
binary.Write(raw, binary.LittleEndian, int32(len(link.Host)))
|
||||
binary.Write(raw, binary.LittleEndian, []byte(link.Host))
|
||||
binary.Write(raw, binary.LittleEndian, []byte(strconv.Itoa(link.En)))
|
||||
binary.Write(raw, binary.LittleEndian, []byte(strconv.Itoa(link.De)))
|
||||
binary.Write(raw, binary.LittleEndian, []byte(GetStrByBool(link.Crypt)))
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.Write(raw.Bytes())
|
||||
}
|
||||
|
||||
func (s *Conn) GetLinkInfo() (link *Link, err error) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
var hostLen, n int
|
||||
var buf []byte
|
||||
if n, err = s.GetLen(); err != nil {
|
||||
return
|
||||
}
|
||||
link = new(Link)
|
||||
if buf, err = s.ReadLen(n); err != nil {
|
||||
return
|
||||
}
|
||||
if link.Id, err = GetLenByBytes(buf[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
link.ConnType = string(buf[4:7])
|
||||
if hostLen, err = GetLenByBytes(buf[7:11]); err != nil {
|
||||
return
|
||||
} else {
|
||||
link.Host = string(buf[11 : 11+hostLen])
|
||||
link.En = GetIntNoErrByStr(string(buf[11+hostLen]))
|
||||
link.De = GetIntNoErrByStr(string(buf[12+hostLen]))
|
||||
link.Crypt = GetBoolByStr(string(buf[13+hostLen]))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//write connect success
|
||||
func (s *Conn) WriteSuccess(id int) (int, error) {
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
binary.Write(raw, binary.LittleEndian, int32(id))
|
||||
binary.Write(raw, binary.LittleEndian, []byte("1"))
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.Write(raw.Bytes())
|
||||
}
|
||||
|
||||
//write connect fail
|
||||
func (s *Conn) WriteFail(id int) (int, error) {
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
binary.Write(raw, binary.LittleEndian, int32(id))
|
||||
binary.Write(raw, binary.LittleEndian, []byte("0"))
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.Write(raw.Bytes())
|
||||
}
|
||||
|
||||
//close
|
||||
func (s *Conn) Close() error {
|
||||
|
@ -351,29 +400,18 @@ func (s *Conn) WriteClose() (int, error) {
|
|||
|
||||
//write main
|
||||
func (s *Conn) WriteMain() (int, error) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.Write([]byte(WORK_MAIN))
|
||||
}
|
||||
|
||||
//write chan
|
||||
func (s *Conn) WriteChan() (int, error) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.Write([]byte(WORK_CHAN))
|
||||
}
|
||||
|
||||
//write test
|
||||
func (s *Conn) WriteTest() (int, error) {
|
||||
return s.Write([]byte(TEST_FLAG))
|
||||
}
|
||||
|
||||
//write test
|
||||
func (s *Conn) WriteSuccess() (int, error) {
|
||||
return s.Write([]byte(CONN_SUCCESS))
|
||||
}
|
||||
|
||||
//write test
|
||||
func (s *Conn) WriteFail() (int, error) {
|
||||
return s.Write([]byte(CONN_ERROR))
|
||||
}
|
||||
|
||||
//获取长度+内容
|
||||
func GetLenBytes(buf []byte) (b []byte, err error) {
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
|
|
|
@ -17,61 +17,8 @@ var (
|
|||
once sync.Once
|
||||
)
|
||||
|
||||
type Flow struct {
|
||||
ExportFlow int64 //出口流量
|
||||
InletFlow int64 //入口流量
|
||||
FlowLimit int64 //流量限制,出口+入口 /M
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
Cnf *Config
|
||||
Id int //id
|
||||
VerifyKey string //验证密钥
|
||||
Addr string //客户端ip地址
|
||||
Remark string //备注
|
||||
Status bool //是否开启
|
||||
IsConnect bool //是否连接
|
||||
RateLimit int //速度限制 /kb
|
||||
Flow *Flow //流量
|
||||
Rate *Rate //速度控制
|
||||
}
|
||||
|
||||
type Tunnel struct {
|
||||
Id int //Id
|
||||
TcpPort int //服务端与客户端通信端口
|
||||
Mode string //启动方式
|
||||
Target string //目标
|
||||
Status bool //是否开启
|
||||
Client *Client //所属客户端id
|
||||
Flow *Flow
|
||||
Config *Config
|
||||
UseClientCnf bool //是否继承客户端配置
|
||||
Remark string //备注
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
U string //socks5验证用户名
|
||||
P string //socks5验证密码
|
||||
Compress string //压缩方式
|
||||
Crypt bool //是否加密
|
||||
Mux bool //是否加密
|
||||
CompressEncode int //加密方式
|
||||
CompressDecode int //解密方式
|
||||
}
|
||||
|
||||
type Host struct {
|
||||
Host string //启动方式
|
||||
Target string //目标
|
||||
HeaderChange string //host修改
|
||||
HostChange string //host修改
|
||||
Flow *Flow
|
||||
Client *Client
|
||||
Remark string //备注
|
||||
}
|
||||
|
||||
func NewCsv() *Csv {
|
||||
c := new(Csv)
|
||||
return c
|
||||
return new(Csv)
|
||||
}
|
||||
|
||||
type Csv struct {
|
||||
|
@ -108,7 +55,6 @@ func (s *Csv) StoreTasksToCsv() {
|
|||
task.Config.Compress,
|
||||
utils.GetStrByBool(task.Status),
|
||||
GetStrByBool(task.Config.Crypt),
|
||||
GetStrByBool(task.Config.Mux),
|
||||
strconv.Itoa(task.Config.CompressEncode),
|
||||
strconv.Itoa(task.Config.CompressDecode),
|
||||
strconv.Itoa(task.Id),
|
||||
|
@ -160,17 +106,16 @@ func (s *Csv) LoadTaskFromCsv() {
|
|||
P: item[4],
|
||||
Compress: item[5],
|
||||
Crypt: GetBoolByStr(item[7]),
|
||||
Mux: GetBoolByStr(item[8]),
|
||||
CompressEncode: GetIntNoErrByStr(item[9]),
|
||||
CompressDecode: GetIntNoErrByStr(item[10]),
|
||||
CompressEncode: GetIntNoErrByStr(item[8]),
|
||||
CompressDecode: GetIntNoErrByStr(item[9]),
|
||||
},
|
||||
Status: utils.GetBoolByStr(item[6]),
|
||||
Id: GetIntNoErrByStr(item[11]),
|
||||
UseClientCnf: GetBoolByStr(item[13]),
|
||||
Remark: item[14],
|
||||
Id: GetIntNoErrByStr(item[10]),
|
||||
UseClientCnf: GetBoolByStr(item[12]),
|
||||
Remark: item[13],
|
||||
}
|
||||
post.Flow = new(Flow)
|
||||
if post.Client, err = s.GetClient(GetIntNoErrByStr(item[12])); err != nil {
|
||||
if post.Client, err = s.GetClient(GetIntNoErrByStr(item[11])); err != nil {
|
||||
continue
|
||||
}
|
||||
tasks = append(tasks, post)
|
||||
|
@ -284,13 +229,12 @@ func (s *Csv) LoadClientFromCsv() {
|
|||
VerifyKey: item[1],
|
||||
Remark: item[2],
|
||||
Status: GetBoolByStr(item[3]),
|
||||
RateLimit: GetIntNoErrByStr(item[9]),
|
||||
RateLimit: GetIntNoErrByStr(item[8]),
|
||||
Cnf: &Config{
|
||||
U: item[4],
|
||||
P: item[5],
|
||||
Crypt: GetBoolByStr(item[6]),
|
||||
Mux: GetBoolByStr(item[7]),
|
||||
Compress: item[8],
|
||||
Compress: item[7],
|
||||
},
|
||||
}
|
||||
if post.Id > s.ClientIncreaseId {
|
||||
|
@ -301,7 +245,7 @@ func (s *Csv) LoadClientFromCsv() {
|
|||
post.Rate.Start()
|
||||
}
|
||||
post.Flow = new(Flow)
|
||||
post.Flow.FlowLimit = int64(utils.GetIntNoerrByStr(item[10]))
|
||||
post.Flow.FlowLimit = int64(utils.GetIntNoerrByStr(item[9]))
|
||||
clients = append(clients, post)
|
||||
}
|
||||
s.Clients = clients
|
||||
|
@ -407,10 +351,14 @@ func (s *Csv) GetClientId() int {
|
|||
func (s *Csv) UpdateClient(t *Client) error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
for k, v := range s.Clients {
|
||||
for _, v := range s.Clients {
|
||||
if v.Id == t.Id {
|
||||
s.Clients = append(s.Clients[:k], s.Clients[k+1:]...)
|
||||
s.Clients = append(s.Clients, t)
|
||||
v.Cnf = t.Cnf
|
||||
v.VerifyKey = t.VerifyKey
|
||||
v.Remark = t.Remark
|
||||
v.RateLimit = t.RateLimit
|
||||
v.Flow = t.Flow
|
||||
v.Rate = t.Rate
|
||||
s.StoreClientsToCsv()
|
||||
return nil
|
||||
}
|
||||
|
@ -458,7 +406,6 @@ func (s *Csv) StoreClientsToCsv() {
|
|||
client.Cnf.U,
|
||||
client.Cnf.P,
|
||||
utils.GetStrByBool(client.Cnf.Crypt),
|
||||
utils.GetStrByBool(client.Cnf.Mux),
|
||||
client.Cnf.Compress,
|
||||
strconv.Itoa(client.RateLimit),
|
||||
strconv.Itoa(int(client.Flow.FlowLimit)),
|
||||
|
@ -480,15 +427,3 @@ func GetCsvDb() *Csv {
|
|||
return CsvDb
|
||||
}
|
||||
|
||||
//深拷贝Tunnel
|
||||
func DeepCopyConfig(c *Config) *Config {
|
||||
return &Config{
|
||||
U: c.U,
|
||||
P: c.P,
|
||||
Compress: c.Compress,
|
||||
Crypt: c.Crypt,
|
||||
Mux: c.Mux,
|
||||
CompressEncode: c.CompressEncode,
|
||||
CompressDecode: c.CompressDecode,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Link struct {
|
||||
Id int //id
|
||||
ConnType string //连接类型
|
||||
Host string //目标
|
||||
En int //加密
|
||||
De int //解密
|
||||
Crypt bool //加密
|
||||
Conn *Conn
|
||||
Flow *Flow
|
||||
UdpListener *net.UDPConn
|
||||
Rate *Rate
|
||||
UdpRemoteAddr *net.UDPAddr
|
||||
}
|
||||
|
||||
func NewLink(id int, connType string, host string, en, de int, crypt bool, conn *Conn, flow *Flow, udpListener *net.UDPConn, rate *Rate, UdpRemoteAddr *net.UDPAddr) *Link {
|
||||
return &Link{
|
||||
Id: id,
|
||||
ConnType: connType,
|
||||
Host: host,
|
||||
En: en,
|
||||
De: de,
|
||||
Crypt: crypt,
|
||||
Conn: conn,
|
||||
Flow: flow,
|
||||
UdpListener: udpListener,
|
||||
Rate: rate,
|
||||
UdpRemoteAddr: UdpRemoteAddr,
|
||||
}
|
||||
}
|
||||
|
||||
type Flow struct {
|
||||
ExportFlow int64 //出口流量
|
||||
InletFlow int64 //入口流量
|
||||
FlowLimit int64 //流量限制,出口+入口 /M
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *Flow) Add(in, out int) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.InletFlow += int64(in)
|
||||
s.ExportFlow += int64(out)
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
Cnf *Config
|
||||
Id int //id
|
||||
VerifyKey string //验证密钥
|
||||
Addr string //客户端ip地址
|
||||
Remark string //备注
|
||||
Status bool //是否开启
|
||||
IsConnect bool //是否连接
|
||||
RateLimit int //速度限制 /kb
|
||||
Flow *Flow //流量
|
||||
Rate *Rate //速度控制
|
||||
id int
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *Client) GetId() int {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.id++
|
||||
return s.id
|
||||
}
|
||||
|
||||
type Tunnel struct {
|
||||
Id int //Id
|
||||
TcpPort int //服务端与客户端通信端口
|
||||
Mode string //启动方式
|
||||
Target string //目标
|
||||
Status bool //是否开启
|
||||
Client *Client //所属客户端id
|
||||
Flow *Flow
|
||||
Config *Config
|
||||
UseClientCnf bool //是否继承客户端配置
|
||||
Remark string //备注
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
U string //socks5验证用户名
|
||||
P string //socks5验证密码
|
||||
Compress string //压缩方式
|
||||
Crypt bool //是否加密
|
||||
CompressEncode int //加密方式
|
||||
CompressDecode int //解密方式
|
||||
}
|
||||
|
||||
type Host struct {
|
||||
Host string //启动方式
|
||||
Target string //目标
|
||||
HeaderChange string //host修改
|
||||
HostChange string //host修改
|
||||
Flow *Flow
|
||||
Client *Client
|
||||
Remark string //备注
|
||||
}
|
||||
|
||||
//深拷贝Config
|
||||
func DeepCopyConfig(c *Config) *Config {
|
||||
return &Config{
|
||||
U: c.U,
|
||||
P: c.P,
|
||||
Compress: c.Compress,
|
||||
Crypt: c.Crypt,
|
||||
CompressEncode: c.CompressEncode,
|
||||
CompressDecode: c.CompressDecode,
|
||||
}
|
||||
}
|
|
@ -1,13 +1,15 @@
|
|||
package utils
|
||||
|
||||
import "sync"
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
const poolSize = 64 * 1024
|
||||
const poolSizeSmall = 100
|
||||
const poolSizeUdp = 1472
|
||||
const poolSizeCopy = 32 * 1024
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
var BufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, poolSize)
|
||||
},
|
||||
|
@ -18,18 +20,24 @@ var BufPoolUdp = sync.Pool{
|
|||
return make([]byte, poolSizeUdp)
|
||||
},
|
||||
}
|
||||
var bufPoolMax = sync.Pool{
|
||||
var BufPoolMax = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, poolSize)
|
||||
},
|
||||
}
|
||||
var bufPoolSmall = sync.Pool{
|
||||
var BufPoolSmall = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, poolSizeSmall)
|
||||
},
|
||||
}
|
||||
var bufPoolCopy = sync.Pool{
|
||||
var BufPoolCopy = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, poolSizeCopy)
|
||||
},
|
||||
}
|
||||
|
||||
func PutBufPoolCopy(buf []byte) {
|
||||
if cap(buf) == poolSizeCopy {
|
||||
BufPoolCopy.Put(buf[:poolSizeCopy])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package utils
|
|||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
|
@ -11,8 +10,6 @@ import (
|
|||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -26,9 +23,8 @@ const (
|
|||
RES_SIGN = "sign"
|
||||
RES_MSG = "msg0"
|
||||
RES_CLOSE = "clse"
|
||||
NEW_CONN = "conn" //新连接标志
|
||||
CONN_SUCCESS = "sucs"
|
||||
CONN_ERROR = "fail"
|
||||
TEST_FLAG = "tst"
|
||||
CONN_TCP = "tcp"
|
||||
CONN_UDP = "udp"
|
||||
UnauthorizedBytes = `HTTP/1.1 401 Unauthorized
|
||||
|
@ -42,32 +38,6 @@ WWW-Authenticate: Basic realm="easyProxy"
|
|||
`
|
||||
)
|
||||
|
||||
//copy
|
||||
func Relay(in, out net.Conn, compressType int, crypt, mux bool, rate *Rate) (n int64, err error) {
|
||||
switch compressType {
|
||||
case COMPRESS_SNAPY_ENCODE:
|
||||
n, err = copyBuffer(NewSnappyConn(in, crypt, rate), out)
|
||||
out.Close()
|
||||
NewSnappyConn(in, crypt, rate).Write([]byte(IO_EOF))
|
||||
case COMPRESS_SNAPY_DECODE:
|
||||
n, err = copyBuffer(in, NewSnappyConn(out, crypt, rate))
|
||||
in.Close()
|
||||
if !mux {
|
||||
out.Close()
|
||||
}
|
||||
case COMPRESS_NONE_ENCODE:
|
||||
n, err = copyBuffer(NewCryptConn(in, crypt, rate), out)
|
||||
out.Close()
|
||||
NewCryptConn(in, crypt, rate).Write([]byte(IO_EOF))
|
||||
case COMPRESS_NONE_DECODE:
|
||||
n, err = copyBuffer(in, NewCryptConn(out, crypt, rate))
|
||||
in.Close()
|
||||
if !mux {
|
||||
out.Close()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//判断压缩方式
|
||||
func GetCompressType(compress string) (int, int) {
|
||||
|
@ -152,71 +122,11 @@ func GetIntNoErrByStr(str string) int {
|
|||
return i
|
||||
}
|
||||
|
||||
// io.copy的优化版,读取buffer长度原为32*1024,与snappy不同,导致读取出的内容存在差异,不利于解密
|
||||
//内存优化 用到pool,快速回收
|
||||
func copyBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
|
||||
for {
|
||||
//放在里面是为了加快回收和重利用
|
||||
buf := bufPoolCopy.Get().([]byte)
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
bufPoolCopy.Put(buf)
|
||||
if nw > 0 {
|
||||
written += int64(nw)
|
||||
}
|
||||
if ew != nil {
|
||||
err = ew
|
||||
break
|
||||
}
|
||||
if nr != nw {
|
||||
err = io.ErrShortWrite
|
||||
break
|
||||
}
|
||||
} else {
|
||||
bufPoolCopy.Put(buf)
|
||||
}
|
||||
if er != nil {
|
||||
if er != io.EOF {
|
||||
err = er
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return written, err
|
||||
}
|
||||
|
||||
//连接重置 清空缓存区
|
||||
func FlushConn(c net.Conn) {
|
||||
c.SetReadDeadline(time.Now().Add(time.Second * 3))
|
||||
buf := bufPool.Get().([]byte)
|
||||
defer bufPool.Put(buf)
|
||||
for {
|
||||
if _, err := c.Read(buf); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
c.SetReadDeadline(time.Time{})
|
||||
}
|
||||
|
||||
//简单的一个校验值
|
||||
func Getverifyval(vkey string) string {
|
||||
return Md5(vkey)
|
||||
}
|
||||
|
||||
//wait replay group
|
||||
//conn1 网桥 conn2
|
||||
func ReplayWaitGroup(conn1 net.Conn, conn2 net.Conn, compressEncode, compressDecode int, crypt, mux bool, rate *Rate) (out int64, in int64) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
in, _ = Relay(conn1, conn2, compressEncode, crypt, mux, rate)
|
||||
wg.Done()
|
||||
}()
|
||||
out, _ = Relay(conn2, conn1, compressDecode, crypt, mux, rate)
|
||||
wg.Wait()
|
||||
return
|
||||
}
|
||||
|
||||
func ChangeHostAndHeader(r *http.Request, host string, header string, addr string) {
|
||||
if host != "" {
|
||||
|
@ -236,8 +146,8 @@ func ChangeHostAndHeader(r *http.Request, host string, header string, addr strin
|
|||
r.Header.Set("X-Real-IP", addr)
|
||||
}
|
||||
|
||||
func ReadAllFromFile(filePth string) ([]byte, error) {
|
||||
f, err := os.Open(filePth)
|
||||
func ReadAllFromFile(filePath string) ([]byte, error) {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ func (s *BaseController) display(tpl ...string) {
|
|||
}
|
||||
ip := s.Ctx.Request.Host
|
||||
if strings.LastIndex(ip, ":") > 0 {
|
||||
s.Data["ip"] = utils.GetHostByName(ip[0:])
|
||||
arr := strings.Split(utils.GetHostByName(ip), ":")
|
||||
s.Data["ip"] = arr[0]
|
||||
}
|
||||
s.Data["p"] = server.Bridge.TunnelPort
|
||||
s.Data["proxyPort"] = beego.AppConfig.String("hostPort")
|
||||
|
|
|
@ -38,7 +38,6 @@ func (s *ClientController) Add() {
|
|||
P: s.GetString("p"),
|
||||
Compress: s.GetString("compress"),
|
||||
Crypt: s.GetBoolNoErr("crypt"),
|
||||
Mux: s.GetBoolNoErr("mux"),
|
||||
},
|
||||
RateLimit: s.GetIntNoErr("rate_limit"),
|
||||
Flow: &utils.Flow{
|
||||
|
@ -91,7 +90,6 @@ func (s *ClientController) Edit() {
|
|||
c.Cnf.P = s.GetString("p")
|
||||
c.Cnf.Compress = s.GetString("compress")
|
||||
c.Cnf.Crypt = s.GetBoolNoErr("crypt")
|
||||
c.Cnf.Mux = s.GetBoolNoErr("mux")
|
||||
c.Flow.FlowLimit = int64(s.GetIntNoErr("flow_limit"))
|
||||
c.RateLimit = s.GetIntNoErr("rate_limit")
|
||||
if c.Rate != nil {
|
||||
|
|
|
@ -81,7 +81,6 @@ func (s *IndexController) Add() {
|
|||
P: s.GetString("p"),
|
||||
Compress: s.GetString("compress"),
|
||||
Crypt: s.GetBoolNoErr("crypt"),
|
||||
Mux: s.GetBoolNoErr("mux"),
|
||||
},
|
||||
Id: server.CsvDb.GetTaskId(),
|
||||
UseClientCnf: s.GetBoolNoErr("use_client"),
|
||||
|
@ -136,7 +135,6 @@ func (s *IndexController) Edit() {
|
|||
t.Config.P = s.GetString("p")
|
||||
t.Config.Compress = s.GetString("compress")
|
||||
t.Config.Crypt = s.GetBoolNoErr("crypt")
|
||||
t.Config.Mux = s.GetBoolNoErr("mux")
|
||||
t.UseClientCnf = s.GetBoolNoErr("use_client")
|
||||
t.Remark = s.GetString("remark")
|
||||
if t.Client, err = server.CsvDb.GetClient(s.GetIntNoErr("client_id")); err != nil {
|
||||
|
|
|
@ -38,13 +38,6 @@
|
|||
<option value="1">加密</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="compress">
|
||||
<label class="control-label">是否TCP复用</label>
|
||||
<select class="form-control" name="crypt">
|
||||
<option value="0">不启用</option>
|
||||
<option value="1">启用</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tile-footer">
|
||||
|
|
|
@ -43,13 +43,6 @@
|
|||
<option {{if eq true .c.Cnf.Crypt}}selected{{end}} value="1">加密</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="compress">
|
||||
<label class="control-label">是否启用TCP复用(所有模式均支持)</label>
|
||||
<select class="form-control" name="mux">
|
||||
<option {{if eq false .c.Cnf.Mux}}selected{{end}} value="0">不启用</option>
|
||||
<option {{if eq true .c.Cnf.Mux}}selected{{end}} value="1">启用</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tile-footer">
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
<th>备注</th>
|
||||
<th>压缩方式</th>
|
||||
<th>加密</th>
|
||||
<th>多路复用</th>
|
||||
<th>用户名</th>
|
||||
<th>密码</th>
|
||||
<th>状态</th>
|
||||
|
@ -126,7 +125,6 @@
|
|||
{data: "Addr"},
|
||||
{data: "Addr"},
|
||||
{data: "Addr"},
|
||||
{data: "Addr"},
|
||||
{data: "Remark"},
|
||||
{data: "Status"},
|
||||
{data: "IsConnect"},
|
||||
|
@ -193,20 +191,10 @@
|
|||
render: function (data, type, row, meta) {
|
||||
return row.Cnf.U
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: -8,
|
||||
render: function (data, type, row, meta) {
|
||||
if (row.Cnf.Mux == "0") {
|
||||
return "不启用"
|
||||
} else {
|
||||
return "启用"
|
||||
}
|
||||
}
|
||||
}
|
||||
,
|
||||
{
|
||||
targets: -9,
|
||||
targets: -8,
|
||||
render: function (data, type, row, meta) {
|
||||
if (row.Cnf.Crypt == "0") {
|
||||
return "不加密"
|
||||
|
@ -217,7 +205,7 @@
|
|||
}
|
||||
,
|
||||
{
|
||||
targets: -10,
|
||||
targets: -9,
|
||||
render: function (data, type, row, meta) {
|
||||
return row.Cnf.Compress
|
||||
}
|
||||
|
|
|
@ -59,13 +59,6 @@
|
|||
<option value="1">加密</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="mux">
|
||||
<label class="control-label">是否TCP复用</label>
|
||||
<select class="form-control" name="mux">
|
||||
<option value="0">不启用</option>
|
||||
<option value="1">启用</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="u">
|
||||
<label class="control-label">验证用户名(仅socks5,web穿透支持)</label>
|
||||
<input class="form-control" type="text" name="u" placeholder="不填则无需验证">
|
||||
|
|
|
@ -55,13 +55,6 @@
|
|||
<option {{if eq true .t.Config.Crypt}}selected{{end}} value="1">加密</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="mux">
|
||||
<label class="control-label">是否启用TCP复用(所有模式均支持)</label>
|
||||
<select class="form-control" name="mux">
|
||||
<option {{if eq false .t.Config.Mux}}selected{{end}} value="0">不启用</option>
|
||||
<option {{if eq true .t.Config.Mux}}selected{{end}} value="1">启用</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="u">
|
||||
<label class="control-label">验证用户名(仅socks5,web穿透支持)</label>
|
||||
<input class="form-control" value="{{.t.Config.U}}" type="text" name="u"
|
||||
|
|
|
@ -26,26 +26,16 @@
|
|||
</p>
|
||||
<p><b>使用步骤:</b></p>
|
||||
<ul>
|
||||
<li>将a.proxy.com,b.proxy.com解析到公网服务器{{.ip}}</li>
|
||||
<li>使用nginx监听这两个个域名,并配置ssl等……</li>
|
||||
<li>在nginx配置中添加反向代理(或直接将http监听端口改为80)<br>
|
||||
<pre><code>
|
||||
server {
|
||||
listen 80;
|
||||
server_name a.proxy.com b.proxy.com;#也可以是泛解析*.proxy.com
|
||||
#ssl等配置
|
||||
<b>location / {
|
||||
proxy_pass http://127.0.0.1:{{.proxyPort}};
|
||||
}</b>
|
||||
}
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>在域名代理管理中添加一个客户端,选择压缩方式,保存。 <a href="/index/add?type=hostServer">立即添加</a></li>
|
||||
{{/*<li>在域名代理管理中找到新加的客户端(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
|
||||
<li>将*.proxy.com解析到公网服务器{{.ip}}</li>
|
||||
<li>在客户端管理中创建一个客户端,记录下验证密钥</li>
|
||||
<li>点击该客户端的域名管理,添加两条规则规则:1、域名:a.proxy.com,内网目标:127.0.0.1:81,2、域名:b.proxy.com,内网目标:127.0.0.1:82</li>
|
||||
<li>内网客户端运行<code>
|
||||
<pre>./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥</pre>
|
||||
</code></pre></li>
|
||||
<li>现在访问a.proxy.com,b.proxy.com即可成功</li>
|
||||
</ul>
|
||||
<p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动</p>
|
||||
<p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,<b>如需使用https请在配置文件中将https端口设置为443,和将对应的证书文件路径添加到配置文件中
|
||||
</b></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -61,10 +51,13 @@
|
|||
</p>
|
||||
<p><b>使用步骤:</b></p>
|
||||
<ul>
|
||||
<li>在tcp隧道管理中添加一条隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),选择压缩方式,保存。 <a
|
||||
href="/index/add?type=tunnelServer">立即添加</a></li>
|
||||
{{/*<li>在tcp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
|
||||
<li>访问公网服务器ip({{.ip}}):填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:ssh -p 8001 root@{{.ip}}</li>
|
||||
<li>在客户端管理中创建一个客户端,记录下验证密钥</li>
|
||||
<li>内网客户端运行<code>
|
||||
<pre>./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥</pre>
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>在该客户端隧道管理中添加一条tcp隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),选择压缩方式,保存。</li>
|
||||
<li>访问公网服务器ip({{.ip}}),填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:ssh -p 8001 root@{{.ip}}</li>
|
||||
</ul>
|
||||
<p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动</p>
|
||||
</div>
|
||||
|
@ -80,9 +73,12 @@
|
|||
</p>
|
||||
<p><b>使用步骤:</b></p>
|
||||
<ul>
|
||||
<li>在udp隧道管理中添加一条隧道,填写监听的端口(8002)、内网目标ip和目标端口(10.1.50.102:53),选择压缩方式,保存。 <a
|
||||
href="/index/add?type=udpServer">立即添加</a></li>
|
||||
{{/*<li>在udp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
|
||||
<li>在客户端管理中创建一个客户端,记录下验证密钥</li>
|
||||
<li>内网客户端运行<code>
|
||||
<pre>./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥</pre>
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>在该客户端的隧道管理中添加一条udp隧道,填写监听的端口(53)、内网目标ip和目标端口(10.1.50.102:53),选择压缩方式,保存。</li>
|
||||
<li>修改本机dns为{{.ip}},则相当于使用10.1.50.202作为dns服务器</li>
|
||||
</ul>
|
||||
<p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动</p>
|
||||
|
@ -101,9 +97,12 @@
|
|||
</p>
|
||||
<p><b>使用步骤:</b></p>
|
||||
<ul>
|
||||
<li>在socks5隧道管理中添加一条隧道,填写监听的端口(8003),验证用户名和密码自行选择(建议先不填,部分客户端不支持,proxifer支持),选择压缩方式,保存。 <a
|
||||
href="/index/add?type=sock5Server">立即添加</a></li>
|
||||
{{/*<li>在socks5代理管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
|
||||
<li>在客户端管理中创建一个客户端,记录下验证密钥</li>
|
||||
<li>内网客户端运行<code>
|
||||
<pre>./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥</pre>
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>在该客户端隧道管理中添加一条socks5代理,填写监听的端口(8003),验证用户名和密码自行选择(建议先不填,部分客户端不支持,proxifer支持),选择压缩方式,保存。</li>
|
||||
<li>在外网环境的本机配置socks5代理,ip为公网服务器ip({{.ip}}),端口为填写的监听端口(8003),即可畅享内网了</li>
|
||||
</ul>
|
||||
<p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动</p>
|
||||
|
@ -120,13 +119,23 @@
|
|||
</p>
|
||||
<p><b>使用步骤:</b></p>
|
||||
<ul>
|
||||
<li>在http隧道管理中添加一条隧道,填写监听的端口(8004),选择压缩方式,保存。 <a
|
||||
href="/index/add?type=httpProxyServer">立即添加</a></li>
|
||||
<li>在http代理管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>
|
||||
<li>在客户端管理中创建一个客户端,记录下验证密钥</li>
|
||||
<li>内网客户端运行<code>
|
||||
<pre>./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥</pre>
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>在该客户端隧道管理中添加一条http代理,填写监听的端口(8004),选择压缩方式,保存。</li>
|
||||
<li>在外网环境的本机配置http代理,ip为公网服务器ip({{.ip}}),端口为填写的监听端口(8004),即可访问了</li>
|
||||
</ul>
|
||||
<p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="tile">
|
||||
<p><b>单个客户端可以田间多条隧道或者域名解析</b></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</main>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<th>host改写</th>
|
||||
<th>压缩方式</th>
|
||||
<th>加密</th>
|
||||
<th>多路复用</th>
|
||||
<th>用户名</th>
|
||||
<th>密码</th>
|
||||
<th>出口流量</th>
|
||||
|
@ -84,7 +83,6 @@
|
|||
{data: 'HostChange'},
|
||||
{data: 'HostChange'},
|
||||
{data: 'HostChange'},
|
||||
{data: 'HostChange'},
|
||||
{data: 'Target'},
|
||||
],
|
||||
bFilter: false,
|
||||
|
@ -127,20 +125,10 @@
|
|||
render: function (data, type, row, meta) {
|
||||
return row.Client.Cnf.U
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: -6,
|
||||
render: function (data, type, row, meta) {
|
||||
if (row.Client.Cnf.Mux == "0") {
|
||||
return "不启用"
|
||||
} else {
|
||||
return "启用"
|
||||
}
|
||||
}
|
||||
}
|
||||
,
|
||||
{
|
||||
targets: -7,
|
||||
targets: -6,
|
||||
render: function (data, type, row, meta) {
|
||||
if (row.Client.Cnf.Crypt == "0") {
|
||||
return "不加密"
|
||||
|
@ -151,7 +139,7 @@
|
|||
}
|
||||
,
|
||||
{
|
||||
targets: -8,
|
||||
targets: -7,
|
||||
render: function (data, type, row, meta) {
|
||||
return row.Client.Cnf.Compress
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<th>内网目标</th>
|
||||
<th>压缩方式</th>
|
||||
<th>加密传输</th>
|
||||
<th>TCP多路复用</th>
|
||||
<th>用户名</th>
|
||||
<th>密码</th>
|
||||
<th>客户端状态</th>
|
||||
|
@ -114,7 +113,6 @@
|
|||
{data: 'Target'},
|
||||
{data: 'Compress'},
|
||||
{data: 'Crypt'},
|
||||
{data: 'Mux'},
|
||||
{data: 'U'},
|
||||
{data: 'P'},
|
||||
{data: 'ClientStatus'},
|
||||
|
@ -152,7 +150,7 @@
|
|||
}
|
||||
}
|
||||
}, {
|
||||
targets: 8,
|
||||
targets: 7,
|
||||
render: function (data, type, row, meta) {
|
||||
if (row.UseClientCnf == true) {
|
||||
return row.Client.Cnf.U
|
||||
|
@ -161,7 +159,7 @@
|
|||
}
|
||||
}
|
||||
}, {
|
||||
targets: 9,
|
||||
targets: 8,
|
||||
render: function (data, type, row, meta) {
|
||||
if (row.UseClientCnf == true) {
|
||||
return row.Client.Cnf.P
|
||||
|
@ -181,7 +179,7 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
targets: -9,
|
||||
targets: -8,
|
||||
render: function (data, type, row, meta) {
|
||||
if (row.UseClientCnf == true) {
|
||||
crypt = row.Client.Cnf.Crypt
|
||||
|
@ -195,21 +193,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: -8,
|
||||
render: function (data, type, row, meta) {
|
||||
if (row.UseClientCnf == true) {
|
||||
mux = row.Client.Cnf.Mux
|
||||
} else {
|
||||
mux = row.Config.Mux
|
||||
}
|
||||
if (mux == "0") {
|
||||
return "不启用"
|
||||
} else {
|
||||
return "启用"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: -5,
|
||||
render: function (data, type, row, meta) {
|
||||
|
|
Loading…
Reference in New Issue