Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 85dad9d458 | |||
| 2b763f8606 | 
							
								
								
									
										73
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								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 | ||||||
| 
 | 
 | ||||||
| @ -17,8 +15,7 @@ Browser <--/                   \--> Device | |||||||
| 
 | 
 | ||||||
| It's the kind of thing you'd use to build a poor man's VPN, or port-forward router. | It's the kind of thing you'd use to build a poor man's VPN, or port-forward router. | ||||||
| 
 | 
 | ||||||
| The M-PROXY Protocol | # The M-PROXY Protocol | ||||||
| =================== |  | ||||||
| 
 | 
 | ||||||
| This is similar to "The PROXY Protocol" (a la HAProxy), but desgined for multiplexed tls, http, tcp, and udp | This is similar to "The PROXY Protocol" (a la HAProxy), but desgined for multiplexed tls, http, tcp, and udp | ||||||
| tunneled over arbitrary streams (such as WebSockets). | tunneled over arbitrary streams (such as WebSockets). | ||||||
| @ -60,8 +57,7 @@ service port             (string) The listening port, such as 443. Useful for no | |||||||
| host or server name      (string) Useful for services that can be routed by name, such as http, https, smtp, and dns. | host or server name      (string) Useful for services that can be routed by name, such as http, https, smtp, and dns. | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Tunneled TCP SNI Packet | ## Tunneled TCP SNI Packet | ||||||
| ----------------------- |  | ||||||
| 
 | 
 | ||||||
| You should see that the result is simply all of the original packet with a leading header. | You should see that the result is simply all of the original packet with a leading header. | ||||||
| 
 | 
 | ||||||
| @ -91,15 +87,13 @@ Note that `16 03 01 00` starts at the 29th byte (at index 28 or 0x1C) instead of | |||||||
| The v1 header uses strings for address and service descriptor information, | The v1 header uses strings for address and service descriptor information, | ||||||
| but future versions may be binary. | but future versions may be binary. | ||||||
| 
 | 
 | ||||||
| API | # API | ||||||
| === |  | ||||||
| 
 | 
 | ||||||
| ```js | ```js | ||||||
| var Packer = require('proxy-packer'); | var Packer = require('proxy-packer'); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Unpacker / Parser State Machine | ## Unpacker / Parser State Machine | ||||||
| ----------------------- |  | ||||||
| 
 | 
 | ||||||
| The unpacker creates a state machine. | The unpacker creates a state machine. | ||||||
| 
 | 
 | ||||||
| @ -108,28 +102,28 @@ composing a full message with header and data (unless data length is 0). | |||||||
| 
 | 
 | ||||||
| The state machine progresses through these states: | The state machine progresses through these states: | ||||||
| 
 | 
 | ||||||
| * version | -   version | ||||||
| * headerLength | -   headerLength | ||||||
| * header | -   header | ||||||
| * data | -   data | ||||||
| 
 | 
 | ||||||
| At the end of the data event (which may or may not contain a buffer of data) | At the end of the data event (which may or may not contain a buffer of data) | ||||||
| one of the appropriate handlers will be called. | one of the appropriate handlers will be called. | ||||||
| 
 | 
 | ||||||
| * control | -   control | ||||||
| * connection | -   connection | ||||||
| * message | -   message | ||||||
| * pause | -   pause | ||||||
| * resume | -   resume | ||||||
| * end | -   end | ||||||
| * error | -   error | ||||||
| 
 | 
 | ||||||
| ```js | ```js | ||||||
| unpacker = Packer.create(handlers); // Create a state machine for unpacking | unpacker = Packer.create(handlers); // Create a state machine for unpacking | ||||||
| 
 | 
 | ||||||
| unpacker.fns.addData(chunk); // process a chunk of data | unpacker.fns.addData(chunk); // process a chunk of data | ||||||
| 
 | 
 | ||||||
| handlers.oncontrol = function (tun) { }                   // for communicating with the proxy | handlers.oncontrol = function(tun) {}; // for communicating with the proxy | ||||||
| // tun.data is an array | // tun.data is an array | ||||||
| //     '[ -1, "[Error] bad hello" ]' | //     '[ -1, "[Error] bad hello" ]' | ||||||
| //     '[ 0, "[Error] out-of-band error message" ]' | //     '[ 0, "[Error] out-of-band error message" ]' | ||||||
| @ -137,22 +131,22 @@ handlers.oncontrol = function (tun) { }                   // for communicating w | |||||||
| //     '[ 1, "add_token" ]' | //     '[ 1, "add_token" ]' | ||||||
| //     '[ 1, "delete_token" ]' | //     '[ 1, "delete_token" ]' | ||||||
| 
 | 
 | ||||||
| handlers.onconnection = function (tun) { }                // a client has established a connection | handlers.onconnection = function(tun) {}; // a client has established a connection | ||||||
| 
 | 
 | ||||||
| handlers.onmessage = function (tun) { }                   // a client has sent a message | handlers.onmessage = function(tun) {}; // a client has sent a message | ||||||
| // tun = { family, address, port, data | // tun = { family, address, port, data | ||||||
| //       , service, serviceport, name }; | //       , service, serviceport, name }; | ||||||
| 
 | 
 | ||||||
| handlers.onpause = function (tun) { }                     // proxy requests to pause upload to a client | handlers.onpause = function(tun) {}; // proxy requests to pause upload to a client | ||||||
| // tun = { family, address, port }; | // tun = { family, address, port }; | ||||||
| 
 | 
 | ||||||
| handlers.onresume = function (tun) { }                    // proxy requests to resume upload to a client | handlers.onresume = function(tun) {}; // proxy requests to resume upload to a client | ||||||
| // tun = { family, address, port }; | // tun = { family, address, port }; | ||||||
| 
 | 
 | ||||||
| handlers.onend = function (tun) { }                       // proxy requests to close a client's socket | handlers.onend = function(tun) {}; // proxy requests to close a client's socket | ||||||
| // tun = { family, address, port }; | // tun = { family, address, port }; | ||||||
| 
 | 
 | ||||||
| handlers.onerror = function (err) { }                     // proxy is relaying a client's error | handlers.onerror = function(err) {}; // proxy is relaying a client's error | ||||||
| // err = { message, family, address, port }; | // err = { message, family, address, port }; | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| @ -163,8 +157,7 @@ handlers.onconnect = function (tun) { }                   // a new client has co | |||||||
| 
 | 
 | ||||||
| --> | --> | ||||||
| 
 | 
 | ||||||
| Packer & Extras | ## Packer & Extras | ||||||
| ------ |  | ||||||
| 
 | 
 | ||||||
| Packs header metadata about connection into a buffer (potentially with original data), ready to send. | Packs header metadata about connection into a buffer (potentially with original data), ready to send. | ||||||
| 
 | 
 | ||||||
| @ -195,12 +188,12 @@ var socket = Packer.Stream.wrapSocket(socketOrStream);   // workaround for https | |||||||
| ```js | ```js | ||||||
| var myTransform = Packer.Transform.create({ | var myTransform = Packer.Transform.create({ | ||||||
| 	address: { | 	address: { | ||||||
|     family: '...' | 		family: '...', | ||||||
|   , address: '...' | 		address: '...', | ||||||
|   , port: '...' | 		port: '...' | ||||||
|   } | 	}, | ||||||
| 	// hint at the service to be used | 	// hint at the service to be used | ||||||
| , service: 'https' | 	service: 'https' | ||||||
| }); | }); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| @ -217,6 +210,7 @@ hexdump output.bin | |||||||
| Where `input.json` looks something like this: | Where `input.json` looks something like this: | ||||||
| 
 | 
 | ||||||
| `input.json`: | `input.json`: | ||||||
|  | 
 | ||||||
| ``` | ``` | ||||||
| { "version": 1 | { "version": 1 | ||||||
| , "address": { | , "address": { | ||||||
| @ -231,12 +225,12 @@ Where `input.json` looks something like this: | |||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Raw TCP SNI Packet | ## Raw TCP SNI Packet | ||||||
| ------------------ |  | ||||||
| 
 | 
 | ||||||
| and `sni.tcp.bin` is any captured tcp packet, such as this one with a tls hello: | and `sni.tcp.bin` is any captured tcp packet, such as this one with a tls hello: | ||||||
| 
 | 
 | ||||||
| `sni.tcp.bin`: | `sni.tcp.bin`: | ||||||
|  | 
 | ||||||
| ``` | ``` | ||||||
|          0  1  2  3  4  5  6  7  8  9  A  B  C  D  D  F |          0  1  2  3  4  5  6  7  8  9  A  B  C  D  D  F | ||||||
| 0000000 16 03 01 00 c2 01 00 00 be 03 03 57 e3 76 50 66 | 0000000 16 03 01 00 c2 01 00 00 be 03 03 57 e3 76 50 66 | ||||||
| @ -255,8 +249,7 @@ and `sni.tcp.bin` is any captured tcp packet, such as this one with a tls hello: | |||||||
| 00000c7 | 00000c7 | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Tunneled TCP SNI Packet | ## Tunneled TCP SNI Packet | ||||||
| ----------------------- |  | ||||||
| 
 | 
 | ||||||
| You should see that the result is simply all of the original packet with a leading header. | You should see that the result is simply all of the original packet with a leading header. | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										164
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								index.js
									
									
									
									
									
								
							| @ -3,36 +3,37 @@ | |||||||
| var Packer = module.exports; | var Packer = module.exports; | ||||||
| 
 | 
 | ||||||
| var serviceEvents = { | var serviceEvents = { | ||||||
|   default: 'tunnelData' | 	default: 'tunnelData', | ||||||
| , connection: 'tunnelConnection' | 	connection: 'tunnelConnection', | ||||||
| , control: 'tunnelControl' | 	control: 'tunnelControl', | ||||||
| , error:   'tunnelError' | 	error: 'tunnelError', | ||||||
| , end:     'tunnelEnd' | 	end: 'tunnelEnd', | ||||||
| , pause:   'tunnelPause' | 	pause: 'tunnelPause', | ||||||
| , resume:  'tunnelResume' | 	resume: 'tunnelResume' | ||||||
| }; | }; | ||||||
| var serviceFuncs = { | var serviceFuncs = { | ||||||
|   default: 'onmessage' | 	default: 'onmessage', | ||||||
| , connection: 'onconnection' | 	connection: 'onconnection', | ||||||
| , control: 'oncontrol' | 	control: 'oncontrol', | ||||||
| , error:   'onerror' | 	error: 'onerror', | ||||||
| , end:     'onend' | 	end: 'onend', | ||||||
| , pause:   'onpause' | 	pause: 'onpause', | ||||||
| , resume:  'onresume' | 	resume: 'onresume' | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| Packer.create = function(opts) { | Packer.create = function(opts) { | ||||||
| 	var machine; | 	var machine; | ||||||
| 
 | 
 | ||||||
| 	if (!opts.onMessage && !opts.onmessage) { | 	if (!opts.onMessage && !opts.onmessage) { | ||||||
|     machine = new (require('events').EventEmitter)(); | 		machine = new (require('events')).EventEmitter(); | ||||||
| 	} else { | 	} else { | ||||||
| 		machine = {}; | 		machine = {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	machine.onmessage = opts.onmessage || opts.onMessage; | 	machine.onmessage = opts.onmessage || opts.onMessage; | ||||||
| 	machine.oncontrol = opts.oncontrol || opts.onControl; | 	machine.oncontrol = opts.oncontrol || opts.onControl; | ||||||
|   machine.onconnection = opts.onconnection || opts.onConnection || function () {}; | 	machine.onconnection = | ||||||
|  | 		opts.onconnection || opts.onConnection || function() {}; | ||||||
| 	machine.onerror = opts.onerror || opts.onError; | 	machine.onerror = opts.onerror || opts.onError; | ||||||
| 	machine.onend = opts.onend || opts.onEnd; | 	machine.onend = opts.onend || opts.onEnd; | ||||||
| 	machine.onpause = opts.onpause || opts.onPause; | 	machine.onpause = opts.onpause || opts.onPause; | ||||||
| @ -46,7 +47,7 @@ Packer.create = function (opts) { | |||||||
| 	machine.bufIndex = 0; | 	machine.bufIndex = 0; | ||||||
| 	machine.fns.collectData = function(chunk, size) { | 	machine.fns.collectData = function(chunk, size) { | ||||||
| 		var chunkLeft = chunk.length - machine.chunkIndex; | 		var chunkLeft = chunk.length - machine.chunkIndex; | ||||||
|     var hasLen = (size > 0); | 		var hasLen = size > 0; | ||||||
| 
 | 
 | ||||||
| 		if (!hasLen) { | 		if (!hasLen) { | ||||||
| 			return Buffer.alloc(0); | 			return Buffer.alloc(0); | ||||||
| @ -67,7 +68,10 @@ Packer.create = function (opts) { | |||||||
| 
 | 
 | ||||||
| 		// 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, | ||||||
|  | 			machine.chunkIndex + partLen | ||||||
|  | 		); | ||||||
| 		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.
 | ||||||
| @ -87,8 +91,8 @@ Packer.create = function (opts) { | |||||||
| 	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; | ||||||
| @ -96,7 +100,6 @@ Packer.create = function (opts) { | |||||||
| 		return true; | 		return true; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 	machine.headerLen = 0; | 	machine.headerLen = 0; | ||||||
| 	machine.fns.headerLength = function(chunk) { | 	machine.fns.headerLength = function(chunk) { | ||||||
| 		//console.log('');
 | 		//console.log('');
 | ||||||
| @ -146,7 +149,6 @@ Packer.create = function (opts) { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 		//
 | 		//
 | ||||||
| 		// data, end, error
 | 		// data, end, error
 | ||||||
| 		//
 | 		//
 | ||||||
| @ -174,9 +176,15 @@ Packer.create = function (opts) { | |||||||
| 
 | 
 | ||||||
| 		//console.log('msn', machine.service);
 | 		//console.log('msn', machine.service);
 | ||||||
| 		if (machine.emit) { | 		if (machine.emit) { | ||||||
|       machine.emit(serviceEvents[machine.service] || serviceEvents[msg.service] || serviceEvents.default); | 			machine.emit( | ||||||
|  | 				serviceEvents[machine.service] || | ||||||
|  | 					serviceEvents[msg.service] || | ||||||
|  | 					serviceEvents.default | ||||||
|  | 			); | ||||||
| 		} else { | 		} else { | ||||||
|       (machine[serviceFuncs[machine.service]] || machine[serviceFuncs[msg.service]] || machine[serviceFuncs.default])(msg); | 			(machine[serviceFuncs[machine.service]] || | ||||||
|  | 				machine[serviceFuncs[msg.service]] || | ||||||
|  | 				machine[serviceFuncs.default])(msg); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return true; | 		return true; | ||||||
| @ -197,7 +205,7 @@ Packer.create = function (opts) { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if ('data' === machine.states[machine.state] && 0 === machine.bodyLen) { | 		if ('data' === machine.states[machine.state] && 0 === machine.bodyLen) { | ||||||
|       machine.fns[machine.states[machine.state]](chunk) | 			machine.fns[machine.states[machine.state]](chunk); | ||||||
| 			machine.state += 1; | 			machine.state += 1; | ||||||
| 			machine.state %= machine.states.length; | 			machine.state %= machine.states.length; | ||||||
| 		} | 		} | ||||||
| @ -222,32 +230,48 @@ Packer.packHeader = function (meta, data, service, andBody, oldways) { | |||||||
| 		meta.service = service; | 		meta.service = service; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|   var size = data && data.byteLength || 0; | 	var size = (data && data.byteLength) || 0; | ||||||
| 	var sizeReserve = andBody ? size : 0; | 	var sizeReserve = andBody ? size : 0; | ||||||
| 	var version = 1; | 	var version = 1; | ||||||
| 	var header; | 	var header; | ||||||
| 	if (service === 'control') { | 	if (service === 'control') { | ||||||
| 		header = Buffer.from(['', '', '', size, service].join(',')); | 		header = Buffer.from(['', '', '', size, service].join(',')); | ||||||
|   } | 	} else if (service === 'connection') { | ||||||
|   else if (service === 'connection') { | 		header = Buffer.from( | ||||||
|     header = Buffer.from([ | 			[ | ||||||
|       meta.family, meta.address, meta.port, size, | 				meta.family, | ||||||
|       'connection', (meta.serviceport || ''), (meta.name || ''), | 				meta.address, | ||||||
|       (meta.service || '') | 				meta.port, | ||||||
|     ].join(',')); | 				size, | ||||||
|   } | 				'connection', | ||||||
|   else { | 				meta.serviceport || '', | ||||||
|     header = Buffer.from([ | 				meta.name || '', | ||||||
|       meta.family, meta.address, meta.port, size, | 				meta.service || '' | ||||||
|       (meta.service || ''), (meta.serviceport || ''), (meta.name || '') | 			].join(',') | ||||||
|     ].join(',')); | 		); | ||||||
|  | 	} else { | ||||||
|  | 		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 metaBuf = Buffer.from([255 - version, header.length]); | ||||||
|   var buf = Buffer.alloc(metaBuf.byteLength + header.byteLength + sizeReserve); | 	var buf = Buffer.alloc( | ||||||
|  | 		metaBuf.byteLength + header.byteLength + sizeReserve | ||||||
|  | 	); | ||||||
| 
 | 
 | ||||||
| 	metaBuf.copy(buf, 0); | 	metaBuf.copy(buf, 0); | ||||||
| 	header.copy(buf, 2); | 	header.copy(buf, 2); | ||||||
|   if (sizeReserve) { data.copy(buf, 2 + header.byteLength); } | 	if (sizeReserve) { | ||||||
|  | 		data.copy(buf, 2 + header.byteLength); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	return buf; | 	return buf; | ||||||
| }; | }; | ||||||
| @ -268,30 +292,31 @@ function extractSocketProps(socket, propNames) { | |||||||
| 		}); | 		}); | ||||||
| 	} else if (socket._handle) { | 	} else if (socket._handle) { | ||||||
| 		if ( | 		if ( | ||||||
|       socket._handle._parent | 			socket._handle._parent && | ||||||
|       && socket._handle._parent.owner | 			socket._handle._parent.owner && | ||||||
|       && socket._handle._parent.owner.stream | 			socket._handle._parent.owner.stream && | ||||||
|       && socket._handle._parent.owner.stream.remotePort | 			socket._handle._parent.owner.stream.remotePort | ||||||
| 		) { | 		) { | ||||||
| 			propNames.forEach(function(propName) { | 			propNames.forEach(function(propName) { | ||||||
| 				props[propName] = socket._handle._parent.owner.stream[propName]; | 				props[propName] = socket._handle._parent.owner.stream[propName]; | ||||||
| 			}); | 			}); | ||||||
| 		} else if ( | 		} else if ( | ||||||
|       socket._handle._parentWrap | 			socket._handle._parentWrap && | ||||||
|       && socket._handle._parentWrap.remotePort | 			socket._handle._parentWrap.remotePort | ||||||
| 		) { | 		) { | ||||||
| 			propNames.forEach(function(propName) { | 			propNames.forEach(function(propName) { | ||||||
| 				props[propName] = socket._handle._parentWrap[propName]; | 				props[propName] = socket._handle._parentWrap[propName]; | ||||||
| 			}); | 			}); | ||||||
| 		} else if ( | 		} else if ( | ||||||
|       socket._handle._parentWrap | 			socket._handle._parentWrap && | ||||||
|       && socket._handle._parentWrap._handle | 			socket._handle._parentWrap._handle && | ||||||
|       && socket._handle._parentWrap._handle.owner | 			socket._handle._parentWrap._handle.owner && | ||||||
|       && socket._handle._parentWrap._handle.owner.stream | 			socket._handle._parentWrap._handle.owner.stream && | ||||||
|       && socket._handle._parentWrap._handle.owner.stream.remotePort | 			socket._handle._parentWrap._handle.owner.stream.remotePort | ||||||
| 		) { | 		) { | ||||||
| 			propNames.forEach(function(propName) { | 			propNames.forEach(function(propName) { | ||||||
|         props[propName] = socket._handle._parentWrap._handle.owner.stream[propName]; | 				props[propName] = | ||||||
|  | 					socket._handle._parentWrap._handle.owner.stream[propName]; | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -306,7 +331,8 @@ function extractSocketProp(socket, propName) { | |||||||
| 
 | 
 | ||||||
| 	try { | 	try { | ||||||
| 		value = value || socket._handle._parentWrap[propName]; | 		value = value || socket._handle._parentWrap[propName]; | ||||||
|     value = value || socket._handle._parentWrap._handle.owner.stream[propName]; | 		value = | ||||||
|  | 			value || socket._handle._parentWrap._handle.owner.stream[propName]; | ||||||
| 	} catch (e) {} | 	} catch (e) {} | ||||||
| 
 | 
 | ||||||
| 	return value || ''; | 	return value || ''; | ||||||
| @ -317,12 +343,17 @@ Packer.socketToAddr = function (socket) { | |||||||
| 	// tlsSocket.remoteAddress = remoteAddress; // causes core dump
 | 	// tlsSocket.remoteAddress = remoteAddress; // causes core dump
 | ||||||
| 	// console.log(tlsSocket.remoteAddress);
 | 	// console.log(tlsSocket.remoteAddress);
 | ||||||
| 
 | 
 | ||||||
|   var props = extractSocketProps(socket, [ 'remoteFamily', 'remoteAddress', 'remotePort', 'localPort' ]); | 	var props = extractSocketProps(socket, [ | ||||||
|  | 		'remoteFamily', | ||||||
|  | 		'remoteAddress', | ||||||
|  | 		'remotePort', | ||||||
|  | 		'localPort' | ||||||
|  | 	]); | ||||||
| 	return { | 	return { | ||||||
|     family:  props.remoteFamily | 		family: props.remoteFamily, | ||||||
|   , address: props.remoteAddress | 		address: props.remoteAddress, | ||||||
|   , port:    props.remotePort | 		port: props.remotePort, | ||||||
|   , serviceport: props.localPort | 		serviceport: props.localPort | ||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -334,13 +365,12 @@ 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 = [ | ||||||
| @ -444,8 +474,8 @@ 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) { | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
| 	"name": "proxy-packer", | 	"name": "proxy-packer", | ||||||
|   "version": "2.0.3", | 	"version": "2.0.4", | ||||||
| 	"description": "A strategy for packing and unpacking a proxy stream (i.e. packets through a tunnel). Handles multiplexed and tls connections. Used by telebit and telebitd.", | 	"description": "A strategy for packing and unpacking a proxy stream (i.e. packets through a tunnel). Handles multiplexed and tls connections. Used by telebit and telebitd.", | ||||||
| 	"main": "index.js", | 	"main": "index.js", | ||||||
| 	"scripts": { | 	"scripts": { | ||||||
|  | |||||||
| @ -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" | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										26
									
								
								test/pack.js
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								test/pack.js
									
									
									
									
									
								
							| @ -1,4 +1,4 @@ | |||||||
| ;(function () { | (function() { | ||||||
| 	'use strict'; | 	'use strict'; | ||||||
| 
 | 
 | ||||||
| 	var fs = require('fs'); | 	var fs = require('fs'); | ||||||
| @ -7,20 +7,23 @@ 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( | ||||||
|  | 		path.resolve(path.dirname(infile), json.filepath), | ||||||
|  | 		null | ||||||
|  | 	); | ||||||
| 	var Packer = require('../index.js'); | 	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() { | ||||||
| @ -40,6 +43,13 @@ 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)" | ||||||
|  | 	); | ||||||
|  | })(); | ||||||
|  | |||||||
							
								
								
									
										160
									
								
								test/parse.js
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								test/parse.js
									
									
									
									
									
								
							| @ -5,37 +5,61 @@ var hello = require('fs').readFileSync(__dirname + '/sni.hello.bin'); | |||||||
| var version = 1; | var version = 1; | ||||||
| function getAddress() { | function getAddress() { | ||||||
| 	return { | 	return { | ||||||
|     family: 'IPv4' | 		family: 'IPv4', | ||||||
|   , address: '127.0.1.1' | 		address: '127.0.1.1', | ||||||
|   , port: 4321 | 		port: 4321, | ||||||
|   , service: 'foo-https' | 		service: 'foo-https', | ||||||
|   , serviceport: 443 | 		serviceport: 443, | ||||||
|   , name: 'foo-pokemap.hellabit.com' | 		name: 'foo-pokemap.hellabit.com' | ||||||
| 	}; | 	}; | ||||||
| } | } | ||||||
| var addr = getAddress(); | var addr = getAddress(); | ||||||
| var connectionHeader = addr.family + ',' + addr.address + ',' + addr.port | var connectionHeader = | ||||||
|   + ',0,connection,' | 	addr.family + | ||||||
|   + (addr.serviceport || '') + ',' + (addr.name || '') + ',' + (addr.service || '') | 	',' + | ||||||
|   ; | 	addr.address + | ||||||
| var           header = addr.family + ',' + addr.address + ',' + addr.port | 	',' + | ||||||
|   + ',' + hello.byteLength + ',' + (addr.service || '') + ',' | 	addr.port + | ||||||
|   + (addr.serviceport || '') + ',' + (addr.name || '') | 	',0,connection,' + | ||||||
|   ; | 	(addr.serviceport || '') + | ||||||
| var        endHeader = addr.family + ',' + addr.address + ',' + addr.port | 	',' + | ||||||
|   + ',0,end,' | 	(addr.name || '') + | ||||||
|   + (addr.serviceport || '') + ',' + (addr.name || '') | 	',' + | ||||||
|   ; | 	(addr.service || ''); | ||||||
|  | var header = | ||||||
|  | 	addr.family + | ||||||
|  | 	',' + | ||||||
|  | 	addr.address + | ||||||
|  | 	',' + | ||||||
|  | 	addr.port + | ||||||
|  | 	',' + | ||||||
|  | 	hello.byteLength + | ||||||
|  | 	',' + | ||||||
|  | 	(addr.service || '') + | ||||||
|  | 	',' + | ||||||
|  | 	(addr.serviceport || '') + | ||||||
|  | 	',' + | ||||||
|  | 	(addr.name || ''); | ||||||
|  | var endHeader = | ||||||
|  | 	addr.family + | ||||||
|  | 	',' + | ||||||
|  | 	addr.address + | ||||||
|  | 	',' + | ||||||
|  | 	addr.port + | ||||||
|  | 	',0,end,' + | ||||||
|  | 	(addr.serviceport || '') + | ||||||
|  | 	',' + | ||||||
|  | 	(addr.name || ''); | ||||||
| var buf = Buffer.concat([ | var buf = Buffer.concat([ | ||||||
|   Buffer.from([ 255 - version, connectionHeader.length ]) | 	Buffer.from([255 - version, connectionHeader.length]), | ||||||
| , Buffer.from(connectionHeader) | 	Buffer.from(connectionHeader), | ||||||
| , Buffer.from([ 255 - version, header.length ]) | 	Buffer.from([255 - version, header.length]), | ||||||
| , Buffer.from(header) | 	Buffer.from(header), | ||||||
| , hello | 	hello, | ||||||
| , Buffer.from([ 255 - version, endHeader.length ]) | 	Buffer.from([255 - version, endHeader.length]), | ||||||
| , Buffer.from(endHeader) | 	Buffer.from(endHeader) | ||||||
| ]); | ]); | ||||||
| var services = { 'ssh': 22, 'http': 4080, 'https': 8443 }; | var services = { ssh: 22, http: 4080, https: 8443 }; | ||||||
| var clients = {}; | var clients = {}; | ||||||
| var count = 0; | var count = 0; | ||||||
| var packer = require('../'); | var packer = require('../'); | ||||||
| @ -43,21 +67,29 @@ var machine = packer.create({ | |||||||
| 	onconnection: function(tun) { | 	onconnection: function(tun) { | ||||||
| 		console.info(''); | 		console.info(''); | ||||||
| 		if (!tun.service || 'connection' === tun.service) { | 		if (!tun.service || 'connection' === tun.service) { | ||||||
|       throw new Error("missing service: " + JSON.stringify(tun)); | 			throw new Error('missing service: ' + JSON.stringify(tun)); | ||||||
| 		} | 		} | ||||||
| 		console.info('[onConnection]'); | 		console.info('[onConnection]'); | ||||||
| 		count += 1; | 		count += 1; | ||||||
|   } | 	}, | ||||||
| , onmessage: function (tun) { | 	onmessage: function(tun) { | ||||||
| 		//console.log('onmessage', tun);
 | 		//console.log('onmessage', tun);
 | ||||||
| 		var id = tun.family + ',' + tun.address + ',' + tun.port; | 		var id = tun.family + ',' + tun.address + ',' + tun.port; | ||||||
| 		var service = 'https'; | 		var service = 'https'; | ||||||
| 		var port = services[service]; | 		var port = services[service]; | ||||||
| 		var servername = sni(tun.data); | 		var servername = sni(tun.data); | ||||||
| 
 | 
 | ||||||
|     console.info('[onMessage]', service, port, servername, tun.data.byteLength); | 		console.info( | ||||||
|  | 			'[onMessage]', | ||||||
|  | 			service, | ||||||
|  | 			port, | ||||||
|  | 			servername, | ||||||
|  | 			tun.data.byteLength | ||||||
|  | 		); | ||||||
| 		if (!tun.data.equals(hello)) { | 		if (!tun.data.equals(hello)) { | ||||||
|       throw new Error("'data' packet is not equal to original 'hello' packet"); | 			throw new Error( | ||||||
|  | 				"'data' packet is not equal to original 'hello' packet" | ||||||
|  | 			); | ||||||
| 		} | 		} | ||||||
| 		//console.log('all', tun.data.byteLength, 'bytes are equal');
 | 		//console.log('all', tun.data.byteLength, 'bytes are equal');
 | ||||||
| 		//console.log('src:', tun.family, tun.address + ':' + tun.port + ':' + tun.serviceport);
 | 		//console.log('src:', tun.family, tun.address + ':' + tun.port + ':' + tun.serviceport);
 | ||||||
| @ -72,11 +104,11 @@ var machine = packer.create({ | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		count += 1; | 		count += 1; | ||||||
|   } | 	}, | ||||||
| , onerror: function () { | 	onerror: function() { | ||||||
|     throw new Error("Did not expect onerror"); | 		throw new Error('Did not expect onerror'); | ||||||
|   } | 	}, | ||||||
| , onend: function () { | 	onend: function() { | ||||||
| 		console.info('[onEnd]'); | 		console.info('[onEnd]'); | ||||||
| 		count += 1; | 		count += 1; | ||||||
| 	} | 	} | ||||||
| @ -93,16 +125,16 @@ packts.push(packer.packHeader(getAddress(), null, 'end')); | |||||||
| packed = Buffer.concat(packts); | packed = Buffer.concat(packts); | ||||||
| 
 | 
 | ||||||
| if (!packed.equals(buf)) { | if (!packed.equals(buf)) { | ||||||
|   console.error(""); | 	console.error(''); | ||||||
| 	console.error(buf.toString('hex') === packed.toString('hex')); | 	console.error(buf.toString('hex') === packed.toString('hex')); | ||||||
|   console.error(""); | 	console.error(''); | ||||||
|   console.error("auto-packed:"); | 	console.error('auto-packed:'); | ||||||
| 	console.error(packed.toString('hex'), packed.byteLength); | 	console.error(packed.toString('hex'), packed.byteLength); | ||||||
|   console.error(""); | 	console.error(''); | ||||||
|   console.error("hand-packed:"); | 	console.error('hand-packed:'); | ||||||
| 	console.error(buf.toString('hex'), buf.byteLength); | 	console.error(buf.toString('hex'), buf.byteLength); | ||||||
|   console.error(""); | 	console.error(''); | ||||||
|   throw new Error("packer (new) did not pack as expected"); | 	throw new Error('packer (new) did not pack as expected'); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| packts = []; | packts = []; | ||||||
| @ -126,27 +158,26 @@ packed = Buffer.concat(packts); | |||||||
| // maching a few things on either side.
 | // maching a few things on either side.
 | ||||||
| //
 | //
 | ||||||
| // Only 6 bytes are changed - two 1 => 0, four ' ' => ''
 | // Only 6 bytes are changed - two 1 => 0, four ' ' => ''
 | ||||||
| var hex = packed.toString('hex') | var hex = packed | ||||||
|  | 	.toString('hex') | ||||||
| 	//.replace(/2c313939/, '2c30')
 | 	//.replace(/2c313939/, '2c30')
 | ||||||
| 	.replace(/32312c312c636f/, '32312c302c636f') | 	.replace(/32312c312c636f/, '32312c302c636f') | ||||||
| 	.replace(/3332312c312c656e64/, '3332312c302c656e64') | 	.replace(/3332312c312c656e64/, '3332312c302c656e64') | ||||||
| 	.replace(/7320/, '73') | 	.replace(/7320/, '73') | ||||||
|   .replace(/20$/, '') | 	.replace(/20$/, ''); | ||||||
|   ; |  | ||||||
| if (hex !== buf.toString('hex')) { | if (hex !== buf.toString('hex')) { | ||||||
|   console.error(""); | 	console.error(''); | ||||||
| 	console.error(buf.toString('hex') === hex); | 	console.error(buf.toString('hex') === hex); | ||||||
|   console.error(""); | 	console.error(''); | ||||||
|   console.error("auto-packed:"); | 	console.error('auto-packed:'); | ||||||
| 	console.error(hex, packed.byteLength); | 	console.error(hex, packed.byteLength); | ||||||
|   console.error(""); | 	console.error(''); | ||||||
|   console.error("hand-packed:"); | 	console.error('hand-packed:'); | ||||||
| 	console.error(buf.toString('hex'), buf.byteLength); | 	console.error(buf.toString('hex'), buf.byteLength); | ||||||
|   console.error(""); | 	console.error(''); | ||||||
|   throw new Error("packer (old) did not pack as expected"); | 	throw new Error('packer (old) did not pack as expected'); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| console.info(''); | console.info(''); | ||||||
| 
 | 
 | ||||||
| // full message in one go
 | // full message in one go
 | ||||||
| @ -156,7 +187,6 @@ clients = {}; | |||||||
| machine.fns.addChunk(buf); | machine.fns.addChunk(buf); | ||||||
| console.info(''); | console.info(''); | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| // messages one byte at a time
 | // messages one byte at a time
 | ||||||
| console.info('[BYTE-BY-BYTE BUFFER]', 1); | console.info('[BYTE-BY-BYTE BUFFER]', 1); | ||||||
| clients = {}; | clients = {}; | ||||||
| @ -165,7 +195,6 @@ buf.forEach(function (byte) { | |||||||
| }); | }); | ||||||
| console.info(''); | console.info(''); | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| // split messages in overlapping thirds
 | // split messages in overlapping thirds
 | ||||||
| // 0-2      (2)
 | // 0-2      (2)
 | ||||||
| // 2-24     (22)
 | // 2-24     (22)
 | ||||||
| @ -176,14 +205,15 @@ console.info(''); | |||||||
| buf = Buffer.concat([buf, buf]); | buf = Buffer.concat([buf, buf]); | ||||||
| console.info('[OVERLAPPING BUFFERS]', buf.length); | console.info('[OVERLAPPING BUFFERS]', buf.length); | ||||||
| clients = {}; | clients = {}; | ||||||
| [ buf.slice(0, 7)                 // version + header
 | [ | ||||||
| , buf.slice(7, 14)                // header
 | 	buf.slice(0, 7), // version + header
 | ||||||
| , buf.slice(14, 21)               // header
 | 	buf.slice(7, 14), // header
 | ||||||
| , buf.slice(21, 28)               // header + body
 | 	buf.slice(14, 21), // header
 | ||||||
| , buf.slice(28, 217)              // body
 | 	buf.slice(21, 28), // header + body
 | ||||||
| , buf.slice(217, 224)             // body + version
 | 	buf.slice(28, 217), // body
 | ||||||
| , buf.slice(224, 238)             // version + header
 | 	buf.slice(217, 224), // body + version
 | ||||||
| , buf.slice(238, buf.byteLength)  // header + body
 | 	buf.slice(224, 238), // version + header
 | ||||||
|  | 	buf.slice(238, buf.byteLength) // header + body
 | ||||||
| ].forEach(function(buf) { | ].forEach(function(buf) { | ||||||
| 	machine.fns.addChunk(Buffer.from(buf)); | 	machine.fns.addChunk(Buffer.from(buf)); | ||||||
| }); | }); | ||||||
| @ -191,7 +221,7 @@ console.info(''); | |||||||
| 
 | 
 | ||||||
| process.on('exit', function() { | process.on('exit', function() { | ||||||
| 	if (count !== 12) { | 	if (count !== 12) { | ||||||
|     throw new Error("should have delivered 12 messages, not " + count); | 		throw new Error('should have delivered 12 messages, not ' + count); | ||||||
| 	} | 	} | ||||||
| 	console.info('TESTS PASS'); | 	console.info('TESTS PASS'); | ||||||
| 	console.info(''); | 	console.info(''); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user