/// /// /// // TODO(ldixon): reorganize the utransformers and rename uproxy-obfuscators. // Ideal: // import Transformer = require('uproxy-obfuscators/transformer'); // import Rabbit = require('uproxy-obfuscators/rabbit.transformer'); // import Fte = require('uproxy-obfuscators/fte.transformer'); // Current: /// // import Rabbit = require('utransformers/src/transformers/uTransformers.fte'); // import Fte = require('utransformers/src/transformers/uTransformers.rabbit'); import PassThrough = require('../simple-transformers/passthrough'); import CaesarCipher = require('../simple-transformers/caesar'); import logging = require('../../../third_party/uproxy-lib/logging/logging'); import net = require('../net/net.types'); import churn_pipe_types = require('./freedom-module.interface'); import Message = churn_pipe_types.Message; var log :logging.Log = new logging.Log('churn-pipe'); /** * Listens on a port for UDP datagrams -- emitting a Freedom message for each * datagram received -- and sends UDP datagrams to a destination, in response * to Freedom messages. * * Each incoming and outgoing message is first passed through an obfuscator. * The obfuscator is used via direct function calls rather than Freedom * message passing owing to the inelegance of receiving a response *back* * from a Freedom module. */ class Pipe { // Socket on which the server is listening. private socket_ :freedom_UdpSocket.Socket; // Obfuscates and deobfuscates messages. private transformer_ :Transformer; // Endpoint to which all messages are sent. private remoteEndpoint_ :net.Endpoint; // TODO: define a type for event dispatcher in freedom-typescript-api constructor (private dispatchEvent_ :(name:string, args:Message) => void) { // TODO: clean up udp sockets this.socket_ = freedom['core.udpsocket'](); } /** * Returns a promise to create a socket, bind to the specified address, and * start listening for datagrams. */ public bind = ( localAddress :string, localPort :number, remoteAddress :string, remotePort :number, transformerName :string, key ?:ArrayBuffer, config ?:string) :Promise => { // First, try to make our transformer. try { this.transformer_ = this.makeTransformer_(transformerName, key, config); } catch (e) { return Promise.reject(e); } // Next, bind to a socket. this.remoteEndpoint_ = { address: remoteAddress, port: remotePort }; return this.socket_.bind(localAddress, localPort) .then((resultCode:number) => { if (resultCode != 0) { return Promise.reject(new Error( 'listen failed with result code ' + resultCode)); } this.socket_.on('onData', this.onData_); }); } private makeTransformer_ = ( // Name of transformer to use, e.g. 'rabbit' or 'none'. name :string, // Key for transformer, if any. key ?:ArrayBuffer, // JSON-encoded configuration, if any. config ?:string) : Transformer => { var transformer :Transformer; // TODO(ldixon): re-enable rabbit and FTE once we can figure out why they // don't load in freedom. /* if (name == 'rabbit') { transformer = Rabbit.Transformer(); } else if (name == 'fte') { transformer = Fte.Transformer(); } else */ if (name == 'caesar') { transformer = new CaesarCipher(); } else if (name == 'none') { transformer = new PassThrough(); } else { throw new Error('unknown transformer: ' + name); } if (key) { transformer.setKey(key); } if (config) { transformer.configure(config); } return transformer; } /** * Sends a message over the network to the remote side. * The message is obfuscated before it hits the wire. */ public send = (buffer:ArrayBuffer) => { return this.sendTo(buffer, this.remoteEndpoint_); } /** * Sends a message over the network to the specified destination. * The message is obfuscated before it hits the wire. */ public sendTo = (buffer:ArrayBuffer, to:net.Endpoint) => { var transformedBuffer = this.transformer_.transform(buffer); return this.socket_.sendTo( transformedBuffer, to.address, to.port).then(() => { return Promise.resolve(); }); } public getLocalEndpoint = () : Promise => { return this.socket_.getInfo().then((socketInfo:freedom_UdpSocket.SocketInfo) => { return { address: socketInfo.localAddress, port: socketInfo.localPort } }); } /** * Called when a message is received over the network from the remote side. * The message is de-obfuscated before the Freedom message is emitted. */ private onData_ = (recvFromInfo:freedom_UdpSocket.RecvFromInfo) => { var transformedBuffer = recvFromInfo.data; var buffer = this.transformer_.restore(transformedBuffer); this.dispatchEvent_('message', { data: buffer, source: { address: recvFromInfo.address, port: recvFromInfo.port } }); } } export = Pipe;