mostly works
This commit is contained in:
parent
4bc1adf7a2
commit
8e5accc6ce
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
23
lib/index.js
23
lib/index.js
|
@ -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;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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 = '';
|
||||||
|
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue