diff --git a/README.md b/README.md index b93d700..795502f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # easyProxy ![](https://img.shields.io/github/stars/cnlh/easyProxy.svg) ![](https://img.shields.io/github/forks/cnlh/easyProxy.svg) ![](https://img.shields.io/github/license/cnlh/easyProxy.svg) -easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**代理服务器。目前支持**tcp、udp流量转发**,可支持任何tcp、udp上层协议(访问内网网站、本地支付接口调试、ssh访问、远程桌面,内网dns解析等等……),此外还**支持内网http代理、内网socks5代理**,可实现在非内网环境下如同使用vpn一样访问内网资源和设备的效果,同时**支持socks5验证,snnapy压缩(节省带宽和流量)、站点保护、加密传输、多路复用**。 +easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**代理服务器。目前支持**tcp、udp流量转发**,可支持任何tcp、udp上层协议(访问内网网站、本地支付接口调试、ssh访问、远程桌面,内网dns解析等等……),此外还**支持内网http代理、内网socks5代理**,可实现在非内网环境下如同使用vpn一样访问内网资源和设备的效果,同时**支持socks5验证,snnapy压缩(节省带宽和流量)、站点保护、加密传输、多路复用、host修改、自定义header**。 目前市面上提供类似服务的有花生壳、TeamView、GoToMyCloud等等,但要使用第三方的公网服务器就必须为第三方付费,并且这些服务都有各种各样的限制,此外,由于数据包会流经第三方,因此对数据安全也是一大隐患。 @@ -36,6 +36,8 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透** - [x] 支持TCP多路复用 - [x] 支持同时开多条tcp、udp隧道等等,且只需要开一个客户端和服务端 - [x] 支持一个服务端,多个客户端模式 +- [x] host修改支持 +- [x] 自定义设置header支持 ## 目录 @@ -49,7 +51,10 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透** 8. [站点密码保护](#站点保护) 9. [加密传输](#加密传输) 10. [TCP多路复用](#多路复用) -11. [配置文件说明](#配置文件) +11. [host修改](#host修改) +12. [自定义header](#自定义header) +13. [获取用户真实 IP](#获取用户真实 IP) + ## 安装 @@ -62,8 +67,9 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透** - 安装源码 > go get github.com/cnlh/easyProxy - 编译 -> go build cmd/proxy_server.go -> go build cmd/proxy_client.go +> go build cmd/server/proxy_server.go + +> go build cmd/client/proxy_client.go ## web管理模式 @@ -71,11 +77,22 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透** ### 介绍 可在网页上配置和管理各个tcp、udp隧道、内网站点代理等等,功能极为强大,操作也非常方便。 +### 服务端配置文件 +- /conf/app.conf + +名称 | 含义 +---|--- +httpport | web管理端口 +password | web界面管理密码 +hostPort | 域名代理模式监听端口 +tcpport | 服务端客户端通信端口 **提示:使用web模式时,服务端执行文件必须在项目根目录,否则无法正确加载配置文件** ### 使用 + + **有两种模式:** 1、单客户端模式,所有的隧道流量均从这个单客户端转发。 @@ -84,13 +101,11 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透** - 服务端 ``` - ./proxy_server -mode=webServer -tcpport=8284 -vkey=DKibZF5TXvic1g3kY + ./proxy_server -vkey=DKibZF5TXvic1g3kY ``` 名称 | 含义 ---|--- -mode | 运行模式 vkey | 验证密钥 -tcpport | 服务端与客户端通信端口 - 客户端 @@ -100,7 +115,7 @@ tcpport | 服务端与客户端通信端口 ``` - 配置 -进入web界面,公网ip:web界面端口(默认8080),密码为123 +进入web界面,公网ip:web界面端口(默认8080),密码默认为123 2、多客户端模式,不同的隧道流量均从不同的客户端转发。 @@ -108,12 +123,11 @@ tcpport | 服务端与客户端通信端口 - 服务端 ``` - ./proxy_server -mode=webServer -tcpport=8284 + ./proxy_server ``` 名称 | 含义 ---|--- mode | 运行模式 -tcpport | 服务端与客户端通信端口 - 客户端 进入web管理界面,有详细的命令 @@ -124,7 +138,6 @@ tcpport | 服务端与客户端通信端口 - ## tcp隧道模式 ### 场景及原理 @@ -354,16 +367,22 @@ easyProxy支持通过 HTTP Basic Auth 来保护你的 web 服务,使用户需 web管理中也可配置 +## host修改 -## 配置文件 -- /conf/app.conf +通常情况下本代理不会修改转发的任何数据。但有一些后端服务会根据 http 请求 header 中的 host 字段来展现不同的网站,例如 nginx 的虚拟主机服务,启用 host-header 的修改功能可以动态修改 http 请求中的 host 字段。该功能仅限于域名代理模式。 -名称 | 含义 ----|--- -httpport | web管理端口 -password | web界面管理密码 -hostPort | 域名代理模式监听端口 +**使用方法:在web管理中设置** + +## 自定义header + +支持对header进行新增或者修改,以配合服务的需要 + +## 获取用户真实 IP + +目前只有域名模式的代理支持这一功能,可以通过用户请求的 header 中的 X-Forwarded-For 和 X-Real-IP 来获取用户真实 IP。 + +**本代理前会在每一个请求中添加了这两个 header。** ## 操作系统支持 diff --git a/bridge/bridge.go b/bridge/bridge.go index 360bb31..1dbf915 100755 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -92,7 +92,6 @@ func (s *Tunnel) cliProcess(c *utils.Conn) error { 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 { @@ -106,6 +105,7 @@ func (s *Tunnel) cliProcess(c *utils.Conn) error { func (s *Tunnel) typeDeal(typeVal string, c *utils.Conn, cFlag string) error { switch typeVal { case utils.WORK_MAIN: + log.Println("客户端连接成功", c.Conn.RemoteAddr()) s.addList(s.SignalList, c, cFlag) case utils.WORK_CHAN: s.addList(s.TunnelList, c, cFlag) @@ -131,14 +131,13 @@ func (s *Tunnel) addList(m map[string]*list, c *utils.Conn, cFlag string) { //新建隧道 func (s *Tunnel) newChan(cFlag string) error { - if err := s.wait(s.SignalList, cFlag); err != nil { + var connPass *utils.Conn + var err error +retry: + if connPass, err = s.waitAndPop(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) + if _, err = connPass.Conn.Write([]byte("chan")); err != nil { goto retry } s.SignalList[cFlag].Add(connPass) @@ -152,10 +151,9 @@ func (s *Tunnel) GetTunnel(cFlag string, en, de int, crypt, mux bool) (c *utils. go s.newChan(cFlag) } retry: - if err = s.wait(s.TunnelList, cFlag); err != nil { + if c, err = s.waitAndPop(s.TunnelList, cFlag); err != nil { return } - c = s.TunnelList[cFlag].Pop() if _, err = c.WriteTest(); err != nil { c.Close() goto retry @@ -212,22 +210,26 @@ func (s *Tunnel) delClient(cFlag string, l map[string]*list) { } //等待 -func (s *Tunnel) wait(m map[string]*list, cFlag string) error { +func (s *Tunnel) waitAndPop(m map[string]*list, cFlag string) (c *utils.Conn, err error) { ticker := time.NewTicker(time.Millisecond * 100) stop := time.After(time.Second * 10) -loop: for { select { case <-ticker.C: - if _, ok := m[cFlag]; ok { + s.lock.Lock() + if v, ok := m[cFlag]; ok && v.Len() > 0 { + c = v.Pop() ticker.Stop() - break loop + s.lock.Unlock() + return } + s.lock.Unlock() case <-stop: - return errors.New("client key: " + cFlag + ",err: get client conn timeout") + err = errors.New("client key: " + cFlag + ",err: get client conn timeout") + return } } - return nil + return } func (s *Tunnel) verify(vKeyMd5 string) bool { diff --git a/client/client.go b/client/client.go index b992982..a57f0c7 100755 --- a/client/client.go +++ b/client/client.go @@ -122,6 +122,5 @@ func Process(c *utils.Conn, typeStr, host string, en, de int, crypt, mux bool) { return } c.WriteSuccess() - go utils.Relay(server, c.Conn, de, crypt, mux) - utils.Relay(c.Conn, server, en, crypt, mux) + utils.ReplayWaitGroup(c.Conn, server, en, de, crypt, mux) } diff --git a/cmd/proxy_client/proxy_client.go b/cmd/proxy_client/proxy_client.go index 8227369..56a20d4 100644 --- a/cmd/proxy_client/proxy_client.go +++ b/cmd/proxy_client/proxy_client.go @@ -14,9 +14,6 @@ var ( 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) diff --git a/cmd/proxy_server/proxy_server.go b/cmd/proxy_server/proxy_server.go index 0047cb6..057059a 100644 --- a/cmd/proxy_server/proxy_server.go +++ b/cmd/proxy_server/proxy_server.go @@ -2,6 +2,7 @@ package main import ( "flag" + "github.com/astaxie/beego" "github.com/cnlh/easyProxy/server" "github.com/cnlh/easyProxy/utils" _ "github.com/cnlh/easyProxy/web/routers" @@ -9,10 +10,9 @@ import ( ) var ( - configPath = flag.String("config", "config.json", "配置文件路径") - TcpPort = flag.Int("tcpport", 8284, "客户端与服务端通信端口") + TcpPort = flag.Int("tcpport", 0, "客户端与服务端通信端口") httpPort = flag.Int("httpport", 8024, "对外监听的端口") - rpMode = flag.String("mode", "client", "启动模式") + rpMode = flag.String("mode", "webServer", "启动模式") tunnelTarget = flag.String("target", "10.1.50.203:80", "远程目标") VerifyKey = flag.String("vkey", "", "验证密钥") u = flag.String("u", "", "验证用户名(socks5和web)") @@ -42,6 +42,15 @@ func main() { CompressEncode: 0, CompressDecode: 0, } + if *TcpPort == 0 { + p, err := beego.AppConfig.Int("tcpport") + if err == nil && *rpMode == "webServer" { + *TcpPort = p + } else { + *TcpPort = 8284 + } + } + log.SetFlags(log.Lshortfile) cnf.CompressDecode, cnf.CompressEncode = utils.GetCompressType(cnf.Compress) server.StartNewServer(*TcpPort, &cnf) } diff --git a/conf/app.conf b/conf/app.conf index 7451221..1f53dd5 100755 --- a/conf/app.conf +++ b/conf/app.conf @@ -11,3 +11,6 @@ password=123 #http监听端口 hostPort=80 + +##客户端与服务端通信端口 +tcpport=8284 diff --git a/conf/hosts.csv b/conf/hosts.csv index db883eb..8c1a9ff 100644 --- a/conf/hosts.csv +++ b/conf/hosts.csv @@ -1,3 +1,2 @@ -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,7n7bxc2bm1fyjfab +a.o.com,127.0.0.1:8082,7hiixust68kbz33a,,www.baidu.com +b.o.com,,7hiixust68kbz33a,,ab diff --git a/server/file.go b/server/file.go index 2a999c7..d64526a 100644 --- a/server/file.go +++ b/server/file.go @@ -23,14 +23,16 @@ type ServerConfig struct { ClientStatus int //客s户端状态 Crypt bool //是否加密 Mux bool //是否加密 - CompressEncode int - CompressDecode int + CompressEncode int //加密方式 + CompressDecode int //解密方式 } type HostList struct { - Vkey string //服务端与客户端通信端口 - Host string //启动方式 - Target string //目标 + Vkey string //服务端与客户端通信端口 + Host string //启动方式 + Target string //目标 + HeaderChange string //host修改 + HostChange string //host修改 } func NewCsv(runList map[string]interface{}) *Csv { @@ -125,64 +127,6 @@ func (s *Csv) LoadTaskFromCsv() { s.Tasks = tasks } -func (s *Csv) StoreHostToCsv() { - // 创建文件 - csvFile, err := os.Create(beego.AppPath + "/conf/hosts.csv") - if err != nil { - panic(err) - } - defer csvFile.Close() - // 获取csv的Writer - writer := csv.NewWriter(csvFile) - // 将map中的Post转换成slice,因为csv的Write需要slice参数 - // 并写入csv文件 - for _, host := range s.Hosts { - record := []string{ - host.Host, - host.Target, - host.Vkey, - } - err1 := writer.Write(record) - if err1 != nil { - panic(err1) - } - } - // 确保所有内存数据刷到csv文件 - writer.Flush() -} - -func (s *Csv) LoadHostFromCsv() { - // 打开文件 - file, err := os.Open(beego.AppPath + "/conf/hosts.csv") - if err != nil { - panic(err) - } - defer file.Close() - - // 获取csv的reader - reader := csv.NewReader(file) - - // 设置FieldsPerRecord为-1 - reader.FieldsPerRecord = -1 - - // 读取文件中所有行保存到slice中 - records, err := reader.ReadAll() - if err != nil { - panic(err) - } - var hosts []*HostList - // 将每一行数据保存到内存slice中 - for _, item := range records { - post := &HostList{ - Vkey: item[2], - Host: item[0], - Target: item[1], - } - hosts = append(hosts, post) - } - s.Hosts = hosts -} - func (s *Csv) NewTask(t *ServerConfig) { s.Tasks = append(s.Tasks, t) s.StoreTasksToCsv() @@ -200,6 +144,18 @@ func (s *Csv) UpdateTask(t *ServerConfig) error { return errors.New("不存在") } +func (s *Csv) UpdateHost(t *HostList) error { + for k, v := range s.Hosts { + if v.Host == t.Host { + s.Hosts = append(s.Hosts[:k], s.Hosts[k+1:]...) + s.Hosts = append(s.Hosts, t) + s.StoreHostToCsv() + return nil + } + } + return errors.New("不存在") +} + func (s *Csv) AddRunList(vKey string, svr interface{}) { s.RunList[vKey] = svr } @@ -229,6 +185,68 @@ func (s *Csv) GetTask(vKey string) (v *ServerConfig, err error) { return } +func (s *Csv) StoreHostToCsv() { + // 创建文件 + csvFile, err := os.Create(beego.AppPath + "/conf/hosts.csv") + if err != nil { + panic(err) + } + defer csvFile.Close() + // 获取csv的Writer + writer := csv.NewWriter(csvFile) + // 将map中的Post转换成slice,因为csv的Write需要slice参数 + // 并写入csv文件 + for _, host := range s.Hosts { + record := []string{ + host.Host, + host.Target, + host.Vkey, + host.HeaderChange, + host.HostChange, + } + err1 := writer.Write(record) + if err1 != nil { + panic(err1) + } + } + // 确保所有内存数据刷到csv文件 + writer.Flush() +} + +func (s *Csv) LoadHostFromCsv() { + // 打开文件 + file, err := os.Open(beego.AppPath + "/conf/hosts.csv") + if err != nil { + panic(err) + } + defer file.Close() + + // 获取csv的reader + reader := csv.NewReader(file) + + // 设置FieldsPerRecord为-1 + reader.FieldsPerRecord = -1 + + // 读取文件中所有行保存到slice中 + records, err := reader.ReadAll() + if err != nil { + panic(err) + } + var hosts []*HostList + // 将每一行数据保存到内存slice中 + for _, item := range records { + post := &HostList{ + Vkey: item[2], + Host: item[0], + Target: item[1], + HeaderChange: item[3], + HostChange: item[4], + } + hosts = append(hosts, post) + } + s.Hosts = hosts +} + func (s *Csv) DelHost(host string) error { for k, v := range s.Hosts { if v.Host == host { diff --git a/server/socks5.go b/server/socks5.go index 37be009..aea8043 100755 --- a/server/socks5.go +++ b/server/socks5.go @@ -162,15 +162,14 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *utils func (s *Sock5ModeServer) handleConnect(c net.Conn) { proxyConn, err := s.doConnect(c, connectMethod) defer func() { - if s.config.Mux { + if s.config.Mux && proxyConn != nil { s.bridge.ReturnTunnel(proxyConn, getverifyval(s.config.VerifyKey)) } }() if err != nil { c.Close() } else { - 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) + utils.ReplayWaitGroup(proxyConn.Conn, c, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, s.config.Mux) } } @@ -200,15 +199,14 @@ func (s *Sock5ModeServer) handleUDP(c net.Conn) { proxyConn, err := s.doConnect(c, associateMethod) defer func() { - if s.config.Mux { + if s.config.Mux && proxyConn != nil { s.bridge.ReturnTunnel(proxyConn, getverifyval(s.config.VerifyKey)) } }() if err != nil { c.Close() } else { - 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) + utils.ReplayWaitGroup(proxyConn.Conn, c, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, s.config.Mux) } } diff --git a/server/tcp.go b/server/tcp.go index 84bd894..aa9bbf1 100755 --- a/server/tcp.go +++ b/server/tcp.go @@ -1,6 +1,7 @@ package server import ( + "bufio" "errors" "fmt" "github.com/astaxie/beego" @@ -9,7 +10,9 @@ import ( "log" "net" "net/http" + "net/http/httputil" "strings" + "sync" ) type process func(c *utils.Conn, s *TunnelModeServer) error @@ -61,28 +64,23 @@ func (s *TunnelModeServer) auth(r *http.Request, c *utils.Conn, u, p string) err return nil } +func (s *TunnelModeServer) dealClient2(c *utils.Conn, cnf *ServerConfig, addr string, method string, rb []byte) error { + return nil +} + //与客户端建立通道 func (s *TunnelModeServer) dealClient(c *utils.Conn, cnf *ServerConfig, addr string, method string, rb []byte) error { -reGet: - link, err := s.bridge.GetTunnel(getverifyval(cnf.VerifyKey), cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, cnf.Mux) + var link *utils.Conn + var err error defer func() { - if cnf.Mux { + if cnf.Mux && link != nil { s.bridge.ReturnTunnel(link, getverifyval(cnf.VerifyKey)) - } else { - c.Close() } }() - if err != nil { - log.Println("conn to client error:", err) - c.Close() + if link, err = s.GetTunnelAndWriteHost(c, cnf, addr); err != nil { + log.Println("get bridge tunnel error: ", err) return err } - if _, err := link.WriteHost(utils.CONN_TCP, addr); err != nil { - c.Close() - link.Close() - log.Println(err) - goto reGet - } if flag, err := link.ReadFlag(); err == nil { if flag == utils.CONN_SUCCESS { if method == "CONNECT" { @@ -90,8 +88,7 @@ reGet: } else if rb != nil { 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) + utils.ReplayWaitGroup(link.Conn, c.Conn, cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, cnf.Mux) } } return nil @@ -102,6 +99,19 @@ func (s *TunnelModeServer) Close() error { return s.listener.Close() } +func (s *TunnelModeServer) GetTunnelAndWriteHost(c *utils.Conn, cnf *ServerConfig, addr string) (*utils.Conn, error) { + var err error + link, err := s.bridge.GetTunnel(getverifyval(cnf.VerifyKey), cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, cnf.Mux) + if err != nil { + return nil, err + } + if _, err = link.WriteHost(utils.CONN_TCP, addr); err != nil { + link.Close() + return nil, err + } + return link, nil +} + //tcp隧道模式 func ProcessTunnel(c *utils.Conn, s *TunnelModeServer) error { _, _, rb, err, r := c.GetHost() @@ -124,29 +134,69 @@ func ProcessHttp(c *utils.Conn, s *TunnelModeServer) error { 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 + var ( + isConn = true + link *utils.Conn + cnf *ServerConfig + host *HostList + wg sync.WaitGroup + ) + for { + r, err := http.ReadRequest(bufio.NewReader(c)) + if err != nil { + break + } + //首次获取conn + if isConn { + isConn = false + if host, cnf, err = GetKeyByHost(r.Host); err != nil { + log.Printf("the host %s is not found !", r.Host) + break + } + + if err = s.auth(r, c, cnf.U, cnf.P); err != nil { + break + } + + if link, err = s.GetTunnelAndWriteHost(c, cnf, host.Target); err != nil { + log.Println("get bridge tunnel error: ", err) + break + } + + if flag, err := link.ReadFlag(); err != nil || flag == utils.CONN_ERROR { + log.Printf("the host %s connection to %s error", r.Host, host.Target) + break + } else { + wg.Add(1) + go func() { + utils.Relay(c.Conn, link.Conn, cnf.CompressDecode, cnf.Crypt, cnf.Mux) + wg.Done() + }() + } + } + utils.ChangeHostAndHeader(r, host.HostChange, host.HeaderChange, c.Conn.RemoteAddr().String()) + b, err := httputil.DumpRequest(r, true) + if err != nil { + break + } + if _, err := link.WriteTo(b, cnf.CompressEncode, cnf.Crypt); err != nil { + break + } } - host, task, err := GetKeyByHost(addr) - if err != nil { - return err + wg.Wait() + if cnf != nil && cnf.Mux && link != nil { + link.WriteTo([]byte(utils.IO_EOF), cnf.CompressEncode, cnf.Crypt) + s.bridge.ReturnTunnel(link, getverifyval(cnf.VerifyKey)) + } else if link != nil { + link.Close() } - 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) + c.Close() + return nil } //web管理方式 diff --git a/server/udp.go b/server/udp.go index bd1b17a..9cda953 100755 --- a/server/udp.go +++ b/server/udp.go @@ -58,7 +58,7 @@ func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) { } if flag, err := conn.ReadFlag(); err == nil { defer func() { - if s.config.Mux { + if conn != nil && s.config.Mux { conn.WriteTo([]byte(utils.IO_EOF), s.config.CompressEncode, s.config.Crypt) s.bridge.ReturnTunnel(conn, getverifyval(s.config.VerifyKey)) } else { diff --git a/utils/conn.go b/utils/conn.go index 616380b..0908739 100755 --- a/utils/conn.go +++ b/utils/conn.go @@ -18,19 +18,17 @@ import ( const cryptKey = "1234567812345678" const poolSize = 64 * 1024 +const poolSizeSmall = 10 type CryptConn struct { conn net.Conn crypt bool - rb []byte - rn int } func NewCryptConn(conn net.Conn, crypt bool) *CryptConn { c := new(CryptConn) c.conn = conn c.crypt = crypt - c.rb = make([]byte, poolSize) return c } @@ -51,21 +49,6 @@ func (s *CryptConn) Write(b []byte) (n int, err error) { //解密读 func (s *CryptConn) Read(b []byte) (n int, err error) { -read: - if len(s.rb) > 0 { - if len(b) >= s.rn { - n = s.rn - copy(b, s.rb[:s.rn]) - s.rn = 0 - s.rb = s.rb[:0] - } else { - n = len(b) - copy(b, s.rb[:len(b)]) - s.rn = n - len(b) - s.rb = s.rb[len(b):] - } - return - } defer func() { if err == nil && n == len(IO_EOF) && string(b[:n]) == IO_EOF { err = io.EOF @@ -74,6 +57,7 @@ read: }() var lens int var buf []byte + var rb []byte c := NewConn(s.conn) if lens, err = c.GetLen(); err != nil { return @@ -82,14 +66,15 @@ read: return } if s.crypt { - if s.rb, err = AesDecrypt(buf, []byte(cryptKey)); err != nil { + if rb, err = AesDecrypt(buf, []byte(cryptKey)); err != nil { return } } else { - s.rb = buf + rb = buf } - s.rn = len(s.rb) - goto read + copy(b, rb) + n = len(rb) + return } type SnappyConn struct { @@ -164,7 +149,12 @@ func (s *Conn) ReadLen(cLen int) ([]byte, error) { if cLen > poolSize { return nil, errors.New("长度错误" + strconv.Itoa(cLen)) } - buf := bufPool.Get().([]byte)[:cLen] + var buf []byte + if cLen <= poolSizeSmall { + buf = bufPoolSmall.Get().([]byte)[:cLen] + } else { + buf = bufPool.Get().([]byte)[:cLen] + } if n, err := io.ReadFull(s, buf); err != nil || n != cLen { return buf, errors.New("读取指定长度错误" + err.Error()) } diff --git a/utils/util.go b/utils/util.go index b47152a..dd69f22 100755 --- a/utils/util.go +++ b/utils/util.go @@ -40,21 +40,21 @@ WWW-Authenticate: Basic realm="easyProxy" func Relay(in, out net.Conn, compressType int, crypt, mux bool) { switch compressType { case COMPRESS_SNAPY_ENCODE: - io.Copy(NewSnappyConn(in, crypt), out) + copyBuffer(NewSnappyConn(in, crypt), out) out.Close() NewSnappyConn(in, crypt).Write([]byte(IO_EOF)) case COMPRESS_SNAPY_DECODE: - io.Copy(in, NewSnappyConn(out, crypt)) + copyBuffer(in, NewSnappyConn(out, crypt)) in.Close() if !mux { out.Close() } case COMPRESS_NONE_ENCODE: - io.Copy(NewCryptConn(in, crypt), out) + copyBuffer(NewCryptConn(in, crypt), out) out.Close() NewCryptConn(in, crypt).Write([]byte(IO_EOF)) case COMPRESS_NONE_DECODE: - io.Copy(in, NewCryptConn(out, crypt)) + copyBuffer(in, NewCryptConn(out, crypt)) in.Close() if !mux { out.Close() @@ -150,11 +150,16 @@ var bufPool = sync.Pool{ return make([]byte, poolSize) }, } +var bufPoolSmall = sync.Pool{ + New: func() interface{} { + return make([]byte, poolSizeSmall) + }, +} // io.copy的优化版,读取buffer长度原为32*1024,与snappy不同,导致读取出的内容存在差异,不利于解密,特此修改 //废除 func copyBuffer(dst io.Writer, src io.Reader) (written int64, err error) { //TODO 回收问题 - buf := bufPool.Get().([]byte) + buf := bufPool.Get().([]byte)[:32*1024] for { nr, er := src.Read(buf) if nr > 0 { @@ -197,3 +202,33 @@ func FlushConn(c net.Conn) { func Getverifyval(vkey string) string { return Md5(vkey) } + +//wait replay group +func ReplayWaitGroup(conn1 net.Conn, conn2 net.Conn, compressEncode, compressDecode int, crypt, mux bool) { + var wg sync.WaitGroup + wg.Add(1) + go func() { + Relay(conn1, conn2, compressEncode, crypt, mux) + wg.Done() + }() + Relay(conn2, conn1, compressDecode, crypt, mux) + wg.Wait() +} + +func ChangeHostAndHeader(r *http.Request, host string, header string, addr string) { + if host != "" { + r.Host = host + } + if header != "" { + h := strings.Split(header, "\n") + for _, v := range h { + hd := strings.Split(v, ":") + if len(hd) == 2 { + r.Header.Set(hd[0], hd[1]) + } + } + } + addr = strings.Split(addr, ":")[0] + r.Header.Set("X-Forwarded-For", addr) + r.Header.Set("X-Real-IP", addr) +} diff --git a/web/controllers/index.go b/web/controllers/index.go index 73cd5bb..3972305 100755 --- a/web/controllers/index.go +++ b/web/controllers/index.go @@ -160,11 +160,40 @@ func (s *IndexController) AddHost() { s.display("index/hadd") } else { h := &server.HostList{ - Vkey: s.GetString("vkey"), - Host: s.GetString("host"), - Target: s.GetString("target"), + Vkey: s.GetString("vkey"), + Host: s.GetString("host"), + Target: s.GetString("target"), + HeaderChange: s.GetString("header"), + HostChange: s.GetString("hostchange"), } server.CsvDb.NewHost(h) s.AjaxOk("添加成功") } } + +func (s *IndexController) EditHost() { + if s.Ctx.Request.Method == "GET" { + host := s.GetString("host") + if h, t, err := server.GetKeyByHost(host); err != nil { + s.error() + } else { + s.Data["t"] = t + s.Data["h"] = h + } + s.SetInfo("修改") + s.display("index/hedit") + } else { + host := s.GetString("host") + if h, _, err := server.GetKeyByHost(host); err != nil { + s.error() + } else { + h.Vkey = s.GetString("vkey") + h.Host = s.GetString("host") + h.Target = s.GetString("target") + h.HeaderChange = s.GetString("header") + h.HostChange = s.GetString("hostchange") + server.CsvDb.UpdateHost(h) + } + s.AjaxOk("修改成功") + } +} diff --git a/web/views/index/hadd.html b/web/views/index/hadd.html index 8cc8b47..bfb185d 100755 --- a/web/views/index/hadd.html +++ b/web/views/index/hadd.html @@ -13,6 +13,15 @@ + +
+ + +
' } } ],