mostly works

This commit is contained in:
AJ ONeal 2015-12-30 08:22:04 +00:00
parent 4bc1adf7a2
commit 8e5accc6ce
7 changed files with 172 additions and 96 deletions

View File

@ -8,6 +8,10 @@ to make home and office devices and services Internet-accessible.
in development in development
```bash
node bin/holepunch.js --debug
```
# License # License
MPL-2.0 MPL-2.0

View File

@ -8,7 +8,8 @@ var cli = require('cli');
// TODO txt records for browser plugin: TXT _http.example.com _https.example.com // TODO txt records for browser plugin: TXT _http.example.com _https.example.com
cli.parse({ cli.parse({
debug: [ false, " show traces and logs", 'boolean', false ] debug: [ false, " show traces and logs", 'boolean', false ]
, 'plain-ports': [ false, " Port numbers to test with plaintext loopback. (default: 65080) (formats: <port>,<internal:external>,<internal:external1|external2>)", 'string' ] , 'plain-ports': [ false, " Port numbers to test with plaintext loopback. (default: 65080) (formats: <port>,<internal:external>)", 'string' ]
//, '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' ] , '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' ] , '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' ]
@ -28,7 +29,7 @@ cli.main(function(_, options) {
internal: parseInt(parts[0], 10) internal: parseInt(parts[0], 10)
, external: (parts[1]||parts[0]).split('|').map(function (port) { , external: (parts[1]||parts[0]).split('|').map(function (port) {
return parseInt(port, 10); return parseInt(port, 10);
}) })[0]
}; };
return opts; return opts;

View File

@ -1,81 +0,0 @@
'use strict';
var PromiseA = require('bluebird').Promise;
var natpmp = require('nat-pmp');
var exec = require('child_process').exec;
exports.pmpForward = function (port) {
return new PromiseA(function (resolve, reject) {
exec('ip route show default', function (err, stdout, stderr) {
var gw;
if (err || stderr) { reject(err || stderr); return; }
// default via 192.168.1.1 dev eth0
gw = stdout.replace(/^default via (\d+\.\d+\.\d+\.\d+) dev[\s\S]+/m, '$1');
console.log('Possible PMP gateway is', gw);
// create a "client" instance connecting to your local gateway
var client = natpmp.connect(gw);
function setPortForward() {
// setup a new port mapping
client.portMapping({
private: port.private || port.public
, public: port.public || port.private
, ttl: port.ttl || 0 // 600
}, function (err, info) {
if (err) {
reject(err);
return;
}
console.log(info);
// {
// type: 'tcp',
// epoch: 8922109,
// private: 22,
// public: 2222,
// ...
// }
resolve();
});
}
// explicitly ask for the current external IP address
setTimeout(function () {
client.externalIp(function (err, info) {
if (err) throw err;
console.log('Current external IP address: %s', info.ip.join('.'));
setPortForward();
});
});
});
});
};
function usage() {
console.warn("");
console.warn("node helpers/pmp-forward [public port] [private port] [ttl]");
console.warn("");
}
function run() {
var pubPort = parseInt(process.argv[2], 10) || 0;
var privPort = parseInt(process.argv[3], 10) || pubPort;
var ttl = parseInt(process.argv[4], 10) || 0;
var options = { public: pubPort, private: privPort, ttl: ttl };
if (!pubPort) {
usage();
return;
}
exports.pmpForward(options).then(function () {
console.log('done');
});
}
if (require.main === module) {
run();
}

View File

@ -43,6 +43,10 @@ module.exports.create = function (args) {
Host: args.loopbackHostname Host: args.loopbackHostname
} }
}).then(function (val) { }).then(function (val) {
if (args.debug) {
console.log('[HP] loopback test reached', val);
}
if (val !== args.value) { if (val !== args.value) {
return PromiseA.reject(new Error("invalid loopback token value")); return PromiseA.reject(new Error("invalid loopback token value"));
} }
@ -53,6 +57,13 @@ module.exports.create = function (args) {
portInfo.ips.push(ip); portInfo.ips.push(ip);
return portInfo; return portInfo;
}, function (err) {
if (args.debug) {
console.error('[HP] loopback test err');
console.error(err.stack);
}
return PromiseA.reject(err);
}); });
} }
@ -67,8 +78,11 @@ module.exports.create = function (args) {
return PromiseA.any(opts.ips.map(function (ip) { return PromiseA.any(opts.ips.map(function (ip) {
ip.ports = []; ip.ports = [];
if (opts.debug) {
console.log('[HP] no pretest', opts.pretest);
}
if (!opts.pretest) { if (!opts.pretest) {
//console.log('[HP pretest]', opts.pretest);
return ip; return ip;
} }
@ -116,8 +130,8 @@ module.exports.create = function (args) {
return PromiseA.any(mappers.map(function (fn) { return PromiseA.any(mappers.map(function (fn) {
var p = fn(args, ips, portInfo); var p = fn(args, ips, portInfo);
if (portInfos.ips.length) { if (portInfo.ips.length) {
return portInfos; return portInfo;
} }
return p; return p;
@ -132,8 +146,9 @@ module.exports.create = function (args) {
}); });
}).then(function () { }).then(function () {
return portInfos; return portInfos;
}, function () { }, function (err) {
console.warn('[HP] RVPN not implemented'); console.warn('[HP] RVPN not implemented');
console.warn(err.stack);
return portInfos; return portInfos;
}); });
}); });

114
lib/pmp.js Normal file
View File

@ -0,0 +1,114 @@
'use strict';
var PromiseA = require('bluebird');
var natpmp = require('holepunch-nat-pmp');
function getGateway() {
var exec = require('child_process').exec;
var netroute;
var gw;
try {
netroute = require('netroute');
gw = netroute.getGateway();
} catch(e) {
}
if (gw) {
return PromiseA.resolve(gw);
}
return new PromiseA(function (resolve, reject) {
exec('ip route show default', function (err, stdout, stderr) {
var gw;
if (err || stderr) { reject(err || stderr); return; }
// default via 192.168.1.1 dev eth0
gw = stdout.replace(/^default via (\d+\.\d+\.\d+\.\d+) dev[\s\S]+/m, '$1');
console.log('Possible PMP gateway is', gw);
return gw;
});
});
}
function pmpForwardHelper(gw, portInfo) {
return new PromiseA(function (resolve, reject) {
// create a "client" instance connecting to your local gateway
var client = natpmp.connect(gw);
function setPortForward() {
// setup a new port mapping
client.portMapping({
private: portInfo.internal || portInfo.private
|| portInfo.external || portInfo.public
, public: portInfo.external || portInfo.public
|| portInfo.internal || portInfo.private
, ttl: portInfo.ttl || 0 // 600
}, function (err, info) {
if (err) {
reject(err);
return;
}
console.log(info);
// {
// type: 'tcp',
// epoch: 8922109,
// private: 22,
// public: 2222,
// ...
// }
resolve();
});
}
// explicitly ask for the current external IP address
setTimeout(function () {
client.externalIp(function (err, info) {
if (err) { throw err; }
console.log('Current external IP address: %s', info.ip.join('.'));
setPortForward();
});
});
});
}
function pmpForward(port) {
return getGateway().then(function (gw) {
return pmpForwardHelper(gw, port);
});
}
module.exports = function (args, ips, portInfo) {
return pmpForward(portInfo);
};
module.exports.pmpForward = pmpForward;
function usage() {
console.warn("");
console.warn("node helpers/pmp-forward [public port] [private port] [ttl]");
console.warn("");
}
function run() {
var pubPort = parseInt(process.argv[2], 10) || 0;
var privPort = parseInt(process.argv[3], 10) || pubPort;
var ttl = parseInt(process.argv[4], 10) || 0;
var options = { public: pubPort, private: privPort, ttl: ttl };
if (!pubPort) {
usage();
return;
}
exports.pmpForward(options).then(function () {
console.log('done');
});
}
if (require.main === module) {
run();
}

View File

@ -8,6 +8,10 @@ function requestAsync(opts) {
return new PromiseA(function (resolve, reject) { return new PromiseA(function (resolve, reject) {
var httpr = (false === opts.secure) ? http : https; var httpr = (false === opts.secure) ? http : https;
console.log('[HP] loopback test opts');
console.log(typeof opts.port, opts.port);
console.log(opts);
var req = httpr.request(opts, function (res) { var req = httpr.request(opts, function (res) {
var data = ''; var data = '';

View File

@ -1,15 +1,22 @@
'use strict'; 'use strict';
var PromiseA = require('bluebird').Promise; //var PromiseA = require('bluebird').Promise;
var natUpnp = require('nat-upnp'); var natUpnp = require('holepunch-upnp');
exports.upnpForward = function (port) { function upnpForward(opts) {
return natUpnp.createClient({ timeout: 1800 }).then(function (client) { return natUpnp.createClient({ timeout: 3000 }).then(function (client) {
return client.portMapping({ return client.portMapping({
public: port.public, public: opts.external || opts.public || opts.internal
private: port.private || port.public, , private: opts.internal || opts.private || opts.public || opts.external
ttl: port.ttl || 0 , ttl: opts.ttl || 0
})/*.then(function () { }).then(function (result) {
if (opts.debug) {
console.log('[HP] upnp result');
console.log(result);
}
return result;
});
/*.then(function () {
var promitter = client.getMappings(); var promitter = client.getMappings();
promitter.on('entry', function (entry, i) { promitter.on('entry', function (entry, i) {
@ -21,10 +28,22 @@ exports.upnpForward = function (port) {
}); });
return promitter; return promitter;
})*/; });*/
});
}
module.exports = function (args, ips, portInfo) {
// TODO ips.forEach
return upnpForward({
debug: args.debug
, private: portInfo.internal
, public: portInfo.external
// , localAddress: ip.localAddress
}); });
}; };
module.exports.upnpForward = upnpForward;
/* /*
client.portUnmapping({ client.portUnmapping({
public: 80 public: 80