Compare commits
	
		
			No commits in common. "master" and "v2.0.2" have entirely different histories.
		
	
	
		
	
		
							
								
								
									
										129
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										129
									
								
								README.md
									
									
									
									
									
								
							| @ -1,4 +1,6 @@ | ||||
| # proxy-packer | a [Root](https://rootprojects.org) project | ||||
| # proxy-packer | ||||
| 
 | ||||
| | Sponsored by [ppl](https://ppl.family) | | ||||
| 
 | ||||
| "The M-PROXY Protocol" for node.js | ||||
| 
 | ||||
| @ -15,7 +17,8 @@ Browser <--/                   \--> Device | ||||
| 
 | ||||
| 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 | ||||
| tunneled over arbitrary streams (such as WebSockets). | ||||
| @ -57,7 +60,8 @@ 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. | ||||
| ``` | ||||
| 
 | ||||
| ## 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. | ||||
| 
 | ||||
| @ -87,13 +91,15 @@ 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, | ||||
| but future versions may be binary. | ||||
| 
 | ||||
| # API | ||||
| API | ||||
| === | ||||
| 
 | ||||
| ```js | ||||
| var Packer = require('proxy-packer'); | ||||
| ``` | ||||
| 
 | ||||
| ## Unpacker / Parser State Machine | ||||
| Unpacker / Parser State Machine | ||||
| ----------------------- | ||||
| 
 | ||||
| The unpacker creates a state machine. | ||||
| 
 | ||||
| @ -102,52 +108,52 @@ composing a full message with header and data (unless data length is 0). | ||||
| 
 | ||||
| The state machine progresses through these states: | ||||
| 
 | ||||
| -   version | ||||
| -   headerLength | ||||
| -   header | ||||
| -   data | ||||
| * version | ||||
| * headerLength | ||||
| * header | ||||
| * 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. | ||||
| 
 | ||||
| -   control | ||||
| -   connection | ||||
| -   message | ||||
| -   pause | ||||
| -   resume | ||||
| -   end | ||||
| -   error | ||||
| * control | ||||
| * connection | ||||
| * message | ||||
| * pause | ||||
| * resume | ||||
| * end | ||||
| * error | ||||
| 
 | ||||
| ```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 | ||||
| // tun.data is an array | ||||
| //     '[ -1, "[Error] bad hello" ]' | ||||
| //     '[ 0, "[Error] out-of-band error message" ]' | ||||
| //     '[ 1, "hello", 254, [ "add_token", "delete_token" ] ]' | ||||
| //     '[ 1, "add_token" ]' | ||||
| //     '[ 1, "delete_token" ]' | ||||
| handlers.oncontrol = function (tun) { }                   // for communicating with the proxy | ||||
|                                                           // tun.data is an array | ||||
|                                                           //     '[ -1, "[Error] bad hello" ]' | ||||
|                                                           //     '[ 0, "[Error] out-of-band error message" ]' | ||||
|                                                           //     '[ 1, "hello", 254, [ "add_token", "delete_token" ] ]' | ||||
|                                                           //     '[ 1, "add_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 | ||||
| // tun = { family, address, port, data | ||||
| //       , service, serviceport, name }; | ||||
| handlers.onmessage = function (tun) { }                   // a client has sent a message | ||||
|                                                           // tun = { family, address, port, data | ||||
|                                                           //       , service, serviceport, name }; | ||||
| 
 | ||||
| handlers.onpause = function(tun) {}; // proxy requests to pause upload to a client | ||||
| // tun = { family, address, port }; | ||||
| handlers.onpause = function (tun) { }                     // proxy requests to pause upload to a client | ||||
|                                                           // tun = { family, address, port }; | ||||
| 
 | ||||
| handlers.onresume = function(tun) {}; // proxy requests to resume upload to a client | ||||
| // tun = { family, address, port }; | ||||
| handlers.onresume = function (tun) { }                    // proxy requests to resume upload to a client | ||||
|                                                           // tun = { family, address, port }; | ||||
| 
 | ||||
| handlers.onend = function(tun) {}; // proxy requests to close a client's socket | ||||
| // tun = { family, address, port }; | ||||
| handlers.onend = function (tun) { }                       // proxy requests to close a client's socket | ||||
|                                                           // tun = { family, address, port }; | ||||
| 
 | ||||
| handlers.onerror = function(err) {}; // proxy is relaying a client's error | ||||
| // err = { message, family, address, port }; | ||||
| handlers.onerror = function (err) { }                     // proxy is relaying a client's error | ||||
|                                                           // err = { message, family, address, port }; | ||||
| ``` | ||||
| 
 | ||||
| <!-- | ||||
| @ -157,43 +163,44 @@ 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. | ||||
| 
 | ||||
| ```js | ||||
| var headerAndBody = Packer.pack(tun, data); // Add M-PROXY header to data | ||||
| // tun = { family, address, port | ||||
| //       , service, serviceport, name } | ||||
| var headerAndBody = Packer.pack(tun, data);               // Add M-PROXY header to data | ||||
|                                                           // tun = { family, address, port | ||||
|                                                           //       , service, serviceport, name } | ||||
| 
 | ||||
| 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) | ||||
| 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) | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| ```js | ||||
| var socket = Packer.Stream.wrapSocket(socketOrStream); // workaround for https://github.com/nodejs/node/issues/8854 | ||||
| // which was just closed recently, but probably still needs | ||||
| // something more like this (below) to work as intended | ||||
| // https://github.com/findhit/proxywrap/blob/master/lib/proxywrap.js | ||||
| var socket = Packer.Stream.wrapSocket(socketOrStream);   // workaround for https://github.com/nodejs/node/issues/8854 | ||||
|                                                          // which was just closed recently, but probably still needs | ||||
|                                                          // something more like this (below) to work as intended | ||||
|                                                          // https://github.com/findhit/proxywrap/blob/master/lib/proxywrap.js | ||||
| ``` | ||||
| 
 | ||||
| ```js | ||||
| var myTransform = Packer.Transform.create({ | ||||
| 	address: { | ||||
| 		family: '...', | ||||
| 		address: '...', | ||||
| 		port: '...' | ||||
| 	}, | ||||
| 	// hint at the service to be used | ||||
| 	service: 'https' | ||||
|   address: { | ||||
|     family: '...' | ||||
|   , address: '...' | ||||
|   , port: '...' | ||||
|   } | ||||
|   // hint at the service to be used | ||||
| , service: 'https' | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| @ -210,7 +217,6 @@ hexdump output.bin | ||||
| Where `input.json` looks something like this: | ||||
| 
 | ||||
| `input.json`: | ||||
| 
 | ||||
| ``` | ||||
| { "version": 1 | ||||
| , "address": { | ||||
| @ -225,12 +231,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: | ||||
| 
 | ||||
| `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 | ||||
| @ -249,7 +255,8 @@ and `sni.tcp.bin` is any captured tcp packet, such as this one with a tls hello: | ||||
| 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. | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										740
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										740
									
								
								index.js
									
									
									
									
									
								
							| @ -3,374 +3,344 @@ | ||||
| var Packer = module.exports; | ||||
| 
 | ||||
| var serviceEvents = { | ||||
| 	default: 'tunnelData', | ||||
| 	connection: 'tunnelConnection', | ||||
| 	control: 'tunnelControl', | ||||
| 	error: 'tunnelError', | ||||
| 	end: 'tunnelEnd', | ||||
| 	pause: 'tunnelPause', | ||||
| 	resume: 'tunnelResume' | ||||
|   default: 'tunnelData' | ||||
| , connection: 'tunnelConnection' | ||||
| , control: 'tunnelControl' | ||||
| , error:   'tunnelError' | ||||
| , end:     'tunnelEnd' | ||||
| , pause:   'tunnelPause' | ||||
| , resume:  'tunnelResume' | ||||
| }; | ||||
| var serviceFuncs = { | ||||
| 	default: 'onmessage', | ||||
| 	connection: 'onconnection', | ||||
| 	control: 'oncontrol', | ||||
| 	error: 'onerror', | ||||
| 	end: 'onend', | ||||
| 	pause: 'onpause', | ||||
| 	resume: 'onresume' | ||||
|   default: 'onmessage' | ||||
| , connection: 'onconnection' | ||||
| , control: 'oncontrol' | ||||
| , error:   'onerror' | ||||
| , end:     'onend' | ||||
| , pause:   'onpause' | ||||
| , resume:  'onresume' | ||||
| }; | ||||
| 
 | ||||
| Packer.create = function(opts) { | ||||
| 	var machine; | ||||
| Packer.create = function (opts) { | ||||
|   var machine; | ||||
| 
 | ||||
| 	if (!opts.onMessage && !opts.onmessage) { | ||||
| 		machine = new (require('events')).EventEmitter(); | ||||
| 	} else { | ||||
| 		machine = {}; | ||||
| 	} | ||||
|   if (!opts.onMessage && !opts.onmessage) { | ||||
|     machine = new (require('events').EventEmitter)(); | ||||
|   } else { | ||||
|     machine = {}; | ||||
|   } | ||||
| 
 | ||||
| 	machine.onmessage = opts.onmessage || opts.onMessage; | ||||
| 	machine.oncontrol = opts.oncontrol || opts.onControl; | ||||
| 	machine.onconnection = | ||||
| 		opts.onconnection || opts.onConnection || function() {}; | ||||
| 	machine.onerror = opts.onerror || opts.onError; | ||||
| 	machine.onend = opts.onend || opts.onEnd; | ||||
| 	machine.onpause = opts.onpause || opts.onPause; | ||||
| 	machine.onresume = opts.onresume || opts.onResume; | ||||
|   machine.onmessage    = opts.onmessage    || opts.onMessage; | ||||
|   machine.oncontrol    = opts.oncontrol    || opts.onControl; | ||||
|   machine.onconnection = opts.onconnection || opts.onConnection || function () {}; | ||||
|   machine.onerror      = opts.onerror      || opts.onError; | ||||
|   machine.onend        = opts.onend        || opts.onEnd; | ||||
|   machine.onpause      = opts.onpause      || opts.onPause; | ||||
|   machine.onresume     = opts.onresume     || opts.onResume; | ||||
| 
 | ||||
| 	machine._version = 1; | ||||
| 	machine.fns = {}; | ||||
|   machine._version = 1; | ||||
|   machine.fns = {}; | ||||
| 
 | ||||
| 	machine.chunkIndex = 0; | ||||
| 	machine.buf = null; | ||||
| 	machine.bufIndex = 0; | ||||
| 	machine.fns.collectData = function(chunk, size) { | ||||
| 		var chunkLeft = chunk.length - machine.chunkIndex; | ||||
| 		var hasLen = size > 0; | ||||
|   machine.chunkIndex = 0; | ||||
|   machine.buf = null; | ||||
|   machine.bufIndex = 0; | ||||
|   machine.fns.collectData = function (chunk, size) { | ||||
|     var chunkLeft = chunk.length - machine.chunkIndex; | ||||
|     var hasLen = (size > 0); | ||||
| 
 | ||||
| 		if (!hasLen) { | ||||
| 			return Buffer.alloc(0); | ||||
| 		} | ||||
|     if (!hasLen) { | ||||
|       return Buffer.alloc(0); | ||||
|     } | ||||
| 
 | ||||
| 		// 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.
 | ||||
| 		if (machine.bufIndex + chunkLeft < size) { | ||||
| 			if (!machine.buf) { | ||||
| 				machine.buf = Buffer.alloc(size); | ||||
| 			} | ||||
| 			chunk.copy(machine.buf, machine.bufIndex, machine.chunkIndex); | ||||
| 			machine.bufIndex += chunkLeft; | ||||
| 			machine.chunkIndex += chunkLeft; | ||||
|     // 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.
 | ||||
|     if (machine.bufIndex + chunkLeft < size) { | ||||
|       if (!machine.buf) { | ||||
|         machine.buf = Buffer.alloc(size); | ||||
|       } | ||||
|       chunk.copy(machine.buf, machine.bufIndex, machine.chunkIndex); | ||||
|       machine.bufIndex += 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.
 | ||||
| 		var partLen = size - machine.bufIndex; | ||||
| 		var part = chunk.slice( | ||||
| 			machine.chunkIndex, | ||||
| 			machine.chunkIndex + partLen | ||||
| 		); | ||||
| 		machine.chunkIndex += partLen; | ||||
|     // Read and mark as read however much data we need from the chunk to complete our buffer.
 | ||||
|     var partLen = size - machine.bufIndex; | ||||
|     var part = chunk.slice(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 (!machine.buf) { | ||||
| 			return part; | ||||
| 		} | ||||
|     // If we had nothing buffered than the part of the chunk we just read is all we need.
 | ||||
|     if (!machine.buf) { | ||||
|       return part; | ||||
|     } | ||||
| 
 | ||||
| 		// Otherwise we need to copy the new data into the buffer.
 | ||||
| 		part.copy(machine.buf, machine.bufIndex); | ||||
| 		// Before returning the buffer we need to clear our reference to it.
 | ||||
| 		var buf = machine.buf; | ||||
| 		machine.buf = null; | ||||
| 		machine.bufIndex = 0; | ||||
| 		return buf; | ||||
| 	}; | ||||
|     // Otherwise we need to copy the new data into the buffer.
 | ||||
|     part.copy(machine.buf, machine.bufIndex); | ||||
|     // Before returning the buffer we need to clear our reference to it.
 | ||||
|     var buf = machine.buf; | ||||
|     machine.buf = null; | ||||
|     machine.bufIndex = 0; | ||||
|     return buf; | ||||
|   }; | ||||
| 
 | ||||
| 	machine.fns.version = function(chunk) { | ||||
| 		//console.log('');
 | ||||
| 		//console.log('[version]');
 | ||||
| 		if (255 - machine._version !== chunk[machine.chunkIndex]) { | ||||
| 			console.error('not v' + machine._version + ' (or data is corrupt)'); | ||||
| 			// no idea how to fix this yet
 | ||||
| 		} | ||||
| 		machine.chunkIndex += 1; | ||||
|   machine.fns.version = function (chunk) { | ||||
|     //console.log('');
 | ||||
|     //console.log('[version]');
 | ||||
|     if ((255 - machine._version) !== chunk[machine.chunkIndex]) { | ||||
|       console.error("not v" + machine._version + " (or data is corrupt)"); | ||||
|       // no idea how to fix this yet
 | ||||
|     } | ||||
|     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; | ||||
| 
 | ||||
| 		return true; | ||||
| 	}; | ||||
|   machine.headerLen = 0; | ||||
|   machine.fns.headerLength = function (chunk) { | ||||
|     //console.log('');
 | ||||
|     //console.log('[headerLength]');
 | ||||
|     machine.headerLen = chunk[machine.chunkIndex]; | ||||
|     machine.chunkIndex += 1; | ||||
| 
 | ||||
| 	machine.fns.header = function(chunk) { | ||||
| 		//console.log('');
 | ||||
| 		//console.log('[header]');
 | ||||
| 		var header = machine.fns.collectData(chunk, machine.headerLen); | ||||
|     return true; | ||||
|   }; | ||||
| 
 | ||||
| 		// We don't have the entire header yet so return false.
 | ||||
| 		if (!header) { | ||||
| 			return false; | ||||
| 		} | ||||
|   machine.fns.header = function (chunk) { | ||||
|     //console.log('');
 | ||||
|     //console.log('[header]');
 | ||||
|     var header = machine.fns.collectData(chunk, machine.headerLen); | ||||
| 
 | ||||
| 		machine._headers = header.toString().split(/,/g); | ||||
|     // We don't have the entire header yet so return false.
 | ||||
|     if (!header) { | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
| 		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._headers = header.toString().split(/,/g); | ||||
| 
 | ||||
| 		return true; | ||||
| 	}; | ||||
|     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.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; | ||||
| 			} | ||||
| 		} | ||||
|     return true; | ||||
|   }; | ||||
| 
 | ||||
| 		//
 | ||||
| 		// data, end, error
 | ||||
| 		//
 | ||||
| 		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'; | ||||
| 			} | ||||
| 		} | ||||
|   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; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
| 		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) { | ||||
| 			msg.service = machine.servicename; | ||||
| 		} | ||||
|     //
 | ||||
|     // data, end, error
 | ||||
|     //
 | ||||
|     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'; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
| 		//console.log('msn', machine.service);
 | ||||
| 		if (machine.emit) { | ||||
| 			machine.emit( | ||||
| 				serviceEvents[machine.service] || | ||||
| 					serviceEvents[msg.service] || | ||||
| 					serviceEvents.default | ||||
| 			); | ||||
| 		} else { | ||||
| 			(machine[serviceFuncs[machine.service]] || | ||||
| 				machine[serviceFuncs[msg.service]] || | ||||
| 				machine[serviceFuncs.default])(msg); | ||||
| 		} | ||||
|     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; | ||||
| 
 | ||||
| 		return true; | ||||
| 	}; | ||||
|     if ('connection' === machine.service) { | ||||
|       msg.service = machine.servicename; | ||||
|     } | ||||
| 
 | ||||
| 	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);
 | ||||
|     //console.log('msn', machine.service);
 | ||||
|     if (machine.emit) { | ||||
|       machine.emit(serviceEvents[machine.service] || serviceEvents[msg.service] || serviceEvents.default); | ||||
|     } else { | ||||
|       (machine[serviceFuncs[machine.service]] || machine[serviceFuncs[msg.service]] || machine[serviceFuncs.default])(msg); | ||||
|     } | ||||
| 
 | ||||
| 			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 true; | ||||
|   }; | ||||
| 
 | ||||
| 	return machine; | ||||
|   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);
 | ||||
| 
 | ||||
|       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) { | ||||
| 	if (oldways && !data) { | ||||
| 		data = Buffer.from(' '); | ||||
| 	} | ||||
| 	if (data && !Buffer.isBuffer(data)) { | ||||
| 		data = Buffer.from(JSON.stringify(data)); | ||||
| 	} | ||||
| 	if (oldways && !data.byteLength) { | ||||
| 		data = Buffer.from(' '); | ||||
| 	} | ||||
| Packer.packHeader = function (meta, data, service, andBody, oldways) { | ||||
|   if (oldways && !data) { | ||||
|     data = Buffer.from(' '); | ||||
|   } | ||||
|   if (data && !Buffer.isBuffer(data)) { | ||||
|     data = Buffer.from(JSON.stringify(data)); | ||||
|   } | ||||
|   if (oldways && !data.byteLength) { | ||||
|     data = Buffer.from(' '); | ||||
|   } | ||||
| 
 | ||||
| 	if (service && -1 === ['control', 'connection'].indexOf(service)) { | ||||
| 		//console.log('end?', service);
 | ||||
| 		meta.service = service; | ||||
| 	} | ||||
|   if (service && -1 === ['control','connection'].indexOf(service)) { | ||||
|     //console.log('end?', service);
 | ||||
|     meta.service = service; | ||||
|   } | ||||
| 
 | ||||
| 	var size = (data && data.byteLength) || 0; | ||||
| 	var sizeReserve = andBody ? size : 0; | ||||
| 	var version = 1; | ||||
| 	var header; | ||||
| 	if (service === 'control') { | ||||
| 		header = Buffer.from(['', '', '', size, service].join(',')); | ||||
| 	} else if (service === 'connection') { | ||||
| 		header = Buffer.from( | ||||
| 			[ | ||||
| 				meta.family, | ||||
| 				meta.address, | ||||
| 				meta.port, | ||||
| 				size, | ||||
| 				'connection', | ||||
| 				meta.serviceport || '', | ||||
| 				meta.name || '', | ||||
| 				meta.service || '' | ||||
| 			].join(',') | ||||
| 		); | ||||
| 	} else { | ||||
| 		header = Buffer.from( | ||||
| 			[ | ||||
| 				meta.family, | ||||
| 				meta.address, | ||||
| 				meta.port, | ||||
| 				size, | ||||
| 				meta.service || '', | ||||
| 				meta.serviceport || '', | ||||
| 				meta.name || '' | ||||
| 			].join(',') | ||||
| 		); | ||||
| 	} | ||||
| 	var metaBuf = Buffer.from([255 - version, header.length]); | ||||
| 	var buf = Buffer.alloc( | ||||
| 		metaBuf.byteLength + header.byteLength + sizeReserve | ||||
| 	); | ||||
|   var size = data && data.byteLength || 0; | ||||
|   var sizeReserve = andBody ? size : 0; | ||||
|   var version = 1; | ||||
|   var header; | ||||
|   if (service === 'control') { | ||||
|     header = Buffer.from(['', '', '', size, service].join(',')); | ||||
|   } | ||||
|   else if (service === 'connection') { | ||||
|     header = Buffer.from([ | ||||
|       meta.family, meta.address, meta.port, size, | ||||
|       'connection', (meta.serviceport || ''), (meta.name || ''), | ||||
|       (meta.service || '') | ||||
|     ].join(',')); | ||||
|   } | ||||
|   else { | ||||
|     header = Buffer.from([ | ||||
|       meta.family, meta.address, meta.port, size, | ||||
|       (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); | ||||
| 	header.copy(buf, 2); | ||||
| 	if (sizeReserve) { | ||||
| 		data.copy(buf, 2 + header.byteLength); | ||||
| 	} | ||||
|   metaBuf.copy(buf, 0); | ||||
|   header.copy(buf, 2); | ||||
|   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); | ||||
| Packer.pack = function (meta, data, service) { | ||||
|   return Packer.packHeader(meta, data, service, true, true); | ||||
| }; | ||||
| 
 | ||||
| function extractSocketProps(socket, propNames) { | ||||
| 	var props = {}; | ||||
|   var props = {}; | ||||
| 
 | ||||
| 	if (socket.remotePort) { | ||||
| 		propNames.forEach(function(propName) { | ||||
| 			props[propName] = socket[propName]; | ||||
| 		}); | ||||
| 	} else if (socket._remotePort) { | ||||
| 		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; | ||||
|   if (socket.remotePort) { | ||||
|     propNames.forEach(function (propName) { | ||||
|       props[propName] = socket[propName]; | ||||
|     }); | ||||
|   } else if (socket._remotePort) { | ||||
|     propNames.forEach(function (propName) { | ||||
|       props[propName] = socket['_' + propName]; | ||||
|     }); | ||||
|   } else if ( | ||||
|     socket._handle | ||||
|     && 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 | ||||
|     && 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; | ||||
| } | ||||
| function extractSocketProp(socket, propName) { | ||||
| 	// remoteAddress, remotePort... ugh... https://github.com/nodejs/node/issues/8854
 | ||||
| 	var value = socket[propName] || socket['_' + propName]; | ||||
| 	try { | ||||
| 		value = value || socket._handle._parent.owner.stream[propName]; | ||||
| 	} catch (e) {} | ||||
|   // remoteAddress, remotePort... ugh... https://github.com/nodejs/node/issues/8854
 | ||||
|   var value = socket[propName] || socket['_' + propName]; | ||||
|   try { | ||||
|     value = value || socket._handle._parent.owner.stream[propName]; | ||||
|   } catch (e) {} | ||||
| 
 | ||||
| 	try { | ||||
| 		value = value || socket._handle._parentWrap[propName]; | ||||
| 		value = | ||||
| 			value || socket._handle._parentWrap._handle.owner.stream[propName]; | ||||
| 	} catch (e) {} | ||||
|   try { | ||||
|     value = value || socket._handle._parentWrap[propName]; | ||||
|     value = value || socket._handle._parentWrap._handle.owner.stream[propName]; | ||||
|   } catch (e) {} | ||||
| 
 | ||||
| 	return value || ''; | ||||
|   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);
 | ||||
| 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 | ||||
| 	}; | ||||
|   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) { | ||||
| 	return address.family + ',' + address.address + ',' + address.port; | ||||
| Packer.addrToId = function (address) { | ||||
|   return address.family + ',' + address.address + ',' + address.port; | ||||
| }; | ||||
| 
 | ||||
| Packer.socketToId = function(socket) { | ||||
| 	return Packer.addrToId(Packer.socketToAddr(socket)); | ||||
| Packer.socketToId = function (socket) { | ||||
|   return Packer.addrToId(Packer.socketToAddr(socket)); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| var addressNames = [ | ||||
| 	'remoteAddress', | ||||
| 	'remotePort', | ||||
| 	'remoteFamily', | ||||
| 	'localAddress', | ||||
| 	'localPort' | ||||
|   'remoteAddress' | ||||
| , 'remotePort' | ||||
| , 'remoteFamily' | ||||
| , 'localAddress' | ||||
| , 'localPort' | ||||
| ]; | ||||
| /* | ||||
| var sockFuncs = [ | ||||
| @ -385,20 +355,20 @@ var sockFuncs = [ | ||||
| ]; | ||||
| */ | ||||
| // Unlike Packer.Stream.create this should handle all of the events needed to make everything work.
 | ||||
| Packer.wrapSocket = function(socket) { | ||||
| 	// node v10.2+ doesn't need a workaround for  https://github.com/nodejs/node/issues/8854
 | ||||
| 	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
 | ||||
| 	/* | ||||
| 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(); | ||||
|   addressNames.forEach(function (name) { | ||||
| @ -448,79 +418,79 @@ var Transform = require('stream').Transform; | ||||
| var util = require('util'); | ||||
| 
 | ||||
| function MyTransform(options) { | ||||
| 	if (!(this instanceof MyTransform)) { | ||||
| 		return new MyTransform(options); | ||||
| 	} | ||||
| 	this.__my_addr = options.address; | ||||
| 	this.__my_service = options.service; | ||||
| 	this.__my_serviceport = options.serviceport; | ||||
| 	this.__my_name = options.name; | ||||
| 	Transform.call(this, options); | ||||
|   if (!(this instanceof MyTransform)) { | ||||
|     return new MyTransform(options); | ||||
|   } | ||||
|   this.__my_addr = options.address; | ||||
|   this.__my_service = options.service; | ||||
|   this.__my_serviceport = options.serviceport; | ||||
|   this.__my_name = options.name; | ||||
|   Transform.call(this, options); | ||||
| } | ||||
| util.inherits(MyTransform, Transform); | ||||
| 
 | ||||
| MyTransform.prototype._transform = function(data, encoding, callback) { | ||||
| 	var address = this.__my_addr; | ||||
| MyTransform.prototype._transform = function (data, encoding, callback) { | ||||
|   var address = this.__my_addr; | ||||
| 
 | ||||
| 	address.service = address.service || this.__my_service; | ||||
| 	address.serviceport = address.serviceport || this.__my_serviceport; | ||||
| 	address.name = address.name || this.__my_name; | ||||
| 	this.push(Packer.pack(address, data)); | ||||
| 	callback(); | ||||
|   address.service = address.service || this.__my_service; | ||||
|   address.serviceport = address.serviceport || this.__my_serviceport; | ||||
|   address.name = address.name || this.__my_name; | ||||
|   this.push(Packer.pack(address, data)); | ||||
|   callback(); | ||||
| }; | ||||
| 
 | ||||
| Packer.Stream = {}; | ||||
| var Dup = { | ||||
| 	write: function(chunk, encoding, cb) { | ||||
| 		//console.log('_write', chunk.byteLength);
 | ||||
| 		this.__my_socket.write(chunk, encoding, cb); | ||||
| 	}, | ||||
| 	read: function(size) { | ||||
| 		//console.log('_read');
 | ||||
| 		var x = this.__my_socket.read(size); | ||||
| 		if (x) { | ||||
| 			console.log('_read', size); | ||||
| 			this.push(x); | ||||
| 		} | ||||
| 	} | ||||
|   write: function (chunk, encoding, cb) { | ||||
|     //console.log('_write', chunk.byteLength);
 | ||||
|     this.__my_socket.write(chunk, encoding, cb); | ||||
|   } | ||||
| , read: function (size) { | ||||
|     //console.log('_read');
 | ||||
|     var x = this.__my_socket.read(size); | ||||
|     if (x) { | ||||
|       console.log('_read', size); | ||||
|       this.push(x); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| Packer.Stream.create = function(socket) { | ||||
| 	if (!Packer.Stream.warned) { | ||||
| 		console.warn('`Stream.create` deprecated, use `wrapSocket` instead'); | ||||
| 		Packer.Stream.warned = true; | ||||
| 	} | ||||
| Packer.Stream.create = function (socket) { | ||||
|   if (!Packer.Stream.warned) { | ||||
|     console.warn('`Stream.create` deprecated, use `wrapSocket` instead'); | ||||
|     Packer.Stream.warned = true; | ||||
|   } | ||||
| 
 | ||||
| 	// Workaround for
 | ||||
| 	// https://github.com/nodejs/node/issues/8854
 | ||||
|   // Workaround for
 | ||||
|   // https://github.com/nodejs/node/issues/8854
 | ||||
| 
 | ||||
| 	// 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 })
 | ||||
| 	// from the old one and then adding back the data with
 | ||||
| 	// sock.push(firstChunk)
 | ||||
| 	var Duplex = require('stream').Duplex; | ||||
| 	var myDuplex = new Duplex(); | ||||
|   // 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 })
 | ||||
|   // from the old one and then adding back the data with
 | ||||
|   // sock.push(firstChunk)
 | ||||
|   var Duplex = require('stream').Duplex; | ||||
|   var myDuplex = new Duplex(); | ||||
| 
 | ||||
| 	myDuplex.__my_socket = socket; | ||||
| 	myDuplex._write = Dup.write; | ||||
| 	myDuplex._read = Dup.read; | ||||
| 	//console.log('plainSocket.*Address');
 | ||||
| 	//console.log('remote:', socket.remoteAddress);
 | ||||
| 	//console.log('local:', socket.localAddress);
 | ||||
| 	//console.log('address():', socket.address());
 | ||||
| 	myDuplex.remoteFamily = socket.remoteFamily; | ||||
| 	myDuplex.remoteAddress = socket.remoteAddress; | ||||
| 	myDuplex.remotePort = socket.remotePort; | ||||
| 	myDuplex.localFamily = socket.localFamily; | ||||
| 	myDuplex.localAddress = socket.localAddress; | ||||
| 	myDuplex.localPort = socket.localPort; | ||||
|   myDuplex.__my_socket = socket; | ||||
|   myDuplex._write = Dup.write; | ||||
|   myDuplex._read = Dup.read; | ||||
|   //console.log('plainSocket.*Address');
 | ||||
|   //console.log('remote:', socket.remoteAddress);
 | ||||
|   //console.log('local:', socket.localAddress);
 | ||||
|   //console.log('address():', socket.address());
 | ||||
|   myDuplex.remoteFamily = socket.remoteFamily; | ||||
|   myDuplex.remoteAddress = socket.remoteAddress; | ||||
|   myDuplex.remotePort = socket.remotePort; | ||||
|   myDuplex.localFamily = socket.localFamily; | ||||
|   myDuplex.localAddress = socket.localAddress; | ||||
|   myDuplex.localPort = socket.localPort; | ||||
| 
 | ||||
| 	return myDuplex; | ||||
|   return myDuplex; | ||||
| }; | ||||
| 
 | ||||
| Packer.Transform = {}; | ||||
| Packer.Transform.create = function(opts) { | ||||
| 	// Note: service refers to the port that the incoming request was from,
 | ||||
| 	// if known (smtps, smtp, https, http, etc)
 | ||||
| 	// { address: '127.0.0.1', service: 'https' }
 | ||||
| 	return new MyTransform(opts); | ||||
| Packer.Transform.create = function (opts) { | ||||
|   // Note: service refers to the port that the incoming request was from,
 | ||||
|   // if known (smtps, smtp, https, http, etc)
 | ||||
|   // { address: '127.0.0.1', service: 'https' }
 | ||||
|   return new MyTransform(opts); | ||||
| }; | ||||
|  | ||||
							
								
								
									
										76
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								package.json
									
									
									
									
									
								
							| @ -1,40 +1,40 @@ | ||||
| { | ||||
| 	"name": "proxy-packer", | ||||
| 	"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.", | ||||
| 	"main": "index.js", | ||||
| 	"scripts": { | ||||
| 		"test": "node test.js" | ||||
| 	}, | ||||
| 	"repository": { | ||||
| 		"type": "git", | ||||
| 		"url": "git+https://git.coolaj86.com/coolaj86/proxy-packer.js.git" | ||||
| 	}, | ||||
| 	"keywords": [ | ||||
| 		"tunnel", | ||||
| 		"telebit", | ||||
| 		"telebitd", | ||||
| 		"localtunnel", | ||||
| 		"ngrok", | ||||
| 		"underpass", | ||||
| 		"tcp", | ||||
| 		"sni", | ||||
| 		"https", | ||||
| 		"ssl", | ||||
| 		"tls", | ||||
| 		"http", | ||||
| 		"proxy", | ||||
| 		"pack", | ||||
| 		"unpack", | ||||
| 		"message", | ||||
| 		"msg", | ||||
| 		"packer", | ||||
| 		"unpacker" | ||||
| 	], | ||||
| 	"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)", | ||||
| 	"license": "(MIT OR Apache-2.0)", | ||||
| 	"bugs": { | ||||
| 		"url": "https://git.coolaj86.com/coolaj86/proxy-packer.js/issues" | ||||
| 	}, | ||||
| 	"homepage": "https://git.coolaj86.com/coolaj86/proxy-packer.js#readme" | ||||
|   "name": "proxy-packer", | ||||
|   "version": "2.0.2", | ||||
|   "description": "A strategy for packing and unpacking a proxy stream (i.e. packets through a tunnel). Handles multiplexed and tls connections. Used by telebit and telebitd.", | ||||
|   "main": "index.js", | ||||
|   "scripts": { | ||||
|     "test": "node test.js" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "git+https://git.coolaj86.com/coolaj86/proxy-packer.js.git" | ||||
|   }, | ||||
|   "keywords": [ | ||||
|     "tunnel", | ||||
|     "telebit", | ||||
|     "telebitd", | ||||
|     "localtunnel", | ||||
|     "ngrok", | ||||
|     "underpass", | ||||
|     "tcp", | ||||
|     "sni", | ||||
|     "https", | ||||
|     "ssl", | ||||
|     "tls", | ||||
|     "http", | ||||
|     "proxy", | ||||
|     "pack", | ||||
|     "unpack", | ||||
|     "message", | ||||
|     "msg", | ||||
|     "packer", | ||||
|     "unpacker" | ||||
|   ], | ||||
|   "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)", | ||||
|   "license": "(MIT OR Apache-2.0)", | ||||
|   "bugs": { | ||||
|     "url": "https://git.coolaj86.com/coolaj86/proxy-packer.js/issues" | ||||
|   }, | ||||
|   "homepage": "https://git.coolaj86.com/coolaj86/proxy-packer.js#readme" | ||||
| } | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| { | ||||
| 	"version": 1, | ||||
| 	"address": { | ||||
| 		"family": "IPv4", | ||||
| 		"address": "127.0.1.1", | ||||
| 		"port": 4321, | ||||
| 		"service": "https", | ||||
| 		"serviceport": 443 | ||||
| 	}, | ||||
| 	"filepath": "./sni.hello.bin" | ||||
| { "version": 1 | ||||
| , "address": { | ||||
|     "family": "IPv4" | ||||
|   , "address": "127.0.1.1" | ||||
|   , "port": 4321 | ||||
|   , "service": "https" | ||||
|   , "serviceport": 443 | ||||
|   } | ||||
| , "filepath": "./sni.hello.bin" | ||||
| } | ||||
|  | ||||
							
								
								
									
										62
									
								
								test/pack.js
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								test/pack.js
									
									
									
									
									
								
							| @ -1,31 +1,28 @@ | ||||
| (function() { | ||||
| 	'use strict'; | ||||
| ;(function () { | ||||
| 'use strict'; | ||||
| 
 | ||||
| 	var fs = require('fs'); | ||||
| 	var infile = process.argv[2]; | ||||
| 	var outfile = process.argv[3]; | ||||
| 	var sni = require('sni'); | ||||
| var fs = require('fs'); | ||||
| var infile = process.argv[2]; | ||||
| var outfile = process.argv[3]; | ||||
| var sni = require('sni'); | ||||
| 
 | ||||
| 	if (!infile || !outfile) { | ||||
| 		console.error('Usage:'); | ||||
| 		console.error('node test/pack.js test/input.json test/output.bin'); | ||||
| 		process.exit(1); | ||||
| 		return; | ||||
| 	} | ||||
| if (!infile || !outfile) { | ||||
|   console.error("Usage:"); | ||||
|   console.error("node test/pack.js test/input.json test/output.bin"); | ||||
|   process.exit(1); | ||||
|   return; | ||||
| } | ||||
| 
 | ||||
| 	var path = require('path'); | ||||
| 	var json = JSON.parse(fs.readFileSync(infile, 'utf8')); | ||||
| 	var data = require('fs').readFileSync( | ||||
| 		path.resolve(path.dirname(infile), json.filepath), | ||||
| 		null | ||||
| 	); | ||||
| 	var Packer = require('../index.js'); | ||||
| var path = require('path'); | ||||
| var json = JSON.parse(fs.readFileSync(infile, 'utf8')); | ||||
| var data = require('fs').readFileSync(path.resolve(path.dirname(infile), json.filepath), null); | ||||
| var Packer = require('../index.js'); | ||||
| 
 | ||||
| 	var servername = sni(data); | ||||
| 	var m = data.toString().match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im); | ||||
| 	var hostname = ((m && m[1].toLowerCase()) || '').split(':')[0]; | ||||
| var servername = sni(data); | ||||
| var m = data.toString().match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im); | ||||
| var hostname = (m && m[1].toLowerCase() || '').split(':')[0]; | ||||
| 
 | ||||
| 	/* | ||||
| /* | ||||
| function pack() { | ||||
|   var version = json.version; | ||||
|   var address = json.address; | ||||
| @ -40,16 +37,9 @@ function pack() { | ||||
| } | ||||
| */ | ||||
| 
 | ||||
| 	json.address.name = servername || hostname; | ||||
| 	var buf = Packer.pack(json.address, data); | ||||
| 	fs.writeFileSync(outfile, buf, null); | ||||
| 	console.log( | ||||
| 		'wrote ' + | ||||
| 			buf.byteLength + | ||||
| 			" bytes to '" + | ||||
| 			outfile + | ||||
| 			"' ('hexdump " + | ||||
| 			outfile + | ||||
| 			"' to inspect)" | ||||
| 	); | ||||
| })(); | ||||
| json.address.name = servername || hostname; | ||||
| var buf = Packer.pack(json.address, data); | ||||
| fs.writeFileSync(outfile, buf, null); | ||||
| console.log("wrote " + buf.byteLength + " bytes to '" + outfile + "' ('hexdump " + outfile + "' to inspect)"); | ||||
| 
 | ||||
| }()); | ||||
|  | ||||
							
								
								
									
										258
									
								
								test/parse.js
									
									
									
									
									
								
							
							
						
						
									
										258
									
								
								test/parse.js
									
									
									
									
									
								
							| @ -4,114 +4,82 @@ var sni = require('sni'); | ||||
| var hello = require('fs').readFileSync(__dirname + '/sni.hello.bin'); | ||||
| var version = 1; | ||||
| function getAddress() { | ||||
| 	return { | ||||
| 		family: 'IPv4', | ||||
| 		address: '127.0.1.1', | ||||
| 		port: 4321, | ||||
| 		service: 'foo-https', | ||||
| 		serviceport: 443, | ||||
| 		name: 'foo-pokemap.hellabit.com' | ||||
| 	}; | ||||
|   return { | ||||
|     family: 'IPv4' | ||||
|   , address: '127.0.1.1' | ||||
|   , port: 4321 | ||||
|   , service: 'foo-https' | ||||
|   , serviceport: 443 | ||||
|   , name: 'foo-pokemap.hellabit.com' | ||||
|   }; | ||||
| } | ||||
| var addr = getAddress(); | ||||
| var connectionHeader = | ||||
| 	addr.family + | ||||
| 	',' + | ||||
| 	addr.address + | ||||
| 	',' + | ||||
| 	addr.port + | ||||
| 	',0,connection,' + | ||||
| 	(addr.serviceport || '') + | ||||
| 	',' + | ||||
| 	(addr.name || '') + | ||||
| 	',' + | ||||
| 	(addr.service || ''); | ||||
| var header = | ||||
| 	addr.family + | ||||
| 	',' + | ||||
| 	addr.address + | ||||
| 	',' + | ||||
| 	addr.port + | ||||
| 	',' + | ||||
| 	hello.byteLength + | ||||
| 	',' + | ||||
| 	(addr.service || '') + | ||||
| 	',' + | ||||
| 	(addr.serviceport || '') + | ||||
| 	',' + | ||||
| 	(addr.name || ''); | ||||
| var endHeader = | ||||
| 	addr.family + | ||||
| 	',' + | ||||
| 	addr.address + | ||||
| 	',' + | ||||
| 	addr.port + | ||||
| 	',0,end,' + | ||||
| 	(addr.serviceport || '') + | ||||
| 	',' + | ||||
| 	(addr.name || ''); | ||||
| var connectionHeader = addr.family + ',' + addr.address + ',' + addr.port | ||||
|   + ',0,connection,' | ||||
|   + (addr.serviceport || '') + ',' + (addr.name || '') + ',' + (addr.service || '') | ||||
|   ; | ||||
| var           header = addr.family + ',' + addr.address + ',' + addr.port | ||||
|   + ',' + hello.byteLength + ',' + (addr.service || '') + ',' | ||||
|   + (addr.serviceport || '') + ',' + (addr.name || '') | ||||
|   ; | ||||
| var        endHeader = addr.family + ',' + addr.address + ',' + addr.port | ||||
|   + ',0,end,' | ||||
|   + (addr.serviceport || '') + ',' + (addr.name || '') | ||||
|   ; | ||||
| var buf = Buffer.concat([ | ||||
| 	Buffer.from([255 - version, connectionHeader.length]), | ||||
| 	Buffer.from(connectionHeader), | ||||
| 	Buffer.from([255 - version, header.length]), | ||||
| 	Buffer.from(header), | ||||
| 	hello, | ||||
| 	Buffer.from([255 - version, endHeader.length]), | ||||
| 	Buffer.from(endHeader) | ||||
|   Buffer.from([ 255 - version, connectionHeader.length ]) | ||||
| , Buffer.from(connectionHeader) | ||||
| , Buffer.from([ 255 - version, header.length ]) | ||||
| , Buffer.from(header) | ||||
| , hello | ||||
| , Buffer.from([ 255 - version, endHeader.length ]) | ||||
| , Buffer.from(endHeader) | ||||
| ]); | ||||
| var services = { ssh: 22, http: 4080, https: 8443 }; | ||||
| var services = { 'ssh': 22, 'http': 4080, 'https': 8443 }; | ||||
| var clients = {}; | ||||
| var count = 0; | ||||
| var packer = require('../'); | ||||
| var machine = packer.create({ | ||||
| 	onconnection: function(tun) { | ||||
| 		console.info(''); | ||||
| 		if (!tun.service || 'connection' === tun.service) { | ||||
| 			throw new Error('missing service: ' + JSON.stringify(tun)); | ||||
| 		} | ||||
| 		console.info('[onConnection]'); | ||||
| 		count += 1; | ||||
| 	}, | ||||
| 	onmessage: function(tun) { | ||||
| 		//console.log('onmessage', tun);
 | ||||
| 		var id = tun.family + ',' + tun.address + ',' + tun.port; | ||||
| 		var service = 'https'; | ||||
| 		var port = services[service]; | ||||
| 		var servername = sni(tun.data); | ||||
|   onconnection: function (tun) { | ||||
|     console.info(''); | ||||
|     if (!tun.service || 'connection' === tun.service) { | ||||
|       throw new Error("missing service: " + JSON.stringify(tun)); | ||||
|     } | ||||
|     console.info('[onConnection]'); | ||||
|     count += 1; | ||||
|   } | ||||
| , onmessage: function (tun) { | ||||
|     //console.log('onmessage', tun);
 | ||||
|     var id = tun.family + ',' + tun.address + ',' + tun.port; | ||||
|     var service = 'https'; | ||||
|     var port = services[service]; | ||||
|     var servername = sni(tun.data); | ||||
| 
 | ||||
| 		console.info( | ||||
| 			'[onMessage]', | ||||
| 			service, | ||||
| 			port, | ||||
| 			servername, | ||||
| 			tun.data.byteLength | ||||
| 		); | ||||
| 		if (!tun.data.equals(hello)) { | ||||
| 			throw new Error( | ||||
| 				"'data' packet is not equal to original 'hello' packet" | ||||
| 			); | ||||
| 		} | ||||
| 		//console.log('all', tun.data.byteLength, 'bytes are equal');
 | ||||
| 		//console.log('src:', tun.family, tun.address + ':' + tun.port + ':' + tun.serviceport);
 | ||||
| 		//console.log('dst:', 'IPv4 127.0.0.1:' + port);
 | ||||
|     console.info('[onMessage]', service, port, servername, tun.data.byteLength); | ||||
|     if (!tun.data.equals(hello)) { | ||||
|       throw new Error("'data' packet is not equal to original 'hello' packet"); | ||||
|     } | ||||
|     //console.log('all', tun.data.byteLength, 'bytes are equal');
 | ||||
|     //console.log('src:', tun.family, tun.address + ':' + tun.port + ':' + tun.serviceport);
 | ||||
|     //console.log('dst:', 'IPv4 127.0.0.1:' + port);
 | ||||
| 
 | ||||
| 		if (!clients[id]) { | ||||
| 			clients[id] = true; | ||||
| 			if (!servername) { | ||||
| 				throw new Error("no servername found for '" + id + "'"); | ||||
| 			} | ||||
| 			//console.log("servername: '" + servername + "'", tun.name);
 | ||||
| 		} | ||||
|     if (!clients[id]) { | ||||
|       clients[id] = true; | ||||
|       if (!servername) { | ||||
|         throw new Error("no servername found for '" + id + "'"); | ||||
|       } | ||||
|       //console.log("servername: '" + servername + "'", tun.name);
 | ||||
|     } | ||||
| 
 | ||||
| 		count += 1; | ||||
| 	}, | ||||
| 	onerror: function() { | ||||
| 		throw new Error('Did not expect onerror'); | ||||
| 	}, | ||||
| 	onend: function() { | ||||
| 		console.info('[onEnd]'); | ||||
| 		count += 1; | ||||
| 	} | ||||
|     count += 1; | ||||
|   } | ||||
| , onerror: function () { | ||||
|     throw new Error("Did not expect onerror"); | ||||
|   } | ||||
| , onend: function () { | ||||
|     console.info('[onEnd]'); | ||||
|     count += 1; | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| var packts, packed; | ||||
| @ -125,16 +93,16 @@ packts.push(packer.packHeader(getAddress(), null, 'end')); | ||||
| packed = Buffer.concat(packts); | ||||
| 
 | ||||
| if (!packed.equals(buf)) { | ||||
| 	console.error(''); | ||||
| 	console.error(buf.toString('hex') === packed.toString('hex')); | ||||
| 	console.error(''); | ||||
| 	console.error('auto-packed:'); | ||||
| 	console.error(packed.toString('hex'), packed.byteLength); | ||||
| 	console.error(''); | ||||
| 	console.error('hand-packed:'); | ||||
| 	console.error(buf.toString('hex'), buf.byteLength); | ||||
| 	console.error(''); | ||||
| 	throw new Error('packer (new) did not pack as expected'); | ||||
|   console.error(""); | ||||
|   console.error(buf.toString('hex') === packed.toString('hex')); | ||||
|   console.error(""); | ||||
|   console.error("auto-packed:"); | ||||
|   console.error(packed.toString('hex'), packed.byteLength); | ||||
|   console.error(""); | ||||
|   console.error("hand-packed:"); | ||||
|   console.error(buf.toString('hex'), buf.byteLength); | ||||
|   console.error(""); | ||||
|   throw new Error("packer (new) did not pack as expected"); | ||||
| } | ||||
| 
 | ||||
| packts = []; | ||||
| @ -158,26 +126,27 @@ packed = Buffer.concat(packts); | ||||
| // 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$/, ''); | ||||
| 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.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
 | ||||
| @ -187,14 +156,16 @@ clients = {}; | ||||
| machine.fns.addChunk(buf); | ||||
| console.info(''); | ||||
| 
 | ||||
| 
 | ||||
| // messages one byte at a time
 | ||||
| console.info('[BYTE-BY-BYTE BUFFER]', 1); | ||||
| clients = {}; | ||||
| buf.forEach(function(byte) { | ||||
| 	machine.fns.addChunk(Buffer.from([byte])); | ||||
| buf.forEach(function (byte) { | ||||
|   machine.fns.addChunk(Buffer.from([ byte ])); | ||||
| }); | ||||
| console.info(''); | ||||
| 
 | ||||
| 
 | ||||
| // split messages in overlapping thirds
 | ||||
| // 0-2      (2)
 | ||||
| // 2-24     (22)
 | ||||
| @ -202,27 +173,26 @@ console.info(''); | ||||
| // 223-225  (2)
 | ||||
| // 225-247  (22)
 | ||||
| // 247-446  (199)
 | ||||
| buf = Buffer.concat([buf, buf]); | ||||
| buf = Buffer.concat([ buf, buf ]); | ||||
| console.info('[OVERLAPPING BUFFERS]', buf.length); | ||||
| clients = {}; | ||||
| [ | ||||
| 	buf.slice(0, 7), // version + header
 | ||||
| 	buf.slice(7, 14), // header
 | ||||
| 	buf.slice(14, 21), // header
 | ||||
| 	buf.slice(21, 28), // header + body
 | ||||
| 	buf.slice(28, 217), // body
 | ||||
| 	buf.slice(217, 224), // body + version
 | ||||
| 	buf.slice(224, 238), // version + header
 | ||||
| 	buf.slice(238, buf.byteLength) // header + body
 | ||||
| ].forEach(function(buf) { | ||||
| 	machine.fns.addChunk(Buffer.from(buf)); | ||||
| [ buf.slice(0, 7)                 // version + header
 | ||||
| , buf.slice(7, 14)                // header
 | ||||
| , buf.slice(14, 21)               // header
 | ||||
| , buf.slice(21, 28)               // header + body
 | ||||
| , buf.slice(28, 217)              // body
 | ||||
| , buf.slice(217, 224)             // body + version
 | ||||
| , buf.slice(224, 238)             // version + header
 | ||||
| , buf.slice(238, buf.byteLength)  // header + body
 | ||||
| ].forEach(function (buf) { | ||||
|   machine.fns.addChunk(Buffer.from(buf)); | ||||
| }); | ||||
| console.info(''); | ||||
| 
 | ||||
| process.on('exit', function() { | ||||
| 	if (count !== 12) { | ||||
| 		throw new Error('should have delivered 12 messages, not ' + count); | ||||
| 	} | ||||
| 	console.info('TESTS PASS'); | ||||
| 	console.info(''); | ||||
| process.on('exit', function () { | ||||
|   if (count !== 12) { | ||||
|     throw new Error("should have delivered 12 messages, not " + count); | ||||
|   } | ||||
|   console.info('TESTS PASS'); | ||||
|   console.info(''); | ||||
| }); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user