v2.0.0: add connection event, fix 0-byte data parser issue

This commit is contained in:
AJ ONeal 2018-08-07 03:44:15 -06:00
parent ff93145be2
commit ece89be3dd
3 changed files with 140 additions and 38 deletions

View File

@ -126,6 +126,7 @@ Packer.create = function (opts) {
machine.service = machine._headers[4];
machine.serviceport = machine._headers[5];
machine.name = machine._headers[6];
machine.servicename = machine._headers[7];
//console.log('machine.service', machine.service);
return true;
@ -167,10 +168,15 @@ Packer.create = function (opts) {
msg.name = machine.name;
msg.data = data;
if ('connection' === machine.service) {
msg.service = machine.servicename;
}
//console.log('msn', machine.service);
if (machine.emit) {
machine.emit(serviceEvents[msg.service] || serviceEvents.default);
machine.emit(serviceEvents[machine.service] || serviceEvents[msg.service] || serviceEvents.default);
} else {
(machine[serviceFuncs[msg.service]] || machine[serviceFuncs.default])(msg);
(machine[serviceFuncs[machine.service]] || machine[serviceFuncs[msg.service]] || machine[serviceFuncs.default])(msg);
}
return true;
@ -190,14 +196,19 @@ Packer.create = function (opts) {
machine.state %= machine.states.length;
}
}
if ('data' === machine.states[machine.state] && 0 === machine.bodyLen) {
machine.fns[machine.states[machine.state]](chunk)
machine.state += 1;
machine.state %= machine.states.length;
}
};
return machine;
};
Packer.packHeader = function (meta, data, service, andBody, oldways) {
if (oldways) {
data = data || Buffer.from(' ');
if (oldways && !data) {
data = Buffer.from(' ');
}
if (data && !Buffer.isBuffer(data)) {
data = new Buffer(JSON.stringify(data));
@ -206,7 +217,8 @@ Packer.packHeader = function (meta, data, service, andBody, oldways) {
data = Buffer.from(' ');
}
if (service && service !== 'control') {
if (service && -1 === ['control','connection'].indexOf(service)) {
//console.log('end?', service);
meta.service = service;
}
@ -217,6 +229,13 @@ Packer.packHeader = function (meta, data, service, andBody, oldways) {
if (service === 'control') {
header = Buffer.from(['', '', '', size, service].join(','));
}
else if (service === 'connection') {
header = Buffer.from([
meta.family, meta.address, meta.port, size,
'connection', (meta.serviceport || ''), (meta.name || ''),
(meta.service || '')
].join(','));
}
else {
header = Buffer.from([
meta.family, meta.address, meta.port, size,
@ -323,6 +342,7 @@ var addressNames = [
, 'localAddress'
, 'localPort'
];
/*
var sockFuncs = [
'address'
, 'destroy'
@ -333,6 +353,7 @@ var sockFuncs = [
, 'setNoDelay'
, 'setTimeout'
];
*/
// Unlike Packer.Stream.create this should handle all of the events needed to make everything work.
Packer.wrapSocket = function (socket) {
// node v10.2+ doesn't need a workaround for https://github.com/nodejs/node/issues/8854

View File

@ -1,6 +1,6 @@
{
"name": "proxy-packer",
"version": "1.5.0",
"version": "2.0.0",
"description": "A strategy for packing and unpacking a proxy stream (i.e. packets through a tunnel). Handles multiplexed and tls connections. Used by telebit and telebitd.",
"main": "index.js",
"scripts": {

View File

@ -3,48 +3,72 @@
var sni = require('sni');
var hello = require('fs').readFileSync(__dirname + '/sni.hello.bin');
var version = 1;
var address = {
family: 'IPv4'
, address: '127.0.1.1'
, port: 4321
, service: 'foo-https'
, serviceport: 443
, name: 'foo-pokemap.hellabit.com'
};
var header = address.family + ',' + address.address + ',' + address.port + ',' + hello.byteLength
+ ',' + (address.service || '') + ',' + (address.serviceport || '') + ',' + (address.name || '')
function getAddress() {
return {
family: 'IPv4'
, address: '127.0.1.1'
, port: 4321
, service: 'foo-https'
, serviceport: 443
, name: 'foo-pokemap.hellabit.com'
};
}
var addr = getAddress();
var connectionHeader = addr.family + ',' + addr.address + ',' + addr.port
+ ',0,connection,'
+ (addr.serviceport || '') + ',' + (addr.name || '') + ',' + (addr.service || '')
;
var header = addr.family + ',' + addr.address + ',' + addr.port
+ ',' + hello.byteLength + ',' + (addr.service || '') + ','
+ (addr.serviceport || '') + ',' + (addr.name || '')
;
var endHeader = addr.family + ',' + addr.address + ',' + addr.port
+ ',0,end,'
+ (addr.serviceport || '') + ',' + (addr.name || '')
;
var buf = Buffer.concat([
Buffer.from([ 255 - version, header.length ])
Buffer.from([ 255 - version, connectionHeader.length ])
, Buffer.from(connectionHeader)
, Buffer.from([ 255 - version, header.length ])
, Buffer.from(header)
, hello
, Buffer.from([ 255 - version, endHeader.length ])
, Buffer.from(endHeader)
]);
var services = { 'ssh': 22, 'http': 4080, 'https': 8443 };
var clients = {};
var count = 0;
var packer = require('../');
var machine = packer.create({
onmessage: function (tun) {
onconnection: function (tun) {
console.info('');
if (!tun.service || 'connection' === tun.service) {
throw new Error("missing service: " + JSON.stringify(tun));
}
console.info('[onConnection]');
count += 1;
}
, onmessage: function (tun) {
//console.log('onmessage', tun);
var id = tun.family + ',' + tun.address + ',' + tun.port;
var service = 'https';
var port = services[service];
var servername = sni(tun.data);
console.log('');
console.log('[onMessage]');
console.info('[onMessage]', service, port, servername, tun.data.byteLength);
if (!tun.data.equals(hello)) {
throw new Error("'data' packet is not equal to original 'hello' packet");
}
console.log('all', tun.data.byteLength, 'bytes are equal');
console.log('src:', tun.family, tun.address + ':' + tun.port + ':' + tun.serviceport);
console.log('dst:', 'IPv4 127.0.0.1:' + port);
//console.log('all', tun.data.byteLength, 'bytes are equal');
//console.log('src:', tun.family, tun.address + ':' + tun.port + ':' + tun.serviceport);
//console.log('dst:', 'IPv4 127.0.0.1:' + port);
if (!clients[id]) {
clients[id] = true;
if (!servername) {
throw new Error("no servername found for '" + id + "'");
}
console.log("servername: '" + servername + "'", tun.name);
//console.log("servername: '" + servername + "'", tun.name);
}
count += 1;
@ -53,36 +77,93 @@ var machine = packer.create({
throw new Error("Did not expect onerror");
}
, onend: function () {
throw new Error("Did not expect onend");
console.info('[onEnd]');
count += 1;
}
});
var packed = packer.pack(address, hello);
var packts, packed;
packts = [];
packts.push(packer.packHeader(getAddress(), null, 'connection'));
//packts.push(packer.pack(address, hello));
packts.push(packer.packHeader(getAddress(), hello));
packts.push(hello);
packts.push(packer.packHeader(getAddress(), null, 'end'));
packed = Buffer.concat(packts);
if (!packed.equals(buf)) {
console.error("");
console.error(buf.toString('hex') === packed.toString('hex'));
console.error("");
console.error("auto-packed:");
console.error(packed.toString('hex'), packed.byteLength);
console.error("");
console.error("hand-packed:");
console.error(buf.toString('hex'), buf.byteLength);
throw new Error("packer did not pack as expected");
console.error("");
throw new Error("packer (new) did not pack as expected");
}
packts = [];
packts.push(packer.pack(getAddress(), null, 'connection'));
packts.push(packer.pack(getAddress(), hello));
//packts.push(packer.packHeader(getAddress(), hello));
//packts.push(hello);
packts.push(packer.pack(getAddress(), null, 'end'));
packed = Buffer.concat(packts);
// XXX TODO REMOVE
//
// Nasty fix for short-term backwards-compat
//
// In the old way of doing things we always have at least one byte
// of data (due to a parser bug which has now been fixed) and so
// there are two strings padded with a space which gives the
// data a length of 1 rather than 0
//
// Here all four of those instances are replaced, but it requires
// maching a few things on either side.
//
// Only 6 bytes are changed - two 1 => 0, four ' ' => ''
var hex = packed.toString('hex')
//.replace(/2c313939/, '2c30')
.replace(/32312c312c636f/, '32312c302c636f')
.replace(/3332312c312c656e64/, '3332312c302c656e64')
.replace(/7320/, '73')
.replace(/20$/, '')
;
if (hex !== buf.toString('hex')) {
console.error("");
console.error(buf.toString('hex') === hex);
console.error("");
console.error("auto-packed:");
console.error(hex, packed.byteLength);
console.error("");
console.error("hand-packed:");
console.error(buf.toString('hex'), buf.byteLength);
console.error("");
throw new Error("packer (old) did not pack as expected");
}
console.log('');
console.info('');
// full message in one go
// 223 = 2 + 22 + 199
console.log('[WHOLE BUFFER]', 2, header.length, hello.length, buf.byteLength);
console.info('[WHOLE BUFFER]', 2, header.length, hello.length, buf.byteLength);
clients = {};
machine.fns.addChunk(buf);
console.log('');
console.info('');
// messages one byte at a time
console.log('[BYTE-BY-BYTE BUFFER]', 1);
console.info('[BYTE-BY-BYTE BUFFER]', 1);
clients = {};
buf.forEach(function (byte) {
machine.fns.addChunk(Buffer.from([ byte ]));
});
console.log('');
console.info('');
// split messages in overlapping thirds
@ -93,7 +174,7 @@ console.log('');
// 225-247 (22)
// 247-446 (199)
buf = Buffer.concat([ buf, buf ]);
console.log('[OVERLAPPING BUFFERS]', buf.length);
console.info('[OVERLAPPING BUFFERS]', buf.length);
clients = {};
[ buf.slice(0, 7) // version + header
, buf.slice(7, 14) // header
@ -106,12 +187,12 @@ clients = {};
].forEach(function (buf) {
machine.fns.addChunk(Buffer.from(buf));
});
console.log('');
console.info('');
process.on('exit', function () {
if (count !== 4) {
throw new Error("should have delivered 4 messages, not", count);
if (count !== 12) {
throw new Error("should have delivered 12 messages, not " + count);
}
console.log('TESTS PASS');
console.log('');
console.info('TESTS PASS');
console.info('');
});