From eccc221e67cd35bff1a6e09f9f2224036ed4bde3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=88=98=E6=B2=B3?=
Date: Fri, 1 Feb 2019 02:06:30 +0800
Subject: [PATCH] =?UTF-8?q?https=20=E3=80=81=E5=AE=A2=E6=88=B7=E7=AB=AF?=
=?UTF-8?q?=E4=B8=8E=E6=9C=8D=E5=8A=A1=E7=AB=AF=E8=BF=9E=E6=8E=A5=E4=BC=98?=
=?UTF-8?q?=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 393 ++++++++++++-------------------
bridge/bridge.go | 288 +++++++++++++---------
client/client.go | 193 ++++++++-------
cmd/proxy_client/proxy_client.go | 3 +-
cmd/proxy_server/proxy_server.go | 3 +-
conf/app.conf | 15 +-
conf/clients.csv | 2 +-
conf/hosts.csv | 2 +-
conf/tasks.csv | 5 +-
server/base.go | 63 +++--
server/http.go | 169 +++++++++++++
server/process.go | 111 ---------
server/server.go | 28 +--
server/socks5.go | 48 +---
server/tcp.go | 80 +++----
server/udp.go | 43 +---
utils/conn.go | 312 +++++++++++++-----------
utils/file.go | 99 ++------
utils/link.go | 116 +++++++++
utils/pool.go | 18 +-
utils/util.go | 96 +-------
web/controllers/base.go | 3 +-
web/controllers/client.go | 2 -
web/controllers/index.go | 2 -
web/views/client/add.html | 7 -
web/views/client/edit.html | 7 -
web/views/client/list.html | 16 +-
web/views/index/add.html | 7 -
web/views/index/edit.html | 7 -
web/views/index/help.html | 69 +++---
web/views/index/hlist.html | 16 +-
web/views/index/list.html | 23 +-
32 files changed, 1106 insertions(+), 1140 deletions(-)
create mode 100644 server/http.go
delete mode 100644 server/process.go
create mode 100644 utils/link.go
diff --git a/README.md b/README.md
index 269d8e6..ceec2ba 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
目前市面上提供类似服务的有花生壳、TeamView、GoToMyCloud等等,但要使用第三方的公网服务器就必须为第三方付费,并且这些服务都有各种各样的限制,此外,由于数据包会流经第三方,因此对数据安全也是一大隐患。
-支持客户端与服务端连接中断自动重连,多路传输,大大的提高请求处理速度,go语言编写,无第三方依赖,各个平台都已经编译在release中,普通个人场景下,内存使用量在10M以下。
+go语言编写,无第三方依赖,各个平台都已经编译在release中。
## 背景

@@ -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,14 +53,18 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
* [带宽限制](#带宽限制)
* [相关说明](#相关说明)
* [流量统计](#流量统计)
- * [连接池](#连接池)
* [热更新支持](#热更新支持)
* [获取用户真实ip](#获取用户真实ip)
* [客户端地址显示](#客户端地址显示)
+* [简单的性能测试](#简单的性能测试)
+ * [qps](#qps)
+ * [速度测试](#速度测试)
+ * [内存和cpu](#内存和cpu)
+ * [额外消耗连接数](#额外消耗连接数)
* [webAPI](#webAPI)
* [客户端](#客户端)
- * [域名代理](#域名代理)
- * [其他代理](#其他代理)
+ * [域名代理](#域名代理)
+ * [其他代理](#其他代理)
## 安装
@@ -78,16 +87,8 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**

### 介绍
-可在网页上配置和管理各个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
+
+### 速度测试
+**测试环境:** 1M带宽云服务器,理论125kb/s,带宽与代理无关,与服务器关系较大。
+
+
+
+### 内存和cpu
+
+**1000次性能测试后**
+
+
+**启动时**
+
+
+### 额外消耗连接数
+为了最大化的提升效率和并发,客户端与服务端之间仅两条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
\ No newline at end of file
+为方便第三方扩展,在web模式下可利用webAPI进行相关操作,详情见
+[webAPI文档](https://github.com/cnlh/easyProxy/wiki/webAPI%E6%96%87%E6%A1%A3)
\ No newline at end of file
diff --git a/bridge/bridge.go b/bridge/bridge.go
index fc41fa1..5e541ab 100755
--- a/bridge/bridge.go
+++ b/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 //隧道
+ TunnelPort int //通信隧道端口
+ listener *net.TCPListener //server端监听
+ 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
+ }
+ v.Unlock()
+ } else {
+ s.clientLock.Unlock()
+ }
+ case <-stop:
+ return false
+ }
}
- s.lock.Unlock()
+ 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("客户端未连接")
- return
+ } else {
+ conn = v.signal
}
- 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
+ }
+ }
+ }
+
+}
diff --git a/client/client.go b/client/client.go
index adeca64..2bcbe7c 100755
--- a/client/client.go
+++ b/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
+ svrAddr string
+ 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)
- for {
- select {
- case <-s.tunnel:
- case <-t.C:
- }
- if s.serverStatus && s.tunnelNum < 5 {
- go s.dealChan()
+ 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.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)
-}
diff --git a/cmd/proxy_client/proxy_client.go b/cmd/proxy_client/proxy_client.go
index 56a20d4..88e5e08 100644
--- a/cmd/proxy_client/proxy_client.go
+++ b/cmd/proxy_client/proxy_client.go
@@ -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
}
diff --git a/cmd/proxy_server/proxy_server.go b/cmd/proxy_server/proxy_server.go
index 74d055b..f031c4e 100644
--- a/cmd/proxy_server/proxy_server.go
+++ b/cmd/proxy_server/proxy_server.go
@@ -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,
diff --git a/conf/app.conf b/conf/app.conf
index 1066958..c1d21c0 100755
--- a/conf/app.conf
+++ b/conf/app.conf
@@ -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
diff --git a/conf/clients.csv b/conf/clients.csv
index 6b86fdc..d22da32 100644
--- a/conf/clients.csv
+++ b/conf/clients.csv
@@ -1 +1 @@
-1,wuz1nozs9dhtxic6,,true,,,0,0,,0,1
+1,wajajb6bl086s2aq,111,true,1,2,1,,20000,1000
diff --git a/conf/hosts.csv b/conf/hosts.csv
index d45288c..3c0d73a 100644
--- a/conf/hosts.csv
+++ b/conf/hosts.csv
@@ -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,测试
diff --git a/conf/tasks.csv b/conf/tasks.csv
index d5d7e38..0d7a03f 100644
--- a/conf/tasks.csv
+++ b/conf/tasks.csv
@@ -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,
diff --git a/server/base.go b/server/base.go
index d1c908a..e5cf236 100644
--- a/server/base.go
+++ b/server/base.go
@@ -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 {
- bridge *bridge.Bridge
- task *utils.Tunnel
- config *utils.Config
+ 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
+}
diff --git a/server/http.go b/server/http.go
new file mode 100644
index 0000000..be2ccf9
--- /dev/null
+++ b/server/http.go
@@ -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)),
+ }
+}
diff --git a/server/process.go b/server/process.go
deleted file mode 100644
index c7341a1..0000000
--- a/server/process.go
+++ /dev/null
@@ -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
-}
diff --git a/server/server.go b/server/server.go
index c9808ca..3cdabb6 100644
--- a/server/server.go
+++ b/server/server.go
@@ -2,27 +2,17 @@ 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{} //运行中的任务
- CsvDb = utils.GetCsvDb()
+ Bridge *bridge.Bridge
+ RunList map[int]interface{} //运行中的任务
+ CsvDb = utils.GetCsvDb()
)
func init() {
@@ -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 {
diff --git a/server/socks5.go b/server/socks5.go
index 6ce3597..3d92707 100755
--- a/server/socks5.go
+++ b/server/socks5.go
@@ -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
- }
- s.sendReply(c, succeeded)
- var flag string
- if flag, err = proxyConn.ReadFlag(); err == nil {
- if flag != utils.CONN_SUCCESS {
- err = errors.New("conn failed")
- }
+ } else {
+ s.sendReply(c, succeeded)
+ 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
diff --git a/server/tcp.go b/server/tcp.go
index a1df0e0..5026294 100755
--- a/server/tcp.go
+++ b/server/tcp.go
@@ -2,21 +2,18 @@ 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
+ process process
+ listener *net.TCPListener
}
//tcp|http|host
@@ -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)
+}
diff --git a/server/udp.go b/server/udp.go
index 7958113..9557130 100755
--- a/server/udp.go
+++ b/server/udp.go
@@ -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)
- }
+ } else {
+ s.task.Flow.Add(len(data), 0)
+ tunnel.SendMsg(data, link)
}
}
diff --git a/utils/conn.go b/utils/conn.go
index 5f539b3..0876f5f 100755
--- a/utils/conn.go
+++ b/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,24 +262,112 @@ 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)))
-}
-
-//获取压缩方式,是否加密
-func (s *Conn) GetConnInfoFromConn() (en, de int, crypt, mux bool) {
- buf, err := s.ReadLen(4)
- if err != nil {
+//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
}
- en, _ = strconv.Atoi(string(buf[0]))
- de, _ = strconv.Atoi(string(buf[1]))
- crypt = GetBoolByStr(string(buf[2]))
- mux = GetBoolByStr(string(buf[3]))
+ raw.Reset()
+ binary.Write(raw, binary.LittleEndian, content)
+ n, err = s.WriteTo(raw.Bytes(), link.En, link.Crypt, link.Rate)
return
}
+//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
+}
+
+//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 {
return s.Conn.Close()
@@ -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{})
diff --git a/utils/file.go b/utils/file.go
index 2a89d38..8b2fca6 100644
--- a/utils/file.go
+++ b/utils/file.go
@@ -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,
- }
-}
diff --git a/utils/link.go b/utils/link.go
new file mode 100644
index 0000000..33a468d
--- /dev/null
+++ b/utils/link.go
@@ -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,
+ }
+}
diff --git a/utils/pool.go b/utils/pool.go
index 56c2b4d..26ad716 100644
--- a/utils/pool.go
+++ b/utils/pool.go
@@ -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])
+ }
+}
diff --git a/utils/util.go b/utils/util.go
index b49d12e..33df543 100755
--- a/utils/util.go
+++ b/utils/util.go
@@ -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
}
diff --git a/web/controllers/base.go b/web/controllers/base.go
index f4c5097..017f528 100755
--- a/web/controllers/base.go
+++ b/web/controllers/base.go
@@ -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")
diff --git a/web/controllers/client.go b/web/controllers/client.go
index 935e126..6f8eb43 100644
--- a/web/controllers/client.go
+++ b/web/controllers/client.go
@@ -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 {
diff --git a/web/controllers/index.go b/web/controllers/index.go
index ec52003..93da245 100755
--- a/web/controllers/index.go
+++ b/web/controllers/index.go
@@ -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 {
diff --git a/web/views/client/add.html b/web/views/client/add.html
index f567d93..d3b651c 100755
--- a/web/views/client/add.html
+++ b/web/views/client/add.html
@@ -38,13 +38,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/web/views/index/edit.html b/web/views/index/edit.html
index e3865cf..670f603 100755
--- a/web/views/index/edit.html
+++ b/web/views/index/edit.html
@@ -55,13 +55,6 @@
-
-
-
-
@@ -61,10 +51,13 @@
使用步骤:
- - 在tcp隧道管理中添加一条隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),选择压缩方式,保存。 立即添加
- {{/*- 在tcp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令
*/}}
- - 访问公网服务器ip({{.ip}}):填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:ssh -p 8001 root@{{.ip}}
+ - 在客户端管理中创建一个客户端,记录下验证密钥
+ - 内网客户端运行
+ ./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥
+
+
+ - 在该客户端隧道管理中添加一条tcp隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),选择压缩方式,保存。
+ - 访问公网服务器ip({{.ip}}),填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:ssh -p 8001 root@{{.ip}}
注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动
@@ -80,9 +73,12 @@
使用步骤:
- - 在udp隧道管理中添加一条隧道,填写监听的端口(8002)、内网目标ip和目标端口(10.1.50.102:53),选择压缩方式,保存。 立即添加
- {{/*- 在udp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令
*/}}
+ - 在客户端管理中创建一个客户端,记录下验证密钥
+ - 内网客户端运行
+ ./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥
+
+
+ - 在该客户端的隧道管理中添加一条udp隧道,填写监听的端口(53)、内网目标ip和目标端口(10.1.50.102:53),选择压缩方式,保存。
- 修改本机dns为{{.ip}},则相当于使用10.1.50.202作为dns服务器
注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动
@@ -101,9 +97,12 @@
使用步骤:
- - 在socks5隧道管理中添加一条隧道,填写监听的端口(8003),验证用户名和密码自行选择(建议先不填,部分客户端不支持,proxifer支持),选择压缩方式,保存。 立即添加
- {{/*- 在socks5代理管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令
*/}}
+ - 在客户端管理中创建一个客户端,记录下验证密钥
+ - 内网客户端运行
+ ./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥
+
+
+ - 在该客户端隧道管理中添加一条socks5代理,填写监听的端口(8003),验证用户名和密码自行选择(建议先不填,部分客户端不支持,proxifer支持),选择压缩方式,保存。
- 在外网环境的本机配置socks5代理,ip为公网服务器ip({{.ip}}),端口为填写的监听端口(8003),即可畅享内网了
注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动
@@ -120,13 +119,23 @@
使用步骤:
- - 在http隧道管理中添加一条隧道,填写监听的端口(8004),选择压缩方式,保存。 立即添加
- - 在http代理管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令
+ - 在客户端管理中创建一个客户端,记录下验证密钥
+ - 内网客户端运行
+ ./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥
+
+
+ - 在该客户端隧道管理中添加一条http代理,填写监听的端口(8004),选择压缩方式,保存。
- 在外网环境的本机配置http代理,ip为公网服务器ip({{.ip}}),端口为填写的监听端口(8004),即可访问了
注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动
+
+
+
diff --git a/web/views/index/hlist.html b/web/views/index/hlist.html
index 9c70763..59168ad 100755
--- a/web/views/index/hlist.html
+++ b/web/views/index/hlist.html
@@ -12,7 +12,6 @@
host改写 |
压缩方式 |
加密 |
- 多路复用 |
用户名 |
密码 |
出口流量 |
@@ -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
}
diff --git a/web/views/index/list.html b/web/views/index/list.html
index 5a8fd56..cf61218 100755
--- a/web/views/index/list.html
+++ b/web/views/index/list.html
@@ -12,7 +12,6 @@
内网目标 |
压缩方式 |
加密传输 |
- TCP多路复用 |
用户名 |
密码 |
客户端状态 |
@@ -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) {