1
0
Fork 0
mirror of https://github.com/openstf/stf synced 2025-10-05 19:42:01 +02:00

Reverse port forwarding maybe works.

This commit is contained in:
Simo Kinnunen 2014-04-14 20:57:24 +09:00
parent af33084a01
commit f5ac9d2315
3 changed files with 280 additions and 0 deletions

View file

@ -26,6 +26,7 @@ module.exports = function(options) {
.dependency(require('./device/plugins/shell'))
.dependency(require('./device/plugins/touch'))
.dependency(require('./device/plugins/install'))
.dependency(require('./device/plugins/forward'))
.dependency(require('./device/plugins/owner'))
.define(function(options, solo) {
if (process.send) {

View file

@ -0,0 +1,244 @@
var net = require('net')
var util = require('util')
var syrup = require('syrup')
var Promise = require('bluebird')
var split = require('split')
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 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) {
var log = logger.createLogger('device:plugins:forward')
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 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, [
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 createForward(data) {
log.info(
'Reverse 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
}
var parser = conn.pipe(split())
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
}
})
// Keep this around
function endListener() {
removeForward(forward)
}
conn.on('end', endListener)
conn.write(util.format(
'FRWD %d %d\n'
, forward.devicePort
, forward.privatePort
))
return resolver.promise
})
}
function removeForward(data) {
var forward = service.forwards[data.devicePort]
if (forward) {
forward.connection.end()
delete service.forwards[data.devicePort]
}
}
function tryConnect(data) {
var resolver = Promise.defer()
var conn = net.connect({
host: data.targetHost
, port: data.targetPort
})
function connectListener() {
resolver.resolve()
}
function errorListener(err) {
resolver.reject(err)
}
conn.on('connect', connectListener)
conn.on('error', errorListener)
return resolver.promise.finally(function() {
conn.removeListener('connect', connectListener)
conn.removeListener('error', errorListener)
conn.end()
})
}
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()
.then(function() {
router
.on(wire.ForwardTestMessage, function(channel, message) {
var reply = wireutil.reply(options.serial)
tryConnect(message)
.then(function(conn) {
conn.end()
push.send([
channel
, reply.okay('success')
])
})
.catch(function() {
push.send([
channel
, reply.fail('fail_connect')
])
})
})
.on(wire.ForwardMessage, function(channel, message) {
var reply = wireutil.reply(options.serial)
createForward(message)
.then(function() {
push.send([
channel
, reply.okay('success')
])
})
.catch(function(err) {
log.error('Reverse port forwarding failed', err.stack)
push.send([
channel
, reply.fail('fail_forward')
])
})
})
.on(wire.ForwardListMessage, function(channel) {
var reply = wireutil.reply(options.serial)
push.send([
channel
, reply.okay('success', listForwards())
])
})
.on(wire.ForwardRemoveMessage, function(channel, message) {
var reply = wireutil.reply(options.serial)
removeForward(message)
push.send([
channel
, reply.okay('success')
])
})
})
})

View file

@ -36,6 +36,11 @@ enum MessageType {
UngroupMessage = 27;
UninstallMessage = 34;
RotateMessage = 35;
ForwardMessage = 36;
ForwardTestMessage = 37;
ForwardListMessage = 38;
ForwardRemoveMessage = 39;
DeviceForwardsMessage = 40;
}
message Envelope {
@ -346,3 +351,33 @@ message LaunchActivityMessage {
message RotateMessage {
required int32 rotation = 1;
}
message ForwardTestMessage {
required string targetHost = 1;
required uint32 targetPort = 2;
}
message ForwardMessage {
required uint32 devicePort = 1;
required string targetHost = 2;
required uint32 targetPort = 3;
required bool system = 4;
}
message ForwardListMessage {
}
message DeviceForward {
required uint32 devicePort = 1;
required string targetHost = 2;
required uint32 targetPort = 3;
required bool system = 4;
}
message DeviceForwardsMessage {
repeated DeviceForward forwards = 1;
}
message ForwardRemoveMessage {
required uint32 devicePort = 1;
}