Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to transport a TCP connection over websockets or another protocol?

I'm wondering, is it possible to transport/tunnel a TCP connection though the websockets protocol?

There is a websockets package and it seems I could io.copy the TCP connection into the websockets connection but I don't know how to reassemble it as TCPConn on the other end.

For example I would like to create a socks5 ServerN(behind NAT) on one end and a ServerIE on the other end. An internet user would make a socks5 request (though the socks5 protocol) to ServerIE and ServerIE would send the socks5 request through the websocket connection to ServerNAT. ServerNAT will then process the socks5 request(i.e. it's a socks5 server).

EDIT: I've wrote some code but unfortunately it gets stuck on reading from the TCP connection into WS. To run it: go run main.go -logtostderr

package main

import (
    "flag"

    log "github.com/golang/glog"
    "golang.org/x/net/websocket"
    "bufio"
    "net"
    "net/http"
    "time"
//  "io"
)

func main() {
    flag.Parse()
    defer log.Flush()
    log.Infof("wsServer()")
    wsCh := wsServer()
    log.Infof("wsNAT()")
    natWS, err := wsNAT()
    if err != nil {
        log.Error(err)
        return
    }
    done := make(chan struct{}, 1)
        log.Infof("listenTCP()")
        conn := listenTCP()
        buf := bufio.NewReader(conn)
    go func() {
        log.Infof("Write TCP to WS")
        n, err := buf.WriteTo(natWS)
        if err != nil {
            log.Fatal(err)
            return
        }
        log.Infof("newConn.ws.ReadFrom(conn) %v", n)
        close(done)
    }()
    wsToTCP(<- wsCh)
    <-done
}
// wsNAT dials /ws endpoint and returns a websocket connection.
func wsNAT() (*websocket.Conn, error) {
    time.Sleep(1 * time.Second)
    origin := "http://localhost/"
    url := "ws://localhost:12345/ws"
    return websocket.Dial(url, "", origin)
}
 // wsServer returns the websocket
 // connections received on /ws endpoint
func wsServer() chan *websocket.Conn {
    wsCh := make(chan *websocket.Conn, 1)
    http.Handle("/ws", websocket.Handler(func(ws *websocket.Conn) {
        wsCh <- ws
        time.Sleep(1 *time.Hour)
    }))
    go func() {
        err := http.ListenAndServe(":12345", nil)
        if err != nil {
            panic("ListenAndServe: " + err.Error())
        }
    }()
    return wsCh
}
// wsToTCP reads a ws connection and converts its payload
// to a TCP connection.
func wsToTCP(ws *websocket.Conn) {
    conn := &net.TCPConn{}
    var msg []byte
    if _, err := ws.Read(msg); err != nil {
        log.Error(err)
        return
    }
    log.Infof("msg %v", msg)
    n, err := conn.Write(msg)
    if err != nil {
        log.Errorf("conn.Write %v, msg %v", err, msg)
        return
    }
    log.Infof("conn is %v, copied %v", conn, n)
    // END: do something with the connection
    // ..... 
}
//ListenTCP returns the first TCP connection received on port 8080
func listenTCP() *net.TCPConn {
    addr := &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 8080}
    lr, err := net.ListenTCP("tcp", addr)
    if err != nil {
        panic(err)

    }
    defer lr.Close()
    ieConn, err := lr.AcceptTCP()
    if err != nil {
        panic(err)
    }
    log.Infof("connection accepted")
    return ieConn
}

To trigger the TCP listener you can issue a request on port 8080 (e.g. curl --socks5 localhost:8080 google.com -v or curl localhost:8080 -v)

like image 207
Anthony Hunt Avatar asked Aug 21 '15 08:08

Anthony Hunt


People also ask

Can WebSocket connect to TCP server?

Absolutely! That is the purpose of websockify. Of course your WebSocket client application will need to be able to implement the protocol of the TCP server. For example, noVNC is a browser VNC client that implements the RFB protocol and by using websockify it can connect to a normal TCP based VNC server.

Is WebSocket on top of TCP?

Conceptually, WebSockets is a layer on top of TCP that: Adds a web origin-based security model for browsers. Adds an addressing and protocol naming mechanism to support multiple services on one port and multiple host names on one IP address.

Are WebSockets over TCP or UDP?

The WebSocket protocol is an independent TCP-based protocol. Its only relationship to HTTP is that its handshake is interpreted by HTTP servers as an Upgrade request.

Is WebSocket a transport protocol?

WebSocket layers on top of TCP, considered as transport layer in TCP/IP model, and one can layer application layer protocol on top of WebSocket. Show activity on this post. HTTP, SSL, HTTPS, WebSockets, etc. are all application layer protocols.


1 Answers

This shouldn't be any problem. I've built TCP forwarders over a number of protocols. Never WebSockets, but I don't see a reason it would be different.

You have two problems:

  • The correct way to make a connection is with net.Dial, not by just creating a net.TCPConn.
  • You're only copying in one direction. You need to copy in both directions.

Here's a super simple proxy that I built this morning for a different purpose (my full code corrupts a packet every so often so I can test bad connectoins). It should be convertible to many other purposes:

func run(inPort int, dest string) {
    l, err := net.Listen("tcp", fmt.Sprintf(":%d", inPort))
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    fmt.Printf("http://%v -> %s\n", l.Addr(), dest)

    for {
        conn, err := l.Accept()
        if err != nil {
            log.Fatal(err)
        }

        fmt.Printf("Connection from %s\n", conn.RemoteAddr())
        go proxy(conn, dest)
    }
}

func proxy(in net.Conn, dest string) {
    out, err := net.Dial("tcp", dest)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    go io.Copy(out, in)
    io.Copy(in, out)
    fmt.Printf("Disconnect from %s\n", in.RemoteAddr())
}

You just need to replace the net.Dial here with a WS call to your server, and on the server side use net.Dial to forward it along.

But probably the most key point is the last few lines. You have to copy in both directions and people forget that.

like image 76
Rob Napier Avatar answered Oct 25 '22 14:10

Rob Napier