From 1f61b99387f003bca69d7f18b8fd35bcf9dcc1d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B2=B3?= Date: Wed, 9 Jan 2019 20:33:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=AB=AF=E5=88=86=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 144 +------- bridge/bridge.go | 241 +++++++++++++ {lib => client}/client.go | 87 ++--- cmd/proxy_client.go | 27 ++ cmd/proxy_server.go | 47 +++ conf/app.conf | 2 +- conf/hosts.csv | 2 +- conf/tasks.csv | 4 +- lib/bridge.go | 228 ------------ lib/init.go | 201 ----------- lib/tcp.go | 326 ----------------- lib/util.go | 330 ------------------ main.go | 10 - {lib => server}/file.go | 84 +---- server/server.go | 209 +++++++++++ {lib => server}/socks5.go | 29 +- server/tcp.go | 191 ++++++++++ {lib => server}/udp.go | 19 +- {lib => utils}/conn.go | 40 ++- {lib => utils}/crypt.go | 4 +- utils/util.go | 204 +++++++++++ {controllers => web/controllers}/base.go | 7 +- {controllers => web/controllers}/index.go | 47 +-- {controllers => web/controllers}/login.go | 0 {routers => web/routers}/router.go | 2 +- .../static}/css/font-awesome.min.css | 0 {static => web/static}/css/main.css | 0 {static => web/static}/fonts/FontAwesome.otf | Bin .../static}/fonts/fontawesome-webfont.eot | Bin .../static}/fonts/fontawesome-webfont.svg | 0 .../static}/fonts/fontawesome-webfont.ttf | Bin .../static}/fonts/fontawesome-webfont.woff | Bin .../static}/fonts/fontawesome-webfont.woff2 | Bin {static => web/static}/img/48.jpg | Bin {static => web/static}/img/favicon.ico | Bin {static => web/static}/js/datatables.min.js | 0 {static => web/static}/js/main.js | 0 {views => web/views}/index/add.html | 4 +- {views => web/views}/index/edit.html | 4 +- {views => web/views}/index/hadd.html | 0 {views => web/views}/index/hlist.html | 0 {views => web/views}/index/index.html | 0 {views => web/views}/index/list.html | 0 {views => web/views}/login/index.html | 0 {views => web/views}/public/error.html | 0 {views => web/views}/public/layout.html | 0 46 files changed, 1062 insertions(+), 1431 deletions(-) create mode 100755 bridge/bridge.go rename {lib => client}/client.go (56%) create mode 100644 cmd/proxy_client.go create mode 100644 cmd/proxy_server.go delete mode 100755 lib/bridge.go delete mode 100644 lib/init.go delete mode 100755 lib/tcp.go delete mode 100755 lib/util.go delete mode 100755 main.go rename {lib => server}/file.go (76%) create mode 100644 server/server.go rename {lib => server}/socks5.go (89%) create mode 100755 server/tcp.go rename {lib => server}/udp.go (77%) rename {lib => utils}/conn.go (90%) rename {lib => utils}/crypt.go (98%) create mode 100755 utils/util.go rename {controllers => web/controllers}/base.go (93%) rename {controllers => web/controllers}/index.go (73%) rename {controllers => web/controllers}/login.go (100%) rename {routers => web/routers}/router.go (83%) rename {static => web/static}/css/font-awesome.min.css (100%) rename {static => web/static}/css/main.css (100%) rename {static => web/static}/fonts/FontAwesome.otf (100%) rename {static => web/static}/fonts/fontawesome-webfont.eot (100%) rename {static => web/static}/fonts/fontawesome-webfont.svg (100%) rename {static => web/static}/fonts/fontawesome-webfont.ttf (100%) rename {static => web/static}/fonts/fontawesome-webfont.woff (100%) rename {static => web/static}/fonts/fontawesome-webfont.woff2 (100%) rename {static => web/static}/img/48.jpg (100%) rename {static => web/static}/img/favicon.ico (100%) rename {static => web/static}/js/datatables.min.js (100%) rename {static => web/static}/js/main.js (100%) rename {views => web/views}/index/add.html (91%) rename {views => web/views}/index/edit.html (96%) rename {views => web/views}/index/hadd.html (100%) rename {views => web/views}/index/hlist.html (100%) rename {views => web/views}/index/index.html (100%) rename {views => web/views}/index/list.html (100%) rename {views => web/views}/login/index.html (100%) rename {views => web/views}/public/error.html (100%) rename {views => web/views}/public/layout.html (100%) diff --git a/README.md b/README.md index 09ef27b..e56891a 100644 --- a/README.md +++ b/README.md @@ -11,19 +11,17 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透** ![image](https://github.com/cnlh/easyProxy/blob/master/image/web.png?raw=true) 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请求后,将请求发送到客户端,客户端再执行这个请求,并将结果返回给服务端,服务端收到后再返回。 - - -特点:支持同时代理多个站点,不同站点之间有联系还可以实现匹配替换 - - -![image](https://github.com/cnlh/easyProxy/blob/master/image/http.png?raw=true) - -**最终效果**: -- 访问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