├── .gitignore ├── LICENSE.txt ├── README.md ├── client.go └── server.go /.gitignore: -------------------------------------------------------------------------------- 1 | personal.go 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jason Chu 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TOHT-Proxy 2 | TCP over HTTP proxy. Tunnels TCP traffic over the HTTP protocol to bypass corporate/school firewalls. To be used with SSH or OpenVPN to form a tunnel for all your traffic. 3 | 4 | Proudly written in Go (It's cross platform!) 5 | 6 | ## What makes this tunnel unique? 7 | It disguises your TCP traffic as HTTP (in a very hacky manner). This is to circumvent firewalls which strictly only 8 | allow valid HTTP that is negotiated by the firewall. This can also be used as a proxy. You have the 9 | client running on your computer behind the firewall, and the server running on the server hosting the VPN/SSH services 10 | (setting up a loopback), or any other web server that can then proxy it to your server hosting the services. 11 | 12 | Ok that was probably a bad explanation, here's an (irl) example for clarity: 13 | - I can't access my personal server because the whitelisting firewall at my school is blocking it. 14 | - I can however access Heroku servers. 15 | - I can set up the server software on Heroku. 16 | - This will form a tunnel from my computer, to Heroku, then to my personal server. 17 | 18 | ## How do I use it? 19 | 1. Install go on your client machine and server 20 | 2. On your client, download client.go and modify these lines near the top of the file 21 | 22 | ```go 23 | const proxyDomain = "127.0.0.1:9002" 24 | const port = "8080" 25 | ``` 26 | 27 | to the domain and port of the server that will be running server.go, and the port you would like to use 28 | as the tunnel on the client. 29 | 30 | 3. On your server, download server.go and modify these lines near the top of the file 31 | 32 | ```go 33 | const port = "9002" 34 | const target = "123.44.32.45:22" 35 | ``` 36 | 37 | to the port the tunnel will be running on (must match proxyDomain port specified in the client), and the target 38 | server's IP and port that you would like to channel TCP traffic to. Can be a lookback like `127.0.0.1:22`. This 39 | will usually be your IP:SSH port or OpenVPN port. Note that this proxy only supports connections to a single port, 40 | which is why you should use SSH or OpenVPN to tunnel all of your traffic. 41 | 4. Run client.go, and server.go 42 | 5. When setting up OpenVPN, change the TCP IP to 127.0.0.1:PORT 43 | **or** when setting up SSH, use `ssh username@127.0.0.1 -p PORT` (PORT being defined under client.go's `const port`) 44 | 45 | ## Limitations 46 | - Can only tunnel to 1 port on a remote server. 47 | - Can be quite unreliable as there are many points of failure and connections drop somewhat frequently. 48 | - Can be very slow to tunnel through multiple servers. 49 | 50 | #### If you are staff member from my school 51 | I intend no harm and I will not mention this to other students. I just wanted to work on a fun challenge and 52 | learn more about how networking works. 53 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "log" 8 | "math/rand" 9 | "net" 10 | ) 11 | 12 | const proxyDomain = "127.0.0.1:9002" 13 | const port = "8080" 14 | 15 | var letters = []rune("abcdefghijklmnopqrstuvwyz1234567890") 16 | 17 | func randSeq(n int) string { 18 | b := make([]rune, n) 19 | for i := range b { 20 | b[i] = letters[rand.Intn(len(letters))] 21 | } 22 | return string(b) 23 | } 24 | 25 | func handleConnection(clientConn net.Conn) { 26 | defer clientConn.Close() 27 | 28 | serverSend, err := net.Dial("tcp", proxyDomain) 29 | if err != nil { 30 | log.Println("Failed to connect to send proxy server!") 31 | return 32 | } 33 | 34 | serverListen, err := net.Dial("tcp", proxyDomain) 35 | if err != nil { 36 | log.Println("Failed to connect to listen proxy server!") 37 | return 38 | } 39 | 40 | defer serverListen.Close() 41 | defer serverSend.Close() 42 | 43 | clientId := randSeq(20) 44 | 45 | wait := make(chan bool) 46 | 47 | go func() { 48 | fmt.Fprintf(serverSend, "POST /transmit HTTP/1.1\r\n") 49 | fmt.Fprintf(serverSend, "Host: "+proxyDomain+"\r\n") 50 | fmt.Fprintf(serverSend, "Accept: */*\r\n") 51 | fmt.Fprintf(serverSend, "Connection: keep-alive\r\n") 52 | fmt.Fprintf(serverSend, "Clientid: "+clientId+"\r\n") 53 | fmt.Fprintf(serverSend, 54 | "Content-Type: multipart/form-data; boundary=----------SWAG------BOUNDARY----\r\n") 55 | // fmt.Fprintf(serverSend, "Transfer-Encoding: chunked\r\n") 56 | fmt.Fprintf(serverSend, "Content-Length: 12345789000\r\n\r\n") 57 | fmt.Fprintf(serverSend, "----------SWAG------BOUNDARY----\r\n") 58 | 59 | _, err = io.Copy(serverSend, clientConn) 60 | if err != nil { 61 | log.Println("Error copying client to server stream", err) 62 | } 63 | 64 | wait <- true 65 | }() 66 | 67 | go func() { 68 | fmt.Fprintf(serverListen, "GET /listen HTTP/1.1\r\n") 69 | fmt.Fprintf(serverListen, "Host: "+proxyDomain+"\r\n") 70 | fmt.Fprintf(serverListen, "Accept: */*\r\n") 71 | fmt.Fprintf(serverListen, "Clientid: "+clientId+"\r\n") 72 | fmt.Fprintf(serverListen, "Connection: keep-alive\r\n") 73 | fmt.Fprintf(serverListen, "\r\n") 74 | 75 | buf := bufio.NewReader(serverListen) 76 | 77 | success := false 78 | 79 | for line, err := buf.ReadString('\n'); true; line, err = buf.ReadString('\n') { 80 | if err != nil { 81 | log.Println("Failed to read following lines") 82 | return 83 | } 84 | 85 | if line == "HTTP/1.1 200 OK\r\n" { 86 | success = true 87 | } 88 | 89 | if success && line == "\r\n" { 90 | break 91 | } 92 | } 93 | 94 | if success { 95 | _, err = io.Copy(clientConn, buf) 96 | 97 | if err != nil { 98 | log.Println("Error copying server to client stream", err) 99 | } 100 | } else { 101 | log.Println("Failed to bind listen connection!") 102 | } 103 | 104 | wait <- true 105 | 106 | }() 107 | 108 | <-wait 109 | } 110 | 111 | func main() { 112 | log.Println("Listening...") 113 | ln, err := net.Listen("tcp", ":"+port) 114 | if err != nil { 115 | log.Println("Error listening!", err) 116 | return 117 | } 118 | 119 | for true { 120 | conn, err := ln.Accept() 121 | if err != nil { 122 | log.Println("Error accepting connection", err) 123 | continue 124 | } 125 | 126 | go handleConnection(conn) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net" 9 | "runtime/debug" 10 | ) 11 | 12 | const port = "9002" 13 | const target = "" 14 | 15 | type client struct { 16 | listenChannel chan bool // Channel that the client is listening on 17 | transmitChannel chan bool // Channel that the client is writing to 18 | listener io.Writer // The thing to write to 19 | listenerConnected bool 20 | transmitter io.Reader // The thing to listen from 21 | transmitterConnected bool 22 | } 23 | 24 | var connectedClients map[string]client = make(map[string]client) 25 | 26 | func bindServer(clientId string) { 27 | if connectedClients[clientId].listenerConnected && connectedClients[clientId].transmitterConnected { 28 | log.Println("Two-way connection to client established!") 29 | log.Println("Client <=|F|=> Proxy <-...-> VPN") 30 | 31 | defer func() { 32 | connectedClients[clientId].listenChannel <- true 33 | connectedClients[clientId].transmitChannel <- true 34 | delete(connectedClients, clientId) 35 | }() 36 | 37 | serverConn, err := net.Dial("tcp", target) 38 | if err != nil { 39 | log.Println("Failed to connect to remote server :/", err) 40 | } 41 | 42 | defer serverConn.Close() 43 | 44 | wait := make(chan bool) 45 | 46 | go func() { 47 | _, err := io.Copy(connectedClients[clientId].listener, serverConn) 48 | if err != nil { 49 | log.Println("Disconnect:", err) 50 | } 51 | 52 | wait <- true 53 | }() 54 | 55 | go func() { 56 | _, err := io.Copy(serverConn, connectedClients[clientId].transmitter) 57 | if err != nil { 58 | log.Println("Disconnect:", err) 59 | } 60 | 61 | wait <- true 62 | }() 63 | 64 | log.Println("Full connection established!") 65 | log.Println("Client <=|F|=> Proxy <---> VPN") 66 | 67 | <-wait 68 | log.Println("Connection closed") 69 | } 70 | } 71 | 72 | func handleConnection(clientConn net.Conn) { 73 | defer clientConn.Close() 74 | defer func() { 75 | if r := recover(); r != nil { 76 | fmt.Println("Connection panic:", r) 77 | debug.PrintStack() 78 | } 79 | }() 80 | 81 | reader := bufio.NewReader(clientConn) 82 | 83 | line, err := reader.ReadString('\n') 84 | if err != nil { 85 | // log.Println("Failed to read first line", err) 86 | return 87 | } 88 | if line == "GET /listen HTTP/1.1\r\n" { 89 | // This is for LISTENING 90 | resolvedId := "" 91 | for line, err = reader.ReadString('\n'); true; line, err = reader.ReadString('\n') { 92 | if err != nil { 93 | // log.Println("Failed to read following lines", err) 94 | return 95 | } 96 | 97 | if len(line) > 10 && line[:10] == "Clientid: " { 98 | resolvedId = line[10:30] 99 | } 100 | 101 | if line == "\r\n" { 102 | break 103 | } 104 | } 105 | 106 | if len(resolvedId) > 1 { 107 | fmt.Fprintf(clientConn, "HTTP/1.1 200 OK\r\n") 108 | fmt.Fprintf(clientConn, "Content-Type: application/octet-stream\r\n") 109 | fmt.Fprintf(clientConn, "Connection: keep-alive\r\n") 110 | fmt.Fprintf(clientConn, "Content-Length: 12345789000\r\n\r\n") 111 | 112 | wait := make(chan bool) 113 | 114 | if _, ok := connectedClients[resolvedId]; !ok { 115 | connectedClients[resolvedId] = client{} 116 | } 117 | 118 | currentClient := connectedClients[resolvedId] 119 | 120 | currentClient.listener = clientConn 121 | currentClient.listenChannel = wait 122 | currentClient.listenerConnected = true 123 | 124 | connectedClients[resolvedId] = currentClient 125 | 126 | // log.Println("Attempting to bind listener") 127 | 128 | go bindServer(resolvedId) 129 | 130 | <-wait 131 | } else { 132 | // log.Println("Failed to find client id!") 133 | } 134 | 135 | } else if line == "POST /transmit HTTP/1.1\r\n" { 136 | // This is for TRANSMITTING 137 | resolvedId := "" 138 | for line, err = reader.ReadString('\n'); true; line, err = reader.ReadString('\n') { 139 | if err != nil { 140 | // log.Println("Failed to read following lines") 141 | return 142 | } 143 | 144 | if len(line) > 10 && line[:10] == "Clientid: " { 145 | resolvedId = line[10:30] 146 | } 147 | 148 | if line == "----------SWAG------BOUNDARY----\r\n" { 149 | break 150 | } 151 | } 152 | 153 | if len(resolvedId) > 1 { 154 | wait := make(chan bool) 155 | 156 | if _, ok := connectedClients[resolvedId]; !ok { 157 | connectedClients[resolvedId] = client{} 158 | } 159 | 160 | currentClient := connectedClients[resolvedId] 161 | 162 | currentClient.transmitter = reader 163 | currentClient.transmitChannel = wait 164 | currentClient.transmitterConnected = true 165 | 166 | connectedClients[resolvedId] = currentClient 167 | 168 | // log.Println("Attempting to bind transmission") 169 | 170 | go bindServer(resolvedId) 171 | 172 | <-wait 173 | } else { 174 | // log.Println("Failed to find client id!") 175 | } 176 | 177 | } else { 178 | fmt.Fprintf(clientConn, "HTTP/1.1 404 Not found\r\n") 179 | fmt.Fprintf(clientConn, "Content-Type: text/plain\r\n") 180 | fmt.Fprintf(clientConn, "Content-Length: 8\r\n\r\n") 181 | fmt.Fprintf(clientConn, "u wot m9") 182 | } 183 | } 184 | 185 | func main() { 186 | log.Println("Listening...") 187 | ln, err := net.Listen("tcp", ":"+port) 188 | if err != nil { 189 | log.Println("Error listening!", err) 190 | return 191 | } 192 | 193 | for true { 194 | conn, err := ln.Accept() 195 | if err != nil { 196 | log.Println("Error accepting connection", err) 197 | continue 198 | } 199 | 200 | go handleConnection(conn) 201 | } 202 | } 203 | --------------------------------------------------------------------------------