diff --git a/README.md b/README.md index 5eec344..0d48769 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ # tcprp -TCP Reverse Proxy,可反向代理任何TCP链接 +TCP Reverse Proxy,可反向代理任何内网TCP链接 + +## 编译 + +``` +go get github.com/Bluek404/tcprp/... +``` + +## 使用 + +客户端放在需要代理的内网服务器上 + +`./client 目标服务器 代理服务器 密钥` + +例如: `./client 127.0.0.1:8080 example.com:8091 KEYKEY` (此处example.com代指服务器地址) + +服务端放在有公网IP的服务器上 + +`./server 服务端口 代理通信端口 密钥` + +例如: `./server :80 :8091 KEYKEY` + +然后用户访问*example.com:80*就可以了 diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..7f0a7a3 --- /dev/null +++ b/client/client.go @@ -0,0 +1,138 @@ +/*/ // + * The MIT License (MIT) + * + * Copyright (c) 2015 Bluek404 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +/*/ // + +package main + +import ( + "fmt" + "io" + "log" + "net" + "os" + "time" +) + +const ( + CMD byte = iota + PROXY +) + +var proxyServer, targetServer, key string + +func init() { + if len(os.Args) != 4 { + fmt.Printf("用法:\n"+ + "%v 目标服务器 代理服务器 密钥\n", os.Args[0]) + os.Exit(2) + } + targetServer, proxyServer, key = os.Args[1], os.Args[2], os.Args[3] +} + +func c2Server(sleep time.Duration) net.Conn { + time.Sleep(time.Second * sleep) + conn, err := net.Dial("tcp", proxyServer) + if err != nil { + log.Println("连接服务端失败:", err, "将于", int(sleep+1), "秒后重试") + return c2Server(sleep + 1) + } + _, err = conn.Write(append([]byte{CMD}, []byte(key)...)) + if err != nil { + log.Println(err) + os.Exit(1) + } + return conn +} + +func copy(dst, src net.Conn) { + io.Copy(dst, src) + dst.Close() + src.Close() +} + +func proxy() { + c, err := net.Dial("tcp", proxyServer) + if err != nil { + log.Println(err) + return + } + _, err = c.Write(append([]byte{PROXY}, []byte(key)...)) + if err != nil { + log.Println(err) + os.Exit(1) + } + // 读取用户IP长度 + buf := make([]byte, 1) + n, err := c.Read(buf) + if err != nil { + log.Println(err) + c.Close() + return + } + if n != 1 { + log.Println("读取用户IP长度失败") + c.Close() + return + } + ipLen := buf[0] + buf = make([]byte, ipLen) + // 读取用户真实IP + n, err = c.Read(buf) + if err != nil { + log.Println(err) + c.Close() + return + } + if n != int(ipLen) { + log.Println("用户IP长度错误") + c.Close() + return + } + clientIP := string(buf) + // 与需代理的程序连接 + pc, err := net.Dial("tcp", targetServer) + if err != nil { + log.Println(err) + c.Close() + return + } + log.Printf("[+]: %v(%v)\n", clientIP, pc.LocalAddr()) + defer log.Printf("[-]: %v(%v)\n", clientIP, pc.LocalAddr()) + go copy(pc, c) + copy(c, pc) +} + +func main() { + conn := c2Server(0) + defer conn.Close() + + buf := make([]byte, 1) + for { + _, err := conn.Read(buf) + if err != nil { + log.Println(err) + conn = c2Server(0) + } + go proxy() + } +} diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..934c441 --- /dev/null +++ b/server/server.go @@ -0,0 +1,195 @@ +/*/ // + * The MIT License (MIT) + * + * Copyright (c) 2015 Bluek404 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +/*/ // + +package main + +import ( + "fmt" + "io" + "log" + "net" + "os" +) + +const proxyBuffSize = 128 + +const ( + CMD byte = iota + PROXY +) + +var ( + serverPort, proxyPort, key string + exit = make(chan int) + proxy = &Proxy{ + request: make(chan bool, proxyBuffSize), + response: make(chan net.Conn, proxyBuffSize), + destroy: make(chan bool), + } +) + +func init() { + if len(os.Args) != 4 { + fmt.Printf("用法:\n"+ + "%v 服务端口 代理通信端口 密钥\n", os.Args[0]) + os.Exit(2) + } + serverPort, proxyPort, key = os.Args[1], os.Args[2], os.Args[3] +} + +func copy(dst, src net.Conn) { + io.Copy(dst, src) + dst.Close() + src.Close() +} + +// 与用户通信 +func serve() { + ln, err := net.Listen("tcp", serverPort) + if err != nil { + log.Println(err) + exit <- 1 + } + for { + conn, err := ln.Accept() + if err != nil { + log.Println(err) + continue + } + go func() { + log.Println("[+]:", conn.RemoteAddr()) + defer log.Println("[-]:", conn.RemoteAddr()) + pconn := proxy.getConn() + if pconn == nil { + // 代理客户端不在线 + conn.Close() + return + } + go copy(conn, pconn) + copy(pconn, conn) + }() + } +} + +type Proxy struct { + request chan bool + response chan net.Conn + // 因为没用心跳包, + // 所以只能靠用户请求来检测在线, + // 但是如果还没有用户请求代理客户端就下线并重新上线, + // 需要手动销毁上一个已关闭链接 + destroy chan bool + online bool +} + +// 与代理客户端通信 +func (p *Proxy) serve() { + go func() { <-p.destroy }() // 接收首次销毁请求,看下面就明白了 + ln, err := net.Listen("tcp", proxyPort) + if err != nil { + log.Println(err) + exit <- 1 + } + for { + conn, err := ln.Accept() + if err != nil { + log.Println(err) + continue + } + go func() { + clientIP := conn.RemoteAddr().String() + buf := make([]byte, 1024) + n, err := conn.Read(buf) + if err != nil { + log.Println(err) + conn.Close() + return + } + k := string(buf[1:n]) + // TODO: 支持超长key和登录后使用session代替key + if k != key { + // 认证失败 + log.Println(conn.RemoteAddr(), "错误key:", k) + conn.Close() + return + } + // 首位字节为请求类型 + switch buf[0] { + case CMD: + log.Println("[+]代理客户端:", clientIP) + defer log.Println("[-]代理客户端:", clientIP) + p.destroy <- true // 请求销毁上一个链接 + defer conn.Close() + p.online = true + for { + select { + case <-p.destroy: + // 新的代理客户端连线,销毁这个 + return + case <-p.request: + _, err = conn.Write([]byte{0}) + if err != nil { + log.Println(err) + p.response <- nil + p.online = false + // 因为现在已经销毁,所以需要开一个线程 + // 用于接收代理客户端上线时的销毁请求 + go func() { <-p.destroy }() + return + } + } + } + case PROXY: + // 发送用户真实IP给代理客户端 + // 首位字节为IP的字节长度 + ipByte := []byte(clientIP) + l := byte(len(ipByte)) + _, err = conn.Write(append([]byte{l}, ipByte...)) + if err != nil { + log.Println(err) + return + } + p.response <- conn + } + }() + } +} + +func (p *Proxy) getConn() net.Conn { + if !p.online { + return nil + } + p.request <- true + c := <-p.response + if c == nil { + return c + } + return c +} + +func main() { + go serve() + go proxy.serve() + os.Exit(<-exit) +}