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
)
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.
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.
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.
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.
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:
net.Dial
, not by just creating a net.TCPConn
.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With