Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TCP hole punching in Node.js

I'm trying to punch a TCP hole through two NATs in node.js. The problem is I can't figure out how to choose which local port the connection should use?

like image 256
Friend of Kim Avatar asked Feb 10 '14 16:02

Friend of Kim


2 Answers

After creating a connection with the public server, you also need to listen on the exact same local (!!) port that was used to establish that connection.

I extended your testcode to a complete tcp hole punching proof of concept:

// server.js
var server = require('net').createServer(function (socket) {
    console.log('> Connect to this public endpoint with clientB:', socket.remoteAddress + ':' + socket.remotePort);
}).listen(4434, function (err) {
    if(err) return console.log(err);
    console.log('> (server) listening on:', server.address().address + ':' + server.address().port)
});

// clientA.js
var c = require('net').createConnection({host : 'PUBLIC_IP_OF_SERVER', port : 4434}, function () {
    console.log('> connected to public server via local endpoint:', c.localAddress + ':' + c.localPort);

    // do not end the connection, keep it open to the public server
    // and start a tcp server listening on the ip/port used to connected to server.js
    var server = require('net').createServer(function (socket) {
        console.log('> (clientA) someone connected, it\s:', socket.remoteAddress, socket.remotePort);
        socket.write("Hello there NAT traversal man, this is a message from a client behind a NAT!");
    }).listen(c.localPort, c.localAddress, function (err) {
        if(err) return console.log(err);
        console.log('> (clientA) listening on:', c.localAddress + ':' + c.localPort);
    });
});

// clientB.js
// read the server's output to find the public endpoint of A:
var c = require('net').createConnection({host : 'PUBLIC_IP_OF_CLIENT_A', port : PUBLIC_PORT_OF_CLIENT_A},function () {
    console.log('> (clientB) connected to clientA!');

    c.on('data', function (data) {
        console.log(data.toString());
    });
});

For a more complete version with signalling happening on the server, I refer to my code here: https://github.com/SamDecrock/node-tcp-hole-punching

like image 158
Sam Avatar answered Sep 23 '22 19:09

Sam


The socket is assigned a local port. To reuse the same port you can connect to the client using the same socket that was used to communicate with the server. This works for you because you are doing TCP hole punching. However, you cannot choose a port yourself.

Here is some test code:

// server.js
require('net').createServer(function(c) {
    c.write(c.remotePort.toString(10));
}).listen(4434);


//client.js
var c = require('net').createConnection({host : '127.0.0.1', port : 4434});
c.on('data', function(data) {
    console.log(data.toString(), c.localPort);
    c.end();
});

c.on('end', function() {
    c.connect({host : '127.0.0.1', port : 4434});
});
like image 22
Friend of Kim Avatar answered Sep 21 '22 19:09

Friend of Kim