diff --git a/README.md b/README.md
index 09ef27b..e56891a 100644
--- a/README.md
+++ b/README.md
@@ -11,19 +11,17 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**

1. web管理模式,可配置多条tcp、udp隧道,多个域名代理等等----> [web管理模式](#web管理模式)
-2. 内网多站点配合代理。----> [http反向代理请求](#http代理请求)
-3. 想在外网通过ssh连接内网的机器,做云服务器到内网服务器端口的映射,或者做微信公众号开发、小程序开发等---->[tcp隧道模式](#tcp隧道模式)
+2. 想在外网通过ssh连接内网的机器,做云服务器到内网服务器端口的映射,或者做微信公众号开发、小程序开发等---->[tcp隧道模式](#tcp隧道模式)
-4. 在非内网环境下使用内网dns,或者需要通过udp访问内网机器等---->[udp隧道模式](#udp隧道模式)
+3. 在非内网环境下使用内网dns,或者需要通过udp访问内网机器等---->[udp隧道模式](#udp隧道模式)
-5. 在外网使用HTTP代理访问内网站点---->[http代理模式](#http代理模式)
+4. 在外网使用HTTP代理访问内网站点---->[http代理模式](#http代理模式)
-6. 搭建一个内网穿透ss,在外网如同使用内网vpn一样访问内网资源或者设备----> [socks5代理模式](#socks5代理模式)
+5. 搭建一个内网穿透ss,在外网如同使用内网vpn一样访问内网资源或者设备----> [socks5代理模式](#socks5代理模式)
## 特点
- [x] 支持snappy压缩,减小传输过程流量消耗
-- [x] 支持多站点配置,兼容多个内网网站,可处理相互之间的跳转包含关系
- [x] 断线自动重连
- [x] 支持多路传输,提高并发
- [x] 跨站自动匹配替换
@@ -44,13 +42,12 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
2. [web管理模式](#web管理模式)(多隧道时推荐)
3. [tcp隧道模式](#tcp隧道模式)
4. [udp隧道模式](#udp隧道模式)
-5. [http反向代理请求](#http代理请求)
-6. [socks5代理模式](#socks5代理模式)
-7. [http代理模式](#http代理模式)
-8. [数据压缩支持](#数据压缩支持)
-9. [站点密码保护](#站点保护)
-10. [加密传输](#加密传输)
-11. [TCP多路复用](#多路复用)
+5. [socks5代理模式](#socks5代理模式)
+6. [http代理模式](#http代理模式)
+7. [数据压缩支持](#数据压缩支持)
+8. [站点密码保护](#站点保护)
+9. [加密传输](#加密传输)
+10. [TCP多路复用](#多路复用)
11. [配置文件说明](#配置文件)
## 安装
@@ -58,13 +55,14 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
1. release安装
> https://github.com/cnlh/easyProxy/releases
-下载对应的系统版本即可,服务端和客户端共用一个程序,go语言开发,无需任何第三方依赖
+下载对应的系统版本即可,服务端和客户端是单独的,go语言开发,无需任何第三方依赖
2. 源码安装
- 安装源码
> go get github.com/cnlh/easyProxy
- 编译
-> go build
+> go build cmd/proxy_server.go
+> go build cmd/proxy_client.go
## web管理模式
@@ -72,6 +70,9 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
### 介绍
可在网页上配置和管理各个tcp、udp隧道、内网站点代理等等,功能极为强大,操作也非常方便。
+
+**提示:使用web模式时,服务端执行文件必须在项目根目录,否则无法正确加载配置文件**
+
### 使用
**有两种模式:**
@@ -218,116 +219,7 @@ target | 目标地址,格式如上
./easyProxy -server=ip:port -vkey=DKibZF5TXvic1g3kY
```
-## http代理请求
-### 场景及原理
-
-较为适用于http,也就是web站点的穿透,服务端与客户端之间建立连接,服务端收到http请求后,将请求发送到客户端,客户端再执行这个请求,并将结果返回给服务端,服务端收到后再返回。
-
-
-特点:支持同时代理多个站点,不同站点之间有联系还可以实现匹配替换
-
-
-
-
-**最终效果**:
-- 访问a.server.com和访问10.1.50.203的80端口相同
-- 访问b.server.com和访问10.1.50.202的80端口相同
-- 访问c.server.com和访问10.1.50.201的80端口相同
-### 使用
-- 服务端
-
-```
-./easyProxy -mode=httpServer -vkey=DKibZF5TXvic1g3kY -tcpport=8284 -httpport=8024
-```
-
-名称 | 含义
----|---
-mode | 运行模式
-vkey | 验证密钥
-tcpport | 服务端与客户端通信端口
-httpport | 代理的http端口(与nginx配合使用)
-
-- 客户端
-
-```
-建立配置文件 config.json
-```
-
-
-```
-./easyProxy -server=ip:port -config=config.json -vkey=DKibZF5TXvic1g3kY
-```
-
-
- 名称 | 含义
----|---
-config | 配置文件路径
-### 配置文件config.json
-
-```
-{
- "SiteList": [
- {
- "host": "a.ourcauc.com",
- "url": "10.1.50.203",
- "port": 80
- },
- {
- "host": "b.ourcauc.com",
- "url": "10.1.50.202",
- "port": 80
- },
- {
- "host": "c.ourcauc.com",
- "url": "10.1.50.203",
- "port": 80
- }
- ],
- "Replace": 0
-}
-```
- 名称 | 含义
----|---
-SiteList | 本地解析的域名列表
-host | 域名地址
-url | 内网代理的地址
-port | 内网代理的地址对应的端口
-Replace | 是否自动匹配替换[(查看场景)](https://github.com/cnlh/easyProxy/issues/1)
-
-
-### nginx代理配置示例
-```
-server {
- listen 80;
- server_name a.ourcauc.com b.ourcauc.com c.ourcauc.com ;
- location / {
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $http_host;
- proxy_set_header X-Nginx-Proxy true;
- proxy_set_header Connection "";
- proxy_pass http://127.0.0.1:8024;
- }
-}
-```
-## 域名配置示例
-> -a A 123.206.77.88
-
-> -b A 123.206.77.88
-
-> -c A 123.206.77.88
-
-### 跨站自动匹配替换说明
-
-例如,访问:a.ourcauc.com,该页面里面有一个超链接为10.1.50.202:80,将根据配置文件自动该将url替换为b.ourcauc.com,以达到跨站也可访问的效果,但需要提前在配置文件中配置这些站点。
-
-如需开启,请加配置文件Replace值设置为1
->注意:开启可能导致不应该被替换的内容被替换,请谨慎开启
-
-### 二级域名示范
-
-[二级域名](https://github.com/cnlh/easyProxy/wiki/%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B)
## socks5代理模式
@@ -422,9 +314,9 @@ httpport | http代理连接端口
- 所有模式均支持数据压缩,可以与加密同时使用
-- 在server端加上参数 -compress=snappy(或在web中设置),例如在TCP隧道模式
+- 在server端加上参数 -compress=snappy(或在web管理中设置)
```
-./easyProxy -mode=tunnelServer -vkey=DKibZF5TXvic1g3kY -tcpport=8284 -httpport=8024 -target=10.1.50.203:80 -compress=snappy
+-compress=snappy
```
## 加密传输
diff --git a/bridge/bridge.go b/bridge/bridge.go
new file mode 100755
index 0000000..5efc80b
--- /dev/null
+++ b/bridge/bridge.go
@@ -0,0 +1,241 @@
+package bridge
+
+import (
+ "errors"
+ "github.com/cnlh/easyProxy/utils"
+ "log"
+ "net"
+ "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 Tunnel struct {
+ TunnelPort int //通信隧道端口
+ listener *net.TCPListener //server端监听
+ SignalList map[string]*list //通信
+ TunnelList map[string]*list //隧道
+ RunList map[string]interface{} //运行中的任务
+ lock sync.Mutex
+ tunnelLock sync.Mutex
+}
+
+func NewTunnel(tunnelPort int, runList map[string]interface{}) *Tunnel {
+ t := new(Tunnel)
+ t.TunnelPort = tunnelPort
+ t.SignalList = make(map[string]*list)
+ t.TunnelList = make(map[string]*list)
+ t.RunList = runList
+ return t
+}
+
+func (s *Tunnel) StartTunnel() error {
+ var err error
+ s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.TunnelPort, ""})
+ if err != nil {
+ return err
+ }
+ go s.tunnelProcess()
+ return nil
+}
+
+//tcp server
+func (s *Tunnel) tunnelProcess() error {
+ var err error
+ for {
+ conn, err := s.listener.Accept()
+ if err != nil {
+ log.Println(err)
+ continue
+ }
+ go s.cliProcess(utils.NewConn(conn))
+ }
+ return err
+}
+
+//验证失败,返回错误验证flag,并且关闭连接
+func (s *Tunnel) verifyError(c *utils.Conn) {
+ c.Conn.Write([]byte(utils.VERIFY_EER))
+ c.Conn.Close()
+}
+
+func (s *Tunnel) cliProcess(c *utils.Conn) error {
+ c.Conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(5) * time.Second))
+ vval := make([]byte, 32)
+ if _, err := c.Conn.Read(vval); err != nil {
+ log.Println("客户端读超时。客户端地址为::", c.Conn.RemoteAddr())
+ c.Conn.Close()
+ return err
+ }
+ if !s.verify(string(vval)) {
+ log.Println("当前客户端连接校验错误,关闭此客户端:", c.Conn.RemoteAddr())
+ s.verifyError(c)
+ return errors.New("验证错误")
+ }
+ log.Println("客户端连接成功: ", c.Conn.RemoteAddr())
+ c.Conn.(*net.TCPConn).SetReadDeadline(time.Time{})
+ //做一个判断 添加到对应的channel里面以供使用
+ if flag, err := c.ReadFlag(); err != nil {
+ return err
+ } else {
+ return s.typeDeal(flag, c, string(vval))
+ }
+}
+
+//tcp连接类型区分
+func (s *Tunnel) typeDeal(typeVal string, c *utils.Conn, cFlag string) error {
+ switch typeVal {
+ case utils.WORK_MAIN:
+ s.addList(s.SignalList, c, cFlag)
+ case utils.WORK_CHAN:
+ s.addList(s.TunnelList, c, cFlag)
+ default:
+ return errors.New("无法识别")
+ }
+ c.SetAlive()
+ return nil
+}
+
+//加到对应的list中
+func (s *Tunnel) addList(m map[string]*list, c *utils.Conn, cFlag string) {
+ s.lock.Lock()
+ if v, ok := m[cFlag]; ok {
+ v.Add(c)
+ } else {
+ l := newList()
+ l.Add(c)
+ m[cFlag] = l
+ }
+ s.lock.Unlock()
+}
+
+//新建隧道
+func (s *Tunnel) newChan(cFlag string) error {
+ if err := s.wait(s.SignalList, cFlag); err != nil {
+ return err
+ }
+retry:
+ connPass := s.SignalList[cFlag].Pop()
+ _, err := connPass.Conn.Write([]byte("chan"))
+ if err != nil {
+ log.Println(err)
+ goto retry
+ }
+ s.SignalList[cFlag].Add(connPass)
+ return nil
+}
+
+//得到一个tcp隧道
+func (s *Tunnel) GetTunnel(cFlag string, en, de int, crypt, mux bool) (c *utils.Conn, err error) {
+ s.tunnelLock.Lock()
+ if v, ok := s.TunnelList[cFlag]; !ok || v.Len() < 3 { //新建通道
+ go s.newChan(cFlag)
+ }
+retry:
+ if err = s.wait(s.TunnelList, cFlag); err != nil {
+ return
+ }
+ c = s.TunnelList[cFlag].Pop()
+ if _, err = c.WriteTest(); err != nil {
+ c.Close()
+ goto retry
+ }
+ c.WriteConnInfo(en, de, crypt, mux)
+ s.tunnelLock.Unlock()
+ return
+}
+
+//得到一个通信通道
+func (s *Tunnel) GetSignal(cFlag string) (err error, conn *utils.Conn) {
+ if v, ok := s.SignalList[cFlag]; !ok || v.Len() == 0 {
+ err = errors.New("客户端未连接")
+ return
+ }
+ conn = s.SignalList[cFlag].Pop()
+ return
+}
+
+//重回slice 复用
+func (s *Tunnel) ReturnSignal(conn *utils.Conn, cFlag string) {
+ if v, ok := s.SignalList[cFlag]; ok {
+ v.Add(conn)
+ }
+}
+
+//重回slice 复用
+func (s *Tunnel) ReturnTunnel(conn *utils.Conn, cFlag string) {
+ if v, ok := s.TunnelList[cFlag]; ok {
+ utils.FlushConn(conn.Conn)
+ v.Add(conn)
+ }
+}
+
+//删除通信通道
+func (s *Tunnel) DelClientSignal(cFlag string) {
+ s.delClient(cFlag, s.SignalList)
+}
+
+//删除隧道
+func (s *Tunnel) DelClientTunnel(cFlag string) {
+ s.delClient(cFlag, s.TunnelList)
+}
+
+func (s *Tunnel) delClient(cFlag string, l map[string]*list) {
+ if t := l[utils.Getverifyval(cFlag)]; t != nil {
+ for {
+ if t.Len() <= 0 {
+ break
+ }
+ t.Pop().Close()
+ }
+ delete(l, utils.Getverifyval(cFlag))
+ }
+}
+
+//等待
+func (s *Tunnel) wait(m map[string]*list, cFlag string) error {
+ ticker := time.NewTicker(time.Millisecond * 100)
+ stop := time.After(time.Second * 10)
+loop:
+ for {
+ select {
+ case <-ticker.C:
+ if _, ok := m[cFlag]; ok {
+ ticker.Stop()
+ break loop
+ }
+ case <-stop:
+ return errors.New("client key: " + cFlag + ",err: get client conn timeout")
+ }
+ }
+ return nil
+}
+
+func (s *Tunnel) verify(vKeyMd5 string) bool {
+ for k := range s.RunList {
+ if utils.Getverifyval(k) == vKeyMd5 {
+ return true
+ }
+ }
+ return false
+}
diff --git a/lib/client.go b/client/client.go
similarity index 56%
rename from lib/client.go
rename to client/client.go
index 5aa4d81..a074e37 100755
--- a/lib/client.go
+++ b/client/client.go
@@ -1,8 +1,7 @@
-package lib
+package client
import (
- "errors"
- "fmt"
+ "github.com/cnlh/easyProxy/utils"
"log"
"net"
"sync"
@@ -28,54 +27,49 @@ func NewRPClient(svraddr string, tcpNum int, vKey string) *TRPClient {
//start
func (s *TRPClient) Start() error {
for i := 0; i < s.tcpNum; i++ {
- go s.newConn()
+ go s.NewConn()
}
return nil
}
//新建
-func (s *TRPClient) newConn() error {
+func (s *TRPClient) NewConn() error {
s.Lock()
conn, err := net.Dial("tcp", s.svrAddr)
if err != nil {
log.Println("连接服务端失败,五秒后将重连")
time.Sleep(time.Second * 5)
s.Unlock()
- go s.newConn()
+ go s.NewConn()
return err
}
s.Unlock()
- return s.process(NewConn(conn))
+ return s.process(utils.NewConn(conn))
}
//处理
-func (s *TRPClient) process(c *Conn) error {
+func (s *TRPClient) process(c *utils.Conn) error {
c.SetAlive()
- if _, err := c.Write([]byte(getverifyval(s.vKey))); err != nil {
+ if _, err := c.Write([]byte(utils.Getverifyval(s.vKey))); err != nil {
return err
}
- c.wMain()
+ c.WriteMain()
for {
flags, err := c.ReadFlag()
if err != nil {
log.Println("服务端断开,五秒后将重连", err)
time.Sleep(5 * time.Second)
- go s.newConn()
+ go s.NewConn()
break
}
switch flags {
- case VERIFY_EER:
+ case utils.VERIFY_EER:
log.Fatalln("vkey:", s.vKey, "不正确,服务端拒绝连接,请检查")
- case RES_SIGN: //代理请求模式
- if err := s.dealHttp(c); err != nil {
- log.Println(err)
- return err
- }
- case WORK_CHAN: //隧道模式,每次开启10个,加快连接速度
+ case utils.WORK_CHAN: //隧道模式,每次开启10个,加快连接速度
for i := 0; i < 5; i++ {
go s.dealChan()
}
- case RES_MSG:
+ case utils.RES_MSG:
log.Println("服务端返回错误。")
default:
log.Println("无法解析该错误。", flags)
@@ -94,15 +88,15 @@ func (s *TRPClient) dealChan() {
return
}
//验证
- if _, err := conn.Write([]byte(getverifyval(s.vKey))); err != nil {
+ if _, err := conn.Write([]byte(utils.Getverifyval(s.vKey))); err != nil {
log.Println("connect to ", s.svrAddr, "error:", err)
return
}
//默认长连接保持
- c := NewConn(conn)
+ c := utils.NewConn(conn)
c.SetAlive()
//写标志
- c.wChan()
+ c.WriteChan()
re:
//获取连接的host type(tcp or udp)
typeStr, host, en, de, crypt, mux, err := c.GetHostFromConn()
@@ -111,51 +105,24 @@ re:
c.Close()
return
}
- //与目标建立连接,超时时间为3
- server, err := net.DialTimeout(typeStr, host, time.Second*3)
- if err != nil {
- log.Println("connect to ", host, "error:", err, mux)
- c.wFail()
- goto end
- }
- c.wSuccess()
- go relay(server, c.conn, de, crypt, mux)
- relay(c.conn, server, en, crypt, mux)
-end:
+ Process(c, typeStr, host, en, de, crypt, mux)
if mux {
- FlushConn(conn)
+ utils.FlushConn(conn)
goto re
} else {
c.Close()
}
}
-//http模式处理
-func (s *TRPClient) dealHttp(c *Conn) error {
- buf := make([]byte, 1024*32)
- en, de, crypt, _ := c.GetConnInfoFromConn()
- n, err := c.ReadFrom(buf, de, crypt)
+func Process(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 {
- c.wError()
- return err
+ log.Println("connect to ", host, "error:", err, mux)
+ c.WriteFail()
+ return
}
- req, err := DecodeRequest(buf[:n])
- if err != nil {
- c.wError()
- return err
- }
- respBytes, err := GetEncodeResponse(req)
- if err != nil {
- c.wError()
- return err
- }
- c.wSign()
- n, err = c.WriteTo(respBytes, en, crypt)
- if err != nil {
- return err
- }
- if n != len(respBytes) {
- return errors.New(fmt.Sprintf("发送数据长度错误,已经发送:%dbyte,总字节长:%dbyte\n", n, len(respBytes)))
- }
- return nil
+ c.WriteSuccess()
+ go utils.Relay(server, c.Conn, de, crypt, mux)
+ utils.Relay(c.Conn, server, en, crypt, mux)
}
diff --git a/cmd/proxy_client.go b/cmd/proxy_client.go
new file mode 100644
index 0000000..fd9b513
--- /dev/null
+++ b/cmd/proxy_client.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "flag"
+ "github.com/cnlh/easyProxy/client"
+ "log"
+ "strings"
+)
+
+var (
+ serverAddr = flag.String("server", "", "服务器地址ip:端口")
+ verifyKey = flag.String("vkey", "", "验证密钥")
+)
+
+
+func main() {
+ flag.Parse()
+ //go func() {
+ // http.ListenAndServe("0.0.0.0:8899", nil)
+ //}()
+ stop := make(chan int)
+ for _, v := range strings.Split(*verifyKey, ",") {
+ log.Println("客户端启动,连接:", *serverAddr, " 验证令牌:", v)
+ go client.NewRPClient(*serverAddr, 1, v).Start()
+ }
+ <-stop
+}
diff --git a/cmd/proxy_server.go b/cmd/proxy_server.go
new file mode 100644
index 0000000..0047cb6
--- /dev/null
+++ b/cmd/proxy_server.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+ "flag"
+ "github.com/cnlh/easyProxy/server"
+ "github.com/cnlh/easyProxy/utils"
+ _ "github.com/cnlh/easyProxy/web/routers"
+ "log"
+)
+
+var (
+ configPath = flag.String("config", "config.json", "配置文件路径")
+ TcpPort = flag.Int("tcpport", 8284, "客户端与服务端通信端口")
+ httpPort = flag.Int("httpport", 8024, "对外监听的端口")
+ rpMode = flag.String("mode", "client", "启动模式")
+ tunnelTarget = flag.String("target", "10.1.50.203:80", "远程目标")
+ VerifyKey = flag.String("vkey", "", "验证密钥")
+ u = flag.String("u", "", "验证用户名(socks5和web)")
+ 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() {
+ flag.Parse()
+ server.VerifyKey = *VerifyKey
+ log.Println("服务端启动,监听tcp服务端端口:", *TcpPort)
+ cnf := server.ServerConfig{
+ TcpPort: *httpPort,
+ Mode: *rpMode,
+ Target: *tunnelTarget,
+ VerifyKey: *VerifyKey,
+ U: *u,
+ P: *p,
+ Compress: *compress,
+ Start: 0,
+ IsRun: 0,
+ ClientStatus: 0,
+ Crypt: utils.GetBoolByStr(*crypt),
+ Mux: utils.GetBoolByStr(*mux),
+ CompressEncode: 0,
+ CompressDecode: 0,
+ }
+ cnf.CompressDecode, cnf.CompressEncode = utils.GetCompressType(cnf.Compress)
+ server.StartNewServer(*TcpPort, &cnf)
+}
diff --git a/conf/app.conf b/conf/app.conf
index f28fad1..7451221 100755
--- a/conf/app.conf
+++ b/conf/app.conf
@@ -4,7 +4,7 @@ appname = easyProxy
httpport = 8080
#启动模式dev|pro
-runmode = pro
+runmode = dev
#web管理密码
password=123
diff --git a/conf/hosts.csv b/conf/hosts.csv
index 30ba681..db883eb 100644
--- a/conf/hosts.csv
+++ b/conf/hosts.csv
@@ -1,3 +1,3 @@
b.proxy.com,127.0.0.1:82,o2430bnq22jgnmcl
b.o.com,127.0.0.1:88,ts08z6vk5nc72fs8
-a.o.com,127.0.0.1:88,ts08z6vk5nc72fs8
+a.o.com,127.0.0.1:88,7n7bxc2bm1fyjfab
diff --git a/conf/tasks.csv b/conf/tasks.csv
index 8c3e4e7..71a3970 100644
--- a/conf/tasks.csv
+++ b/conf/tasks.csv
@@ -1,3 +1,5 @@
8001,tunnelServer,127.0.0.1:88,jq5i7n0sjs1h0jje,aaa,bbb,,1,0,0,0,1
-0,hostServer,,ts08z6vk5nc72fs8,aaac,bbb,snappy,1,0,1,2,3
0,hostServer,,7n7bxc2bm1fyjfab,ab,b,,1,1,1,0,1
+0,hostServer,,ts08z6vk5nc72fs8,aaa,bbb,snappy,1,0,1,2,3
+8002,tunnelServer,127.0.0.1:88,2nxo93wvotb9g75s,,,,1,0,0,0,1
+8025,socks5Server,,2p3qs71oym3zx52w,,,,1,0,0,0,1
diff --git a/lib/bridge.go b/lib/bridge.go
deleted file mode 100755
index 9c368f7..0000000
--- a/lib/bridge.go
+++ /dev/null
@@ -1,228 +0,0 @@
-package lib
-
-import (
- "errors"
- "log"
- "net"
- "sync"
- "time"
-)
-
-type list struct {
- connList chan *Conn
-}
-
-func (l *list) Add(c *Conn) {
- l.connList <- c
-}
-
-func (l *list) Pop() *Conn {
- return <-l.connList
-}
-func (l *list) Len() int {
- return len(l.connList)
-}
-
-func newList() *list {
- l := new(list)
- l.connList = make(chan *Conn, 1000)
- return l
-}
-
-type Tunnel struct {
- tunnelPort int //通信隧道端口
- listener *net.TCPListener //server端监听
- signalList map[string]*list //通信
- tunnelList map[string]*list //隧道
- lock sync.Mutex
- tunnelLock sync.Mutex
-}
-
-func newTunnel(tunnelPort int) *Tunnel {
- t := new(Tunnel)
- t.tunnelPort = tunnelPort
- t.signalList = make(map[string]*list)
- t.tunnelList = make(map[string]*list)
- return t
-}
-
-func (s *Tunnel) StartTunnel() error {
- var err error
- s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.tunnelPort, ""})
- if err != nil {
- return err
- }
- go s.tunnelProcess()
- return nil
-}
-
-//tcp server
-func (s *Tunnel) tunnelProcess() error {
- var err error
- for {
- conn, err := s.listener.Accept()
- if err != nil {
- log.Println(err)
- continue
- }
- go s.cliProcess(NewConn(conn))
- }
- return err
-}
-
-//验证失败,返回错误验证flag,并且关闭连接
-func (s *Tunnel) verifyError(c *Conn) {
- c.conn.Write([]byte(VERIFY_EER))
- c.conn.Close()
-}
-
-func (s *Tunnel) cliProcess(c *Conn) error {
- c.conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(5) * time.Second))
- vval := make([]byte, 32)
- if _, err := c.conn.Read(vval); err != nil {
- log.Println("客户端读超时。客户端地址为::", c.conn.RemoteAddr())
- c.conn.Close()
- return err
- }
- if !verify(string(vval)) {
- log.Println("当前客户端连接校验错误,关闭此客户端:", c.conn.RemoteAddr())
- s.verifyError(c)
- return err
- }
- c.conn.(*net.TCPConn).SetReadDeadline(time.Time{})
- //做一个判断 添加到对应的channel里面以供使用
- if flag, err := c.ReadFlag(); err != nil {
- return err
- } else {
- return s.typeDeal(flag, c, string(vval))
- }
-}
-
-//tcp连接类型区分
-func (s *Tunnel) typeDeal(typeVal string, c *Conn, cFlag string) error {
- switch typeVal {
- case WORK_MAIN:
- s.addList(s.signalList, c, cFlag)
- case WORK_CHAN:
- s.addList(s.tunnelList, c, cFlag)
- default:
- return errors.New("无法识别")
- }
- c.SetAlive()
- return nil
-}
-
-//加到对应的list中
-func (s *Tunnel) addList(m map[string]*list, c *Conn, cFlag string) {
- s.lock.Lock()
- if v, ok := m[cFlag]; ok {
- v.Add(c)
- } else {
- l := newList()
- l.Add(c)
- m[cFlag] = l
- }
- s.lock.Unlock()
-}
-
-//新建隧道
-func (s *Tunnel) newChan(cFlag string) error {
- if err := s.wait(s.signalList, cFlag); err != nil {
- return err
- }
-retry:
- connPass := s.signalList[cFlag].Pop()
- _, err := connPass.conn.Write([]byte("chan"))
- if err != nil {
- log.Println(err)
- goto retry
- }
- s.signalList[cFlag].Add(connPass)
- return nil
-}
-
-//得到一个tcp隧道
-func (s *Tunnel) GetTunnel(cFlag string, en, de int, crypt, mux bool) (c *Conn, err error) {
- s.tunnelLock.Lock()
- if v, ok := s.tunnelList[cFlag]; !ok || v.Len() < 3 { //新建通道
- go s.newChan(cFlag)
- }
-retry:
- if err = s.wait(s.tunnelList, cFlag); err != nil {
- return
- }
- c = s.tunnelList[cFlag].Pop()
- if _, err = c.wTest(); err != nil {
- c.Close()
- goto retry
- }
- c.WriteConnInfo(en, de, crypt, mux)
- s.tunnelLock.Unlock()
- return
-}
-
-//得到一个通信通道
-func (s *Tunnel) GetSignal(cFlag string) (err error, conn *Conn) {
- if v, ok := s.signalList[cFlag]; !ok || v.Len() == 0 {
- err = errors.New("客户端未连接")
- return
- }
- conn = s.signalList[cFlag].Pop()
- return
-}
-
-//重回slice 复用
-func (s *Tunnel) ReturnSignal(conn *Conn, cFlag string) {
- if v, ok := s.signalList[cFlag]; ok {
- v.Add(conn)
- }
-}
-
-//重回slice 复用
-func (s *Tunnel) ReturnTunnel(conn *Conn, cFlag string) {
- if v, ok := s.tunnelList[cFlag]; ok {
- FlushConn(conn.conn)
- v.Add(conn)
- }
-}
-
-//删除通信通道
-func (s *Tunnel) DelClientSignal(cFlag string) {
- s.delClient(cFlag, s.signalList)
-}
-
-//删除隧道
-func (s *Tunnel) DelClientTunnel(cFlag string) {
- s.delClient(cFlag, s.tunnelList)
-}
-
-func (s *Tunnel) delClient(cFlag string, l map[string]*list) {
- if t := l[getverifyval(cFlag)]; t != nil {
- for {
- if t.Len() <= 0 {
- break
- }
- t.Pop().Close()
- }
- delete(l, getverifyval(cFlag))
- }
-}
-
-//等待
-func (s *Tunnel) wait(m map[string]*list, cFlag string) error {
- ticker := time.NewTicker(time.Millisecond * 100)
- stop := time.After(time.Second * 10)
-loop:
- for {
- select {
- case <-ticker.C:
- if _, ok := m[cFlag]; ok {
- ticker.Stop()
- break loop
- }
- case <-stop:
- return errors.New("client key: " + cFlag + ",err: get client conn timeout")
- }
- }
- return nil
-}
diff --git a/lib/init.go b/lib/init.go
deleted file mode 100644
index 78ceacf..0000000
--- a/lib/init.go
+++ /dev/null
@@ -1,201 +0,0 @@
-package lib
-
-import (
- "errors"
- "flag"
- "log"
- "net/http"
- _ "net/http/pprof"
- "reflect"
- "strings"
- "sync"
-)
-
-var (
- configPath = flag.String("config", "config.json", "配置文件路径")
- TcpPort = flag.Int("tcpport", 8284, "客户端与服务端通信端口")
- httpPort = flag.Int("httpport", 8024, "对外监听的端口")
- rpMode = flag.String("mode", "client", "启动模式")
- tunnelTarget = flag.String("target", "10.1.50.203:80", "远程目标")
- verifyKey = flag.String("vkey", "", "验证密钥")
- u = flag.String("u", "", "验证用户名(socks5和web)")
- p = flag.String("p", "", "验证密码(socks5和web)")
- compress = flag.String("compress", "", "数据压缩方式(snappy)")
- serverAddr = flag.String("server", "", "服务器地址ip:端口")
- crypt = flag.String("crypt", "false", "是否加密(true|false)")
- mux = flag.String("mux", "false", "是否TCP多路复用(true|false)")
- config Config
- err error
- RunList map[string]interface{} //运行中的任务
- bridge *Tunnel
- CsvDb *Csv
-)
-
-const cryptKey = "1234567812345678"
-
-func init() {
- RunList = make(map[string]interface{})
-}
-
-func InitClient() {
- flag.Parse()
- if *rpMode == "client" {
- go func() {
- http.ListenAndServe("0.0.0.0:8899", nil)
- }()
- JsonParse := NewJsonStruct()
- if config, err = JsonParse.Load(*configPath); err != nil {
- log.Println("配置文件加载失败")
- }
- stop := make(chan int)
- for _, v := range strings.Split(*verifyKey, ",") {
- log.Println("客户端启动,连接:", *serverAddr, " 验证令牌:", v)
- go NewRPClient(*serverAddr, 1, v).Start()
- }
- <-stop
- }
-}
-func InitMode() {
- flag.Parse()
- if *rpMode == "client" {
- go func() {
- http.ListenAndServe("0.0.0.0:8899", nil)
- }()
- JsonParse := NewJsonStruct()
- if config, err = JsonParse.Load(*configPath); err != nil {
- log.Println("配置文件加载失败")
- }
- stop := make(chan int)
- for _, v := range strings.Split(*verifyKey, ",") {
- log.Println("客户端启动,连接:", *serverAddr, " 验证令牌:", v)
- go NewRPClient(*serverAddr, 1, v).Start()
- }
- <-stop
- } else {
- bridge = newTunnel(*TcpPort)
- if err := bridge.StartTunnel(); err != nil {
- log.Fatalln("服务端开启失败", err)
- }
- log.Println("服务端启动,监听tcp服务端端口:", *TcpPort)
- cnf := ServerConfig{
- TcpPort: *httpPort,
- Mode: *rpMode,
- Target: *tunnelTarget,
- VerifyKey: *verifyKey,
- U: *u,
- P: *p,
- Compress: *compress,
- Start: 0,
- IsRun: 0,
- ClientStatus: 0,
- Crypt: GetBoolByStr(*crypt),
- Mux: GetBoolByStr(*mux),
- CompressEncode: 0,
- CompressDecode: 0,
- }
- cnf.CompressDecode, cnf.CompressEncode = getCompressType(cnf.Compress)
- if svr := newMode(bridge, &cnf);
- svr != nil {
- reflect.ValueOf(svr).MethodByName("Start").Call(nil)
- } else {
- log.Fatalln("启动模式不正确")
- }
- }
-}
-
-//从csv文件中恢复任务
-func InitFromCsv() {
- for _, v := range CsvDb.Tasks {
- if v.Start == 1 {
- log.Println(""+
- "启动模式:", v.Mode, "监听端口:", v.TcpPort, "客户端令牌:", v.VerifyKey)
- AddTask(v)
- }
- }
-}
-
-func newMode(bridge *Tunnel, config *ServerConfig) interface{} {
- switch config.Mode {
- case "httpServer":
- return NewHttpModeServer(bridge, config)
- case "tunnelServer":
- return NewTunnelModeServer(ProcessTunnel, bridge, config)
- case "socks5Server":
- return NewSock5ModeServer(bridge, config)
- case "httpProxyServer":
- return NewTunnelModeServer(ProcessHttp, bridge, config)
- case "udpServer":
- return NewUdpModeServer(bridge, config)
- case "webServer":
- InitCsvDb()
- return NewWebServer(bridge)
- case "hostServer":
- return NewHostServer(config)
- case "httpHostServer":
- return NewTunnelModeServer(ProcessHost, bridge, config)
- }
- return nil
-}
-
-func StopServer(cFlag string) error {
- if v, ok := RunList[cFlag]; ok {
- reflect.ValueOf(v).MethodByName("Close").Call(nil)
- delete(RunList, cFlag)
- if *verifyKey == "" { //多客户端模式关闭相关隧道
- bridge.DelClientSignal(cFlag)
- bridge.DelClientTunnel(cFlag)
- }
- if t, err := CsvDb.GetTask(cFlag); err != nil {
- return err
- } else {
- t.Start = 0
- CsvDb.UpdateTask(t)
- }
- return nil
- }
- return errors.New("未在运行中")
-}
-
-func AddTask(t *ServerConfig) error {
- t.CompressDecode, t.CompressEncode = getCompressType(t.Compress)
- if svr := newMode(bridge, t); svr != nil {
- RunList[t.VerifyKey] = svr
- go func() {
- err := reflect.ValueOf(svr).MethodByName("Start").Call(nil)[0]
- if err.Interface() != nil {
- log.Println("客户端", t.VerifyKey, "启动失败,错误:", err)
- delete(RunList, t.VerifyKey)
- }
- }()
- } else {
- return errors.New("启动模式不正确")
- }
- return nil
-}
-
-func StartTask(vKey string) error {
- if t, err := CsvDb.GetTask(vKey); err != nil {
- return err
- } else {
- AddTask(t)
- t.Start = 1
- CsvDb.UpdateTask(t)
- }
- return nil
-}
-
-func DelTask(vKey string) error {
- if err := StopServer(vKey); err != nil {
- return err
- }
- return CsvDb.DelTask(vKey)
-}
-
-func InitCsvDb() *Csv {
- var once sync.Once
- once.Do(func() {
- CsvDb = NewCsv( bridge, RunList)
- CsvDb.Init()
- })
- return CsvDb
-}
diff --git a/lib/tcp.go b/lib/tcp.go
deleted file mode 100755
index 17a40bd..0000000
--- a/lib/tcp.go
+++ /dev/null
@@ -1,326 +0,0 @@
-package lib
-
-import (
- "errors"
- "fmt"
- "github.com/astaxie/beego"
- "github.com/astaxie/beego/session"
- "io/ioutil"
- "log"
- "net"
- "net/http"
- "strings"
-)
-
-var GlobalHostSessions *session.Manager
-
-const (
- VERIFY_EER = "vkey"
- WORK_MAIN = "main"
- WORK_CHAN = "chan"
- RES_SIGN = "sign"
- RES_MSG = "msg0"
- CONN_SUCCESS = "sucs"
- CONN_ERROR = "fail"
- TEST_FLAG = "tst"
- CONN_TCP = "tcp"
- CONN_UDP = "udp"
- Unauthorized_BYTES = `HTTP/1.1 401 Unauthorized
-Content-Type: text/plain; charset=utf-8
-WWW-Authenticate: Basic realm="easyProxy"
-
-401 Unauthorized`
-)
-
-type process func(c *Conn, s *TunnelModeServer) error
-
-type HttpModeServer struct {
- bridge *Tunnel
- config *ServerConfig
-}
-
-//http
-func NewHttpModeServer(bridge *Tunnel, cnf *ServerConfig) *HttpModeServer {
- s := new(HttpModeServer)
- s.bridge = bridge
- s.config = cnf
- return s
-}
-
-//开始
-func (s *HttpModeServer) Start() {
- http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- retry:
- u := beego.AppConfig.String("basic.user")
- p := beego.AppConfig.String("basic.password")
- if u != "" && p != "" && !checkAuth(r, u, p) {
- w.Header().Set("WWW-Authenticate", `Basic realm="easyProxy""`)
- w.WriteHeader(401)
- w.Write([]byte("401 Unauthorized\n"))
- return
- }
- err, conn := s.bridge.GetSignal(getverifyval(s.config.VerifyKey))
- if err != nil {
- BadRequest(w)
- return
- }
- if err := s.writeRequest(r, conn); err != nil {
- log.Println("write request to client error:", err)
- conn.Close()
- goto retry
- return
- }
- err = s.writeResponse(w, conn)
- if err != nil {
- log.Println("write response error:", err)
- conn.Close()
- goto retry
- return
- }
- s.bridge.ReturnSignal(conn, getverifyval(s.config.VerifyKey))
- })
- log.Fatalln(http.ListenAndServe(fmt.Sprintf(":%d", s.config.TcpPort), nil))
-}
-
-//req转为bytes发送给client端
-func (s *HttpModeServer) writeRequest(r *http.Request, conn *Conn) error {
- raw, err := EncodeRequest(r)
- if err != nil {
- return err
- }
- conn.wSign()
- conn.WriteConnInfo(s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, s.config.Mux)
- c, err := conn.WriteTo(raw, s.config.CompressEncode, s.config.Crypt)
- if err != nil {
- return err
- }
- if c != len(raw) {
- return errors.New("写出长度与字节长度不一致。")
- }
- return nil
-}
-
-//从client读取出Response
-func (s *HttpModeServer) writeResponse(w http.ResponseWriter, c *Conn) error {
- flags, err := c.ReadFlag()
- if err != nil {
- return err
- }
- switch flags {
- case RES_SIGN:
- buf := make([]byte, 1024*1024*32)
- n, err := c.ReadFrom(buf, s.config.CompressDecode, s.config.Crypt)
- if err != nil {
- return err
- }
- resp, err := DecodeResponse(buf[:n])
- if err != nil {
- return err
- }
- bodyBytes, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return err
- }
- for k, v := range resp.Header {
- for _, v2 := range v {
- w.Header().Set(k, v2)
- }
- }
- w.WriteHeader(resp.StatusCode)
- w.Write(bodyBytes)
- case RES_MSG:
- BadRequest(w)
- return errors.New("客户端请求出错")
- default:
- BadRequest(w)
- return errors.New("无法解析此错误")
- }
- return nil
-}
-
-type TunnelModeServer struct {
- process process
- bridge *Tunnel
- config *ServerConfig
- listener *net.TCPListener
-}
-
-//tcp|http|host
-func NewTunnelModeServer(process process, bridge *Tunnel, cnf *ServerConfig) *TunnelModeServer {
- s := new(TunnelModeServer)
- s.bridge = bridge
- s.process = process
- s.config = cnf
- return s
-}
-
-//开始
-func (s *TunnelModeServer) Start() error {
- s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.config.TcpPort, ""})
- if err != nil {
- return err
- }
- for {
- conn, err := s.listener.AcceptTCP()
- if err != nil {
- if strings.Contains(err.Error(), "use of closed network connection") {
- break
- }
- log.Println(err)
- continue
- }
- go s.process(NewConn(conn), s)
- }
- return nil
-}
-
-//权限认证
-func (s *TunnelModeServer) auth(r *http.Request, c *Conn, u, p string) error {
- if u != "" && p != "" && !checkAuth(r, u, p) {
- c.Write([]byte(Unauthorized_BYTES))
- c.Close()
- return errors.New("401 Unauthorized")
- }
- return nil
-}
-
-//与客户端建立通道
-func (s *TunnelModeServer) dealClient(c *Conn, cnf *ServerConfig, addr string, method string, rb []byte) error {
- link, err := s.bridge.GetTunnel(getverifyval(cnf.VerifyKey), cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, cnf.Mux)
- defer func() {
- if cnf.Mux {
- s.bridge.ReturnTunnel(link, getverifyval(cnf.VerifyKey))
- } else {
- c.Close()
- }
- }()
- if err != nil {
- log.Println(err)
- c.Close()
- return err
- }
- if _, err := link.WriteHost(CONN_TCP, addr); err != nil {
- c.Close()
- link.Close()
- log.Println(err)
- return err
- }
- if flag, err := link.ReadFlag(); err == nil {
- if flag == CONN_SUCCESS {
- if method == "CONNECT" {
- fmt.Fprint(c, "HTTP/1.1 200 Connection established\r\n")
- } else {
- link.WriteTo(rb, cnf.CompressEncode, cnf.Crypt)
- }
- go relay(link.conn, c.conn, cnf.CompressEncode, cnf.Crypt, cnf.Mux)
- relay(c.conn, link.conn, cnf.CompressDecode, cnf.Crypt, cnf.Mux)
- }
- }
- return nil
-}
-
-//close
-func (s *TunnelModeServer) Close() error {
- return s.listener.Close()
-}
-
-//tcp隧道模式
-func ProcessTunnel(c *Conn, s *TunnelModeServer) error {
- method, _, rb, err, r := c.GetHost()
- if err == nil {
- if err := s.auth(r, c, s.config.U, s.config.P); err != nil {
- return err
- }
- }
- return s.dealClient(c, s.config, s.config.Target, method, rb)
-}
-
-//http代理模式
-func ProcessHttp(c *Conn, s *TunnelModeServer) error {
- method, addr, rb, err, r := c.GetHost()
- if err != nil {
- c.Close()
- return err
- }
- if err := s.auth(r, c, s.config.U, s.config.P); err != nil {
- return err
- }
- //TODO效率问题
- return s.dealClient(c, s.config, addr, method, rb)
-}
-
-//多客户端域名代理
-func ProcessHost(c *Conn, s *TunnelModeServer) error {
- method, addr, rb, err, r := c.GetHost()
- if err != nil {
- c.Close()
- return err
- }
- host, task, err := getKeyByHost(addr)
- if err := s.auth(r, c, task.U, task.P); err != nil {
- return err
- }
- if err != nil {
- c.Close()
- return err
- }
- return s.dealClient(c, task, host.Target, method, rb)
-}
-
-//web管理方式
-type WebServer struct {
- bridge *Tunnel
-}
-
-//开始
-func (s *WebServer) Start() {
- InitFromCsv()
- p, _ := beego.AppConfig.Int("hostPort")
- t := &ServerConfig{
- TcpPort: p,
- Mode: "httpHostServer",
- Target: "",
- VerifyKey: "",
- U: "",
- P: "",
- Compress: "",
- Start: 1,
- IsRun: 0,
- ClientStatus: 0,
- }
- AddTask(t)
- beego.BConfig.WebConfig.Session.SessionOn = true
- log.Println("web管理启动,访问端口为", beego.AppConfig.String("httpport"))
- beego.SetViewsPath(beego.AppPath + "/views/")
- beego.SetStaticPath("/static/", beego.AppPath+"/static/")
- beego.Run()
-}
-
-//new
-func NewWebServer(bridge *Tunnel) *WebServer {
- s := new(WebServer)
- s.bridge = bridge
- return s
-}
-
-//host
-type HostServer struct {
- config *ServerConfig
-}
-
-//开始
-func (s *HostServer) Start() error {
- return nil
-}
-
-//TODO:host模式的客户端,无需指定和监听端口等
-func NewHostServer(cnf *ServerConfig) *HostServer {
- s := new(HostServer)
- s.config = cnf
- return s
-}
-
-//close
-func (s *HostServer) Close() error {
- return nil
-}
diff --git a/lib/util.go b/lib/util.go
deleted file mode 100755
index 5ef3c8b..0000000
--- a/lib/util.go
+++ /dev/null
@@ -1,330 +0,0 @@
-package lib
-
-import (
- "bufio"
- "bytes"
- "encoding/base64"
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "log"
- "net"
- "net/http"
- "net/http/httputil"
- "net/url"
- "regexp"
- "strconv"
- "strings"
- "sync"
- "time"
-)
-
-var (
- disabledRedirect = errors.New("disabled redirect.")
-)
-
-const (
- COMPRESS_NONE_ENCODE = iota
- COMPRESS_NONE_DECODE
- COMPRESS_SNAPY_ENCODE
- COMPRESS_SNAPY_DECODE
- IO_EOF = "PROXYEOF"
-)
-
-//error
-func BadRequest(w http.ResponseWriter) {
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
-}
-
-//发送请求并转为bytes
-func GetEncodeResponse(req *http.Request) ([]byte, error) {
- var respBytes []byte
- client := new(http.Client)
- client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
- return disabledRedirect
- }
- resp, err := client.Do(req)
- disRedirect := err != nil && strings.Contains(err.Error(), disabledRedirect.Error())
- if err != nil && !disRedirect {
- return respBytes, err
- }
- if !disRedirect {
- defer resp.Body.Close()
- } else {
- resp.Body = nil
- resp.ContentLength = 0
- }
- respBytes, err = EncodeResponse(resp)
- return respBytes, nil
-}
-
-// 将request转为bytes
-func EncodeRequest(r *http.Request) ([]byte, error) {
- raw := bytes.NewBuffer([]byte{})
- reqBytes, err := httputil.DumpRequest(r, true)
- if err != nil {
- return nil, err
- }
- binary.Write(raw, binary.LittleEndian, bool(r.URL.Scheme == "https"))
- binary.Write(raw, binary.LittleEndian, reqBytes)
- return raw.Bytes(), nil
-}
-
-// 将字节转为request
-func DecodeRequest(data []byte) (*http.Request, error) {
- req, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(data[1:])))
- if err != nil {
- return nil, err
- }
- str := strings.Split(req.Host, ":")
- req.Host, err = getHost(str[0])
- if err != nil {
- return nil, err
- }
- scheme := "http"
- if data[0] == 1 {
- scheme = "https"
- }
- req.URL, _ = url.Parse(fmt.Sprintf("%s://%s%s", scheme, req.Host, req.RequestURI))
- req.RequestURI = ""
- return req, nil
-}
-
-// 将response转为字节
-func EncodeResponse(r *http.Response) ([]byte, error) {
- respBytes, err := httputil.DumpResponse(r, true)
- if err != nil {
- return nil, err
- }
- if config.Replace == 1 {
- respBytes = replaceHost(respBytes)
- }
- return respBytes, nil
-}
-
-// 将字节转为response
-func DecodeResponse(data []byte) (*http.Response, error) {
- resp, err := http.ReadResponse(bufio.NewReader(bytes.NewReader(data)), nil)
- if err != nil {
- return nil, err
- }
- return resp, nil
-}
-
-// 根据host地址从配置是文件中查找对应目标
-func getHost(str string) (string, error) {
- for _, v := range config.SiteList {
- if v.Host == str {
- return v.Url + ":" + strconv.Itoa(v.Port), nil
- }
- }
- return "", errors.New("没有找到解析的的host!")
-}
-
-//替换
-func replaceHost(resp []byte) []byte {
- str := string(resp)
- for _, v := range config.SiteList {
- str = strings.Replace(str, v.Url+":"+strconv.Itoa(v.Port), v.Host, -1)
- str = strings.Replace(str, v.Url, v.Host, -1)
- }
- return []byte(str)
-}
-
-//copy
-func relay(in, out net.Conn, compressType int, crypt, mux bool) {
- switch compressType {
- case COMPRESS_SNAPY_ENCODE:
- copyBuffer(NewSnappyConn(in, crypt), out)
- if mux {
- out.Close()
- NewSnappyConn(in, crypt).Write([]byte(IO_EOF))
- }
- case COMPRESS_SNAPY_DECODE:
- copyBuffer(in, NewSnappyConn(out, crypt))
- if mux {
- in.Close()
- }
- case COMPRESS_NONE_ENCODE:
- copyBuffer(NewCryptConn(in, crypt), out)
- if mux {
- out.Close()
- NewCryptConn(in, crypt).Write([]byte(IO_EOF))
- }
- case COMPRESS_NONE_DECODE:
- copyBuffer(in, NewCryptConn(out, crypt))
- if mux {
- in.Close()
- }
- }
- if !mux {
- in.Close()
- out.Close()
- }
-}
-
-//判断压缩方式
-func getCompressType(compress string) (int, int) {
- switch compress {
- case "":
- return COMPRESS_NONE_DECODE, COMPRESS_NONE_ENCODE
- case "snappy":
- return COMPRESS_SNAPY_DECODE, COMPRESS_SNAPY_ENCODE
- default:
- log.Fatalln("数据压缩格式错误")
- }
- return COMPRESS_NONE_DECODE, COMPRESS_NONE_ENCODE
-}
-
-//简单的一个校验值
-func getverifyval(vkey string) string {
- //单客户端模式
- if *verifyKey != "" {
- return Md5(*verifyKey)
- }
- return Md5(vkey)
-}
-
-//验证
-func verify(verifyKeyMd5 string) bool {
- if *verifyKey != "" && getverifyval(*verifyKey) == verifyKeyMd5 {
- return true
- }
- if *verifyKey == "" {
- for k := range RunList {
- if getverifyval(k) == verifyKeyMd5 {
- return true
- }
- }
- }
- return false
-}
-
-//get key by host from x
-func getKeyByHost(host string) (h *HostList, t *ServerConfig, err error) {
- for _, v := range CsvDb.Hosts {
- if strings.Contains(host, v.Host) {
- h = v
- t, err = CsvDb.GetTask(v.Vkey)
- return
- }
- }
- err = errors.New("未找到host对应的内网目标")
- return
-}
-
-//通过host获取对应的ip地址
-func Gethostbyname(hostname string) string {
- if !DomainCheck(hostname) {
- return hostname
- }
- ips, _ := net.LookupIP(hostname)
- if ips != nil {
- for _, v := range ips {
- if v.To4() != nil {
- return v.String()
- }
- }
- }
- return ""
-}
-
-//检查是否是域名
-func DomainCheck(domain string) bool {
- var match bool
- IsLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}(/)"
- NotLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}"
- match, _ = regexp.MatchString(IsLine, domain)
- if !match {
- match, _ = regexp.MatchString(NotLine, domain)
- }
- return match
-}
-
-//检查basic认证
-func checkAuth(r *http.Request, user, passwd string) bool {
- s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
- if len(s) != 2 {
- return false
- }
-
- b, err := base64.StdEncoding.DecodeString(s[1])
- if err != nil {
- return false
- }
-
- pair := strings.SplitN(string(b), ":", 2)
- if len(pair) != 2 {
- return false
- }
- return pair[0] == user && pair[1] == passwd
-}
-
-//get bool by str
-func GetBoolByStr(s string) bool {
- switch s {
- case "1", "true":
- return true
- }
- return false
-}
-
-//get str by bool
-func GetStrByBool(b bool) string {
- if b {
- return "1"
- }
- return "0"
-}
-func GetIntNoerrByStr(str string) int {
- i, _ := strconv.Atoi(str)
- return i
-}
-
-var bufPool = sync.Pool{
- New: func() interface{} {
- return make([]byte, 65535)
- },
-}
-// io.copy的优化版,读取buffer长度原为32*1024,与snappy不同,导致读取出的内容存在差异,不利于解密,特此修改
-func copyBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
- //TODO 回收问题
- buf := bufPool.Get().([]byte)
- for {
- nr, er := src.Read(buf)
- if nr > 0 {
- nw, ew := dst.Write(buf[0:nr])
- if nw > 0 {
- written += int64(nw)
- }
- if ew != nil {
- err = ew
- break
- }
- if nr != nw {
- err = io.ErrShortWrite
- break
- }
- }
- 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)
- for {
- if _, err := c.Read(buf); err != nil {
- break
- }
- }
- c.SetReadDeadline(time.Time{})
-}
diff --git a/main.go b/main.go
deleted file mode 100755
index 5959606..0000000
--- a/main.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package main
-
-import (
- "github.com/cnlh/easyProxy/lib"
- _ "github.com/cnlh/easyProxy/routers"
-)
-
-func main() {
- lib.InitMode()
-}
diff --git a/lib/file.go b/server/file.go
similarity index 76%
rename from lib/file.go
rename to server/file.go
index 2fed1a2..2a999c7 100644
--- a/lib/file.go
+++ b/server/file.go
@@ -1,11 +1,10 @@
-package lib
+package server
import (
"encoding/csv"
- "encoding/json"
"errors"
"github.com/astaxie/beego"
- "io/ioutil"
+ "github.com/cnlh/easyProxy/utils"
"log"
"os"
"strconv"
@@ -34,9 +33,8 @@ type HostList struct {
Target string //目标
}
-func NewCsv(bridge *Tunnel, runList map[string]interface{}) *Csv {
+func NewCsv(runList map[string]interface{}) *Csv {
c := new(Csv)
- c.Bridge = bridge
c.RunList = runList
return c
}
@@ -44,7 +42,6 @@ func NewCsv(bridge *Tunnel, runList map[string]interface{}) *Csv {
type Csv struct {
Tasks []*ServerConfig
Path string
- Bridge *Tunnel
RunList map[string]interface{}
Hosts []*HostList //域名列表
}
@@ -72,8 +69,8 @@ func (s *Csv) StoreTasksToCsv() {
task.P,
task.Compress,
strconv.Itoa(task.Start),
- GetStrByBool(task.Crypt),
- GetStrByBool(task.Mux),
+ utils.GetStrByBool(task.Crypt),
+ utils.GetStrByBool(task.Mux),
strconv.Itoa(task.CompressEncode),
strconv.Itoa(task.CompressDecode),
}
@@ -118,10 +115,10 @@ func (s *Csv) LoadTaskFromCsv() {
P: item[5],
Compress: item[6],
Start: Start,
- Crypt: GetBoolByStr(item[8]),
- Mux: GetBoolByStr(item[9]),
- CompressEncode: GetIntNoerrByStr(item[10]),
- CompressDecode: GetIntNoerrByStr(item[11]),
+ Crypt: utils.GetBoolByStr(item[8]),
+ Mux: utils.GetBoolByStr(item[9]),
+ CompressEncode: utils.GetIntNoerrByStr(item[10]),
+ CompressDecode: utils.GetIntNoerrByStr(item[11]),
}
tasks = append(tasks, post)
}
@@ -130,7 +127,7 @@ func (s *Csv) LoadTaskFromCsv() {
func (s *Csv) StoreHostToCsv() {
// 创建文件
- csvFile, err := os.Create(s.Path + "hosts.csv")
+ csvFile, err := os.Create(beego.AppPath + "/conf/hosts.csv")
if err != nil {
panic(err)
}
@@ -186,38 +183,6 @@ func (s *Csv) LoadHostFromCsv() {
s.Hosts = hosts
}
-func (s *Csv) GetServerConfig(start, length int, typeVal string) ([]*ServerConfig, int) {
- list := make([]*ServerConfig, 0)
- var cnt int
- for _, v := range s.Tasks {
- if v.Mode != typeVal {
- continue
- }
- cnt++
- if start--; start < 0 {
- if length--; length > 0 {
- if _, ok := s.RunList[v.VerifyKey]; ok {
- v.IsRun = 1
- } else {
- v.IsRun = 0
- }
- if s, ok := s.Bridge.signalList[getverifyval(v.VerifyKey)]; ok {
- if s.Len() > 0 {
- v.ClientStatus = 1
- } else {
- v.ClientStatus = 0
- }
- } else {
- v.ClientStatus = 0
- }
- list = append(list, v)
- }
- }
-
- }
- return list, cnt
-}
-
func (s *Csv) NewTask(t *ServerConfig) {
s.Tasks = append(s.Tasks, t)
s.StoreTasksToCsv()
@@ -232,7 +197,6 @@ func (s *Csv) UpdateTask(t *ServerConfig) error {
return nil
}
}
- //TODO:待测试
return errors.New("不存在")
}
@@ -297,31 +261,3 @@ func (s *Csv) GetHostList(start, length int, vKey string) ([]*HostList, int) {
}
return list, cnt
}
-
-type Site struct {
- Host string
- Url string
- Port int
-}
-type Config struct {
- SiteList []Site
- Replace int
-}
-type JsonStruct struct {
-}
-
-func NewJsonStruct() *JsonStruct {
- return &JsonStruct{}
-}
-func (jst *JsonStruct) Load(filename string) (Config, error) {
- data, err := ioutil.ReadFile(filename)
- config := Config{}
- if err != nil {
- return config, errors.New("配置文件打开错误")
- }
- err = json.Unmarshal(data, &config)
- if err != nil {
- return config, errors.New("配置文件解析错误")
- }
- return config, nil
-}
diff --git a/server/server.go b/server/server.go
new file mode 100644
index 0000000..7e71f1a
--- /dev/null
+++ b/server/server.go
@@ -0,0 +1,209 @@
+package server
+
+import (
+ "errors"
+ "github.com/cnlh/easyProxy/bridge"
+ "github.com/cnlh/easyProxy/utils"
+ "log"
+ "reflect"
+ "strings"
+ "sync"
+)
+
+var (
+ Bridge *bridge.Tunnel
+ RunList map[string]interface{} //运行中的任务
+ CsvDb *Csv
+ VerifyKey string
+)
+
+func init() {
+ RunList = make(map[string]interface{})
+}
+
+//从csv文件中恢复任务
+func InitFromCsv() {
+ for _, v := range CsvDb.Tasks {
+ if v.Start == 1 {
+ log.Println("启动模式:", v.Mode, "监听端口:", v.TcpPort, "客户端令牌:", v.VerifyKey)
+ AddTask(v)
+ }
+ }
+}
+
+//start a new server
+func StartNewServer(bridgePort int, cnf *ServerConfig) {
+ Bridge = bridge.NewTunnel(bridgePort, RunList)
+ if err := Bridge.StartTunnel(); err != nil {
+ log.Fatalln("服务端开启失败", err)
+ }
+ if svr := NewMode(Bridge, cnf); svr != nil {
+ RunList[cnf.VerifyKey] = svr
+ err := reflect.ValueOf(svr).MethodByName("Start").Call(nil)[0]
+ if err.Interface() != nil {
+ log.Println(err)
+ }
+ } else {
+ log.Fatalln("启动模式不正确")
+ }
+}
+
+//new a server by mode name
+func NewMode(Bridge *bridge.Tunnel, config *ServerConfig) interface{} {
+ switch config.Mode {
+ case "tunnelServer":
+ return NewTunnelModeServer(ProcessTunnel, Bridge, config)
+ case "socks5Server":
+ return NewSock5ModeServer(Bridge, config)
+ case "httpProxyServer":
+ return NewTunnelModeServer(ProcessHttp, Bridge, config)
+ case "udpServer":
+ return NewUdpModeServer(Bridge, config)
+ case "webServer":
+ InitCsvDb()
+ InitFromCsv()
+ //p, _ := beego.AppConfig.Int("hostPort")
+ t := &ServerConfig{
+ TcpPort: 8088,
+ Mode: "httpHostServer",
+ Target: "",
+ VerifyKey: "",
+ U: "",
+ P: "",
+ Compress: "",
+ Start: 1,
+ IsRun: 0,
+ ClientStatus: 0,
+ }
+ AddTask(t)
+ return NewWebServer(Bridge)
+ case "hostServer":
+ return NewHostServer(config)
+ case "httpHostServer":
+ return NewTunnelModeServer(ProcessHost, Bridge, config)
+ }
+ return nil
+}
+
+//stop server
+func StopServer(cFlag string) error {
+ if v, ok := RunList[cFlag]; ok {
+ reflect.ValueOf(v).MethodByName("Close").Call(nil)
+ delete(RunList, cFlag)
+ if VerifyKey == "" { //多客户端模式关闭相关隧道
+ Bridge.DelClientSignal(cFlag)
+ Bridge.DelClientTunnel(cFlag)
+ }
+ if t, err := CsvDb.GetTask(cFlag); err != nil {
+ return err
+ } else {
+ t.Start = 0
+ CsvDb.UpdateTask(t)
+ }
+ return nil
+ }
+ return errors.New("未在运行中")
+}
+
+//add task
+func AddTask(t *ServerConfig) error {
+ t.CompressDecode, t.CompressEncode = utils.GetCompressType(t.Compress)
+ if svr := NewMode(Bridge, t); svr != nil {
+ RunList[t.VerifyKey] = svr
+ go func() {
+ err := reflect.ValueOf(svr).MethodByName("Start").Call(nil)[0]
+ if err.Interface() != nil {
+ log.Println("客户端", t.VerifyKey, "启动失败,错误:", err)
+ delete(RunList, t.VerifyKey)
+ }
+ }()
+ } else {
+ return errors.New("启动模式不正确")
+ }
+ return nil
+}
+
+//start task
+func StartTask(vKey string) error {
+ if t, err := CsvDb.GetTask(vKey); err != nil {
+ return err
+ } else {
+ AddTask(t)
+ t.Start = 1
+ CsvDb.UpdateTask(t)
+ }
+ return nil
+}
+
+//delete task
+func DelTask(vKey string) error {
+ if err := StopServer(vKey); err != nil {
+ return err
+ }
+ return CsvDb.DelTask(vKey)
+}
+
+//init csv from file
+func InitCsvDb() *Csv {
+ var once sync.Once
+ once.Do(func() {
+ CsvDb = NewCsv(RunList)
+ CsvDb.Init()
+ })
+ return CsvDb
+}
+
+//get key by host from x
+func GetKeyByHost(host string) (h *HostList, t *ServerConfig, err error) {
+ for _, v := range CsvDb.Hosts {
+ if strings.Contains(host, v.Host) {
+ h = v
+ t, err = CsvDb.GetTask(v.Vkey)
+ return
+ }
+ }
+ err = errors.New("未找到host对应的内网目标")
+ return
+}
+
+//get task list by page num
+func GetServerConfig(start, length int, typeVal string) ([]*ServerConfig, int) {
+ list := make([]*ServerConfig, 0)
+ var cnt int
+ for _, v := range CsvDb.Tasks {
+ if v.Mode != typeVal {
+ continue
+ }
+ cnt++
+ if start--; start < 0 {
+ if length--; length > 0 {
+ if _, ok := RunList[v.VerifyKey]; ok {
+ v.IsRun = 1
+ } else {
+ v.IsRun = 0
+ }
+ if s, ok := Bridge.SignalList[getverifyval(v.VerifyKey)]; ok {
+ if s.Len() > 0 {
+ v.ClientStatus = 1
+ } else {
+ v.ClientStatus = 0
+ }
+ } else {
+ v.ClientStatus = 0
+ }
+ list = append(list, v)
+ }
+ }
+
+ }
+ return list, cnt
+}
+
+//get verify value
+//when mode is webServer and vKey is not none
+func getverifyval(vkey string) string {
+ if VerifyKey != "" {
+ return utils.Md5(VerifyKey)
+ }
+ return utils.Md5(vkey)
+}
diff --git a/lib/socks5.go b/server/socks5.go
similarity index 89%
rename from lib/socks5.go
rename to server/socks5.go
index 8c151fe..37be009 100755
--- a/lib/socks5.go
+++ b/server/socks5.go
@@ -1,8 +1,10 @@
-package lib
+package server
import (
"encoding/binary"
"errors"
+ "github.com/cnlh/easyProxy/bridge"
+ "github.com/cnlh/easyProxy/utils"
"io"
"log"
"net"
@@ -44,7 +46,7 @@ const (
)
type Sock5ModeServer struct {
- bridge *Tunnel
+ bridge *bridge.Tunnel
isVerify bool
listener net.Listener
config *ServerConfig
@@ -105,7 +107,7 @@ func (s *Sock5ModeServer) sendReply(c net.Conn, rep uint8) {
}
//do conn
-func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *Conn, err error) {
+func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *utils.Conn, err error) {
addrType := make([]byte, 1)
c.Read(addrType)
var host string
@@ -142,14 +144,14 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *Conn,
s.sendReply(c, succeeded)
var ltype string
if command == associateMethod {
- ltype = CONN_UDP
+ ltype = utils.CONN_UDP
} else {
- ltype = CONN_TCP
+ ltype = utils.CONN_TCP
}
_, err = client.WriteHost(ltype, addr)
var flag string
if flag, err = client.ReadFlag(); err == nil {
- if flag != CONN_SUCCESS {
+ if flag != utils.CONN_SUCCESS {
err = errors.New("conn failed")
}
}
@@ -167,8 +169,8 @@ func (s *Sock5ModeServer) handleConnect(c net.Conn) {
if err != nil {
c.Close()
} else {
- go relay(proxyConn.conn, c, s.config.CompressEncode, s.config.Crypt, s.config.Mux)
- relay(c, proxyConn.conn, s.config.CompressDecode, s.config.Crypt, s.config.Mux)
+ go utils.Relay(proxyConn.Conn, c, s.config.CompressEncode, s.config.Crypt, s.config.Mux)
+ utils.Relay(c, proxyConn.Conn, s.config.CompressDecode, s.config.Crypt, s.config.Mux)
}
}
@@ -205,13 +207,13 @@ func (s *Sock5ModeServer) handleUDP(c net.Conn) {
if err != nil {
c.Close()
} else {
- go relay(proxyConn.conn, c, s.config.CompressEncode, s.config.Crypt, s.config.Mux)
- relay(c, proxyConn.conn, s.config.CompressDecode, s.config.Crypt, s.config.Mux)
+ go utils.Relay(proxyConn.Conn, c, s.config.CompressEncode, s.config.Crypt, s.config.Mux)
+ utils.Relay(c, proxyConn.Conn, s.config.CompressDecode, s.config.Crypt, s.config.Mux)
}
}
//new conn
-func (s *Sock5ModeServer) handleNewConn(c net.Conn) {
+func (s *Sock5ModeServer) handleConn(c net.Conn) {
buf := make([]byte, 2)
if _, err := io.ReadFull(c, buf); err != nil {
log.Println("negotiation err", err)
@@ -285,6 +287,7 @@ func (s *Sock5ModeServer) Auth(c net.Conn) error {
//start
func (s *Sock5ModeServer) Start() error {
+ var err error
s.listener, err = net.Listen("tcp", ":"+strconv.Itoa(s.config.TcpPort))
if err != nil {
return err
@@ -297,7 +300,7 @@ func (s *Sock5ModeServer) Start() error {
}
log.Fatal("accept error: ", err)
}
- go s.handleNewConn(conn)
+ go s.handleConn(conn)
}
return nil
}
@@ -308,7 +311,7 @@ func (s *Sock5ModeServer) Close() error {
}
//new
-func NewSock5ModeServer(bridge *Tunnel, cnf *ServerConfig) *Sock5ModeServer {
+func NewSock5ModeServer(bridge *bridge.Tunnel, cnf *ServerConfig) *Sock5ModeServer {
s := new(Sock5ModeServer)
s.bridge = bridge
s.config = cnf
diff --git a/server/tcp.go b/server/tcp.go
new file mode 100755
index 0000000..a7ccc26
--- /dev/null
+++ b/server/tcp.go
@@ -0,0 +1,191 @@
+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 process func(c *utils.Conn, s *TunnelModeServer) error
+
+type TunnelModeServer struct {
+ process process
+ bridge *bridge.Tunnel
+ config *ServerConfig
+ listener *net.TCPListener
+}
+
+//tcp|http|host
+func NewTunnelModeServer(process process, bridge *bridge.Tunnel, cnf *ServerConfig) *TunnelModeServer {
+ s := new(TunnelModeServer)
+ s.bridge = bridge
+ s.process = process
+ s.config = cnf
+ return s
+}
+
+//开始
+func (s *TunnelModeServer) Start() error {
+ var err error
+ s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.config.TcpPort, ""})
+ if err != nil {
+ return err
+ }
+ for {
+ conn, err := s.listener.AcceptTCP()
+ if err != nil {
+ if strings.Contains(err.Error(), "use of closed network connection") {
+ break
+ }
+ log.Println(err)
+ continue
+ }
+ go s.process(utils.NewConn(conn), s)
+ }
+ 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.Unauthorized_BYTES))
+ c.Close()
+ return errors.New("401 Unauthorized")
+ }
+ return nil
+}
+
+//与客户端建立通道
+func (s *TunnelModeServer) dealClient(c *utils.Conn, cnf *ServerConfig, addr string, method string, rb []byte) error {
+ link, err := s.bridge.GetTunnel(getverifyval(cnf.VerifyKey), cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, cnf.Mux)
+ defer func() {
+ if cnf.Mux {
+ s.bridge.ReturnTunnel(link, getverifyval(cnf.VerifyKey))
+ } else {
+ c.Close()
+ }
+ }()
+ if err != nil {
+ log.Println(err)
+ c.Close()
+ return err
+ }
+ if _, err := link.WriteHost(utils.CONN_TCP, addr); err != nil {
+ c.Close()
+ link.Close()
+ log.Println(err)
+ 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 {
+ link.WriteTo(rb, cnf.CompressEncode, cnf.Crypt)
+ }
+ go utils.Relay(link.Conn, c.Conn, cnf.CompressEncode, cnf.Crypt, cnf.Mux)
+ utils.Relay(c.Conn, link.Conn, cnf.CompressDecode, cnf.Crypt, cnf.Mux)
+ }
+ }
+ return nil
+}
+
+//close
+func (s *TunnelModeServer) Close() error {
+ return s.listener.Close()
+}
+
+//tcp隧道模式
+func ProcessTunnel(c *utils.Conn, s *TunnelModeServer) error {
+ method, _, rb, err, r := c.GetHost()
+ if err == nil {
+ if err := s.auth(r, c, s.config.U, s.config.P); err != nil {
+ return err
+ }
+ }
+ return s.dealClient(c, s.config, s.config.Target, method, rb)
+}
+
+//http代理模式
+func ProcessHttp(c *utils.Conn, s *TunnelModeServer) error {
+ method, addr, rb, err, r := c.GetHost()
+ if err != nil {
+ c.Close()
+ return err
+ }
+ if err := s.auth(r, c, s.config.U, s.config.P); err != nil {
+ return err
+ }
+ //TODO效率问题
+ return s.dealClient(c, s.config, addr, method, rb)
+}
+
+//多客户端域名代理
+func ProcessHost(c *utils.Conn, s *TunnelModeServer) error {
+ method, addr, rb, err, r := c.GetHost()
+ if err != nil {
+ c.Close()
+ return err
+ }
+ host, task, err := GetKeyByHost(addr)
+ if err != nil {
+ return err
+ }
+ if err := s.auth(r, c, task.U, task.P); err != nil {
+ return err
+ }
+ if err != nil {
+ c.Close()
+ return err
+ }
+ return s.dealClient(c, task, host.Target, method, rb)
+}
+
+//web管理方式
+type WebServer struct {
+ bridge *bridge.Tunnel
+}
+
+//开始
+func (s *WebServer) Start() {
+ beego.BConfig.WebConfig.Session.SessionOn = true
+ log.Println("web管理启动,访问端口为", beego.AppConfig.String("httpport"))
+ beego.SetViewsPath(beego.AppPath + "/web/views")
+ beego.SetStaticPath("/static/", "/web/static")
+ beego.Run()
+}
+
+//new
+func NewWebServer(bridge *bridge.Tunnel) *WebServer {
+ s := new(WebServer)
+ s.bridge = bridge
+ return s
+}
+
+//host
+type HostServer struct {
+ config *ServerConfig
+}
+
+//开始
+func (s *HostServer) Start() error {
+ return nil
+}
+
+func NewHostServer(cnf *ServerConfig) *HostServer {
+ s := new(HostServer)
+ s.config = cnf
+ return s
+}
+
+//close
+func (s *HostServer) Close() error {
+ return nil
+}
diff --git a/lib/udp.go b/server/udp.go
similarity index 77%
rename from lib/udp.go
rename to server/udp.go
index d4a1b49..4a54f10 100755
--- a/lib/udp.go
+++ b/server/udp.go
@@ -1,6 +1,8 @@
-package lib
+package server
import (
+ "github.com/cnlh/easyProxy/bridge"
+ "github.com/cnlh/easyProxy/utils"
"io"
"log"
"net"
@@ -8,22 +10,23 @@ import (
)
type UdpModeServer struct {
- bridge *Tunnel
+ bridge *bridge.Tunnel
listener *net.UDPConn
- udpMap map[string]*Conn
+ udpMap map[string]*utils.Conn
config *ServerConfig
}
-func NewUdpModeServer(bridge *Tunnel, cnf *ServerConfig) *UdpModeServer {
+func NewUdpModeServer(bridge *bridge.Tunnel, cnf *ServerConfig) *UdpModeServer {
s := new(UdpModeServer)
s.bridge = bridge
- s.udpMap = make(map[string]*Conn)
+ s.udpMap = make(map[string]*utils.Conn)
s.config = cnf
return s
}
//开始
func (s *UdpModeServer) Start() error {
+ var err error
s.listener, err = net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), s.config.TcpPort, ""})
if err != nil {
return err
@@ -49,7 +52,7 @@ func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) {
log.Println(err)
return
}
- if _, err := conn.WriteHost(CONN_UDP, s.config.Target); err != nil {
+ if _, err := conn.WriteHost(utils.CONN_UDP, s.config.Target); err != nil {
conn.Close()
return
}
@@ -61,7 +64,7 @@ func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) {
conn.Close()
}
}()
- if flag == CONN_SUCCESS {
+ if flag == utils.CONN_SUCCESS {
conn.WriteTo(data, s.config.CompressEncode, s.config.Crypt)
buf := make([]byte, 1024)
//conn.conn.SetReadDeadline(time.Now().Add(time.Duration(time.Second * 3)))
@@ -71,7 +74,7 @@ func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) {
return
}
s.listener.WriteToUDP(buf[:n], addr)
- conn.WriteTo([]byte(IO_EOF), s.config.CompressEncode, s.config.Crypt)
+ conn.WriteTo([]byte(utils.IO_EOF), s.config.CompressEncode, s.config.Crypt)
}
}
}
diff --git a/lib/conn.go b/utils/conn.go
similarity index 90%
rename from lib/conn.go
rename to utils/conn.go
index bf95467..0be6292 100755
--- a/lib/conn.go
+++ b/utils/conn.go
@@ -1,4 +1,4 @@
-package lib
+package utils
import (
"bufio"
@@ -16,6 +16,8 @@ import (
"time"
)
+const cryptKey = "1234567812345678"
+
type CryptConn struct {
conn net.Conn
crypt bool
@@ -126,13 +128,13 @@ func (s *SnappyConn) Read(b []byte) (n int, err error) {
}
type Conn struct {
- conn net.Conn
+ Conn net.Conn
}
//new conn
func NewConn(conn net.Conn) *Conn {
c := new(Conn)
- c.conn = conn
+ c.Conn = conn
return c
}
@@ -160,6 +162,7 @@ func (s *Conn) GetLen() (int, error) {
//写入长度+内容 粘包
func (s *Conn) WriteLen(buf []byte) (int, error) {
var b []byte
+ var err error
if b, err = GetLenBytes(buf); err != nil {
return 0, err
}
@@ -209,7 +212,7 @@ func (s *Conn) WriteHost(ltype string, host string) (int, error) {
//设置连接为长连接
func (s *Conn) SetAlive() {
- conn := s.conn.(*net.TCPConn)
+ conn := s.Conn.(*net.TCPConn)
conn.SetReadDeadline(time.Time{})
conn.SetKeepAlive(true)
conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
@@ -247,17 +250,17 @@ func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.
//单独读(加密|压缩)
func (s *Conn) ReadFrom(b []byte, compress int, crypt bool) (int, error) {
if COMPRESS_SNAPY_DECODE == compress {
- return NewSnappyConn(s.conn, crypt).Read(b)
+ return NewSnappyConn(s.Conn, crypt).Read(b)
}
- return NewCryptConn(s.conn, crypt).Read(b)
+ return NewCryptConn(s.Conn, crypt).Read(b)
}
//单独写(加密|压缩)
func (s *Conn) WriteTo(b []byte, compress int, crypt bool) (n int, err error) {
if COMPRESS_SNAPY_ENCODE == compress {
- return NewSnappyConn(s.conn, crypt).Write(b)
+ return NewSnappyConn(s.Conn, crypt).Write(b)
}
- return NewCryptConn(s.conn, crypt).Write(b)
+ return NewCryptConn(s.Conn, crypt).Write(b)
}
//写压缩方式,加密
@@ -268,7 +271,6 @@ func (s *Conn) WriteConnInfo(en, de int, crypt, mux bool) {
//获取压缩方式,是否加密
func (s *Conn) GetConnInfoFromConn() (en, de int, crypt, mux bool) {
buf, err := s.ReadLen(4)
- //TODO:错误处理
if err != nil {
return
}
@@ -281,51 +283,51 @@ func (s *Conn) GetConnInfoFromConn() (en, de int, crypt, mux bool) {
//close
func (s *Conn) Close() error {
- return s.conn.Close()
+ return s.Conn.Close()
}
//write
func (s *Conn) Write(b []byte) (int, error) {
- return s.conn.Write(b)
+ return s.Conn.Write(b)
}
//read
func (s *Conn) Read(b []byte) (int, error) {
- return s.conn.Read(b)
+ return s.Conn.Read(b)
}
//write error
-func (s *Conn) wError() (int, error) {
+func (s *Conn) WriteError() (int, error) {
return s.Write([]byte(RES_MSG))
}
//write sign flag
-func (s *Conn) wSign() (int, error) {
+func (s *Conn) WriteSign() (int, error) {
return s.Write([]byte(RES_SIGN))
}
//write main
-func (s *Conn) wMain() (int, error) {
+func (s *Conn) WriteMain() (int, error) {
return s.Write([]byte(WORK_MAIN))
}
//write chan
-func (s *Conn) wChan() (int, error) {
+func (s *Conn) WriteChan() (int, error) {
return s.Write([]byte(WORK_CHAN))
}
//write test
-func (s *Conn) wTest() (int, error) {
+func (s *Conn) WriteTest() (int, error) {
return s.Write([]byte(TEST_FLAG))
}
//write test
-func (s *Conn) wSuccess() (int, error) {
+func (s *Conn) WriteSuccess() (int, error) {
return s.Write([]byte(CONN_SUCCESS))
}
//write test
-func (s *Conn) wFail() (int, error) {
+func (s *Conn) WriteFail() (int, error) {
return s.Write([]byte(CONN_ERROR))
}
diff --git a/lib/crypt.go b/utils/crypt.go
similarity index 98%
rename from lib/crypt.go
rename to utils/crypt.go
index 1cfde74..a4a4880 100644
--- a/lib/crypt.go
+++ b/utils/crypt.go
@@ -1,4 +1,4 @@
-package lib
+package utils
import (
"bytes"
@@ -6,7 +6,7 @@ import (
"crypto/cipher"
"crypto/md5"
"encoding/hex"
- "github.com/pkg/errors"
+ "errors"
"math/rand"
"time"
)
diff --git a/utils/util.go b/utils/util.go
new file mode 100755
index 0000000..f631f3a
--- /dev/null
+++ b/utils/util.go
@@ -0,0 +1,204 @@
+package utils
+
+import (
+ "encoding/base64"
+ "io"
+ "log"
+ "net"
+ "net/http"
+ "regexp"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+const (
+ COMPRESS_NONE_ENCODE = iota
+ COMPRESS_NONE_DECODE
+ COMPRESS_SNAPY_ENCODE
+ COMPRESS_SNAPY_DECODE
+ VERIFY_EER = "vkey"
+ WORK_MAIN = "main"
+ WORK_CHAN = "chan"
+ RES_SIGN = "sign"
+ RES_MSG = "msg0"
+ CONN_SUCCESS = "sucs"
+ CONN_ERROR = "fail"
+ TEST_FLAG = "tst"
+ CONN_TCP = "tcp"
+ CONN_UDP = "udp"
+ Unauthorized_BYTES = `HTTP/1.1 401 Unauthorized
+Content-Type: text/plain; charset=utf-8
+WWW-Authenticate: Basic realm="easyProxy"
+
+401 Unauthorized`
+ IO_EOF = "PROXYEOF"
+)
+
+//copy
+func Relay(in, out net.Conn, compressType int, crypt, mux bool) {
+ switch compressType {
+ case COMPRESS_SNAPY_ENCODE:
+ copyBuffer(NewSnappyConn(in, crypt), out)
+ if mux {
+ out.Close()
+ NewSnappyConn(in, crypt).Write([]byte(IO_EOF))
+ }
+ case COMPRESS_SNAPY_DECODE:
+ copyBuffer(in, NewSnappyConn(out, crypt))
+ if mux {
+ in.Close()
+ }
+ case COMPRESS_NONE_ENCODE:
+ copyBuffer(NewCryptConn(in, crypt), out)
+ if mux {
+ out.Close()
+ NewCryptConn(in, crypt).Write([]byte(IO_EOF))
+ }
+ case COMPRESS_NONE_DECODE:
+ copyBuffer(in, NewCryptConn(out, crypt))
+ if mux {
+ in.Close()
+ }
+ }
+ if !mux {
+ in.Close()
+ out.Close()
+ }
+}
+
+//判断压缩方式
+func GetCompressType(compress string) (int, int) {
+ switch compress {
+ case "":
+ return COMPRESS_NONE_DECODE, COMPRESS_NONE_ENCODE
+ case "snappy":
+ return COMPRESS_SNAPY_DECODE, COMPRESS_SNAPY_ENCODE
+ default:
+ log.Fatalln("数据压缩格式错误")
+ }
+ return COMPRESS_NONE_DECODE, COMPRESS_NONE_ENCODE
+}
+
+//通过host获取对应的ip地址
+func Gethostbyname(hostname string) string {
+ if !DomainCheck(hostname) {
+ return hostname
+ }
+ ips, _ := net.LookupIP(hostname)
+ if ips != nil {
+ for _, v := range ips {
+ if v.To4() != nil {
+ return v.String()
+ }
+ }
+ }
+ return ""
+}
+
+//检查是否是域名
+func DomainCheck(domain string) bool {
+ var match bool
+ IsLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}(/)"
+ NotLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}"
+ match, _ = regexp.MatchString(IsLine, domain)
+ if !match {
+ match, _ = regexp.MatchString(NotLine, domain)
+ }
+ return match
+}
+
+//检查basic认证
+func CheckAuth(r *http.Request, user, passwd string) bool {
+ s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
+ if len(s) != 2 {
+ return false
+ }
+
+ b, err := base64.StdEncoding.DecodeString(s[1])
+ if err != nil {
+ return false
+ }
+
+ pair := strings.SplitN(string(b), ":", 2)
+ if len(pair) != 2 {
+ return false
+ }
+ return pair[0] == user && pair[1] == passwd
+}
+
+//get bool by str
+func GetBoolByStr(s string) bool {
+ switch s {
+ case "1", "true":
+ return true
+ }
+ return false
+}
+
+//get str by bool
+func GetStrByBool(b bool) string {
+ if b {
+ return "1"
+ }
+ return "0"
+}
+
+//int
+func GetIntNoerrByStr(str string) int {
+ i, _ := strconv.Atoi(str)
+ return i
+}
+
+var bufPool = sync.Pool{
+ New: func() interface{} {
+ return make([]byte, 65535)
+ },
+}
+// io.copy的优化版,读取buffer长度原为32*1024,与snappy不同,导致读取出的内容存在差异,不利于解密,特此修改
+func copyBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
+ //TODO 回收问题
+ buf := bufPool.Get().([]byte)
+ for {
+ nr, er := src.Read(buf)
+ if nr > 0 {
+ nw, ew := dst.Write(buf[0:nr])
+ if nw > 0 {
+ written += int64(nw)
+ }
+ if ew != nil {
+ err = ew
+ break
+ }
+ if nr != nw {
+ err = io.ErrShortWrite
+ break
+ }
+ }
+ 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)
+ for {
+ if _, err := c.Read(buf); err != nil {
+ break
+ }
+ }
+ c.SetReadDeadline(time.Time{})
+}
+
+//简单的一个校验值
+func Getverifyval(vkey string) string {
+ return Md5(vkey)
+}
diff --git a/controllers/base.go b/web/controllers/base.go
similarity index 93%
rename from controllers/base.go
rename to web/controllers/base.go
index b81143b..9e4fbbc 100755
--- a/controllers/base.go
+++ b/web/controllers/base.go
@@ -2,7 +2,8 @@ package controllers
import (
"github.com/astaxie/beego"
- "github.com/cnlh/easyProxy/lib"
+ "github.com/cnlh/easyProxy/server"
+ "github.com/cnlh/easyProxy/utils"
"strconv"
"strings"
)
@@ -33,8 +34,8 @@ func (s *BaseController) display(tpl ...string) {
}
s.Data["menu"] = s.actionName
ip := s.Ctx.Request.Host
- s.Data["ip"] = lib.Gethostbyname(ip[0:strings.LastIndex(ip, ":")])
- s.Data["p"] = *lib.TcpPort
+ s.Data["ip"] = utils.Gethostbyname(ip[0:strings.LastIndex(ip, ":")])
+ s.Data["p"] = server.Bridge.TunnelPort
s.Data["proxyPort"] = beego.AppConfig.String("hostPort")
s.Layout = "public/layout.html"
s.TplName = tplname
diff --git a/controllers/index.go b/web/controllers/index.go
similarity index 73%
rename from controllers/index.go
rename to web/controllers/index.go
index a6fce5d..73cd5bb 100755
--- a/controllers/index.go
+++ b/web/controllers/index.go
@@ -1,7 +1,8 @@
package controllers
import (
- "github.com/cnlh/easyProxy/lib"
+ "github.com/cnlh/easyProxy/server"
+ "github.com/cnlh/easyProxy/utils"
)
type IndexController struct {
@@ -27,7 +28,7 @@ func (s *IndexController) Udp() {
func (s *IndexController) Socks5() {
s.SetInfo("socks5管理")
- s.SetType("sock5Server")
+ s.SetType("socks5Server")
s.display("index/list")
}
@@ -46,7 +47,7 @@ func (s *IndexController) Host() {
func (s *IndexController) GetServerConfig() {
start, length := s.GetAjaxParams()
taskType := s.GetString("type")
- list, cnt := lib.CsvDb.GetServerConfig(start, length, taskType)
+ list, cnt := server.GetServerConfig(start, length, taskType)
s.AjaxTable(list, cnt, cnt)
}
@@ -56,20 +57,20 @@ func (s *IndexController) Add() {
s.SetInfo("新增")
s.display()
} else {
- t := &lib.ServerConfig{
+ t := &server.ServerConfig{
TcpPort: s.GetIntNoErr("port"),
Mode: s.GetString("type"),
Target: s.GetString("target"),
- VerifyKey: lib.GetRandomString(16),
+ VerifyKey: utils.GetRandomString(16),
U: s.GetString("u"),
P: s.GetString("p"),
Compress: s.GetString("compress"),
- Crypt: lib.GetBoolByStr(s.GetString("crypt")),
- Mux: lib.GetBoolByStr(s.GetString("mux")),
+ Crypt: utils.GetBoolByStr(s.GetString("crypt")),
+ Mux: utils.GetBoolByStr(s.GetString("mux")),
IsRun: 0,
}
- lib.CsvDb.NewTask(t)
- if err := lib.AddTask(t); err != nil {
+ server.CsvDb.NewTask(t)
+ if err := server.AddTask(t); err != nil {
s.AjaxErr(err.Error())
} else {
s.AjaxOk("添加成功")
@@ -80,7 +81,7 @@ func (s *IndexController) Add() {
func (s *IndexController) Edit() {
if s.Ctx.Request.Method == "GET" {
vKey := s.GetString("vKey")
- if t, err := lib.CsvDb.GetTask(vKey); err != nil {
+ if t, err := server.CsvDb.GetTask(vKey); err != nil {
s.error()
} else {
s.Data["t"] = t
@@ -89,7 +90,7 @@ func (s *IndexController) Edit() {
s.display()
} else {
vKey := s.GetString("vKey")
- if t, err := lib.CsvDb.GetTask(vKey); err != nil {
+ if t, err := server.CsvDb.GetTask(vKey); err != nil {
s.error()
} else {
t.TcpPort = s.GetIntNoErr("port")
@@ -98,11 +99,11 @@ func (s *IndexController) Edit() {
t.U = s.GetString("u")
t.P = s.GetString("p")
t.Compress = s.GetString("compress")
- t.Crypt = lib.GetBoolByStr(s.GetString("crypt"))
- t.Mux = lib.GetBoolByStr(s.GetString("mux"))
- lib.CsvDb.UpdateTask(t)
- lib.StopServer(t.VerifyKey)
- lib.StartTask(t.VerifyKey)
+ t.Crypt = utils.GetBoolByStr(s.GetString("crypt"))
+ t.Mux = utils.GetBoolByStr(s.GetString("mux"))
+ server.CsvDb.UpdateTask(t)
+ server.StopServer(t.VerifyKey)
+ server.StartTask(t.VerifyKey)
}
s.AjaxOk("修改成功")
}
@@ -110,14 +111,14 @@ func (s *IndexController) Edit() {
func (s *IndexController) Stop() {
vKey := s.GetString("vKey")
- if err := lib.StopServer(vKey); err != nil {
+ if err := server.StopServer(vKey); err != nil {
s.AjaxErr("停止失败")
}
s.AjaxOk("停止成功")
}
func (s *IndexController) Del() {
vKey := s.GetString("vKey")
- if err := lib.DelTask(vKey); err != nil {
+ if err := server.DelTask(vKey); err != nil {
s.AjaxErr("删除失败")
}
s.AjaxOk("删除成功")
@@ -125,7 +126,7 @@ func (s *IndexController) Del() {
func (s *IndexController) Start() {
vKey := s.GetString("vKey")
- if err := lib.StartTask(vKey); err != nil {
+ if err := server.StartTask(vKey); err != nil {
s.AjaxErr("开启失败")
}
s.AjaxOk("开启成功")
@@ -139,14 +140,14 @@ func (s *IndexController) HostList() {
} else {
start, length := s.GetAjaxParams()
vkey := s.GetString("vkey")
- list, cnt := lib.CsvDb.GetHostList(start, length, vkey)
+ list, cnt := server.CsvDb.GetHostList(start, length, vkey)
s.AjaxTable(list, cnt, cnt)
}
}
func (s *IndexController) DelHost() {
host := s.GetString("host")
- if err := lib.CsvDb.DelHost(host); err != nil {
+ if err := server.CsvDb.DelHost(host); err != nil {
s.AjaxErr("删除失败")
}
s.AjaxOk("删除成功")
@@ -158,12 +159,12 @@ func (s *IndexController) AddHost() {
s.SetInfo("新增")
s.display("index/hadd")
} else {
- h := &lib.HostList{
+ h := &server.HostList{
Vkey: s.GetString("vkey"),
Host: s.GetString("host"),
Target: s.GetString("target"),
}
- lib.CsvDb.NewHost(h)
+ server.CsvDb.NewHost(h)
s.AjaxOk("添加成功")
}
}
diff --git a/controllers/login.go b/web/controllers/login.go
similarity index 100%
rename from controllers/login.go
rename to web/controllers/login.go
diff --git a/routers/router.go b/web/routers/router.go
similarity index 83%
rename from routers/router.go
rename to web/routers/router.go
index 7e31e70..c411dbb 100755
--- a/routers/router.go
+++ b/web/routers/router.go
@@ -2,7 +2,7 @@ package routers
import (
"github.com/astaxie/beego"
- "github.com/cnlh/easyProxy/controllers"
+ "github.com/cnlh/easyProxy/web/controllers"
)
func init() {
diff --git a/static/css/font-awesome.min.css b/web/static/css/font-awesome.min.css
similarity index 100%
rename from static/css/font-awesome.min.css
rename to web/static/css/font-awesome.min.css
diff --git a/static/css/main.css b/web/static/css/main.css
similarity index 100%
rename from static/css/main.css
rename to web/static/css/main.css
diff --git a/static/fonts/FontAwesome.otf b/web/static/fonts/FontAwesome.otf
similarity index 100%
rename from static/fonts/FontAwesome.otf
rename to web/static/fonts/FontAwesome.otf
diff --git a/static/fonts/fontawesome-webfont.eot b/web/static/fonts/fontawesome-webfont.eot
similarity index 100%
rename from static/fonts/fontawesome-webfont.eot
rename to web/static/fonts/fontawesome-webfont.eot
diff --git a/static/fonts/fontawesome-webfont.svg b/web/static/fonts/fontawesome-webfont.svg
similarity index 100%
rename from static/fonts/fontawesome-webfont.svg
rename to web/static/fonts/fontawesome-webfont.svg
diff --git a/static/fonts/fontawesome-webfont.ttf b/web/static/fonts/fontawesome-webfont.ttf
similarity index 100%
rename from static/fonts/fontawesome-webfont.ttf
rename to web/static/fonts/fontawesome-webfont.ttf
diff --git a/static/fonts/fontawesome-webfont.woff b/web/static/fonts/fontawesome-webfont.woff
similarity index 100%
rename from static/fonts/fontawesome-webfont.woff
rename to web/static/fonts/fontawesome-webfont.woff
diff --git a/static/fonts/fontawesome-webfont.woff2 b/web/static/fonts/fontawesome-webfont.woff2
similarity index 100%
rename from static/fonts/fontawesome-webfont.woff2
rename to web/static/fonts/fontawesome-webfont.woff2
diff --git a/static/img/48.jpg b/web/static/img/48.jpg
similarity index 100%
rename from static/img/48.jpg
rename to web/static/img/48.jpg
diff --git a/static/img/favicon.ico b/web/static/img/favicon.ico
similarity index 100%
rename from static/img/favicon.ico
rename to web/static/img/favicon.ico
diff --git a/static/js/datatables.min.js b/web/static/js/datatables.min.js
similarity index 100%
rename from static/js/datatables.min.js
rename to web/static/js/datatables.min.js
diff --git a/static/js/main.js b/web/static/js/main.js
similarity index 100%
rename from static/js/main.js
rename to web/static/js/main.js
diff --git a/views/index/add.html b/web/views/index/add.html
similarity index 91%
rename from views/index/add.html
rename to web/views/index/add.html
index 1ba6341..107c8aa 100755
--- a/views/index/add.html
+++ b/web/views/index/add.html
@@ -17,7 +17,7 @@
@@ -75,7 +75,7 @@
arr["all"] = ["type", "port", "compress", "u", "p", "target"]
arr["tunnelServer"] = ["type", "port", "target", "compress", "u", "p", "tcp隧道模式,提供一条tcp隧道,适用于ssh、远程桌面等,添加后会自动生成一个客户端验证key
在内网机器执行./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口
建立成功后,访问公网服务器的设定端口,则相当于访问内网目标地址的目标端口"]
arr["udpServer"] = ["type", "port", "target", "compress", "udp隧道模式,提供一条udp隧道,适用于dns、内网dns访问等,添加后会自动生成一个客户端验证key
在内网机器执行./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口
建立成功后,访问公网服务器的设定端口,则相当于访问内网目标地址的udp目标端口"]
- arr["sock5Server"] = ["type", "port", "compress", "u", "p", "socks5代理模式,内网socks5代理,配合proxifer,可如同使用vpn一样访问内网设备或资源,添加后会自动生成一个客户端验证key
在内网机器执行./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口
建立成功后,在外网环境下本机配置socks5代理,即访问内网设备或者资源 "]
+ arr["socks5Server"] = ["type", "port", "compress", "u", "p", "socks5代理模式,内网socks5代理,配合proxifer,可如同使用vpn一样访问内网设备或资源,添加后会自动生成一个客户端验证key
在内网机器执行./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口
建立成功后,在外网环境下本机配置socks5代理,即访问内网设备或者资源 "]
arr["httpProxyServer"] = ["type", "port", "compress", "u", "p", " http代理模式,内网http代理,可访问内网网站,添加后会自动生成一个客户端验证key
在内网机器执行./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口
建立成功后,在外网环境下本机配置http代理,即访问内网站点"]
arr["hostServer"] = ["type", "compress", "u", "p", "域名分发模式,使用域名代理内网服务,适用于小程序开发、公众号开发、站点演示等,添加后会自动生成一个客户端验证key
在内网机器执行./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口
建立成功后,使用nginx将请求反向代理到本程序,再进行域名配置,即可解析"]
diff --git a/views/index/edit.html b/web/views/index/edit.html
similarity index 96%
rename from views/index/edit.html
rename to web/views/index/edit.html
index 34e561d..3e1dc16 100755
--- a/views/index/edit.html
+++ b/web/views/index/edit.html
@@ -10,7 +10,7 @@
@@ -72,7 +72,7 @@
arr["all"] = ["type", "port", "compress", "u", "p", "target"]
arr["tunnelServer"] = ["type", "port", "target", "u", "p", "compress"]
arr["udpServer"] = ["type", "port", "target", "compress"]
- arr["sock5Server"] = ["type", "port", "compress", "u", "p"]
+ arr["socks5Server"] = ["type", "port", "compress", "u", "p"]
arr["httpProxyServer"] = ["type", "port", "compress", "u", "p"]
arr["hostServer"] = ["type", "compress", "u", "p"]
diff --git a/views/index/hadd.html b/web/views/index/hadd.html
similarity index 100%
rename from views/index/hadd.html
rename to web/views/index/hadd.html
diff --git a/views/index/hlist.html b/web/views/index/hlist.html
similarity index 100%
rename from views/index/hlist.html
rename to web/views/index/hlist.html
diff --git a/views/index/index.html b/web/views/index/index.html
similarity index 100%
rename from views/index/index.html
rename to web/views/index/index.html
diff --git a/views/index/list.html b/web/views/index/list.html
similarity index 100%
rename from views/index/list.html
rename to web/views/index/list.html
diff --git a/views/login/index.html b/web/views/login/index.html
similarity index 100%
rename from views/login/index.html
rename to web/views/login/index.html
diff --git a/views/public/error.html b/web/views/public/error.html
similarity index 100%
rename from views/public/error.html
rename to web/views/public/error.html
diff --git a/views/public/layout.html b/web/views/public/layout.html
similarity index 100%
rename from views/public/layout.html
rename to web/views/public/layout.html