Compare commits
	
		
			17 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 85dad9d458 | |||
| 2b763f8606 | |||
| e699c44480 | |||
| 7da7746a31 | |||
| bcd332fea1 | |||
| d8dd3b32b4 | |||
| 9241604639 | |||
| ece89be3dd | |||
| ff93145be2 | |||
| 5524b7dcac | |||
| 2e4e73e48b | |||
| 178bd67375 | |||
| 
						 | 
					b8c423edca | ||
| 
						 | 
					407e7c21c6 | ||
| 
						 | 
					6ed367d3d7 | ||
| 
						 | 
					7f3a5b4f04 | ||
| 
						 | 
					ca885876d2 | 
							
								
								
									
										339
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										339
									
								
								README.md
									
									
									
									
									
								
							@ -1,6 +1,4 @@
 | 
				
			|||||||
# proxy-packer
 | 
					# proxy-packer | a [Root](https://rootprojects.org) project
 | 
				
			||||||
 | 
					 | 
				
			||||||
| Sponsored by [ppl](https://ppl.family) |
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
"The M-PROXY Protocol" for node.js
 | 
					"The M-PROXY Protocol" for node.js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -13,10 +11,11 @@ Browser <---- M-PROXY Service ----> Device
 | 
				
			|||||||
Browser <--/                   \--> Device
 | 
					Browser <--/                   \--> Device
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<small>Many clients may connect to a single device. A single client may connect to many devices.</small>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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).
 | 
				
			||||||
@ -48,141 +47,209 @@ data length              (string) the number of bytes in the wrapped packet, in
 | 
				
			|||||||
                                  These optional values can be very useful at the start of a new connection
 | 
					                                  These optional values can be very useful at the start of a new connection
 | 
				
			||||||
service name             (string) Either a standard service name (port + protocol), such as 'https'
 | 
					service name             (string) Either a standard service name (port + protocol), such as 'https'
 | 
				
			||||||
                                  as listed in /etc/services, otherwise 'tls', 'tcp', or 'udp' for generics
 | 
					                                  as listed in /etc/services, otherwise 'tls', 'tcp', or 'udp' for generics
 | 
				
			||||||
                                  Also 'control' is used for messages to the proxy (such as 'pause' events)
 | 
					                                  Also used for messages with the proxy (i.e. authentication)
 | 
				
			||||||
 | 
					                                    * 'control' for proxy<->server messages, including authentication, health, etc
 | 
				
			||||||
 | 
					                                    * 'connection' for a specific client
 | 
				
			||||||
 | 
					                                    * 'error' for a specific client
 | 
				
			||||||
 | 
					                                    * 'pause' to pause upload to a specific client (not the whole tunnel)
 | 
				
			||||||
 | 
					                                    * 'resume' to resume upload to a specific client (not the whole tunnel)
 | 
				
			||||||
service port             (string) The listening port, such as 443. Useful for non-standard or dynamic services.
 | 
					service port             (string) The listening port, such as 443. Useful for non-standard or dynamic services.
 | 
				
			||||||
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.
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
v1 is text-based. Future versions may be binary.
 | 
					## Tunneled TCP SNI Packet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
API
 | 
					You should see that the result is simply all of the original packet with a leading header.
 | 
				
			||||||
===
 | 
					
 | 
				
			||||||
 | 
					Note that `16 03 01 00` starts at the 29th byte (at index 28 or 0x1C) instead of at index 0:
 | 
				
			||||||
```js
 | 
					
 | 
				
			||||||
var Packer = require('proxy-packer');
 | 
					```
 | 
				
			||||||
```
 | 
					         0  1  2  3  4  5  6  7  8  9  A  B  C  D  D  F
 | 
				
			||||||
 | 
					0000000 fe 1a 49 50 76 34 2c 31 32 37 2e 30 2e 31 2e 31 <-- 0xfe = v1, 0x1a = 26 more bytes for header
 | 
				
			||||||
```js
 | 
					0000010 2c 34 34 33 2c 31 39 39 2c 66 6f 6f
 | 
				
			||||||
unpacker = Packer.create(handlers);                       // Create a state machine for unpacking
 | 
					                                            16 03 01 00 <-- first 4 bytes of tcp packet
 | 
				
			||||||
 | 
					0000020 c2 01 00 00 be 03 03 57 e3 76 50 66 03 df 99 76
 | 
				
			||||||
handlers.oncontrol = function (tun) { }                   // for communicating with the proxy
 | 
					0000030 24 c8 31 e6 e8 08 34 6b b4 7b bb 2c f3 17 aa 5c
 | 
				
			||||||
                                                          // tun.data is an array
 | 
					0000040 ec 09 da da 83 5a b2 00 00 56 00 ff c0 24 c0 23
 | 
				
			||||||
                                                          //     '[ -1, "[Error] bad hello" ]'
 | 
					0000050 c0 0a c0 09 c0 08 c0 28 c0 27 c0 14 c0 13 c0 12
 | 
				
			||||||
                                                          //     '[ 0, "[Error] out-of-band error message" ]'
 | 
					0000060 c0 26 c0 25 c0 05 c0 04 c0 03 c0 2a c0 29 c0 0f
 | 
				
			||||||
                                                          //     '[ 1, "hello", 254, [ "add_token", "delete_token" ] ]'
 | 
					0000070 c0 0e c0 0d 00 6b 00 67 00 39 00 33 00 16 00 3d
 | 
				
			||||||
                                                          //     '[ 1, "add_token" ]'
 | 
					0000080 00 3c 00 35 00 2f 00 0a c0 07 c0 11 c0 02 c0 0c
 | 
				
			||||||
                                                          //     '[ 1, "delete_token" ]'
 | 
					0000090 00 05 00 04 00 af 00 ae 00 8d 00 8c 00 8a 00 8b
 | 
				
			||||||
 | 
					00000a0 01 00 00 3f 00 00 00 19 00 17 00 00 14 70 6f 6b
 | 
				
			||||||
handlers.onmessage = function (tun) { }                   // a client has sent a message
 | 
					00000b0 65 6d 61 70 2e 68 65 6c 6c 61 62 69 74 2e 63 6f
 | 
				
			||||||
                                                          // tun = { family, address, port, data
 | 
					00000c0 6d 00 0a 00 08 00 06 00 17 00 18 00 19 00 0b 00
 | 
				
			||||||
                                                          //       , service, serviceport, name };
 | 
					00000d0 02 01 00 00 0d 00 0c 00 0a 05 01 04 01 02 01 04
 | 
				
			||||||
 | 
					00000e0 03 02 03
 | 
				
			||||||
handlers.onpause = function (tun) { }                     // proxy requests to pause upload to a client
 | 
					00000e3
 | 
				
			||||||
                                                          // tun = { family, address, port };
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
handlers.onresume = function (tun) { }                    // proxy requests to resume upload to a client
 | 
					The v1 header uses strings for address and service descriptor information,
 | 
				
			||||||
                                                          // tun = { family, address, port };
 | 
					but future versions may be binary.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
handlers.onend = function (tun) { }                       // proxy requests to close a client's socket
 | 
					# API
 | 
				
			||||||
                                                          // tun = { family, address, port };
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
handlers.onerror = function (err) { }                     // proxy is relaying a client's error
 | 
					var Packer = require('proxy-packer');
 | 
				
			||||||
                                                          // err = { message, family, address, port };
 | 
					```
 | 
				
			||||||
```
 | 
					
 | 
				
			||||||
 | 
					## Unpacker / Parser State Machine
 | 
				
			||||||
<!--
 | 
					
 | 
				
			||||||
TODO
 | 
					The unpacker creates a state machine.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
handlers.onconnect = function (tun) { }                   // a new client has connected
 | 
					Each data chunk going in must be in sequence (tcp guarantees this),
 | 
				
			||||||
 | 
					composing a full message with header and data (unless data length is 0).
 | 
				
			||||||
-->
 | 
					
 | 
				
			||||||
 | 
					The state machine progresses through these states:
 | 
				
			||||||
```js
 | 
					
 | 
				
			||||||
var chunk = Packer.pack(tun, data);                       // Add M-PROXY header to data
 | 
					-   version
 | 
				
			||||||
                                                          // tun = { family, address, port
 | 
					-   headerLength
 | 
				
			||||||
                                                          //       , service, serviceport, name }
 | 
					-   header
 | 
				
			||||||
 | 
					-   data
 | 
				
			||||||
var addr = Packer.socketToAddr(socket);                   // Probe raw, raw socket for address info
 | 
					
 | 
				
			||||||
 | 
					At the end of the data event (which may or may not contain a buffer of data)
 | 
				
			||||||
var id = Packer.addrToId(address);                        // Turn M-PROXY address info into a deterministic id
 | 
					one of the appropriate handlers will be called.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var id = Packer.socketToId(socket);                       // Turn raw, raw socket info into a deterministic id
 | 
					-   control
 | 
				
			||||||
```
 | 
					-   connection
 | 
				
			||||||
 | 
					-   message
 | 
				
			||||||
## API Helpers
 | 
					-   pause
 | 
				
			||||||
 | 
					-   resume
 | 
				
			||||||
```js
 | 
					-   end
 | 
				
			||||||
var socket = Packer.Stream.wrapSocket(socketOrStream);   // workaround for https://github.com/nodejs/node/issues/8854
 | 
					-   error
 | 
				
			||||||
                                                         // which was just closed recently, but probably still needs
 | 
					
 | 
				
			||||||
                                                         // something more like this (below) to work as intended
 | 
					```js
 | 
				
			||||||
                                                         // https://github.com/findhit/proxywrap/blob/master/lib/proxywrap.js
 | 
					unpacker = Packer.create(handlers); // Create a state machine for unpacking
 | 
				
			||||||
```
 | 
					
 | 
				
			||||||
 | 
					unpacker.fns.addData(chunk); // process a chunk of data
 | 
				
			||||||
```js
 | 
					
 | 
				
			||||||
var myTransform = Packer.Transform.create({
 | 
					handlers.oncontrol = function(tun) {}; // for communicating with the proxy
 | 
				
			||||||
  address: {
 | 
					// tun.data is an array
 | 
				
			||||||
    family: '...'
 | 
					//     '[ -1, "[Error] bad hello" ]'
 | 
				
			||||||
  , address: '...'
 | 
					//     '[ 0, "[Error] out-of-band error message" ]'
 | 
				
			||||||
  , port: '...'
 | 
					//     '[ 1, "hello", 254, [ "add_token", "delete_token" ] ]'
 | 
				
			||||||
  }
 | 
					//     '[ 1, "add_token" ]'
 | 
				
			||||||
  // hint at the service to be used
 | 
					//     '[ 1, "delete_token" ]'
 | 
				
			||||||
, service: 'https'
 | 
					
 | 
				
			||||||
});
 | 
					handlers.onconnection = function(tun) {}; // a client has established a connection
 | 
				
			||||||
```
 | 
					
 | 
				
			||||||
 | 
					handlers.onmessage = function(tun) {}; // a client has sent a message
 | 
				
			||||||
# Testing an implementation
 | 
					// tun = { family, address, port, data
 | 
				
			||||||
 | 
					//       , service, serviceport, name };
 | 
				
			||||||
If you want to write a compatible packer, just make sure that for any given input
 | 
					
 | 
				
			||||||
you get the same output as the packer does.
 | 
					handlers.onpause = function(tun) {}; // proxy requests to pause upload to a client
 | 
				
			||||||
 | 
					// tun = { family, address, port };
 | 
				
			||||||
```bash
 | 
					
 | 
				
			||||||
node test/pack.js input.json output.bin
 | 
					handlers.onresume = function(tun) {}; // proxy requests to resume upload to a client
 | 
				
			||||||
hexdump output.bin
 | 
					// tun = { family, address, port };
 | 
				
			||||||
```
 | 
					
 | 
				
			||||||
 | 
					handlers.onend = function(tun) {}; // proxy requests to close a client's socket
 | 
				
			||||||
Where `input.json` looks something like this:
 | 
					// tun = { family, address, port };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`input.json`:
 | 
					handlers.onerror = function(err) {}; // proxy is relaying a client's error
 | 
				
			||||||
```
 | 
					// err = { message, family, address, port };
 | 
				
			||||||
{ "version": 1
 | 
					```
 | 
				
			||||||
, "address": {
 | 
					
 | 
				
			||||||
    "family": "IPv4"
 | 
					<!--
 | 
				
			||||||
  , "address": "127.0.1.1"
 | 
					TODO
 | 
				
			||||||
  , "port": 4321
 | 
					
 | 
				
			||||||
  , "service": "foo"
 | 
					handlers.onconnect = function (tun) { }                   // a new client has connected
 | 
				
			||||||
  , "serviceport": 443
 | 
					
 | 
				
			||||||
  , "name": 'example.com'
 | 
					-->
 | 
				
			||||||
  }
 | 
					
 | 
				
			||||||
, "filepath": "./sni.tcp.bin"
 | 
					## Packer & Extras
 | 
				
			||||||
}
 | 
					
 | 
				
			||||||
```
 | 
					Packs header metadata about connection into a buffer (potentially with original data), ready to send.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Raw TCP SNI Packet
 | 
					```js
 | 
				
			||||||
------------------
 | 
					var headerAndBody = Packer.pack(tun, data); // Add M-PROXY header to data
 | 
				
			||||||
 | 
					// tun = { family, address, port
 | 
				
			||||||
and `sni.tcp.bin` is any captured tcp packet, such as this one with a tls hello:
 | 
					//       , service, serviceport, name }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`sni.tcp.bin`:
 | 
					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)
 | 
				
			||||||
         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
 | 
					var addr = Packer.socketToAddr(socket); // Probe raw, raw socket for address info
 | 
				
			||||||
0000010 03 df 99 76 24 c8 31 e6 e8 08 34 6b b4 7b bb 2c
 | 
					
 | 
				
			||||||
0000020 f3 17 aa 5c ec 09 da da 83 5a b2 00 00 56 00 ff
 | 
					var id = Packer.addrToId(address); // Turn M-PROXY address info into a deterministic id
 | 
				
			||||||
0000030 c0 24 c0 23 c0 0a c0 09 c0 08 c0 28 c0 27 c0 14
 | 
					
 | 
				
			||||||
0000040 c0 13 c0 12 c0 26 c0 25 c0 05 c0 04 c0 03 c0 2a
 | 
					var id = Packer.socketToId(socket); // Turn raw, raw socket info into a deterministic id
 | 
				
			||||||
0000050 c0 29 c0 0f c0 0e c0 0d 00 6b 00 67 00 39 00 33
 | 
					```
 | 
				
			||||||
0000060 00 16 00 3d 00 3c 00 35 00 2f 00 0a c0 07 c0 11
 | 
					
 | 
				
			||||||
0000070 c0 02 c0 0c 00 05 00 04 00 af 00 ae 00 8d 00 8c
 | 
					## API Helpers
 | 
				
			||||||
0000080 00 8a 00 8b 01 00 00 3f 00 00 00 19 00 17 00 00
 | 
					
 | 
				
			||||||
0000090 14 70 6f 6b 65 6d 61 70 2e 68 65 6c 6c 61 62 69
 | 
					```js
 | 
				
			||||||
00000a0 74 2e 63 6f 6d 00 0a 00 08 00 06 00 17 00 18 00
 | 
					var socket = Packer.Stream.wrapSocket(socketOrStream); // workaround for https://github.com/nodejs/node/issues/8854
 | 
				
			||||||
00000b0 19 00 0b 00 02 01 00 00 0d 00 0c 00 0a 05 01 04
 | 
					// which was just closed recently, but probably still needs
 | 
				
			||||||
00000c0 01 02 01 04 03 02 03
 | 
					// something more like this (below) to work as intended
 | 
				
			||||||
00000c7
 | 
					// https://github.com/findhit/proxywrap/blob/master/lib/proxywrap.js
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Tunneled TCP SNI Packet
 | 
					```js
 | 
				
			||||||
-----------------------
 | 
					var myTransform = Packer.Transform.create({
 | 
				
			||||||
 | 
						address: {
 | 
				
			||||||
 | 
							family: '...',
 | 
				
			||||||
 | 
							address: '...',
 | 
				
			||||||
 | 
							port: '...'
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						// hint at the service to be used
 | 
				
			||||||
 | 
						service: 'https'
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Testing an implementation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you want to write a compatible packer, just make sure that for any given input
 | 
				
			||||||
 | 
					you get the same output as the packer does.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					node test/pack.js input.json output.bin
 | 
				
			||||||
 | 
					hexdump output.bin
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Where `input.json` looks something like this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`input.json`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{ "version": 1
 | 
				
			||||||
 | 
					, "address": {
 | 
				
			||||||
 | 
					    "family": "IPv4"
 | 
				
			||||||
 | 
					  , "address": "127.0.1.1"
 | 
				
			||||||
 | 
					  , "port": 4321
 | 
				
			||||||
 | 
					  , "service": "foo"
 | 
				
			||||||
 | 
					  , "serviceport": 443
 | 
				
			||||||
 | 
					  , "name": 'example.com'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					, "filepath": "./sni.tcp.bin"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Raw TCP SNI Packet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					and `sni.tcp.bin` is any captured tcp packet, such as this one with a tls hello:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`sni.tcp.bin`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					         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
 | 
				
			||||||
 | 
					0000010 03 df 99 76 24 c8 31 e6 e8 08 34 6b b4 7b bb 2c
 | 
				
			||||||
 | 
					0000020 f3 17 aa 5c ec 09 da da 83 5a b2 00 00 56 00 ff
 | 
				
			||||||
 | 
					0000030 c0 24 c0 23 c0 0a c0 09 c0 08 c0 28 c0 27 c0 14
 | 
				
			||||||
 | 
					0000040 c0 13 c0 12 c0 26 c0 25 c0 05 c0 04 c0 03 c0 2a
 | 
				
			||||||
 | 
					0000050 c0 29 c0 0f c0 0e c0 0d 00 6b 00 67 00 39 00 33
 | 
				
			||||||
 | 
					0000060 00 16 00 3d 00 3c 00 35 00 2f 00 0a c0 07 c0 11
 | 
				
			||||||
 | 
					0000070 c0 02 c0 0c 00 05 00 04 00 af 00 ae 00 8d 00 8c
 | 
				
			||||||
 | 
					0000080 00 8a 00 8b 01 00 00 3f 00 00 00 19 00 17 00 00
 | 
				
			||||||
 | 
					0000090 14 70 6f 6b 65 6d 61 70 2e 68 65 6c 6c 61 62 69
 | 
				
			||||||
 | 
					00000a0 74 2e 63 6f 6d 00 0a 00 08 00 06 00 17 00 18 00
 | 
				
			||||||
 | 
					00000b0 19 00 0b 00 02 01 00 00 0d 00 0c 00 0a 05 01 04
 | 
				
			||||||
 | 
					00000c0 01 02 01 04 03 02 03
 | 
				
			||||||
 | 
					00000c7
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										665
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										665
									
								
								index.js
									
									
									
									
									
								
							@ -3,266 +3,376 @@
 | 
				
			|||||||
var Packer = module.exports;
 | 
					var Packer = module.exports;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var serviceEvents = {
 | 
					var serviceEvents = {
 | 
				
			||||||
  default: 'tunnelData'
 | 
						default: 'tunnelData',
 | 
				
			||||||
, control: 'tunnelControl'
 | 
						connection: 'tunnelConnection',
 | 
				
			||||||
, error:   'tunnelError'
 | 
						control: 'tunnelControl',
 | 
				
			||||||
, end:     'tunnelEnd'
 | 
						error: 'tunnelError',
 | 
				
			||||||
, pause:   'tunnelPause'
 | 
						end: 'tunnelEnd',
 | 
				
			||||||
, resume:  'tunnelResume'
 | 
						pause: 'tunnelPause',
 | 
				
			||||||
 | 
						resume: 'tunnelResume'
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
var serviceFuncs = {
 | 
					var serviceFuncs = {
 | 
				
			||||||
  default: 'onmessage'
 | 
						default: 'onmessage',
 | 
				
			||||||
, control: 'oncontrol'
 | 
						connection: 'onconnection',
 | 
				
			||||||
, error:   'onerror'
 | 
						control: 'oncontrol',
 | 
				
			||||||
, end:     'onend'
 | 
						error: 'onerror',
 | 
				
			||||||
, pause:   'onpause'
 | 
						end: 'onend',
 | 
				
			||||||
, resume:  'onresume'
 | 
						pause: 'onpause',
 | 
				
			||||||
 | 
						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.onerror   = opts.onerror   || opts.onError;
 | 
						machine.onconnection =
 | 
				
			||||||
  machine.onend     = opts.onend     || opts.onEnd;
 | 
							opts.onconnection || opts.onConnection || function() {};
 | 
				
			||||||
  machine.onpause   = opts.onpause   || opts.onPause;
 | 
						machine.onerror = opts.onerror || opts.onError;
 | 
				
			||||||
  machine.onresume  = opts.onresume  || opts.onResume;
 | 
						machine.onend = opts.onend || opts.onEnd;
 | 
				
			||||||
 | 
						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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (size <= 0) {
 | 
							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];
 | 
					 | 
				
			||||||
    //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 = machine.fns.collectData(chunk, machine.bodyLen);
 | 
							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';
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // We don't have the entire body yet so return false.
 | 
							msg.family = machine.family;
 | 
				
			||||||
    if (!data) {
 | 
							msg.address = machine.address;
 | 
				
			||||||
      return false;
 | 
							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 = data.toString();
 | 
					 | 
				
			||||||
        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 (machine.emit) {
 | 
							return true;
 | 
				
			||||||
      machine.emit(serviceEvents[msg.service] || serviceEvents.default);
 | 
						};
 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      (machine[serviceFuncs[msg.service]] || machine[serviceFuncs.default])(msg);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return true;
 | 
						machine.state = 0;
 | 
				
			||||||
  };
 | 
						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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  machine.state = 0;
 | 
								if (true === machine.fns[machine.states[machine.state]](chunk)) {
 | 
				
			||||||
  machine.states = ['version', 'headerLength', 'header', 'data'];
 | 
									machine.state += 1;
 | 
				
			||||||
  machine.fns.addChunk = function (chunk) {
 | 
									machine.state %= machine.states.length;
 | 
				
			||||||
    //console.log('');
 | 
								}
 | 
				
			||||||
    //console.log('[addChunk]');
 | 
							}
 | 
				
			||||||
    machine.chunkIndex = 0;
 | 
							if ('data' === machine.states[machine.state] && 0 === machine.bodyLen) {
 | 
				
			||||||
    while (machine.chunkIndex < chunk.length) {
 | 
								machine.fns[machine.states[machine.state]](chunk);
 | 
				
			||||||
      //console.log('chunkIndex:', machine.chunkIndex, 'state:', machine.state);
 | 
								machine.state += 1;
 | 
				
			||||||
 | 
								machine.state %= machine.states.length;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (true === machine.fns[machine.states[machine.state]](chunk)) {
 | 
						return machine;
 | 
				
			||||||
        machine.state += 1;
 | 
					 | 
				
			||||||
        machine.state %= machine.states.length;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return machine;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Packer.pack = function (meta, data, service) {
 | 
					Packer.packHeader = function(meta, data, service, andBody, oldways) {
 | 
				
			||||||
  data = data || Buffer.from(' ');
 | 
						if (oldways && !data) {
 | 
				
			||||||
  if (!Buffer.isBuffer(data)) {
 | 
							data = Buffer.from(' ');
 | 
				
			||||||
    data = new Buffer(JSON.stringify(data));
 | 
						}
 | 
				
			||||||
  }
 | 
						if (data && !Buffer.isBuffer(data)) {
 | 
				
			||||||
  if (!data.byteLength) {
 | 
							data = Buffer.from(JSON.stringify(data));
 | 
				
			||||||
    data = Buffer.from(' ');
 | 
						}
 | 
				
			||||||
  }
 | 
						if (oldways && !data.byteLength) {
 | 
				
			||||||
 | 
							data = Buffer.from(' ');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (service && service !== 'control') {
 | 
						if (service && -1 === ['control', 'connection'].indexOf(service)) {
 | 
				
			||||||
    meta.service = service;
 | 
							//console.log('end?', service);
 | 
				
			||||||
  }
 | 
							meta.service = service;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var version = 1;
 | 
						var size = (data && data.byteLength) || 0;
 | 
				
			||||||
  var header;
 | 
						var sizeReserve = andBody ? size : 0;
 | 
				
			||||||
  if (service === 'control') {
 | 
						var version = 1;
 | 
				
			||||||
    header = Buffer.from(['', '', '', data.byteLength, service].join(','));
 | 
						var header;
 | 
				
			||||||
  }
 | 
						if (service === 'control') {
 | 
				
			||||||
  else {
 | 
							header = Buffer.from(['', '', '', size, service].join(','));
 | 
				
			||||||
    header = Buffer.from([
 | 
						} else if (service === 'connection') {
 | 
				
			||||||
      meta.family, meta.address, meta.port, data.byteLength,
 | 
							header = Buffer.from(
 | 
				
			||||||
      (meta.service || ''), (meta.serviceport || ''), (meta.name || '')
 | 
								[
 | 
				
			||||||
    ].join(','));
 | 
									meta.family,
 | 
				
			||||||
  }
 | 
									meta.address,
 | 
				
			||||||
  var metaBuf = Buffer.from([ 255 - version, header.length ]);
 | 
									meta.port,
 | 
				
			||||||
  var buf = Buffer.alloc(metaBuf.byteLength + header.byteLength + data.byteLength);
 | 
									size,
 | 
				
			||||||
 | 
									'connection',
 | 
				
			||||||
 | 
									meta.serviceport || '',
 | 
				
			||||||
 | 
									meta.name || '',
 | 
				
			||||||
 | 
									meta.service || ''
 | 
				
			||||||
 | 
								].join(',')
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							header = Buffer.from(
 | 
				
			||||||
 | 
								[
 | 
				
			||||||
 | 
									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);
 | 
				
			||||||
  data.copy(buf, 2 + header.byteLength);
 | 
						if (sizeReserve) {
 | 
				
			||||||
 | 
							data.copy(buf, 2 + header.byteLength);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return buf;
 | 
						return buf;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					Packer.pack = function(meta, data, service) {
 | 
				
			||||||
 | 
						return Packer.packHeader(meta, data, service, true, true);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function extractSocketProp(socket, propName) {
 | 
					function extractSocketProps(socket, propNames) {
 | 
				
			||||||
  // remoteAddress, remotePort... ugh... https://github.com/nodejs/node/issues/8854
 | 
						var props = {};
 | 
				
			||||||
  var value = socket[propName] || socket['_' + propName];
 | 
					 | 
				
			||||||
  try {
 | 
					 | 
				
			||||||
    value = value || socket._handle._parent.owner.stream[propName];
 | 
					 | 
				
			||||||
  } catch (e) {}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  try {
 | 
						if (socket.remotePort) {
 | 
				
			||||||
    value = value || socket._handle._parentWrap[propName];
 | 
							propNames.forEach(function(propName) {
 | 
				
			||||||
    value = value || socket._handle._parentWrap._handle.owner.stream[propName];
 | 
								props[propName] = socket[propName];
 | 
				
			||||||
  } catch (e) {}
 | 
							});
 | 
				
			||||||
 | 
						} else if (socket._remotePort) {
 | 
				
			||||||
  return value || '';
 | 
							propNames.forEach(function(propName) {
 | 
				
			||||||
 | 
								props[propName] = socket['_' + propName];
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						} else if (socket._handle) {
 | 
				
			||||||
 | 
							if (
 | 
				
			||||||
 | 
								socket._handle._parent &&
 | 
				
			||||||
 | 
								socket._handle._parent.owner &&
 | 
				
			||||||
 | 
								socket._handle._parent.owner.stream &&
 | 
				
			||||||
 | 
								socket._handle._parent.owner.stream.remotePort
 | 
				
			||||||
 | 
							) {
 | 
				
			||||||
 | 
								propNames.forEach(function(propName) {
 | 
				
			||||||
 | 
									props[propName] = socket._handle._parent.owner.stream[propName];
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							} else if (
 | 
				
			||||||
 | 
								socket._handle._parentWrap &&
 | 
				
			||||||
 | 
								socket._handle._parentWrap.remotePort
 | 
				
			||||||
 | 
							) {
 | 
				
			||||||
 | 
								propNames.forEach(function(propName) {
 | 
				
			||||||
 | 
									props[propName] = socket._handle._parentWrap[propName];
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							} else if (
 | 
				
			||||||
 | 
								socket._handle._parentWrap &&
 | 
				
			||||||
 | 
								socket._handle._parentWrap._handle &&
 | 
				
			||||||
 | 
								socket._handle._parentWrap._handle.owner &&
 | 
				
			||||||
 | 
								socket._handle._parentWrap._handle.owner.stream &&
 | 
				
			||||||
 | 
								socket._handle._parentWrap._handle.owner.stream.remotePort
 | 
				
			||||||
 | 
							) {
 | 
				
			||||||
 | 
								propNames.forEach(function(propName) {
 | 
				
			||||||
 | 
									props[propName] =
 | 
				
			||||||
 | 
										socket._handle._parentWrap._handle.owner.stream[propName];
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return props;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
Packer.socketToAddr = function (socket) {
 | 
					function extractSocketProp(socket, propName) {
 | 
				
			||||||
  // TODO BUG XXX
 | 
						// remoteAddress, remotePort... ugh... https://github.com/nodejs/node/issues/8854
 | 
				
			||||||
  // https://github.com/nodejs/node/issues/8854
 | 
						var value = socket[propName] || socket['_' + propName];
 | 
				
			||||||
  // tlsSocket.remoteAddress = remoteAddress; // causes core dump
 | 
						try {
 | 
				
			||||||
  // console.log(tlsSocket.remoteAddress);
 | 
							value = value || socket._handle._parent.owner.stream[propName];
 | 
				
			||||||
 | 
						} catch (e) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {
 | 
						try {
 | 
				
			||||||
    family:  extractSocketProp(socket, 'remoteFamily')
 | 
							value = value || socket._handle._parentWrap[propName];
 | 
				
			||||||
  , address: extractSocketProp(socket, 'remoteAddress')
 | 
							value =
 | 
				
			||||||
  , port:    extractSocketProp(socket, 'remotePort')
 | 
								value || socket._handle._parentWrap._handle.owner.stream[propName];
 | 
				
			||||||
  };
 | 
						} catch (e) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return value || '';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					Packer.socketToAddr = function(socket) {
 | 
				
			||||||
 | 
						// TODO BUG XXX
 | 
				
			||||||
 | 
						// https://github.com/nodejs/node/issues/8854
 | 
				
			||||||
 | 
						// tlsSocket.remoteAddress = remoteAddress; // causes core dump
 | 
				
			||||||
 | 
						// console.log(tlsSocket.remoteAddress);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var props = extractSocketProps(socket, [
 | 
				
			||||||
 | 
							'remoteFamily',
 | 
				
			||||||
 | 
							'remoteAddress',
 | 
				
			||||||
 | 
							'remotePort',
 | 
				
			||||||
 | 
							'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 = [
 | 
				
			||||||
  'address'
 | 
					  'address'
 | 
				
			||||||
, 'destroy'
 | 
					, 'destroy'
 | 
				
			||||||
@ -273,9 +383,23 @@ var sockFuncs = [
 | 
				
			|||||||
, 'setNoDelay'
 | 
					, 'setNoDelay'
 | 
				
			||||||
, 'setTimeout'
 | 
					, 'setTimeout'
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
// Improved workaround for  https://github.com/nodejs/node/issues/8854
 | 
					*/
 | 
				
			||||||
// 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
 | 
				
			||||||
 | 
						addressNames.forEach(function(name) {
 | 
				
			||||||
 | 
							Object.defineProperty(socket, name, {
 | 
				
			||||||
 | 
								enumerable: false,
 | 
				
			||||||
 | 
								configurable: true,
 | 
				
			||||||
 | 
								get: function() {
 | 
				
			||||||
 | 
									return extractSocketProp(socket, name);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						return socket;
 | 
				
			||||||
 | 
						// Improved workaround for  https://github.com/nodejs/node/issues/8854
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
					  // 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) {
 | 
				
			||||||
    myDuplex[name] = extractSocketProp(socket, name);
 | 
					    myDuplex[name] = extractSocketProp(socket, name);
 | 
				
			||||||
@ -317,85 +441,86 @@ Packer.wrapSocket = function (socket) {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return myDuplex;
 | 
					  return myDuplex;
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var Transform = require('stream').Transform;
 | 
					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": "1.4.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)"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					})();
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										263
									
								
								test/parse.js
									
									
									
									
									
								
							
							
						
						
									
										263
									
								
								test/parse.js
									
									
									
									
									
								
							@ -3,87 +3,197 @@
 | 
				
			|||||||
var sni = require('sni');
 | 
					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;
 | 
				
			||||||
var address = {
 | 
					function getAddress() {
 | 
				
			||||||
  family: 'IPv4'
 | 
						return {
 | 
				
			||||||
, address: '127.0.1.1'
 | 
							family: 'IPv4',
 | 
				
			||||||
, port: 4321
 | 
							address: '127.0.1.1',
 | 
				
			||||||
, service: 'foo-https'
 | 
							port: 4321,
 | 
				
			||||||
, serviceport: 443
 | 
							service: 'foo-https',
 | 
				
			||||||
, name: 'foo-pokemap.hellabit.com'
 | 
							serviceport: 443,
 | 
				
			||||||
};
 | 
							name: 'foo-pokemap.hellabit.com'
 | 
				
			||||||
var header = address.family + ',' + address.address + ',' + address.port + ',' + hello.byteLength
 | 
						};
 | 
				
			||||||
  + ',' + (address.service || '') + ',' + (address.serviceport || '') + ',' + (address.name || '')
 | 
					}
 | 
				
			||||||
  ;
 | 
					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([
 | 
					var buf = Buffer.concat([
 | 
				
			||||||
  Buffer.from([ 255 - version, header.length ])
 | 
						Buffer.from([255 - version, connectionHeader.length]),
 | 
				
			||||||
, Buffer.from(header)
 | 
						Buffer.from(connectionHeader),
 | 
				
			||||||
, hello
 | 
						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 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({
 | 
				
			||||||
  onmessage: function (tun) {
 | 
						onconnection: function(tun) {
 | 
				
			||||||
    var id = tun.family + ',' + tun.address + ',' + tun.port;
 | 
							console.info('');
 | 
				
			||||||
    var service = 'https';
 | 
							if (!tun.service || 'connection' === tun.service) {
 | 
				
			||||||
    var port = services[service];
 | 
								throw new Error('missing service: ' + JSON.stringify(tun));
 | 
				
			||||||
    var servername = sni(tun.data);
 | 
							}
 | 
				
			||||||
 | 
							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.info(
 | 
				
			||||||
    console.log('[onMessage]');
 | 
								'[onMessage]',
 | 
				
			||||||
    if (!tun.data.equals(hello)) {
 | 
								service,
 | 
				
			||||||
      throw new Error("'data' packet is not equal to original 'hello' packet");
 | 
								port,
 | 
				
			||||||
    }
 | 
								servername,
 | 
				
			||||||
    console.log('all', tun.data.byteLength, 'bytes are equal');
 | 
								tun.data.byteLength
 | 
				
			||||||
    console.log('src:', tun.family, tun.address + ':' + tun.port + ':' + tun.serviceport);
 | 
							);
 | 
				
			||||||
    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() {
 | 
				
			||||||
    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)) {
 | 
					if (!packed.equals(buf)) {
 | 
				
			||||||
  console.error(buf.toString('hex') === packed.toString('hex'));
 | 
						console.error('');
 | 
				
			||||||
  console.error(packed.toString('hex'), packed.byteLength);
 | 
						console.error(buf.toString('hex') === packed.toString('hex'));
 | 
				
			||||||
  console.error(buf.toString('hex'), buf.byteLength);
 | 
						console.error('');
 | 
				
			||||||
  throw new Error("packer did not pack as expected");
 | 
						console.error('auto-packed:');
 | 
				
			||||||
 | 
						console.error(packed.toString('hex'), packed.byteLength);
 | 
				
			||||||
 | 
						console.error('');
 | 
				
			||||||
 | 
						console.error('hand-packed:');
 | 
				
			||||||
 | 
						console.error(buf.toString('hex'), buf.byteLength);
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
console.log('');
 | 
					// 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.info('');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// full message in one go
 | 
					// full message in one go
 | 
				
			||||||
// 223 = 2 + 22 + 199
 | 
					// 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 = {};
 | 
					clients = {};
 | 
				
			||||||
machine.fns.addChunk(buf);
 | 
					machine.fns.addChunk(buf);
 | 
				
			||||||
console.log('');
 | 
					console.info('');
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// messages one byte at a time
 | 
					// messages one byte at a time
 | 
				
			||||||
console.log('[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.log('');
 | 
					console.info('');
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// split messages in overlapping thirds
 | 
					// split messages in overlapping thirds
 | 
				
			||||||
// 0-2      (2)
 | 
					// 0-2      (2)
 | 
				
			||||||
@ -92,26 +202,27 @@ console.log('');
 | 
				
			|||||||
// 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.log('[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.log('');
 | 
					console.info('');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
process.on('exit', function () {
 | 
					process.on('exit', function() {
 | 
				
			||||||
  if (count !== 4) {
 | 
						if (count !== 12) {
 | 
				
			||||||
    throw new Error("should have delivered 4 messages, not", count);
 | 
							throw new Error('should have delivered 12 messages, not ' + count);
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
  console.log('TESTS PASS');
 | 
						console.info('TESTS PASS');
 | 
				
			||||||
  console.log('');
 | 
						console.info('');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user