From 8e5accc6ce0a3fabea3ccf2bdcd9db94a1840746 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 30 Dec 2015 08:22:04 +0000 Subject: [PATCH] mostly works --- README.md | 4 + bin/holepunch.js | 5 +- helpers/pmp-forward.js | 81 ------------------ lib/index.js | 23 ++++- lib/pmp.js | 114 +++++++++++++++++++++++++ lib/request.js | 4 + helpers/upnp-forward.js => lib/upnp.js | 37 ++++++-- 7 files changed, 172 insertions(+), 96 deletions(-) delete mode 100644 helpers/pmp-forward.js create mode 100644 lib/pmp.js rename helpers/upnp-forward.js => lib/upnp.js (64%) diff --git a/README.md b/README.md index e78e231..8fcab35 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ to make home and office devices and services Internet-accessible. in development +```bash +node bin/holepunch.js --debug +``` + # License MPL-2.0 diff --git a/bin/holepunch.js b/bin/holepunch.js index 6cb6568..bf8d1de 100755 --- a/bin/holepunch.js +++ b/bin/holepunch.js @@ -8,7 +8,8 @@ var cli = require('cli'); // TODO txt records for browser plugin: TXT _http.example.com _https.example.com cli.parse({ debug: [ false, " show traces and logs", 'boolean', false ] -, 'plain-ports': [ false, " Port numbers to test with plaintext loopback. (default: 65080) (formats: ,,)", 'string' ] +, 'plain-ports': [ false, " Port numbers to test with plaintext loopback. (default: 65080) (formats: ,)", 'string' ] +//, 'plain-ports': [ false, " Port numbers to test with plaintext loopback. (default: 65080) (formats: ,,)", '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' ] @@ -28,7 +29,7 @@ cli.main(function(_, options) { internal: parseInt(parts[0], 10) , external: (parts[1]||parts[0]).split('|').map(function (port) { return parseInt(port, 10); - }) + })[0] }; return opts; diff --git a/helpers/pmp-forward.js b/helpers/pmp-forward.js deleted file mode 100644 index c105bf3..0000000 --- a/helpers/pmp-forward.js +++ /dev/null @@ -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(); -} diff --git a/lib/index.js b/lib/index.js index 4a84e50..cbcd439 100644 --- a/lib/index.js +++ b/lib/index.js @@ -43,6 +43,10 @@ module.exports.create = function (args) { Host: args.loopbackHostname } }).then(function (val) { + if (args.debug) { + console.log('[HP] loopback test reached', val); + } + if (val !== args.value) { return PromiseA.reject(new Error("invalid loopback token value")); } @@ -53,6 +57,13 @@ module.exports.create = function (args) { portInfo.ips.push(ip); 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) { ip.ports = []; + if (opts.debug) { + console.log('[HP] no pretest', opts.pretest); + } + if (!opts.pretest) { - //console.log('[HP pretest]', opts.pretest); return ip; } @@ -116,8 +130,8 @@ module.exports.create = function (args) { return PromiseA.any(mappers.map(function (fn) { var p = fn(args, ips, portInfo); - if (portInfos.ips.length) { - return portInfos; + if (portInfo.ips.length) { + return portInfo; } return p; @@ -132,8 +146,9 @@ module.exports.create = function (args) { }); }).then(function () { return portInfos; - }, function () { + }, function (err) { console.warn('[HP] RVPN not implemented'); + console.warn(err.stack); return portInfos; }); }); diff --git a/lib/pmp.js b/lib/pmp.js new file mode 100644 index 0000000..f86635e --- /dev/null +++ b/lib/pmp.js @@ -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(); +} diff --git a/lib/request.js b/lib/request.js index f65dff3..b07115b 100644 --- a/lib/request.js +++ b/lib/request.js @@ -8,6 +8,10 @@ function requestAsync(opts) { return new PromiseA(function (resolve, reject) { 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 data = ''; diff --git a/helpers/upnp-forward.js b/lib/upnp.js similarity index 64% rename from helpers/upnp-forward.js rename to lib/upnp.js index f42dfe0..c97f738 100644 --- a/helpers/upnp-forward.js +++ b/lib/upnp.js @@ -1,15 +1,22 @@ 'use strict'; -var PromiseA = require('bluebird').Promise; -var natUpnp = require('nat-upnp'); +//var PromiseA = require('bluebird').Promise; +var natUpnp = require('holepunch-upnp'); -exports.upnpForward = function (port) { - return natUpnp.createClient({ timeout: 1800 }).then(function (client) { +function upnpForward(opts) { + return natUpnp.createClient({ timeout: 3000 }).then(function (client) { return client.portMapping({ - public: port.public, - private: port.private || port.public, - ttl: port.ttl || 0 - })/*.then(function () { + public: opts.external || opts.public || opts.internal + , private: opts.internal || opts.private || opts.public || opts.external + , ttl: opts.ttl || 0 + }).then(function (result) { + if (opts.debug) { + console.log('[HP] upnp result'); + console.log(result); + } + return result; + }); + /*.then(function () { var promitter = client.getMappings(); promitter.on('entry', function (entry, i) { @@ -21,10 +28,22 @@ exports.upnpForward = function (port) { }); 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({ public: 80