diff --git a/client.js b/client.js index df87cf1..273951a 100644 --- a/client.js +++ b/client.js @@ -6,26 +6,34 @@ var sni = require('sni'); // TODO ask oauth3.org where to connect // TODO reconnect on disconnect -function pack(address, data) { - var version = 1; - var header = /*servername + ',' +*/address.family + ',' + address.address + ',' + address.port + ',' + data.byteLength; - var meta = [ 255 - version, header.length ]; - var buf = Buffer.alloc(meta.length + header.length + data.byteLength); - - buf.write(meta[0], 0); - buf.write(meta[1], 1); - buf.write(header, 2); - buf.write(data, 2 + header.length); - - return buf; -} - // Assumption: will not get next tcp packet unless previous packet succeeded //var services = { 'ssh': 22, 'http': 80, 'https': 443 }; var services = { 'ssh': 22, 'http': 4080, 'https': 8443 }; -var tunneler = net.connect({ port: 5443 , host: 'pokemap.hellabit.com' }, function () { - var token = jwt.sign({ name: 'pokemap.hellabit.com' }, 'shhhhh'); - var clients = {}; +var hostname = 'aj.daplie.me'; // 'pokemap.hellabit.com' + +function addrToId(address) { + return address.family + ',' + address.address + ',' + address.port; +} + +/* +function socketToAddr(socket) { + return { family: socket.remoteFamily, address: socket.remoteAddress, port: socket.remotePort }; +} + +function socketToId(socket) { + return addrToId(socketToAddr(socket)); +} +*/ + +var tunneler = net.connect({ port: 5443 , host: hostname }, function () { + var token = jwt.sign({ name: hostname }, 'shhhhh'); + var localclients = {}; + + setInterval(function () { + console.log(''); + console.log('localclients.length:', Object.keys(localclients).length); + console.log(''); + }, 5000); tunneler.write(token); @@ -34,15 +42,30 @@ var tunneler = net.connect({ port: 5443 , host: 'pokemap.hellabit.com' }, functi // a place to store data // file management // Synergy Teamwork Paradigm = Jabberwocky - var machine = require('./machine.js').create(); - machine.onMessage = function (opts) { - var id = opts.family + ',' + opts.address + ',' + opts.port; + var pack = require('tunnel-packer').pack; + + function onMessage(opts) { + var id = addrToId(opts); var service = 'https'; var port = services[service]; - var client; + var lclient; - if (clients[id]) { - clients[id].write(opts.data); + if (opts.data.byteLength < 20) { + if ('|__ERROR__|' === opts.data.toString('utf8') + || '|__END__|' === opts.data.toString('utf8')) { + + console.log("end '" + opts.address + "'"); + if (localclients[id]) { + localclients[id].end(); + delete localclients[id]; + } + return; + } + } + + if (localclients[id]) { + console.log("received data from '" + opts.address + "'", opts.data.byteLength); + localclients[id].write(opts.data); return; } @@ -50,30 +73,36 @@ var tunneler = net.connect({ port: 5443 , host: 'pokemap.hellabit.com' }, functi if (!servername) { console.warn("no servername found for '" + id + "'"); - tunneler.write(pack(opts, '|__ERROR__|')); + tunneler.write(pack(opts, Buffer.from('|__ERROR__|'))); return; } console.log("servername: '" + servername + "'"); - clients = clients[id] = net.createConnect({ port: port, host: servername }, function () { - client.on('data', function (chunk) { - console.error('client Data'); + lclient = localclients[id] = net.createConnection({ port: port, host: '127.0.0.1' }, function () { + + lclient.on('data', function (chunk) { + console.log("client '" + opts.address + "' sent ", chunk.byteLength, "bytes"); tunneler.write(pack(opts, chunk)); }); - client.on('error', function (err) { + lclient.on('error', function (err) { console.error('client Error'); console.error(err); - tunneler.write(pack(opts, '|__ERROR__|')); + delete localclients[id]; + tunneler.write(pack(opts, Buffer.from('|__ERROR__|'))); }); - client.on('end', function () { - console.error('client End'); - tunneler.write(pack(opts, '|__END__|')); + lclient.on('end', function () { + console.log('client End'); + delete localclients[id]; + tunneler.write(pack(opts, Buffer.from('|__END__|'))); }); - client.write(opts.data); + console.log('received data', opts.data.byteLength); + lclient.write(opts.data); }); - }; + } + + var machine = require('tunnel-packer').create({ onMessage: onMessage }); tunneler.on('data', machine.fns.addChunk); diff --git a/machine.js b/machine.js deleted file mode 100644 index 4436399..0000000 --- a/machine.js +++ /dev/null @@ -1,166 +0,0 @@ -'use strict'; - -module.exports.create = function () { - - var machine = {}; - machine._version = 1; - machine.state = 0; - machine.states = { 0: 'version', 1: 'headerLength', 2: 'header', 3: 'data'/*, 4: 'error'*/ }; - machine.states_length = Object.keys(machine.states).length; - machine.chunkIndex = 0; - machine.fns = {}; - - function debug(chunk, i, len) { - i = i || 0; - len = len || chunk.length - i; - console.log(chunk.slice(i, len)[0]); - console.log(chunk); - console.log('state:', machine.states[machine.state]); - console.log('statei:', machine.state); - console.log('index:', machine.chunkIndex); - } - - 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; - }; - - - machine.headerLen = 0; - machine.fns.headerLength = function (chunk) { - //console.log(''); - //console.log('[headerLength]'); - machine.headerLen = chunk[machine.chunkIndex]; - machine.chunkIndex += 1; - - return true; - }; - - - machine.buf = null; - machine.bufIndex = 0; - //var buf = Buffer.alloc(4096); - machine.fns.header = function (chunk) { - //console.log(''); - //console.log('[header]'); - var curSize = machine.bufIndex + (chunk.length - machine.chunkIndex); - var partLen = 0; - var str = ''; - var part; - - if (curSize < machine.headerLen) { - // I still don't have the whole header, - // so just create a large enough buffer, - // write these bits, and wait for the next chunk. - if (!machine.buf) { - machine.buf = Buffer.alloc(machine.headerLen); - } - - // partLen should be no more than the available size - partLen = Math.min(machine.headerLen - machine.bufIndex, chunk.length - machine.chunkIndex); - part = chunk.slice(machine.chunkIndex, machine.chunkIndex + partLen); - chunk.copy(machine.buf, machine.bufIndex, machine.chunkIndex, machine.chunkIndex + partLen); - machine.chunkIndex += partLen; // this MUST be chunk.length - machine.bufIndex += partLen; - - return false; - } - else { - // it's now ready to discover the whole header - if (machine.buf) { - str += machine.buf.slice(0, machine.bufIndex).toString(); - } - - partLen = machine.headerLen - str.length; - part = chunk.slice(machine.chunkIndex, machine.chunkIndex + partLen); - str += part.toString(); - - machine.chunkIndex += partLen; - machine.buf = null; // back to null - machine.bufIndex = 0; // back to 0 - - machine._headers = str.split(/,/g); - - machine.family = machine._headers[0]; - machine.address = machine._headers[1]; - machine.port = machine._headers[2]; - machine.bodyLen = parseInt(machine._headers[3], 10) || -1; - - return true; - } - }; - - machine.fns.data = function (chunk) { - //console.log(''); - //console.log('[data]'); - var curSize = machine.bufIndex + (chunk.length - machine.chunkIndex); - //console.log('curSize:', curSize); - //console.log('bodyLen:', machine.bodyLen, typeof machine.bodyLen); - var partLen = 0; - - partLen = Math.min(machine.bodyLen - machine.bufIndex, chunk.length - machine.chunkIndex); - - if (curSize < machine.bodyLen) { - //console.log('curSize < bodyLen'); - - // I still don't have the whole header, - // so just create a large enough buffer, - // write these bits, and wait for the next chunk. - if (!machine.buf) { - machine.buf = Buffer.alloc(machine.bodyLen); - } - - chunk.copy(machine.buf, machine.bufIndex, machine.chunkIndex, machine.chunkIndex + partLen); - machine.chunkIndex += partLen; // this MUST be chunk.length - machine.bufIndex += partLen; - - return false; - } - - if (machine.bufIndex > 0) { - // the completing remainder of the body is in the current slice - chunk.copy(machine.buf, machine.bufIndex, machine.chunkIndex, machine.chunkIndex + partLen); - } - else { - // the whole body is in the current slice - machine.buf = chunk.slice(machine.chunkIndex, machine.chunkIndex + partLen); - } - machine.bufIndex += partLen; - - machine.onMessage({ - family: machine.family - , address: machine.address - , port: machine.port - , data: machine.buf.slice(0, machine.bufIndex) - }); - - machine.chunkIndex += partLen; // === chunk.length - machine.buf = null; // reset to null - machine.bufIndex = 0; // reset to 0 - - return true; - }; - 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; - } - } - }; - - return machine; - -}; diff --git a/package.json b/package.json index 471bf47..0a4d70f 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "homepage": "https://github.com/Daplie/node-tunnel-client#readme", "dependencies": { "jsonwebtoken": "^7.1.9", - "sni": "^1.0.0" + "sni": "^1.0.0", + "tunnel-packer": "^1.0.0" } } diff --git a/sni.hello.bin b/sni.hello.bin deleted file mode 100644 index 85b0344..0000000 Binary files a/sni.hello.bin and /dev/null differ diff --git a/test-machine.js b/test-machine.js deleted file mode 100644 index 03e08dd..0000000 --- a/test-machine.js +++ /dev/null @@ -1,92 +0,0 @@ -'use strict'; - -var sni = require('sni'); -var machine = require('./machine.js').create(); -var hello = require('fs').readFileSync('./sni.hello.bin'); -var version = 1; -var header = 'IPv4,127.0.1.1,443,' + hello.byteLength; -var buf = Buffer.concat([ - Buffer.from([ 255 - version, header.length ]) -, Buffer.from(header) -, hello -]); -var services = { 'ssh': 22, 'http': 4080, 'https': 8443 }; -var clients = {}; -var count = 0; - -machine.onMessage = function (opts) { - var id = opts.family + ',' + opts.address + ',' + opts.port; - var service = 'https'; - var port = services[service]; - var servername = sni(opts.data); - - console.log(''); - console.log('[onMessage]'); - if (!opts.data.equals(hello)) { - throw new Error("'data' packet is not equal to original 'hello' packet"); - } - console.log('all', opts.data.byteLength, 'bytes are equal'); - console.log('src:', opts.family, opts.address + ':' + opts.port); - 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 + "'"); - } - - count += 1; -}; - - -console.log(''); - -// full message in one go -// 223 = 2 + 22 + 199 -console.log('[WHOLE BUFFER]', 2, header.length, hello.length, buf.byteLength); -clients = {}; -machine.fns.addChunk(buf); -console.log(''); - - -// messages one byte at a time -console.log('[BYTE-BY-BYTE BUFFER]', 1); -clients = {}; -buf.forEach(function (byte) { - machine.fns.addChunk(Buffer.from([ byte ])); -}); -console.log(''); - - -// split messages in overlapping thirds -// 0-2 (2) -// 2-24 (22) -// 24-223 (199) -// 223-225 (2) -// 225-247 (22) -// 247-446 (199) -buf = Buffer.concat([ buf, buf ]); -console.log('[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)); -}); -console.log(''); - -process.on('exit', function () { - if (count !== 4) { - throw new Error("should have delivered 4 messages, not", count); - } - console.log('TESTS PASS'); - console.log(''); -});