I have a few problems to understand the theory of exposing clients behind NATs via a public server, for example to use the client as a proxy (preferably SOCKS5). I want to solve this problem using Go and there are some awesome Go libraries out there for SOCKS5 so this is no problem.
If a client behind a NAT connects to a public server, how do others users can connect (maybe on a different port) and how should I forward the requests?
e.g. if A (public server) listens on port 5544 and C (the client behind NAT connects to it while C also has a local SOCKS5 server where the requests are getting forwarded to) how does (U) the user or maybe even multiple users forward the request to C (the first connection made on A:5544)
[ U --> A:5544 <--> C <--> C local socks5 server]
User connects to A, C is connected to A, the traffic gets forwarded to C (note that C doesn't listen for incoming connections cause C is connected to A and not reverse, because C is behind a NAT)
I hope you understand what I mean.
Thank you in advance!
Edit:
To point out what i mean. C - the client behind the NAT does not directly listen on TCP it just opens up a TCP connection (net.Dial() ...) to the public server which soulde expose the client - the socks5 proxy on C is not directly exposed so the dialed TCP connection soulde somehow behave like a TCP listener.
I think basically it's the same technique that teamviewer is using. But how exactly? And also it's important that multiple users can connect to the client "C" through "A".
Addition: baloo's answer got me a step further but now the problem is that if the answer/traffic from the Client <--> Remote server connection get's copied (on the public server side code) via
_, err := io.Copy(usrConn, s.ProxyClient)
The data gets streamed/tunneled but if I request a site through the proxy the site is loading and loading this causes also the problem that other "User" connections are blocked. (I get a Site response on the first request so far but the request of the whole site is still pending... I may have a wrong understanding of the SOCKS5 protocol?)
Here is my messy prototype code for the public server: http://pastebin.com/7Y2JBVE0
And this is the code for the client (reverse proxy): http://pastebin.com/w3i9cap4
You may use httputil.ReverseProxy
to redirect User to local servers:
Also see: http://blog.charmes.net/2015/07/reverse-proxy-in-go.html
Test sample code:
package main
import (
"net/http"
"net/http/httputil"
"net/url"
)
func handler(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.Host = r.URL.Host
handler.ServeHTTP(w, r)
})
}
func main() {
u, _ := url.Parse("https://translate.google.com")
reverseProxy := httputil.NewSingleHostReverseProxy(u)
http.ListenAndServe(":3000", handler(reverseProxy))
}
The connections has to be paired as to make two "clients" talk to each other, where one of them acts as the SOCKS5 server and one as the SOCKS5 client. You accept TCP connections on the public server from both the "server behind NAT" and the "client that connects to server behind NAT".
Once both connections are up, you could use io.Copy to proxy the inputs and outputs between the two connections.
You will have to keep a pool of connections around, so that there will be one SOCKS5 server connection available to any new SOCKS5 client connection request.
The server behind NAT will have to dial a new connection to the public server and then serve SOCKS5 requests on that connection using a generic SOCKS5 server that accepts io.ReadWriteCloser.
A simplified and untested example:
package main
import (
"io"
"log"
"net"
)
func main() {
lnServer, err := net.Listen("tcp", "1234")
if err != nil {
log.Fatalln(err)
}
lnClients, err := net.Listen("tcp", "1235")
if err != nil {
log.Fatalln(err)
}
serverCh := make(chan net.Conn)
go func() {
for {
conn, err := lnServer.Accept()
if err != nil {
// handle errors
}
serverCh <- conn
}
}()
for {
conn, err := ln.Accept()
if err != nil {
// handle errors
}
sc <- serverCh
go io.Copy(sc, conn)
io.Copy(conn, sc)
}
}
An easier approach to avoid creating your own connection pooling and pairing might be to create a VPN between the two servers. Where the public server forwards all connections towards the NAT'ed client. Which will be able to accept connections on a virtual IP that is assigned through VPN. One popular solution for doing this setup is OpenVPN.