update docs, refactor
This commit is contained in:
parent
a13a4e62b2
commit
08051e3401
92
README.md
92
README.md
|
@ -3,6 +3,9 @@
|
||||||
A client that works in combination with [stunneld.js](https://github.com/Daplie/node-tunnel-server)
|
A client that works in combination with [stunneld.js](https://github.com/Daplie/node-tunnel-server)
|
||||||
to allow you to serve http and https from any computer, anywhere through a secure tunnel.
|
to allow you to serve http and https from any computer, anywhere through a secure tunnel.
|
||||||
|
|
||||||
|
* CLI
|
||||||
|
* Library
|
||||||
|
|
||||||
CLI
|
CLI
|
||||||
===
|
===
|
||||||
|
|
||||||
|
@ -43,3 +46,92 @@ Examples of good: education, business, pleasure. Examples of evil: crime, abuse,
|
||||||
```bash
|
```bash
|
||||||
stunnel.js --agree-tos --email john@example.com --locals http:john.example.com:4080,https:john.example.com:8443
|
stunnel.js --agree-tos --email john@example.com --locals http:john.example.com:4080,https:john.example.com:8443
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Library
|
||||||
|
=======
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var stunnel = require('stunnel');
|
||||||
|
|
||||||
|
stunnel.connect({
|
||||||
|
stunneld: 'wss://tunnel.example.com'
|
||||||
|
, token: '...'
|
||||||
|
, locals: [
|
||||||
|
// defaults to sending http to local port 80 and https to local port 443
|
||||||
|
{ hostname: 'doe.net' }
|
||||||
|
|
||||||
|
// sends both http and https to local port 3000 (httpolyglot)
|
||||||
|
, { protocol: 'https', hostname: 'john.doe.net', port: 3000 }
|
||||||
|
|
||||||
|
// send http to local port 4080 and https to local port 8443
|
||||||
|
, { protocol: 'https', hostname: 'jane.doe.net', port: 4080 }
|
||||||
|
, { protocol: 'https', hostname: 'jane.doe.net', port: 8443 }
|
||||||
|
]
|
||||||
|
|
||||||
|
, net: require('net')
|
||||||
|
, insecure: false
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
* You can get sneaky with `net` and provide a `createConnection` that returns a `streams.Duplex`.
|
||||||
|
|
||||||
|
### Token
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var tokenData = { domains: [ 'doe.net', 'john.doe.net', 'jane.doe.net' ] }
|
||||||
|
var secret = 'shhhhh';
|
||||||
|
var token = jwt.sign(tokenData, secret);
|
||||||
|
```
|
||||||
|
|
||||||
|
### net
|
||||||
|
|
||||||
|
Let's say you want to handle http requests in-process
|
||||||
|
or decrypt https before passing it to the local http handler.
|
||||||
|
You could do a little magic like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
stunnel.connect({
|
||||||
|
// ...
|
||||||
|
, net: {
|
||||||
|
createConnection: function (info, cb) {
|
||||||
|
// data is the hello packet / first chunk
|
||||||
|
// info = { data, servername, port, host, remoteAddress: { family, address, port } }
|
||||||
|
// socket = { write, push, end, events: [ 'readable', 'data', 'error', 'end' ] };
|
||||||
|
|
||||||
|
var Dup = {
|
||||||
|
write: function (chunk, encoding, cb) {
|
||||||
|
this.__my_socket.write(chunk, encoding);
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
, read: function (size) {
|
||||||
|
var x = this.__my_socket.read(size);
|
||||||
|
if (x) {
|
||||||
|
console.log('_read', size);
|
||||||
|
this.push(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var myDuplex = new (require('streams').Duplex);
|
||||||
|
|
||||||
|
myDuplex.__my_socket = socket;
|
||||||
|
myDuplex._write = Dup.write;
|
||||||
|
myDuplex._read = Dup.read;
|
||||||
|
|
||||||
|
myDuplex.remoteFamily = info.remoteFamily;
|
||||||
|
myDuplex.remoteAddress = info.remoteAddress;
|
||||||
|
myDuplex.remotePort = info.remotePort;
|
||||||
|
|
||||||
|
// socket.local{Family,Address,Port}
|
||||||
|
myDuplex.localFamily = 'IPv4';
|
||||||
|
myDuplex.localAddress = '127.0.01';
|
||||||
|
myDuplex.localPort = info.port;
|
||||||
|
|
||||||
|
httpsServer.emit('connection', myDuplex);
|
||||||
|
|
||||||
|
return myDuplex;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
100
bin/stunnel.js
100
bin/stunnel.js
|
@ -8,38 +8,43 @@ var program = require('commander');
|
||||||
var url = require('url');
|
var url = require('url');
|
||||||
var stunnel = require('../wsclient.js');
|
var stunnel = require('../wsclient.js');
|
||||||
|
|
||||||
|
function parseProxy(location) {
|
||||||
|
// http:john.example.com:3000
|
||||||
|
// http://john.example.com:3000
|
||||||
|
var parts = location.split(':');
|
||||||
|
//var dual = false;
|
||||||
|
if (/\./.test(parts[0])) {
|
||||||
|
//dual = true;
|
||||||
|
parts[2] = parts[1];
|
||||||
|
parts[1] = parts[0];
|
||||||
|
parts[0] = 'https';
|
||||||
|
}
|
||||||
|
parts[0] = parts[0].toLowerCase();
|
||||||
|
parts[1] = parts[1].toLowerCase().replace(/(\/\/)?/, '') || '*';
|
||||||
|
parts[2] = parseInt(parts[2], 10) || 0;
|
||||||
|
if (!parts[2]) {
|
||||||
|
// TODO grab OS list of standard ports?
|
||||||
|
if ('http' === parts[0]) {
|
||||||
|
parts[2] = 80;
|
||||||
|
}
|
||||||
|
else if ('https' === parts[0]) {
|
||||||
|
parts[2] = 443;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("port must be specified - ex: tls:*:1337");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
protocol: parts[0]
|
||||||
|
, hostname: parts[1]
|
||||||
|
, port: parts[2]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function collectProxies(val, memo) {
|
function collectProxies(val, memo) {
|
||||||
var vals = val.split(/,/g);
|
var vals = val.split(/,/g);
|
||||||
vals.map(function (location) {
|
vals.map(parseProxy).forEach(function (val) {
|
||||||
// http:john.example.com:3000
|
|
||||||
// http://john.example.com:3000
|
|
||||||
var parts = location.split(':');
|
|
||||||
if (!parts[1]) {
|
|
||||||
parts[1] = parts[0];
|
|
||||||
parts[0] = 'https';
|
|
||||||
}
|
|
||||||
parts[0] = parts[0].toLowerCase();
|
|
||||||
parts[1] = parts[1].toLowerCase().replace(/(\/\/)?/, '') || '*';
|
|
||||||
parts[2] = parseInt(parts[2], 10) || 0;
|
|
||||||
if (!parts[2]) {
|
|
||||||
// TODO grab OS list of standard ports?
|
|
||||||
if ('http' === parts[0]) {
|
|
||||||
parts[2] = 80;
|
|
||||||
}
|
|
||||||
else if ('https' === parts[0]) {
|
|
||||||
parts[2] = 443;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error("port must be specified - ex: tls:*:1337");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
protocol: parts[0]
|
|
||||||
, hostname: parts[1]
|
|
||||||
, port: parts[2]
|
|
||||||
};
|
|
||||||
}).forEach(function (val) {
|
|
||||||
memo.push(val);
|
memo.push(val);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -83,45 +88,12 @@ program.locals.forEach(function (proxy) {
|
||||||
tokenData.domains = Object.keys(domainsMap);
|
tokenData.domains = Object.keys(domainsMap);
|
||||||
tokenData.name = tokenData.domains[0];
|
tokenData.name = tokenData.domains[0];
|
||||||
|
|
||||||
program.services = {};
|
|
||||||
program.locals.forEach(function (proxy) {
|
|
||||||
//program.services = { 'ssh': 22, 'http': 80, 'https': 443 };
|
|
||||||
program.services[proxy.protocol] = proxy.port;
|
|
||||||
});
|
|
||||||
program.token = program.token || jwt.sign(tokenData, program.secret || 'shhhhh');
|
program.token = program.token || jwt.sign(tokenData, program.secret || 'shhhhh');
|
||||||
|
|
||||||
program.net = {
|
program.net = {
|
||||||
createConnection: function (info, cb) {
|
createConnection: function (info, cb) {
|
||||||
/*
|
|
||||||
var Dup = {
|
|
||||||
write: function (chunk, encoding, cb) {
|
|
||||||
//console.log('_write', chunk.byteLength);
|
|
||||||
this.__my_socket.write(chunk, encoding);
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
, read: function (size) {
|
|
||||||
//console.log('_read');
|
|
||||||
var x = this.__my_socket.read(size);
|
|
||||||
if (x) {
|
|
||||||
console.log('_read', size);
|
|
||||||
this.push(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var myDuplex = new (require('streams').Duplex);
|
|
||||||
myDuplex._write = Dup.write;
|
|
||||||
myDuplex._read = Dup.read;
|
|
||||||
myDuplex.remoteFamily = socket.remoteFamily;
|
|
||||||
myDuplex.remoteAddress = socket.remoteAddress;
|
|
||||||
myDuplex.remotePort = socket.remotePort;
|
|
||||||
myDuplex.localFamily = socket.localFamily;
|
|
||||||
myDuplex.localAddress = socket.localAddress;
|
|
||||||
myDuplex.localPort = socket.localPort;
|
|
||||||
httpsServer.emit('connection', myDuplex);
|
|
||||||
*/
|
|
||||||
|
|
||||||
// data is the hello packet / first chunk
|
// data is the hello packet / first chunk
|
||||||
// info = { data, servername, port, host, remoteAddress: { family, address, port } }
|
// info = { data, servername, port, host, remoteFamily, remoteAddress, remotePort }
|
||||||
var net = require('net');
|
var net = require('net');
|
||||||
// socket = { write, push, end, events: [ 'readable', 'data', 'error', 'end' ] };
|
// socket = { write, push, end, events: [ 'readable', 'data', 'error', 'end' ] };
|
||||||
var socket = net.createConnection({ port: info.port, host: info.host }, cb);
|
var socket = net.createConnection({ port: info.port, host: info.host }, cb);
|
||||||
|
|
14
wsclient.js
14
wsclient.js
|
@ -17,6 +17,12 @@ return;
|
||||||
//*/
|
//*/
|
||||||
|
|
||||||
function run(copts) {
|
function run(copts) {
|
||||||
|
copts.services = {};
|
||||||
|
copts.locals.forEach(function (proxy) {
|
||||||
|
//program.services = { 'ssh': 22, 'http': 80, 'https': 443 };
|
||||||
|
copts.services[proxy.protocol] = proxy.port;
|
||||||
|
});
|
||||||
|
|
||||||
var services = copts.services; // TODO pair with hostname / sni
|
var services = copts.services; // TODO pair with hostname / sni
|
||||||
var token = copts.token;
|
var token = copts.token;
|
||||||
var tunnelUrl = copts.stunneld.replace(/\/$/, '') + '/?access_token=' + token;
|
var tunnelUrl = copts.stunneld.replace(/\/$/, '') + '/?access_token=' + token;
|
||||||
|
@ -71,11 +77,9 @@ function run(copts) {
|
||||||
, port: port
|
, port: port
|
||||||
, host: '127.0.0.1'
|
, host: '127.0.0.1'
|
||||||
, data: opts.data
|
, data: opts.data
|
||||||
, remoteAddress: {
|
, remoteFamily: opts.family
|
||||||
family: opts.family
|
, remoteAddress: opts.address
|
||||||
, address: opts.address
|
, remotePort: opts.port
|
||||||
, port: opts.port
|
|
||||||
}
|
|
||||||
}, function () {
|
}, function () {
|
||||||
//console.log("[=>] first packet from tunneler to '" + cid + "' as '" + opts.service + "'", opts.data.byteLength);
|
//console.log("[=>] first packet from tunneler to '" + cid + "' as '" + opts.service + "'", opts.data.byteLength);
|
||||||
localclients[cid].write(opts.data);
|
localclients[cid].write(opts.data);
|
||||||
|
|
Loading…
Reference in New Issue