[tor-commits] [flashproxy/master] Flatten the com/rtmfp hierarchy.

dcf at torproject.org dcf at torproject.org
Sun Jun 12 08:56:28 UTC 2011


commit d5c1258762ff34b369f9fb6cef651122c3436cdc
Author: David Fifield <david at bamsoftware.com>
Date:   Tue May 24 22:55:34 2011 -0700

    Flatten the com/rtmfp hierarchy.
    
    So that parallel files in the master branch are in the same place in
    this branch.
---
 Makefile                                      |    2 +-
 README                                        |   88 ++++---
 Utils.as                                      |   25 ++
 com/rtmfpcat/Makefile                         |   11 -
 com/rtmfpcat/README                           |  118 ---------
 com/rtmfpcat/Utils.as                         |   25 --
 com/rtmfpcat/connector.py                     |  326 -------------------------
 com/rtmfpcat/facilitator.py                   |  146 -----------
 com/rtmfpcat/rtmfp/RTMFPSocket.as             |  231 -----------------
 com/rtmfpcat/rtmfp/RTMFPSocketClient.as       |   57 -----
 com/rtmfpcat/rtmfp/events/RTMFPSocketEvent.as |   25 --
 com/rtmfpcat/rtmfpcat.as                      |  208 ----------------
 connector.py                                  |    4 +-
 facilitator.py                                |   11 +-
 rtmfp/RTMFPSocket.as                          |  231 +++++++++++++++++
 rtmfp/RTMFPSocketClient.as                    |   57 +++++
 rtmfp/events/RTMFPSocketEvent.as              |   25 ++
 rtmfpcat.as                                   |  208 ++++++++++++++++
 swfcat.as                                     |   82 +-----
 19 files changed, 622 insertions(+), 1258 deletions(-)

diff --git a/Makefile b/Makefile
index 5390327..3595385 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 MXMLC ?= mxmlc
 
-TARGETS = swfcat.swf com/rtmfpcat/rtmfpcat.swf
+TARGETS = rtmfpcat.swf
 
 all: $(TARGETS)
 
diff --git a/README b/README
index 755ba01..17fadf5 100644
--- a/README
+++ b/README
@@ -5,12 +5,24 @@ Adobe Flash proxy running on another computer. The Flash proxy can be
 run just by opening a web page in a computer that has Flash Player
 installed.
 
+This RTMFP version leverages the NAT-punching capabilities of Adobe's
+Cirrus server, making it possible for clients behind a NAT to still
+get access to Tor. The big operational difference between this version
+and the swfcat version is that now the client must maintain a rtmfpcat
+running in client mode open in his/her browser. The client's rtmfpcat
+talks to the rtmfpcat proxy running on another computer (location uncertain)
+via the UDP-based RTMFP to funnel data to a Tor relay/bridge.
+
 There are five main parts. Our terminology for each part is in quotes.
 1. The Tor "client," running on someone's localhost.
-2. A "connector," which waits for connections from the Flash proxy and
+2. A "connector," which waits for connections from the client's rtmfpcat and
    the Tor client, and joins them together.
 3. A Flash "proxy," running in someone's web browser. This piece is
-   called swfcat because it is like a netcat implemented in Flash.
+   called rtmfpcat (totally ripped off of swfcat) because it is like a
+   netcat implemented in Flash. The rtmfpcat exists on both the local
+   and remote ends since it's the easiest way to take advantage of RTMFP.
+   We could get rid of the client-side rtmfpcat by making the connector
+   speak RTMFP, but that's too much work for now. 
 4. A "facilitator," a pseudo-HTTP server that keeps a list of clients
    that want a connection, and hands them out to proxies.
 5. A Tor "relay," which is just a normal Tor relay except that its host
@@ -18,9 +30,7 @@ There are five main parts. Our terminology for each part is in quotes.
 
 == Quick start
 
-A demonstration page that doesn't require building anything is here:
-
-http://www.stanford.edu/~dcf/tor/
+Will put up a demo page soon.
 
 === Building
 
@@ -29,34 +39,40 @@ Download the (free software) Flex SDK.
 Put its bin directory in your PATH. The important executable is mxmlc.
 To build, run
 	$ make
-Copy the resulting swfcat.swf file to a web server.
+Copy the resulting rtmfpcat.swf file to a web server.
 
 On the computer that will be the facilitator, run
-        sudo ./crossdomaind.py
-	./facilitator.py
+     sudo ./crossdomaind.py
+	 ./facilitator.py
 crossdomaind.py needs to be run on any server that will accept
 connections from a Flash proxy. It serves a chunk of data on port 843.
-The facilitator runs on port 9002 by default.
+The facilitator runs on port 9002 by default. Note that this is a different
+facilitator script than the swfcat one, since this facilitator needs to
+deal with Cirrus client IDs instead of ip:port tuples.
 
 The client needs to be running a version of Tor that supports the
 Socks4Proxy configuration. This means version 0.2.2.1-alpha or
 later--not the current stable release.
 
 On the client, run
-	./connector.py -f <FACILITATOR_IP>
-Replace <FACILITATOR_IP> with the IP address of the facilitator. (If you
-are running the facilitator locally, be sure to use an external IP
-address, not 127.0.0.1.) The connector informs the facilitator that it
-wants a connection, then listens on 0.0.0.0:9000 and 127.0.0.1:9001. The
-Flash proxy will connect on port 9000 and the local Tor will connect on
-9001.
-
-In a browser somewhere, open swfcat.swf and pass a parameter telling it
-the facilitator to use, for example
-	http://www.example.com/swfcat.swf?facilitator=<FACILITATOR_IP>:9002&debug=1
-The facilitator will return the client address that was registered by
-connector.py, and the Flash proxy will open a connection to a hardcoded
-Tor relay and the given client.
+	./connector.py
+This is a modified form of the swfcat connector.py that has different
+defaults, equivalent to passing 127.0.0.1:9001 for [LOCAL][:PORT] and
+127.0.0.1:3333 [REMOTE][:PORT] to the swfcat connector.
+
+Also on the client, open up the browser to rtmfpcat.swf. Passing no
+arguments should give you good defaults (expects the facilitator running
+on Nate's server). rtmfpcat will connect to the Cirrus server to
+obtain a client ID which it then registers with the facilitator.
+
+In a browser somewhere, open rtmfpcat.swf and pass the "?proxy=true" query
+string, telling it to operate in proxy mode.
+	http://www.example.com/rtmfpcat.swf?proxy=true
+This rtmfpcat will also connect to the Cirrus server to obtain a client ID,
+and then it will ping the facilitator to check if there are any registered
+client IDs. If there is one, it will open a RTMFP connection (coordinated
+by the Cirrus server) to the client and an additional connection to a 
+hardcoded Tor relay (David's bridge, nicknamed eRYaZuvY02FpExln).
 
 Back on the client, start Tor with the following configuration:
 	UseBridges 1
@@ -68,8 +84,8 @@ If you see messages like
 	[notice] no known bridge descriptors running yet; stalling
 try deleting the files in ~/.tor or /var/lib/tor.
 
-You will be able to see byte counts flowing in the browser displaying
-swfcat.swf, and eventually be able to build a circuit.
+You will be able to see byte counts flowing in both browsers displaying
+rtmfpcat.swf (client and proxy), and eventually be able to build a circuit.
 
 == Rationale
 
@@ -81,22 +97,26 @@ changing pool of addresses.
 
 == Design notes
 
-The Tor relay address is hardcoded in swfcat.as. It could be any relay,
+The Tor relay address is hardcoded in rtmfpcat.as. It could be any relay,
 with the caveat that the server also has to serve a crossdomain policy.
 
-The Tor client needs to be able to listen for an incoming connection,
-which generally means not being behind NAT.
-
-Clients register with the facilitator by sending an HTTP-like message:
+Client rtmfpcats register with the facilitator by sending an HTTP-like message:
 	POST / HTTP/1.0\r\n
 	\r\n
-	client=:9000
+	client=<CIRRUS-CLIENT-ID>
+
+The <CIRRUS-CLIENT-ID> is returned by Adobe's developer Cirrus server
+as soon as the rtfmpcat can connect to it. Each rtmfpcat needs to connect
+to a server like this to get one of these client IDs, since the Cirrus
+server uses these to coordinate RTMFP connections (including NAT punching).
+The need to communicate with a Cirrus server in addition to a facilitator is
+one of the major weaknesses of this design.
 
-The Flash proxy also gets a client address using something like HTTP:
+The proxy rtmfpcat gets a client id using something like HTTP:
 	GET / HTTP/1.0\r\n
 	\r\n
-The server sends back an address specification (no HTTP header):
-	192.168.0.102:8888
+The server sends back an id specification (no HTTP header):
+    51ae8ed56c3705e4ad3755cdd3328c27720984778bfff71d9ec9f2647331d39b
 
 == ActionScript programming
 
diff --git a/Utils.as b/Utils.as
new file mode 100644
index 0000000..48f9a62
--- /dev/null
+++ b/Utils.as
@@ -0,0 +1,25 @@
+package
+{
+  
+  public class Utils {
+    
+    /* Parse an address in the form "host:port". Returns an Object with
+       keys "host" (String) and "port" (int). Returns null on error. */
+    public static function parseAddrSpec(spec:String):Object
+    {
+        var parts:Array;
+        var addr:Object;
+
+        parts = spec.split(":", 2);
+        if (parts.length != 2 || !parseInt(parts[1]))
+            return null;
+        addr = {}
+        addr.host = parts[0];
+        addr.port = parseInt(parts[1]);
+
+        return addr;
+    }
+    
+  }
+  
+}
\ No newline at end of file
diff --git a/com/rtmfpcat/Makefile b/com/rtmfpcat/Makefile
deleted file mode 100644
index fbcfc20..0000000
--- a/com/rtmfpcat/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-MXMLC ?= mxmlc
-
-TARGETS = rtmfpcat.swf
-
-all: $(TARGETS)
-
-%.swf: %.as
-	$(MXMLC) -output $@ $^
-
-clean:
-	rm -f $(TARGETS)
diff --git a/com/rtmfpcat/README b/com/rtmfpcat/README
deleted file mode 100644
index 0432a63..0000000
--- a/com/rtmfpcat/README
+++ /dev/null
@@ -1,118 +0,0 @@
-== Introduction
-
-This is a set of tools that make it possible to connect Tor through an
-Adobe Flash proxy running on another computer. The Flash proxy can be
-run just by opening a web page in a computer that has Flash Player
-installed.
-
-This RTMFP version leverages the NAT-punching capabilities of Adobe's
-Cirrus server, making it possible for clients behind a NAT to still
-get access to Tor. The big operational difference between this version
-and the swfcat version is that now the client must maintain a rtmfpcat
-running in client mode open in his/her browser. The client's rtmfpcat
-talks to the rtmfpcat proxy running on another computer (location uncertain)
-via the UDP-based RTMFP to funnel data to a Tor relay/bridge.
-
-There are five main parts. Our terminology for each part is in quotes.
-1. The Tor "client," running on someone's localhost.
-2. A "connector," which waits for connections from the client's rtmfpcat and
-   the Tor client, and joins them together.
-3. A Flash "proxy," running in someone's web browser. This piece is
-   called rtmfpcat (totally ripped off of swfcat) because it is like a
-   netcat implemented in Flash. The rtmfpcat exists on both the local
-   and remote ends since it's the easiest way to take advantage of RTMFP.
-   We could get rid of the client-side rtmfpcat by making the connector
-   speak RTMFP, but that's too much work for now. 
-4. A "facilitator," a pseudo-HTTP server that keeps a list of clients
-   that want a connection, and hands them out to proxies.
-5. A Tor "relay," which is just a normal Tor relay except that its host
-   must also serve a Flash crossdomain policy.
-
-== Quick start
-
-Will put up a demo page soon.
-
-=== Building
-
-Download the (free software) Flex SDK.
-	http://opensource.adobe.com/wiki/display/flexsdk/Flex+SDK
-Put its bin directory in your PATH. The important executable is mxmlc.
-To build, run
-	$ make
-Copy the resulting rtmfpcat.swf file to a web server.
-
-On the computer that will be the facilitator, run
-     sudo ./crossdomaind.py
-	 ./facilitator.py
-crossdomaind.py needs to be run on any server that will accept
-connections from a Flash proxy. It serves a chunk of data on port 843.
-The facilitator runs on port 9002 by default. Note that this is a different
-facilitator script than the swfcat one, since this facilitator needs to
-deal with Cirrus client IDs instead of ip:port tuples.
-
-On the client, run
-	./connector.py
-This is a modified form of the swfcat connector.py that has different
-defaults, equivalent to passing 127.0.0.1:9001 for [LOCAL][:PORT] and
-127.0.0.1:3333 [REMOTE][:PORT] to the swfcat connector.
-
-Also on the client, open up the browser to rtmfpcat.swf. Passing no
-arguments should give you good defaults (expects the facilitator running
-on Nate's server). rtmfpcat will connect to the Cirrus server to
-obtain a client ID which it then registers with the facilitator.
-
-In a browser somewhere, open rtmfpcat.swf and pass the "?proxy=true" query
-string, telling it to operate in proxy mode.
-	http://www.example.com/rtmfpcat.swf?proxy=true
-This rtmfpcat will also connect to the Cirrus server to obtain a client ID,
-and then it will ping the facilitator to check if there are any registered
-client IDs. If there is one, it will open a RTMFP connection (coordinated
-by the Cirrus server) to the client and an additional connection to a 
-hardcoded Tor relay (David's bridge, nicknamed eRYaZuvY02FpExln).
-
-Back on the client, start Tor with the following configuration:
-	UseBridges 1
-	Bridge 127.0.0.1:9001
-	Socks4Proxy 127.0.0.1:9001
-
-You will be able to see byte counts flowing in both browsers displaying
-rtmfpcat.swf (client and proxy), and eventually be able to build a circuit.
-
-== Rationale
-
-The purpose of this project is to create many, generally ephemeral
-bridge IP addresses, with the goal of outpacing a censor's ability to
-block them. Rather than increasing the number of bridges at static
-addresses, we aim to make existing bridges reachable by a larger and
-changing pool of addresses.
-
-== Design notes
-
-The Tor relay address is hardcoded in rtmfpcat.as. It could be any relay,
-with the caveat that the server also has to serve a crossdomain policy.
-
-Client rtmfpcats register with the facilitator by sending an HTTP-like message:
-	POST / HTTP/1.0\r\n
-	\r\n
-	client=<CIRRUS-CLIENT-ID>
-
-The <CIRRUS-CLIENT-ID> is returned by Adobe's developer Cirrus server
-as soon as the rtfmpcat can connect to it. Each rtmfpcat needs to connect
-to a server like this to get one of these client IDs, since the Cirrus
-server uses these to coordinate RTMFP connections (including NAT punching).
-The need to communicate with a Cirrus server in addition to a facilitator is
-one of the major weaknesses of this design.
-
-The proxy rtmfpcat gets a client id using something like HTTP:
-	GET / HTTP/1.0\r\n
-	\r\n
-The server sends back an id specification (no HTTP header):
-    51ae8ed56c3705e4ad3755cdd3328c27720984778bfff71d9ec9f2647331d39b
-
-== ActionScript programming
-
-A good tutorial on ActionScript programming with the Flex tools, with
-sample code:
-
-http://www.senocular.com/flash/tutorials/as3withmxmlc/
-http://www.senocular.com/flash/tutorials/as3withmxmlc/AS3Flex2b3StarterFiles.zip
diff --git a/com/rtmfpcat/Utils.as b/com/rtmfpcat/Utils.as
deleted file mode 100644
index 48f9a62..0000000
--- a/com/rtmfpcat/Utils.as
+++ /dev/null
@@ -1,25 +0,0 @@
-package
-{
-  
-  public class Utils {
-    
-    /* Parse an address in the form "host:port". Returns an Object with
-       keys "host" (String) and "port" (int). Returns null on error. */
-    public static function parseAddrSpec(spec:String):Object
-    {
-        var parts:Array;
-        var addr:Object;
-
-        parts = spec.split(":", 2);
-        if (parts.length != 2 || !parseInt(parts[1]))
-            return null;
-        addr = {}
-        addr.host = parts[0];
-        addr.port = parseInt(parts[1]);
-
-        return addr;
-    }
-    
-  }
-  
-}
\ No newline at end of file
diff --git a/com/rtmfpcat/connector.py b/com/rtmfpcat/connector.py
deleted file mode 100755
index 63bbc5c..0000000
--- a/com/rtmfpcat/connector.py
+++ /dev/null
@@ -1,326 +0,0 @@
-#!/usr/bin/env python
-
-import getopt
-import httplib
-import re
-import select
-import socket
-import struct
-import sys
-import time
-import urllib
-import xml.sax.saxutils
-
-DEFAULT_REMOTE_ADDRESS = "127.0.0.1"
-DEFAULT_REMOTE_PORT = 3333
-DEFAULT_LOCAL_ADDRESS = "127.0.0.1"
-DEFAULT_LOCAL_PORT = 9001
-DEFAULT_FACILITATOR_PORT = 9002
-
-def usage(f = sys.stdout):
-    print >> f, """\
-Usage: %(progname)s -f FACILITATOR[:PORT] [LOCAL][:PORT] [REMOTE][:PORT]
-Wait for connections on a local and a remote port. When any pair of connections
-exists, data is ferried between them until one side is closed. By default
-LOCAL is "%(local)s" and REMOTE is "%(remote)s".
-
-The local connection acts as a SOCKS4a proxy, but the host and port in the SOCKS
-request are ignored and the local connection is always joined to a remote
-connection.
-
-If the -f option is given, then the REMOTE address is advertised to the given
-FACILITATOR.
-  -f, --facilitator=HOST[:PORT]  advertise willingness to receive connections to
-                                   HOST:PORT. By default PORT is %(fac_port)d.
-  -h, --help                     show this help.\
-""" % {
-    "progname": sys.argv[0],
-    "local": format_addr((DEFAULT_LOCAL_ADDRESS, DEFAULT_LOCAL_PORT)),
-    "remote": format_addr((DEFAULT_REMOTE_ADDRESS, DEFAULT_REMOTE_PORT)),
-    "fac_port": DEFAULT_FACILITATOR_PORT,
-}
-
-def parse_addr_spec(spec, defhost = None, defport = None):
-    host = None
-    port = None
-    m = None
-    # IPv6 syntax.
-    if not m:
-        m = re.match(r'^\[(.+)\]:(\d+)$', spec)
-        if m:
-            host, port = m.groups()
-            af = socket.AF_INET6
-    if not m:
-        m = re.match(r'^\[(.+)\]:?$', spec)
-        if m:
-            host, = m.groups()
-            af = socket.AF_INET6
-    # IPv4 syntax.
-    if not m:
-        m = re.match(r'^(.+):(\d+)$', spec)
-        if m:
-            host, port = m.groups()
-            af = socket.AF_INET
-    if not m:
-        m = re.match(r'^:?(\d+)$', spec)
-        if m:
-            port, = m.groups()
-            af = 0
-    if not m:
-        host = spec
-        af = 0
-    host = host or defhost
-    port = port or defport
-    if not (host and port):
-        raise ValueError("Bad address specification \"%s\"" % spec)
-    return host, int(port)
-
-def format_addr(addr):
-    host, port = addr
-    if not host:
-        return u":%d" % port
-    # Numeric IPv6 address?
-    try:
-        addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST)
-        af = addrs[0][0]
-    except socket.gaierror, e:
-        af = 0
-    if af == socket.AF_INET6:
-        return u"[%s]:%d" % (host, port)
-    else:
-        return u"%s:%d" % (host, port)
-
-facilitator_addr = None
-
-opts, args = getopt.gnu_getopt(sys.argv[1:], "f:h", ["facilitator", "help"])
-for o, a in opts:
-    if o == "-f" or o == "--facilitator":
-        facilitator_addr = parse_addr_spec(a, None, DEFAULT_FACILITATOR_PORT)
-    elif o == "-h" or o == "--help":
-        usage()
-        sys.exit()
-
-if len(args) == 0:
-    local_addr = (DEFAULT_LOCAL_ADDRESS, DEFAULT_LOCAL_PORT)
-    remote_addr = (DEFAULT_REMOTE_ADDRESS, DEFAULT_REMOTE_PORT)
-elif len(args) == 1:
-    local_addr = parse_addr_spec(args[0], DEFAULT_LOCAL_ADDRESS, DEFAULT_LOCAL_PORT)
-    remote_addr = (DEFAULT_REMOTE_ADDRESS, DEFAULT_REMOTE_PORT)
-elif len(args) == 2:
-    local_addr = parse_addr_spec(args[0], DEFAULT_LOCAL_ADDRESS, DEFAULT_LOCAL_PORT)
-    remote_addr = parse_addr_spec(args[1], DEFAULT_REMOTE_ADDRESS, DEFAULT_REMOTE_PORT)
-else:
-    usage(sys.stderr)
-    sys.exit(1)
-
-
-class RemotePending(object):
-    """A class encapsulating a socket and a time of connection."""
-    def __init__(self, fd):
-        self.fd = fd
-        self.birthday = time.time()
-
-    def fileno(self):
-        return self.fd.fileno()
-
-    def is_expired(self, timeout):
-        return time.time() - self.birthday > timeout
-
-def listen_socket(addr):
-    """Return a nonblocking socket listening on the given address."""
-    addrinfo = socket.getaddrinfo(addr[0], addr[1], 0, socket.SOCK_STREAM, socket.IPPROTO_TCP)[0]
-    s = socket.socket(addrinfo[0], addrinfo[1], addrinfo[2])
-    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-    s.bind(addr)
-    s.listen(10)
-    s.setblocking(0)
-    return s
-
-# How long to wait for a crossdomain policy request before deciding that this is
-# a normal socket.
-CROSSDOMAIN_TIMEOUT = 2.0
-
-# Local socket, accepting SOCKS requests from localhost
-local_s = listen_socket(local_addr)
-# Remote socket, accepting both crossdomain policy requests and remote proxy
-# connections.
-remote_s = listen_socket(remote_addr)
-
-# Sockets that may be crossdomain policy requests or may be normal remote
-# connections.
-crossdomain_pending = []
-# Remote connection sockets.
-remotes = []
-# New local sockets waiting to finish their SOCKS negotiation.
-socks_pending = []
-# Local Tor sockets, after SOCKS negotiation.
-locals = []
-
-# Bidirectional mapping between local sockets and remote sockets.
-local_for = {}
-remote_for = {}
-
-
-def handle_policy_request(fd):
-    print "handle_policy_request"
-    addr = fd.getpeername()
-    data = fd.recv(100)
-    if data == "<policy-file-request/>\0":
-        print "Sending crossdomain policy to %s." % format_addr(addr)
-        fd.sendall("""
-<cross-domain-policy>
-<allow-access-from domain="*" to-ports="%s"/>
-</cross-domain-policy>
-\0""" % xml.sax.saxutils.escape(str(remote_addr[1])))
-    elif data == "":
-        print "No data from %s." % format_addr(addr)
-    else:
-        print "Unexpected data from %s." % format_addr(addr)
-
-def grab_string(s, pos):
-    """Grab a NUL-terminated string from the given string, starting at the given
-    offset. Return (pos, str) tuple, or (pos, None) on error."""
-    i = pos
-    while i < len(s):
-        if s[i] == '\0':
-            return (i + 1, s[pos:i])
-        i += 1
-    return pos, None
-
-def parse_socks_request(data):
-    try:
-        ver, cmd, dport, o1, o2, o3, o4 = struct.unpack(">BBHBBBB", data[:8])
-    except struct.error:
-        print "Couldn't unpack SOCKS4 header."
-        return None
-    if ver != 4:
-        print "SOCKS header has wrong version (%d)." % ver
-        return None
-    if cmd != 1:
-        print "SOCKS header had wrong command (%d)." % cmd
-        return None
-    pos, userid = grab_string(data, 8)
-    if userid is None:
-        print "Couldn't read userid from SOCKS header."
-        return None
-    if o1 == 0 and o2 == 0 and o3 == 0 and o4 != 0:
-        pos, dest = grab_string(data, pos)
-        if dest is None:
-            print "Couldn't read destination from SOCKS4a header."
-            return None
-    else:
-        dest = "%d.%d.%d.%d" % (o1, o2, o3, o4)
-    return dest, dport
-
-def handle_socks_request(fd):
-    print "handle_socks_request"
-    addr = fd.getpeername()
-    data = fd.recv(100)
-    dest_addr = parse_socks_request(data)
-    if dest_addr is None:
-        # Error reply.
-        fd.sendall(struct.pack(">BBHBBBB", 0, 91, 0, 0, 0, 0, 0))
-        return False
-    print "Got SOCKS request for %s." % format_addr(dest_addr)
-    fd.sendall(struct.pack(">BBHBBBB", 0, 90, dest_addr[1], 127, 0, 0, 1))
-    # Note we throw away the requested address and port.
-    return True
-
-def handle_remote_connection(fd):
-    print "handle_remote_connection"
-    match_proxies()
-
-def handle_local_connection(fd):
-    print "handle_local_connection"
-    if facilitator_addr:
-        register(facilitator_addr, remote_addr[1])
-    match_proxies()
-
-def register(addr, port):
-    spec = format_addr((None, port))
-    print "Registering \"%s\" with %s." % (spec, format_addr(addr))
-    http = httplib.HTTPConnection(*addr)
-    http.request("POST", "/", urllib.urlencode({"client": spec}))
-    http.close()
-
-def match_proxies():
-    while locals and remotes:
-        remote = remotes.pop(0)
-        local = locals.pop(0)
-        remote_addr, remote_port = remote.getpeername()
-        local_addr, local_port = local.getpeername()
-        print "Linking %s and %s." % (format_addr(local.getpeername()), format_addr(remote.getpeername()))
-        remote_for[local] = remote
-        local_for[remote] = local
-
-if facilitator_addr:
-    register(facilitator_addr, remote_addr[1])
-
-while True:
-    rset = [remote_s, local_s] + crossdomain_pending + socks_pending + remote_for.keys() + local_for.keys() + remotes
-    rset, _, _ = select.select(rset, [], [], CROSSDOMAIN_TIMEOUT)
-    for fd in rset:
-        if fd == remote_s:
-            remote_c, addr = fd.accept()
-            print "Remote connection from %s." % format_addr(addr)
-            crossdomain_pending.append(RemotePending(remote_c))
-        elif fd == local_s:
-            local_c, addr = fd.accept()
-            print "Local connection from %s." % format_addr(addr)
-            socks_pending.append(local_c)
-            if facilitator_addr:
-                register(facilitator_addr, remote_addr[1])
-        elif fd in crossdomain_pending:
-            print "Data from crossdomain-pending %s." % format_addr(addr)
-            handle_policy_request(fd.fd)
-            fd.fd.close()
-            crossdomain_pending.remove(fd)
-        elif fd in socks_pending:
-            print "SOCKS request from %s." % format_addr(addr)
-            if handle_socks_request(fd):
-                locals.append(fd)
-                handle_local_connection(fd)
-            else:
-                fd.close()
-            socks_pending.remove(fd)
-        elif fd in local_for:
-            local = local_for[fd]
-            data = fd.recv(1024)
-            if not data:
-                print "EOF from remote %s." % format_addr(fd.getpeername())
-                fd.close()
-                local.close()
-                del local_for[fd]
-                del remote_for[local]
-                if facilitator_addr:
-                    register(facilitator_addr, remote_addr[1])
-            else:
-                local.sendall(data)
-        elif fd in remote_for:
-            remote = remote_for[fd]
-            data = fd.recv(1024)
-            if not data:
-                print "EOF from local %s." % format_addr(fd.getpeername())
-                fd.close()
-                remote.close()
-                del remote_for[fd]
-                del local_for[remote]
-            else:
-                remote.sendall(data)
-        elif fd in remotes:
-            data = fd.recv(1024)
-            if not data:
-                print "EOF from unconnected remote %s." % format_addr(fd.getpeername())
-            else:
-                print "Data from unconnected remote %s." % format_addr(fd.getpeername())
-            fd.close()
-            remotes.remove(fd)
-        match_proxies()
-    while crossdomain_pending:
-        pending = crossdomain_pending[0]
-        if not pending.is_expired(CROSSDOMAIN_TIMEOUT):
-            break
-        print "Expired pending crossdomain from %s." % format_addr(pending.fd.getpeername())
-        crossdomain_pending.pop(0)
-        remotes.append(pending.fd)
-        handle_remote_connection(pending.fd)
diff --git a/com/rtmfpcat/facilitator.py b/com/rtmfpcat/facilitator.py
deleted file mode 100755
index 8c6a44e..0000000
--- a/com/rtmfpcat/facilitator.py
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/usr/bin/env python
-
-import BaseHTTPServer
-import getopt
-import cgi
-import re
-import sys
-import socket
-from collections import deque
-
-DEFAULT_ADDRESS = "0.0.0.0"
-DEFAULT_PORT = 9002
-
-def usage(f = sys.stdout):
-	print >> f, """\
-Usage: %(progname)s <OPTIONS> [HOST] [PORT]
-Flash bridge facilitator: Register client addresses with HTTP POST requests
-and serve them out again with HTTP GET. Listen on HOST and PORT, by default
-%(addr)s %(port)d.
-  -h, --help		   show this help.\
-""" % {
-	"progname": sys.argv[0],
-	"addr": DEFAULT_ADDRESS,
-	"port": DEFAULT_PORT,
-}
-
-REGS = deque()
-
-class Reg(object):
-	def __init__(self, id):
-		self.id = id
-
-	def __unicode__(self):
-	  return u"%s" % (self.id)
-
-	def __str__(self):
-		return unicode(self).encode("UTF-8")
-
-	def __cmp__(self, other):
-		return cmp((self.id), (other.id))
-
-	@staticmethod
-	def parse(spec, defhost = None, defport = None):
-		host = None
-		port = None
-		m = re.match(r'^\[(.+)\]:(\d*)$', spec)
-		if m:
-			host, port = m.groups()
-			af = socket.AF_INET6
-		else:
-			m = re.match(r'^(.*):(\d*)$', spec)
-			if m:
-				host, port = m.groups()
-				if host:
-					af = socket.AF_INET
-				else:
-					# Has to be guessed from format of defhost.
-					af = 0
-		host = host or defhost
-		port = port or defport
-		if not (host and port):
-			raise ValueError("Bad address specification \"%s\"" % spec)
-
-		try:
-			addrs = socket.getaddrinfo(host, port, af, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST)
-		except socket.gaierror, e:
-			raise ValueError("Bad host or port: \"%s\" \"%s\": %s" % (host, port, str(e)))
-		if not addrs:
-			raise ValueError("Bad host or port: \"%s\" \"%s\"" % (host, port))
-
-		af = addrs[0][0]
-		host, port = socket.getnameinfo(addrs[0][4], socket.NI_NUMERICHOST | socket.NI_NUMERICSERV)
-		return Reg(af, host, int(port))
-
-def fetch_reg():
-	"""Get a client registration, or None if none is available."""
-	if not REGS:
-		return None
-	return REGS.popleft()
-
-class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
-	def do_GET(self):
-		print "From " + str(self.client_address) + " received: GET:",
-		reg = fetch_reg()
-		if reg:
-			print "Handing out " + str(reg) + ". Clients: " + str(len(REGS))
-			self.request.send(str(reg))
-		else:
-			print "Registration list is empty"
-			self.request.send("Registration list empty")
-
-	def do_POST(self):
-		print "From " + str(self.client_address) + " received: POST:",
-		data = self.rfile.readline().strip()
-		print data + " :",
-		try:
-			vals = cgi.parse_qs(data, False, True)
-		except ValueError, e:
-			print "Syntax error in POST:", str(e)
-			return
-
-		client_specs = vals.get("client")
-		if client_specs is None or len(client_specs) != 1:
-			print "In POST: need exactly one \"client\" param"
-			return
-		val = client_specs[0]
-
-		try:
-			reg = Reg(val)
-		except ValueError, e:
-			print "Can't parse client \"%s\": %s" % (val, str(e))
-			return
-
-		if reg not in list(REGS):
-			REGS.append(reg)
-			print "Registration " + str(reg) + " added. Registrations: " + str(len(REGS))
-		else:
-			print "Registration " + str(reg) + " already present. Registrations: " + str(len(REGS))
-
-opts, args = getopt.gnu_getopt(sys.argv[1:], "h", ["help"])
-for o, a in opts:
-	if o == "-h" or o == "--help":
-		usage()
-		sys.exit()
-
-if len(args) == 0:
-	address = (DEFAULT_ADDRESS, DEFAULT_PORT)
-elif len(args) == 1:
-	# Either HOST or PORT may be omitted; figure out which one.
-	if args[0].isdigit():
-		address = (DEFAULT_ADDRESS, args[0])
-	else:
-		address = (args[0], DEFAULT_PORT)
-elif len(args) == 2:
-	address = (args[0], args[1])
-else:
-	usage(sys.stderr)
-	sys.exit(1)
-
-# Setup the server
-server = BaseHTTPServer.HTTPServer(address, Handler)
-
-print "Starting Facilitator on " + str(address) + "..."
-
-# Run server... Single threaded serving of requests...
-server.serve_forever()
diff --git a/com/rtmfpcat/rtmfp/RTMFPSocket.as b/com/rtmfpcat/rtmfp/RTMFPSocket.as
deleted file mode 100644
index bb00994..0000000
--- a/com/rtmfpcat/rtmfp/RTMFPSocket.as
+++ /dev/null
@@ -1,231 +0,0 @@
-/* RTMFPSocket abstraction
- * Author: Nate Hardison, May 2011
- * 
- * This code is heavily based off of BelugaFile, an open-source
- * Air file-transfer application written by Nicholas Bliyk.
- * Website: http://www.belugafile.com/
- * Source: http://code.google.com/p/belugafile/ 
- *
- */
-
-package rtmfp
-{
-    import flash.events.Event;
-    import flash.events.EventDispatcher;
-    import flash.events.IOErrorEvent;
-    import flash.events.NetStatusEvent;
-    import flash.events.ProgressEvent;
-    import flash.events.SecurityErrorEvent;
-    import flash.net.NetConnection;
-    import flash.net.NetStream;
-    import flash.utils.ByteArray;
-    import flash.utils.clearInterval;
-    import flash.utils.setInterval;
-    import flash.utils.setTimeout;
-    
-    import rtmfp.RTMFPSocketClient;
-    import rtmfp.events.RTMFPSocketEvent;
-
-    [Event(name="connectSuccess", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
-  	[Event(name="connectFail", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
-  	[Event(name="publishStart", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
-  	[Event(name="peerConnected", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
-  	[Event(name="peeringSuccess", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
-  	[Event(name="peeringFail", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
-  	[Event(name="peerDisconnected", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
-    public class RTMFPSocket extends EventDispatcher
-    {
-        /* The name of the "media" to pass between peers */
-        private static const DATA:String = "data";
-		    private static const DEFAULT_CIRRUS_ADDRESS:String = "rtmfp://p2p.rtmfp.net";
-		    private static const DEFAULT_CIRRUS_KEY:String = RTMFP::CIRRUS_KEY;
-		    private static const DEFAULT_CONNECT_TIMEOUT:uint = 4000;
-		
-        /* Connection to the Cirrus rendezvous service */
-        private var connection:NetConnection;
-		
-		    /* ID of the peer to connect to */
-		    private var peerID:String;
-		
-		    /* Data streams to be established with peer */
-		    private var sendStream:NetStream;
-		    private var recvStream:NetStream;
-		    
-		    /* Timeouts */
-		    private var connectionTimeout:int;
-		    private var peerConnectTimeout:uint;
-
-        public function RTMFPSocket(){}
-        
-        public function connect(addr:String = DEFAULT_CIRRUS_ADDRESS, key:String = DEFAULT_CIRRUS_KEY):void
-        {
-          connection = new NetConnection();
-			    connection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatusEvent);
-			    connection.addEventListener(IOErrorEvent.IO_ERROR, onIOErrorEvent);
-			    connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityErrorEvent);
-          connection.connect(addr + "/" + key);
-			    connectionTimeout = setInterval(fail, DEFAULT_CONNECT_TIMEOUT);
-        }
-
-        public function close():void
-        {
-          connection.close();
-        }
-
-        public function get id():String
-        {
-          if (connection != null && connection.connected) {
-            return connection.nearID;
-          }
-          
-          return null;
-        }
-        
-        public function get connected():Boolean
-        {
-          return (connection != null && connection.connected);
-        }
-        
-        public function readBytes(bytes:ByteArray):void
-        {
-          recvStream.client.bytes.readBytes(bytes);
-        }
-
-        public function writeBytes(bytes:ByteArray):void
-        {
-          sendStream.send("dataAvailable", bytes);
-        }
-        
-        public function get peer():String
-        {
-          return this.peerID;
-        }
-
-        public function set peer(peerID:String):void
-        {
-          if (peerID == null || peerID.length == 0) {
-            throw new Error("Peer ID is null/empty.")
-    			} else if (peerID == connection.nearID) {
-    				throw new Error("Peer ID cannot be the same as our ID.");
-    			} else if (this.peerID == peerID) {
-    			  throw new Error("Already connected to peer " + peerID + ".");
-    			} else if (this.recvStream != null) {
-            throw new Error("Cannot connect to a second peer.");
-          }
-          
-          this.peerID = peerID;
-
-    			recvStream = new NetStream(connection, peerID);
-    			var client:RTMFPSocketClient = new RTMFPSocketClient();
-    			client.addEventListener(ProgressEvent.SOCKET_DATA, onDataAvailable, false, 0, true);
-    			client.addEventListener(RTMFPSocketClient.PEER_CONNECT_ACKNOWLEDGED, onPeerConnectAcknowledged, false, 0, true);
-    			recvStream.client = client;
-    			recvStream.addEventListener(NetStatusEvent.NET_STATUS, onRecvStreamEvent);
-    			recvStream.play(DATA);
-    			setTimeout(onPeerConnectTimeout, peerConnectTimeout, recvStream);
-        }
-        
-        private function startPublishStream():void
-        {
-          sendStream = new NetStream(connection, NetStream.DIRECT_CONNECTIONS);
-    			sendStream.addEventListener(NetStatusEvent.NET_STATUS, onSendStreamEvent);
-    			var o:Object = new Object();
-    			o.onPeerConnect = onPeerConnect;
-    			sendStream.client = o;
-    			sendStream.publish(DATA);
-        }
-        
-        private function fail():void
-        {
-          clearInterval(connectionTimeout);
-    			dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.CONNECT_FAIL));
-        }
-        
-        private function onDataAvailable(event:ProgressEvent):void
-        {
-          dispatchEvent(event);
-        }
-        
-        private function onIOErrorEvent(event:IOErrorEvent):void
-        {
-          fail();
-        }
-        
-        private function onNetStatusEvent(event:NetStatusEvent):void
-        {
-          switch (event.info.code) {
-      			case "NetConnection.Connect.Success" :
-      				clearInterval(connectionTimeout);
-      				startPublishStream();
-      				dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.CONNECT_SUCCESS));
-      				break;
-      			case "NetStream.Connect.Success" :
-      				break;
-      			case "NetStream.Publish.BadName" :
-      			  fail();
-      				break;
-      			case "NetStream.Connect.Closed" :
-      			  // we've disconnected from the peer
-      			  // can reset to accept another
-      			  // clear the publish stream and re-publish another
-      			  dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_DISCONNECTED, recvStream));
-      				break;
-      		} 
-        }
-        
-        private function onPeerConnect(peer:NetStream):Boolean
-        {
-          // establish a bidirectional stream with the peer
-          if (peerID == null) {
-            this.peer = peer.farID;
-          }
-          
-          // disallow additional peers connecting to us
-          if (peer.farID != peerID) return false;
-          
-          peer.send("setPeerConnectAcknowledged");
-          dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_CONNECTED, peer));
-          
-          return true;
-        }
-        
-        private function onPeerConnectAcknowledged(event:Event):void
-        {
-    			dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_SUCCESS, recvStream));
-        }
-        
-        private function onPeerConnectTimeout(peer:NetStream):void
-        {
-          if (!recvStream.client) return;
-    			if (!RTMFPSocketClient(recvStream.client).peerConnectAcknowledged) {
-    				dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_FAIL, recvStream));
-    			}
-        }
-        
-        private function onSecurityErrorEvent(event:SecurityErrorEvent):void
-        {
-          fail();
-        }
-        
-        private function onSendStreamEvent(event:NetStatusEvent):void
-        {
-    			switch (event.info.code) {
-    				case ("NetStream.Publish.Start") :
-    				  dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PUBLISH_START));
-    				  break;
-    				case ("NetStream.Play.Reset") :
-    				case ("NetStream.Play.Start") :
-    					break;
-    			}
-    		}
-    		private function onRecvStreamEvent(event:NetStatusEvent):void
-    		{
-    			switch (event.info.code) {
-    				case ("NetStream.Publish.Start") :
-    				case ("NetStream.Play.Reset") :
-    				case ("NetStream.Play.Start") :
-    					break;
-    			}
-    		}
-    }
-}
diff --git a/com/rtmfpcat/rtmfp/RTMFPSocketClient.as b/com/rtmfpcat/rtmfp/RTMFPSocketClient.as
deleted file mode 100644
index d9fcffa..0000000
--- a/com/rtmfpcat/rtmfp/RTMFPSocketClient.as
+++ /dev/null
@@ -1,57 +0,0 @@
-package rtmfp
-{
-	import flash.events.Event;
-	import flash.events.EventDispatcher;
-	import flash.events.ProgressEvent;
-	import flash.utils.ByteArray;
-
-	[Event(name="peerConnectAcknowledged", type="flash.events.Event")]
-	public dynamic class RTMFPSocketClient extends EventDispatcher {
-		public static const PEER_CONNECT_ACKNOWLEDGED:String = "peerConnectAcknowledged";
-		
-		private var _bytes:ByteArray;
-		private var _peerID:String;
-		private var _peerConnectAcknowledged:Boolean;
-		
-		public function RTMFPSocketClient()
-		{
-			super();
-			_bytes = new ByteArray();
-			_peerID = null;
-			_peerConnectAcknowledged = false;
-		}
-		
-		public function get bytes():ByteArray
-		{
-		  return _bytes;
-		}
-		
-		public function dataAvailable(bytes:ByteArray):void
-		{
-		  this._bytes.clear();
-		  bytes.readBytes(this._bytes);
-		  dispatchEvent(new ProgressEvent(ProgressEvent.SOCKET_DATA, false, false, this._bytes.bytesAvailable, this._bytes.length));
-		}
-		
-		public function get peerConnectAcknowledged():Boolean
-		{
-		  return _peerConnectAcknowledged;
-		}
-		
-		public function setPeerConnectAcknowledged():void
-		{
-			_peerConnectAcknowledged = true;
-			dispatchEvent(new Event(PEER_CONNECT_ACKNOWLEDGED));
-		}
-		
-		public function get peerID():String
-		{
-		  return _peerID;
-		}
-		
-		public function set peerID(id:String):void
-		{
-		  _peerID = id;
-		}
-	}
-}
\ No newline at end of file
diff --git a/com/rtmfpcat/rtmfp/events/RTMFPSocketEvent.as b/com/rtmfpcat/rtmfp/events/RTMFPSocketEvent.as
deleted file mode 100644
index c5b4af1..0000000
--- a/com/rtmfpcat/rtmfp/events/RTMFPSocketEvent.as
+++ /dev/null
@@ -1,25 +0,0 @@
-package rtmfp.events
-{
-	import flash.events.Event;
-	import flash.net.NetStream;
-
-	public class RTMFPSocketEvent extends Event
-	{
-		public static const CONNECT_SUCCESS:String   = "connectSuccess";
-		public static const CONNECT_FAIL:String      = "connectFail";
-		public static const PUBLISH_START:String     = "publishStart";
-		public static const PEER_CONNECTED:String    = "peerConnected";
-		public static const PEER_DISCONNECTED:String = "peerDisconnected";
-		public static const PEERING_SUCCESS:String   = "peeringSuccess";
-		public static const PEERING_FAIL:String      = "peeringFail";
-		
-		public var stream:NetStream;
-		
-		public function RTMFPSocketEvent(type:String, streamVal:NetStream = null, bubbles:Boolean = false, cancelable:Boolean = false)
-		{
-			super(type, bubbles, cancelable);
-			stream = streamVal;
-		}
-		
-	}
-}
\ No newline at end of file
diff --git a/com/rtmfpcat/rtmfpcat.as b/com/rtmfpcat/rtmfpcat.as
deleted file mode 100644
index 328274a..0000000
--- a/com/rtmfpcat/rtmfpcat.as
+++ /dev/null
@@ -1,208 +0,0 @@
-package
-{
-    import flash.display.Sprite;
-    import flash.text.TextField;
-    import flash.net.Socket;
-    import flash.events.Event;
-    import flash.events.EventDispatcher;
-    import flash.events.IOErrorEvent;
-    import flash.events.NetStatusEvent;
-    import flash.events.ProgressEvent;
-    import flash.events.SecurityErrorEvent;
-    import flash.utils.ByteArray;
-    import flash.utils.setTimeout;
-    
-    import rtmfp.RTMFPSocket;
-    import rtmfp.events.RTMFPSocketEvent;
-    import Utils;
-    
-    public class rtmfpcat extends Sprite {
-      
-      /* Nate's facilitator -- also serving a crossdomain policy */
-      private const DEFAULT_FAC_ADDR:Object = {
-        host: "128.12.179.80",
-        port: 9002
-      };
-      
-      private const DEFAULT_TOR_CLIENT_ADDR:Object = {
-        host: "127.0.0.1",
-        port: 3333
-      };
-      
-      /* David's relay (nickname 3VXRyxz67OeRoqHn) that also serves a
-         crossdomain policy. */
-      private const DEFAULT_TOR_PROXY_ADDR:Object = {
-        host: "173.255.221.44",
-        port: 9001
-      };
-      
-      // Milliseconds.
-      private const FACILITATOR_POLL_INTERVAL:int = 10000;
-      
-      private var output_text:TextField;
-      
-      private var s_f:Socket;
-      private var s_r:RTMFPSocket;
-      private var s_t:Socket;
-      
-      private var fac_addr:Object;
-      private var tor_addr:Object;
-      
-      private var proxy_mode:Boolean;
-      
-      public function rtmfpcat()
-      {
-        output_text = new TextField();
-        output_text.width = 400;
-        output_text.height = 300;
-        output_text.background = true;
-        output_text.backgroundColor = 0x001f0f;
-        output_text.textColor = 0x44CC44;
-        addChild(output_text);
-
-        puts("Starting.");
-        
-        this.loaderInfo.addEventListener(Event.COMPLETE, onLoaderInfoComplete);
-      }
-      
-      private function puts(s:String):void
-      {
-          output_text.appendText(s + "\n");
-          output_text.scrollV = output_text.maxScrollV;
-      }
-      
-      private function onLoaderInfoComplete(e:Event):void
-      {
-        var fac_spec:String;
-        var tor_spec:String;
-
-        puts("Parameters loaded.");
-        
-        proxy_mode = (this.loaderInfo.parameters["proxy"] != null);
-        
-        fac_spec = this.loaderInfo.parameters["facilitator"];
-        if (!fac_spec) {
-          puts("No \"facilitator\" specification provided...using default.");
-          fac_addr = DEFAULT_FAC_ADDR;
-        } else {
-          puts("Facilitator spec: \"" + fac_spec + "\"");
-          fac_addr = Utils.parseAddrSpec(fac_spec);
-        }
-        
-        if (!fac_addr) {
-            puts("Error: Facilitator spec must be in the form \"host:port\".");
-            return;
-        }
-        
-        tor_spec = this.loaderInfo.parameters["tor"];
-        if (!tor_spec) {
-          puts("No Tor specification provided...using default.");
-          if (proxy_mode) tor_addr = DEFAULT_TOR_PROXY_ADDR;
-          else tor_addr = DEFAULT_TOR_CLIENT_ADDR;
-        } else {
-          puts("Tor spec: \"" + tor_spec + "\"")
-          tor_addr = Utils.parseAddrSpec(tor_spec);
-        }
-
-        if (!tor_addr) {
-          puts("Error: Tor spec must be in the form \"host:port\".");
-          return;
-        }
-
-        establishRTMFPConnection();
-      }
-      
-      private function establishRTMFPConnection():void
-      {
-        s_r = new RTMFPSocket();
-        s_r.addEventListener(RTMFPSocketEvent.CONNECT_SUCCESS, function (e:Event):void {
-          puts("Cirrus: connected with id " + s_r.id + ".");
-          establishFacilitatorConnection();
-        });
-        s_r.addEventListener(RTMFPSocketEvent.CONNECT_FAIL, function (e:Event):void {
-          puts("Error: failed to connect to Cirrus.");
-        });
-        s_r.addEventListener(RTMFPSocketEvent.PUBLISH_START, function(e:RTMFPSocketEvent):void {
-          puts("Publishing started.");
-        });
-        s_r.addEventListener(RTMFPSocketEvent.PEER_CONNECTED, function(e:RTMFPSocketEvent):void {
-          puts("Peer connected.");
-        });
-        s_r.addEventListener(RTMFPSocketEvent.PEER_DISCONNECTED, function(e:RTMFPSocketEvent):void {
-          puts("Peer disconnected.");
-        });
-        s_r.addEventListener(RTMFPSocketEvent.PEERING_SUCCESS, function(e:RTMFPSocketEvent):void {
-          puts("Peering success.");
-          establishTorConnection();
-        });
-        s_r.addEventListener(RTMFPSocketEvent.PEERING_FAIL, function(e:RTMFPSocketEvent):void {
-          puts("Peering fail.");
-        });
-        s_r.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
-          var bytes:ByteArray = new ByteArray();
-          s_r.readBytes(bytes);
-          puts("RTMFP: read " + bytes.length + " bytes.");
-          s_t.writeBytes(bytes);
-        });
-        
-        s_r.connect();
-      }
-      
-      private function establishTorConnection():void
-      {
-        s_t = new Socket();
-        s_t.addEventListener(Event.CONNECT, function (e:Event):void {
-          puts("Tor: connected to " + tor_addr.host + ":" + tor_addr.port + ".");
-        });
-        s_t.addEventListener(Event.CLOSE, function (e:Event):void {
-          puts("Tor: closed connection.");
-        });
-        s_t.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
-          puts("Tor: I/O error: " + e.text + ".");
-        });
-        s_t.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
-          var bytes:ByteArray = new ByteArray();
-          s_t.readBytes(bytes, 0, e.bytesLoaded);
-          puts("Tor: read " + bytes.length + " bytes.");
-          s_r.writeBytes(bytes);
-        });
-        s_t.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
-          puts("Tor: security error: " + e.text + ".");
-        });
-
-        s_t.connect(tor_addr.host, tor_addr.port);
-      }
-      
-      private function establishFacilitatorConnection():void
-      {
-        s_f = new Socket();
-        s_f.addEventListener(Event.CONNECT, function (e:Event):void {
-          puts("Facilitator: connected to " + fac_addr.host + ":" + fac_addr.port + ".");
-          if (proxy_mode) s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
-          else s_f.writeUTFBytes("POST / HTTP/1.0\r\n\r\nclient=" + s_r.id + "\r\n");
-        });
-        s_f.addEventListener(Event.CLOSE, function (e:Event):void {
-          puts("Facilitator: connection closed.");
-          if (proxy_mode) {
-            setTimeout(establishFacilitatorConnection, FACILITATOR_POLL_INTERVAL);
-          }
-        });
-        s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
-          puts("Facilitator: I/O error: " + e.text + ".");
-        });
-        s_f.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
-          var clientID:String = s_f.readMultiByte(e.bytesLoaded, "utf-8");
-          puts("Facilitator: got \"" + clientID + "\"");
-          if (clientID != "Registration list empty") {
-            puts("Connecting to " + clientID + ".");
-            s_r.peer = clientID;
-          }
-        });
-        s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
-          puts("Facilitator: security error: " + e.text + ".");
-        });
-
-        s_f.connect(fac_addr.host, fac_addr.port);
-      }
-  }
-}
\ No newline at end of file
diff --git a/connector.py b/connector.py
index f836d5f..df8f929 100755
--- a/connector.py
+++ b/connector.py
@@ -12,8 +12,8 @@ import time
 import urllib
 import xml.sax.saxutils
 
-DEFAULT_REMOTE_ADDRESS = "0.0.0.0"
-DEFAULT_REMOTE_PORT = 9000
+DEFAULT_REMOTE_ADDRESS = "127.0.0.1"
+DEFAULT_REMOTE_PORT = 3333
 DEFAULT_LOCAL_ADDRESS = "127.0.0.1"
 DEFAULT_LOCAL_PORT = 9001
 DEFAULT_FACILITATOR_PORT = 9002
diff --git a/facilitator.py b/facilitator.py
index d909f5f..fde81b7 100755
--- a/facilitator.py
+++ b/facilitator.py
@@ -63,18 +63,17 @@ def format_addr(addr):
         return u"%s:%d" % (host, port)
 
 class Reg(object):
-    def __init__(self, host, port):
-        self.host = host
-        self.port = port
+    def __init__(self, id):
+        self.id = id
 
     def __unicode__(self):
-        return format_addr((self.host, self.port))
+        return u"%s" % (self.id)
 
     def __str__(self):
         return unicode(self).encode("UTF-8")
 
     def __cmp__(self, other):
-        return cmp((self.host, self.port), (other.host, other.port))
+        return cmp((self.id), (other.id))
 
     @staticmethod
     def parse(spec, defhost = None, defport = None):
@@ -167,7 +166,7 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
         val = client_specs[0]
 
         try:
-            reg = Reg.parse(val, self.client_address[0])
+            reg = Reg(val)
         except ValueError, e:
             log(u"client %s syntax error in %s: %s" % (format_addr(self.client_address), repr(val), repr(str(e))))
             return
diff --git a/rtmfp/RTMFPSocket.as b/rtmfp/RTMFPSocket.as
new file mode 100644
index 0000000..bb00994
--- /dev/null
+++ b/rtmfp/RTMFPSocket.as
@@ -0,0 +1,231 @@
+/* RTMFPSocket abstraction
+ * Author: Nate Hardison, May 2011
+ * 
+ * This code is heavily based off of BelugaFile, an open-source
+ * Air file-transfer application written by Nicholas Bliyk.
+ * Website: http://www.belugafile.com/
+ * Source: http://code.google.com/p/belugafile/ 
+ *
+ */
+
+package rtmfp
+{
+    import flash.events.Event;
+    import flash.events.EventDispatcher;
+    import flash.events.IOErrorEvent;
+    import flash.events.NetStatusEvent;
+    import flash.events.ProgressEvent;
+    import flash.events.SecurityErrorEvent;
+    import flash.net.NetConnection;
+    import flash.net.NetStream;
+    import flash.utils.ByteArray;
+    import flash.utils.clearInterval;
+    import flash.utils.setInterval;
+    import flash.utils.setTimeout;
+    
+    import rtmfp.RTMFPSocketClient;
+    import rtmfp.events.RTMFPSocketEvent;
+
+    [Event(name="connectSuccess", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="connectFail", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="publishStart", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="peerConnected", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="peeringSuccess", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="peeringFail", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="peerDisconnected", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+    public class RTMFPSocket extends EventDispatcher
+    {
+        /* The name of the "media" to pass between peers */
+        private static const DATA:String = "data";
+		    private static const DEFAULT_CIRRUS_ADDRESS:String = "rtmfp://p2p.rtmfp.net";
+		    private static const DEFAULT_CIRRUS_KEY:String = RTMFP::CIRRUS_KEY;
+		    private static const DEFAULT_CONNECT_TIMEOUT:uint = 4000;
+		
+        /* Connection to the Cirrus rendezvous service */
+        private var connection:NetConnection;
+		
+		    /* ID of the peer to connect to */
+		    private var peerID:String;
+		
+		    /* Data streams to be established with peer */
+		    private var sendStream:NetStream;
+		    private var recvStream:NetStream;
+		    
+		    /* Timeouts */
+		    private var connectionTimeout:int;
+		    private var peerConnectTimeout:uint;
+
+        public function RTMFPSocket(){}
+        
+        public function connect(addr:String = DEFAULT_CIRRUS_ADDRESS, key:String = DEFAULT_CIRRUS_KEY):void
+        {
+          connection = new NetConnection();
+			    connection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatusEvent);
+			    connection.addEventListener(IOErrorEvent.IO_ERROR, onIOErrorEvent);
+			    connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityErrorEvent);
+          connection.connect(addr + "/" + key);
+			    connectionTimeout = setInterval(fail, DEFAULT_CONNECT_TIMEOUT);
+        }
+
+        public function close():void
+        {
+          connection.close();
+        }
+
+        public function get id():String
+        {
+          if (connection != null && connection.connected) {
+            return connection.nearID;
+          }
+          
+          return null;
+        }
+        
+        public function get connected():Boolean
+        {
+          return (connection != null && connection.connected);
+        }
+        
+        public function readBytes(bytes:ByteArray):void
+        {
+          recvStream.client.bytes.readBytes(bytes);
+        }
+
+        public function writeBytes(bytes:ByteArray):void
+        {
+          sendStream.send("dataAvailable", bytes);
+        }
+        
+        public function get peer():String
+        {
+          return this.peerID;
+        }
+
+        public function set peer(peerID:String):void
+        {
+          if (peerID == null || peerID.length == 0) {
+            throw new Error("Peer ID is null/empty.")
+    			} else if (peerID == connection.nearID) {
+    				throw new Error("Peer ID cannot be the same as our ID.");
+    			} else if (this.peerID == peerID) {
+    			  throw new Error("Already connected to peer " + peerID + ".");
+    			} else if (this.recvStream != null) {
+            throw new Error("Cannot connect to a second peer.");
+          }
+          
+          this.peerID = peerID;
+
+    			recvStream = new NetStream(connection, peerID);
+    			var client:RTMFPSocketClient = new RTMFPSocketClient();
+    			client.addEventListener(ProgressEvent.SOCKET_DATA, onDataAvailable, false, 0, true);
+    			client.addEventListener(RTMFPSocketClient.PEER_CONNECT_ACKNOWLEDGED, onPeerConnectAcknowledged, false, 0, true);
+    			recvStream.client = client;
+    			recvStream.addEventListener(NetStatusEvent.NET_STATUS, onRecvStreamEvent);
+    			recvStream.play(DATA);
+    			setTimeout(onPeerConnectTimeout, peerConnectTimeout, recvStream);
+        }
+        
+        private function startPublishStream():void
+        {
+          sendStream = new NetStream(connection, NetStream.DIRECT_CONNECTIONS);
+    			sendStream.addEventListener(NetStatusEvent.NET_STATUS, onSendStreamEvent);
+    			var o:Object = new Object();
+    			o.onPeerConnect = onPeerConnect;
+    			sendStream.client = o;
+    			sendStream.publish(DATA);
+        }
+        
+        private function fail():void
+        {
+          clearInterval(connectionTimeout);
+    			dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.CONNECT_FAIL));
+        }
+        
+        private function onDataAvailable(event:ProgressEvent):void
+        {
+          dispatchEvent(event);
+        }
+        
+        private function onIOErrorEvent(event:IOErrorEvent):void
+        {
+          fail();
+        }
+        
+        private function onNetStatusEvent(event:NetStatusEvent):void
+        {
+          switch (event.info.code) {
+      			case "NetConnection.Connect.Success" :
+      				clearInterval(connectionTimeout);
+      				startPublishStream();
+      				dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.CONNECT_SUCCESS));
+      				break;
+      			case "NetStream.Connect.Success" :
+      				break;
+      			case "NetStream.Publish.BadName" :
+      			  fail();
+      				break;
+      			case "NetStream.Connect.Closed" :
+      			  // we've disconnected from the peer
+      			  // can reset to accept another
+      			  // clear the publish stream and re-publish another
+      			  dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_DISCONNECTED, recvStream));
+      				break;
+      		} 
+        }
+        
+        private function onPeerConnect(peer:NetStream):Boolean
+        {
+          // establish a bidirectional stream with the peer
+          if (peerID == null) {
+            this.peer = peer.farID;
+          }
+          
+          // disallow additional peers connecting to us
+          if (peer.farID != peerID) return false;
+          
+          peer.send("setPeerConnectAcknowledged");
+          dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_CONNECTED, peer));
+          
+          return true;
+        }
+        
+        private function onPeerConnectAcknowledged(event:Event):void
+        {
+    			dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_SUCCESS, recvStream));
+        }
+        
+        private function onPeerConnectTimeout(peer:NetStream):void
+        {
+          if (!recvStream.client) return;
+    			if (!RTMFPSocketClient(recvStream.client).peerConnectAcknowledged) {
+    				dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_FAIL, recvStream));
+    			}
+        }
+        
+        private function onSecurityErrorEvent(event:SecurityErrorEvent):void
+        {
+          fail();
+        }
+        
+        private function onSendStreamEvent(event:NetStatusEvent):void
+        {
+    			switch (event.info.code) {
+    				case ("NetStream.Publish.Start") :
+    				  dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PUBLISH_START));
+    				  break;
+    				case ("NetStream.Play.Reset") :
+    				case ("NetStream.Play.Start") :
+    					break;
+    			}
+    		}
+    		private function onRecvStreamEvent(event:NetStatusEvent):void
+    		{
+    			switch (event.info.code) {
+    				case ("NetStream.Publish.Start") :
+    				case ("NetStream.Play.Reset") :
+    				case ("NetStream.Play.Start") :
+    					break;
+    			}
+    		}
+    }
+}
diff --git a/rtmfp/RTMFPSocketClient.as b/rtmfp/RTMFPSocketClient.as
new file mode 100644
index 0000000..d9fcffa
--- /dev/null
+++ b/rtmfp/RTMFPSocketClient.as
@@ -0,0 +1,57 @@
+package rtmfp
+{
+	import flash.events.Event;
+	import flash.events.EventDispatcher;
+	import flash.events.ProgressEvent;
+	import flash.utils.ByteArray;
+
+	[Event(name="peerConnectAcknowledged", type="flash.events.Event")]
+	public dynamic class RTMFPSocketClient extends EventDispatcher {
+		public static const PEER_CONNECT_ACKNOWLEDGED:String = "peerConnectAcknowledged";
+		
+		private var _bytes:ByteArray;
+		private var _peerID:String;
+		private var _peerConnectAcknowledged:Boolean;
+		
+		public function RTMFPSocketClient()
+		{
+			super();
+			_bytes = new ByteArray();
+			_peerID = null;
+			_peerConnectAcknowledged = false;
+		}
+		
+		public function get bytes():ByteArray
+		{
+		  return _bytes;
+		}
+		
+		public function dataAvailable(bytes:ByteArray):void
+		{
+		  this._bytes.clear();
+		  bytes.readBytes(this._bytes);
+		  dispatchEvent(new ProgressEvent(ProgressEvent.SOCKET_DATA, false, false, this._bytes.bytesAvailable, this._bytes.length));
+		}
+		
+		public function get peerConnectAcknowledged():Boolean
+		{
+		  return _peerConnectAcknowledged;
+		}
+		
+		public function setPeerConnectAcknowledged():void
+		{
+			_peerConnectAcknowledged = true;
+			dispatchEvent(new Event(PEER_CONNECT_ACKNOWLEDGED));
+		}
+		
+		public function get peerID():String
+		{
+		  return _peerID;
+		}
+		
+		public function set peerID(id:String):void
+		{
+		  _peerID = id;
+		}
+	}
+}
\ No newline at end of file
diff --git a/rtmfp/events/RTMFPSocketEvent.as b/rtmfp/events/RTMFPSocketEvent.as
new file mode 100644
index 0000000..c5b4af1
--- /dev/null
+++ b/rtmfp/events/RTMFPSocketEvent.as
@@ -0,0 +1,25 @@
+package rtmfp.events
+{
+	import flash.events.Event;
+	import flash.net.NetStream;
+
+	public class RTMFPSocketEvent extends Event
+	{
+		public static const CONNECT_SUCCESS:String   = "connectSuccess";
+		public static const CONNECT_FAIL:String      = "connectFail";
+		public static const PUBLISH_START:String     = "publishStart";
+		public static const PEER_CONNECTED:String    = "peerConnected";
+		public static const PEER_DISCONNECTED:String = "peerDisconnected";
+		public static const PEERING_SUCCESS:String   = "peeringSuccess";
+		public static const PEERING_FAIL:String      = "peeringFail";
+		
+		public var stream:NetStream;
+		
+		public function RTMFPSocketEvent(type:String, streamVal:NetStream = null, bubbles:Boolean = false, cancelable:Boolean = false)
+		{
+			super(type, bubbles, cancelable);
+			stream = streamVal;
+		}
+		
+	}
+}
\ No newline at end of file
diff --git a/rtmfpcat.as b/rtmfpcat.as
new file mode 100644
index 0000000..328274a
--- /dev/null
+++ b/rtmfpcat.as
@@ -0,0 +1,208 @@
+package
+{
+    import flash.display.Sprite;
+    import flash.text.TextField;
+    import flash.net.Socket;
+    import flash.events.Event;
+    import flash.events.EventDispatcher;
+    import flash.events.IOErrorEvent;
+    import flash.events.NetStatusEvent;
+    import flash.events.ProgressEvent;
+    import flash.events.SecurityErrorEvent;
+    import flash.utils.ByteArray;
+    import flash.utils.setTimeout;
+    
+    import rtmfp.RTMFPSocket;
+    import rtmfp.events.RTMFPSocketEvent;
+    import Utils;
+    
+    public class rtmfpcat extends Sprite {
+      
+      /* Nate's facilitator -- also serving a crossdomain policy */
+      private const DEFAULT_FAC_ADDR:Object = {
+        host: "128.12.179.80",
+        port: 9002
+      };
+      
+      private const DEFAULT_TOR_CLIENT_ADDR:Object = {
+        host: "127.0.0.1",
+        port: 3333
+      };
+      
+      /* David's relay (nickname 3VXRyxz67OeRoqHn) that also serves a
+         crossdomain policy. */
+      private const DEFAULT_TOR_PROXY_ADDR:Object = {
+        host: "173.255.221.44",
+        port: 9001
+      };
+      
+      // Milliseconds.
+      private const FACILITATOR_POLL_INTERVAL:int = 10000;
+      
+      private var output_text:TextField;
+      
+      private var s_f:Socket;
+      private var s_r:RTMFPSocket;
+      private var s_t:Socket;
+      
+      private var fac_addr:Object;
+      private var tor_addr:Object;
+      
+      private var proxy_mode:Boolean;
+      
+      public function rtmfpcat()
+      {
+        output_text = new TextField();
+        output_text.width = 400;
+        output_text.height = 300;
+        output_text.background = true;
+        output_text.backgroundColor = 0x001f0f;
+        output_text.textColor = 0x44CC44;
+        addChild(output_text);
+
+        puts("Starting.");
+        
+        this.loaderInfo.addEventListener(Event.COMPLETE, onLoaderInfoComplete);
+      }
+      
+      private function puts(s:String):void
+      {
+          output_text.appendText(s + "\n");
+          output_text.scrollV = output_text.maxScrollV;
+      }
+      
+      private function onLoaderInfoComplete(e:Event):void
+      {
+        var fac_spec:String;
+        var tor_spec:String;
+
+        puts("Parameters loaded.");
+        
+        proxy_mode = (this.loaderInfo.parameters["proxy"] != null);
+        
+        fac_spec = this.loaderInfo.parameters["facilitator"];
+        if (!fac_spec) {
+          puts("No \"facilitator\" specification provided...using default.");
+          fac_addr = DEFAULT_FAC_ADDR;
+        } else {
+          puts("Facilitator spec: \"" + fac_spec + "\"");
+          fac_addr = Utils.parseAddrSpec(fac_spec);
+        }
+        
+        if (!fac_addr) {
+            puts("Error: Facilitator spec must be in the form \"host:port\".");
+            return;
+        }
+        
+        tor_spec = this.loaderInfo.parameters["tor"];
+        if (!tor_spec) {
+          puts("No Tor specification provided...using default.");
+          if (proxy_mode) tor_addr = DEFAULT_TOR_PROXY_ADDR;
+          else tor_addr = DEFAULT_TOR_CLIENT_ADDR;
+        } else {
+          puts("Tor spec: \"" + tor_spec + "\"")
+          tor_addr = Utils.parseAddrSpec(tor_spec);
+        }
+
+        if (!tor_addr) {
+          puts("Error: Tor spec must be in the form \"host:port\".");
+          return;
+        }
+
+        establishRTMFPConnection();
+      }
+      
+      private function establishRTMFPConnection():void
+      {
+        s_r = new RTMFPSocket();
+        s_r.addEventListener(RTMFPSocketEvent.CONNECT_SUCCESS, function (e:Event):void {
+          puts("Cirrus: connected with id " + s_r.id + ".");
+          establishFacilitatorConnection();
+        });
+        s_r.addEventListener(RTMFPSocketEvent.CONNECT_FAIL, function (e:Event):void {
+          puts("Error: failed to connect to Cirrus.");
+        });
+        s_r.addEventListener(RTMFPSocketEvent.PUBLISH_START, function(e:RTMFPSocketEvent):void {
+          puts("Publishing started.");
+        });
+        s_r.addEventListener(RTMFPSocketEvent.PEER_CONNECTED, function(e:RTMFPSocketEvent):void {
+          puts("Peer connected.");
+        });
+        s_r.addEventListener(RTMFPSocketEvent.PEER_DISCONNECTED, function(e:RTMFPSocketEvent):void {
+          puts("Peer disconnected.");
+        });
+        s_r.addEventListener(RTMFPSocketEvent.PEERING_SUCCESS, function(e:RTMFPSocketEvent):void {
+          puts("Peering success.");
+          establishTorConnection();
+        });
+        s_r.addEventListener(RTMFPSocketEvent.PEERING_FAIL, function(e:RTMFPSocketEvent):void {
+          puts("Peering fail.");
+        });
+        s_r.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+          var bytes:ByteArray = new ByteArray();
+          s_r.readBytes(bytes);
+          puts("RTMFP: read " + bytes.length + " bytes.");
+          s_t.writeBytes(bytes);
+        });
+        
+        s_r.connect();
+      }
+      
+      private function establishTorConnection():void
+      {
+        s_t = new Socket();
+        s_t.addEventListener(Event.CONNECT, function (e:Event):void {
+          puts("Tor: connected to " + tor_addr.host + ":" + tor_addr.port + ".");
+        });
+        s_t.addEventListener(Event.CLOSE, function (e:Event):void {
+          puts("Tor: closed connection.");
+        });
+        s_t.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+          puts("Tor: I/O error: " + e.text + ".");
+        });
+        s_t.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+          var bytes:ByteArray = new ByteArray();
+          s_t.readBytes(bytes, 0, e.bytesLoaded);
+          puts("Tor: read " + bytes.length + " bytes.");
+          s_r.writeBytes(bytes);
+        });
+        s_t.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
+          puts("Tor: security error: " + e.text + ".");
+        });
+
+        s_t.connect(tor_addr.host, tor_addr.port);
+      }
+      
+      private function establishFacilitatorConnection():void
+      {
+        s_f = new Socket();
+        s_f.addEventListener(Event.CONNECT, function (e:Event):void {
+          puts("Facilitator: connected to " + fac_addr.host + ":" + fac_addr.port + ".");
+          if (proxy_mode) s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
+          else s_f.writeUTFBytes("POST / HTTP/1.0\r\n\r\nclient=" + s_r.id + "\r\n");
+        });
+        s_f.addEventListener(Event.CLOSE, function (e:Event):void {
+          puts("Facilitator: connection closed.");
+          if (proxy_mode) {
+            setTimeout(establishFacilitatorConnection, FACILITATOR_POLL_INTERVAL);
+          }
+        });
+        s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+          puts("Facilitator: I/O error: " + e.text + ".");
+        });
+        s_f.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+          var clientID:String = s_f.readMultiByte(e.bytesLoaded, "utf-8");
+          puts("Facilitator: got \"" + clientID + "\"");
+          if (clientID != "Registration list empty") {
+            puts("Connecting to " + clientID + ".");
+            s_r.peer = clientID;
+          }
+        });
+        s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
+          puts("Facilitator: security error: " + e.text + ".");
+        });
+
+        s_f.connect(fac_addr.host, fac_addr.port);
+      }
+  }
+}
\ No newline at end of file
diff --git a/swfcat.as b/swfcat.as
index fd3625d..76de948 100644
--- a/swfcat.as
+++ b/swfcat.as
@@ -249,7 +249,6 @@ import flash.events.ProgressEvent;
 import flash.events.SecurityErrorEvent;
 import flash.net.Socket;
 import flash.utils.ByteArray;
-import flash.utils.setInterval;
 
 /* An instance of a client-relay connection. */
 class ProxyPair extends EventDispatcher
@@ -266,19 +265,6 @@ class ProxyPair extends EventDispatcher
 
     // Parent swfcat, for UI updates.
     private var ui:swfcat;
-    
-    // Proxy transfer rate limit in KB/s
-    private var rate_limit:uint;
-    
-    // Default limit is 10KB/s
-    private const DEFAULT_RATE_LIMIT:uint = 10000;
-    
-    private var curr_rate:Number;
-    private var historical_rate:Number;
-    private var lifetime:uint;
-    
-    // in milliseconds
-    private const DEFAULT_RATE_TIMEOUT:uint = 1000;
 
     public function log(msg:String):void
     {
@@ -292,16 +278,11 @@ class ProxyPair extends EventDispatcher
             "," + this.addr_r.host + ":" + this.addr_r.port + ">";
     }
 
-    public function ProxyPair(ui:swfcat, addr_c:Object, addr_r:Object, rate_limit:uint = DEFAULT_RATE_LIMIT)
+    public function ProxyPair(ui:swfcat, addr_c:Object, addr_r:Object)
     {
         this.ui = ui;
         this.addr_c = addr_c;
         this.addr_r = addr_r;
-        this.rate_limit = rate_limit;
-        this.curr_rate = 0.0;
-        this.historical_rate = 0.0;
-        this.lifetime = 0;
-        setInterval(update_transfer_rate, DEFAULT_RATE_TIMEOUT)
     }
 
     public function connect():void
@@ -365,53 +346,18 @@ class ProxyPair extends EventDispatcher
     private function client_connected(e:Event):void
     {
         log("Client: connected.");
-        add_data_listeners();
-    }
-    
-    private function add_data_listeners():void
-    {
-      s_r.addEventListener(ProgressEvent.SOCKET_DATA, transfer_to_client);
-      s_c.addEventListener(ProgressEvent.SOCKET_DATA, transfer_to_relay);
-    }
-    
-    private function remove_data_listeners():void
-    {
-      s_r.removeEventListener(ProgressEvent.SOCKET_DATA, transfer_to_client);
-      s_c.removeEventListener(ProgressEvent.SOCKET_DATA, transfer_to_relay);
-    }
-    
-    private function transfer_to_client(e:ProgressEvent):void
-    {
-      log("Tor: read " + e.bytesLoaded + ".");
-      transfer_bytes(s_r, s_c, e.bytesLoaded);
-    }
-    
-    private function transfer_to_relay(e:ProgressEvent):void
-    {
-      log("Client: read " + e.bytesLoaded + ".");
-      transfer_bytes(s_c, s_r, e.bytesLoaded);
-    }
-    
-    private function transfer_bytes(src:Socket, dst:Socket, num_bytes:uint):void
-    {
-      var bytes:ByteArray = new ByteArray();
-      src.readBytes(bytes, 0, num_bytes);
-      dst.writeBytes(bytes);
-      curr_rate += num_bytes;
-      if (rate_limit_exceeded()) remove_data_listeners();
-    }
-    
-    private function rate_limit_exceeded():Boolean
-    {
-      return curr_rate > rate_limit;
-    }
-    
-    private function update_transfer_rate():void
-    {
-      lifetime++;
-      historical_rate = (0.7 * historical_rate + 0.3 * curr_rate) / lifetime;
-      curr_rate = historical_rate;
-      log("Historical rate: " + historical_rate);
-      log("Current rate: " + curr_rate);
+
+        s_r.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+            var bytes:ByteArray = new ByteArray();
+            s_r.readBytes(bytes, 0, e.bytesLoaded);
+            log("Tor: read " + bytes.length + ".");
+            s_c.writeBytes(bytes);
+        });
+        s_c.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+            var bytes:ByteArray = new ByteArray();
+            s_c.readBytes(bytes, 0, e.bytesLoaded);
+            log("Client: read " + bytes.length + ".");
+            s_r.writeBytes(bytes);
+        });
     }
 }





More information about the tor-commits mailing list