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
)