golang websocket to tcp forwarder
I’ve been writing a simple websocket to tcp forwarder in golang for a project I’m working on.
I previously showed a simple echo example using Gorilla websockets. The echoing example blocks on the sockets until data is available, however that isn’t really going to work here. At first I tried to use the SetReadDeadLine, mirroring non-blocking IO in C. While this worked, it wasn’t really an ideal solution. What I ended up with was my first use of go routines! Basically breaking the code into two threads each of which blocks on reads from the 2 sockets. The code is below for reference (it currently forwards traffic to/from localhost, but the first 12bytes sent from the client should be the destination host port, I’ll update this post then that codes in place):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | package main import ( "github.com/gorilla/websocket" "net/http" "net" "fmt" "io" ) var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } func print_binary(s []byte) { fmt.Printf( "print b:" ); for n := 0;n < len(s);n++ { fmt.Printf( "%d," ,s[n]); } fmt.Printf( "\n" ); } func address_decode(address_bin []byte) (string,string) { var host string = "127.0.0.1" var port string = "22" ; return host,port } func forwardtcp(wsconn *websocket.Conn,conn net.Conn) { for { // Receive and forward pending data from tcp socket to web socket tcpbuffer := make([]byte, 1024) n,err := conn.Read(tcpbuffer) if err == io.EOF { fmt.Printf( "TCP Read failed" ); break ; } if err == nil { fmt.Printf( "Forwarding from tcp to ws: %d bytes: %s\n" ,n,tcpbuffer) print_binary(tcpbuffer) wsconn.WriteMessage(websocket.BinaryMessage,tcpbuffer[:n]) } } } func forwardws (wsconn *websocket.Conn,conn net.Conn) { for { // Send pending data to tcp socket n,buffer,err := wsconn.ReadMessage() if err == io.EOF { fmt.Printf( "WS Read Failed" ); break ; } if err == nil { s := string(buffer[:len(buffer)]) fmt.Printf( "Received (from ws) forwarding to tcp: %d bytes: %s %d\n" ,len(buffer),s,n) print_binary(buffer) conn.Write(buffer) } } } func wsProxyHandler(w http.ResponseWriter, r *http.Request) { wsconn, err := upgrader.Upgrade(w, r, nil) if err != nil { //log.Println(err) return } // get connection address and port address := make([]byte, 16) n,address,err := wsconn.ReadMessage() if err != nil { fmt.Printf( "address read error" ); fmt.Printf( "read %d bytes" ,n); } print_binary(address) host, port := address_decode(address) conn, err := net.Dial( "tcp" , host + ":" + port) if err != nil { // handle error } go forwardtcp(wsconn,conn) go forwardws(wsconn,conn) fmt.Printf( "websocket closed" ); } func main() { http.HandleFunc( "/echo" , wsProxyHandler) http.Handle( "/" , http.FileServer(http.Dir( "." ))) err := http.ListenAndServe( ":8080" , nil) if err != nil { panic( "Error: " + err.Error()) } } |