working better

This commit is contained in:
AJ ONeal 2015-12-31 02:21:29 +00:00
parent d11a51ff90
commit bef1fbaba4
8 changed files with 214 additions and 83 deletions

View File

@ -47,15 +47,14 @@ holepunch --plain-ports 80,65080 --tls-ports 443,65443
### API
This is the current Dec 30th alpha api
This is the current Dec 30th api in master
```javascript
var punch = require('holepunch');
punch({
debug: true
, plainPorts: [{ internal: 80, external: 80 }]
, tlsPorts: [{ internal: 443, external: 443 }]
, mappings: [{ internal: 443, external: 443, secure: true }]
, ipifyUrls: ['api.ipify.org'],
, protocols: ['none', 'upnp', 'pmp']
, rvpnConfigs: []

View File

@ -12,7 +12,9 @@ cli.parse({
//, 'plain-ports': [ false, " Port numbers to test with plaintext loopback. (default: 65080) (formats: <port>,<internal:external>,<internal:external1|external2>)", 'string' ]
, 'tls-ports': [ false, " Port numbers to test with tls loopback. (default: null)", 'string' ]
, 'ipify-urls': [ false, " Comma separated list of URLs to test for external ip. (default: api.ipify.org)", 'string' ]
, 'protocols': [ false, " Comma separated list of ip mapping protocols. (default: none,upnp,pmp)", 'string' ]
, protocols: [ false, " Comma separated list of ip mapping protocols. (default: none,upnp,pmp)", 'string' ]
//, upnp: [ false, " Use nat-upnp. (default: true)", 'boolean' ]
//, pmp: [ false, " Use nat-pmp. (default: true)", 'boolean' ]
, 'rvpn-configs': [ false, " Comma separated list of Reverse VPN config files in the order they should be tried. (default: null)", 'string' ]
// TODO allow standalone, webroot, etc
});
@ -22,6 +24,11 @@ cli.main(function(_, options) {
console.log('');
var args = {};
var hp = require('../');
var loopback = require('../lib/loopback-listener');
var plainPorts = options['plain-ports'];
var tlsPorts = options['tls-ports'];
var pretest;
var protocols;
function parsePorts(portstr) {
var parts = portstr.split(':');
@ -45,9 +52,7 @@ cli.main(function(_, options) {
}
args.debug = options.debug;
args.plainPorts = options['plain-ports'];
args.tlsPorts = options['tls-ports'];
args.protocols = options.protocols;
protocols = options.protocols;
args.ipifyUrls = options['ipify-urls'];
args.rvpnConfigs = options['rvpn-configs'];
@ -56,21 +61,44 @@ cli.main(function(_, options) {
} else {
args.ipifyUrls = (args.ipifyUrls || 'api.ipify.org').split(',');
}
if ('false' === args.protocols || false === args.protocols) {
args.protocols = [];
if ('false' === protocols || false === protocols) {
protocols = [];
} else {
args.protocols = (args.protocols || 'none,upnp,pmp').split(',');
protocols = (protocols || 'none,upnp,pmp').split(',');
}
// Coerce to string. cli returns a number although we request a string.
args.tlsPorts = (args.tlsPorts || "").toString().split(',').filter(exists).map(parsePorts);
tlsPorts = (tlsPorts || "").toString().split(',').filter(exists).map(parsePorts);
args.rvpnConfigs = (args.rvpnConfigs || "").toString().split(',').filter(exists);
if ('false' === args.plainPorts || false === args.plainPorts) {
args.plainPorts = [];
if ('false' === plainPorts || false === plainPorts) {
plainPorts = [];
} else {
args.plainPorts = (args.plainPorts || "65080").toString().split(',').map(parsePorts);
plainPorts = (plainPorts || "65080").toString().split(',').map(parsePorts);
}
pretest = (-1 !== protocols.indexOf('none'));
args.upnp = options.upnp
|| (-1 !== protocols.indexOf('upnp')) || (-1 !== protocols.indexOf('ssdp'));
args.pmp = options.pmp
|| (-1 !== protocols.indexOf('pmp')) || (-1 !== protocols.indexOf('nat-pmp'));
args.mappings = plainPorts.map(function (info) {
info.secure = false;
info.loopback = pretest;
return info;
}).concat(tlsPorts.map(function (info) {
info.secure = true;
info.loopback = pretest;
return info;
}));
//var servers = loopback.create(args);
loopback.create(args);
if (args.debug) {
console.log('[HP] create servers');
//console.log(servers);
}
return hp.create(args).then(function () {
return hp(args).then(function () {
//console.log('[HP] wishing wanting waiting');
console.log('complete, exiting');
process.exit(0);

49
bin/service.js Normal file
View File

@ -0,0 +1,49 @@
'use strict';
var punch = require('../');
function touch() {
punch({
mappings: [
{ internal: 80
, external: 80
, secure: false
, loopback: false
}
, { internal: 65080
, external: 65080
, secure: false
, loopback: false
}
, { internal: 65443
, external: 65443
, secure: false
, loopback: false
}
, { internal: 443
, external: 443
, secure: false
, loopback: false
}
, { internal: 65022
, external: 65022
, secure: false
, loopback: false
}
, { internal: 22
, external: 22
, secure: false
, loopback: false
}
]
, upnp: true
, pmp: true
, debug: true
}).then(function (results) {
console.log('map results');
console.log(results);
});
}
setInterval(touch, 90 * 60 * 1000);
touch();

View File

@ -2,30 +2,10 @@
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
@ -71,12 +51,9 @@ module.exports = function (opts) {
if (opts.debug) {
console.log('[HP] external ip opts:');
console.log(opts);
console.log('[HP] external ifaces:');
console.log(ifaces);
}
ifaces.forEach(function (iface) {
opts.ifaces.forEach(function (iface) {
promises.push(requestAsync({
family: iface.family
, method: 'GET'

View File

@ -1,23 +1,35 @@
'use strict';
var PromiseA = require('bluebird');
var loopback = require('./loopback-listener');
var os = require('os');
module.exports.create = function (args) {
module.exports = function (args) {
if (args.debug) {
console.log('[HP] create holepuncher');
console.log(args);
}
var servers = loopback.create(args);
//var promises = [];
if (args.debug) {
console.log('[HP] create servers');
//console.log(servers);
}
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);
});
function getExternalIps() {
if (!args.ipifyUrls || !args.ipifyUrls.length) {
return PromiseA.resolve(args.ifaces.map(function (iface) {
return {
family: iface.family
//, address: addr
, address: iface.address // TODO check where this is used
, localAddress: iface.address
};
}));
}
return PromiseA.any(args.ipifyUrls.map(function (ipifyUrl) {
var getIp = require('./external-ip');
@ -87,29 +99,39 @@ module.exports.create = function (args) {
}
if (!opts.pretest) {
return ip;
return PromiseA.reject(new Error("[not an error]: skip the loopback test"));
}
return testOpenPort(ip, opts.portInfo);
}));
}
return getExternalIps().then(function (ips) {
var pretest = (-1 !== args.protocols.indexOf('none'));
var portInfos = args.plainPorts.map(function (info) {
info.secure = false;
return info;
}).concat(args.tlsPorts.map(function (info) {
info.secure = true;
return info;
}));
args.ifaces = ifacenames.reduce(function (all, ifacename) {
var ifs = interfaces[ifacename];
return PromiseA.all(portInfos.map(function (info) {
ifs.forEach(function (iface) {
if (!iface.internal && !/^fe80/.test(iface.address)) {
all.push(iface);
}
});
return all;
}, []);
if (args.debug) {
console.log('[HP] external ifaces:');
console.log(args.ifaces);
}
return getExternalIps().then(function (ips) {
var portInfos = args.mappings;
return PromiseA.all(portInfos.map(function (mapping) {
// TODO clone-merge args
return testPort({
portInfo: info
portInfo: mapping
, ips: ips
, pretest: pretest
, pretest: mapping.loopback
});
})).then(function (portInfos) {
if (args.debug) {
@ -119,27 +141,29 @@ module.exports.create = function (args) {
return portInfos;
}, function () {
// at least one port could not be mapped
var mappers = [];
var upnps = [];
var pmps = [];
var pu = PromiseA.resolve();
var pm = PromiseA.resolve();
if (-1 !== args.protocols.indexOf('upnp')
|| -1 !== args.protocols.indexOf('ssdp')
) {
if (args.upnp) {
if (args.debug) {
console.log('[HP] will try upnp');
}
mappers.push(require('./upnp'));
upnps.push(require('./upnp'));
}
if (-1 !== args.protocols.indexOf('pmp')
|| -1 !== args.protocols.indexOf('nat-pmp')
) {
if (args.pmp) {
if (args.debug) {
console.log('[HP] will try nat-pmp');
}
mappers.push(require('./pmp'));
pmps.push(require('./pmp'));
}
return PromiseA.all(portInfos.map(function (portInfo) {
/*
// TODO create single dgram listeners and serialize upnp requests
// because we can't have multiple requests bound to the same port, duh
return PromiseA.any(mappers.map(function (fn) {
var p = fn(args, ips, portInfo);
@ -149,6 +173,41 @@ module.exports.create = function (args) {
return p;
}));
*/
var good;
function nextu(fn) {
pu = pu.then(function () {
return fn(args, ips, portInfo);
}).then(function (results) {
good = results;
return null;
}, function (/*err*/) {
return null;
});
}
function nextm(fn) {
pm = pm.then(function () {
return fn(args, ips, portInfo);
}).then(function (results) {
good = results;
return null;
}, function (/*err*/) {
return null;
});
}
upnps.forEach(nextu);
pmps.forEach(nextm);
return PromiseA.any([pu, pm]).then(function () {
if (!good) {
return PromiseA.reject(new Error("no port map success"));
}
return null;
});
})).then(function () {
if (args.debug) {
console.log("[HP] all ports successfully mapped");

View File

@ -16,18 +16,20 @@ module.exports.create = function (opts) {
app.use('/', middleware(opts));
(opts.plainPorts||[]).forEach(function (plainPort) {
var plainServer = http.createServer();
plainServer.__plainPort = plainPort;
plainServer.on('request', app);
results.plainServers.push(plainServer);
});
opts.mappings.forEach(function (mapping) {
var server;
(opts.tlsPorts||[]).forEach(function (tlsPort) {
var tlsServer = https.createServer(httpsOptions);
tlsServer.__tlsPort = tlsPort;
tlsServer.on('request', app);
results.tlsServers.push(tlsServer);
if (false === mapping.secure) {
server = http.createServer();
server.__plainPort = mapping;
server.on('request', app);
results.plainServers.push(server);
} else {
server = https.createServer(httpsOptions);
server.__tlsPort = mapping;
server.on('request', app);
results.tlsServers.push(server);
}
});
function onListen() {

View File

@ -37,6 +37,9 @@ function pmpForwardHelper(gw, portInfo) {
return new PromiseA(function (resolve, reject) {
// create a "client" instance connecting to your local gateway
var client = natpmp.connect(gw);
client.on('error', function (err) {
reject(err);
});
function setPortForward() {
// setup a new port mapping
@ -60,6 +63,8 @@ function pmpForwardHelper(gw, portInfo) {
// public: 2222,
// ...
// }
client.close();
resolve();
});
}
@ -96,6 +101,7 @@ module.exports = function (args, ips, portInfo) {
module.exports.pmpForward = pmpForward;
/*
function usage() {
console.warn("");
console.warn("node helpers/pmp-forward [public port] [private port] [ttl]");
@ -121,3 +127,4 @@ function run() {
if (require.main === module) {
run();
}
*/

View File

@ -2,6 +2,7 @@
var PromiseA = require('bluebird').Promise;
var natUpnp = require('holepunch-upnp');
var client;
function upnpForward(opts) {
if (opts.debug) {
@ -9,7 +10,7 @@ function upnpForward(opts) {
console.log(opts);
}
return natUpnp.createClient({ timeout: 3 * 1000 }).then(function (client) {
function useClient(client) {
if (opts.debug) {
console.log('[HP] [upnp] created client');
console.log(client);
@ -39,7 +40,16 @@ function upnpForward(opts) {
return promitter;
});*/
}
if (client) {
return useClient(client);
} else {
return natUpnp.createClient({ timeout: 3 * 1000 }).then(function (_client) {
client = _client;
useClient(client);
});
}
}
module.exports = function (args, ips, portInfo) {