From 617e20522bb71be489dc6c4c42e71d3aa2eeec70 Mon Sep 17 00:00:00 2001 From: Simo Kinnunen Date: Thu, 9 Oct 2014 18:34:13 +0900 Subject: [PATCH] Reverse port forwarding backend actually works now. UI needs some work for the "new vision". --- lib/db/api.js | 17 -- lib/units/device/plugins/forward.js | 321 ++++++++++++++------------ lib/units/device/resources/minirev.js | 93 ++++++++ lib/units/websocket/index.js | 42 ++-- lib/util/forwardutil.js | 116 ++++++++++ lib/wire/wire.proto | 1 - vendor/minirev/armeabi-v7a/minirev | Bin 0 -> 17664 bytes vendor/minirev/armeabi/minirev | Bin 0 -> 17656 bytes vendor/minirev/mips/minirev | Bin 0 -> 70984 bytes vendor/minirev/x86/minirev | Bin 0 -> 13508 bytes 10 files changed, 395 insertions(+), 195 deletions(-) create mode 100644 lib/units/device/resources/minirev.js create mode 100644 lib/util/forwardutil.js create mode 100755 vendor/minirev/armeabi-v7a/minirev create mode 100755 vendor/minirev/armeabi/minirev create mode 100755 vendor/minirev/mips/minirev create mode 100755 vendor/minirev/x86/minirev diff --git a/lib/db/api.js b/lib/db/api.js index d1606897..3345dc38 100644 --- a/lib/db/api.js +++ b/lib/db/api.js @@ -100,23 +100,6 @@ dbapi.lookupUserByAdbFingerprint = function(fingerprint) { }) } -dbapi.addUserForward = function(email, forward) { - var devicePort = forward.devicePort - return db.run(r.table('users').get(email).update({ - forwards: r.row('forwards').default([]).filter(function(forward) { - return forward('devicePort').ne(devicePort) - }).append(forward) - })) -} - -dbapi.removeUserForward = function(email, devicePort) { - return db.run(r.table('users').get(email).update({ - forwards: r.row('forwards').default([]).filter(function(forward) { - return forward('devicePort').ne(devicePort) - }) - })) -} - dbapi.loadGroup = function(email) { return db.run(r.table('devices').getAll(email, { index: 'owner' diff --git a/lib/units/device/plugins/forward.js b/lib/units/device/plugins/forward.js index d8949175..33526d3a 100644 --- a/lib/units/device/plugins/forward.js +++ b/lib/units/device/plugins/forward.js @@ -1,157 +1,174 @@ var net = require('net') -var util = require('util') -var syrup = require('syrup') var Promise = require('bluebird') -var split = require('split') +var syrup = require('syrup') -var logger = require('../../../util/logger') -var devutil = require('../../../util/devutil') -var streamutil = require('../../../util/streamutil') -var lifecycle = require('../../../util/lifecycle') var wire = require('../../../wire') +var logger = require('../../../util/logger') +var lifecycle = require('../../../util/lifecycle') +var streamutil = require('../../../util/streamutil') +var forwardutil = require('../../../util/forwardutil') var wireutil = require('../../../wire/util') module.exports = syrup.serial() .dependency(require('../support/adb')) .dependency(require('../support/router')) .dependency(require('../support/push')) - .dependency(require('../resources/remote')) - .define(function(options, adb, router, push, remote) { + .dependency(require('../resources/minirev')) + .define(function(options, adb, router, push, minirev) { var log = logger.createLogger('device:plugins:forward') + var plugin = Object.create(null) - var service = { - port: 2810 - , privatePorts: (function() { - var ports = [] - for (var i = 2520; i <= 2540; ++i) { - ports.push(i) - } - return ports - })() - , forwards: Object.create(null) - } + function ForwardManager() { + var forwards = Object.create(null) - function openService() { - log.info('Launching reverse port forwarding service') - return devutil.ensureUnusedPort(adb, options.serial, service.port) - .timeout(10000) - .then(function() { - return adb.shell(options.serial, [ - 'exec' - , remote.bin - , '--lib', remote.lib - , '--listen-forward', service.port - ]) - .timeout(10000) - .then(function(out) { - lifecycle.share('Forward shell', out) - streamutil.talk(log, 'Forward shell says: "%s"', out) - }) - .then(function() { - return devutil.waitForPort(adb, options.serial, service.port) - .timeout(10000) - }) - .then(function(conn) { - conn.end() - }) - }) - } + function Forward(conn, to) { + var proxies = Object.create(null) - function createForward(data) { - log.info( - 'Reverse port forwarding port %d to %s:%d' - , data.devicePort - , data.targetHost - , data.targetPort - ) - - var forward = service.forwards[data.devicePort] - - if (forward) { - if (forward.targetHost === data.targetHost && - forward.targetPort === data.targetPort) { - return Promise.resolve() - } - else if (forward.system) { - return Promise.reject(new Error('Cannot rebind system port')) - } - else { - removeForward(forward) - } - } - - return adb.openTcp(options.serial, service.port) - .timeout(10000) - .then(function(conn) { - var resolver = Promise.defer() - - var forward = { - devicePort: data.devicePort - , targetHost: data.targetHost - , targetPort: data.targetPort - , system: !!data.system - , privatePort: service.privatePorts.pop() - , connection: conn + function Proxy(fd) { + function maybeSend() { + var chunk + while ((chunk = this.read())) { + if (!conn.write(chunk)) { + break + } + } } - var parser = conn.pipe(split()) + function killListeners() { + src.removeListener('readable', maybeSend) + conn.removeListener('drain', maybeSend) + conn.removeListener('end', killListeners) + } - parser.on('data', function(chunk) { - var cmd = chunk.toString().trim() - switch (cmd) { - case 'OKAY': - resolver.resolve(forward) - break - case 'FAIL': - resolver.reject(new Error('Remote replied with FAIL')) - break - case 'CNCT': - adb.openTcp(options.serial, forward.privatePort) - .done(function(dstream) { - return tryConnect(forward) - .then(function(ustream) { - ustream.pipe(dstream) - dstream.pipe(ustream) - }) - }) - break + var src = new forwardutil.ForwardWriter(fd) + .on('readable', maybeSend) + .on('error', function(err) { + log.error('Proxy writer %d had an error', fd, to, err.stack) + }) + + conn.on('drain', maybeSend) + conn.on('end', killListeners) + + this.dest = net.connect(to) + .once('end', function() { + delete proxies[fd] + killListeners() + }) + .on('error', function(err) { + log.error('Proxy reader %d had an error', fd, to, err.stack) + }) + + this.dest.pipe(src) + + this.stop = function() { + //this.dest.unpipe(this.src) + this.dest.end() + }.bind(this) + } + + conn.pipe(new forwardutil.ForwardParser()) + .on('packet', function(fd, packet) { + var proxy = proxies[fd] + + if (!proxy) { + // New connection + proxy = proxies[fd] = new Proxy(fd) + } + + proxy.dest.write(packet) + }) + .on('fin', function(fd) { + // The connection ended + if (proxies[fd]) { + proxies[fd].stop() } }) - // Keep this around - function endListener() { - removeForward(forward) - } + this.end = function() { + conn.end() + } + } - conn.on('end', endListener) + this.add = function(port, conn, to) { + forwards[port] = new Forward(conn, to) + } - conn.write(util.format( - 'FRWD %d %d\n' - , forward.devicePort - , forward.privatePort - )) - - return resolver.promise - }) - } - - function removeForward(data) { - log.info('Removing reverse port forwarding on port %d', data.devicePort) - var forward = service.forwards[data.devicePort] - if (forward) { - forward.connection.end() - delete service.forwards[data.devicePort] + this.remove = function(port) { + if (forwards[port]) { + forwards[port].end() + } } } - function tryConnect(data) { + var manager = new ForwardManager() + + function startService() { + log.info('Launching reverse port forwarding service') + return adb.shell(options.serial, [ + 'exec' + , minirev.bin + ]) + .timeout(10000) + .then(function(out) { + lifecycle.share('Forward shell', out) + streamutil.talk(log, 'Forward shell says: "%s"', out) + }) + } + + function connectService(times) { + function tryConnect(times, delay) { + return adb.openLocal(options.serial, 'localabstract:minirev') + .timeout(10000) + .catch(function(err) { + if (/closed/.test(err.message) && times > 1) { + return Promise.delay(delay) + .then(function() { + return tryConnect(--times, delay * 2) + }) + } + return Promise.reject(err) + }) + } + log.info('Connecting to reverse port forwarding service') + return tryConnect(times, 100) + } + + function awaitServer() { + return connectService(5) + .then(function(conn) { + conn.end() + return true + }) + } + + plugin.createForward = function(port, to) { + log.info( + 'Creating reverse port forward from ":%d" to "%s:%d"' + , port + , to.host + , to.port + ) + return connectService(1) + .then(function(out) { + var header = new Buffer(4) + header.writeUInt16LE(0, 0) + header.writeUInt16LE(port, 2) + out.write(header) + return manager.add(port, out, to) + }) + } + + plugin.removeForward = function(port) { + log.info('Removing reverse port forward ":%d"', port) + manager.remove(port) + return Promise.resolve() + } + + plugin.connect = function(options) { var resolver = Promise.defer() - var conn = net.connect({ - host: data.targetHost - , port: data.targetPort - }) + var conn = net.connect(options) function connectListener() { resolver.resolve(conn) @@ -170,31 +187,16 @@ module.exports = syrup.serial() }) } - function resetForwards() { - Object.keys(service.forwards).forEach(function(privatePort) { - service.forwards[privatePort].connection.end() - delete service.forwards[privatePort] - }) - } - - function listForwards() { - return Object.keys(service.forwards).map(function(privatePort) { - var forward = service.forwards[privatePort] - return { - devicePort: forward.devicePort - , targetHost: forward.targetHost - , targetPort: forward.targetPort - , system: !!forward.system - } - }) - } - - return openService() + return startService() + .then(awaitServer) .then(function() { router .on(wire.ForwardTestMessage, function(channel, message) { var reply = wireutil.reply(options.serial) - tryConnect(message) + plugin.connect({ + host: message.targetHost + , port: message.targetPort + }) .then(function(conn) { conn.end() push.send([ @@ -211,7 +213,10 @@ module.exports = syrup.serial() }) .on(wire.ForwardCreateMessage, function(channel, message) { var reply = wireutil.reply(options.serial) - createForward(message) + plugin.createForward(message.devicePort, { + host: message.targetHost + , port: message.targetPort + }) .then(function() { push.send([ channel @@ -228,11 +233,21 @@ module.exports = syrup.serial() }) .on(wire.ForwardRemoveMessage, function(channel, message) { var reply = wireutil.reply(options.serial) - removeForward(message) - push.send([ - channel - , reply.okay('success') - ]) + plugin.removeForward(message.devicePort) + .then(function() { + push.send([ + channel + , reply.okay('success') + ]) + }) + .catch(function(err) { + log.error('Reverse port unforwarding failed', err.stack) + push.send([ + channel + , reply.fail('fail') + ]) + }) }) }) + .return(plugin) }) diff --git a/lib/units/device/resources/minirev.js b/lib/units/device/resources/minirev.js new file mode 100644 index 00000000..b0778fe0 --- /dev/null +++ b/lib/units/device/resources/minirev.js @@ -0,0 +1,93 @@ +var util = require('util') + +var Promise = require('bluebird') +var syrup = require('syrup') + +var logger = require('../../../util/logger') +var pathutil = require('../../../util/pathutil') +var devutil = require('../../../util/devutil') +var streamutil = require('../../../util/streamutil') + +module.exports = syrup.serial() + .dependency(require('../support/adb')) + .dependency(require('../support/properties')) + .define(function(options, adb, properties) { + var log = logger.createLogger('device:resources:minirev') + + var resources = { + bin: { + src: pathutil.vendor(util.format( + 'minirev/%s/minirev' + , properties['ro.product.cpu.abi'] + )) + , dest: '/data/local/tmp/minirev' + , comm: 'minirev' + , mode: 0755 + } + } + + function removeResource(res) { + return adb.shell(options.serial, ['rm', res.dest]) + .timeout(10000) + .then(function(out) { + return streamutil.readAll(out) + }) + .return(res) + } + + function installResource(res) { + return adb.push(options.serial, res.src, res.dest, res.mode) + .timeout(10000) + .then(function(transfer) { + return new Promise(function(resolve, reject) { + transfer.on('error', reject) + transfer.on('end', resolve) + }) + }) + .return(res) + } + + function ensureNotBusy(res) { + return adb.shell(options.serial, [res.dest, '-h']) + .timeout(10000) + .then(function(out) { + // Can be "Text is busy", "text busy" + return streamutil.findLine(out, (/busy/i)) + .timeout(10000) + .then(function() { + log.info('Binary is busy, will retry') + return Promise.delay(1000) + }) + .then(function() { + return ensureNotBusy(res) + }) + .catch(streamutil.NoSuchLineError, function() { + return res + }) + }) + } + + function installAll() { + return Promise.all([ + removeResource(resources.bin).then(installResource).then(ensureNotBusy) + ]) + } + + function stop() { + return devutil.killProcsByComm( + adb + , options.serial + , resources.bin.comm + , resources.bin.dest + ) + .timeout(15000) + } + + return stop() + .then(installAll) + .then(function() { + return { + bin: resources.bin.dest + } + }) + }) diff --git a/lib/units/websocket/index.js b/lib/units/websocket/index.js index 21992b10..bae570b2 100644 --- a/lib/units/websocket/index.js +++ b/lib/units/websocket/index.js @@ -676,32 +676,26 @@ module.exports = function(options) { if (!data.targetHost || data.targetHost === 'localhost') { data.targetHost = socket.request.ip } - dbapi.addUserForward(user.email, data) - .then(function() { - socket.emit('forward.create', data) - joinChannel(responseChannel) - push.send([ - channel - , wireutil.transaction( - responseChannel - , new wire.ForwardCreateMessage(data) - ) - ]) - }) + socket.emit('forward.create', data) + joinChannel(responseChannel) + push.send([ + channel + , wireutil.transaction( + responseChannel + , new wire.ForwardCreateMessage(data) + ) + ]) }) .on('forward.remove', function(channel, responseChannel, data) { - dbapi.removeUserForward(user.email, data.devicePort) - .then(function() { - socket.emit('forward.remove', data) - joinChannel(responseChannel) - push.send([ - channel - , wireutil.transaction( - responseChannel - , new wire.ForwardRemoveMessage(data) - ) - ]) - }) + socket.emit('forward.remove', data) + joinChannel(responseChannel) + push.send([ + channel + , wireutil.transaction( + responseChannel + , new wire.ForwardRemoveMessage(data) + ) + ]) }) .on('logcat.start', function(channel, responseChannel, data) { joinChannel(responseChannel) diff --git a/lib/util/forwardutil.js b/lib/util/forwardutil.js new file mode 100644 index 00000000..7314cf07 --- /dev/null +++ b/lib/util/forwardutil.js @@ -0,0 +1,116 @@ +var util = require('util') +var stream = require('stream') + +var HEADER_SIZE = 4 +var MAX_PACKET_SIZE = 0xFFFF + +function ForwardParser() { + stream.Transform.call(this) + this._header = new Buffer(4) + this._needLength = -HEADER_SIZE + this._target = 0 +} + +util.inherits(ForwardParser, stream.Transform) + +ForwardParser.prototype._transform = function(chunk, encoding, done) { + var cursor = 0 + + while (cursor < chunk.length) { + var diff = chunk.length - cursor + + // Do we need a header? + if (this._needLength < 0) { + // Still missing a header? + if (chunk.length < -this._needLength) { + // Save what we're received so far. + chunk.copy( + this._header + , HEADER_SIZE + this._needLength + , cursor + , cursor + -this._needLength + ) + break + } + + // Combine previous and current chunk in case the header was split. + chunk.copy( + this._header + , HEADER_SIZE + this._needLength + , cursor + , cursor + -this._needLength + ) + + cursor += -this._needLength + + this._target = this._header.readUInt16LE(0) + this._needLength = this._header.readUInt16LE(2) + + if (this._needLength === 0) { + // This is a fin packet + this.emit('fin', this._target) + this._needLength = -HEADER_SIZE + } + } + // Do we have a full data packet? + else if (diff >= this._needLength) { + this.emit( + 'packet' + , this._target + , chunk.slice(cursor, cursor + this._needLength) + ) + cursor += this._needLength + this._needLength = -HEADER_SIZE + } + // We have a partial data packet. + else { + this.emit('packet', this._target, chunk.slice(cursor, cursor + diff)) + this._needLength -= diff + cursor += diff + } + } + + done() +} + +module.exports.ForwardParser = ForwardParser + +function ForwardWriter(target) { + stream.Transform.call(this) + this._target = target +} + +util.inherits(ForwardWriter, stream.Transform) + +ForwardWriter.prototype._transform = function(chunk, encoding, done) { + var header + , length + + do { + length = Math.min(MAX_PACKET_SIZE, chunk.length) + + header = new Buffer(HEADER_SIZE) + header.writeUInt16LE(this._target, 0) + header.writeUInt16LE(length, 2) + + this.push(header) + this.push(chunk.slice(0, length)) + + chunk = chunk.slice(length) + } + while (chunk.length) + + done() +} + +ForwardWriter.prototype._flush = function(done) { + var header = new Buffer(HEADER_SIZE) + header.writeUInt16LE(this._target, 0) + header.writeUInt16LE(0, 2) + + this.push(header) + + done() +} + +module.exports.ForwardWriter = ForwardWriter diff --git a/lib/wire/wire.proto b/lib/wire/wire.proto index 941ad235..6a64dbf7 100644 --- a/lib/wire/wire.proto +++ b/lib/wire/wire.proto @@ -400,7 +400,6 @@ message ForwardCreateMessage { required uint32 devicePort = 1; required string targetHost = 2; required uint32 targetPort = 3; - optional bool system = 4; } message ForwardRemoveMessage { diff --git a/vendor/minirev/armeabi-v7a/minirev b/vendor/minirev/armeabi-v7a/minirev new file mode 100755 index 0000000000000000000000000000000000000000..a70f9cc8ca38719c23179d5042525e78104cc29c GIT binary patch literal 17664 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 DmYhN& literal 0 HcmV?d00001 diff --git a/vendor/minirev/mips/minirev b/vendor/minirev/mips/minirev new file mode 100755 index 0000000000000000000000000000000000000000..60c126c9ce15920f911625ac404f8c932b4d299e GIT binary patch literal 70984 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 RYoCdak9yB03Y-)5{tsI8krw~} literal 0 HcmV?d00001 diff --git a/vendor/minirev/x86/minirev b/vendor/minirev/x86/minirev new file mode 100755 index 0000000000000000000000000000000000000000..8a35397f2f66ce6a3d80dc275cb2775f271eba16 GIT binary patch literal 13508 zcmeHOe|%Kcm4A~w;1!0kVFMkl2OK#O&ai`F3^!a4V;NI%4QM|>moT9NfOfx7CKFOO@SoU zJSQtmKSzbVNNkt%EPy7^n?Pfl0M0}@+o=9iq<)twNxSyQ3R5@Am`G!~6?A})JwiT8 z*MgqeKKqcCzoczPTO`oDWOHL^NmFBJYoPUGqAutN(mbTO(^!aqHUrPgz%8?jQt}sP z&~M1VD>Lw$Gw|vRd_x9)R|Y=aK$n5GErb584E(+fT+6_FGw^@Pz<-p1|0DzdMFzfq z8qWUx$26SwI*@_CJPjxR@2278|3e0DWZ>^+;O8^&=>txAbLixK@NYa$>CEK34Eze< z@-(_sTievQxvsV`)EGg&uESsJj|4gd(iZX8ZLO^fZmn(bH#SKPb)iU;)DmcI4Yx|o z{-&mIozxs?ZVN=DhSoqp61BFsHbw#v1g=n6s%r|j5xXF1OS`|dUTO{a>shz1xkaj* zl2q5SLuw1xZG}JxWiH$jk>Kt1Qd1*5KO_ZO!c9%Jb?63UR!R*mt&O2bgR~_OAt#c* zb&J#%X$?`7#f-MlBNN%~hhqM^x&XNSo5QUUR!uil8>sh3{E)o4t&KefwKgEy-rN?c zugl9TXbYnwo0~0IXTka=X7F%)Rc7#9g~ahd*boor-RF?pNLL^+=6H}kkHo>c8tF4g zd@eV^C;oCs8S7>trDF%<%k;+%WDb%8=~GCIT{%d9hs2oRLRyH#X93cDku!Cejr^4s zPQVx%d0tj#qGAUjD(obLQo8_Wp+midXyhS6bnX#Cbm~z;D7%{wo!vu-jz38V#eYtS z4(=sH!%q{Ux5A5I3W~% zgAj%`2r(dU6Jk)t2x0j52r)ntgfPMfgdRzn{8Be^lZ$%rOPfqCqUJ$nQ^zOqETRR1 zq@{)gPD=zi{HZ>H(;`8dJhfNgv`o-Le7C@9p_ZeRqUD2~#E%P{BM|H%J|u9CM6j25pTIdH!AFSi6*xyGxSRNHfpdg{PZIAHI7ceD zmw2bZIby;6#9IW;kqh<_-z0F3VDKREYJqbkgG0p21kMo+zDB%I;2hcDapE4}>$N{^ z+_290C0w&>;7`{{QuK|n_)}xYCnw{3R2oQ+zQ^CY?a2ytHEKz^q;;#WGc!x8P)p-c z1I)-3BNgc@6)mDF+D=v0WYs7@)#${eqnpc3bRrmOicW+g8=@08w7;sGRqTMJsH()H z6(@usSx1%0$r-c>h5RSFxu0`i|E}oDF0=J{CBu%`uQ0{pf%k5|tM<+TJ^BHADzP7_ zHfI0Y7Pg}WU*G6sP*FiU5}?cmld?(xfu%+^cw&9ppkAYvI(m|r^1ijW z>)oH~JJlj>SgTQs8z3QMlg|EvMdQQq3iTV&lucWG!7?huK~%A?Pa76&rQ^d|ks7}> z3-yKlj9>mATXgjN4&g)_Gd_cnnGq7WuAm}r|KoqGKA$CcEqOmYIU{N z5ABMygT^REM4O2A$sl{;(U(l;aHG{+)V5<-C`F-2aL8heFvvw_k5%9v&eyG6?bx>}~ zTVnsUIYYA^Mk+OUHeeY|>z;rlq z(&;_uJFg{8wi$vg=}jd5oDHVK+WX$~i4(JZ!`=z~9yesB9KM8JB}Wb0Rf(-H+Or+H zFLthde(EW>Nqir5(YNKZ^DiTtx822T0@>(2MRNNa(6N*@fzeHyR2FJ5iT{5&{_O`y z*2_M4QgOs)V(g!EI%4lY?vpl0_nUxvr5v5ea W-_Yy0|7Vmtk?rX2fuW)kvLn_) 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 Y#o)VG|Hv!a6kM=1_+GQ}1f=u%e^IL=&Hw-a literal 0 HcmV?d00001