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)
|
||||
to allow you to serve http and https from any computer, anywhere through a secure tunnel.
|
||||
|
||||
* CLI
|
||||
* Library
|
||||
|
||||
CLI
|
||||
===
|
||||
|
||||
|
@ -43,3 +46,92 @@ Examples of good: education, business, pleasure. Examples of evil: crime, abuse,
|
|||
```bash
|
||||
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 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) {
|
||||
var vals = val.split(/,/g);
|
||||
vals.map(function (location) {
|
||||
// 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) {
|
||||
vals.map(parseProxy).forEach(function (val) {
|
||||
memo.push(val);
|
||||
});
|
||||
|
||||
|
@ -83,45 +88,12 @@ program.locals.forEach(function (proxy) {
|
|||
tokenData.domains = Object.keys(domainsMap);
|
||||
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.net = {
|
||||
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
|
||||
// info = { data, servername, port, host, remoteAddress: { family, address, port } }
|
||||
// info = { data, servername, port, host, remoteFamily, remoteAddress, remotePort }
|
||||
var net = require('net');
|
||||
// socket = { write, push, end, events: [ 'readable', 'data', 'error', 'end' ] };
|
||||
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) {
|
||||
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 token = copts.token;
|
||||
var tunnelUrl = copts.stunneld.replace(/\/$/, '') + '/?access_token=' + token;
|
||||
|
@ -71,11 +77,9 @@ function run(copts) {
|
|||
, port: port
|
||||
, host: '127.0.0.1'
|
||||
, data: opts.data
|
||||
, remoteAddress: {
|
||||
family: opts.family
|
||||
, address: opts.address
|
||||
, port: opts.port
|
||||
}
|
||||
, remoteFamily: opts.family
|
||||
, remoteAddress: opts.address
|
||||
, remotePort: opts.port
|
||||
}, function () {
|
||||
//console.log("[=>] first packet from tunneler to '" + cid + "' as '" + opts.service + "'", opts.data.byteLength);
|
||||
localclients[cid].write(opts.data);
|
||||
|
|
Loading…
Reference in New Issue