├── LICENSE ├── README.md ├── client └── client.go └── server └── server.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Bluek404 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tcprp 2 | TCP Reverse Proxy,可反向代理任何内网TCP链接 3 | 4 | 比如一个临时的网站、Minecraft服务器、其他什么使用TCP协议的程序 5 | 6 | 并且在不想设置路由器端口映射或者没有公网IP的情况下 7 | 8 | ## 编译 9 | 10 | ``` 11 | go get github.com/Bluek404/tcprp/... 12 | ``` 13 | 14 | ## 使用 15 | 16 | 客户端放在需要代理的内网服务器上 17 | 18 | `./client 目标服务器 代理服务器 密钥` 19 | 20 | 例如: `./client 127.0.0.1:8080 example.com:8091 KEYKEY` (此处example.com代指服务器地址) 21 | 22 | 服务端放在有公网IP的服务器上 23 | 24 | `./server 服务端口 代理通信端口 密钥` 25 | 26 | 例如: `./server :80 :8091 KEYKEY` 27 | 28 | 然后用户访问*example.com:80*就可以了 29 | -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | /*/ // 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Bluek404 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | /*/ // 24 | 25 | package main 26 | 27 | import ( 28 | "fmt" 29 | "io" 30 | "log" 31 | "net" 32 | "os" 33 | "time" 34 | ) 35 | 36 | const ( 37 | CMD byte = iota 38 | PROXY 39 | ) 40 | 41 | var proxyServer, targetServer, key string 42 | 43 | func init() { 44 | if len(os.Args) != 4 { 45 | fmt.Printf("用法:\n"+ 46 | "%v 目标服务器 代理服务器 密钥\n", os.Args[0]) 47 | os.Exit(2) 48 | } 49 | targetServer, proxyServer, key = os.Args[1], os.Args[2], os.Args[3] 50 | } 51 | 52 | func c2Server(sleep time.Duration) net.Conn { 53 | time.Sleep(time.Second * sleep) 54 | conn, err := net.Dial("tcp", proxyServer) 55 | if err != nil { 56 | log.Println("连接服务端失败:", err, "将于", int(sleep+1), "秒后重试") 57 | return c2Server(sleep + 1) 58 | } 59 | _, err = conn.Write(append([]byte{CMD}, []byte(key)...)) 60 | if err != nil { 61 | log.Println(err) 62 | os.Exit(1) 63 | } 64 | return conn 65 | } 66 | 67 | func copy(dst, src net.Conn) { 68 | io.Copy(dst, src) 69 | dst.Close() 70 | src.Close() 71 | } 72 | 73 | func proxy() { 74 | c, err := net.Dial("tcp", proxyServer) 75 | if err != nil { 76 | log.Println(err) 77 | return 78 | } 79 | _, err = c.Write(append([]byte{PROXY}, []byte(key)...)) 80 | if err != nil { 81 | log.Println(err) 82 | os.Exit(1) 83 | } 84 | // 读取用户IP长度 85 | buf := make([]byte, 1) 86 | n, err := c.Read(buf) 87 | if err != nil { 88 | log.Println(err) 89 | c.Close() 90 | return 91 | } 92 | if n != 1 { 93 | log.Println("读取用户IP长度失败") 94 | c.Close() 95 | return 96 | } 97 | ipLen := buf[0] 98 | buf = make([]byte, ipLen) 99 | // 读取用户真实IP 100 | n, err = c.Read(buf) 101 | if err != nil { 102 | log.Println(err) 103 | c.Close() 104 | return 105 | } 106 | if n != int(ipLen) { 107 | log.Println("用户IP长度错误") 108 | c.Close() 109 | return 110 | } 111 | clientIP := string(buf) 112 | // 与需代理的程序连接 113 | pc, err := net.Dial("tcp", targetServer) 114 | if err != nil { 115 | log.Println(err) 116 | c.Close() 117 | return 118 | } 119 | log.Printf("[+]: %v(%v)\n", clientIP, pc.LocalAddr()) 120 | defer log.Printf("[-]: %v(%v)\n", clientIP, pc.LocalAddr()) 121 | go copy(pc, c) 122 | copy(c, pc) 123 | } 124 | 125 | func main() { 126 | conn := c2Server(0) 127 | defer conn.Close() 128 | 129 | buf := make([]byte, 1) 130 | for { 131 | _, err := conn.Read(buf) 132 | if err != nil { 133 | log.Println(err) 134 | conn = c2Server(0) 135 | } 136 | go proxy() 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | /*/ // 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Bluek404 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | /*/ // 24 | 25 | package main 26 | 27 | import ( 28 | "fmt" 29 | "io" 30 | "log" 31 | "net" 32 | "os" 33 | ) 34 | 35 | const proxyBuffSize = 128 36 | 37 | const ( 38 | CMD byte = iota 39 | PROXY 40 | ) 41 | 42 | var ( 43 | serverPort, proxyPort, key string 44 | exit = make(chan int) 45 | proxy = &Proxy{ 46 | request: make(chan bool, proxyBuffSize), 47 | response: make(chan net.Conn, proxyBuffSize), 48 | destroy: make(chan bool), 49 | } 50 | ) 51 | 52 | func init() { 53 | if len(os.Args) != 4 { 54 | fmt.Printf("用法:\n"+ 55 | "%v 服务端口 代理通信端口 密钥\n", os.Args[0]) 56 | os.Exit(2) 57 | } 58 | serverPort, proxyPort, key = os.Args[1], os.Args[2], os.Args[3] 59 | } 60 | 61 | func copy(dst, src net.Conn) { 62 | io.Copy(dst, src) 63 | dst.Close() 64 | src.Close() 65 | } 66 | 67 | // 与用户通信 68 | func serve() { 69 | ln, err := net.Listen("tcp", serverPort) 70 | if err != nil { 71 | log.Println(err) 72 | exit <- 1 73 | } 74 | for { 75 | conn, err := ln.Accept() 76 | if err != nil { 77 | log.Println(err) 78 | continue 79 | } 80 | go func() { 81 | log.Println("[+]:", conn.RemoteAddr()) 82 | defer log.Println("[-]:", conn.RemoteAddr()) 83 | pconn := proxy.getConn() 84 | if pconn == nil { 85 | // 代理客户端不在线 86 | conn.Close() 87 | return 88 | } 89 | go copy(conn, pconn) 90 | copy(pconn, conn) 91 | }() 92 | } 93 | } 94 | 95 | type Proxy struct { 96 | request chan bool 97 | response chan net.Conn 98 | // 因为没用心跳包, 99 | // 所以只能靠用户请求来检测在线, 100 | // 但是如果还没有用户请求代理客户端就下线并重新上线, 101 | // 需要手动销毁上一个已关闭链接 102 | destroy chan bool 103 | online bool 104 | } 105 | 106 | // 与代理客户端通信 107 | func (p *Proxy) serve() { 108 | go func() { <-p.destroy }() // 接收首次销毁请求,看下面就明白了 109 | ln, err := net.Listen("tcp", proxyPort) 110 | if err != nil { 111 | log.Println(err) 112 | exit <- 1 113 | } 114 | for { 115 | conn, err := ln.Accept() 116 | if err != nil { 117 | log.Println(err) 118 | continue 119 | } 120 | go func() { 121 | clientIP := conn.RemoteAddr().String() 122 | buf := make([]byte, 1024) 123 | n, err := conn.Read(buf) 124 | if err != nil { 125 | log.Println(err) 126 | conn.Close() 127 | return 128 | } 129 | k := string(buf[1:n]) 130 | // TODO: 支持超长key和登录后使用session代替key 131 | if k != key { 132 | // 认证失败 133 | log.Println(conn.RemoteAddr(), "错误key:", k) 134 | conn.Close() 135 | return 136 | } 137 | // 首位字节为请求类型 138 | switch buf[0] { 139 | case CMD: 140 | log.Println("[+]代理客户端:", clientIP) 141 | defer log.Println("[-]代理客户端:", clientIP) 142 | p.destroy <- true // 请求销毁上一个链接 143 | defer conn.Close() 144 | p.online = true 145 | for { 146 | select { 147 | case <-p.destroy: 148 | // 新的代理客户端连线,销毁这个 149 | return 150 | case <-p.request: 151 | _, err = conn.Write([]byte{0}) 152 | if err != nil { 153 | log.Println(err) 154 | p.response <- nil 155 | p.online = false 156 | // 因为现在已经销毁,所以需要开一个线程 157 | // 用于接收代理客户端上线时的销毁请求 158 | go func() { <-p.destroy }() 159 | return 160 | } 161 | } 162 | } 163 | case PROXY: 164 | // 发送用户真实IP给代理客户端 165 | // 首位字节为IP的字节长度 166 | ipByte := []byte(clientIP) 167 | l := byte(len(ipByte)) 168 | _, err = conn.Write(append([]byte{l}, ipByte...)) 169 | if err != nil { 170 | log.Println(err) 171 | return 172 | } 173 | p.response <- conn 174 | } 175 | }() 176 | } 177 | } 178 | 179 | func (p *Proxy) getConn() net.Conn { 180 | if !p.online { 181 | return nil 182 | } 183 | p.request <- true 184 | c := <-p.response 185 | if c == nil { 186 | return c 187 | } 188 | return c 189 | } 190 | 191 | func main() { 192 | go serve() 193 | go proxy.serve() 194 | os.Exit(<-exit) 195 | } 196 | --------------------------------------------------------------------------------