diff --git a/README.md b/README.md index b60c789..d165f51 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,24 @@ Connection #007 发送到客户端数据包, 稍后加密数据: {"id":null,"met ``` +## 参数说明 +* 客户端使用服务的方式启动 +``` +# 创建服务 +./miner-proxy -install -debug -client -l :5556 -r {服务器ip}:5558 -secret_key 123456789 + +``` +* 服务端使用服务的方式启动 +``` +./miner-proxy -install -debug -l :5558 -r {矿池host+port} -secret_key 123456789 +``` +* 运行服务`./miner-proxy -start` +* 停止服务`./miner-proxy -stop` +* 重启服务`./miner-proxy -restart` +* 删除服务`./miner-proxy -remove` +* 删查看服务状态`./miner-proxy -stat` + + ## 矿工添加矿池示例 ### 开源矿工 1. ![](./images/open-miner-add.png) diff --git a/cmd/run/main.go b/cmd/run/main.go new file mode 100644 index 0000000..8bb82b5 --- /dev/null +++ b/cmd/run/main.go @@ -0,0 +1,174 @@ +package main + +import ( + "flag" + "fmt" + "github.com/kardianos/service" + "log" + "miner-proxy/pkg" + "miner-proxy/proxy" + "net" + "os" + "strings" +) + +var ( + version = "0.0.0-src" + matchid = uint64(0) + connid = uint64(0) + logger pkg.Logger + + localAddr = flag.String("l", ":9999", "本地监听地址") + remoteAddr = flag.String("r", "localhost:80", "远程代理地址或者远程本程序的监听地址") + secretKey = flag.String("secret_key", "", "数据包加密密钥, 只有远程地址也是本服务时才可使用") + isClient = flag.Bool("client", false, "是否是客户端, 该参数必须准确, 默认服务端, 只有 secret_key 不为空时需要区分") + debug = flag.Bool("debug", false, "是否开启debug") + install = flag.Bool("install", false, "添加到系统服务, 并且开机自动启动") + remove = flag.Bool("remove", false, "移除系统服务, 并且关闭开机自动启动") + stop = flag.Bool("stop", false, "暂停代理服务") + restart = flag.Bool("restart", false, "重启代理服务") + start = flag.Bool("start", false, "启动代理服务") + stat = flag.Bool("stat", false, "查看代理服务状态") +) + +type proxyService struct{} + +func (p *proxyService) Start(s service.Service) error { + go p.run() + return nil +} + +func (p *proxyService) run() { + logger := pkg.ColorLogger{} + + logger.Info("miner-proxy (%s) proxing from %v to %v ", version, *localAddr, *remoteAddr) + + laddr, err := net.ResolveTCPAddr("tcp", *localAddr) + if err != nil { + logger.Warn("Failed to resolve local address: %s", err) + os.Exit(1) + } + raddr, err := net.ResolveTCPAddr("tcp", *remoteAddr) + if err != nil { + logger.Warn("Failed to resolve remote address: %s", err) + os.Exit(1) + } + listener, err := net.ListenTCP("tcp", laddr) + if err != nil { + logger.Warn("Failed to open local port to listen: %s", err) + os.Exit(1) + } + if len(*secretKey)%16 != 0 { + for len(*secretKey)%16 != 0 { + *secretKey += "0" + } + } + + for { + conn, err := listener.AcceptTCP() + if err != nil { + logger.Warn("Failed to accept connection '%s'", err) + continue + } + connid++ + p := proxy.New(conn, laddr, raddr) + p.SecretKey = *secretKey + p.IsClient = *isClient + p.Log = pkg.ColorLogger{ + Prefix: fmt.Sprintf("Connection #%03d ", connid), + Verbose: *debug, + } + go p.Start() + } +} + +func (p *proxyService) Stop(s service.Service) error { + return nil +} + +func getArgs() []string { + var result []string + cmds := []string{ + "install", "remove", "stop", "restart", "start", "stat", + } +A: + for _, v := range os.Args[1:] { + for _, c := range cmds { + if strings.Contains(v, c) { + continue A + } + } + result = append(result, v) + } + return result +} + +func main() { + flag.Parse() + svcConfig := &service.Config{ + Name: "miner-proxy", + DisplayName: "miner-proxy", + Description: "miner encryption proxy service", + Arguments: getArgs(), + } + s, err := service.New(new(proxyService), svcConfig) + if err != nil { + log.Fatalln(err) + } + if *install { + if err := s.Install(); err != nil { + log.Fatalln("代理服务安装失败", err) + return + } + log.Println("代理服务安装成功") + return + } + + if *remove { + if err := s.Uninstall(); err != nil { + log.Fatalln("移除代理系统服务失败", err) + } + log.Println("成功移除代理系统服务") + return + } + + if *stop { + if err := s.Stop(); err != nil { + log.Fatalln("停止代理服务失败", err) + } + log.Println("成功停止代理服务") + return + } + + if *restart { + if err := s.Restart(); err != nil { + log.Fatalln("重启代理服务失败", err) + } + log.Println("成功重启代理服务") + return + } + if *stat { + stat, err := s.Status() + if err != nil { + log.Fatalln("获取代理服务状态失败", err) + } + switch stat { + case service.StatusUnknown: + log.Println("未知的状态") + case service.StatusRunning: + log.Println("运行中") + case service.StatusStopped: + log.Println("停止运行") + } + return + } + + if *start { + if err := s.Start(); err != nil { + log.Fatalln("启动代理服务失败", err) + } + log.Println("启动代理服务成功") + return + } + s.Run() +} diff --git a/cmd/tcp-proxy/main.go b/cmd/tcp-proxy/main.go deleted file mode 100644 index 68fef10..0000000 --- a/cmd/tcp-proxy/main.go +++ /dev/null @@ -1,116 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "miner-proxy/pkg" - "miner-proxy/proxy" - "net" - "os" - "regexp" - "strings" -) - -var ( - version = "0.0.0-src" - matchid = uint64(0) - connid = uint64(0) - logger pkg.Logger - - localAddr = flag.String("l", ":9999", "本地监听地址") - remoteAddr = flag.String("r", "localhost:80", "远程代理地址或者远程本程序的监听地址") - secretKey = flag.String("secret_key", "", "数据包加密密钥, 只有远程地址也是本服务时才可使用") - isClient = flag.Bool("client", false, "是否是客户端, 该参数必须准确, 默认服务端, 只有 secret_key 不为空时需要区分") - debug = flag.Bool("debug", false, "是否开启debug") -) - -func main() { - flag.Parse() - - logger := pkg.ColorLogger{} - - logger.Info("miner-proxy (%s) proxing from %v to %v ", version, *localAddr, *remoteAddr) - - laddr, err := net.ResolveTCPAddr("tcp", *localAddr) - if err != nil { - logger.Warn("Failed to resolve local address: %s", err) - os.Exit(1) - } - raddr, err := net.ResolveTCPAddr("tcp", *remoteAddr) - if err != nil { - logger.Warn("Failed to resolve remote address: %s", err) - os.Exit(1) - } - listener, err := net.ListenTCP("tcp", laddr) - if err != nil { - logger.Warn("Failed to open local port to listen: %s", err) - os.Exit(1) - } - if len(*secretKey)%16 != 0 { - for len(*secretKey)%16 != 0 { - *secretKey += "0" - } - } - - for { - conn, err := listener.AcceptTCP() - if err != nil { - logger.Warn("Failed to accept connection '%s'", err) - continue - } - connid++ - p := proxy.New(conn, laddr, raddr) - p.SecretKey = *secretKey - p.IsClient = *isClient - p.Log = pkg.ColorLogger{ - Prefix: fmt.Sprintf("Connection #%03d ", connid), - Verbose: *debug, - } - go p.Start() - } -} - -func createMatcher(match string) func([]byte) { - if match == "" { - return nil - } - re, err := regexp.Compile(match) - if err != nil { - logger.Warn("Invalid match regex: %s", err) - return nil - } - - logger.Info("Matching %s", re.String()) - return func(input []byte) { - ms := re.FindAll(input, -1) - for _, m := range ms { - matchid++ - logger.Info("Match #%d: %s", matchid, string(m)) - } - } -} - -func createReplacer(replace string) func([]byte) []byte { - if replace == "" { - return nil - } - //split by / (TODO: allow slash escapes) - parts := strings.Split(replace, "~") - if len(parts) != 2 { - logger.Warn("Invalid replace option") - return nil - } - - re, err := regexp.Compile(string(parts[0])) - if err != nil { - logger.Warn("Invalid replace regex: %s", err) - return nil - } - - repl := []byte(parts[1]) - - logger.Info("Replacing %s with %s", re.String(), repl) - return func(input []byte) []byte { - return re.ReplaceAll(input, repl) - } -} diff --git a/go.mod b/go.mod index a7ed3d5..cdcc686 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.13 require ( github.com/dustin/go-humanize v1.0.0 + github.com/kardianos/service v1.2.0 // indirect github.com/mattn/go-colorable v0.1.4 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b ) diff --git a/go.sum b/go.sum index deb7403..c55d267 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/kardianos/service v1.2.0 h1:bGuZ/epo3vrt8IPC7mnKQolqFeYJb7Cs8Rk4PSOBB/g= +github.com/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= @@ -8,3 +10,5 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/proxy/proxy.go b/proxy/proxy.go index ca23f20..c351987 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -3,12 +3,12 @@ package proxy import ( "bytes" "crypto/tls" + "encoding/hex" "github.com/dustin/go-humanize" "io" "log" "miner-proxy/pkg" "net" - "strings" "sync" "sync/atomic" "time" @@ -123,21 +123,28 @@ func (p *Proxy) err(s string, err error) { if p.erred { return } - if err != io.EOF { - p.Log.Warn(s, err) - } + //if err != io.EOF { + p.Log.Warn(s, err) + //} p.errsig <- true p.erred = true } +var ( + proxyStart = []byte{115, 116, 97, 114, 116, 45, 112, 114, 111, 120, 121, 45, 101, 110, 99, 114, 121, 112, 116, 105, 111, 110} + proxyStartStr = string(proxyStart) + proxyEnd = []byte{115, 116, 97, 114, 116, 45, 112, 114, 111, 120, 121, 45, 101, 110, 100} + proxyEndStr = string(proxyEnd) +) + func (p *Proxy) pipe(src, dst io.ReadWriter, sendServer bool) { islocal := src == p.lconn var dataDirection string if islocal { - dataDirection = ">>> %d bytes sent%s" + dataDirection = "local >>> server %d bytes sent%s" } else { - dataDirection = "<<< %d bytes recieved%s" + dataDirection = "server >>> local %d bytes recieved%s" } var byteFormat string @@ -148,10 +155,10 @@ func (p *Proxy) pipe(src, dst io.ReadWriter, sendServer bool) { } //directional copy (64k buffer) - buff := make([]byte, 0xffff) + buff := make([]byte, 1024) for { n, err := src.Read(buff) - if err != nil { + if err != nil && err != io.EOF { p.err("Read failed '%s'\n", err) return } @@ -171,23 +178,38 @@ func (p *Proxy) pipe(src, dst io.ReadWriter, sendServer bool) { p.Log.Debug(dataDirection, n, "") p.Log.Trace(byteFormat, b) if p.SecretKey != "" { - - if bytes.HasPrefix(b, []byte("start-proxy")) { - b, err = pkg.AesDecrypt(bytes.TrimLeft(b, "start-proxy"), []byte(p.SecretKey)) - p.Log.Debug("解密后数据包: %s", strings.TrimSpace(string(b))) + if bytes.HasPrefix(b, proxyStart) { + + for !bytes.Contains(b, proxyEnd) { + var temp = make([]byte, 1) + n, err := src.Read(temp) + if err != nil { + p.err("Read proxyEnd failed '%s'\n", err) + return + } + if len(temp) == 0 { + continue + } + + b = append(b, temp[:n]...) + } + b = bytes.ReplaceAll(b, proxyEnd, nil) + p.Log.Debug("接受到加密数据包, 加密数据: %s; 数据大小: %d", hex.Dump(b)[:55], len(b)) + b, err = pkg.AesDecrypt(bytes.ReplaceAll(b, proxyStart, nil), []byte(p.SecretKey)) } if p.IsClient && sendServer { // 如果是客户端并且发送到服务端的数据全加密 - p.Log.Debug("发送到服务端数据包, 稍后加密数据: %s", strings.TrimSpace(string(b))) b, err = pkg.AesEncrypt(b, []byte(p.SecretKey)) - b = append([]byte("start-proxy"), b...) - + b = append(proxyStart, b...) + b = append(b, proxyEnd...) + p.Log.Debug("发送到服务端数据包, 加密数据: %s; 数据大小: %d", hex.Dump(b)[:55], len(b)) } if !p.IsClient && !sendServer { - p.Log.Debug("发送到客户端数据包, 稍后加密数据: %s", strings.TrimSpace(string(b))) b, err = pkg.AesEncrypt(b, []byte(p.SecretKey)) - b = append([]byte("start-proxy"), b...) + b = append(proxyStart, b...) + b = append(b, proxyEnd...) + p.Log.Debug("发送到客户端数据包, 加密数据: %s; 数据大小: %d", hex.Dump(b)[:55], len(b)) } if err != nil {