守护进程 负载均衡

pull/1219/head v0.0.13
刘河 2019-02-03 00:54:43 +08:00
parent 662a799f02
commit da899fd3db
20 changed files with 341 additions and 129 deletions

View File

@ -29,6 +29,11 @@ go语言编写无第三方依赖各个平台都已经编译在release中
* [release安装](#release安装)
* [web管理](#web管理模式)(多隧道时推荐)
* [启动](#启动)
* [服务端测试](#服务端测试)
* [服务端启动](#服务端启动)
* [web管理](#web管理)
* [客户端启动](#客户端启动)
* [服务端停止或重启](#服务端停止或重启)
* [配置文件说明](#服务端配置文件)
* [详细使用说明](#详细说明)
* [http|https域名解析](#域名解析)
@ -54,6 +59,8 @@ go语言编写无第三方依赖各个平台都已经编译在release中
* [自定义404页面](#404页面配置)
* [流量限制](#流量限制)
* [带宽限制](#带宽限制)
* [负载均衡](#负载均衡)
* [守护进程](#守护进程)
* [相关说明](#相关说明)
* [流量统计](#流量统计)
* [热更新支持](#热更新支持)
@ -81,9 +88,9 @@ go语言编写无第三方依赖各个平台都已经编译在release中
- 安装源码(另有snappy、beego包)
> go get github.com/cnlh/easyProxy
- 编译
> go build cmd/proxy_server/proxy_server.go
> go build cmd/server/proxy_server.go
> go build cmd/proxy_client/proxy_client.go
> go build cmd/client/proxy_client.go
## web管理模式
@ -98,22 +105,32 @@ go语言编写无第三方依赖各个平台都已经编译在release中
### 启动
- 服务端
#### 服务端测试
```
./proxy_server test
```
如有错误请及时修改配置文件,无错误可继续进行下去
#### 服务端启动
```
./proxy_server start
```
如果无需daemon运行去掉start即可
```
./proxy_server
```
- 客户端
```
./proxy_server -server=ip:port -vkey=web界面中显示的密钥
```
- 配置
#### web管理
进入web界面公网ip:web界面端口默认8080密码默认为123
进入web管理界面有详细的命令
进入web管理界面有详细的说明
#### 客户端启动
```
./proxy_server -server=ip:port -vkey=web界面中显示的密钥
```
#### 服务端停止或重启
如果是daemon启动
```
./proxy_server stop|restart
```
### 服务端配置文件
- /conf/app.conf
@ -122,6 +139,7 @@ go语言编写无第三方依赖各个平台都已经编译在release中
---|---
httpport | web管理端口
password | web界面管理密码
hostPort | 域名代理模式监听端口
tcpport | 服务端客户端通信端口
pemPath | ssl certFile绝对路径
keyPath | ssl keyFile绝对路径
@ -138,12 +156,13 @@ httpProxyPort | http代理监听端口
- 有一个域名proxy.com有一台公网机器ip为1.1.1.1
- 两个内网开发站点127.0.0.1:81127.0.0.1:82
- 想通过http|https://a.proxy.com访问127.0.0.1:81通过http|https://b.proxy.com访问127.0.0.1:82
- 例如配置文件中tcpport为8284
**使用步骤**
- 将*.proxy.com解析到公网服务器1.1.1.1
- 在客户端管理中创建一个客户端,记录下验证密钥
- 点击该客户端的域名管理添加两条规则规则1、域名a.proxy.com内网目标127.0.0.1:812、域名b.proxy.com内网目标127.0.0.1:82
-内网客户端运行
- 内网客户端运行
```
./proxy_client server=1.1.1.1:8284 -vkey=客户端的密钥
@ -158,7 +177,7 @@ httpProxyPort | http代理监听端口
**适用范围:** ssh、远程桌面等tcp连接场景
**假设场景:**
想通过访问公网服务器1.1.1.1的8001端口连接内网机器10.1.50.101的22端口实现ssh连接
想通过访问公网服务器1.1.1.1的8001端口连接内网机器10.1.50.101的22端口实现ssh连接例如配置文件中tcpport为8284
**使用步骤**
- 在客户端管理中创建一个客户端,记录下验证密钥
@ -176,7 +195,7 @@ httpProxyPort | http代理监听端口
**适用范围:** 内网dns解析等udp连接场景
**假设场景:**
内网有一台dns10.1.50.102:53在非内网环境下想使用该dns公网服务器为1.1.1.1
内网有一台dns10.1.50.102:53在非内网环境下想使用该dns公网服务器为1.1.1.1例如配置文件中tcpport为8284
**使用步骤**
- 在客户端管理中创建一个客户端,记录下验证密钥
@ -193,7 +212,7 @@ httpProxyPort | http代理监听端口
**适用范围:** 在外网环境下如同使用vpn一样访问内网设备或者资源
**假设场景:**
想将公网服务器1.1.1.1的8003端口作为socks5代理达到访问内网任意设备或者资源的效果
想将公网服务器1.1.1.1的8003端口作为socks5代理达到访问内网任意设备或者资源的效果例如配置文件中tcpport为8284
**使用步骤**
- 在客户端管理中创建一个客户端,记录下验证密钥
@ -209,7 +228,7 @@ httpProxyPort | http代理监听端口
**适用范围:** 在外网环境下使用http代理访问内网站点
**假设场景:**
想将公网服务器1.1.1.1的8004端口作为http代理访问内网网站
想将公网服务器1.1.1.1的8004端口作为http代理访问内网网站例如配置文件中tcpport为8284
**使用步骤**
- 在客户端管理中创建一个客户端,记录下验证密钥
@ -220,13 +239,14 @@ httpProxyPort | http代理监听端口
- 在该客户端隧道管理中添加一条http代理填写监听的端口8004选择压缩方式保存。
- 在外网环境的本机配置http代理ip为公网服务器ip127.0.0.1),端口为填写的监听端口(8004),即可访问了
### 使用https
在配置文件中将httpsProxyPort设置为443或者其他你想配置的端口和将对应的证书文件路径添加到配置文件中即可畅销https了
### 与nginx配合
普通场景下使用本代理已经能满足使用要求但是有时候我们还需要在云服务器上运行nginx来保证静态文件缓存等本代理可和nginx配合使用在配置文件中将httpProxyPort设置为非80端口并在nginx中配置代理
有时候我们还需要在云服务器上运行https来保证静态文件缓存等本代理可和nginx配合使用在配置文件中将httpProxyPort设置为非80端口并在nginx中配置代理
```
server {
listen 80;
@ -491,6 +511,18 @@ authip | 免验证ip适用于web api
支持客户端级带宽限制,带宽计算方式为入口和出口总和,权重均衡
### 负载均衡
本代理支持域名解析模式的负载均衡在web域名添加或者编辑中内网目标分行填写多个目标即可实现轮训级别的负载均衡
### 守护进程
本代理支持守护进程,使用示例如下,服务端客户端所有模式通用,支持linuxdarwinwindows。
```
./proxy_(client|server) start|stop|restart xxxxxx
```
```
proxy_(client|server).exe start|stop|restart xxxxxx
```
## 相关说明
### 获取用户真实ip
@ -531,4 +563,4 @@ authip | 免验证ip适用于web api
## webAPI
为方便第三方扩展在web模式下可利用webAPI进行相关操作详情见
[webAPI文档](https://github.com/cnlh/easyProxy/wiki/webAPI%E6%96%87%E6%A1%A3)
[webAPI文档](https://github.com/cnlh/easyProxy/wiki/webAPI%E6%96%87%E6%A1%A3)

View File

@ -3,7 +3,6 @@ package bridge
import (
"errors"
"github.com/cnlh/easyProxy/utils"
"log"
"net"
"sync"
"time"
@ -52,7 +51,7 @@ func (s *Bridge) tunnelProcess() error {
for {
conn, err := s.listener.Accept()
if err != nil {
log.Println(err)
utils.Println(err)
continue
}
go s.cliProcess(utils.NewConn(conn))
@ -77,7 +76,7 @@ func (s *Bridge) cliProcess(c *utils.Conn) {
//验证
id, err := utils.GetCsvDb().GetIdByVerifyKey(string(buf), c.Conn.RemoteAddr().String())
if err != nil {
log.Println("当前客户端连接校验错误,关闭此客户端:", c.Conn.RemoteAddr())
utils.Println("当前客户端连接校验错误,关闭此客户端:", c.Conn.RemoteAddr())
s.verifyError(c)
return
}
@ -116,7 +115,7 @@ func (s *Bridge) typeDeal(typeVal string, c *utils.Conn, id int) {
stop: make(chan bool),
linkStatusMap: make(map[int]bool),
}
log.Printf("客户端%d连接成功,地址为:%s", id, c.Conn.RemoteAddr())
utils.Printf("客户端%d连接成功,地址为:%s", id, c.Conn.RemoteAddr())
s.Client[id].signal = c
s.clientLock.Unlock()
go s.GetStatus(id)
@ -168,7 +167,7 @@ func (s *Bridge) SendLinkInfo(clientId int, link *utils.Link) (tunnel *utils.Con
s.clientLock.Unlock()
v.signal.SendLinkInfo(link)
if err != nil {
log.Println("send error:", err, link.Id)
utils.Println("send error:", err, link.Id)
s.DelClient(clientId)
return
}
@ -258,7 +257,7 @@ func (s *Bridge) clientCopy(clientId int) {
for {
if id, err := client.tunnel.GetLen(); err != nil {
s.closeClient(clientId)
log.Println("读取msg id 错误", err, id)
utils.Println("读取msg id 错误", err, id)
break
} else {
client.Lock()
@ -267,7 +266,7 @@ func (s *Bridge) clientCopy(clientId int) {
if content, err := client.tunnel.GetMsgContent(link); err != nil {
utils.PutBufPoolCopy(content)
s.closeClient(clientId)
log.Println("read msg content error", err, "close client")
utils.Println("read msg content error", err, "close client")
break
} else {
if len(content) == len(utils.IO_EOF) && string(content) == utils.IO_EOF {

View File

@ -2,7 +2,6 @@ package client
import (
"github.com/cnlh/easyProxy/utils"
"log"
"net"
"sync"
"time"
@ -40,7 +39,7 @@ func (s *TRPClient) NewConn() {
retry:
conn, err := net.Dial("tcp", s.svrAddr)
if err != nil {
log.Println("连接服务端失败,五秒后将重连")
utils.Println("连接服务端失败,五秒后将重连")
time.Sleep(time.Second * 5)
goto retry
return
@ -61,12 +60,12 @@ func (s *TRPClient) processor(c *utils.Conn) {
for {
flags, err := c.ReadFlag()
if err != nil {
log.Println("服务端断开,正在重新连接")
utils.Println("服务端断开,正在重新连接")
break
}
switch flags {
case utils.VERIFY_EER:
log.Fatalf("vKey:%s不正确,服务端拒绝连接,请检查", s.vKey)
utils.Fatalf("vKey:%s不正确,服务端拒绝连接,请检查", s.vKey)
case utils.NEW_CONN:
if link, err := c.GetLinkInfo(); err != nil {
break
@ -77,12 +76,12 @@ func (s *TRPClient) processor(c *utils.Conn) {
go s.linkProcess(link, c)
}
case utils.RES_CLOSE:
log.Fatal("该vkey被另一客户连接")
utils.Fatalln("该vkey被另一客户连接")
case utils.RES_MSG:
log.Println("服务端返回错误,重新连接")
utils.Println("服务端返回错误,重新连接")
break
default:
log.Println("无法解析该错误,重新连接")
utils.Println("无法解析该错误,重新连接")
break
}
}
@ -96,7 +95,7 @@ func (s *TRPClient) linkProcess(link *utils.Link, c *utils.Conn) {
if err != nil {
c.WriteFail(link.Id)
log.Println("connect to ", link.Host, "error:", err)
utils.Println("connect to ", link.Host, "error:", err)
return
}
@ -135,12 +134,12 @@ func (s *TRPClient) dealChan() {
//创建一个tcp连接
conn, err := net.Dial("tcp", s.svrAddr)
if err != nil {
log.Println("connect to ", s.svrAddr, "error:", err)
utils.Println("connect to ", s.svrAddr, "error:", err)
return
}
//验证
if _, err := conn.Write([]byte(utils.Getverifyval(s.vKey))); err != nil {
log.Println("connect to ", s.svrAddr, "error:", err)
utils.Println("connect to ", s.svrAddr, "error:", err)
return
}
//默认长连接保持
@ -152,14 +151,14 @@ func (s *TRPClient) dealChan() {
go func() {
for {
if id, err := s.tunnel.GetLen(); err != nil {
log.Println("get msg id error")
utils.Println("get msg id error")
break
} else {
s.Lock()
if v, ok := s.linkMap[id]; ok {
s.Unlock()
if content, err := s.tunnel.GetMsgContent(v); err != nil {
log.Println("get msg content error:", err, id)
utils.Println("get msg content error:", err, id)
break
} else {
if len(content) == len(utils.IO_EOF) && string(content) == utils.IO_EOF {

View File

@ -3,20 +3,30 @@ package main
import (
"flag"
"github.com/cnlh/easyProxy/client"
"log"
"github.com/cnlh/easyProxy/utils"
_ "github.com/cnlh/easyProxy/utils"
"strings"
)
const VERSION = "v0.0.13"
var (
serverAddr = flag.String("server", "", "服务器地址ip:端口")
verifyKey = flag.String("vkey", "", "验证密钥")
logType = flag.String("log", "stdout", "日志输出方式stdout|file")
)
func main() {
flag.Parse()
utils.InitDaemon("client")
if *logType == "stdout" {
utils.InitLogFile("client", true)
} else {
utils.InitLogFile("client", false)
}
stop := make(chan int)
for _, v := range strings.Split(*verifyKey, ",") {
log.Println("客户端启动,连接:", *serverAddr, " 验证令牌:", v)
utils.Println("客户端启动,连接:", *serverAddr, " 验证令牌:", v)
go client.NewRPClient(*serverAddr, v).Start()
}
<-stop

View File

@ -6,23 +6,36 @@ import (
"github.com/cnlh/easyProxy/server"
"github.com/cnlh/easyProxy/utils"
_ "github.com/cnlh/easyProxy/web/routers"
"log"
"os"
)
const VERSION = "v0.0.13"
var (
TcpPort = flag.Int("tcpport", 0, "客户端与服务端通信端口")
httpPort = flag.Int("httpport", 8024, "对外监听的端口")
rpMode = flag.String("mode", "webServer", "启动模式")
tunnelTarget = flag.String("target", "10.1.50.203:80", "远程目标")
tunnelTarget = flag.String("target", "127.0.0.1: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)")
logType = flag.String("log", "stdout", "日志输出方式stdout|file")
)
func main() {
flag.Parse()
var test bool
if len(os.Args) > 1 && os.Args[1] == "test" {
test = true
}
utils.InitDaemon("server")
if *logType == "stdout" || test {
utils.InitLogFile("server", true)
} else {
utils.InitLogFile("server", false)
}
task := &utils.Tunnel{
TcpPort: *httpPort,
Mode: *rpMode,
@ -59,10 +72,10 @@ func main() {
*TcpPort = 8284
}
}
log.Println("服务端启动监听tcp服务端端口", *TcpPort)
utils.Println("服务端启动监听tcp服务端端口", *TcpPort)
task.Config.CompressDecode, task.Config.CompressEncode = utils.GetCompressType(task.Config.Compress)
if *rpMode!="webServer" {
if *rpMode != "webServer" {
server.CsvDb.Tasks[0] = task
}
server.StartNewServer(*TcpPort, task)
server.StartNewServer(*TcpPort, task, test)
}

View File

@ -1 +1,2 @@
a.o.com,127.0.0.1:8082,1,,www.baidu.com,测试
a.o.com,"127.0.0.1:8082
127.0.0.1:8080",1,,www.baidu.com,测试

1 a.o.com 127.0.0.1:8082 127.0.0.1:8082 127.0.0.1:8080 1 www.baidu.com 测试
2

View File

@ -6,7 +6,7 @@ import (
"github.com/astaxie/beego"
"github.com/cnlh/easyProxy/bridge"
"github.com/cnlh/easyProxy/utils"
"log"
"net"
"net/http"
"net/http/httputil"
"strconv"
@ -49,25 +49,38 @@ func (s *httpServer) Start() error {
}
if s.httpPort > 0 {
if !s.TestTcpPort(s.httpPort) {
utils.Fatalln("http端口", s.httpPort, "被占用!")
}
http = s.NewServer(s.httpPort)
go func() {
log.Println("启动http监听,端口为", s.httpPort)
utils.Println("启动http监听,端口为", s.httpPort)
err := http.ListenAndServe()
if err != nil {
log.Fatalln(err)
utils.Fatalln(err)
}
}()
}
if s.httpsPort > 0 {
if !s.TestTcpPort(s.httpsPort) {
utils.Fatalln("https端口", s.httpsPort, "被占用!")
}
if !utils.FileExists(s.pemPath) {
utils.Fatalf("ssl certFile文件%s不存在", s.pemPath)
}
if !utils.FileExists(s.keyPath) {
utils.Fatalf("ssl keyFile文件%s不存在", s.keyPath)
}
https = s.NewServer(s.httpsPort)
go func() {
log.Println("启动https监听,端口为", s.httpsPort)
utils.Println("启动https监听,端口为", s.httpsPort)
err := https.ListenAndServeTLS(s.pemPath, s.keyPath)
if err != nil {
log.Fatalln(err)
utils.Fatalln(err)
}
}()
}
startFinish <- true
select {
case <-s.stop:
if http != nil {
@ -77,6 +90,7 @@ func (s *httpServer) Start() error {
https.Close()
}
}
return nil
}
@ -110,7 +124,7 @@ func (s *httpServer) process(c *utils.Conn, r *http.Request) {
//首次获取conn
if isConn {
if host, err = GetInfoByHost(r.Host); err != nil {
log.Printf("the host %s is not found !", r.Host)
utils.Printf("the host %s is not found !", r.Host)
break
}
//流量限制
@ -122,7 +136,7 @@ func (s *httpServer) process(c *utils.Conn, r *http.Request) {
if err = s.auth(r, c, host.Client.Cnf.U, host.Client.Cnf.P); err != nil {
break
}
link = utils.NewLink(host.Client.GetId(), utils.CONN_TCP, host.Target, host.Client.Cnf.CompressEncode, host.Client.Cnf.CompressDecode, host.Client.Cnf.Crypt, c, host.Flow, nil, host.Client.Rate, nil)
link = utils.NewLink(host.Client.GetId(), utils.CONN_TCP, host.GetRandomTarget(), host.Client.Cnf.CompressEncode, host.Client.Cnf.CompressDecode, host.Client.Cnf.Crypt, c, host.Flow, nil, host.Client.Rate, nil)
if tunnel, err = s.bridge.SendLinkInfo(host.Client.Id, link); err != nil {
break
}
@ -166,3 +180,12 @@ func (s *httpServer) NewServer(port int) *http.Server {
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
}
}
func (s *httpServer) TestTcpPort(port int) bool {
l, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), port, ""})
defer l.Close()
if err != nil {
return false
}
return true
}

View File

@ -5,44 +5,58 @@ import (
"github.com/cnlh/easyProxy/bridge"
"github.com/cnlh/easyProxy/utils"
"log"
"os"
"reflect"
"strings"
)
var (
Bridge *bridge.Bridge
RunList map[int]interface{} //运行中的任务
CsvDb = utils.GetCsvDb()
Bridge *bridge.Bridge
RunList map[int]interface{} //运行中的任务
CsvDb = utils.GetCsvDb()
startFinish chan bool
)
func init() {
RunList = make(map[int]interface{})
startFinish = make(chan bool)
}
//从csv文件中恢复任务
func InitFromCsv() {
for _, v := range CsvDb.Tasks {
if v.Status {
log.Println("启动模式:", v.Mode, "监听端口:", v.TcpPort)
utils.Println("启动模式:", v.Mode, "监听端口:", v.TcpPort)
AddTask(v)
}
}
}
//start a new server
func StartNewServer(bridgePort int, cnf *utils.Tunnel) {
Bridge = bridge.NewTunnel(bridgePort, RunList)
if err := Bridge.StartTunnel(); err != nil {
log.Fatalln("服务端开启失败", err)
}
if svr := NewMode(Bridge, cnf); svr != nil {
RunList[cnf.Id] = svr
err := reflect.ValueOf(svr).MethodByName("Start").Call(nil)[0]
if err.Interface() != nil {
log.Println(err)
func StartNewServer(bridgePort int, cnf *utils.Tunnel, test bool) {
go func() {
Bridge = bridge.NewTunnel(bridgePort, RunList)
if err := Bridge.StartTunnel(); err != nil {
utils.Fatalln("服务端开启失败", err)
}
if svr := NewMode(Bridge, cnf); svr != nil {
RunList[cnf.Id] = svr
err := reflect.ValueOf(svr).MethodByName("Start").Call(nil)[0]
if err.Interface() != nil {
utils.Fatalln(err)
}
} else {
utils.Fatalln("启动模式不正确")
}
}()
for {
select {
case <-startFinish:
if test {
log.Println("测试完成,未发现错误")
os.Exit(0)
}
}
} else {
log.Fatalln("启动模式不正确")
}
}
@ -98,7 +112,7 @@ func AddTask(t *utils.Tunnel) error {
go func() {
err := reflect.ValueOf(svr).MethodByName("Start").Call(nil)[0]
if err.Interface() != nil {
log.Println("客户端", t.Id, "启动失败,错误:", err)
utils.Fatalln("服务端", t.Id, "启动失败,错误:", err)
delete(RunList, t.Id)
}
}()

View File

@ -6,7 +6,6 @@ import (
"github.com/cnlh/easyProxy/bridge"
"github.com/cnlh/easyProxy/utils"
"io"
"log"
"net"
"strconv"
"strings"
@ -66,7 +65,7 @@ func (s *Sock5ModeServer) handleRequest(c net.Conn) {
_, err := io.ReadFull(c, header)
if err != nil {
log.Println("illegal request", err)
utils.Println("illegal request", err)
c.Close()
return
}
@ -163,7 +162,7 @@ func (s *Sock5ModeServer) handleBind(c net.Conn) {
//udp
func (s *Sock5ModeServer) handleUDP(c net.Conn) {
log.Println("UDP Associate")
utils.Println("UDP Associate")
/*
+----+------+------+----------+----------+----------+
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
@ -176,7 +175,7 @@ func (s *Sock5ModeServer) handleUDP(c net.Conn) {
// relay udp datagram silently, without any notification to the requesting client
if buf[2] != 0 {
// does not support fragmentation, drop it
log.Println("does not support fragmentation, drop")
utils.Println("does not support fragmentation, drop")
dummy := make([]byte, maxUDPPacketSize)
c.Read(dummy)
}
@ -188,13 +187,13 @@ func (s *Sock5ModeServer) handleUDP(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)
utils.Println("negotiation err", err)
c.Close()
return
}
if version := buf[0]; version != 5 {
log.Println("only support socks5, request from: ", c.RemoteAddr())
utils.Println("only support socks5, request from: ", c.RemoteAddr())
c.Close()
return
}
@ -202,7 +201,7 @@ func (s *Sock5ModeServer) handleConn(c net.Conn) {
methods := make([]byte, nMethods)
if len, err := c.Read(methods); len != int(nMethods) || err != nil {
log.Println("wrong method")
utils.Println("wrong method")
c.Close()
return
}
@ -211,7 +210,7 @@ func (s *Sock5ModeServer) handleConn(c net.Conn) {
c.Write(buf)
if err := s.Auth(c); err != nil {
c.Close()
log.Println("验证失败:", err)
utils.Println("验证失败:", err)
return
}
} else {
@ -270,7 +269,7 @@ func (s *Sock5ModeServer) Start() error {
if strings.Contains(err.Error(), "use of closed network connection") {
break
}
log.Fatal("accept error: ", err)
utils.Fatalln("accept error: ", err)
}
if !s.ResetConfig() {
conn.Close()

View File

@ -5,7 +5,6 @@ import (
"github.com/astaxie/beego"
"github.com/cnlh/easyProxy/bridge"
"github.com/cnlh/easyProxy/utils"
"log"
"net"
"strings"
)
@ -39,7 +38,7 @@ func (s *TunnelModeServer) Start() error {
if strings.Contains(err.Error(), "use of closed network connection") {
break
}
log.Println(err)
utils.Println(err)
continue
}
go s.process(utils.NewConn(conn), s)
@ -71,12 +70,13 @@ type WebServer struct {
}
//开始
func (s *WebServer) Start() {
func (s *WebServer) Start() error {
beego.BConfig.WebConfig.Session.SessionOn = true
log.Println("web管理启动访问端口为", beego.AppConfig.String("httpport"))
utils.Println("web管理启动访问端口为", beego.AppConfig.String("httpport"))
beego.SetViewsPath(beego.AppPath + "/web/views")
beego.SetStaticPath("/static", beego.AppPath+"/web/static")
beego.Run()
return errors.New("web管理启动失败")
}
//new

View File

@ -7,7 +7,6 @@ import (
"errors"
"github.com/golang/snappy"
"io"
"log"
"net"
"net/http"
"net/url"
@ -99,7 +98,7 @@ func (s *SnappyConn) Write(b []byte) (n int, err error) {
n = len(b)
if s.crypt {
if b, err = AesEncrypt(b, []byte(cryptKey)); err != nil {
log.Println("encode crypt error:", err)
Println("encode crypt error:", err)
return
}
}
@ -125,7 +124,7 @@ func (s *SnappyConn) Read(b []byte) (n int, err error) {
var bs []byte
if s.crypt {
if bs, err = AesDecrypt(buf[:n], []byte(cryptKey)); err != nil {
log.Println("decode crypt error:", err)
Println("decode crypt error:", err)
return
}
} else {
@ -170,9 +169,13 @@ func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.
return
}
if hostPortURL.Opaque == "443" { //https访问
address = r.Host + ":443"
if strings.Index(r.Host, ":") == -1 { //host不带端口 默认80
address = r.Host + ":443"
} else {
address = r.Host
}
} else { //http访问
if strings.Index(hostPortURL.Host, ":") == -1 { //host不带端口 默认80
if strings.Index(r.Host, ":") == -1 { //host不带端口 默认80
address = r.Host + ":80"
} else {
address = r.Host

69
utils/daemon.go Normal file
View File

@ -0,0 +1,69 @@
package utils
import (
"github.com/astaxie/beego"
"io/ioutil"
"log"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
)
func InitDaemon(f string) {
if len(os.Args) < 2 {
return
}
var args []string
args = append(args, os.Args[0])
if len(os.Args) >= 2 {
args = append(args, os.Args[2:]...)
}
args = append(args, "-log=file")
switch os.Args[1] {
case "start":
start(args, f)
os.Exit(0)
case "stop":
stop(f, args[0])
os.Exit(0)
case "restart":
stop(f, args[0])
start(args, f)
os.Exit(0)
}
}
func start(osArgs []string, f string) {
cmd := exec.Command(osArgs[0], osArgs[1:]...)
cmd.Start()
log.Println("执行启动成功")
if cmd.Process.Pid > 0 {
d1 := []byte(strconv.Itoa(cmd.Process.Pid))
ioutil.WriteFile(beego.AppPath+"/proxy_"+f+".pid", d1, 0600)
}
}
func stop(f string, p string) {
var c *exec.Cmd
var err error
switch runtime.GOOS {
case "windows":
p := strings.Split(p, `\`)
c = exec.Command("taskkill", "/F", "/IM", p[len(p)-1])
case "linux", "darwin":
b, err := ioutil.ReadFile(beego.AppPath + "/proxy_" + f + ".pid")
if err == nil {
c = exec.Command("/bin/bash", "-c", `kill -9 `+string(b))
} else {
log.Println("停止服务失败,pid文件不存在")
}
}
err = c.Run()
if err != nil {
log.Println("停止服务失败,", err)
} else {
log.Println("停止服务成功")
}
}

View File

@ -4,7 +4,6 @@ import (
"encoding/csv"
"errors"
"github.com/astaxie/beego"
"log"
"os"
"strconv"
"strings"
@ -40,7 +39,7 @@ func (s *Csv) StoreTasksToCsv() {
// 创建文件
csvFile, err := os.Create(beego.AppPath + "/conf/tasks.csv")
if err != nil {
log.Fatalf(err.Error())
Fatalf(err.Error())
}
defer csvFile.Close()
writer := csv.NewWriter(csvFile)
@ -63,7 +62,7 @@ func (s *Csv) StoreTasksToCsv() {
}
err := writer.Write(record)
if err != nil {
log.Fatalf(err.Error())
Fatalf(err.Error())
}
}
writer.Flush()
@ -91,7 +90,7 @@ func (s *Csv) LoadTaskFromCsv() {
path := beego.AppPath + "/conf/tasks.csv"
records, err := s.openFile(path)
if err != nil {
log.Fatal("配置文件打开错误:", path)
Fatalln("配置文件打开错误:", path)
}
var tasks []*Tunnel
// 将每一行数据保存到内存slice中
@ -218,7 +217,7 @@ func (s *Csv) LoadClientFromCsv() {
path := beego.AppPath + "/conf/clients.csv"
records, err := s.openFile(path)
if err != nil {
log.Fatal("配置文件打开错误:", path)
Fatalln("配置文件打开错误:", path)
}
var clients []*Client
// 将每一行数据保存到内存slice中
@ -254,7 +253,7 @@ func (s *Csv) LoadHostFromCsv() {
path := beego.AppPath + "/conf/hosts.csv"
records, err := s.openFile(path)
if err != nil {
log.Fatal("配置文件打开错误:", path)
Fatalln("配置文件打开错误:", path)
}
var hosts []*Host
// 将每一行数据保存到内存slice中
@ -392,7 +391,7 @@ func (s *Csv) StoreClientsToCsv() {
// 创建文件
csvFile, err := os.Create(beego.AppPath + "/conf/clients.csv")
if err != nil {
log.Fatalf(err.Error())
Fatalln(err.Error())
}
defer csvFile.Close()
writer := csv.NewWriter(csvFile)
@ -411,7 +410,7 @@ func (s *Csv) StoreClientsToCsv() {
}
err := writer.Write(record)
if err != nil {
log.Fatalf(err.Error())
Fatalln(err.Error())
}
}
writer.Flush()

View File

@ -2,6 +2,7 @@ package utils
import (
"net"
"strings"
"sync"
)
@ -101,6 +102,23 @@ type Host struct {
Flow *Flow
Client *Client
Remark string //备注
NowIndex int
TargetArr []string
sync.RWMutex
}
func (s *Host) GetRandomTarget() string {
if s.TargetArr == nil {
s.TargetArr = strings.Split(s.Target, "\n")
}
s.Lock()
defer s.Unlock()
if s.NowIndex >= len(s.TargetArr)-1 {
s.NowIndex = 0
} else {
s.NowIndex++
}
return s.TargetArr[s.NowIndex]
}
//深拷贝Config

45
utils/log.go Normal file
View File

@ -0,0 +1,45 @@
package utils
import (
"github.com/astaxie/beego"
"log"
"os"
"runtime"
)
var Log *log.Logger
func InitLogFile(f string, isStdout bool) {
var prefix string
if !isStdout {
logFile, err := os.OpenFile(beego.AppPath+"/proxy_"+f+"_log.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766)
if err != nil {
log.Fatalln("open file error !")
}
if runtime.GOOS == "windows" {
prefix = "\r\n"
}
Log = log.New(logFile, prefix, log.Ldate|log.Ltime)
} else {
Log = log.New(os.Stdout, "", log.Ldate|log.Ltime)
}
}
func Println(v ...interface{}) {
Log.Println(v ...)
}
func Fatalln(v ...interface{}) {
Log.SetPrefix("error ")
Log.Fatalln(v ...)
Log.SetPrefix("")
}
func Fatalf(format string, v ...interface{}) {
Log.SetPrefix("error ")
Log.Fatalf(format, v...)
Log.SetPrefix("")
}
func Printf(format string, v ...interface{}) {
Log.Printf(format, v...)
}

View File

@ -1,23 +0,0 @@
package utils
import (
"log"
"testing"
)
var rate = NewRate(100 * 1024)
func TestRate_Get(t *testing.T) {
rate.Start()
for i := 0; i < 5; i++ {
go test(i)
}
test(5)
}
func test(i int) {
for {
rate.Get(64 * 1024)
log.Println("get ok", i)
}
}

View File

@ -3,7 +3,6 @@ package utils
import (
"encoding/base64"
"io/ioutil"
"log"
"net"
"net/http"
"os"
@ -38,7 +37,6 @@ WWW-Authenticate: Basic realm="easyProxy"
`
)
//判断压缩方式
func GetCompressType(compress string) (int, int) {
switch compress {
@ -47,7 +45,7 @@ func GetCompressType(compress string) (int, int) {
case "snappy":
return COMPRESS_SNAPY_DECODE, COMPRESS_SNAPY_ENCODE
default:
log.Fatalln("数据压缩格式错误")
Fatalln("数据压缩格式错误")
}
return COMPRESS_NONE_DECODE, COMPRESS_NONE_ENCODE
}
@ -127,7 +125,6 @@ func Getverifyval(vkey string) string {
return Md5(vkey)
}
func ChangeHostAndHeader(r *http.Request, host string, header string, addr string) {
if host != "" {
r.Host = host
@ -153,3 +150,14 @@ func ReadAllFromFile(filePath string) ([]byte, error) {
}
return ioutil.ReadAll(f)
}
// FileExists reports whether the named file or directory exists.
func FileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}

View File

@ -250,6 +250,7 @@ func (s *IndexController) EditHost() {
h.HeaderChange = s.GetString("header")
h.HostChange = s.GetString("hostchange")
h.Remark = s.GetString("remark")
h.TargetArr = nil
server.CsvDb.UpdateHost(h)
var err error
if h.Client, err = server.CsvDb.GetClient(s.GetIntNoErr("client_id")); err != nil {

View File

@ -14,11 +14,13 @@
</div>
<div class="form-group">
<label class="control-label">id</label>
<input value="{{.client_id}}" class="form-control" type="text" name="client_id" placeholder="客户端id">
<input value="{{.client_id}}" class="form-control" type="text" name="client_id"
placeholder="客户端id">
</div>
<div class="form-group">
<label class="control-label"></label>
<input class="form-control" type="text" name="target" placeholder="内网隧道目标例如10.1.50.203:22">
<textarea class="form-control" rows="4" type="text" name="target"
placeholder="内网隧道目标例如10.1.50.203:22换行分隔"></textarea>
</div>
<div class="form-group" id="header">
<label class="control-label">header()</label>

View File

@ -20,8 +20,8 @@
</div>
<div class="form-group">
<label class="control-label"></label>
<input class="form-control" value="{{.h.Target}}" type="text" name="target"
placeholder="内网隧道目标例如10.1.50.203:22">
<textarea class="form-control" rows="4" type="text" name="target"
placeholder="内网隧道目标例如10.1.50.203:22,换行分隔">{{.h.Target}}</textarea>
</div>
<div class="form-group" id="header">
<label class="control-label">header()</label>