v2.0.4: make prettier
This commit is contained in:
parent
e699c44480
commit
2b763f8606
125
README.md
125
README.md
|
@ -17,8 +17,7 @@ Browser <--/ \--> Device
|
||||||
|
|
||||||
It's the kind of thing you'd use to build a poor man's VPN, or port-forward router.
|
It's the kind of thing you'd use to build a poor man's VPN, or port-forward router.
|
||||||
|
|
||||||
The M-PROXY Protocol
|
# The M-PROXY Protocol
|
||||||
===================
|
|
||||||
|
|
||||||
This is similar to "The PROXY Protocol" (a la HAProxy), but desgined for multiplexed tls, http, tcp, and udp
|
This is similar to "The PROXY Protocol" (a la HAProxy), but desgined for multiplexed tls, http, tcp, and udp
|
||||||
tunneled over arbitrary streams (such as WebSockets).
|
tunneled over arbitrary streams (such as WebSockets).
|
||||||
|
@ -60,8 +59,7 @@ service port (string) The listening port, such as 443. Useful for no
|
||||||
host or server name (string) Useful for services that can be routed by name, such as http, https, smtp, and dns.
|
host or server name (string) Useful for services that can be routed by name, such as http, https, smtp, and dns.
|
||||||
```
|
```
|
||||||
|
|
||||||
Tunneled TCP SNI Packet
|
## Tunneled TCP SNI Packet
|
||||||
-----------------------
|
|
||||||
|
|
||||||
You should see that the result is simply all of the original packet with a leading header.
|
You should see that the result is simply all of the original packet with a leading header.
|
||||||
|
|
||||||
|
@ -91,15 +89,13 @@ Note that `16 03 01 00` starts at the 29th byte (at index 28 or 0x1C) instead of
|
||||||
The v1 header uses strings for address and service descriptor information,
|
The v1 header uses strings for address and service descriptor information,
|
||||||
but future versions may be binary.
|
but future versions may be binary.
|
||||||
|
|
||||||
API
|
# API
|
||||||
===
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var Packer = require('proxy-packer');
|
var Packer = require('proxy-packer');
|
||||||
```
|
```
|
||||||
|
|
||||||
Unpacker / Parser State Machine
|
## Unpacker / Parser State Machine
|
||||||
-----------------------
|
|
||||||
|
|
||||||
The unpacker creates a state machine.
|
The unpacker creates a state machine.
|
||||||
|
|
||||||
|
@ -108,52 +104,52 @@ composing a full message with header and data (unless data length is 0).
|
||||||
|
|
||||||
The state machine progresses through these states:
|
The state machine progresses through these states:
|
||||||
|
|
||||||
* version
|
- version
|
||||||
* headerLength
|
- headerLength
|
||||||
* header
|
- header
|
||||||
* data
|
- data
|
||||||
|
|
||||||
At the end of the data event (which may or may not contain a buffer of data)
|
At the end of the data event (which may or may not contain a buffer of data)
|
||||||
one of the appropriate handlers will be called.
|
one of the appropriate handlers will be called.
|
||||||
|
|
||||||
* control
|
- control
|
||||||
* connection
|
- connection
|
||||||
* message
|
- message
|
||||||
* pause
|
- pause
|
||||||
* resume
|
- resume
|
||||||
* end
|
- end
|
||||||
* error
|
- error
|
||||||
|
|
||||||
```js
|
```js
|
||||||
unpacker = Packer.create(handlers); // Create a state machine for unpacking
|
unpacker = Packer.create(handlers); // Create a state machine for unpacking
|
||||||
|
|
||||||
unpacker.fns.addData(chunk); // process a chunk of data
|
unpacker.fns.addData(chunk); // process a chunk of data
|
||||||
|
|
||||||
handlers.oncontrol = function (tun) { } // for communicating with the proxy
|
handlers.oncontrol = function(tun) {}; // for communicating with the proxy
|
||||||
// tun.data is an array
|
// tun.data is an array
|
||||||
// '[ -1, "[Error] bad hello" ]'
|
// '[ -1, "[Error] bad hello" ]'
|
||||||
// '[ 0, "[Error] out-of-band error message" ]'
|
// '[ 0, "[Error] out-of-band error message" ]'
|
||||||
// '[ 1, "hello", 254, [ "add_token", "delete_token" ] ]'
|
// '[ 1, "hello", 254, [ "add_token", "delete_token" ] ]'
|
||||||
// '[ 1, "add_token" ]'
|
// '[ 1, "add_token" ]'
|
||||||
// '[ 1, "delete_token" ]'
|
// '[ 1, "delete_token" ]'
|
||||||
|
|
||||||
handlers.onconnection = function (tun) { } // a client has established a connection
|
handlers.onconnection = function(tun) {}; // a client has established a connection
|
||||||
|
|
||||||
handlers.onmessage = function (tun) { } // a client has sent a message
|
handlers.onmessage = function(tun) {}; // a client has sent a message
|
||||||
// tun = { family, address, port, data
|
// tun = { family, address, port, data
|
||||||
// , service, serviceport, name };
|
// , service, serviceport, name };
|
||||||
|
|
||||||
handlers.onpause = function (tun) { } // proxy requests to pause upload to a client
|
handlers.onpause = function(tun) {}; // proxy requests to pause upload to a client
|
||||||
// tun = { family, address, port };
|
// tun = { family, address, port };
|
||||||
|
|
||||||
handlers.onresume = function (tun) { } // proxy requests to resume upload to a client
|
handlers.onresume = function(tun) {}; // proxy requests to resume upload to a client
|
||||||
// tun = { family, address, port };
|
// tun = { family, address, port };
|
||||||
|
|
||||||
handlers.onend = function (tun) { } // proxy requests to close a client's socket
|
handlers.onend = function(tun) {}; // proxy requests to close a client's socket
|
||||||
// tun = { family, address, port };
|
// tun = { family, address, port };
|
||||||
|
|
||||||
handlers.onerror = function (err) { } // proxy is relaying a client's error
|
handlers.onerror = function(err) {}; // proxy is relaying a client's error
|
||||||
// err = { message, family, address, port };
|
// err = { message, family, address, port };
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
@ -163,44 +159,43 @@ handlers.onconnect = function (tun) { } // a new client has co
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
Packer & Extras
|
## Packer & Extras
|
||||||
------
|
|
||||||
|
|
||||||
Packs header metadata about connection into a buffer (potentially with original data), ready to send.
|
Packs header metadata about connection into a buffer (potentially with original data), ready to send.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var headerAndBody = Packer.pack(tun, data); // Add M-PROXY header to data
|
var headerAndBody = Packer.pack(tun, data); // Add M-PROXY header to data
|
||||||
// tun = { family, address, port
|
// tun = { family, address, port
|
||||||
// , service, serviceport, name }
|
// , service, serviceport, name }
|
||||||
|
|
||||||
var headerBuf = Packer.packHeader(tun, data); // Same as above, but creates a buffer for header only
|
var headerBuf = Packer.packHeader(tun, data); // Same as above, but creates a buffer for header only
|
||||||
// (data can be converted to a buffer or sent as-is)
|
// (data can be converted to a buffer or sent as-is)
|
||||||
|
|
||||||
var addr = Packer.socketToAddr(socket); // Probe raw, raw socket for address info
|
var addr = Packer.socketToAddr(socket); // Probe raw, raw socket for address info
|
||||||
|
|
||||||
var id = Packer.addrToId(address); // Turn M-PROXY address info into a deterministic id
|
var id = Packer.addrToId(address); // Turn M-PROXY address info into a deterministic id
|
||||||
|
|
||||||
var id = Packer.socketToId(socket); // Turn raw, raw socket info into a deterministic id
|
var id = Packer.socketToId(socket); // Turn raw, raw socket info into a deterministic id
|
||||||
```
|
```
|
||||||
|
|
||||||
## API Helpers
|
## API Helpers
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var socket = Packer.Stream.wrapSocket(socketOrStream); // workaround for https://github.com/nodejs/node/issues/8854
|
var socket = Packer.Stream.wrapSocket(socketOrStream); // workaround for https://github.com/nodejs/node/issues/8854
|
||||||
// which was just closed recently, but probably still needs
|
// which was just closed recently, but probably still needs
|
||||||
// something more like this (below) to work as intended
|
// something more like this (below) to work as intended
|
||||||
// https://github.com/findhit/proxywrap/blob/master/lib/proxywrap.js
|
// https://github.com/findhit/proxywrap/blob/master/lib/proxywrap.js
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var myTransform = Packer.Transform.create({
|
var myTransform = Packer.Transform.create({
|
||||||
address: {
|
address: {
|
||||||
family: '...'
|
family: '...',
|
||||||
, address: '...'
|
address: '...',
|
||||||
, port: '...'
|
port: '...'
|
||||||
}
|
},
|
||||||
// hint at the service to be used
|
// hint at the service to be used
|
||||||
, service: 'https'
|
service: 'https'
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -217,6 +212,7 @@ hexdump output.bin
|
||||||
Where `input.json` looks something like this:
|
Where `input.json` looks something like this:
|
||||||
|
|
||||||
`input.json`:
|
`input.json`:
|
||||||
|
|
||||||
```
|
```
|
||||||
{ "version": 1
|
{ "version": 1
|
||||||
, "address": {
|
, "address": {
|
||||||
|
@ -231,12 +227,12 @@ Where `input.json` looks something like this:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Raw TCP SNI Packet
|
## Raw TCP SNI Packet
|
||||||
------------------
|
|
||||||
|
|
||||||
and `sni.tcp.bin` is any captured tcp packet, such as this one with a tls hello:
|
and `sni.tcp.bin` is any captured tcp packet, such as this one with a tls hello:
|
||||||
|
|
||||||
`sni.tcp.bin`:
|
`sni.tcp.bin`:
|
||||||
|
|
||||||
```
|
```
|
||||||
0 1 2 3 4 5 6 7 8 9 A B C D D F
|
0 1 2 3 4 5 6 7 8 9 A B C D D F
|
||||||
0000000 16 03 01 00 c2 01 00 00 be 03 03 57 e3 76 50 66
|
0000000 16 03 01 00 c2 01 00 00 be 03 03 57 e3 76 50 66
|
||||||
|
@ -255,8 +251,7 @@ and `sni.tcp.bin` is any captured tcp packet, such as this one with a tls hello:
|
||||||
00000c7
|
00000c7
|
||||||
```
|
```
|
||||||
|
|
||||||
Tunneled TCP SNI Packet
|
## Tunneled TCP SNI Packet
|
||||||
-----------------------
|
|
||||||
|
|
||||||
You should see that the result is simply all of the original packet with a leading header.
|
You should see that the result is simply all of the original packet with a leading header.
|
||||||
|
|
||||||
|
|
740
index.js
740
index.js
|
@ -3,344 +3,374 @@
|
||||||
var Packer = module.exports;
|
var Packer = module.exports;
|
||||||
|
|
||||||
var serviceEvents = {
|
var serviceEvents = {
|
||||||
default: 'tunnelData'
|
default: 'tunnelData',
|
||||||
, connection: 'tunnelConnection'
|
connection: 'tunnelConnection',
|
||||||
, control: 'tunnelControl'
|
control: 'tunnelControl',
|
||||||
, error: 'tunnelError'
|
error: 'tunnelError',
|
||||||
, end: 'tunnelEnd'
|
end: 'tunnelEnd',
|
||||||
, pause: 'tunnelPause'
|
pause: 'tunnelPause',
|
||||||
, resume: 'tunnelResume'
|
resume: 'tunnelResume'
|
||||||
};
|
};
|
||||||
var serviceFuncs = {
|
var serviceFuncs = {
|
||||||
default: 'onmessage'
|
default: 'onmessage',
|
||||||
, connection: 'onconnection'
|
connection: 'onconnection',
|
||||||
, control: 'oncontrol'
|
control: 'oncontrol',
|
||||||
, error: 'onerror'
|
error: 'onerror',
|
||||||
, end: 'onend'
|
end: 'onend',
|
||||||
, pause: 'onpause'
|
pause: 'onpause',
|
||||||
, resume: 'onresume'
|
resume: 'onresume'
|
||||||
};
|
};
|
||||||
|
|
||||||
Packer.create = function (opts) {
|
Packer.create = function(opts) {
|
||||||
var machine;
|
var machine;
|
||||||
|
|
||||||
if (!opts.onMessage && !opts.onmessage) {
|
if (!opts.onMessage && !opts.onmessage) {
|
||||||
machine = new (require('events').EventEmitter)();
|
machine = new (require('events')).EventEmitter();
|
||||||
} else {
|
} else {
|
||||||
machine = {};
|
machine = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
machine.onmessage = opts.onmessage || opts.onMessage;
|
machine.onmessage = opts.onmessage || opts.onMessage;
|
||||||
machine.oncontrol = opts.oncontrol || opts.onControl;
|
machine.oncontrol = opts.oncontrol || opts.onControl;
|
||||||
machine.onconnection = opts.onconnection || opts.onConnection || function () {};
|
machine.onconnection =
|
||||||
machine.onerror = opts.onerror || opts.onError;
|
opts.onconnection || opts.onConnection || function() {};
|
||||||
machine.onend = opts.onend || opts.onEnd;
|
machine.onerror = opts.onerror || opts.onError;
|
||||||
machine.onpause = opts.onpause || opts.onPause;
|
machine.onend = opts.onend || opts.onEnd;
|
||||||
machine.onresume = opts.onresume || opts.onResume;
|
machine.onpause = opts.onpause || opts.onPause;
|
||||||
|
machine.onresume = opts.onresume || opts.onResume;
|
||||||
|
|
||||||
machine._version = 1;
|
machine._version = 1;
|
||||||
machine.fns = {};
|
machine.fns = {};
|
||||||
|
|
||||||
machine.chunkIndex = 0;
|
machine.chunkIndex = 0;
|
||||||
machine.buf = null;
|
machine.buf = null;
|
||||||
machine.bufIndex = 0;
|
machine.bufIndex = 0;
|
||||||
machine.fns.collectData = function (chunk, size) {
|
machine.fns.collectData = function(chunk, size) {
|
||||||
var chunkLeft = chunk.length - machine.chunkIndex;
|
var chunkLeft = chunk.length - machine.chunkIndex;
|
||||||
var hasLen = (size > 0);
|
var hasLen = size > 0;
|
||||||
|
|
||||||
if (!hasLen) {
|
if (!hasLen) {
|
||||||
return Buffer.alloc(0);
|
return Buffer.alloc(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First handle case where we don't have all the data we need yet. We need to save
|
// First handle case where we don't have all the data we need yet. We need to save
|
||||||
// what we have in a buffer, and increment the index for both the buffer and the chunk.
|
// what we have in a buffer, and increment the index for both the buffer and the chunk.
|
||||||
if (machine.bufIndex + chunkLeft < size) {
|
if (machine.bufIndex + chunkLeft < size) {
|
||||||
if (!machine.buf) {
|
if (!machine.buf) {
|
||||||
machine.buf = Buffer.alloc(size);
|
machine.buf = Buffer.alloc(size);
|
||||||
}
|
}
|
||||||
chunk.copy(machine.buf, machine.bufIndex, machine.chunkIndex);
|
chunk.copy(machine.buf, machine.bufIndex, machine.chunkIndex);
|
||||||
machine.bufIndex += chunkLeft;
|
machine.bufIndex += chunkLeft;
|
||||||
machine.chunkIndex += chunkLeft;
|
machine.chunkIndex += chunkLeft;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read and mark as read however much data we need from the chunk to complete our buffer.
|
// Read and mark as read however much data we need from the chunk to complete our buffer.
|
||||||
var partLen = size - machine.bufIndex;
|
var partLen = size - machine.bufIndex;
|
||||||
var part = chunk.slice(machine.chunkIndex, machine.chunkIndex+partLen);
|
var part = chunk.slice(
|
||||||
machine.chunkIndex += partLen;
|
machine.chunkIndex,
|
||||||
|
machine.chunkIndex + partLen
|
||||||
|
);
|
||||||
|
machine.chunkIndex += partLen;
|
||||||
|
|
||||||
// If we had nothing buffered than the part of the chunk we just read is all we need.
|
// If we had nothing buffered than the part of the chunk we just read is all we need.
|
||||||
if (!machine.buf) {
|
if (!machine.buf) {
|
||||||
return part;
|
return part;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise we need to copy the new data into the buffer.
|
// Otherwise we need to copy the new data into the buffer.
|
||||||
part.copy(machine.buf, machine.bufIndex);
|
part.copy(machine.buf, machine.bufIndex);
|
||||||
// Before returning the buffer we need to clear our reference to it.
|
// Before returning the buffer we need to clear our reference to it.
|
||||||
var buf = machine.buf;
|
var buf = machine.buf;
|
||||||
machine.buf = null;
|
machine.buf = null;
|
||||||
machine.bufIndex = 0;
|
machine.bufIndex = 0;
|
||||||
return buf;
|
return buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
machine.fns.version = function (chunk) {
|
machine.fns.version = function(chunk) {
|
||||||
//console.log('');
|
//console.log('');
|
||||||
//console.log('[version]');
|
//console.log('[version]');
|
||||||
if ((255 - machine._version) !== chunk[machine.chunkIndex]) {
|
if (255 - machine._version !== chunk[machine.chunkIndex]) {
|
||||||
console.error("not v" + machine._version + " (or data is corrupt)");
|
console.error('not v' + machine._version + ' (or data is corrupt)');
|
||||||
// no idea how to fix this yet
|
// no idea how to fix this yet
|
||||||
}
|
}
|
||||||
machine.chunkIndex += 1;
|
machine.chunkIndex += 1;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
machine.headerLen = 0;
|
||||||
|
machine.fns.headerLength = function(chunk) {
|
||||||
|
//console.log('');
|
||||||
|
//console.log('[headerLength]');
|
||||||
|
machine.headerLen = chunk[machine.chunkIndex];
|
||||||
|
machine.chunkIndex += 1;
|
||||||
|
|
||||||
machine.headerLen = 0;
|
return true;
|
||||||
machine.fns.headerLength = function (chunk) {
|
};
|
||||||
//console.log('');
|
|
||||||
//console.log('[headerLength]');
|
|
||||||
machine.headerLen = chunk[machine.chunkIndex];
|
|
||||||
machine.chunkIndex += 1;
|
|
||||||
|
|
||||||
return true;
|
machine.fns.header = function(chunk) {
|
||||||
};
|
//console.log('');
|
||||||
|
//console.log('[header]');
|
||||||
|
var header = machine.fns.collectData(chunk, machine.headerLen);
|
||||||
|
|
||||||
machine.fns.header = function (chunk) {
|
// We don't have the entire header yet so return false.
|
||||||
//console.log('');
|
if (!header) {
|
||||||
//console.log('[header]');
|
return false;
|
||||||
var header = machine.fns.collectData(chunk, machine.headerLen);
|
}
|
||||||
|
|
||||||
// We don't have the entire header yet so return false.
|
machine._headers = header.toString().split(/,/g);
|
||||||
if (!header) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
machine._headers = header.toString().split(/,/g);
|
machine.family = machine._headers[0];
|
||||||
|
machine.address = machine._headers[1];
|
||||||
|
machine.port = machine._headers[2];
|
||||||
|
machine.bodyLen = parseInt(machine._headers[3], 10) || 0;
|
||||||
|
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);
|
||||||
|
|
||||||
machine.family = machine._headers[0];
|
return true;
|
||||||
machine.address = machine._headers[1];
|
};
|
||||||
machine.port = machine._headers[2];
|
|
||||||
machine.bodyLen = parseInt(machine._headers[3], 10) || 0;
|
|
||||||
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;
|
machine.fns.data = function(chunk) {
|
||||||
};
|
//console.log('');
|
||||||
|
//console.log('[data]');
|
||||||
|
var data;
|
||||||
|
// The 'connection' event may not have a body
|
||||||
|
// Other events may not have a body either
|
||||||
|
if (machine.bodyLen) {
|
||||||
|
data = machine.fns.collectData(chunk, machine.bodyLen);
|
||||||
|
// We don't have the entire body yet so return false.
|
||||||
|
if (!data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
machine.fns.data = function (chunk) {
|
//
|
||||||
//console.log('');
|
// data, end, error
|
||||||
//console.log('[data]');
|
//
|
||||||
var data;
|
var msg = {};
|
||||||
// The 'connection' event may not have a body
|
if ('error' === machine.service) {
|
||||||
// Other events may not have a body either
|
try {
|
||||||
if (machine.bodyLen) {
|
msg = JSON.parse(data.toString());
|
||||||
data = machine.fns.collectData(chunk, machine.bodyLen);
|
} catch (e) {
|
||||||
// We don't have the entire body yet so return false.
|
msg.message = 'e:' + JSON.stringify(data);
|
||||||
if (!data) {
|
msg.code = 'E_UNKNOWN_ERR';
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
msg.family = machine.family;
|
||||||
|
msg.address = machine.address;
|
||||||
|
msg.port = machine.port;
|
||||||
|
msg.service = machine.service;
|
||||||
|
msg.serviceport = machine.serviceport;
|
||||||
|
msg.name = machine.name;
|
||||||
|
msg.data = data;
|
||||||
|
|
||||||
//
|
if ('connection' === machine.service) {
|
||||||
// data, end, error
|
msg.service = machine.servicename;
|
||||||
//
|
}
|
||||||
var msg = {};
|
|
||||||
if ('error' === machine.service) {
|
|
||||||
try {
|
|
||||||
msg = JSON.parse(data.toString());
|
|
||||||
} catch(e) {
|
|
||||||
msg.message = 'e:' + JSON.stringify(data);
|
|
||||||
msg.code = 'E_UNKNOWN_ERR';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.family = machine.family;
|
//console.log('msn', machine.service);
|
||||||
msg.address = machine.address;
|
if (machine.emit) {
|
||||||
msg.port = machine.port;
|
machine.emit(
|
||||||
msg.service = machine.service;
|
serviceEvents[machine.service] ||
|
||||||
msg.serviceport = machine.serviceport;
|
serviceEvents[msg.service] ||
|
||||||
msg.name = machine.name;
|
serviceEvents.default
|
||||||
msg.data = data;
|
);
|
||||||
|
} else {
|
||||||
|
(machine[serviceFuncs[machine.service]] ||
|
||||||
|
machine[serviceFuncs[msg.service]] ||
|
||||||
|
machine[serviceFuncs.default])(msg);
|
||||||
|
}
|
||||||
|
|
||||||
if ('connection' === machine.service) {
|
return true;
|
||||||
msg.service = machine.servicename;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
//console.log('msn', machine.service);
|
machine.state = 0;
|
||||||
if (machine.emit) {
|
machine.states = ['version', 'headerLength', 'header', 'data'];
|
||||||
machine.emit(serviceEvents[machine.service] || serviceEvents[msg.service] || serviceEvents.default);
|
machine.fns.addChunk = function(chunk) {
|
||||||
} else {
|
//console.log('');
|
||||||
(machine[serviceFuncs[machine.service]] || machine[serviceFuncs[msg.service]] || machine[serviceFuncs.default])(msg);
|
//console.log('[addChunk]');
|
||||||
}
|
machine.chunkIndex = 0;
|
||||||
|
while (machine.chunkIndex < chunk.length) {
|
||||||
|
//console.log('chunkIndex:', machine.chunkIndex, 'state:', machine.state);
|
||||||
|
|
||||||
return true;
|
if (true === machine.fns[machine.states[machine.state]](chunk)) {
|
||||||
};
|
machine.state += 1;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
machine.state = 0;
|
return machine;
|
||||||
machine.states = ['version', 'headerLength', 'header', 'data'];
|
|
||||||
machine.fns.addChunk = function (chunk) {
|
|
||||||
//console.log('');
|
|
||||||
//console.log('[addChunk]');
|
|
||||||
machine.chunkIndex = 0;
|
|
||||||
while (machine.chunkIndex < chunk.length) {
|
|
||||||
//console.log('chunkIndex:', machine.chunkIndex, 'state:', machine.state);
|
|
||||||
|
|
||||||
if (true === machine.fns[machine.states[machine.state]](chunk)) {
|
|
||||||
machine.state += 1;
|
|
||||||
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) {
|
Packer.packHeader = function(meta, data, service, andBody, oldways) {
|
||||||
if (oldways && !data) {
|
if (oldways && !data) {
|
||||||
data = Buffer.from(' ');
|
data = Buffer.from(' ');
|
||||||
}
|
}
|
||||||
if (data && !Buffer.isBuffer(data)) {
|
if (data && !Buffer.isBuffer(data)) {
|
||||||
data = Buffer.from(JSON.stringify(data));
|
data = Buffer.from(JSON.stringify(data));
|
||||||
}
|
}
|
||||||
if (oldways && !data.byteLength) {
|
if (oldways && !data.byteLength) {
|
||||||
data = Buffer.from(' ');
|
data = Buffer.from(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (service && -1 === ['control','connection'].indexOf(service)) {
|
if (service && -1 === ['control', 'connection'].indexOf(service)) {
|
||||||
//console.log('end?', service);
|
//console.log('end?', service);
|
||||||
meta.service = service;
|
meta.service = service;
|
||||||
}
|
}
|
||||||
|
|
||||||
var size = data && data.byteLength || 0;
|
var size = (data && data.byteLength) || 0;
|
||||||
var sizeReserve = andBody ? size : 0;
|
var sizeReserve = andBody ? size : 0;
|
||||||
var version = 1;
|
var version = 1;
|
||||||
var header;
|
var header;
|
||||||
if (service === 'control') {
|
if (service === 'control') {
|
||||||
header = Buffer.from(['', '', '', size, service].join(','));
|
header = Buffer.from(['', '', '', size, service].join(','));
|
||||||
}
|
} else if (service === 'connection') {
|
||||||
else if (service === 'connection') {
|
header = Buffer.from(
|
||||||
header = Buffer.from([
|
[
|
||||||
meta.family, meta.address, meta.port, size,
|
meta.family,
|
||||||
'connection', (meta.serviceport || ''), (meta.name || ''),
|
meta.address,
|
||||||
(meta.service || '')
|
meta.port,
|
||||||
].join(','));
|
size,
|
||||||
}
|
'connection',
|
||||||
else {
|
meta.serviceport || '',
|
||||||
header = Buffer.from([
|
meta.name || '',
|
||||||
meta.family, meta.address, meta.port, size,
|
meta.service || ''
|
||||||
(meta.service || ''), (meta.serviceport || ''), (meta.name || '')
|
].join(',')
|
||||||
].join(','));
|
);
|
||||||
}
|
} else {
|
||||||
var metaBuf = Buffer.from([ 255 - version, header.length ]);
|
header = Buffer.from(
|
||||||
var buf = Buffer.alloc(metaBuf.byteLength + header.byteLength + sizeReserve);
|
[
|
||||||
|
meta.family,
|
||||||
|
meta.address,
|
||||||
|
meta.port,
|
||||||
|
size,
|
||||||
|
meta.service || '',
|
||||||
|
meta.serviceport || '',
|
||||||
|
meta.name || ''
|
||||||
|
].join(',')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
var metaBuf = Buffer.from([255 - version, header.length]);
|
||||||
|
var buf = Buffer.alloc(
|
||||||
|
metaBuf.byteLength + header.byteLength + sizeReserve
|
||||||
|
);
|
||||||
|
|
||||||
metaBuf.copy(buf, 0);
|
metaBuf.copy(buf, 0);
|
||||||
header.copy(buf, 2);
|
header.copy(buf, 2);
|
||||||
if (sizeReserve) { data.copy(buf, 2 + header.byteLength); }
|
if (sizeReserve) {
|
||||||
|
data.copy(buf, 2 + header.byteLength);
|
||||||
|
}
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
};
|
};
|
||||||
Packer.pack = function (meta, data, service) {
|
Packer.pack = function(meta, data, service) {
|
||||||
return Packer.packHeader(meta, data, service, true, true);
|
return Packer.packHeader(meta, data, service, true, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
function extractSocketProps(socket, propNames) {
|
function extractSocketProps(socket, propNames) {
|
||||||
var props = {};
|
var props = {};
|
||||||
|
|
||||||
if (socket.remotePort) {
|
if (socket.remotePort) {
|
||||||
propNames.forEach(function (propName) {
|
propNames.forEach(function(propName) {
|
||||||
props[propName] = socket[propName];
|
props[propName] = socket[propName];
|
||||||
});
|
});
|
||||||
} else if (socket._remotePort) {
|
} else if (socket._remotePort) {
|
||||||
propNames.forEach(function (propName) {
|
propNames.forEach(function(propName) {
|
||||||
props[propName] = socket['_' + propName];
|
props[propName] = socket['_' + propName];
|
||||||
});
|
});
|
||||||
} else if (socket._handle) {
|
} else if (socket._handle) {
|
||||||
if (
|
if (
|
||||||
socket._handle._parent
|
socket._handle._parent &&
|
||||||
&& socket._handle._parent.owner
|
socket._handle._parent.owner &&
|
||||||
&& socket._handle._parent.owner.stream
|
socket._handle._parent.owner.stream &&
|
||||||
&& socket._handle._parent.owner.stream.remotePort
|
socket._handle._parent.owner.stream.remotePort
|
||||||
) {
|
) {
|
||||||
propNames.forEach(function (propName) {
|
propNames.forEach(function(propName) {
|
||||||
props[propName] = socket._handle._parent.owner.stream[propName];
|
props[propName] = socket._handle._parent.owner.stream[propName];
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
socket._handle._parentWrap
|
socket._handle._parentWrap &&
|
||||||
&& socket._handle._parentWrap.remotePort
|
socket._handle._parentWrap.remotePort
|
||||||
) {
|
) {
|
||||||
propNames.forEach(function (propName) {
|
propNames.forEach(function(propName) {
|
||||||
props[propName] = socket._handle._parentWrap[propName];
|
props[propName] = socket._handle._parentWrap[propName];
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
socket._handle._parentWrap
|
socket._handle._parentWrap &&
|
||||||
&& socket._handle._parentWrap._handle
|
socket._handle._parentWrap._handle &&
|
||||||
&& socket._handle._parentWrap._handle.owner
|
socket._handle._parentWrap._handle.owner &&
|
||||||
&& socket._handle._parentWrap._handle.owner.stream
|
socket._handle._parentWrap._handle.owner.stream &&
|
||||||
&& socket._handle._parentWrap._handle.owner.stream.remotePort
|
socket._handle._parentWrap._handle.owner.stream.remotePort
|
||||||
) {
|
) {
|
||||||
propNames.forEach(function (propName) {
|
propNames.forEach(function(propName) {
|
||||||
props[propName] = socket._handle._parentWrap._handle.owner.stream[propName];
|
props[propName] =
|
||||||
});
|
socket._handle._parentWrap._handle.owner.stream[propName];
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
return props;
|
}
|
||||||
|
return props;
|
||||||
}
|
}
|
||||||
function extractSocketProp(socket, propName) {
|
function extractSocketProp(socket, propName) {
|
||||||
// remoteAddress, remotePort... ugh... https://github.com/nodejs/node/issues/8854
|
// remoteAddress, remotePort... ugh... https://github.com/nodejs/node/issues/8854
|
||||||
var value = socket[propName] || socket['_' + propName];
|
var value = socket[propName] || socket['_' + propName];
|
||||||
try {
|
try {
|
||||||
value = value || socket._handle._parent.owner.stream[propName];
|
value = value || socket._handle._parent.owner.stream[propName];
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
value = value || socket._handle._parentWrap[propName];
|
value = value || socket._handle._parentWrap[propName];
|
||||||
value = value || socket._handle._parentWrap._handle.owner.stream[propName];
|
value =
|
||||||
} catch (e) {}
|
value || socket._handle._parentWrap._handle.owner.stream[propName];
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
return value || '';
|
return value || '';
|
||||||
}
|
}
|
||||||
Packer.socketToAddr = function (socket) {
|
Packer.socketToAddr = function(socket) {
|
||||||
// TODO BUG XXX
|
// TODO BUG XXX
|
||||||
// https://github.com/nodejs/node/issues/8854
|
// https://github.com/nodejs/node/issues/8854
|
||||||
// tlsSocket.remoteAddress = remoteAddress; // causes core dump
|
// tlsSocket.remoteAddress = remoteAddress; // causes core dump
|
||||||
// console.log(tlsSocket.remoteAddress);
|
// console.log(tlsSocket.remoteAddress);
|
||||||
|
|
||||||
var props = extractSocketProps(socket, [ 'remoteFamily', 'remoteAddress', 'remotePort', 'localPort' ]);
|
var props = extractSocketProps(socket, [
|
||||||
return {
|
'remoteFamily',
|
||||||
family: props.remoteFamily
|
'remoteAddress',
|
||||||
, address: props.remoteAddress
|
'remotePort',
|
||||||
, port: props.remotePort
|
'localPort'
|
||||||
, serviceport: props.localPort
|
]);
|
||||||
};
|
return {
|
||||||
|
family: props.remoteFamily,
|
||||||
|
address: props.remoteAddress,
|
||||||
|
port: props.remotePort,
|
||||||
|
serviceport: props.localPort
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Packer.addrToId = function (address) {
|
Packer.addrToId = function(address) {
|
||||||
return address.family + ',' + address.address + ',' + address.port;
|
return address.family + ',' + address.address + ',' + address.port;
|
||||||
};
|
};
|
||||||
|
|
||||||
Packer.socketToId = function (socket) {
|
Packer.socketToId = function(socket) {
|
||||||
return Packer.addrToId(Packer.socketToAddr(socket));
|
return Packer.addrToId(Packer.socketToAddr(socket));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var addressNames = [
|
var addressNames = [
|
||||||
'remoteAddress'
|
'remoteAddress',
|
||||||
, 'remotePort'
|
'remotePort',
|
||||||
, 'remoteFamily'
|
'remoteFamily',
|
||||||
, 'localAddress'
|
'localAddress',
|
||||||
, 'localPort'
|
'localPort'
|
||||||
];
|
];
|
||||||
/*
|
/*
|
||||||
var sockFuncs = [
|
var sockFuncs = [
|
||||||
|
@ -355,20 +385,20 @@ var sockFuncs = [
|
||||||
];
|
];
|
||||||
*/
|
*/
|
||||||
// Unlike Packer.Stream.create this should handle all of the events needed to make everything work.
|
// Unlike Packer.Stream.create this should handle all of the events needed to make everything work.
|
||||||
Packer.wrapSocket = function (socket) {
|
Packer.wrapSocket = function(socket) {
|
||||||
// node v10.2+ doesn't need a workaround for https://github.com/nodejs/node/issues/8854
|
// node v10.2+ doesn't need a workaround for https://github.com/nodejs/node/issues/8854
|
||||||
addressNames.forEach(function (name) {
|
addressNames.forEach(function(name) {
|
||||||
Object.defineProperty(socket, name, {
|
Object.defineProperty(socket, name, {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get: function() {
|
get: function() {
|
||||||
return extractSocketProp(socket, name);
|
return extractSocketProp(socket, name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return socket;
|
return socket;
|
||||||
// Improved workaround for https://github.com/nodejs/node/issues/8854
|
// Improved workaround for https://github.com/nodejs/node/issues/8854
|
||||||
/*
|
/*
|
||||||
// TODO use defineProperty to override remotePort, etc
|
// TODO use defineProperty to override remotePort, etc
|
||||||
var myDuplex = new require('stream').Duplex();
|
var myDuplex = new require('stream').Duplex();
|
||||||
addressNames.forEach(function (name) {
|
addressNames.forEach(function (name) {
|
||||||
|
@ -418,79 +448,79 @@ var Transform = require('stream').Transform;
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
function MyTransform(options) {
|
function MyTransform(options) {
|
||||||
if (!(this instanceof MyTransform)) {
|
if (!(this instanceof MyTransform)) {
|
||||||
return new MyTransform(options);
|
return new MyTransform(options);
|
||||||
}
|
}
|
||||||
this.__my_addr = options.address;
|
this.__my_addr = options.address;
|
||||||
this.__my_service = options.service;
|
this.__my_service = options.service;
|
||||||
this.__my_serviceport = options.serviceport;
|
this.__my_serviceport = options.serviceport;
|
||||||
this.__my_name = options.name;
|
this.__my_name = options.name;
|
||||||
Transform.call(this, options);
|
Transform.call(this, options);
|
||||||
}
|
}
|
||||||
util.inherits(MyTransform, Transform);
|
util.inherits(MyTransform, Transform);
|
||||||
|
|
||||||
MyTransform.prototype._transform = function (data, encoding, callback) {
|
MyTransform.prototype._transform = function(data, encoding, callback) {
|
||||||
var address = this.__my_addr;
|
var address = this.__my_addr;
|
||||||
|
|
||||||
address.service = address.service || this.__my_service;
|
address.service = address.service || this.__my_service;
|
||||||
address.serviceport = address.serviceport || this.__my_serviceport;
|
address.serviceport = address.serviceport || this.__my_serviceport;
|
||||||
address.name = address.name || this.__my_name;
|
address.name = address.name || this.__my_name;
|
||||||
this.push(Packer.pack(address, data));
|
this.push(Packer.pack(address, data));
|
||||||
callback();
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
Packer.Stream = {};
|
Packer.Stream = {};
|
||||||
var Dup = {
|
var Dup = {
|
||||||
write: function (chunk, encoding, cb) {
|
write: function(chunk, encoding, cb) {
|
||||||
//console.log('_write', chunk.byteLength);
|
//console.log('_write', chunk.byteLength);
|
||||||
this.__my_socket.write(chunk, encoding, cb);
|
this.__my_socket.write(chunk, encoding, cb);
|
||||||
}
|
},
|
||||||
, read: function (size) {
|
read: function(size) {
|
||||||
//console.log('_read');
|
//console.log('_read');
|
||||||
var x = this.__my_socket.read(size);
|
var x = this.__my_socket.read(size);
|
||||||
if (x) {
|
if (x) {
|
||||||
console.log('_read', size);
|
console.log('_read', size);
|
||||||
this.push(x);
|
this.push(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Packer.Stream.create = function (socket) {
|
Packer.Stream.create = function(socket) {
|
||||||
if (!Packer.Stream.warned) {
|
if (!Packer.Stream.warned) {
|
||||||
console.warn('`Stream.create` deprecated, use `wrapSocket` instead');
|
console.warn('`Stream.create` deprecated, use `wrapSocket` instead');
|
||||||
Packer.Stream.warned = true;
|
Packer.Stream.warned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for
|
// Workaround for
|
||||||
// https://github.com/nodejs/node/issues/8854
|
// https://github.com/nodejs/node/issues/8854
|
||||||
|
|
||||||
// https://www.google.com/#q=get+socket+address+from+file+descriptor
|
// https://www.google.com/#q=get+socket+address+from+file+descriptor
|
||||||
// TODO try creating a new net.Socket({ handle: socket._handle, fd: socket._handle.fd })
|
// TODO try creating a new net.Socket({ handle: socket._handle, fd: socket._handle.fd })
|
||||||
// from the old one and then adding back the data with
|
// from the old one and then adding back the data with
|
||||||
// sock.push(firstChunk)
|
// sock.push(firstChunk)
|
||||||
var Duplex = require('stream').Duplex;
|
var Duplex = require('stream').Duplex;
|
||||||
var myDuplex = new Duplex();
|
var myDuplex = new Duplex();
|
||||||
|
|
||||||
myDuplex.__my_socket = socket;
|
myDuplex.__my_socket = socket;
|
||||||
myDuplex._write = Dup.write;
|
myDuplex._write = Dup.write;
|
||||||
myDuplex._read = Dup.read;
|
myDuplex._read = Dup.read;
|
||||||
//console.log('plainSocket.*Address');
|
//console.log('plainSocket.*Address');
|
||||||
//console.log('remote:', socket.remoteAddress);
|
//console.log('remote:', socket.remoteAddress);
|
||||||
//console.log('local:', socket.localAddress);
|
//console.log('local:', socket.localAddress);
|
||||||
//console.log('address():', socket.address());
|
//console.log('address():', socket.address());
|
||||||
myDuplex.remoteFamily = socket.remoteFamily;
|
myDuplex.remoteFamily = socket.remoteFamily;
|
||||||
myDuplex.remoteAddress = socket.remoteAddress;
|
myDuplex.remoteAddress = socket.remoteAddress;
|
||||||
myDuplex.remotePort = socket.remotePort;
|
myDuplex.remotePort = socket.remotePort;
|
||||||
myDuplex.localFamily = socket.localFamily;
|
myDuplex.localFamily = socket.localFamily;
|
||||||
myDuplex.localAddress = socket.localAddress;
|
myDuplex.localAddress = socket.localAddress;
|
||||||
myDuplex.localPort = socket.localPort;
|
myDuplex.localPort = socket.localPort;
|
||||||
|
|
||||||
return myDuplex;
|
return myDuplex;
|
||||||
};
|
};
|
||||||
|
|
||||||
Packer.Transform = {};
|
Packer.Transform = {};
|
||||||
Packer.Transform.create = function (opts) {
|
Packer.Transform.create = function(opts) {
|
||||||
// Note: service refers to the port that the incoming request was from,
|
// Note: service refers to the port that the incoming request was from,
|
||||||
// if known (smtps, smtp, https, http, etc)
|
// if known (smtps, smtp, https, http, etc)
|
||||||
// { address: '127.0.0.1', service: 'https' }
|
// { address: '127.0.0.1', service: 'https' }
|
||||||
return new MyTransform(opts);
|
return new MyTransform(opts);
|
||||||
};
|
};
|
||||||
|
|
76
package.json
76
package.json
|
@ -1,40 +1,40 @@
|
||||||
{
|
{
|
||||||
"name": "proxy-packer",
|
"name": "proxy-packer",
|
||||||
"version": "2.0.3",
|
"version": "2.0.4",
|
||||||
"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.",
|
"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",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node test.js"
|
"test": "node test.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://git.coolaj86.com/coolaj86/proxy-packer.js.git"
|
"url": "git+https://git.coolaj86.com/coolaj86/proxy-packer.js.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"tunnel",
|
"tunnel",
|
||||||
"telebit",
|
"telebit",
|
||||||
"telebitd",
|
"telebitd",
|
||||||
"localtunnel",
|
"localtunnel",
|
||||||
"ngrok",
|
"ngrok",
|
||||||
"underpass",
|
"underpass",
|
||||||
"tcp",
|
"tcp",
|
||||||
"sni",
|
"sni",
|
||||||
"https",
|
"https",
|
||||||
"ssl",
|
"ssl",
|
||||||
"tls",
|
"tls",
|
||||||
"http",
|
"http",
|
||||||
"proxy",
|
"proxy",
|
||||||
"pack",
|
"pack",
|
||||||
"unpack",
|
"unpack",
|
||||||
"message",
|
"message",
|
||||||
"msg",
|
"msg",
|
||||||
"packer",
|
"packer",
|
||||||
"unpacker"
|
"unpacker"
|
||||||
],
|
],
|
||||||
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
|
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
|
||||||
"license": "(MIT OR Apache-2.0)",
|
"license": "(MIT OR Apache-2.0)",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://git.coolaj86.com/coolaj86/proxy-packer.js/issues"
|
"url": "https://git.coolaj86.com/coolaj86/proxy-packer.js/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://git.coolaj86.com/coolaj86/proxy-packer.js#readme"
|
"homepage": "https://git.coolaj86.com/coolaj86/proxy-packer.js#readme"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
{ "version": 1
|
{
|
||||||
, "address": {
|
"version": 1,
|
||||||
"family": "IPv4"
|
"address": {
|
||||||
, "address": "127.0.1.1"
|
"family": "IPv4",
|
||||||
, "port": 4321
|
"address": "127.0.1.1",
|
||||||
, "service": "https"
|
"port": 4321,
|
||||||
, "serviceport": 443
|
"service": "https",
|
||||||
}
|
"serviceport": 443
|
||||||
, "filepath": "./sni.hello.bin"
|
},
|
||||||
|
"filepath": "./sni.hello.bin"
|
||||||
}
|
}
|
||||||
|
|
62
test/pack.js
62
test/pack.js
|
@ -1,28 +1,31 @@
|
||||||
;(function () {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var infile = process.argv[2];
|
var infile = process.argv[2];
|
||||||
var outfile = process.argv[3];
|
var outfile = process.argv[3];
|
||||||
var sni = require('sni');
|
var sni = require('sni');
|
||||||
|
|
||||||
if (!infile || !outfile) {
|
if (!infile || !outfile) {
|
||||||
console.error("Usage:");
|
console.error('Usage:');
|
||||||
console.error("node test/pack.js test/input.json test/output.bin");
|
console.error('node test/pack.js test/input.json test/output.bin');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var json = JSON.parse(fs.readFileSync(infile, 'utf8'));
|
var json = JSON.parse(fs.readFileSync(infile, 'utf8'));
|
||||||
var data = require('fs').readFileSync(path.resolve(path.dirname(infile), json.filepath), null);
|
var data = require('fs').readFileSync(
|
||||||
var Packer = require('../index.js');
|
path.resolve(path.dirname(infile), json.filepath),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
var Packer = require('../index.js');
|
||||||
|
|
||||||
var servername = sni(data);
|
var servername = sni(data);
|
||||||
var m = data.toString().match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im);
|
var m = data.toString().match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im);
|
||||||
var hostname = (m && m[1].toLowerCase() || '').split(':')[0];
|
var hostname = ((m && m[1].toLowerCase()) || '').split(':')[0];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
function pack() {
|
function pack() {
|
||||||
var version = json.version;
|
var version = json.version;
|
||||||
var address = json.address;
|
var address = json.address;
|
||||||
|
@ -37,9 +40,16 @@ function pack() {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
json.address.name = servername || hostname;
|
json.address.name = servername || hostname;
|
||||||
var buf = Packer.pack(json.address, data);
|
var buf = Packer.pack(json.address, data);
|
||||||
fs.writeFileSync(outfile, buf, null);
|
fs.writeFileSync(outfile, buf, null);
|
||||||
console.log("wrote " + buf.byteLength + " bytes to '" + outfile + "' ('hexdump " + outfile + "' to inspect)");
|
console.log(
|
||||||
|
'wrote ' +
|
||||||
}());
|
buf.byteLength +
|
||||||
|
" bytes to '" +
|
||||||
|
outfile +
|
||||||
|
"' ('hexdump " +
|
||||||
|
outfile +
|
||||||
|
"' to inspect)"
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
|
258
test/parse.js
258
test/parse.js
|
@ -4,82 +4,114 @@ var sni = require('sni');
|
||||||
var hello = require('fs').readFileSync(__dirname + '/sni.hello.bin');
|
var hello = require('fs').readFileSync(__dirname + '/sni.hello.bin');
|
||||||
var version = 1;
|
var version = 1;
|
||||||
function getAddress() {
|
function getAddress() {
|
||||||
return {
|
return {
|
||||||
family: 'IPv4'
|
family: 'IPv4',
|
||||||
, address: '127.0.1.1'
|
address: '127.0.1.1',
|
||||||
, port: 4321
|
port: 4321,
|
||||||
, service: 'foo-https'
|
service: 'foo-https',
|
||||||
, serviceport: 443
|
serviceport: 443,
|
||||||
, name: 'foo-pokemap.hellabit.com'
|
name: 'foo-pokemap.hellabit.com'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
var addr = getAddress();
|
var addr = getAddress();
|
||||||
var connectionHeader = addr.family + ',' + addr.address + ',' + addr.port
|
var connectionHeader =
|
||||||
+ ',0,connection,'
|
addr.family +
|
||||||
+ (addr.serviceport || '') + ',' + (addr.name || '') + ',' + (addr.service || '')
|
',' +
|
||||||
;
|
addr.address +
|
||||||
var header = addr.family + ',' + addr.address + ',' + addr.port
|
',' +
|
||||||
+ ',' + hello.byteLength + ',' + (addr.service || '') + ','
|
addr.port +
|
||||||
+ (addr.serviceport || '') + ',' + (addr.name || '')
|
',0,connection,' +
|
||||||
;
|
(addr.serviceport || '') +
|
||||||
var endHeader = addr.family + ',' + addr.address + ',' + addr.port
|
',' +
|
||||||
+ ',0,end,'
|
(addr.name || '') +
|
||||||
+ (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([
|
var buf = Buffer.concat([
|
||||||
Buffer.from([ 255 - version, connectionHeader.length ])
|
Buffer.from([255 - version, connectionHeader.length]),
|
||||||
, Buffer.from(connectionHeader)
|
Buffer.from(connectionHeader),
|
||||||
, Buffer.from([ 255 - version, header.length ])
|
Buffer.from([255 - version, header.length]),
|
||||||
, Buffer.from(header)
|
Buffer.from(header),
|
||||||
, hello
|
hello,
|
||||||
, Buffer.from([ 255 - version, endHeader.length ])
|
Buffer.from([255 - version, endHeader.length]),
|
||||||
, Buffer.from(endHeader)
|
Buffer.from(endHeader)
|
||||||
]);
|
]);
|
||||||
var services = { 'ssh': 22, 'http': 4080, 'https': 8443 };
|
var services = { ssh: 22, http: 4080, https: 8443 };
|
||||||
var clients = {};
|
var clients = {};
|
||||||
var count = 0;
|
var count = 0;
|
||||||
var packer = require('../');
|
var packer = require('../');
|
||||||
var machine = packer.create({
|
var machine = packer.create({
|
||||||
onconnection: function (tun) {
|
onconnection: function(tun) {
|
||||||
console.info('');
|
console.info('');
|
||||||
if (!tun.service || 'connection' === tun.service) {
|
if (!tun.service || 'connection' === tun.service) {
|
||||||
throw new Error("missing service: " + JSON.stringify(tun));
|
throw new Error('missing service: ' + JSON.stringify(tun));
|
||||||
}
|
}
|
||||||
console.info('[onConnection]');
|
console.info('[onConnection]');
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
},
|
||||||
, onmessage: function (tun) {
|
onmessage: function(tun) {
|
||||||
//console.log('onmessage', tun);
|
//console.log('onmessage', tun);
|
||||||
var id = tun.family + ',' + tun.address + ',' + tun.port;
|
var id = tun.family + ',' + tun.address + ',' + tun.port;
|
||||||
var service = 'https';
|
var service = 'https';
|
||||||
var port = services[service];
|
var port = services[service];
|
||||||
var servername = sni(tun.data);
|
var servername = sni(tun.data);
|
||||||
|
|
||||||
console.info('[onMessage]', service, port, servername, tun.data.byteLength);
|
console.info(
|
||||||
if (!tun.data.equals(hello)) {
|
'[onMessage]',
|
||||||
throw new Error("'data' packet is not equal to original 'hello' packet");
|
service,
|
||||||
}
|
port,
|
||||||
//console.log('all', tun.data.byteLength, 'bytes are equal');
|
servername,
|
||||||
//console.log('src:', tun.family, tun.address + ':' + tun.port + ':' + tun.serviceport);
|
tun.data.byteLength
|
||||||
//console.log('dst:', 'IPv4 127.0.0.1:' + port);
|
);
|
||||||
|
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);
|
||||||
|
|
||||||
if (!clients[id]) {
|
if (!clients[id]) {
|
||||||
clients[id] = true;
|
clients[id] = true;
|
||||||
if (!servername) {
|
if (!servername) {
|
||||||
throw new Error("no servername found for '" + id + "'");
|
throw new Error("no servername found for '" + id + "'");
|
||||||
}
|
}
|
||||||
//console.log("servername: '" + servername + "'", tun.name);
|
//console.log("servername: '" + servername + "'", tun.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
},
|
||||||
, onerror: function () {
|
onerror: function() {
|
||||||
throw new Error("Did not expect onerror");
|
throw new Error('Did not expect onerror');
|
||||||
}
|
},
|
||||||
, onend: function () {
|
onend: function() {
|
||||||
console.info('[onEnd]');
|
console.info('[onEnd]');
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var packts, packed;
|
var packts, packed;
|
||||||
|
@ -93,16 +125,16 @@ packts.push(packer.packHeader(getAddress(), null, 'end'));
|
||||||
packed = Buffer.concat(packts);
|
packed = Buffer.concat(packts);
|
||||||
|
|
||||||
if (!packed.equals(buf)) {
|
if (!packed.equals(buf)) {
|
||||||
console.error("");
|
console.error('');
|
||||||
console.error(buf.toString('hex') === packed.toString('hex'));
|
console.error(buf.toString('hex') === packed.toString('hex'));
|
||||||
console.error("");
|
console.error('');
|
||||||
console.error("auto-packed:");
|
console.error('auto-packed:');
|
||||||
console.error(packed.toString('hex'), packed.byteLength);
|
console.error(packed.toString('hex'), packed.byteLength);
|
||||||
console.error("");
|
console.error('');
|
||||||
console.error("hand-packed:");
|
console.error('hand-packed:');
|
||||||
console.error(buf.toString('hex'), buf.byteLength);
|
console.error(buf.toString('hex'), buf.byteLength);
|
||||||
console.error("");
|
console.error('');
|
||||||
throw new Error("packer (new) did not pack as expected");
|
throw new Error('packer (new) did not pack as expected');
|
||||||
}
|
}
|
||||||
|
|
||||||
packts = [];
|
packts = [];
|
||||||
|
@ -126,27 +158,26 @@ packed = Buffer.concat(packts);
|
||||||
// maching a few things on either side.
|
// maching a few things on either side.
|
||||||
//
|
//
|
||||||
// Only 6 bytes are changed - two 1 => 0, four ' ' => ''
|
// Only 6 bytes are changed - two 1 => 0, four ' ' => ''
|
||||||
var hex = packed.toString('hex')
|
var hex = packed
|
||||||
//.replace(/2c313939/, '2c30')
|
.toString('hex')
|
||||||
.replace(/32312c312c636f/, '32312c302c636f')
|
//.replace(/2c313939/, '2c30')
|
||||||
.replace(/3332312c312c656e64/, '3332312c302c656e64')
|
.replace(/32312c312c636f/, '32312c302c636f')
|
||||||
.replace(/7320/, '73')
|
.replace(/3332312c312c656e64/, '3332312c302c656e64')
|
||||||
.replace(/20$/, '')
|
.replace(/7320/, '73')
|
||||||
;
|
.replace(/20$/, '');
|
||||||
if (hex !== buf.toString('hex')) {
|
if (hex !== buf.toString('hex')) {
|
||||||
console.error("");
|
console.error('');
|
||||||
console.error(buf.toString('hex') === hex);
|
console.error(buf.toString('hex') === hex);
|
||||||
console.error("");
|
console.error('');
|
||||||
console.error("auto-packed:");
|
console.error('auto-packed:');
|
||||||
console.error(hex, packed.byteLength);
|
console.error(hex, packed.byteLength);
|
||||||
console.error("");
|
console.error('');
|
||||||
console.error("hand-packed:");
|
console.error('hand-packed:');
|
||||||
console.error(buf.toString('hex'), buf.byteLength);
|
console.error(buf.toString('hex'), buf.byteLength);
|
||||||
console.error("");
|
console.error('');
|
||||||
throw new Error("packer (old) did not pack as expected");
|
throw new Error('packer (old) did not pack as expected');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
console.info('');
|
console.info('');
|
||||||
|
|
||||||
// full message in one go
|
// full message in one go
|
||||||
|
@ -156,16 +187,14 @@ clients = {};
|
||||||
machine.fns.addChunk(buf);
|
machine.fns.addChunk(buf);
|
||||||
console.info('');
|
console.info('');
|
||||||
|
|
||||||
|
|
||||||
// messages one byte at a time
|
// messages one byte at a time
|
||||||
console.info('[BYTE-BY-BYTE BUFFER]', 1);
|
console.info('[BYTE-BY-BYTE BUFFER]', 1);
|
||||||
clients = {};
|
clients = {};
|
||||||
buf.forEach(function (byte) {
|
buf.forEach(function(byte) {
|
||||||
machine.fns.addChunk(Buffer.from([ byte ]));
|
machine.fns.addChunk(Buffer.from([byte]));
|
||||||
});
|
});
|
||||||
console.info('');
|
console.info('');
|
||||||
|
|
||||||
|
|
||||||
// split messages in overlapping thirds
|
// split messages in overlapping thirds
|
||||||
// 0-2 (2)
|
// 0-2 (2)
|
||||||
// 2-24 (22)
|
// 2-24 (22)
|
||||||
|
@ -173,26 +202,27 @@ console.info('');
|
||||||
// 223-225 (2)
|
// 223-225 (2)
|
||||||
// 225-247 (22)
|
// 225-247 (22)
|
||||||
// 247-446 (199)
|
// 247-446 (199)
|
||||||
buf = Buffer.concat([ buf, buf ]);
|
buf = Buffer.concat([buf, buf]);
|
||||||
console.info('[OVERLAPPING BUFFERS]', buf.length);
|
console.info('[OVERLAPPING BUFFERS]', buf.length);
|
||||||
clients = {};
|
clients = {};
|
||||||
[ buf.slice(0, 7) // version + header
|
[
|
||||||
, buf.slice(7, 14) // header
|
buf.slice(0, 7), // version + header
|
||||||
, buf.slice(14, 21) // header
|
buf.slice(7, 14), // header
|
||||||
, buf.slice(21, 28) // header + body
|
buf.slice(14, 21), // header
|
||||||
, buf.slice(28, 217) // body
|
buf.slice(21, 28), // header + body
|
||||||
, buf.slice(217, 224) // body + version
|
buf.slice(28, 217), // body
|
||||||
, buf.slice(224, 238) // version + header
|
buf.slice(217, 224), // body + version
|
||||||
, buf.slice(238, buf.byteLength) // header + body
|
buf.slice(224, 238), // version + header
|
||||||
].forEach(function (buf) {
|
buf.slice(238, buf.byteLength) // header + body
|
||||||
machine.fns.addChunk(Buffer.from(buf));
|
].forEach(function(buf) {
|
||||||
|
machine.fns.addChunk(Buffer.from(buf));
|
||||||
});
|
});
|
||||||
console.info('');
|
console.info('');
|
||||||
|
|
||||||
process.on('exit', function () {
|
process.on('exit', function() {
|
||||||
if (count !== 12) {
|
if (count !== 12) {
|
||||||
throw new Error("should have delivered 12 messages, not " + count);
|
throw new Error('should have delivered 12 messages, not ' + count);
|
||||||
}
|
}
|
||||||
console.info('TESTS PASS');
|
console.info('TESTS PASS');
|
||||||
console.info('');
|
console.info('');
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue