A TCP relay mechanism with Node.js


These TCP relay scripts can be used to expose any TCP/IP service running behind a NAT. This includes services that use HTTP, SSH, and so on. The latest source code is available at GitHub. Use npm to install.

The relay server script is meant to be executed on the server visible on the internet as follows

tcprelays --relayPort port --servicePort port

relayPort is the port where the relay server will listen for incoming connections from the relay client. servicePort is the port where external clients can connect to the service exposed through the relay.

The relay client script is meant to be executed on a machine behind a NAT as follows

tcprelayc --host host --port port --relayHost host --relayPort port [--numConn count]

host is any server visible to the machine behind the NAT, it can also be localhost. port is the port of the service you want to expose through the relay. relayServer is the host or IP address of the server visible on the internet, and already executing the relay server script. relayPort is the port where this script will connect with the relay server. numConn is the number of unused sockets relay client maintains with the relay server. As soon as it detects data activity on a socket, relay client establishes another connection.

If you’re using HTTP/S, use a reverse proxy such as http-proxy, between the relay client and the local service e.g.

var httpProxy = require('http-proxy');
httpProxy.createProxyServer({target:'http://host:port'}).listen(port);

One external module is used in these scripts to parse commands line options. It can be installed using npm as follows

sudo npm -g install optimist@latest

Programming Interface

Create and start a relay client thus

var relayClient = require("node-tcp-relay")
var newRelayClient = relayClient.createRelayClient("hostname", 8080, "relayserver", 10080, 1);

End relay client

newRelayClient.end();

Create and start a relay server thus

var relayServer = require("node-tcp-relay");
var newRelayServer = relayServer.createRelayServer(10080, 10081);

End relay server

newRelayServer.end();    

13 thoughts on “A TCP relay mechanism with Node.js

  1. I like this. It means I could have some service running at home behind a NAT that would supply updated data on demand to my no.de server. One application that comes to mind is home security. First I would have a server at home with access to web cameras, microphones, temperature sensors etc. Second I would have a server on the Internet which I can access from anywhere, coffeshops, office, hotel, mobile phone. Then, when I want to check home security, or listen in on the babysitter, or check that I did indeed turn off the burner in the kitchen, I can access this simply using the Internet server. This could be made robust enough to survive NAT box reboots, and DHCP address changes at home.

    1. Glad you liked it. The uses you mention are quite interesting, using the tls module of node should make communication more secure.

  2. Hi Michael,
    Thanks for the great example.
    I’m trying to test it and running into some problems.
    (This is my first Node.js attempt, so I apologize if I’m getting something very basic wrong).

    In the relays code:
    var clientSocket = socketPair[uniqueKey(socket)];
    this is always undefined, since the server socket is the one that writes to the socketpair here:
    socketPair[uniqueKey(socket)] = nextSocket;
    socketPair[uniqueKey(nextSocket)] = socket;

    however, this code won’t get reached if nextSocket is undefined, which is the case
    since it only gets assigned when the relay server socket gets a connection.

    What am I missing here? (below is some terminal session output)
    Thanks!

    >node relays.js –rp 49999 –sp 49998
    { _: [],
    ‘$0’: ‘node ./relays.js’,
    rp: 49999,
    sp: 49998 }

    (different shell window)
    >node relayc.js –sh localhost –sp 49997 –rh localhost –rp 49999
    { _: [],
    ‘$0’: ‘node ./relayc.js’,
    sh: ‘localhost’,
    sp: 49997,
    rh: ‘localhost’,
    rp: 49999 }

    node.js:134
    throw e; // process.nextTick error, or ‘error’ event on first tick
    ^
    Error: ECONNREFUSED, Connection refused
    at Socket._onConnect (net.js:601:18)
    at IOWatcher.onWritable [as callback] (net.js:186:12)

    If I telnet localhost 49999 and send “hello” I get:
    next relay socket established
    relay socket data:hello

    relay socket closed
    client socket pair not found

    1. You probably don’t have any service running at port 49997, the connection refused message by relayc.js could be due to that. Having something listening at port 49997 (or try netcat) will resolve that.

      You might want to take a look at the net api and the examples provided there to get a feeling for how things work in relays.js and relayc.js: http://nodejs.org/docs/latest/api/net.html

  3. Hi Devendra,

    Nice example. Thanks a lot for sharing. It works fine as it is. I would like to ask few suggestions..

    The relayserver “server port” is exposed to everyone. How we can control the access here? Could you give some hints (any database in server can restrict with password?)

    Also, if i need to have multiple localservers connecting to one relay server, can i still use a single server port at relay server? and how can be a client is given access to specific local server.

    Appreciate your help.

    Thanks.
    Chava

    1. Hi Chava,

      Editing reply to explain my thought process better…

      If you intend to use the HTTP protocol, you can use a reverse proxy to route requests to multiple servers behind the NAT. The relay client does not need to be aware that you are using a reverse proxy. The reverse proxy can also handle authentication.

      Authentication can also be implemented in your application/service. The relay does not need to be aware that you are doing it.

      If you have time and money to throw at the problem, I would suggest looking into standards such as STUN/TURN to traverse NAT.

      Kind Regards,
      Devendra

      1. Thanks Devendra,

        I have my https server behind NAT and my mobile need to access the web (none of them has public IP). Is there any reverse proxy (i can give a public IP) that can be connected with web servers with private IP?

        I tried ssh reverse tunnelling but i don’t like that solution.

        Yes, I tried COTURN but I don’t find much help in TCP relaying clients. The examples there only for UDP relaying.

        Thanks for your suggestions.
        Chava

      2. Hi Chava,

        You can put an HTTP reverse proxy between the relay client and your other services. That way, incoming HTTP requests can be routed to different services. Reverse proxies can also be setup to perform authentication. For instance, https://github.com/movableink/doorman can authenticate via OAuth.

        Kind Regards,
        Devendra

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s