Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http.get option localAddress not working #3625

Closed
k1tzu opened this issue Nov 2, 2015 · 4 comments
Closed

http.get option localAddress not working #3625

k1tzu opened this issue Nov 2, 2015 · 4 comments
Labels
feature request Issues that request new features to be added to Node.js. http Issues or PRs related to the http subsystem. question Issues that look for answers.

Comments

@k1tzu
Copy link

k1tzu commented Nov 2, 2015

Hello! The binding to Local interface option is not working at all.
The case: Arch Linux system with two different ethernet interfaces.
eth0: 192.168.0.173/24
eth1: 192.168.0.182/24
routing table is
default via 192.168.0.1 dev eth0 metric 280
default via 192.168.0.1 dev eth1 metric 281

Every interface has a listening http server on other end with the address: 192.168.0.1
So I'm trying to send a request to each of them with nodejs.
Sample code:

var http = require('http');
var options = {};
options.port = 80;
options.host = "192.168.0.1";
options.path = '/index.html';
options.headers = {'Referer': 'http://' + options.host + ':' + options.port + '/index.html'};

options.localAddress = '192.168.0.182'; //NOT WORKING!! - REQUEST GOING FROM 192.168.0.173!

var creq = http.get(options);
creq.on('response', function (res) {
    res.on('data', function (chunk) {
        console.log(chunk);
    });
});

I've even tried creating a socket for that request with new net.Socket({ handle: net._createServerHandle( '192.168.0.182')}); and requests kept going from the wrong address.
There's workaround with 'curl --interface eth1 ...' that works, but it's not a good solution.

@mscdex mscdex added the http Issues or PRs related to the http subsystem. label Nov 2, 2015
@mscdex
Copy link
Contributor

mscdex commented Nov 2, 2015

IIRC routing on Linux uses the route with the lowest metric value when multiple routes match, which in this case would agree with what you're experiencing.

@k1tzu
Copy link
Author

k1tzu commented Nov 2, 2015

Of cause it uses the route with the lowest metric! But that's the question!
I need to send packets to both interfaces with nodejs.

In C language it is a simplest question to resolve with SO_BINDTODEVICE socket option. And I thought nodejs do this also in some intellectual way like

 //convert localAddress to the Interface name
#ifdef __linux__
     //SO_BINDTODEVICE to the interface
#elif __APPLE__
     //IP_BOUND_IF  to the interface
#endif

No problemo

@bnoordhuis
Copy link
Member

Perhaps it's stating the obvious but the .localAddress option binds to an address, not an interface. There is no direct support for SO_BINDTODEVICE in node.js but you can jury-rig it with socat(1):

# so-bindtodevice needs root
$ socat TCP4:<address>:80,so-bindtodevice=<dev> EXEC:'node script.js',nofork,fdin=3,fdout=3

With script.js looking something like this:

var socket = require('net').Socket({ fd: 3, readable: true, writable: true });
var req = require('http').get({
  createConnection: () => socket,
  headers: { Host: 'example.com' },
}, res => res.pipe(process.stdout));

@k1tzu
Copy link
Author

k1tzu commented Nov 2, 2015

I've found another workaround that works. Using ffi and call to setsockopt. But I wish I could do that with nodejs only.

var http = require('http'),
    net = require('net'),
    util = require('util'),
    ffi = require('ffi');

var SOL_SOCKET = 1;
var SO_BINDTODEVICE = 25;
var current = ffi.Library(null, {
    'setsockopt': ['int', ['int', 'int', 'int', 'string', 'int']]
});

var remoteIp = "192.168.0.1";
var ourIp = "192.168.0.182";
var ourInterface = "eth1";

function bindingAgent(options) {
    http.Agent.call(this, options);
    this.createConnection = bindingCreateConnection;
}

util.inherits(bindingAgent, http.Agent);

function bindingCreateConnection(port, host, options) {
    var socket;
    socket = new net.Socket({handle: net._createServerHandle(ourIp)});
    var iface = ourInterface;
    var r = current.setsockopt(socket._handle.fd, SOL_SOCKET, SO_BINDTODEVICE, iface, 6);
    if (r === -1)
        throw new Error("getsockopt(SO_BINDTODEVICE) error");

    socket.connect(port, host);

    return socket;
}

var optionsAgent = {};
var ourBindingAgent = new bindingAgent(optionsAgent);

var httpReq = {};
httpReq.port = 80;
httpReq.host = remoteIp;
httpReq.path = '/index.html';
httpReq.headers = {'Referer': 'http://' + remoteIp + ':' + httpReq.port + '/index.html'};
httpReq.agent = ourBindingAgent;

var body = '';
var creq = http.get(httpReq);
creq.on('response', function (res) {
    res.setEncoding('utf8');
    res.on('data', function (chunk) {
        console.log(chunk);
    });
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Issues that request new features to be added to Node.js. http Issues or PRs related to the http subsystem. question Issues that look for answers.
Projects
None yet
Development

No branches or pull requests

5 participants