holepunch.js/lib/external-ip.js

113 lines
2.9 KiB
JavaScript

'use strict';
var PromiseA = require('bluebird');
//var dns = PromiseA.promisifyAll(require('dns'));
var os = require('os');
var requestAsync = require('./request');
module.exports = function (opts) {
var promises = [];
var interfaces = os.networkInterfaces();
var ifacenames = Object.keys(interfaces).filter(function (ifacename) {
// http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/
// https://wiki.archlinux.org/index.php/Network_configuration#Device_names
// we do not include tun and bridge devices because we're trying
// to see if any physical interface is internet-connected first
return /^(en|sl|wl|ww|eth|net|lan|wifi|inet)/.test(ifacename);
});
var ifaces = ifacenames.reduce(function (all, ifacename) {
var ifs = interfaces[ifacename];
ifs.forEach(function (iface) {
if (!iface.internal && !/^fe80/.test(iface.address)) {
all.push(iface);
}
});
return all;
}, []);
/*
// TODO how to support servername
promises.push(dns.resolve4Async(hostname).then(function (ips) {
return ips.map(function (ip) {
return {
address: ip
, family: 'IPv4'
};
});
}));
promises.push(dns.resolve6Async(hostname).then(function (ips) {
return ips.map(function (ip) {
return {
address: ip
, family: 'IPv6'
};
});
}));
*/
function parseIp(ip) {
if (!/\d+\.\d+\.\d+\.\d+/.test(ip) && !/\w+\:\w+/.test(ip)) {
return PromiseA.reject(new Error("bad response '" + ip + "'"));
}
return ip;
}
function ignoreEinval(err) {
if ('EINVAL' === err.code) {
if (opts.debug) {
console.warn('[HP] tried to bind to invalid address:');
console.warn(err.stack);
}
return null;
}
return PromiseA.reject(err);
}
if (opts.debug) {
console.log('[HP] external ip opts:');
console.log(opts);
console.log('[HP] external ifaces:');
console.log(ifaces);
}
ifaces.forEach(function (iface) {
promises.push(requestAsync({
family: iface.family
, method: 'GET'
, headers: {
Host: opts.hostname
}
, localAddress: iface.address
, servername: opts.hostname // is this actually sent to tls.connect()?
, hostname: opts.hostname // if so we could do the DNS ourselves
// and use the ip address here
, port: opts.port || 443
, pathname: opts.pathname || opts.path || '/'
}).then(parseIp, ignoreEinval).then(function (addr) {
return {
family: iface.family
, address: addr
, localAddress: iface.address
};
}));
});
return PromiseA.all(promises).then(function (results) {
if (opts.debug) {
console.log('[HP] got all ip address types');
console.log(results);
}
return results;
}, function (err) {
console.error('[HP] error');
console.error(err);
});
};