[tor-commits] [fog/master] The files have been reorganized into a python and go project.

infinity0 at torproject.org infinity0 at torproject.org
Fri Aug 1 16:50:37 UTC 2014


commit 0612c1405d8e0d17b9cc82c530fa5a9557b49d6d
Author: Quinn Jarrell <qjarrell at gosynapsify.com>
Date:   Fri Jul 4 23:49:50 2014 -0400

    The files have been reorganized into a python and go project.
---
 .gitignore               |    3 +-
 Makefile                 |   12 -
 fog-client/fog-client    |  538 +++++++++++++++++++++++++++++++++++++++++++++
 fog-client/fog/socks.py  |   59 +++++
 fog-client/fogrc         |   17 ++
 fog-client/setup.py      |   23 ++
 fog-client/torrc         |    5 +
 fog-server/Makefile      |   12 +
 fog-server/fog-server.go |  548 ++++++++++++++++++++++++++++++++++++++++++++++
 fog-server/pt_test.go    |   39 ++++
 fog-server/stack.go      |   57 +++++
 fog-server/stack_test.go |  120 ++++++++++
 fog-server/torrc         |    5 +
 fog/socks.py             |   59 -----
 fogrc                    |   17 --
 obfs-flash-client        |  538 ---------------------------------------------
 obfs-flash-server.go     |  548 ----------------------------------------------
 pt_test.go               |   39 ----
 setup.py                 |   23 --
 stack.go                 |   57 -----
 stack_test.go            |  120 ----------
 torrc                    |    5 -
 torrc-server             |    5 -
 23 files changed, 1425 insertions(+), 1424 deletions(-)

diff --git a/.gitignore b/.gitignore
index 452a1a4..8184caa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-/mkii/obfs-flash-server
+/fog-server/fog-server
+*.pyc
diff --git a/Makefile b/Makefile
deleted file mode 100644
index b8a4d24..0000000
--- a/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-GOBUILDFLAGS =
-
-obfs-flash-server: obfs-flash-server.go stack.go
-	go build $(GOBUILDFLAGS) -o "$@" $^
-
-test:
-	go test -v
-
-clean:
-	rm -f obfs-flash-server
-
-.PHONY: test clean
diff --git a/fog-client/fog-client b/fog-client/fog-client
new file mode 100755
index 0000000..9b2e7ef
--- /dev/null
+++ b/fog-client/fog-client
@@ -0,0 +1,538 @@
+#!/usr/bin/python
+
+import argparse
+import os
+import sys
+
+from collections import namedtuple
+from functools import partial
+
+# TODO(infinity0): this is temporary workaround until we do #10047
+if sys.platform == 'win32':
+    os.environ["KILL_CHILDREN_ON_DEATH"] = "1"
+from pyptlib.util import parse_addr_spec
+from pyptlib.util.subproc import auto_killall, Popen
+from pyptlib.client import ClientTransportPlugin
+
+from subprocess import PIPE
+
+from twisted.internet.defer import Deferred, DeferredList
+from twisted.internet.stdio import StandardIO
+from twisted.internet.protocol import Factory, connectionDone
+from twisted.internet.endpoints import TCP4ClientEndpoint
+from twisted.protocols.basic import LineReceiver
+from twisted.protocols.portforward import ProxyServer as _ProxyServer
+from twisted.python import log
+from txsocksx.client import SOCKS4ClientEndpoint, SOCKS5ClientEndpoint
+from fog.socks import SOCKSv4InterceptorFactory
+
+import shlex
+
+import logging
+
+DEFAULT_CONFIG_FILE_NAME = os.path.dirname(os.path.realpath(__file__)) + '/fogrc'
+
+logger = None
+def pt_setup_logger():
+    global logger
+    logger = logging.getLogger('fog-logger')
+    logger.setLevel(logging.WARNING)
+    ch = logging.StreamHandler()
+    ch.setLevel(logging.DEBUG)
+    logger.addHandler(ch)
+
+def pt_child_env(managed_ver, env=os.environ):
+    """
+    Prepare the environment for a child PT process, by clearing all TOR_PT_*
+    envvars except TOR_PT_STATE_LOCATION and TOR_PT_MANAGED_TRANSPORT_VER.
+    """
+    exempt = ['TOR_PT_STATE_LOCATION']
+    cur_env = [(k, v) for k, v in env.iteritems()
+                      if not k.startswith('TOR_PT_') or k in exempt]
+    cur_env.append(('TOR_PT_MANAGED_TRANSPORT_VER', ','.join(managed_ver)))
+    return cur_env
+
+class MethodSpec(namedtuple('MethodSpec', 'name protocol addrport args opts')):
+    @classmethod
+    def fromLine(cls, line):
+        args = line.rstrip('\n').split(' ')
+        name = args[0]
+        protocol = args[1]
+        addrport = parse_addr_spec(args[2])
+        args = args[3][-5:].split(',') if len(args) > 3 and args[3].startswith("ARGS=") else []
+        opts = args[4][-9:].split(',') if len(args) > 4 and args[4].startswith("OPT-ARGS=") else []
+        return MethodSpec(name, protocol, addrport, args, opts)
+
+def branch(parent):
+    """
+    Returns a new Deferred that does not advance the callback-chain of the parent.
+
+    See http://xph.us/2009/12/10/asynchronous-programming-in-python.html for motivation.
+    """
+    d = Deferred()
+    parent.addCallback(lambda v: (v, d.callback(v))[0])
+    parent.addErrback(lambda f: (f, d.errback(f))[1])
+    return d
+
+class ManagedTransportProtocolV1(LineReceiver):
+    """
+    A Twisted IProtocol to read PT output.
+
+    See pt-spec.txt and others for details of the protocol.
+    """
+    # TODO(infinity0): eventually this could be padded out and moved to pyptlib
+
+    delimiter = os.linesep
+    protocol_version = "1"
+
+    def __init__(self):
+        self.cmethods = {}
+        self._dCMethodsDone = Deferred()
+        self._dPluginError = Deferred()
+        # dPluginError triggers errors on all sub-events, not the other way round
+        # so fatal sub-events should call _abort rather than errback on their Deferreds
+        self._dPluginError.addErrback(lambda f: (f, self._fireCMethodsDone().errback(f))[0])
+        # TODO(infinity0): call _abort if we don't recv CMETHODS DONE within n sec
+
+    def whenCMethodsDone(self):
+        """
+        Return a new Deferred that calls-back when CMETHODS DONE is received.
+        """
+        return branch(self._dCMethodsDone)
+
+    def whenPluginError(self):
+        """
+        Return a new Deferred that errors-back when the remote plugin fails.
+
+        Note: the success chain (callback) is never fired.
+        """
+        return branch(self._dPluginError)
+
+    def lineReceived(self, line):
+        if not line: return
+
+        (kw, args) = line.split(' ', 1)
+        if kw == "VERSION":
+            version = args.strip()
+            if version != self.protocol_version:
+                self._abort(ValueError("child used unsupported managed transport version: %s" % version))
+        elif kw == "CMETHOD":
+            cmethod = MethodSpec.fromLine(args)
+            self.cmethods[cmethod.name] = cmethod
+        elif kw == "CMETHODS" and args == "DONE":
+            self._fireCMethodsDone().callback(self.cmethods)
+        else:
+            pass # ignore unrecognised line
+
+    def connectionLost(self, reason=connectionDone):
+        self._firePluginError().errback(reason)
+
+    def _abort(self, exc):
+        self._firePluginError().errback(exc)
+        self.transport.loseConnection()
+
+    def _fireCMethodsDone(self):
+        """Return dCMethodsDone or a dummy if it was already called."""
+        if self._dCMethodsDone:
+            d = self._dCMethodsDone
+            self._dCMethodsDone = None
+            return d
+        return Deferred().addErrback(lambda *args: None)
+
+    def _firePluginError(self):
+        """Return dPluginError or a dummy if it was already called."""
+        if self._dPluginError:
+            d = self._dPluginError
+            self._dPluginError = None
+            return d
+        return Deferred().addErrback(lambda *args: None)
+
+# TODO(infinity0): remove this class when twisted update their side
+class ProxyServer(_ProxyServer):
+
+    def connectionMade(self):
+        # code copied from super class, except instead of connecting
+        # to a TCP endpoint we abstract that out to a child method
+        self.transport.pauseProducing()
+
+        client = self.clientProtocolFactory()
+        client.setServer(self)
+
+        if self.reactor is None:
+            from twisted.internet import reactor
+            self.reactor = reactor
+
+        self.connectProxyClient(client)
+
+    def connectProxyClient(self, client):
+        raise NotImplementedError()
+
+class OneUseSOCKSWrapper(ProxyServer):
+
+    def connectProxyClient(self, client):
+        local_host, local_port = self.factory.method_spec.addrport
+        TCPPoint = TCP4ClientEndpoint(
+            self.reactor,
+            local_host,
+            local_port)
+        # Next PT may need either SOCKS4 or SOCKS5 so check its protocol and get the required class
+        socks_endpoint_class = self.getSocksEndpointClass()
+        SOCKSPoint = socks_endpoint_class(
+                    self.factory.remote_host,
+                    self.factory.remote_port,
+                    TCPPoint)
+        # Store port for debugging messages before stopListening is called.
+        # listen_port will not have a port after stopListening is called.
+        stored_port = self.factory.listen_port.getHost().port
+        d_port_closed = self.factory.listen_port.stopListening()
+        d_port_closed.addCallback(
+            lambda x: logger.debug("Closed factory listener %s on port %s" % (self.factory, stored_port)))
+        d_port_closed.addErrback(
+            lambda x: logger.warn("Failed to close factory listener %s listening on port %s" % (self.factory, stored_port)))
+        d = SOCKSPoint.connect(client)
+        d.chainDeferred(self.factory.d_connected)
+        @d.addErrback
+        def _gotError(error):
+            log.err(error, "error connecting to SOCKS server")
+
+    def getSocksEndpointClass(self):
+        """
+        Checks self.factory.method_spec.protocol and returns the appropriate socks endpoint class.
+        """
+        socks_endpoint_class = None
+        if self.factory.method_spec.protocol == 'socks4':
+            socks_endpoint_class = SOCKS4ClientEndpoint
+        elif self.factory.method_spec.protocol == 'socks5':
+            socks_endpoint_class = SOCKS5ClientEndpoint
+        else:
+            raise ValueError("Pluggable transport requires unknown protocol %s. Supported protocols are %s" %
+                            (self.factory.method_spec.protocol, ('socks4', 'socks5')))
+        return socks_endpoint_class
+
+class OneUseSOCKSFactory(Factory):
+    protocol = OneUseSOCKSWrapper
+    def __init__(self, method_spec, remote_host, remote_port):
+        self._connected_once = False
+        self.method_spec = method_spec
+        self.remote_host = remote_host
+        self.remote_port = remote_port
+        self.d_connected = Deferred()
+        self.listen_port = None
+
+    def __str__(self):
+        return "OneUseSOCKSFactory connecting %s to %s:%s" % (self.method_spec, self.remote_host, self.remote_port)
+
+    def __repr__(self):
+        return "OneUseSOCKSFactory(%s, %s, %s)" % (self.method_spec, self.remote_host, self.remote_port)
+
+    def setListenPort(self, listen_port):
+        """
+        Sets the listen_port object.
+        :param function listen_port: The function returned from a ListenTCP call. Used to shutdown the port when a connection is made.
+        """
+        self.listen_port = listen_port
+
+    def whenConnected(self):
+        """
+        Returns a new Deferred that triggers when a connection is successfully made.
+        """
+        return branch(self.d_connected)
+
+    def buildProtocol(self, addr):
+        """
+        Only allows one protocol to be created. After that it always returns None
+        :param twisted.internet.interfaces.IAddress addr: an object implementing L{twisted.internet.interfaces.IAddress}
+        """
+        if self._connected_once:
+            return None
+        else:
+            self._connected_once = True
+            return Factory.buildProtocol(self, addr)
+
+if sys.platform == "win32":
+    # TODO(infinity0): push this upstream to Twisted
+    from twisted.internet import _pollingfile
+    import msvcrt
+
+    _StandardIO = StandardIO
+    class StandardIO(_StandardIO):
+
+        def __init__(self, proto, stdin=None, stdout=None, reactor=None):
+            """
+            Start talking to standard IO with the given protocol.
+
+            Also, put it stdin/stdout/stderr into binary mode.
+            """
+            if reactor is None:
+                import twisted.internet.reactor
+                reactor = twisted.internet.reactor
+
+            _pollingfile._PollingTimer.__init__(self, reactor)
+            self.proto = proto
+
+            fdstdin = stdin or sys.stdin.fileno()
+            fdstdout = stdout or sys.stdout.fileno()
+
+            for stdfd in (fdstdin, fdstdout):
+                msvcrt.setmode(stdfd, os.O_BINARY)
+
+            hstdin = msvcrt.get_osfhandle(fdstdin)
+            self.stdin = _pollingfile._PollableReadPipe(
+                hstdin, self.dataReceived, self.readConnectionLost)
+
+            hstdout = msvcrt.get_osfhandle(fdstdout)
+            self.stdout = _pollingfile._PollableWritePipe(
+                hstdout, self.writeConnectionLost)
+
+            self._addPollableResource(self.stdin)
+            self._addPollableResource(self.stdout)
+
+            self.proto.makeConnection(self)
+
+def pt_launch_child(reactor, client, methodnames, pt_method_name, cmdline):
+    """Launch a child PT and ensure it has the right transport methods."""
+    cur_env = pt_child_env(ManagedTransportProtocolV1.protocol_version)
+    environment = dict(cur_env + {
+            "TOR_PT_CLIENT_TRANSPORTS": ",".join(methodnames),
+        }.items())
+    sub_proc = Popen(cmdline,
+        stdout = PIPE,
+        env = environment,
+        )
+    sub_protocol = ManagedTransportProtocolV1()
+    # we ought to pass reactor=reactor in below, but this breaks Twisted 12
+    StandardIO(sub_protocol, stdin=sub_proc.stdout.fileno())
+    methoddefers = [sub_protocol.whenCMethodsDone().addCallback(
+                        partial(pt_require_child, client, name, pt_method_name))
+                    for name in methodnames]
+    return sub_proc, sub_protocol, methoddefers
+
+def pt_require_child(client, childmethod, pt_method_name, cmethods):
+    """Callback for checking a child PT has the right transport methods."""
+    if childmethod not in cmethods:
+        client.reportMethodError(pt_method_name, "failed to start required child transport: %s" % childmethod)
+        raise ValueError()
+    return cmethods[childmethod]
+
+def pt_setup_socks_shim(pt_name, pt_chain, success_list, dest_address, dest_port, reactor, proxy_deferreds):
+    """
+    Launches a socks proxy server to link two PTs together.
+    :param str pt_name: The name of the pt to send traffic to.
+    :param list pt_chain: The list of PTs in this chain.
+    :param list success_list: A list of tuples containing a launch status boolean, MethodSpec pairs.
+        Ex: [(True, MethodSpec(name='dummy', protocol='socks4', addrport=('127.0.0.1', 58982), args=[], opts=[])),
+            (True, MethodSpec(name='b64', protocol='socks4', addrport=('127.0.0.1', 58981), args=[], opts=[]))]
+    :param str dest_address: The address for the next PT to send its results to.
+    :param int dest_port: The port for the next PT to send to.
+    :param twisted.internet.interfaces.IReactor reactor: Reactor to attack the TCP server to.
+
+    :param list proxy_deferreds: This list has each factorys' deferred appended to it.
+
+    :returns twisted.internet.interfaces.IListeningPort: An IListeningPort used for shutting down a factory after a connection is made.
+    """
+    methodspec = [r[1] for r in success_list if r[1].name == pt_name][0] # Returns the resulting methodspec.
+    factory = OneUseSOCKSFactory(methodspec, dest_address, dest_port)
+    # TODO switch to using endpoints instead of listenTCP
+    proxy_server = reactor.listenTCP(interface='127.0.0.1', port=0, factory=factory)
+    factory.setListenPort(proxy_server)
+    proxy_deferreds.append(factory.whenConnected())
+    logger.debug("launched %s on port %s with dest %s:%s" % (pt_name, proxy_server.getHost().port, dest_address, dest_port))
+    return proxy_server
+
+def pt_launch_chain(dest_address, dest_port, pt_chain, _chain_set_up, reactor, success_list):
+    """
+    Launches a chain of pluggable transports by connecting each pt with SOCKS proxies.
+    :param str dest_address: The bridge address to connect to.
+    :param int dest_port: The bridge port to connect to.
+    :param list pt_chain: The list of pt names to launch.
+    :param function _chain_set_up: The function to call when the shims have been set up.
+    :param twisted.internet.interfaces.IReactor reactor: Reactor to install this PT to.
+    :param list success_list: A list of tuples containing a launch status boolean, MethodSpec pairs.
+        Ex: [(True, MethodSpec(name='dummy', protocol='socks4', addrport=('127.0.0.1', 58982), args=[], opts=[])),
+            (True, MethodSpec(name='b64', protocol='socks4', addrport=('127.0.0.1', 58981), args=[], opts=[]))]
+    """
+    proxy_deferreds = []
+    last_pt_name = pt_chain[-1]
+    logger.debug("launching chain %s" % pt_chain)
+    # Initialize prev_server to the port picked by the last proxy server as that's the only one we know yet.
+    last_server = pt_setup_socks_shim(last_pt_name, pt_chain, success_list, dest_address, dest_port,
+                                    reactor, proxy_deferreds)
+    prev_server = last_server
+    for pt_name in reversed(pt_chain[:-1]):
+        # Loops through the pts linking them together through SOCKS proxies, skipping the last pt.
+        prev_server = pt_setup_socks_shim(pt_name, pt_chain, success_list, '127.0.0.1', prev_server.getHost().port,
+                                        reactor, proxy_deferreds)
+    def check_chain_all_connected(protocol_list):
+        """
+        Checks all the shims launched to see if they successfully connected.
+        :param list protocol_list: A list of tuples containing status boolean, twisted.protocols.portforward.ProxyClient pairs.
+            Ex: [(True, <twisted.protocols.portforward.ProxyClient instance at 0x10b825518>),
+                 (True, <twisted.protocols.portforward.ProxyClient instance at 0x10b829518>)]
+        """
+        if all([result[0] for result in protocol_list]):
+            logger.debug("All PT shims connected correctly")
+        else:
+            # At this point the SOCKS protocol is in communication mode so no need to call makeReply(91)
+            # This assumes that the child pluggable transport will shut down the connection cleanly.
+            failed_protocols = [x[1] for x in protocol_list if x[0] == False]
+            logger.error("Shims %s failed to connect." % failed_protocols)
+            raise ValueError()
+
+    finished = DeferredList(proxy_deferreds)
+    finished.addCallback(check_chain_all_connected)
+    _chain_set_up(prev_server.getHost().host, prev_server.getHost().port)
+
+def pt_launch_interceptor(reactor, client, configuration, pt_method_name, success_list):
+    """
+    Launches a SOCKS interceptor.
+    :param twisted.internet.interfaces.IReactor reactor: Reactor to install this PT to.
+    :param pyptlib.client.ClientTransportPlugin client: PT client API.
+    :param Config configuration: The configuration structure for this pair.
+    :param str pt_method_name: The name of the pt chain to launch. Ex: "obfs3_flashproxy"
+    :param list success_list: A list of tuples containing a launch status boolean, MethodSpec pairs.
+        Ex: [(True, MethodSpec(name='dummy', protocol='socks4', addrport=('127.0.0.1', 58982), args=[], opts=[])),
+            (True, MethodSpec(name='b64', protocol='socks4', addrport=('127.0.0.1', 58981), args=[], opts=[]))]
+    """
+    logger.debug("launching interceptor")
+    pt_chain = configuration.alias_map[pt_method_name]
+    success = all(r[0] for r in success_list if r[1].name in pt_chain)
+    # failure was already reported by pt_require_child, just return
+    if not success: return
+    socks_interceptor = SOCKSv4InterceptorFactory(pt_method_name,
+                        lambda dest_address, dest_port, pt_method_name, chain_finished:
+                            pt_launch_chain(dest_address, dest_port, pt_chain, chain_finished, reactor, success_list))
+    # TODO switch to using endpoints instead of listenTCP
+    interceptor = reactor.listenTCP(interface='127.0.0.1', port=0, factory=socks_interceptor)
+    interceptor_port = interceptor.getHost().port
+    client.reportMethodSuccess(pt_method_name, "socks4", ("127.0.0.1", interceptor_port))
+    client.reportMethodsEnd()
+
+def pt_setup_transports(reactor, client, configuration, pt_method_name):
+    """
+    Launches the PT processes.
+    :param twisted.internet.interfaces.IReactor reactor: Reactor to install this PT to.
+    :param pyptlib.client.ClientTransportPlugin client: PT client API.
+    :param Config configuration: The configuration structure for this pair.
+    :param str pt_method_name: The name of the pt chain to launch. Ex: "obfs3_flashproxy"
+    """
+    logger.debug("Setting up transports %s" % pt_method_name)
+    if pt_method_name in configuration.alias_map:
+        pt_chain = configuration.alias_map[pt_method_name]
+    else:
+        logger.error('Pluggable Transport Combination %s not found in configuration alias map.' % pt_method_name)
+        raise KeyError()
+
+    defer_list = []
+
+    if len(pt_chain) < 2:
+        raise ValueError("PT Chain %s does not contain enough transports." % pt_chain)
+
+    for pt in pt_chain:
+        if pt in configuration.transport_map:
+            pt_cmdline = configuration.transport_map[pt]
+        else:
+            raise ValueError("Pluggable transport %s not found in transport_map. Check your configuration file." % pt)
+        _, _, defer = pt_launch_child(reactor, client, [pt], pt_method_name, pt_cmdline)
+        defer_list.extend(defer)
+    whenAllDone = DeferredList(defer_list, consumeErrors=False)
+    whenAllDone.addCallback(lambda success_list: pt_launch_interceptor(reactor, client, configuration, pt_method_name, success_list))
+
+
+class Config():
+    # Transport map links a pluggable transport name to the a commandline to launch it.
+    # Ex: {'b64' : 'exec obfsproxy managed'}
+    transport_map = None
+
+    #Alias map links a pluggable transport chain name to a list of individual pluggable transports
+    # Ex: {'dummy_b64_dummy2' : ['dummy''b64''dummy2']}
+    alias_map = None
+
+    def __init__(self, transport_map, alias_map):
+        self.transport_map = transport_map
+        self.alias_map = alias_map
+
+    def __repr__(self):
+        return "Config(%s, %s)" % (self.transport_map, self.alias_map)
+
+    def __str__(self):
+        return "Config Object with transport_map: %s, and alias_map %s." % (self.transport_map, self.alias_map)
+
+    @classmethod
+    def parse(cls, config_string):
+        """
+        Reads a configuration string and returns an instance of configuration. Uses shlex to parse configuration lines.
+        :param str config_string: The string which will be parsed to populate the transport_map and alias_map hash tables.
+        See the file example-fog-config for format.
+        """
+        # TODO Add possibility of reading a ClientTransportPlugin with multiple transport types
+        # Ex: ClientTransportPlugin obfs3,scramblesuit obfsclient --option=value
+
+        line_counter = 0
+        lines = config_string.split('\n')
+        transport_map = {}
+        alias_map = {}
+
+        for line in lines:
+            line_counter += 1
+            if len(line) > 0 and line[0] != '#' : # Check for empty lines and comment tags on the first
+                line = line.strip()
+                delimited_tokens = shlex.split(line)
+                if len(delimited_tokens) > 1:
+                    config_line_type = delimited_tokens[0] # This can be either Alias or ClientTransportPlugin
+                    if config_line_type == 'ClientTransportPlugin':
+                        cls.parse_transport_line(transport_map, delimited_tokens, line_counter)
+                    elif config_line_type == 'Alias':
+                        cls.parse_alias_line(alias_map, transport_map, delimited_tokens, line_counter)
+                    else:
+                        logger.warn("Configuration file has unknown line %s: '%s'" % (line_counter, line))
+        return cls(transport_map, alias_map)
+
+    @classmethod
+    def parse_transport_line(cls, transport_map, delimited_tokens, line_counter):
+        transport_name = delimited_tokens[1]
+        transport_cmdline = delimited_tokens[2:]
+        if transport_name in transport_map:
+            raise ValueError('Configuration file has duplicate ClientTransportPlugin lines. Duplicate line is at line number %s' % line_counter)
+        transport_map[transport_name] = transport_cmdline
+
+    @classmethod
+    def parse_alias_line(cls, alias_map, transport_map, delimited_tokens, line_counter):
+        alias_name = delimited_tokens[1] # Example: "obfs3_flashproxy"
+        alias_path = delimited_tokens[2].split('|') # Example: "obfs3|flashproxy"
+        if alias_name in alias_map:
+            raise ValueError('Configuration file has duplicate Alias lines. Duplicate line is at line number %s' % line_counter)
+        for pt_name in alias_path:
+            if pt_name not in transport_map:
+                raise KeyError('Transport map is missing pluggable transport %s needed for chain %s. Check your configuration file for a ClientTransportPlugin line can launch %s' % (pt_name, alias_name, pt_name))
+        alias_map[alias_name] = alias_path
+
+def main(*args):
+    parser = argparse.ArgumentParser()
+    parser.add_argument("-f", help="fog configuration file path",
+        metavar='FOGFILE', type=argparse.FileType('r'), default=DEFAULT_CONFIG_FILE_NAME)
+
+    pt_setup_logger()
+    # TODO(infinity0): add an "external" mode, which would require us to run
+    # obfsproxy in external mode too.
+
+    opts = parser.parse_args(args)
+    configuration = None
+    file_contents = opts.f.read()
+    configuration = Config.parse(file_contents)
+    pt_method_names = configuration.alias_map.keys()
+    client = ClientTransportPlugin()
+    client.init(pt_method_names) # Initialize our possible methods to all the chains listed by the fog file and stored in alias map.
+    if not client.getTransports():
+        logger.error("no transports to serve. pt_method_names may be invalid.")
+        return 1
+
+    from twisted.internet import reactor
+    auto_killall(1, cleanup=reactor.stop)
+    #TODO Change from launching a single pair to launching multiple chains.
+    pt_setup_transports(reactor, client, configuration, pt_method_names[0])
+    reactor.run(installSignalHandlers=0)
+    return 0
+
+if __name__ == "__main__":
+    sys.exit(main(*sys.argv[1:]))
+
diff --git a/fog-client/fog/__init__.py b/fog-client/fog/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/fog-client/fog/socks.py b/fog-client/fog/socks.py
new file mode 100644
index 0000000..b44135d
--- /dev/null
+++ b/fog-client/fog/socks.py
@@ -0,0 +1,59 @@
+from twisted.protocols import socks
+from twisted.internet.protocol import Factory
+import logging
+
+logger = logging.getLogger('fog-logger')
+
+class SOCKSv4InterceptorProtocol(socks.SOCKSv4):
+    """
+    A modified SOCKS protocol which extracts the requested ip and port
+    and redirects connections to the first pluggable transport in the chain.
+    """
+
+    def __init__(self, factory, pt_method_name):
+        """
+        :param twisted.internet.protocol.factory factory: The factory that launched this protocol
+        :param pt_method_name: The name of the chain to be launched when a new connection is received
+        """
+        self.factory = factory
+        self._pt_method_name = pt_method_name
+        socks.SOCKSv4.__init__(self)
+
+    def _dataReceived2(self, server, user, version, code, port):
+        """
+        Extracts the requested ip and port and redirects to a different address
+        """
+        if code == 1: # CONNECT
+            assert version == 4, "Bad version code: %s" % version
+            if not self.authorize(code, server, port, user):
+                self.makeReply(91)
+                return
+            def _chain_set_up(remote_address, remote_port):
+                logger.debug("chain finished, connecting %s:%s" % (remote_address, remote_port))
+                # Connect to our remote address instead of the requested one
+                d = self.connectClass(remote_address, remote_port, socks.SOCKSv4Outgoing, self)
+                d.addErrback(lambda result, self = self: self.makeReply(91))
+            self.factory._new_conn_callback(server, port, self._pt_method_name, _chain_set_up)
+            assert self.buf == "", "hmm, still stuff in buffer... %s" % repr(self.buf)
+        else:
+            super(SOCKSv4InterceptorProtocol, self)._dataReceived2(server, user, version, code, port)
+
+class SOCKSv4InterceptorFactory(Factory):
+
+    def __init__(self, pt_method_name, new_conn_callback):
+        """
+        :param str pt_method_name: The name of the pt_method that this factory is launching.
+        :param function new_conn_callback: The function to be called when a connection is made.
+            def new_conn_callback
+                :param str server: The ip address requested by the SOCKS client.
+                :param int port: The port requested by the SOCKS client.
+                :param str pt_method_name: The name of the pt_method this factory is a part of.
+                :param function chain_set_up: The function to be called when the chain has finished setting up.
+                            :param str remote_address: The address to relay the SOCKS request to.
+                            :param int remote_port: The port to to send the SOCKS request to.
+        """
+        self._pt_method_name = pt_method_name
+        self._new_conn_callback = new_conn_callback
+
+    def buildProtocol(self, addr):
+        return SOCKSv4InterceptorProtocol(self, self._pt_method_name)
\ No newline at end of file
diff --git a/fog-client/fogrc b/fog-client/fogrc
new file mode 100644
index 0000000..ee28514
--- /dev/null
+++ b/fog-client/fogrc
@@ -0,0 +1,17 @@
+#Based off of ticket #9744
+#Client transports are setup like so:
+#ClientTransportPlugin name commandline
+#For instance to launch obfs3, the client transport line should be this
+#ClientTransportPlugin obfs3 obfsproxy managed
+#
+#For chaining transports together, an alias line is used.
+#Alias chainname firsttransportname|secondtransportname
+#tor expects alias to use underscores instead of pipes. So an alias links the tor version of a plugin chain to the actual plugins. See ticket #9580
+
+ClientTransportPlugin obfs3 obfsproxy managed
+ClientTransportPlugin flashproxy flashproxy-client --transport obfs3|websocket --register 127.0.0.1:0 :9000 
+# If port 9000 cannot be portforwarded change it to a port that can be ported like so:
+#ClientTransportPlugin flashproxy flashproxy-client --transport obfs3|websocket --register 127.0.0.1:0 :3923
+# use a different facilitator
+#ClientTransportPlugin flashproxy flashproxy-client --transport obfs3|websocket -f http://siteb.fp-facilitator.org/fac/ --register —-register-methods=http 127.0.0.1:0 :3923 
+Alias obfs3_flashproxy obfs3|flashproxy
diff --git a/fog-client/setup.py b/fog-client/setup.py
new file mode 100644
index 0000000..125da2d
--- /dev/null
+++ b/fog-client/setup.py
@@ -0,0 +1,23 @@
+from distutils.core import setup
+import py2exe
+
+# if py2exe complains "can't find P", try one of the following workarounds:
+#
+# a. py2exe doesn't support zipped eggs - http://www.py2exe.org/index.cgi/ExeWithEggs
+#  You should give the --always-unzip option to easy_install, or you can use setup.py directly
+#  $ python setup.py install --record install.log --single-version-externally-managed
+#  Don't forget to remove the previous zipped egg.
+#
+# b. Add an empty __init__.py to the P/ top-level directory, if it's missing
+#  - this is due to a bug (or misleading documentation) in python's imp.find_module()
+
+setup(
+    console=["fog-client"],
+    zipfile="py2exe-fog-client.zip",
+    options={
+        "py2exe": {
+            "includes": ["pyptlib", "twisted", "txsocksx"],
+            "packages": ["ometa", "terml", "zope.interface"],
+        },
+    },
+)
diff --git a/fog-client/torrc b/fog-client/torrc
new file mode 100644
index 0000000..98b1f47
--- /dev/null
+++ b/fog-client/torrc
@@ -0,0 +1,5 @@
+UseBridges 1
+Bridge obfs3_flashproxy 127.0.0.1:9000
+LearnCircuitBuildTimeout 0
+CircuitBuildTimeout 300
+ClientTransportPlugin obfs3_flashproxy exec ./fog-client
diff --git a/fog-server/Makefile b/fog-server/Makefile
new file mode 100644
index 0000000..c7d474b
--- /dev/null
+++ b/fog-server/Makefile
@@ -0,0 +1,12 @@
+GOBUILDFLAGS =
+
+./fog-server: ./fog-server.go ./stack.go
+	go build $(GOBUILDFLAGS) -o "$@" $^
+
+test:
+	go test -v
+
+clean:
+	rm -f ./fog-server
+
+.PHONY: test clean
diff --git a/fog-server/fog-server.go b/fog-server/fog-server.go
new file mode 100644
index 0000000..bb34219
--- /dev/null
+++ b/fog-server/fog-server.go
@@ -0,0 +1,548 @@
+package main
+
+import (
+	"bufio"
+	"errors"
+	"flag"
+	"fmt"
+	"io"
+	"net"
+	"os"
+	"os/exec"
+	"os/signal"
+	"strings"
+	"sync"
+	"syscall"
+	"sort"
+	"time"
+)
+
+import "git.torproject.org/pluggable-transports/goptlib.git"
+
+const connStackSize = 1000
+const subprocessWaitTimeout = 30 * time.Second
+
+var logFile = os.Stderr
+
+var ptInfo pt.ServerInfo
+
+// When a connection handler starts, +1 is written to this channel; when it
+// ends, -1 is written.
+var handlerChan = make(chan int)
+
+func usage() {
+	fmt.Printf("Usage: %s [OPTIONS]\n", os.Args[0])
+	fmt.Printf("Chains websocket and obfsproxy server transports. pt-websocket-server and\n")
+	fmt.Printf("obfsproxy must be in PATH.\n")
+	fmt.Printf("\n")
+	fmt.Printf("  -h, --help   show this help.\n")
+	fmt.Printf("  --log FILE   log messages to FILE (default stderr).\n")
+	fmt.Printf("  --port PORT  listen on PORT (overrides Tor's requested port).\n")
+}
+
+var logMutex sync.Mutex
+
+func log(format string, v ...interface{}) {
+	dateStr := time.Now().Format("2006-01-02 15:04:05")
+	logMutex.Lock()
+	defer logMutex.Unlock()
+	msg := fmt.Sprintf(format, v...)
+	fmt.Fprintf(logFile, "%s %s\n", dateStr, msg)
+}
+
+type ProcList []*os.Process
+
+func (procs ProcList) Signal(sig os.Signal) {
+	for _, p := range procs {
+		log("Sending signal %q to process with pid %d.", sig, p.Pid)
+		err := p.Signal(sig)
+		if err != nil {
+			log("Error sending signal %q to process with pid %d: %s.", sig, p.Pid, err)
+		}
+	}
+}
+
+func (procs ProcList) Kill() {
+	for _, p := range procs {
+		log("Killing process with pid %d.", p.Pid)
+		err := p.Kill()
+		if err != nil {
+			log("Error killing process with pid %d: %s.", p.Pid, err)
+			continue
+		}
+		state, err := p.Wait()
+		if err != nil {
+			log("Error waiting on process with pid %d: %s.", state.Pid(), err)
+			continue
+		}
+		if !state.Exited() {
+			log("Process with pid %d didn't exit.", state.Pid())
+			continue
+		}
+	}
+}
+
+type Chain struct {
+	MethodName   string
+	ExtLn, IntLn *net.TCPListener
+	ProcsAddr    *net.TCPAddr
+	Procs        ProcList
+	// This stack forwards external IP addresses to the extended ORPort.
+	Conns *Stack
+}
+
+func (chain *Chain) CloseListeners() {
+	if chain.ExtLn != nil {
+		err := chain.ExtLn.Close()
+		if err != nil {
+			log("Error closing external listener: %s.", err)
+		}
+	}
+	if chain.IntLn != nil {
+		err := chain.IntLn.Close()
+		if err != nil {
+			log("Error closing internal listener: %s.", err)
+		}
+	}
+}
+
+func (chain *Chain) Shutdown() {
+	chain.CloseListeners()
+	chain.Procs.Kill()
+	for {
+		elem, ok := chain.Conns.Pop()
+		if !ok {
+			break
+		}
+		conn := elem.(*net.TCPConn)
+		log("Closing stale connection from %s.", conn.RemoteAddr())
+		err := conn.Close()
+		if err != nil {
+		}
+	}
+}
+
+func findBindAddr(r io.Reader, methodName string) (*net.TCPAddr, error) {
+	br := bufio.NewReader(r)
+	for {
+		line, err := br.ReadString('\n')
+		if err != nil {
+			return nil, err
+		}
+		log("Received from sub-transport: %q.", line)
+		fields := strings.Fields(strings.TrimRight(line, "\n"))
+		if len(fields) < 1 {
+			continue
+		}
+		keyword := fields[0]
+		args := fields[1:]
+		if keyword == "SMETHOD" && len(args) >= 2 && args[0] == methodName {
+			bindaddr, err := net.ResolveTCPAddr("tcp", args[1])
+			if err != nil {
+				return nil, err
+			}
+			return bindaddr, nil
+		} else if keyword == "SMETHODS" && len(args) == 1 && args[0] == "DONE" {
+			break
+		}
+	}
+	return nil, errors.New(fmt.Sprintf("no SMETHOD %s found before SMETHODS DONE", methodName))
+}
+
+// Escape a string for a ServerTransportOptions serialization.
+func escape(s string) string {
+	repl := strings.NewReplacer(":", "\\:", ";", "\\;", "=", "\\=", "\\", "\\\\")
+	return repl.Replace(s)
+}
+
+func encodeServerTransportOptions(methodName string, opts pt.Args) string {
+	if opts == nil {
+		return ""
+	}
+	keys := make([]string, 0, len(opts))
+	for key, _ := range opts {
+		keys = append(keys, key)
+	}
+	sort.Strings(keys)
+	parts := make([]string, 0, len(keys))
+	for _, key := range keys {
+		for _, value := range opts[key] {
+			parts = append(parts, escape(methodName) + ":" + escape(key) + "=" + escape(value))
+		}
+	}
+	return strings.Join(parts, ";")
+}
+
+// Represents a server transport plugin configuration like:
+// 	ServerTransportPlugin MethodName exec Command
+type ServerTransportPlugin struct {
+	MethodName string
+	Command    []string
+	Options    pt.Args
+}
+
+func startProcesses(connectBackAddr net.Addr, plugins []ServerTransportPlugin) (bindAddr *net.TCPAddr, procs ProcList, err error) {
+	var stdout io.ReadCloser
+
+	defer func() {
+		if err != nil {
+			// Kill subprocesses before returning error.
+			procs.Kill()
+			procs = procs[:0]
+		}
+	}()
+
+	bindAddr = connectBackAddr.(*net.TCPAddr)
+	for _, plugin := range plugins {
+		// This plugin has its TOR_PT_ORPORT set to the previous
+		// bindAddr.
+		cmd := exec.Command(plugin.Command[0], plugin.Command[1:]...)
+		cmd.Env = []string{
+			"TOR_PT_MANAGED_TRANSPORT_VER=1",
+			"TOR_PT_STATE_LOCATION=" + os.Getenv("TOR_PT_STATE_LOCATION"),
+			"TOR_PT_EXTENDED_SERVER_PORT=",
+			"TOR_PT_ORPORT=" + bindAddr.String(),
+			"TOR_PT_SERVER_TRANSPORTS=" + plugin.MethodName,
+			"TOR_PT_SERVER_BINDADDR=" + plugin.MethodName + "-127.0.0.1:0",
+		}
+		serverTransportOptions := encodeServerTransportOptions(plugin.MethodName, plugin.Options)
+		if serverTransportOptions != "" {
+			cmd.Env = append(cmd.Env, "TOR_PT_SERVER_TRANSPORT_OPTIONS=" + serverTransportOptions)
+		}
+		log("%s environment %q", cmd.Args[0], cmd.Env)
+		stdout, err = cmd.StdoutPipe()
+		if err != nil {
+			log("Failed to open %s stdout pipe: %s.", cmd.Args[0], err)
+			return
+		}
+		err = cmd.Start()
+		if err != nil {
+			log("Failed to start %s: %s.", cmd.Args[0], err)
+			return
+		}
+		log("Exec %s with args %q pid %d.", cmd.Path, cmd.Args, cmd.Process.Pid)
+		procs = append(procs, cmd.Process)
+
+		bindAddr, err = findBindAddr(stdout, plugin.MethodName)
+		if err != nil {
+			log("Failed to find %s bindaddr: %s.", cmd.Args[0], err)
+			return
+		}
+		log("%s bindaddr is %s.", cmd.Args[0], bindAddr)
+	}
+
+	return bindAddr, procs, err
+}
+
+func acceptLoop(name string, ln *net.TCPListener, ch chan *net.TCPConn) {
+	for {
+		conn, err := ln.AcceptTCP()
+		if err != nil {
+			log("%s accept: %s.", name, err)
+			break
+		}
+		log("%s connection from %s.", name, conn.RemoteAddr())
+		ch <- conn
+	}
+	close(ch)
+}
+
+func copyLoop(a, b *net.TCPConn) error {
+	var wg sync.WaitGroup
+
+	wg.Add(2)
+
+	go func() {
+		n, err := io.Copy(b, a)
+		if err != nil {
+			log("After %d bytes from %s to %s: %s.", n, a.RemoteAddr(), b.RemoteAddr(), err)
+		}
+		a.CloseRead()
+		b.CloseWrite()
+		wg.Done()
+	}()
+
+	go func() {
+		n, err := io.Copy(a, b)
+		if err != nil {
+			log("After %d bytes from %s to %s: %s.", n, b.RemoteAddr(), a.RemoteAddr(), err)
+		}
+		b.CloseRead()
+		a.CloseWrite()
+		wg.Done()
+	}()
+
+	wg.Wait()
+
+	return nil
+}
+
+func handleExternalConnection(conn *net.TCPConn, chain *Chain) error {
+	handlerChan <- 1
+	defer func() {
+		handlerChan <- -1
+	}()
+
+	chain.Conns.Push(conn)
+	log("handleExternalConnection: now %d conns buffered.", chain.Conns.Length())
+	procsConn, err := net.DialTCP("tcp", nil, chain.ProcsAddr)
+	if err != nil {
+		log("error dialing proxy chain: %s.", err)
+		return err
+	}
+	err = copyLoop(conn, procsConn)
+	if err != nil {
+		log("error copying between ext and proxy chain: %s.", err)
+		return err
+	}
+	return nil
+}
+
+func handleInternalConnection(conn *net.TCPConn, chain *Chain) error {
+	handlerChan <- 1
+	defer func() {
+		handlerChan <- -1
+	}()
+
+	elem, ok := chain.Conns.Pop()
+	if !ok {
+		log("Underflow of connection stack, closing connection.")
+		err := conn.Close()
+		if err != nil {
+			log("Error in close: %s.", err)
+		}
+		return errors.New("connection stack underflow")
+	}
+	extConn := elem.(*net.TCPConn)
+	log("Connecting to ORPort using remote addr %s.", extConn.RemoteAddr())
+	log("handleInternalConnection: now %d conns buffered.", chain.Conns.Length())
+	or, err := pt.DialOr(&ptInfo, extConn.RemoteAddr().String(), chain.MethodName)
+	if err != nil {
+		log("Error connecting to ORPort: %s.", err)
+		return err
+	}
+	err = copyLoop(or, conn)
+	if err != nil {
+		log("Error copying between int and ORPort: %s.", err)
+		return err
+	}
+	return nil
+}
+
+func listenerLoop(chain *Chain) {
+	extChan := make(chan *net.TCPConn)
+	intChan := make(chan *net.TCPConn)
+	go acceptLoop("external", chain.ExtLn, extChan)
+	go acceptLoop("internal", chain.IntLn, intChan)
+
+loop:
+	for {
+		select {
+		case conn, ok := <-extChan:
+			if !ok {
+				break loop
+			}
+			go handleExternalConnection(conn, chain)
+		case conn, ok := <-intChan:
+			if !ok {
+				break loop
+			}
+			go handleInternalConnection(conn, chain)
+		}
+	}
+}
+
+func startChain(methodName string, bindaddr *net.TCPAddr, plugins []ServerTransportPlugin) (*Chain, error) {
+	chain := &Chain{}
+	var err error
+
+	chain.MethodName = methodName
+	chain.Conns = NewStack(connStackSize)
+
+	// Start internal listener (the proxy chain connects back to this).
+	chain.IntLn, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0})
+	if err != nil {
+		log("Error opening internal listener: %s.", err)
+		chain.Shutdown()
+		return nil, err
+	}
+	log("Internal listener on %s.", chain.IntLn.Addr())
+
+	// Start subprocesses.
+	chain.ProcsAddr, chain.Procs, err = startProcesses(chain.IntLn.Addr(), plugins)
+	if err != nil {
+		log("Error starting proxy chain: %s.", err)
+		chain.Shutdown()
+		return nil, err
+	}
+	log("Proxy chain on %s.", chain.ProcsAddr)
+
+	// Start external Internet listener (listens on bindaddr and connects to
+	// proxy chain).
+	chain.ExtLn, err = net.ListenTCP("tcp", bindaddr)
+	if err != nil {
+		log("Error opening external listener: %s.", err)
+		chain.Shutdown()
+		return nil, err
+	}
+	log("External listener on %s.", chain.ExtLn.Addr())
+
+	go listenerLoop(chain)
+
+	return chain, nil
+}
+
+type Configuration struct {
+	// Map from method names to command strings.
+	Transports map[string][]string
+	// Map from method names to ServerTransportOptions.
+	Options map[string]pt.Args
+	// Map from tor-friendly names like "obfs3_websocket" to systematic
+	// names like "obfs3|websocket".
+	Aliases map[string]string
+}
+
+func (conf *Configuration) MethodNames() []string {
+	result := make([]string, 0)
+	// We understand all the single transports
+	for k, _ := range conf.Transports {
+		result = append(result, k)
+	}
+	// and aliases.
+	for k, _ := range conf.Aliases {
+		result = append(result, k)
+	}
+	return result
+}
+
+// Parse a (possibly composed) method name into a slice of single method names.
+func (conf *Configuration) ParseMethodName(methodName string) []string {
+	if name, ok := conf.Aliases[methodName]; ok {
+		methodName = name
+	}
+	return strings.Split(methodName, "|")
+}
+
+func (conf *Configuration) PluginList(methodName string) ([]ServerTransportPlugin, error) {
+	names := conf.ParseMethodName(methodName)
+	stp := make([]ServerTransportPlugin, 0)
+	for _, name := range names {
+		command, ok := conf.Transports[name]
+		if !ok {
+			return nil, errors.New(fmt.Sprintf("no transport named %q", name))
+		}
+		options := conf.Options[name]
+		stp = append(stp, ServerTransportPlugin{name, command, options})
+	}
+	return stp, nil
+}
+
+// Simulate loading a configuration file.
+func getConfiguration() (conf *Configuration) {
+	conf = new(Configuration)
+	conf.Transports = make(map[string][]string)
+	conf.Aliases = make(map[string]string)
+	conf.Options = make(map[string]pt.Args)
+	conf.Transports["obfs3"] = []string{"obfsproxy", "managed"}
+	conf.Transports["websocket"] = []string{"pt-websocket-server"}
+	// conf.Options["obfs3"] = make(pt.Args)
+	// conf.Options["obfs3"]["secret"] = []string{"foo"}
+	conf.Aliases["obfs3_websocket"] = "obfs3|websocket"
+	return conf
+}
+
+func main() {
+	var logFilename string
+	var port int
+
+	flag.Usage = usage
+	flag.StringVar(&logFilename, "log", "", "log file to write to")
+	flag.IntVar(&port, "port", 0, "port to listen on if unspecified by Tor")
+	flag.Parse()
+
+	if logFilename != "" {
+		f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Can't open log file %q: %s.\n", logFilename, err.Error())
+			os.Exit(1)
+		}
+		logFile = f
+	}
+
+	log("Starting.")
+
+	var err error
+	conf := getConfiguration()
+	ptInfo, err = pt.ServerSetup(conf.MethodNames())
+	if err != nil {
+		log("Error in ServerSetup: %s", err)
+		os.Exit(1)
+	}
+
+	chains := make([]*Chain, 0)
+	for _, bindaddr := range ptInfo.Bindaddrs {
+		// Override tor's requested port (which is 0 if this transport
+		// has not been run before) with the one requested by the --port
+		// option.
+		if port != 0 {
+			bindaddr.Addr.Port = port
+		}
+
+		plugins, err := conf.PluginList(bindaddr.MethodName)
+		if err != nil {
+			pt.SmethodError(bindaddr.MethodName, err.Error())
+			continue
+		}
+
+		chain, err := startChain(bindaddr.MethodName, bindaddr.Addr, plugins)
+		if err != nil {
+			pt.SmethodError(bindaddr.MethodName, err.Error())
+			continue
+		}
+		pt.Smethod(bindaddr.MethodName, chain.ExtLn.Addr())
+		chains = append(chains, chain)
+	}
+	pt.SmethodsDone()
+
+	var numHandlers int = 0
+	var sig os.Signal
+	sigChan := make(chan os.Signal, 1)
+	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+
+	sig = nil
+	for sig == nil {
+		select {
+		case n := <-handlerChan:
+			numHandlers += n
+		case sig = <-sigChan:
+		}
+	}
+	log("Got first signal %q with %d running handlers.", sig, numHandlers)
+	for _, chain := range chains {
+		chain.CloseListeners()
+		chain.Procs.Signal(sig)
+	}
+
+	if sig == syscall.SIGTERM {
+		log("Caught signal %q, exiting.", sig)
+		return
+	}
+
+	sig = nil
+	for sig == nil && numHandlers != 0 {
+		select {
+		case n := <-handlerChan:
+			numHandlers += n
+			log("%d remaining handlers.", numHandlers)
+		case sig = <-sigChan:
+		}
+	}
+	if sig != nil {
+		log("Got second signal %q with %d running handlers.", sig, numHandlers)
+		for _, chain := range chains {
+			chain.Procs.Signal(sig)
+		}
+	}
+
+	log("Exiting.")
+}
diff --git a/fog-server/pt_test.go b/fog-server/pt_test.go
new file mode 100644
index 0000000..35d3aab
--- /dev/null
+++ b/fog-server/pt_test.go
@@ -0,0 +1,39 @@
+package main
+
+import "testing"
+
+import "git.torproject.org/pluggable-transports/goptlib.git"
+
+func TestEncodeServerTransportOptions(t *testing.T) {
+	tests := [...]struct {
+		methodName string
+		opts       pt.Args
+		expected   string
+	}{
+		{
+			"foo",
+			pt.Args{},
+			"",
+		},
+		{
+			"foo",
+			pt.Args{
+				"key": []string{"value1", "value2"},
+				"something": []string{"value1", "value2"},
+			},
+			"foo:key=value1;foo:key=value2;foo:something=value1;foo:something=value2",
+		},
+		{
+			"m:m",
+			pt.Args{"k;k": []string{"v=v", "b\\b"}},
+			"m\\:m:k\\;k=v\\=v;m\\:m:k\\;k=b\\\\b",
+		},
+	}
+
+	for _, test := range tests {
+		output := encodeServerTransportOptions(test.methodName, test.opts)
+		if output != test.expected {
+			t.Errorf("%q %q → %q (expected %q)", test.methodName, test.opts, output, test.expected)
+		}
+	}
+}
diff --git a/fog-server/stack.go b/fog-server/stack.go
new file mode 100644
index 0000000..16cddd6
--- /dev/null
+++ b/fog-server/stack.go
@@ -0,0 +1,57 @@
+package main
+
+import "sync"
+
+// A fixed-size stack. If a push exceeds the capacity of the underlying slice,
+// the least recently added element is lost.
+type Stack struct {
+	buf        []interface{}
+	base, head int
+	m          sync.Mutex
+}
+
+// Create a stack with the given capacity.
+func NewStack(capacity int) *Stack {
+	return &Stack{buf: make([]interface{}, capacity+1)}
+}
+
+func (s *Stack) clamp(x int) int {
+	x = x % len(s.buf)
+	if x < 0 {
+		x += len(s.buf)
+	}
+	return x
+}
+
+func (s *Stack) Length() int {
+	s.m.Lock()
+	defer s.m.Unlock()
+	return s.clamp(s.head - s.base)
+}
+
+// If this push causes the stack to overflow, the first return value is the
+// discarded element and the second return value is false. Otherwise the second
+// return value is true.
+func (s *Stack) Push(x interface{}) (interface{}, bool) {
+	s.m.Lock()
+	defer s.m.Unlock()
+	s.buf[s.head] = x
+	s.head = s.clamp(s.head + 1)
+	if s.head == s.base {
+		s.base = s.clamp(s.base + 1)
+		return s.buf[s.head], false
+	}
+	return nil, true
+}
+
+// The second return value is false if the stack was empty, and true otherwise.
+// The first return value is defined only when the second is true.
+func (s *Stack) Pop() (interface{}, bool) {
+	s.m.Lock()
+	defer s.m.Unlock()
+	if s.head == s.base {
+		return nil, false
+	}
+	s.head = s.clamp(s.head - 1)
+	return s.buf[s.head], true
+}
diff --git a/fog-server/stack_test.go b/fog-server/stack_test.go
new file mode 100644
index 0000000..3188a2d
--- /dev/null
+++ b/fog-server/stack_test.go
@@ -0,0 +1,120 @@
+package main
+
+import "testing"
+
+// Test operations on a zero-capacity stack.
+func TestZeroCapacity(t *testing.T) {
+	var ok bool
+
+	s := NewStack(0)
+	if s.Length() != 0 {
+		t.Fatal("initial length is not 0")
+	}
+	_, ok = s.Push("a")
+	if ok || s.Length() != 0 {
+		t.Fatal()
+	}
+	_, ok = s.Pop()
+	if ok || s.Length() != 0 {
+		t.Fatal()
+	}
+}
+
+func TestPushPop(t *testing.T) {
+	var x interface{}
+	var ok bool
+
+	s := NewStack(3)
+
+	// Push elems.
+	if s.Length() != 0 {
+		t.Fatal("initial length is not 0")
+	}
+	_, ok = s.Push("a")
+	if !ok || s.Length() != 1 {
+		t.Fatal()
+	}
+	_, ok = s.Push("b")
+	if !ok || s.Length() != 2 {
+		t.Fatal()
+	}
+
+	// Pop to empty.
+	x, ok = s.Pop()
+	if !ok || x != "b" || s.Length() != 1 {
+		t.Fatal()
+	}
+	x, ok = s.Pop()
+	if !ok || x != "a" || s.Length() != 0 {
+		t.Fatal()
+	}
+	// Pop one past empty.
+	x, ok = s.Pop()
+	if ok {
+		t.Fatal()
+	}
+
+	// Push to capacity.
+	s.Push("c")
+	s.Push("d")
+	_, ok = s.Push("e")
+	if !ok || s.Length() != 3 {
+		t.Fatal("push to capacity is not at capacity")
+	}
+	// Push one past capacity.
+	x, ok = s.Push("f")
+	if ok || s.Length() != 3 {
+		t.Fatal()
+	}
+	if x != "c" {
+		t.Fatal("mismatch in overwritten element")
+	}
+
+	// Pop to empty.
+	x, ok = s.Pop()
+	if !ok || x != "f" || s.Length() != 2 {
+		t.Fatal()
+	}
+	x, ok = s.Pop()
+	if !ok || x != "e" || s.Length() != 1 {
+		t.Fatal()
+	}
+	x, ok = s.Pop()
+	if !ok || x != "d" || s.Length() != 0 {
+		t.Fatal()
+	}
+	// Pop one past empty.
+	x, ok = s.Pop()
+	if ok {
+		t.Fatal()
+	}
+}
+
+// Test underflow of an initially empty stack.
+func TestUnderflowEmpty(t *testing.T) {
+	var ok bool
+
+	s := NewStack(3)
+	_, ok = s.Pop()
+	if ok {
+		t.Fatal()
+	}
+}
+
+// Test underflow of a stack that had been full.
+func TestUnderflowFull(t *testing.T) {
+	var ok bool
+
+	s := NewStack(3)
+	s.Push("a")
+	s.Push("b")
+	s.Push("c")
+	s.Push("d")
+	s.Pop()
+	s.Pop()
+	s.Pop()
+	_, ok = s.Pop()
+	if ok {
+		t.Fatal()
+	}
+}
diff --git a/fog-server/torrc b/fog-server/torrc
new file mode 100644
index 0000000..f103ddc
--- /dev/null
+++ b/fog-server/torrc
@@ -0,0 +1,5 @@
+ORPort 9999
+ExtORPort 5555
+BridgeRelay 1
+SocksPort 0
+ServerTransportPlugin obfs3_websocket exec ./bin/fog-server
diff --git a/fog/__init__.py b/fog/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/fog/socks.py b/fog/socks.py
deleted file mode 100644
index e3b483b..0000000
--- a/fog/socks.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from twisted.protocols import socks
-from twisted.internet.protocol import Factory
-import logging
-
-logger = logging.getLogger('obfs-flash-logger')
-
-class SOCKSv4InterceptorProtocol(socks.SOCKSv4):
-    """
-    A modified SOCKS protocol which extracts the requested ip and port
-    and redirects connections to the first pluggable transport in the chain.
-    """
-
-    def __init__(self, factory, pt_method_name):
-        """
-        :param twisted.internet.protocol.factory factory: The factory that launched this protocol
-        :param pt_method_name: The name of the chain to be launched when a new connection is received
-        """
-        self.factory = factory
-        self._pt_method_name = pt_method_name
-        socks.SOCKSv4.__init__(self)
-
-    def _dataReceived2(self, server, user, version, code, port):
-        """
-        Extracts the requested ip and port and redirects to a different address
-        """
-        if code == 1: # CONNECT
-            assert version == 4, "Bad version code: %s" % version
-            if not self.authorize(code, server, port, user):
-                self.makeReply(91)
-                return
-            def _chain_set_up(remote_address, remote_port):
-                logger.debug("chain finished, connecting %s:%s" % (remote_address, remote_port))
-                # Connect to our remote address instead of the requested one
-                d = self.connectClass(remote_address, remote_port, socks.SOCKSv4Outgoing, self)
-                d.addErrback(lambda result, self = self: self.makeReply(91))
-            self.factory._new_conn_callback(server, port, self._pt_method_name, _chain_set_up)
-            assert self.buf == "", "hmm, still stuff in buffer... %s" % repr(self.buf)
-        else:
-            super(SOCKSv4InterceptorProtocol, self)._dataReceived2(server, user, version, code, port)
-
-class SOCKSv4InterceptorFactory(Factory):
-
-    def __init__(self, pt_method_name, new_conn_callback):
-        """
-        :param str pt_method_name: The name of the pt_method that this factory is launching.
-        :param function new_conn_callback: The function to be called when a connection is made.
-            def new_conn_callback
-                :param str server: The ip address requested by the SOCKS client.
-                :param int port: The port requested by the SOCKS client.
-                :param str pt_method_name: The name of the pt_method this factory is a part of.
-                :param function chain_set_up: The function to be called when the chain has finished setting up.
-                            :param str remote_address: The address to relay the SOCKS request to.
-                            :param int remote_port: The port to to send the SOCKS request to.
-        """
-        self._pt_method_name = pt_method_name
-        self._new_conn_callback = new_conn_callback
-
-    def buildProtocol(self, addr):
-        return SOCKSv4InterceptorProtocol(self, self._pt_method_name)
\ No newline at end of file
diff --git a/fogrc b/fogrc
deleted file mode 100644
index ee28514..0000000
--- a/fogrc
+++ /dev/null
@@ -1,17 +0,0 @@
-#Based off of ticket #9744
-#Client transports are setup like so:
-#ClientTransportPlugin name commandline
-#For instance to launch obfs3, the client transport line should be this
-#ClientTransportPlugin obfs3 obfsproxy managed
-#
-#For chaining transports together, an alias line is used.
-#Alias chainname firsttransportname|secondtransportname
-#tor expects alias to use underscores instead of pipes. So an alias links the tor version of a plugin chain to the actual plugins. See ticket #9580
-
-ClientTransportPlugin obfs3 obfsproxy managed
-ClientTransportPlugin flashproxy flashproxy-client --transport obfs3|websocket --register 127.0.0.1:0 :9000 
-# If port 9000 cannot be portforwarded change it to a port that can be ported like so:
-#ClientTransportPlugin flashproxy flashproxy-client --transport obfs3|websocket --register 127.0.0.1:0 :3923
-# use a different facilitator
-#ClientTransportPlugin flashproxy flashproxy-client --transport obfs3|websocket -f http://siteb.fp-facilitator.org/fac/ --register —-register-methods=http 127.0.0.1:0 :3923 
-Alias obfs3_flashproxy obfs3|flashproxy
diff --git a/obfs-flash-client b/obfs-flash-client
deleted file mode 100755
index 18adff4..0000000
--- a/obfs-flash-client
+++ /dev/null
@@ -1,538 +0,0 @@
-#!/usr/bin/python
-
-import argparse
-import os
-import sys
-
-from collections import namedtuple
-from functools import partial
-
-# TODO(infinity0): this is temporary workaround until we do #10047
-if sys.platform == 'win32':
-    os.environ["KILL_CHILDREN_ON_DEATH"] = "1"
-from pyptlib.util import parse_addr_spec
-from pyptlib.util.subproc import auto_killall, Popen
-from pyptlib.client import ClientTransportPlugin
-
-from subprocess import PIPE
-
-from twisted.internet.defer import Deferred, DeferredList
-from twisted.internet.stdio import StandardIO
-from twisted.internet.protocol import Factory, connectionDone
-from twisted.internet.endpoints import TCP4ClientEndpoint
-from twisted.protocols.basic import LineReceiver
-from twisted.protocols.portforward import ProxyServer as _ProxyServer
-from twisted.python import log
-from txsocksx.client import SOCKS4ClientEndpoint, SOCKS5ClientEndpoint
-from fog.socks import SOCKSv4InterceptorFactory
-
-import shlex
-
-import logging
-
-DEFAULT_CONFIG_FILE_NAME = 'fogrc'
-
-logger = None
-def pt_setup_logger():
-    global logger
-    logger = logging.getLogger('obfs-flash-logger')
-    logger.setLevel(logging.WARNING)
-    ch = logging.StreamHandler()
-    ch.setLevel(logging.DEBUG)
-    logger.addHandler(ch)
-
-def pt_child_env(managed_ver, env=os.environ):
-    """
-    Prepare the environment for a child PT process, by clearing all TOR_PT_*
-    envvars except TOR_PT_STATE_LOCATION and TOR_PT_MANAGED_TRANSPORT_VER.
-    """
-    exempt = ['TOR_PT_STATE_LOCATION']
-    cur_env = [(k, v) for k, v in env.iteritems()
-                      if not k.startswith('TOR_PT_') or k in exempt]
-    cur_env.append(('TOR_PT_MANAGED_TRANSPORT_VER', ','.join(managed_ver)))
-    return cur_env
-
-class MethodSpec(namedtuple('MethodSpec', 'name protocol addrport args opts')):
-    @classmethod
-    def fromLine(cls, line):
-        args = line.rstrip('\n').split(' ')
-        name = args[0]
-        protocol = args[1]
-        addrport = parse_addr_spec(args[2])
-        args = args[3][-5:].split(',') if len(args) > 3 and args[3].startswith("ARGS=") else []
-        opts = args[4][-9:].split(',') if len(args) > 4 and args[4].startswith("OPT-ARGS=") else []
-        return MethodSpec(name, protocol, addrport, args, opts)
-
-def branch(parent):
-    """
-    Returns a new Deferred that does not advance the callback-chain of the parent.
-
-    See http://xph.us/2009/12/10/asynchronous-programming-in-python.html for motivation.
-    """
-    d = Deferred()
-    parent.addCallback(lambda v: (v, d.callback(v))[0])
-    parent.addErrback(lambda f: (f, d.errback(f))[1])
-    return d
-
-class ManagedTransportProtocolV1(LineReceiver):
-    """
-    A Twisted IProtocol to read PT output.
-
-    See pt-spec.txt and others for details of the protocol.
-    """
-    # TODO(infinity0): eventually this could be padded out and moved to pyptlib
-
-    delimiter = os.linesep
-    protocol_version = "1"
-
-    def __init__(self):
-        self.cmethods = {}
-        self._dCMethodsDone = Deferred()
-        self._dPluginError = Deferred()
-        # dPluginError triggers errors on all sub-events, not the other way round
-        # so fatal sub-events should call _abort rather than errback on their Deferreds
-        self._dPluginError.addErrback(lambda f: (f, self._fireCMethodsDone().errback(f))[0])
-        # TODO(infinity0): call _abort if we don't recv CMETHODS DONE within n sec
-
-    def whenCMethodsDone(self):
-        """
-        Return a new Deferred that calls-back when CMETHODS DONE is received.
-        """
-        return branch(self._dCMethodsDone)
-
-    def whenPluginError(self):
-        """
-        Return a new Deferred that errors-back when the remote plugin fails.
-
-        Note: the success chain (callback) is never fired.
-        """
-        return branch(self._dPluginError)
-
-    def lineReceived(self, line):
-        if not line: return
-
-        (kw, args) = line.split(' ', 1)
-        if kw == "VERSION":
-            version = args.strip()
-            if version != self.protocol_version:
-                self._abort(ValueError("child used unsupported managed transport version: %s" % version))
-        elif kw == "CMETHOD":
-            cmethod = MethodSpec.fromLine(args)
-            self.cmethods[cmethod.name] = cmethod
-        elif kw == "CMETHODS" and args == "DONE":
-            self._fireCMethodsDone().callback(self.cmethods)
-        else:
-            pass # ignore unrecognised line
-
-    def connectionLost(self, reason=connectionDone):
-        self._firePluginError().errback(reason)
-
-    def _abort(self, exc):
-        self._firePluginError().errback(exc)
-        self.transport.loseConnection()
-
-    def _fireCMethodsDone(self):
-        """Return dCMethodsDone or a dummy if it was already called."""
-        if self._dCMethodsDone:
-            d = self._dCMethodsDone
-            self._dCMethodsDone = None
-            return d
-        return Deferred().addErrback(lambda *args: None)
-
-    def _firePluginError(self):
-        """Return dPluginError or a dummy if it was already called."""
-        if self._dPluginError:
-            d = self._dPluginError
-            self._dPluginError = None
-            return d
-        return Deferred().addErrback(lambda *args: None)
-
-# TODO(infinity0): remove this class when twisted update their side
-class ProxyServer(_ProxyServer):
-
-    def connectionMade(self):
-        # code copied from super class, except instead of connecting
-        # to a TCP endpoint we abstract that out to a child method
-        self.transport.pauseProducing()
-
-        client = self.clientProtocolFactory()
-        client.setServer(self)
-
-        if self.reactor is None:
-            from twisted.internet import reactor
-            self.reactor = reactor
-
-        self.connectProxyClient(client)
-
-    def connectProxyClient(self, client):
-        raise NotImplementedError()
-
-class OneUseSOCKSWrapper(ProxyServer):
-
-    def connectProxyClient(self, client):
-        local_host, local_port = self.factory.method_spec.addrport
-        TCPPoint = TCP4ClientEndpoint(
-            self.reactor,
-            local_host,
-            local_port)
-        # Next PT may need either SOCKS4 or SOCKS5 so check its protocol and get the required class
-        socks_endpoint_class = self.getSocksEndpointClass()
-        SOCKSPoint = socks_endpoint_class(
-                    self.factory.remote_host,
-                    self.factory.remote_port,
-                    TCPPoint)
-        # Store port for debugging messages before stopListening is called.
-        # listen_port will not have a port after stopListening is called.
-        stored_port = self.factory.listen_port.getHost().port
-        d_port_closed = self.factory.listen_port.stopListening()
-        d_port_closed.addCallback(
-            lambda x: logger.debug("Closed factory listener %s on port %s" % (self.factory, stored_port)))
-        d_port_closed.addErrback(
-            lambda x: logger.warn("Failed to close factory listener %s listening on port %s" % (self.factory, stored_port)))
-        d = SOCKSPoint.connect(client)
-        d.chainDeferred(self.factory.d_connected)
-        @d.addErrback
-        def _gotError(error):
-            log.err(error, "error connecting to SOCKS server")
-
-    def getSocksEndpointClass(self):
-        """
-        Checks self.factory.method_spec.protocol and returns the appropriate socks endpoint class.
-        """
-        socks_endpoint_class = None
-        if self.factory.method_spec.protocol == 'socks4':
-            socks_endpoint_class = SOCKS4ClientEndpoint
-        elif self.factory.method_spec.protocol == 'socks5':
-            socks_endpoint_class = SOCKS5ClientEndpoint
-        else:
-            raise ValueError("Pluggable transport requires unknown protocol %s. Supported protocols are %s" %
-                            (self.factory.method_spec.protocol, ('socks4', 'socks5')))
-        return socks_endpoint_class
-
-class OneUseSOCKSFactory(Factory):
-    protocol = OneUseSOCKSWrapper
-    def __init__(self, method_spec, remote_host, remote_port):
-        self._connected_once = False
-        self.method_spec = method_spec
-        self.remote_host = remote_host
-        self.remote_port = remote_port
-        self.d_connected = Deferred()
-        self.listen_port = None
-
-    def __str__(self):
-        return "OneUseSOCKSFactory connecting %s to %s:%s" % (self.method_spec, self.remote_host, self.remote_port)
-
-    def __repr__(self):
-        return "OneUseSOCKSFactory(%s, %s, %s)" % (self.method_spec, self.remote_host, self.remote_port)
-
-    def setListenPort(self, listen_port):
-        """
-        Sets the listen_port object.
-        :param function listen_port: The function returned from a ListenTCP call. Used to shutdown the port when a connection is made.
-        """
-        self.listen_port = listen_port
-
-    def whenConnected(self):
-        """
-        Returns a new Deferred that triggers when a connection is successfully made.
-        """
-        return branch(self.d_connected)
-
-    def buildProtocol(self, addr):
-        """
-        Only allows one protocol to be created. After that it always returns None
-        :param twisted.internet.interfaces.IAddress addr: an object implementing L{twisted.internet.interfaces.IAddress}
-        """
-        if self._connected_once:
-            return None
-        else:
-            self._connected_once = True
-            return Factory.buildProtocol(self, addr)
-
-if sys.platform == "win32":
-    # TODO(infinity0): push this upstream to Twisted
-    from twisted.internet import _pollingfile
-    import msvcrt
-
-    _StandardIO = StandardIO
-    class StandardIO(_StandardIO):
-
-        def __init__(self, proto, stdin=None, stdout=None, reactor=None):
-            """
-            Start talking to standard IO with the given protocol.
-
-            Also, put it stdin/stdout/stderr into binary mode.
-            """
-            if reactor is None:
-                import twisted.internet.reactor
-                reactor = twisted.internet.reactor
-
-            _pollingfile._PollingTimer.__init__(self, reactor)
-            self.proto = proto
-
-            fdstdin = stdin or sys.stdin.fileno()
-            fdstdout = stdout or sys.stdout.fileno()
-
-            for stdfd in (fdstdin, fdstdout):
-                msvcrt.setmode(stdfd, os.O_BINARY)
-
-            hstdin = msvcrt.get_osfhandle(fdstdin)
-            self.stdin = _pollingfile._PollableReadPipe(
-                hstdin, self.dataReceived, self.readConnectionLost)
-
-            hstdout = msvcrt.get_osfhandle(fdstdout)
-            self.stdout = _pollingfile._PollableWritePipe(
-                hstdout, self.writeConnectionLost)
-
-            self._addPollableResource(self.stdin)
-            self._addPollableResource(self.stdout)
-
-            self.proto.makeConnection(self)
-
-def pt_launch_child(reactor, client, methodnames, pt_method_name, cmdline):
-    """Launch a child PT and ensure it has the right transport methods."""
-    cur_env = pt_child_env(ManagedTransportProtocolV1.protocol_version)
-    environment = dict(cur_env + {
-            "TOR_PT_CLIENT_TRANSPORTS": ",".join(methodnames),
-        }.items())
-    sub_proc = Popen(cmdline,
-        stdout = PIPE,
-        env = environment,
-        )
-    sub_protocol = ManagedTransportProtocolV1()
-    # we ought to pass reactor=reactor in below, but this breaks Twisted 12
-    StandardIO(sub_protocol, stdin=sub_proc.stdout.fileno())
-    methoddefers = [sub_protocol.whenCMethodsDone().addCallback(
-                        partial(pt_require_child, client, name, pt_method_name))
-                    for name in methodnames]
-    return sub_proc, sub_protocol, methoddefers
-
-def pt_require_child(client, childmethod, pt_method_name, cmethods):
-    """Callback for checking a child PT has the right transport methods."""
-    if childmethod not in cmethods:
-        client.reportMethodError(pt_method_name, "failed to start required child transport: %s" % childmethod)
-        raise ValueError()
-    return cmethods[childmethod]
-
-def pt_setup_socks_shim(pt_name, pt_chain, success_list, dest_address, dest_port, reactor, proxy_deferreds):
-    """
-    Launches a socks proxy server to link two PTs together.
-    :param str pt_name: The name of the pt to send traffic to.
-    :param list pt_chain: The list of PTs in this chain.
-    :param list success_list: A list of tuples containing a launch status boolean, MethodSpec pairs.
-        Ex: [(True, MethodSpec(name='dummy', protocol='socks4', addrport=('127.0.0.1', 58982), args=[], opts=[])),
-            (True, MethodSpec(name='b64', protocol='socks4', addrport=('127.0.0.1', 58981), args=[], opts=[]))]
-    :param str dest_address: The address for the next PT to send its results to.
-    :param int dest_port: The port for the next PT to send to.
-    :param twisted.internet.interfaces.IReactor reactor: Reactor to attack the TCP server to.
-
-    :param list proxy_deferreds: This list has each factorys' deferred appended to it.
-
-    :returns twisted.internet.interfaces.IListeningPort: An IListeningPort used for shutting down a factory after a connection is made.
-    """
-    methodspec = [r[1] for r in success_list if r[1].name == pt_name][0] # Returns the resulting methodspec.
-    factory = OneUseSOCKSFactory(methodspec, dest_address, dest_port)
-    # TODO switch to using endpoints instead of listenTCP
-    proxy_server = reactor.listenTCP(interface='127.0.0.1', port=0, factory=factory)
-    factory.setListenPort(proxy_server)
-    proxy_deferreds.append(factory.whenConnected())
-    logger.debug("launched %s on port %s with dest %s:%s" % (pt_name, proxy_server.getHost().port, dest_address, dest_port))
-    return proxy_server
-
-def pt_launch_chain(dest_address, dest_port, pt_chain, _chain_set_up, reactor, success_list):
-    """
-    Launches a chain of pluggable transports by connecting each pt with SOCKS proxies.
-    :param str dest_address: The bridge address to connect to.
-    :param int dest_port: The bridge port to connect to.
-    :param list pt_chain: The list of pt names to launch.
-    :param function _chain_set_up: The function to call when the shims have been set up.
-    :param twisted.internet.interfaces.IReactor reactor: Reactor to install this PT to.
-    :param list success_list: A list of tuples containing a launch status boolean, MethodSpec pairs.
-        Ex: [(True, MethodSpec(name='dummy', protocol='socks4', addrport=('127.0.0.1', 58982), args=[], opts=[])),
-            (True, MethodSpec(name='b64', protocol='socks4', addrport=('127.0.0.1', 58981), args=[], opts=[]))]
-    """
-    proxy_deferreds = []
-    last_pt_name = pt_chain[-1]
-    logger.debug("launching chain %s" % pt_chain)
-    # Initialize prev_server to the port picked by the last proxy server as that's the only one we know yet.
-    last_server = pt_setup_socks_shim(last_pt_name, pt_chain, success_list, dest_address, dest_port,
-                                    reactor, proxy_deferreds)
-    prev_server = last_server
-    for pt_name in reversed(pt_chain[:-1]):
-        # Loops through the pts linking them together through SOCKS proxies, skipping the last pt.
-        prev_server = pt_setup_socks_shim(pt_name, pt_chain, success_list, '127.0.0.1', prev_server.getHost().port,
-                                        reactor, proxy_deferreds)
-    def check_chain_all_connected(protocol_list):
-        """
-        Checks all the shims launched to see if they successfully connected.
-        :param list protocol_list: A list of tuples containing status boolean, twisted.protocols.portforward.ProxyClient pairs.
-            Ex: [(True, <twisted.protocols.portforward.ProxyClient instance at 0x10b825518>),
-                 (True, <twisted.protocols.portforward.ProxyClient instance at 0x10b829518>)]
-        """
-        if all([result[0] for result in protocol_list]):
-            logger.debug("All PT shims connected correctly")
-        else:
-            # At this point the SOCKS protocol is in communication mode so no need to call makeReply(91)
-            # This assumes that the child pluggable transport will shut down the connection cleanly.
-            failed_protocols = [x[1] for x in protocol_list if x[0] == False]
-            logger.error("Shims %s failed to connect." % failed_protocols)
-            raise ValueError()
-
-    finished = DeferredList(proxy_deferreds)
-    finished.addCallback(check_chain_all_connected)
-    _chain_set_up(prev_server.getHost().host, prev_server.getHost().port)
-
-def pt_launch_interceptor(reactor, client, configuration, pt_method_name, success_list):
-    """
-    Launches a SOCKS interceptor.
-    :param twisted.internet.interfaces.IReactor reactor: Reactor to install this PT to.
-    :param pyptlib.client.ClientTransportPlugin client: PT client API.
-    :param Config configuration: The configuration structure for this pair.
-    :param str pt_method_name: The name of the pt chain to launch. Ex: "obfs3_flashproxy"
-    :param list success_list: A list of tuples containing a launch status boolean, MethodSpec pairs.
-        Ex: [(True, MethodSpec(name='dummy', protocol='socks4', addrport=('127.0.0.1', 58982), args=[], opts=[])),
-            (True, MethodSpec(name='b64', protocol='socks4', addrport=('127.0.0.1', 58981), args=[], opts=[]))]
-    """
-    logger.debug("launching interceptor")
-    pt_chain = configuration.alias_map[pt_method_name]
-    success = all(r[0] for r in success_list if r[1].name in pt_chain)
-    # failure was already reported by pt_require_child, just return
-    if not success: return
-    socks_interceptor = SOCKSv4InterceptorFactory(pt_method_name,
-                        lambda dest_address, dest_port, pt_method_name, chain_finished:
-                            pt_launch_chain(dest_address, dest_port, pt_chain, chain_finished, reactor, success_list))
-    # TODO switch to using endpoints instead of listenTCP
-    interceptor = reactor.listenTCP(interface='127.0.0.1', port=0, factory=socks_interceptor)
-    interceptor_port = interceptor.getHost().port
-    client.reportMethodSuccess(pt_method_name, "socks4", ("127.0.0.1", interceptor_port))
-    client.reportMethodsEnd()
-
-def pt_setup_transports(reactor, client, configuration, pt_method_name):
-    """
-    Launches the PT processes.
-    :param twisted.internet.interfaces.IReactor reactor: Reactor to install this PT to.
-    :param pyptlib.client.ClientTransportPlugin client: PT client API.
-    :param Config configuration: The configuration structure for this pair.
-    :param str pt_method_name: The name of the pt chain to launch. Ex: "obfs3_flashproxy"
-    """
-    logger.debug("Setting up transports %s" % pt_method_name)
-    if pt_method_name in configuration.alias_map:
-        pt_chain = configuration.alias_map[pt_method_name]
-    else:
-        logger.error('Pluggable Transport Combination %s not found in configuration alias map.' % pt_method_name)
-        raise KeyError()
-
-    defer_list = []
-
-    if len(pt_chain) < 2:
-        raise ValueError("PT Chain %s does not contain enough transports." % pt_chain)
-
-    for pt in pt_chain:
-        if pt in configuration.transport_map:
-            pt_cmdline = configuration.transport_map[pt]
-        else:
-            raise ValueError("Pluggable transport %s not found in transport_map. Check your configuration file." % pt)
-        _, _, defer = pt_launch_child(reactor, client, [pt], pt_method_name, pt_cmdline)
-        defer_list.extend(defer)
-    whenAllDone = DeferredList(defer_list, consumeErrors=False)
-    whenAllDone.addCallback(lambda success_list: pt_launch_interceptor(reactor, client, configuration, pt_method_name, success_list))
-
-
-class Config():
-    # Transport map links a pluggable transport name to the a commandline to launch it.
-    # Ex: {'b64' : 'exec obfsproxy managed'}
-    transport_map = None
-
-    #Alias map links a pluggable transport chain name to a list of individual pluggable transports
-    # Ex: {'dummy_b64_dummy2' : ['dummy''b64''dummy2']}
-    alias_map = None
-
-    def __init__(self, transport_map, alias_map):
-        self.transport_map = transport_map
-        self.alias_map = alias_map
-
-    def __repr__(self):
-        return "Config(%s, %s)" % (self.transport_map, self.alias_map)
-
-    def __str__(self):
-        return "Config Object with transport_map: %s, and alias_map %s." % (self.transport_map, self.alias_map)
-
-    @classmethod
-    def parse(cls, config_string):
-        """
-        Reads a configuration string and returns an instance of configuration. Uses shlex to parse configuration lines.
-        :param str config_string: The string which will be parsed to populate the transport_map and alias_map hash tables.
-        See the file example-fog-config for format.
-        """
-        # TODO Add possibility of reading a ClientTransportPlugin with multiple transport types
-        # Ex: ClientTransportPlugin obfs3,scramblesuit obfsclient --option=value
-
-        line_counter = 0
-        lines = config_string.split('\n')
-        transport_map = {}
-        alias_map = {}
-
-        for line in lines:
-            line_counter += 1
-            if len(line) > 0 and line[0] != '#' : # Check for empty lines and comment tags on the first
-                line = line.strip()
-                delimited_tokens = shlex.split(line)
-                if len(delimited_tokens) > 1:
-                    config_line_type = delimited_tokens[0] # This can be either Alias or ClientTransportPlugin
-                    if config_line_type == 'ClientTransportPlugin':
-                        cls.parse_transport_line(transport_map, delimited_tokens, line_counter)
-                    elif config_line_type == 'Alias':
-                        cls.parse_alias_line(alias_map, transport_map, delimited_tokens, line_counter)
-                    else:
-                        logger.warn("Configuration file has unknown line %s: '%s'" % (line_counter, line))
-        return cls(transport_map, alias_map)
-
-    @classmethod
-    def parse_transport_line(cls, transport_map, delimited_tokens, line_counter):
-        transport_name = delimited_tokens[1]
-        transport_cmdline = delimited_tokens[2:]
-        if transport_name in transport_map:
-            raise ValueError('Configuration file has duplicate ClientTransportPlugin lines. Duplicate line is at line number %s' % line_counter)
-        transport_map[transport_name] = transport_cmdline
-
-    @classmethod
-    def parse_alias_line(cls, alias_map, transport_map, delimited_tokens, line_counter):
-        alias_name = delimited_tokens[1] # Example: "obfs3_flashproxy"
-        alias_path = delimited_tokens[2].split('|') # Example: "obfs3|flashproxy"
-        if alias_name in alias_map:
-            raise ValueError('Configuration file has duplicate Alias lines. Duplicate line is at line number %s' % line_counter)
-        for pt_name in alias_path:
-            if pt_name not in transport_map:
-                raise KeyError('Transport map is missing pluggable transport %s needed for chain %s. Check your configuration file for a ClientTransportPlugin line can launch %s' % (pt_name, alias_name, pt_name))
-        alias_map[alias_name] = alias_path
-
-def main(*args):
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-f", help="fog configuration file path",
-        metavar='FOGFILE', type=argparse.FileType('r'), default=DEFAULT_CONFIG_FILE_NAME)
-
-    pt_setup_logger()
-    # TODO(infinity0): add an "external" mode, which would require us to run
-    # obfsproxy in external mode too.
-
-    opts = parser.parse_args(args)
-    configuration = None
-    file_contents = opts.f.read()
-    configuration = Config.parse(file_contents)
-    pt_method_names = configuration.alias_map.keys()
-    client = ClientTransportPlugin()
-    client.init(pt_method_names) # Initialize our possible methods to all the chains listed by the fog file and stored in alias map.
-    if not client.getTransports():
-        logger.error("no transports to serve. pt_method_names may be invalid.")
-        return 1
-
-    from twisted.internet import reactor
-    auto_killall(1, cleanup=reactor.stop)
-    #TODO Change from launching a single pair to launching multiple chains.
-    pt_setup_transports(reactor, client, configuration, pt_method_names[0])
-    reactor.run(installSignalHandlers=0)
-    return 0
-
-if __name__ == "__main__":
-    sys.exit(main(*sys.argv[1:]))
-
diff --git a/obfs-flash-server.go b/obfs-flash-server.go
deleted file mode 100644
index bb34219..0000000
--- a/obfs-flash-server.go
+++ /dev/null
@@ -1,548 +0,0 @@
-package main
-
-import (
-	"bufio"
-	"errors"
-	"flag"
-	"fmt"
-	"io"
-	"net"
-	"os"
-	"os/exec"
-	"os/signal"
-	"strings"
-	"sync"
-	"syscall"
-	"sort"
-	"time"
-)
-
-import "git.torproject.org/pluggable-transports/goptlib.git"
-
-const connStackSize = 1000
-const subprocessWaitTimeout = 30 * time.Second
-
-var logFile = os.Stderr
-
-var ptInfo pt.ServerInfo
-
-// When a connection handler starts, +1 is written to this channel; when it
-// ends, -1 is written.
-var handlerChan = make(chan int)
-
-func usage() {
-	fmt.Printf("Usage: %s [OPTIONS]\n", os.Args[0])
-	fmt.Printf("Chains websocket and obfsproxy server transports. pt-websocket-server and\n")
-	fmt.Printf("obfsproxy must be in PATH.\n")
-	fmt.Printf("\n")
-	fmt.Printf("  -h, --help   show this help.\n")
-	fmt.Printf("  --log FILE   log messages to FILE (default stderr).\n")
-	fmt.Printf("  --port PORT  listen on PORT (overrides Tor's requested port).\n")
-}
-
-var logMutex sync.Mutex
-
-func log(format string, v ...interface{}) {
-	dateStr := time.Now().Format("2006-01-02 15:04:05")
-	logMutex.Lock()
-	defer logMutex.Unlock()
-	msg := fmt.Sprintf(format, v...)
-	fmt.Fprintf(logFile, "%s %s\n", dateStr, msg)
-}
-
-type ProcList []*os.Process
-
-func (procs ProcList) Signal(sig os.Signal) {
-	for _, p := range procs {
-		log("Sending signal %q to process with pid %d.", sig, p.Pid)
-		err := p.Signal(sig)
-		if err != nil {
-			log("Error sending signal %q to process with pid %d: %s.", sig, p.Pid, err)
-		}
-	}
-}
-
-func (procs ProcList) Kill() {
-	for _, p := range procs {
-		log("Killing process with pid %d.", p.Pid)
-		err := p.Kill()
-		if err != nil {
-			log("Error killing process with pid %d: %s.", p.Pid, err)
-			continue
-		}
-		state, err := p.Wait()
-		if err != nil {
-			log("Error waiting on process with pid %d: %s.", state.Pid(), err)
-			continue
-		}
-		if !state.Exited() {
-			log("Process with pid %d didn't exit.", state.Pid())
-			continue
-		}
-	}
-}
-
-type Chain struct {
-	MethodName   string
-	ExtLn, IntLn *net.TCPListener
-	ProcsAddr    *net.TCPAddr
-	Procs        ProcList
-	// This stack forwards external IP addresses to the extended ORPort.
-	Conns *Stack
-}
-
-func (chain *Chain) CloseListeners() {
-	if chain.ExtLn != nil {
-		err := chain.ExtLn.Close()
-		if err != nil {
-			log("Error closing external listener: %s.", err)
-		}
-	}
-	if chain.IntLn != nil {
-		err := chain.IntLn.Close()
-		if err != nil {
-			log("Error closing internal listener: %s.", err)
-		}
-	}
-}
-
-func (chain *Chain) Shutdown() {
-	chain.CloseListeners()
-	chain.Procs.Kill()
-	for {
-		elem, ok := chain.Conns.Pop()
-		if !ok {
-			break
-		}
-		conn := elem.(*net.TCPConn)
-		log("Closing stale connection from %s.", conn.RemoteAddr())
-		err := conn.Close()
-		if err != nil {
-		}
-	}
-}
-
-func findBindAddr(r io.Reader, methodName string) (*net.TCPAddr, error) {
-	br := bufio.NewReader(r)
-	for {
-		line, err := br.ReadString('\n')
-		if err != nil {
-			return nil, err
-		}
-		log("Received from sub-transport: %q.", line)
-		fields := strings.Fields(strings.TrimRight(line, "\n"))
-		if len(fields) < 1 {
-			continue
-		}
-		keyword := fields[0]
-		args := fields[1:]
-		if keyword == "SMETHOD" && len(args) >= 2 && args[0] == methodName {
-			bindaddr, err := net.ResolveTCPAddr("tcp", args[1])
-			if err != nil {
-				return nil, err
-			}
-			return bindaddr, nil
-		} else if keyword == "SMETHODS" && len(args) == 1 && args[0] == "DONE" {
-			break
-		}
-	}
-	return nil, errors.New(fmt.Sprintf("no SMETHOD %s found before SMETHODS DONE", methodName))
-}
-
-// Escape a string for a ServerTransportOptions serialization.
-func escape(s string) string {
-	repl := strings.NewReplacer(":", "\\:", ";", "\\;", "=", "\\=", "\\", "\\\\")
-	return repl.Replace(s)
-}
-
-func encodeServerTransportOptions(methodName string, opts pt.Args) string {
-	if opts == nil {
-		return ""
-	}
-	keys := make([]string, 0, len(opts))
-	for key, _ := range opts {
-		keys = append(keys, key)
-	}
-	sort.Strings(keys)
-	parts := make([]string, 0, len(keys))
-	for _, key := range keys {
-		for _, value := range opts[key] {
-			parts = append(parts, escape(methodName) + ":" + escape(key) + "=" + escape(value))
-		}
-	}
-	return strings.Join(parts, ";")
-}
-
-// Represents a server transport plugin configuration like:
-// 	ServerTransportPlugin MethodName exec Command
-type ServerTransportPlugin struct {
-	MethodName string
-	Command    []string
-	Options    pt.Args
-}
-
-func startProcesses(connectBackAddr net.Addr, plugins []ServerTransportPlugin) (bindAddr *net.TCPAddr, procs ProcList, err error) {
-	var stdout io.ReadCloser
-
-	defer func() {
-		if err != nil {
-			// Kill subprocesses before returning error.
-			procs.Kill()
-			procs = procs[:0]
-		}
-	}()
-
-	bindAddr = connectBackAddr.(*net.TCPAddr)
-	for _, plugin := range plugins {
-		// This plugin has its TOR_PT_ORPORT set to the previous
-		// bindAddr.
-		cmd := exec.Command(plugin.Command[0], plugin.Command[1:]...)
-		cmd.Env = []string{
-			"TOR_PT_MANAGED_TRANSPORT_VER=1",
-			"TOR_PT_STATE_LOCATION=" + os.Getenv("TOR_PT_STATE_LOCATION"),
-			"TOR_PT_EXTENDED_SERVER_PORT=",
-			"TOR_PT_ORPORT=" + bindAddr.String(),
-			"TOR_PT_SERVER_TRANSPORTS=" + plugin.MethodName,
-			"TOR_PT_SERVER_BINDADDR=" + plugin.MethodName + "-127.0.0.1:0",
-		}
-		serverTransportOptions := encodeServerTransportOptions(plugin.MethodName, plugin.Options)
-		if serverTransportOptions != "" {
-			cmd.Env = append(cmd.Env, "TOR_PT_SERVER_TRANSPORT_OPTIONS=" + serverTransportOptions)
-		}
-		log("%s environment %q", cmd.Args[0], cmd.Env)
-		stdout, err = cmd.StdoutPipe()
-		if err != nil {
-			log("Failed to open %s stdout pipe: %s.", cmd.Args[0], err)
-			return
-		}
-		err = cmd.Start()
-		if err != nil {
-			log("Failed to start %s: %s.", cmd.Args[0], err)
-			return
-		}
-		log("Exec %s with args %q pid %d.", cmd.Path, cmd.Args, cmd.Process.Pid)
-		procs = append(procs, cmd.Process)
-
-		bindAddr, err = findBindAddr(stdout, plugin.MethodName)
-		if err != nil {
-			log("Failed to find %s bindaddr: %s.", cmd.Args[0], err)
-			return
-		}
-		log("%s bindaddr is %s.", cmd.Args[0], bindAddr)
-	}
-
-	return bindAddr, procs, err
-}
-
-func acceptLoop(name string, ln *net.TCPListener, ch chan *net.TCPConn) {
-	for {
-		conn, err := ln.AcceptTCP()
-		if err != nil {
-			log("%s accept: %s.", name, err)
-			break
-		}
-		log("%s connection from %s.", name, conn.RemoteAddr())
-		ch <- conn
-	}
-	close(ch)
-}
-
-func copyLoop(a, b *net.TCPConn) error {
-	var wg sync.WaitGroup
-
-	wg.Add(2)
-
-	go func() {
-		n, err := io.Copy(b, a)
-		if err != nil {
-			log("After %d bytes from %s to %s: %s.", n, a.RemoteAddr(), b.RemoteAddr(), err)
-		}
-		a.CloseRead()
-		b.CloseWrite()
-		wg.Done()
-	}()
-
-	go func() {
-		n, err := io.Copy(a, b)
-		if err != nil {
-			log("After %d bytes from %s to %s: %s.", n, b.RemoteAddr(), a.RemoteAddr(), err)
-		}
-		b.CloseRead()
-		a.CloseWrite()
-		wg.Done()
-	}()
-
-	wg.Wait()
-
-	return nil
-}
-
-func handleExternalConnection(conn *net.TCPConn, chain *Chain) error {
-	handlerChan <- 1
-	defer func() {
-		handlerChan <- -1
-	}()
-
-	chain.Conns.Push(conn)
-	log("handleExternalConnection: now %d conns buffered.", chain.Conns.Length())
-	procsConn, err := net.DialTCP("tcp", nil, chain.ProcsAddr)
-	if err != nil {
-		log("error dialing proxy chain: %s.", err)
-		return err
-	}
-	err = copyLoop(conn, procsConn)
-	if err != nil {
-		log("error copying between ext and proxy chain: %s.", err)
-		return err
-	}
-	return nil
-}
-
-func handleInternalConnection(conn *net.TCPConn, chain *Chain) error {
-	handlerChan <- 1
-	defer func() {
-		handlerChan <- -1
-	}()
-
-	elem, ok := chain.Conns.Pop()
-	if !ok {
-		log("Underflow of connection stack, closing connection.")
-		err := conn.Close()
-		if err != nil {
-			log("Error in close: %s.", err)
-		}
-		return errors.New("connection stack underflow")
-	}
-	extConn := elem.(*net.TCPConn)
-	log("Connecting to ORPort using remote addr %s.", extConn.RemoteAddr())
-	log("handleInternalConnection: now %d conns buffered.", chain.Conns.Length())
-	or, err := pt.DialOr(&ptInfo, extConn.RemoteAddr().String(), chain.MethodName)
-	if err != nil {
-		log("Error connecting to ORPort: %s.", err)
-		return err
-	}
-	err = copyLoop(or, conn)
-	if err != nil {
-		log("Error copying between int and ORPort: %s.", err)
-		return err
-	}
-	return nil
-}
-
-func listenerLoop(chain *Chain) {
-	extChan := make(chan *net.TCPConn)
-	intChan := make(chan *net.TCPConn)
-	go acceptLoop("external", chain.ExtLn, extChan)
-	go acceptLoop("internal", chain.IntLn, intChan)
-
-loop:
-	for {
-		select {
-		case conn, ok := <-extChan:
-			if !ok {
-				break loop
-			}
-			go handleExternalConnection(conn, chain)
-		case conn, ok := <-intChan:
-			if !ok {
-				break loop
-			}
-			go handleInternalConnection(conn, chain)
-		}
-	}
-}
-
-func startChain(methodName string, bindaddr *net.TCPAddr, plugins []ServerTransportPlugin) (*Chain, error) {
-	chain := &Chain{}
-	var err error
-
-	chain.MethodName = methodName
-	chain.Conns = NewStack(connStackSize)
-
-	// Start internal listener (the proxy chain connects back to this).
-	chain.IntLn, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0})
-	if err != nil {
-		log("Error opening internal listener: %s.", err)
-		chain.Shutdown()
-		return nil, err
-	}
-	log("Internal listener on %s.", chain.IntLn.Addr())
-
-	// Start subprocesses.
-	chain.ProcsAddr, chain.Procs, err = startProcesses(chain.IntLn.Addr(), plugins)
-	if err != nil {
-		log("Error starting proxy chain: %s.", err)
-		chain.Shutdown()
-		return nil, err
-	}
-	log("Proxy chain on %s.", chain.ProcsAddr)
-
-	// Start external Internet listener (listens on bindaddr and connects to
-	// proxy chain).
-	chain.ExtLn, err = net.ListenTCP("tcp", bindaddr)
-	if err != nil {
-		log("Error opening external listener: %s.", err)
-		chain.Shutdown()
-		return nil, err
-	}
-	log("External listener on %s.", chain.ExtLn.Addr())
-
-	go listenerLoop(chain)
-
-	return chain, nil
-}
-
-type Configuration struct {
-	// Map from method names to command strings.
-	Transports map[string][]string
-	// Map from method names to ServerTransportOptions.
-	Options map[string]pt.Args
-	// Map from tor-friendly names like "obfs3_websocket" to systematic
-	// names like "obfs3|websocket".
-	Aliases map[string]string
-}
-
-func (conf *Configuration) MethodNames() []string {
-	result := make([]string, 0)
-	// We understand all the single transports
-	for k, _ := range conf.Transports {
-		result = append(result, k)
-	}
-	// and aliases.
-	for k, _ := range conf.Aliases {
-		result = append(result, k)
-	}
-	return result
-}
-
-// Parse a (possibly composed) method name into a slice of single method names.
-func (conf *Configuration) ParseMethodName(methodName string) []string {
-	if name, ok := conf.Aliases[methodName]; ok {
-		methodName = name
-	}
-	return strings.Split(methodName, "|")
-}
-
-func (conf *Configuration) PluginList(methodName string) ([]ServerTransportPlugin, error) {
-	names := conf.ParseMethodName(methodName)
-	stp := make([]ServerTransportPlugin, 0)
-	for _, name := range names {
-		command, ok := conf.Transports[name]
-		if !ok {
-			return nil, errors.New(fmt.Sprintf("no transport named %q", name))
-		}
-		options := conf.Options[name]
-		stp = append(stp, ServerTransportPlugin{name, command, options})
-	}
-	return stp, nil
-}
-
-// Simulate loading a configuration file.
-func getConfiguration() (conf *Configuration) {
-	conf = new(Configuration)
-	conf.Transports = make(map[string][]string)
-	conf.Aliases = make(map[string]string)
-	conf.Options = make(map[string]pt.Args)
-	conf.Transports["obfs3"] = []string{"obfsproxy", "managed"}
-	conf.Transports["websocket"] = []string{"pt-websocket-server"}
-	// conf.Options["obfs3"] = make(pt.Args)
-	// conf.Options["obfs3"]["secret"] = []string{"foo"}
-	conf.Aliases["obfs3_websocket"] = "obfs3|websocket"
-	return conf
-}
-
-func main() {
-	var logFilename string
-	var port int
-
-	flag.Usage = usage
-	flag.StringVar(&logFilename, "log", "", "log file to write to")
-	flag.IntVar(&port, "port", 0, "port to listen on if unspecified by Tor")
-	flag.Parse()
-
-	if logFilename != "" {
-		f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "Can't open log file %q: %s.\n", logFilename, err.Error())
-			os.Exit(1)
-		}
-		logFile = f
-	}
-
-	log("Starting.")
-
-	var err error
-	conf := getConfiguration()
-	ptInfo, err = pt.ServerSetup(conf.MethodNames())
-	if err != nil {
-		log("Error in ServerSetup: %s", err)
-		os.Exit(1)
-	}
-
-	chains := make([]*Chain, 0)
-	for _, bindaddr := range ptInfo.Bindaddrs {
-		// Override tor's requested port (which is 0 if this transport
-		// has not been run before) with the one requested by the --port
-		// option.
-		if port != 0 {
-			bindaddr.Addr.Port = port
-		}
-
-		plugins, err := conf.PluginList(bindaddr.MethodName)
-		if err != nil {
-			pt.SmethodError(bindaddr.MethodName, err.Error())
-			continue
-		}
-
-		chain, err := startChain(bindaddr.MethodName, bindaddr.Addr, plugins)
-		if err != nil {
-			pt.SmethodError(bindaddr.MethodName, err.Error())
-			continue
-		}
-		pt.Smethod(bindaddr.MethodName, chain.ExtLn.Addr())
-		chains = append(chains, chain)
-	}
-	pt.SmethodsDone()
-
-	var numHandlers int = 0
-	var sig os.Signal
-	sigChan := make(chan os.Signal, 1)
-	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
-
-	sig = nil
-	for sig == nil {
-		select {
-		case n := <-handlerChan:
-			numHandlers += n
-		case sig = <-sigChan:
-		}
-	}
-	log("Got first signal %q with %d running handlers.", sig, numHandlers)
-	for _, chain := range chains {
-		chain.CloseListeners()
-		chain.Procs.Signal(sig)
-	}
-
-	if sig == syscall.SIGTERM {
-		log("Caught signal %q, exiting.", sig)
-		return
-	}
-
-	sig = nil
-	for sig == nil && numHandlers != 0 {
-		select {
-		case n := <-handlerChan:
-			numHandlers += n
-			log("%d remaining handlers.", numHandlers)
-		case sig = <-sigChan:
-		}
-	}
-	if sig != nil {
-		log("Got second signal %q with %d running handlers.", sig, numHandlers)
-		for _, chain := range chains {
-			chain.Procs.Signal(sig)
-		}
-	}
-
-	log("Exiting.")
-}
diff --git a/pt_test.go b/pt_test.go
deleted file mode 100644
index 35d3aab..0000000
--- a/pt_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package main
-
-import "testing"
-
-import "git.torproject.org/pluggable-transports/goptlib.git"
-
-func TestEncodeServerTransportOptions(t *testing.T) {
-	tests := [...]struct {
-		methodName string
-		opts       pt.Args
-		expected   string
-	}{
-		{
-			"foo",
-			pt.Args{},
-			"",
-		},
-		{
-			"foo",
-			pt.Args{
-				"key": []string{"value1", "value2"},
-				"something": []string{"value1", "value2"},
-			},
-			"foo:key=value1;foo:key=value2;foo:something=value1;foo:something=value2",
-		},
-		{
-			"m:m",
-			pt.Args{"k;k": []string{"v=v", "b\\b"}},
-			"m\\:m:k\\;k=v\\=v;m\\:m:k\\;k=b\\\\b",
-		},
-	}
-
-	for _, test := range tests {
-		output := encodeServerTransportOptions(test.methodName, test.opts)
-		if output != test.expected {
-			t.Errorf("%q %q → %q (expected %q)", test.methodName, test.opts, output, test.expected)
-		}
-	}
-}
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 66f2262..0000000
--- a/setup.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from distutils.core import setup
-import py2exe
-
-# if py2exe complains "can't find P", try one of the following workarounds:
-#
-# a. py2exe doesn't support zipped eggs - http://www.py2exe.org/index.cgi/ExeWithEggs
-#  You should give the --always-unzip option to easy_install, or you can use setup.py directly
-#  $ python setup.py install --record install.log --single-version-externally-managed
-#  Don't forget to remove the previous zipped egg.
-#
-# b. Add an empty __init__.py to the P/ top-level directory, if it's missing
-#  - this is due to a bug (or misleading documentation) in python's imp.find_module()
-
-setup(
-    console=["obfs-flash-client"],
-    zipfile="py2exe-obfs-flash-client.zip",
-    options={
-        "py2exe": {
-            "includes": ["pyptlib", "twisted", "txsocksx"],
-            "packages": ["ometa", "terml", "zope.interface"],
-        },
-    },
-)
diff --git a/stack.go b/stack.go
deleted file mode 100644
index 16cddd6..0000000
--- a/stack.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package main
-
-import "sync"
-
-// A fixed-size stack. If a push exceeds the capacity of the underlying slice,
-// the least recently added element is lost.
-type Stack struct {
-	buf        []interface{}
-	base, head int
-	m          sync.Mutex
-}
-
-// Create a stack with the given capacity.
-func NewStack(capacity int) *Stack {
-	return &Stack{buf: make([]interface{}, capacity+1)}
-}
-
-func (s *Stack) clamp(x int) int {
-	x = x % len(s.buf)
-	if x < 0 {
-		x += len(s.buf)
-	}
-	return x
-}
-
-func (s *Stack) Length() int {
-	s.m.Lock()
-	defer s.m.Unlock()
-	return s.clamp(s.head - s.base)
-}
-
-// If this push causes the stack to overflow, the first return value is the
-// discarded element and the second return value is false. Otherwise the second
-// return value is true.
-func (s *Stack) Push(x interface{}) (interface{}, bool) {
-	s.m.Lock()
-	defer s.m.Unlock()
-	s.buf[s.head] = x
-	s.head = s.clamp(s.head + 1)
-	if s.head == s.base {
-		s.base = s.clamp(s.base + 1)
-		return s.buf[s.head], false
-	}
-	return nil, true
-}
-
-// The second return value is false if the stack was empty, and true otherwise.
-// The first return value is defined only when the second is true.
-func (s *Stack) Pop() (interface{}, bool) {
-	s.m.Lock()
-	defer s.m.Unlock()
-	if s.head == s.base {
-		return nil, false
-	}
-	s.head = s.clamp(s.head - 1)
-	return s.buf[s.head], true
-}
diff --git a/stack_test.go b/stack_test.go
deleted file mode 100644
index 3188a2d..0000000
--- a/stack_test.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package main
-
-import "testing"
-
-// Test operations on a zero-capacity stack.
-func TestZeroCapacity(t *testing.T) {
-	var ok bool
-
-	s := NewStack(0)
-	if s.Length() != 0 {
-		t.Fatal("initial length is not 0")
-	}
-	_, ok = s.Push("a")
-	if ok || s.Length() != 0 {
-		t.Fatal()
-	}
-	_, ok = s.Pop()
-	if ok || s.Length() != 0 {
-		t.Fatal()
-	}
-}
-
-func TestPushPop(t *testing.T) {
-	var x interface{}
-	var ok bool
-
-	s := NewStack(3)
-
-	// Push elems.
-	if s.Length() != 0 {
-		t.Fatal("initial length is not 0")
-	}
-	_, ok = s.Push("a")
-	if !ok || s.Length() != 1 {
-		t.Fatal()
-	}
-	_, ok = s.Push("b")
-	if !ok || s.Length() != 2 {
-		t.Fatal()
-	}
-
-	// Pop to empty.
-	x, ok = s.Pop()
-	if !ok || x != "b" || s.Length() != 1 {
-		t.Fatal()
-	}
-	x, ok = s.Pop()
-	if !ok || x != "a" || s.Length() != 0 {
-		t.Fatal()
-	}
-	// Pop one past empty.
-	x, ok = s.Pop()
-	if ok {
-		t.Fatal()
-	}
-
-	// Push to capacity.
-	s.Push("c")
-	s.Push("d")
-	_, ok = s.Push("e")
-	if !ok || s.Length() != 3 {
-		t.Fatal("push to capacity is not at capacity")
-	}
-	// Push one past capacity.
-	x, ok = s.Push("f")
-	if ok || s.Length() != 3 {
-		t.Fatal()
-	}
-	if x != "c" {
-		t.Fatal("mismatch in overwritten element")
-	}
-
-	// Pop to empty.
-	x, ok = s.Pop()
-	if !ok || x != "f" || s.Length() != 2 {
-		t.Fatal()
-	}
-	x, ok = s.Pop()
-	if !ok || x != "e" || s.Length() != 1 {
-		t.Fatal()
-	}
-	x, ok = s.Pop()
-	if !ok || x != "d" || s.Length() != 0 {
-		t.Fatal()
-	}
-	// Pop one past empty.
-	x, ok = s.Pop()
-	if ok {
-		t.Fatal()
-	}
-}
-
-// Test underflow of an initially empty stack.
-func TestUnderflowEmpty(t *testing.T) {
-	var ok bool
-
-	s := NewStack(3)
-	_, ok = s.Pop()
-	if ok {
-		t.Fatal()
-	}
-}
-
-// Test underflow of a stack that had been full.
-func TestUnderflowFull(t *testing.T) {
-	var ok bool
-
-	s := NewStack(3)
-	s.Push("a")
-	s.Push("b")
-	s.Push("c")
-	s.Push("d")
-	s.Pop()
-	s.Pop()
-	s.Pop()
-	_, ok = s.Pop()
-	if ok {
-		t.Fatal()
-	}
-}
diff --git a/torrc b/torrc
deleted file mode 100644
index 32c558e..0000000
--- a/torrc
+++ /dev/null
@@ -1,5 +0,0 @@
-UseBridges 1
-Bridge obfs3_flashproxy 127.0.0.1:9000
-LearnCircuitBuildTimeout 0
-CircuitBuildTimeout 300
-ClientTransportPlugin obfs3_flashproxy exec ./obfs-flash-client
diff --git a/torrc-server b/torrc-server
deleted file mode 100644
index 55b4a31..0000000
--- a/torrc-server
+++ /dev/null
@@ -1,5 +0,0 @@
-ORPort 9999
-ExtORPort 5555
-BridgeRelay 1
-SocksPort 0
-ServerTransportPlugin obfs3_websocket exec ./obfs-flash-server





More information about the tor-commits mailing list