tested working upnp and pmp
This commit is contained in:
parent
62509a4800
commit
6382701c91
66
README.md
66
README.md
|
@ -1,17 +1,79 @@
|
||||||
# holepunch
|
# holepunch
|
||||||
|
|
||||||
A node.js library and cli for using UPnP SSDP
|
A node.js library (api) and command (cli) for using UPnP SSDP
|
||||||
and ZeroConf (Bonjour) NAT-PMP
|
and ZeroConf (Bonjour) NAT-PMP
|
||||||
to make home and office devices and services Internet-accessible.
|
to make home and office devices and services Internet-accessible.
|
||||||
|
|
||||||
## Progress
|
## Progress
|
||||||
|
|
||||||
in development
|
it now works :-)
|
||||||
|
|
||||||
|
still in development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
git clone git@github.com:Daplie/holepunch.git
|
||||||
|
|
||||||
|
pushd holepunch
|
||||||
|
|
||||||
node bin/holepunch.js --debug
|
node bin/holepunch.js --debug
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
**Commandline Tool**
|
||||||
|
```bash
|
||||||
|
npm install --global holepunch
|
||||||
|
```
|
||||||
|
|
||||||
|
**node.js Library**
|
||||||
|
```
|
||||||
|
npm install --save holepunch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commandline (CLI)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
holepunch --help
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
holepunch --plain-ports 80,65080 --tls-ports 443,65443
|
||||||
|
```
|
||||||
|
|
||||||
|
TODO `--prebound-ports 22`
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
holepunch.js [OPTIONS] [ARGS]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--debug BOOLEAN show traces and logs
|
||||||
|
|
||||||
|
--plain-ports STRING Port numbers to test with plaintext loopback.
|
||||||
|
(default: 65080)
|
||||||
|
(formats: <port>,<internal:external>)
|
||||||
|
|
||||||
|
--tls-ports STRING Port numbers to test with tls loopback.
|
||||||
|
(default: null)
|
||||||
|
|
||||||
|
--ipify-urls STRING Comma separated list of URLs to test for external ip.
|
||||||
|
(default: api.ipify.org)
|
||||||
|
|
||||||
|
--protocols STRING Comma separated list of ip mapping protocols.
|
||||||
|
(default: none,upnp,pmp)
|
||||||
|
|
||||||
|
--rvpn-configs STRING Comma separated list of Reverse VPN config files in
|
||||||
|
the order they should be tried. (default: null)
|
||||||
|
|
||||||
|
-h, --help Display help and usage details
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var punch = require('holepunch');
|
||||||
|
```
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
MPL-2.0
|
MPL-2.0
|
||||||
|
|
|
@ -71,7 +71,8 @@ cli.main(function(_, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return hp.create(args).then(function () {
|
return hp.create(args).then(function () {
|
||||||
console.log('wishing wanting waiting');
|
//console.log('[HP] wishing wanting waiting');
|
||||||
//process.exit(0);
|
console.log('complete, exiting');
|
||||||
|
process.exit(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -41,7 +41,7 @@ function createSocket() {
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('message', function (chunk, info) {
|
socket.on('message', function (chunk, info) {
|
||||||
var message = chunk.toString();
|
var message = chunk.toString('utf8');
|
||||||
|
|
||||||
console.log('[MESSAGE RECEIVED]');
|
console.log('[MESSAGE RECEIVED]');
|
||||||
console.log(info);
|
console.log(info);
|
||||||
|
|
14
lib/index.js
14
lib/index.js
|
@ -14,7 +14,7 @@ module.exports.create = function (args) {
|
||||||
|
|
||||||
if (args.debug) {
|
if (args.debug) {
|
||||||
console.log('[HP] create servers');
|
console.log('[HP] create servers');
|
||||||
console.log(servers);
|
//console.log(servers);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExternalIps() {
|
function getExternalIps() {
|
||||||
|
@ -63,8 +63,8 @@ module.exports.create = function (args) {
|
||||||
return portInfo;
|
return portInfo;
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
if (args.debug) {
|
if (args.debug) {
|
||||||
console.error('[HP] loopback test err');
|
console.log('[HP] loopback did not complete');
|
||||||
console.error(err.stack);
|
console.log(err.stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PromiseA.reject(err);
|
return PromiseA.reject(err);
|
||||||
|
@ -83,7 +83,7 @@ module.exports.create = function (args) {
|
||||||
ip.ports = [];
|
ip.ports = [];
|
||||||
|
|
||||||
if (opts.debug) {
|
if (opts.debug) {
|
||||||
console.log('[HP] no pretest', opts.pretest);
|
console.log('[HP] pretest = ', opts.pretest);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opts.pretest) {
|
if (!opts.pretest) {
|
||||||
|
@ -124,12 +124,18 @@ module.exports.create = function (args) {
|
||||||
if (-1 !== args.protocols.indexOf('upnp')
|
if (-1 !== args.protocols.indexOf('upnp')
|
||||||
|| -1 !== args.protocols.indexOf('ssdp')
|
|| -1 !== args.protocols.indexOf('ssdp')
|
||||||
) {
|
) {
|
||||||
|
if (args.debug) {
|
||||||
|
console.log('[HP] will try upnp');
|
||||||
|
}
|
||||||
mappers.push(require('./upnp'));
|
mappers.push(require('./upnp'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-1 !== args.protocols.indexOf('pmp')
|
if (-1 !== args.protocols.indexOf('pmp')
|
||||||
|| -1 !== args.protocols.indexOf('nat-pmp')
|
|| -1 !== args.protocols.indexOf('nat-pmp')
|
||||||
) {
|
) {
|
||||||
|
if (args.debug) {
|
||||||
|
console.log('[HP] will try nat-pmp');
|
||||||
|
}
|
||||||
mappers.push(require('./pmp'));
|
mappers.push(require('./pmp'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
lib/pmp.js
16
lib/pmp.js
|
@ -68,9 +68,11 @@ function pmpForwardHelper(gw, portInfo) {
|
||||||
// TODO why did I use a setTimeout here? event loop / timing bug?
|
// TODO why did I use a setTimeout here? event loop / timing bug?
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
client.externalIp(function (err, info) {
|
client.externalIp(function (err, info) {
|
||||||
console.error('[HP] Error: setTimeout client.externalIp:');
|
if (err) {
|
||||||
console.error(err.stack);
|
console.error('[HP] Error: setTimeout client.externalIp:');
|
||||||
if (err) { return PromiseA.reject(err); }
|
console.error(err.stack);
|
||||||
|
return PromiseA.reject(err);
|
||||||
|
}
|
||||||
console.log('Current external IP address: %s', info.ip.join('.'));
|
console.log('Current external IP address: %s', info.ip.join('.'));
|
||||||
setPortForward();
|
setPortForward();
|
||||||
});
|
});
|
||||||
|
@ -78,13 +80,17 @@ function pmpForwardHelper(gw, portInfo) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function pmpForward(port) {
|
function pmpForward(portInfo) {
|
||||||
return getGateway().then(function (gw) {
|
return getGateway().then(function (gw) {
|
||||||
return pmpForwardHelper(gw, port);
|
return pmpForwardHelper(gw, portInfo);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function (args, ips, portInfo) {
|
module.exports = function (args, ips, portInfo) {
|
||||||
|
if (args.debug) {
|
||||||
|
console.log('[HP] [pmp] portInfo');
|
||||||
|
console.log(portInfo);
|
||||||
|
}
|
||||||
return pmpForward(portInfo);
|
return pmpForward(portInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,8 +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');
|
if (opts.debug) {
|
||||||
console.log(opts);
|
console.log('[HP] requestAsync opts');
|
||||||
|
console.log(opts);
|
||||||
|
}
|
||||||
|
|
||||||
var req = httpr.request(opts, function (res) {
|
var req = httpr.request(opts, function (res) {
|
||||||
var data = '';
|
var data = '';
|
||||||
|
@ -22,10 +24,13 @@ function requestAsync(opts) {
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
res.on('data', function (chunk) {
|
res.on('data', function (chunk) {
|
||||||
|
clearTimeout(req.__timtok);
|
||||||
|
|
||||||
if (opts.debug > 2) {
|
if (opts.debug > 2) {
|
||||||
console.log('HP: request chunk:');
|
console.log('HP: request chunk:');
|
||||||
console.log(chunk);
|
console.log(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
data += chunk.toString('utf8');
|
data += chunk.toString('utf8');
|
||||||
});
|
});
|
||||||
res.on('end', function () {
|
res.on('end', function () {
|
||||||
|
@ -38,6 +43,13 @@ function requestAsync(opts) {
|
||||||
});
|
});
|
||||||
|
|
||||||
req.on('error', reject);
|
req.on('error', reject);
|
||||||
|
req.setTimeout(3 * 1000);
|
||||||
|
req.on('socket', function (socket) {
|
||||||
|
req.__timtok = setTimeout(function () {
|
||||||
|
req.abort();
|
||||||
|
}, 3 * 1000);
|
||||||
|
socket.setTimeout(3 * 1000);
|
||||||
|
});
|
||||||
req.end();
|
req.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
18
lib/upnp.js
18
lib/upnp.js
|
@ -1,17 +1,27 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
//var PromiseA = require('bluebird').Promise;
|
var PromiseA = require('bluebird').Promise;
|
||||||
var natUpnp = require('holepunch-upnp');
|
var natUpnp = require('holepunch-upnp');
|
||||||
|
|
||||||
function upnpForward(opts) {
|
function upnpForward(opts) {
|
||||||
return natUpnp.createClient({ timeout: 3000 }).then(function (client) {
|
if (opts.debug) {
|
||||||
|
console.log('[HP] [upnp] opts');
|
||||||
|
console.log(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return natUpnp.createClient({ timeout: 3 * 1000 }).then(function (client) {
|
||||||
|
if (opts.debug) {
|
||||||
|
console.log('[HP] [upnp] created client');
|
||||||
|
console.log(client);
|
||||||
|
}
|
||||||
|
|
||||||
return client.portMapping({
|
return client.portMapping({
|
||||||
public: opts.external || opts.public || opts.internal
|
public: opts.external || opts.public || opts.internal
|
||||||
, private: opts.internal || opts.private || opts.public || opts.external
|
, private: opts.internal || opts.private || opts.public || opts.external
|
||||||
, ttl: opts.ttl || 0
|
, ttl: opts.ttl || 0
|
||||||
}).then(function (result) {
|
}).then(function (result) {
|
||||||
if (opts.debug) {
|
if (opts.debug) {
|
||||||
console.log('[HP] upnp result');
|
console.log('[HP] [upnp] result');
|
||||||
console.log(result);
|
console.log(result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -37,7 +47,9 @@ module.exports = function (args, ips, portInfo) {
|
||||||
return upnpForward({
|
return upnpForward({
|
||||||
debug: args.debug
|
debug: args.debug
|
||||||
, private: portInfo.internal
|
, private: portInfo.internal
|
||||||
|
, internal: portInfo.internal
|
||||||
, public: portInfo.external
|
, public: portInfo.external
|
||||||
|
, external: portInfo.external
|
||||||
// , localAddress: ip.localAddress
|
// , localAddress: ip.localAddress
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "holepunch",
|
"name": "holepunch",
|
||||||
"version": "1.0.0-alpha.1",
|
"version": "1.0.0-alpha.2",
|
||||||
"description": "Get a direct ip connection by any means possible - direct (public ip), upnp (Microsoft), nat-pmp (Apple), or punch a hole through a firewall using a Reverse VPN (Daplie).",
|
"description": "Get a direct ip connection by any means possible - direct (public ip), upnp (Microsoft), nat-pmp (Apple), or punch a hole through a firewall using a Reverse VPN (Daplie).",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"holepunch": "holepunch.js"
|
"holepunch": "bin/holepunch.js"
|
||||||
},
|
},
|
||||||
"directories": {
|
"directories": {
|
||||||
"example": "examples"
|
"example": "examples"
|
||||||
|
@ -16,10 +16,11 @@
|
||||||
"holepunch-nat-pmp": "^1.0.0-alpha.1",
|
"holepunch-nat-pmp": "^1.0.0-alpha.1",
|
||||||
"holepunch-upnp": "^1.0.0-alpha.1",
|
"holepunch-upnp": "^1.0.0-alpha.1",
|
||||||
"localhost.daplie.com-certificates": "^1.1.2",
|
"localhost.daplie.com-certificates": "^1.1.2",
|
||||||
"netroute": "^1.0.2",
|
|
||||||
"request": "^2.67.0",
|
|
||||||
"scmp": "^1.0.0"
|
"scmp": "^1.0.0"
|
||||||
},
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"netroute": "^1.0.2"
|
||||||
|
},
|
||||||
"devDependencies": {},
|
"devDependencies": {},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
|
Loading…
Reference in New Issue