begin cli

This commit is contained in:
AJ ONeal 2015-12-29 18:32:37 +00:00
parent 04e788ddb4
commit 1928278e3f
3 changed files with 214 additions and 0 deletions

94
bin/holepunch.js Normal file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env node
'use strict';
var cli = require('cli');
//var mkdirp = require('mkdirp');
// TODO link with RVPN service: server, email, domains, agree-tos
// 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: <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' ]
, '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
});
// ignore certonly and extraneous arguments
cli.main(function(_, options) {
console.log('');
var args = {};
//var hp = require('../');
function parsePorts(portstr) {
var parts = portstr.split(':');
var opts = {
internal: parseInt(parts[0], 10)
, external: (parts[1]||parts[0]).split('|').map(function (port) {
return parseInt(port, 10);
})
};
return opts;
}
function exists(x) {
return x;
}
console.log('options');
console.log(options);
args.debug = options.debug;
args.plainPorts = options['plain-ports'];
args.tlsPorts = options['tls-ports'];
args.protocols = options.protocols;
args.ipifyUrls = options['ipify-urls'];
args.rvpnConfigs = options['rvpn-configs'];
if ('false' === args.ipifyUrls || false === args.ipifyUrls) {
args.ipifyUrls = [];
} else {
args.ipifyUrls = (args.ipifyUrls || 'api.ipify.org').split(',');
}
if ('false' === args.protocols || false === args.protocols) {
args.protocols = [];
} else {
args.protocols = (args.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);
args.rvpnConfigs = (args.rvpnConfigs || "").toString().split(',').filter(exists);
if ('false' === args.plainPorts || false === args.plainPorts) {
args.plainPorts = [];
} else {
args.plainPorts = (args.plainPorts || "65080").toString().split(',').map(parsePorts);
}
console.log('args');
console.log(args);
/*
return hp.create({
debug: args.debug
, plainPorts: args.plainPorts
, tlsPorts: args.plainPorts
}).register(args, function (err, results) {
if (err) {
console.error('[Error]: letsencrypt-cli');
console.error(err.stack || err);
return;
}
// should get back account, path to certs, pems, etc?
console.log('\nCertificates installed at:');
console.log(Object.keys(results).filter(function (key) {
return /Path/.test(key);
}).map(function (key) {
return results[key];
}).join('\n'));
process.exit(0);
});
*/
});

60
lib/loopback-listener.js Normal file
View File

@ -0,0 +1,60 @@
'use strict';
var http = require('http');
var https = require('https');
var express = require('express');
var middleware = module.exports.middleware = require('./middleware');
module.exports.create = function (opts) {
var httpsOptions = opts.httpsOptions || require('localhost.daplie.com-certificates');
var results = {
plainServers: []
, tlsServers: []
};
var app = express();
app.use('/', middleware(opts));
(opts.plainPorts||[]).forEach(function (plainPort) {
var plainServer = http.createServer();
plainServer.__plainPort = plainPort;
http.on('request', app);
results.plainServers.push(plainServer);
});
(opts.tlsPorts||[]).forEach(function (tlsPort) {
var tlsServer = https.createServer(httpsOptions);
tlsServer.__tlsPort = tlsPort;
http.on('request', app);
results.tlsServers.push(tlsServer);
});
function onListen() {
/*jshint validthis: true*/
var server = this;
var addr = server.address();
var proto = 'honorCipherOrder' in server ? 'https' : 'http';
console.info('Listening on ' + proto + '://' + addr.address + ':' + addr.port);
}
process.nextTick(function () {
results.plainServers.forEach(function (plainServer) {
plainServer.listen(
plainServer.__plainPort.port
, plainServer.__plainPort.address || '0.0.0.0'
, onListen
);
});
results.tlsServers.forEach(function (tlsServer) {
tlsServer.listen(
tlsServer.__tlsPort.port
, tlsServer.__tlsPort.address || '0.0.0.0'
, onListen
);
});
});
return results;
};

60
lib/middleware.js Normal file
View File

@ -0,0 +1,60 @@
'use strict';
var scmp = require('scmp');
function middleware(opts) {
var key = opts.key;
var val = opts.value;
var vhost = opts.vhost;
var pathnamePrefix = opts.prefix || '/.well-known/com.daplie.loopback/';
var defaultHostname = 'loopback.daplie.invalid';
if (!key) {
opts.key = require('crypto').randomBytes(8).toString('hex');
}
if (!val) {
opts.value = require('crypto').randomBytes(16).toString('hex');
}
if (!vhost && vhost !== false) {
vhost = defaultHostname;
}
if ('/' !== pathnamePrefix[pathnamePrefix.length - 1]) {
pathnamePrefix += '/';
}
return function (req, res, next) {
var hostname = (req.hostname || req.headers.host || '').toLowerCase();
var urlpath = (req.pathname || req.url);
if (vhost !== false && vhost !== hostname) {
if (opts.debug) {
console.log("[HP] Host '" + hostname + "' failed to match '" + vhost + "'");
}
next();
return;
}
if (0 !== urlpath.indexOf(pathnamePrefix)) {
if (opts.debug) {
console.log("[HP] Pathname '" + urlpath + "'"
+ " failed to match '" + pathnamePrefix + "'");
}
next();
return;
}
if (scmp(key, urlpath.substr(pathnamePrefix.length, key.length))) {
if (opts.debug) {
console.log("[HP] Pathname '" + urlpath + "'"
+ " failed to match '" + pathnamePrefix + key + "'");
}
next();
return;
}
res.setHeader('Content-Type', 'text/plain');
res.end(val);
};
}
module.exports = middleware;