diff --git a/README.md b/README.md index fdfe2d6..bdb8a18 100644 --- a/README.md +++ b/README.md @@ -77,11 +77,14 @@ Where `input.json` looks something like this: } ``` +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 + 0 1 2 3 4 5 6 7 8 9 A B C D D F 0000000 16 03 01 00 c2 01 00 00 be 03 03 57 e3 76 50 66 0000010 03 df 99 76 24 c8 31 e6 e8 08 34 6b b4 7b bb 2c 0000020 f3 17 aa 5c ec 09 da da 83 5a b2 00 00 56 00 ff @@ -98,14 +101,18 @@ and `sni.tcp.bin` is any captured tcp packet, such as this one with a tls hello: 00000c7 ``` +Tunneled TCP SNI Packet +----------------------- + You should see that the result is simply all of the original packet with a leading header. Note that `16 03 01 00` starts at the 29th byte (at index 28 or 0x1C) instead of at index 0: ``` - 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 fe 1a 49 50 76 34 2c 31 32 37 2e 30 2e 31 2e 31 <-- 0xfe = v1, 0x1a = 26 more bytes for header -0000010 2c 34 34 33 2c 31 39 39 2c 66 6f 6f 16 03 01 00 <-- first 4 bytes of tcp packet +0000010 2c 34 34 33 2c 31 39 39 2c 66 6f 6f + 16 03 01 00 <-- first 4 bytes of tcp packet 0000020 c2 01 00 00 be 03 03 57 e3 76 50 66 03 df 99 76 0000030 24 c8 31 e6 e8 08 34 6b b4 7b bb 2c f3 17 aa 5c 0000040 ec 09 da da 83 5a b2 00 00 56 00 ff c0 24 c0 23 @@ -120,4 +127,4 @@ Note that `16 03 01 00` starts at the 29th byte (at index 28 or 0x1C) instead of 00000d0 02 01 00 00 0d 00 0c 00 0a 05 01 04 01 02 01 04 00000e0 03 02 03 00000e3 -``` +``` \ No newline at end of file diff --git a/index.js b/index.js index b7a27ad..4f0bf3c 100644 --- a/index.js +++ b/index.js @@ -226,24 +226,30 @@ Packer.pack = function (address, data, service) { return buf; }; +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) {} + + try { + value = value || socket._handle._parentWrap[propName]; + value = value || socket._handle._parentWrap._handle.owner.stream[propName]; + } catch (e) {} + + return value || ''; +} Packer.socketToAddr = function (socket) { // TODO BUG XXX // https://github.com/nodejs/node/issues/8854 // tlsSocket.remoteAddress = remoteAddress; // causes core dump // console.log(tlsSocket.remoteAddress); - function extractValue(name) { - return socket[name] - || socket['_'+name] - || socket._handle._parentWrap[name] - || socket._handle._parentWrap._handle.owner.stream[name] - ; - } - return { - family: extractValue('remoteFamily') - , address: extractValue('remoteAddress') - , port: extractValue('remotePort') + family: extractSocketProp(socket, 'remoteFamily') + , address: extractSocketProp(socket, 'remoteAddress') + , port: extractSocketProp(socket, 'remotePort') }; }; @@ -262,6 +268,53 @@ Packer.socketToId = function (socket) { * */ + var addressNames = [ + 'remoteAddress' + , 'remotePort' + , 'remoteFamily' + , 'localAddress' + , 'localPort' + ]; +// Imporoved workaround for https://github.com/nodejs/node/issues/8854 +// Unlike Duplex this should handle all of the events needed to make everything work. +Packer.wrapSocket = function (socket) { + var myDuplex = new require('stream').Duplex(); + addressNames.forEach(function (name) { + myDuplex[name] = extractSocketProp(socket, name); + }); + + // Handle everything needed for the write part of the Duplex. We need to overwrite the + // `end` function because there is no other way to know when the other side calls it. + myDuplex._write = socket.write.bind(socket); + myDuplex.end = socket.end.bind(socket); + + // Handle everything needed for the read part of the Duplex. See the example under + // https://nodejs.org/api/stream.html#stream_readable_push_chunk_encoding. + myDuplex._read = function () { + socket.resume(); + }; + socket.on('data', function (chunk) { + if (!myDuplex.push(chunk)) { + socket.pause(); + } + }); + socket.on('end', function () { + myDuplex.push(null); + }); + + // Handle the the things not directly related to reading or writing + socket.on('error', function (err) { + console.error('[error] wrapped socket errored - ' + err.toString()); + myDuplex.emit('error', err); + }); + socket.on('close', function () { + myDuplex.emit('close'); + }); + myDuplex.destroy = socket.destroy.bind(socket); + + return myDuplex; +}; + var Transform = require('stream').Transform; var util = require('util'); @@ -274,23 +327,20 @@ function MyTransform(options) { Transform.call(this, options); } util.inherits(MyTransform, Transform); -function transform(me, data, encoding, callback) { - var address = me.__my_addr; - address.service = address.service || me.__my_service; - me.push(Packer.pack(address, data)); - callback(); -} MyTransform.prototype._transform = function (data, encoding, callback) { - return transform(this, data, encoding, callback); + var address = this.__my_addr; + + address.service = address.service || this.__my_service; + 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(); + this.__my_socket.write(chunk, encoding, cb); } , read: function (size) { //console.log('_read'); @@ -302,6 +352,11 @@ var Dup = { } }; 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 diff --git a/package.json b/package.json index 6e09267..9d24193 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tunnel-packer", - "version": "1.2.1", + "version": "1.3.1", "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": {