[tor-commits] [flashproxy/master] Merge branch 'fac-build'

infinity0 at torproject.org infinity0 at torproject.org
Thu Nov 21 13:18:46 UTC 2013


commit dfd80a48930b328a8410172bc74fcfdf8593428f
Merge: 751158f a54308b
Author: Ximin Luo <infinity0 at gmx.com>
Date:   Wed Nov 13 16:23:40 2013 +0000

    Merge branch 'fac-build'
    
    Conflicts:
    	Makefile
    	facilitator/Makefile
    	facilitator/facilitator-test.py
    	facilitator/init.d/facilitator.in

 Makefile                                       |   12 +-
 facilitator/.gitignore                         |   28 ++
 facilitator/INSTALL                            |   27 ++
 facilitator/Makefile                           |   21 --
 facilitator/Makefile.am                        |  140 ++++++++
 facilitator/README                             |   36 +-
 facilitator/appengine/README                   |    8 +-
 facilitator/appengine/app.yaml                 |    3 +-
 facilitator/appengine/config.go                |    5 +
 facilitator/appengine/fp-reg.go                |    7 +-
 facilitator/autogen.sh                         |    2 +
 facilitator/configure.ac                       |   49 +++
 facilitator/default/facilitator                |   11 +
 facilitator/default/facilitator-email-poller   |    7 +
 facilitator/default/facilitator-reg-daemon     |   11 +
 facilitator/doc/appengine-howto.txt            |   56 ---
 facilitator/doc/appspot-howto.txt              |   58 ++++
 facilitator/doc/email-howto.txt                |   75 ++++
 facilitator/doc/facilitator-design.txt         |   44 +++
 facilitator/doc/facilitator-howto.txt          |  199 -----------
 facilitator/doc/gmail-howto.txt                |   61 ----
 facilitator/doc/http-howto.txt                 |   49 +++
 facilitator/doc/server-howto.txt               |   55 +++
 facilitator/examples/fp-facilitator.conf.in    |   28 ++
 facilitator/examples/reg-email.pass            |   10 +
 facilitator/fac.py                             |   11 +-
 facilitator/facilitator-email-poller           |   49 +--
 facilitator/facilitator-test                   |  437 -----------------------
 facilitator/facilitator-test.py                |  444 ++++++++++++++++++++++++
 facilitator/init.d/facilitator                 |  120 -------
 facilitator/init.d/facilitator-email-poller    |  119 -------
 facilitator/init.d/facilitator-email-poller.in |  133 +++++++
 facilitator/init.d/facilitator-reg-daemon      |  119 -------
 facilitator/init.d/facilitator-reg-daemon.in   |  132 +++++++
 facilitator/init.d/facilitator.in              |  133 +++++++
 35 files changed, 1528 insertions(+), 1171 deletions(-)

diff --cc Makefile
index 207115c,8771405..9e8d2dd
--- a/Makefile
+++ b/Makefile
@@@ -1,90 -1,74 +1,96 @@@
 -VERSION = 1.3
 +# Makefile for a self-contained binary distribution of flashproxy-client.
 +#
 +# This builds two zipball targets, dist and dist-exe, for POSIX and Windows
 +# respectively. Both can be extracted and run in-place by the end user.
 +# (PGP-signed forms also exist, sign and sign-exe.)
 +#
 +# If you are a distro packager, instead see the separate build scripts for each
 +# source component, all of which have an `install` target:
 +# - client: Makefile.client
 +# - common: setup-common.py
 +# - facilitator: facilitator/{configure.ac,Makefile.am}
 +#
 +# Not for the faint-hearted: it is possible to build dist-exe on GNU/Linux by
 +# using wine to install the windows versions of Python, py2exe, and m2crypto,
 +# then running `make PYTHON_W32="wine python" dist-exe`.
 +
 +PACKAGE = flashproxy-client
 +VERSION = $(shell sh version.sh)
 +DISTNAME = $(PACKAGE)-$(VERSION)
 +
 +THISFILE = $(lastword $(MAKEFILE_LIST))
 +PYTHON = python
 +PYTHON_W32 = $(PYTHON)
  
 -DESTDIR =
 -PREFIX = /usr/local
 -BINDIR = $(PREFIX)/bin
 -MANDIR = $(PREFIX)/share/man
 +MAKE_CLIENT = $(MAKE) -f Makefile.client PYTHON="$(PYTHON)"
 +# don't rebuild man pages due to VCS giving spurious timestamps, see #9940
 +REBUILD_MAN = 0
  
 -PYTHON = python
 -export PY2EXE_TMPDIR = py2exe-tmp
 +# all is N/A for a binary package, but include for completeness
 +all: dist
  
 -CLIENT_BIN = flashproxy-client flashproxy-reg-appspot flashproxy-reg-email flashproxy-reg-http flashproxy-reg-url
 -CLIENT_MAN = doc/flashproxy-client.1 doc/flashproxy-reg-appspot.1 doc/flashproxy-reg-email.1 doc/flashproxy-reg-http.1 doc/flashproxy-reg-url.1
 -CLIENT_DIST_FILES = $(CLIENT_BIN) Makefile README LICENSE ChangeLog torrc
 -CLIENT_DIST_DOC_FILES = $(CLIENT_MAN)
 +DISTDIR = dist/$(DISTNAME)
 +$(DISTDIR): Makefile.client setup-common.py $(THISFILE)
 +	mkdir -p $(DISTDIR)
 +	$(MAKE_CLIENT) DESTDIR=$(DISTDIR) bindir=/ docdir=/ man1dir=/doc/ \
 +	  REBUILD_MAN="$(REBUILD_MAN)" install
 +	$(PYTHON) setup-common.py build_py -d $(DISTDIR)
  
 -all: $(CLIENT_DIST_FILES) $(CLIENT_MAN)
 -	:
 +dist/%.zip: dist/%
 +	cd dist && zip -q -r -9 "$(@:dist/%=%)" "$(<:dist/%=%)"
  
 -%.1: %.1.txt
 -	rm -f $@
 -	a2x --no-xmllint --xsltproc-opts "--stringparam man.th.title.max.length 24" -d manpage -f manpage $<
 +dist/%.zip.asc: dist/%.zip
 +	rm -f "$@"
 +	gpg --sign --detach-sign --armor "$<"
 +	gpg --verify "$@" "$<"
  
 -install:
 -	mkdir -p $(DESTDIR)$(BINDIR)
 -	mkdir -p $(DESTDIR)$(MANDIR)/man1
 -	cp -f $(CLIENT_BIN) $(DESTDIR)$(BINDIR)
 -	cp -f $(CLIENT_MAN) $(DESTDIR)$(MANDIR)/man1
 +dist: force-dist $(DISTDIR).zip
  
 -DISTNAME = flashproxy-client-$(VERSION)
 -DISTDIR = dist/$(DISTNAME)
 -dist: $(CLIENT_MAN)
 -	rm -rf dist
 -	mkdir -p $(DISTDIR)
 -	mkdir $(DISTDIR)/doc
 -	cp -f $(CLIENT_DIST_FILES) $(DISTDIR)
 -	cp -f $(CLIENT_DIST_DOC_FILES) $(DISTDIR)/doc
 -	cd dist && zip -q -r -9 $(DISTNAME).zip $(DISTNAME)
 -
 -dist/$(DISTNAME).zip: $(CLIENT_DIST_FILES)
 -	$(MAKE) dist
 -
 -sign: dist/$(DISTNAME).zip
 -	rm -f dist/$(DISTNAME).zip.asc
 -	cd dist && gpg --sign --detach-sign --armor $(DISTNAME).zip
 -	cd dist && gpg --verify $(DISTNAME).zip.asc $(DISTNAME).zip
 -
 -$(PY2EXE_TMPDIR)/dist: $(CLIENT_BIN)
 -	rm -rf $(PY2EXE_TMPDIR)
 -	$(PYTHON) setup.py py2exe -q
 -
 -dist-exe: DISTNAME := $(DISTNAME)-win32
 -dist-exe: CLIENT_BIN := $(PY2EXE_TMPDIR)/dist/*
 -dist-exe: CLIENT_MAN := $(addsuffix .txt,$(CLIENT_MAN))
 -# Delegate to the "dist" target using the substitutions above.
 -dist-exe: $(PY2EXE_TMPDIR)/dist setup.py dist
 -
 -clean:
 -	rm -f *.pyc
 +sign: force-dist $(DISTDIR).zip.asc
 +
 +PY2EXE_TMPDIR = py2exe-tmp
 +export PY2EXE_TMPDIR
 +$(PY2EXE_TMPDIR): setup-client-exe.py
 +	$(PYTHON_W32) setup-client-exe.py py2exe -q
 +
 +DISTDIR_W32 = $(DISTDIR)-win32
 +# below, we override DST_SCRIPT and DST_MAN1 for windows
 +$(DISTDIR_W32): $(PY2EXE_TMPDIR) $(THISFILE)
 +	mkdir -p $(DISTDIR_W32)
 +	$(MAKE_CLIENT) DESTDIR=$(DISTDIR_W32) bindir=/ docdir=/ man1dir=/doc/ \
 +	  DST_SCRIPT= DST_MAN1='$$(SRC_MAN1)' \
 +	  REBUILD_MAN="$(REBUILD_MAN)" install
 +	cp -t $(DISTDIR_W32) $(PY2EXE_TMPDIR)/dist/*
 +
 +dist-exe: force-dist-exe $(DISTDIR_W32).zip
 +
 +sign-exe: force-dist-exe $(DISTDIR_W32).zip.asc
 +
 +# clean is N/A for a binary package, but include for completeness
 +clean: distclean
 +
 +distclean:
 +	$(MAKE_CLIENT) clean
 +	$(PYTHON) setup-common.py clean --all
  	rm -rf dist $(PY2EXE_TMPDIR)
  
 -clean-all: clean
 -	rm -f doc/*.1
 +test: check
 +check:
 +	$(MAKE_CLIENT) check
 +	$(PYTHON) setup-common.py test
- 	cd facilitator && ./facilitator-test
- 	cd proxy && ./flashproxy-test.js
+ 
 -test:
 -	./flashproxy-client-test
+ 
+ test-full: test
+ 	cd facilitator && \
+ 	  { test -x ./config.status && ./config.status || \
+ 	  { test -x ./configure || ./autogen.sh; } && ./configure; } \
+ 	  && make && make check
+ 	cd proxy && make test
  
 -.PHONY: all install dist sign dist-exe clean clean-all test test-full
 +force-dist:
 +	rm -rf $(DISTDIR) $(DISTDIR).zip
 +
 +force-dist-exe:
 +	rm -rf $(DISTDIR_W32) $(DISTDIR_W32).zip $(PY2EXE_TMPDIR)
 +
- .PHONY: all dist sign dist-exe sign-exe clean distclean test check force-dist force-dist-exe
++.PHONY: all dist sign dist-exe sign-exe clean distclean test check test-full force-dist force-dist-exe
diff --cc facilitator/facilitator-email-poller
index 5348219,d4e1e16..be1ab58
--- a/facilitator/facilitator-email-poller
+++ b/facilitator/facilitator-email-poller
@@@ -19,11 -19,9 +19,9 @@@ import tim
  import fac
  
  from hashlib import sha1
 -from M2Crypto import SSL, X509
 +from M2Crypto import SSL
  
- DEFAULT_IMAP_HOST = "imap.gmail.com"
  DEFAULT_IMAP_PORT = 993
- DEFAULT_EMAIL_ADDRESS = "flashproxyreg.a at gmail.com"
  DEFAULT_LOG_FILENAME = "facilitator-email-poller.log"
  
  POLL_INTERVAL = 60
diff --cc facilitator/facilitator-test.py
index 0000000,e00ea5e..8debb82
mode 000000,100755..100755
--- a/facilitator/facilitator-test.py
+++ b/facilitator/facilitator-test.py
@@@ -1,0 -1,199 +1,444 @@@
+ #!/usr/bin/env python
+ 
++from cStringIO import StringIO
+ import os
+ import socket
+ import subprocess
++import tempfile
++import sys
+ import time
+ import unittest
+ 
+ import fac
++from fac import Transport, Endpoint
++
++# Import the facilitator program as a module.
++import imp
++dont_write_bytecode = sys.dont_write_bytecode
++sys.dont_write_bytecode = True
++facilitator = imp.load_source("facilitator", "facilitator")
++Endpoints = facilitator.Endpoints
++parse_relay_file = facilitator.parse_relay_file
++sys.dont_write_bytecode = dont_write_bytecode
++del dont_write_bytecode
++del facilitator
+ 
+ FACILITATOR_HOST = "127.0.0.1"
 -FACILITATOR_PORT = 39002
++FACILITATOR_PORT = 39002 # diff port to not conflict with production service
+ FACILITATOR_ADDR = (FACILITATOR_HOST, FACILITATOR_PORT)
++CLIENT_TP = "websocket"
++RELAY_TP = "websocket"
++PROXY_TPS = ["websocket", "webrtc"]
+ 
+ def gimme_socket(host, port):
+     addrinfo = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP)[0]
+     s = socket.socket(addrinfo[0], addrinfo[1], addrinfo[2])
+     s.settimeout(10.0)
+     s.connect(addrinfo[4])
+     return s
+ 
++class EndpointsTest(unittest.TestCase):
++
++    def setUp(self):
++        self.pts = Endpoints(af=socket.AF_INET)
++
++    def test_addEndpoints_twice(self):
++        self.pts.addEndpoint("A", "a|b|p")
++        self.assertFalse(self.pts.addEndpoint("A", "zzz"))
++        self.assertEquals(self.pts._endpoints["A"], Transport("a|b", "p"))
++
++    def test_delEndpoints_twice(self):
++        self.pts.addEndpoint("A", "a|b|p")
++        self.assertTrue(self.pts.delEndpoint("A"))
++        self.assertFalse(self.pts.delEndpoint("A"))
++        self.assertEquals(self.pts._endpoints.get("A"), None)
++
++    def test_Endpoints_indexing(self):
++        self.assertEquals(self.pts._indexes.get("p"), None)
++        # test defaultdict works as expected
++        self.assertEquals(self.pts._indexes["p"]["a|b"], set(""))
++        self.pts.addEndpoint("A", "a|b|p")
++        self.assertEquals(self.pts._indexes["p"]["a|b"], set("A"))
++        self.pts.addEndpoint("B", "a|b|p")
++        self.assertEquals(self.pts._indexes["p"]["a|b"], set("AB"))
++        self.pts.delEndpoint("A")
++        self.assertEquals(self.pts._indexes["p"]["a|b"], set("B"))
++        self.pts.delEndpoint("B")
++        self.assertEquals(self.pts._indexes["p"]["a|b"], set(""))
++
++    def test_serveReg_maxserve_infinite_roundrobin(self):
++        # case for servers, they never exhaust
++        self.pts.addEndpoint("A", "a|p")
++        self.pts.addEndpoint("B", "a|p")
++        self.pts.addEndpoint("C", "a|p")
++        for i in xrange(64): # 64 is infinite ;)
++            served = set()
++            served.add(self.pts._serveReg("ABC").addr)
++            served.add(self.pts._serveReg("ABC").addr)
++            served.add(self.pts._serveReg("ABC").addr)
++            self.assertEquals(served, set("ABC"))
++
++    def test_serveReg_maxserve_finite_exhaustion(self):
++        # case for clients, we don't want to keep serving them
++        self.pts = Endpoints(af=socket.AF_INET, maxserve=5)
++        self.pts.addEndpoint("A", "a|p")
++        self.pts.addEndpoint("B", "a|p")
++        self.pts.addEndpoint("C", "a|p")
++        # test getNumUnservedEndpoints whilst we're at it
++        self.assertEquals(self.pts.getNumUnservedEndpoints(), 3)
++        served = set()
++        served.add(self.pts._serveReg("ABC").addr)
++        self.assertEquals(self.pts.getNumUnservedEndpoints(), 2)
++        served.add(self.pts._serveReg("ABC").addr)
++        self.assertEquals(self.pts.getNumUnservedEndpoints(), 1)
++        served.add(self.pts._serveReg("ABC").addr)
++        self.assertEquals(self.pts.getNumUnservedEndpoints(), 0)
++        self.assertEquals(served, set("ABC"))
++        for i in xrange(5-2):
++            served = set()
++            served.add(self.pts._serveReg("ABC").addr)
++            served.add(self.pts._serveReg("ABC").addr)
++            served.add(self.pts._serveReg("ABC").addr)
++            self.assertEquals(served, set("ABC"))
++        remaining = set("ABC")
++        remaining.remove(self.pts._serveReg(remaining).addr)
++        self.assertRaises(KeyError, self.pts._serveReg, "ABC")
++        remaining.remove(self.pts._serveReg(remaining).addr)
++        self.assertRaises(KeyError, self.pts._serveReg, "ABC")
++        remaining.remove(self.pts._serveReg(remaining).addr)
++        self.assertRaises(KeyError, self.pts._serveReg, "ABC")
++        self.assertEquals(remaining, set())
++        self.assertEquals(self.pts.getNumUnservedEndpoints(), 0)
++
++    def test_match_normal(self):
++        self.pts.addEndpoint("A", "a|p")
++        self.pts2 = Endpoints(af=socket.AF_INET)
++        self.pts2.addEndpoint("B", "a|p")
++        self.pts2.addEndpoint("C", "b|p")
++        self.pts2.addEndpoint("D", "a|q")
++        expected = (Endpoint("A", Transport("a","p")), Endpoint("B", Transport("a","p")))
++        empty = Endpoints.EMPTY_MATCH
++        self.assertEquals(expected, Endpoints.match(self.pts, self.pts2, ["p"]))
++        self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
++
++    def test_match_unequal_client_server(self):
++        self.pts.addEndpoint("A", "a|p")
++        self.pts2 = Endpoints(af=socket.AF_INET)
++        self.pts2.addEndpoint("B", "a|q")
++        expected = (Endpoint("A", Transport("a","p")), Endpoint("B", Transport("a","q")))
++        empty = Endpoints.EMPTY_MATCH
++        self.assertEquals(expected, Endpoints.match(self.pts, self.pts2, ["p", "q"]))
++        self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["p"]))
++        self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["q"]))
++        self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
++
++    def test_match_raw_server(self):
++        self.pts.addEndpoint("A", "p")
++        self.pts2 = Endpoints(af=socket.AF_INET)
++        self.pts2.addEndpoint("B", "p")
++        expected = (Endpoint("A", Transport("","p")), Endpoint("B", Transport("","p")))
++        empty = Endpoints.EMPTY_MATCH
++        self.assertEquals(expected, Endpoints.match(self.pts, self.pts2, ["p"]))
++        self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
++
++    def test_match_many_inners(self):
++        self.pts.addEndpoint("A", "a|p")
++        self.pts.addEndpoint("B", "b|p")
++        self.pts.addEndpoint("C", "p")
++        self.pts2 = Endpoints(af=socket.AF_INET)
++        self.pts2.addEndpoint("D", "a|p")
++        self.pts2.addEndpoint("E", "b|p")
++        self.pts2.addEndpoint("F", "p")
++        # this test ensures we have a sane policy for selecting between inners pools
++        expected = set()
++        expected.add((Endpoint("A", Transport("a","p")), Endpoint("D", Transport("a","p"))))
++        expected.add((Endpoint("B", Transport("b","p")), Endpoint("E", Transport("b","p"))))
++        expected.add((Endpoint("C", Transport("","p")), Endpoint("F", Transport("","p"))))
++        result = set()
++        result.add(Endpoints.match(self.pts, self.pts2, ["p"]))
++        result.add(Endpoints.match(self.pts, self.pts2, ["p"]))
++        result.add(Endpoints.match(self.pts, self.pts2, ["p"]))
++        empty = Endpoints.EMPTY_MATCH
++        self.assertEquals(expected, result)
++        self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
++        self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
++        self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
++
++    def test_match_exhaustion(self):
++        self.pts.addEndpoint("A", "p")
++        self.pts2 = Endpoints(af=socket.AF_INET, maxserve=2)
++        self.pts2.addEndpoint("B", "p")
++        Endpoints.match(self.pts2, self.pts, ["p"])
++        Endpoints.match(self.pts2, self.pts, ["p"])
++        empty = Endpoints.EMPTY_MATCH
++        self.assertTrue("B" not in self.pts2._endpoints)
++        self.assertTrue("B" not in self.pts2._indexes["p"][""])
++        self.assertEquals(empty, Endpoints.match(self.pts2, self.pts, ["p"]))
++
++
+ class FacilitatorTest(unittest.TestCase):
++
++    def test_transport_parse(self):
++        self.assertEquals(Transport.parse("a"), Transport("", "a"))
++        self.assertEquals(Transport.parse("|a"), Transport("", "a"))
++        self.assertEquals(Transport.parse("a|b|c"), Transport("a|b","c"))
++        self.assertEquals(Transport.parse(Transport("a|b","c")), Transport("a|b","c"))
++        self.assertRaises(ValueError, Transport, "", "")
++        self.assertRaises(ValueError, Transport, "a", "")
++        self.assertRaises(ValueError, Transport.parse, "")
++        self.assertRaises(ValueError, Transport.parse, "|")
++        self.assertRaises(ValueError, Transport.parse, "a|")
++        self.assertRaises(ValueError, Transport.parse, ["a"])
++        self.assertRaises(ValueError, Transport.parse, [Transport("a", "b")])
++
++    def test_parse_relay_file(self):
++        fp = StringIO()
++        fp.write("websocket 0.0.1.0:1\n")
++        fp.flush()
++        fp.seek(0)
++        af = socket.AF_INET
++        servers = { af: Endpoints(af=af) }
++        parse_relay_file(servers, fp)
++        self.assertEquals(servers[af]._endpoints, {('0.0.1.0', 1): Transport('', 'websocket')})
++
++class FacilitatorProcTest(unittest.TestCase):
+     IPV4_CLIENT_ADDR = ("1.1.1.1", 9000)
+     IPV6_CLIENT_ADDR = ("[11::11]", 9000)
+     IPV4_PROXY_ADDR = ("2.2.2.2", 13000)
+     IPV6_PROXY_ADDR = ("[22::22]", 13000)
++    IPV4_RELAY_ADDR = ("0.0.1.0", 1)
++    IPV6_RELAY_ADDR = ("[0:0::1:0]", 1)
+ 
+     def gimme_socket(self):
+         return gimme_socket(FACILITATOR_HOST, FACILITATOR_PORT)
+ 
+     def setUp(self):
++        self.relay_file = tempfile.NamedTemporaryFile()
++        self.relay_file.write("%s %s\n" % (RELAY_TP, fac.format_addr(self.IPV4_RELAY_ADDR)))
++        self.relay_file.write("%s %s\n" % (RELAY_TP, fac.format_addr(self.IPV6_RELAY_ADDR)))
++        self.relay_file.flush()
++        self.relay_file.seek(0)
+         fn = os.path.join(os.path.dirname(__file__), "./facilitator")
 -        self.process = subprocess.Popen(["python", fn, "-d", "-p", str(FACILITATOR_PORT), "-r", "0.0.1.0:1", "-l", "/dev/null"])
++        self.process = subprocess.Popen(["python", fn, "-d", "-p", str(FACILITATOR_PORT), "-r", self.relay_file.name, "-l", "/dev/null"])
+         time.sleep(0.1)
+ 
+     def tearDown(self):
+         ret = self.process.poll()
+         if ret is not None:
+             raise Exception("facilitator subprocess exited unexpectedly with status %d" % ret)
+         self.process.terminate()
+ 
+     def test_timeout(self):
+         """Test that the socket will not accept slow writes indefinitely.
+         Successive sends should not reset the timeout counter."""
+         s = self.gimme_socket()
+         time.sleep(0.3)
+         s.send("w")
+         time.sleep(0.3)
+         s.send("w")
+         time.sleep(0.3)
+         s.send("w")
+         time.sleep(0.3)
+         s.send("w")
+         time.sleep(0.3)
+         self.assertRaises(socket.error, s.send, "w")
+ 
+     def test_readline_limit(self):
+         """Test that reads won't buffer indefinitely."""
+         s = self.gimme_socket()
+         buflen = 0
+         try:
+             while buflen + 1024 < 200000:
+                 s.send("X" * 1024)
+                 buflen += 1024
++            # TODO(dcf1): sometimes no error is raised, and this test fails
+             self.fail("should have raised a socket error")
+         except socket.error:
+             pass
+ 
+     def test_af_v4_v4(self):
+         """Test that IPv4 proxies can get IPv4 clients."""
 -        fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR)
 -        fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR)
 -        reg = fac.get_reg(FACILITATOR_ADDR, self.IPV4_PROXY_ADDR)
++        fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR, CLIENT_TP)
++        fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR, CLIENT_TP)
++        reg = fac.get_reg(FACILITATOR_ADDR, self.IPV4_PROXY_ADDR, PROXY_TPS)
+         self.assertEqual(reg["client"], fac.format_addr(self.IPV4_CLIENT_ADDR))
+ 
+     def test_af_v4_v6(self):
+         """Test that IPv4 proxies do not get IPv6 clients."""
 -        fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR)
 -        reg = fac.get_reg(FACILITATOR_ADDR, self.IPV4_PROXY_ADDR)
++        fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR, CLIENT_TP)
++        reg = fac.get_reg(FACILITATOR_ADDR, self.IPV4_PROXY_ADDR, PROXY_TPS)
+         self.assertEqual(reg["client"], "")
+ 
+     def test_af_v6_v4(self):
+         """Test that IPv6 proxies do not get IPv4 clients."""
 -        fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR)
 -        reg = fac.get_reg(FACILITATOR_ADDR, self.IPV6_PROXY_ADDR)
++        fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR, CLIENT_TP)
++        reg = fac.get_reg(FACILITATOR_ADDR, self.IPV6_PROXY_ADDR, PROXY_TPS)
+         self.assertEqual(reg["client"], "")
+ 
+     def test_af_v6_v6(self):
+         """Test that IPv6 proxies can get IPv6 clients."""
 -        fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR)
 -        fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR)
 -        reg = fac.get_reg(FACILITATOR_ADDR, self.IPV6_PROXY_ADDR)
++        fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR, CLIENT_TP)
++        fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR, CLIENT_TP)
++        reg = fac.get_reg(FACILITATOR_ADDR, self.IPV6_PROXY_ADDR, PROXY_TPS)
+         self.assertEqual(reg["client"], fac.format_addr(self.IPV6_CLIENT_ADDR))
+ 
 -    def test_check_back_in(self):
 -        """Test that facilitator responses contain a CHECK-BACK-IN key with a
 -        numeric value."""
 -        reg = fac.get_reg(FACILITATOR_ADDR, self.IPV6_PROXY_ADDR)
++    def test_fields(self):
++        """Test that facilitator responses contain all the required fields."""
++        fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR, CLIENT_TP)
++        reg = fac.get_reg(FACILITATOR_ADDR, self.IPV4_PROXY_ADDR, PROXY_TPS)
++        self.assertEqual(reg["client"], fac.format_addr(self.IPV4_CLIENT_ADDR))
++        self.assertEqual(reg["client-transport"], CLIENT_TP)
++        self.assertEqual(reg["relay"], fac.format_addr(self.IPV4_RELAY_ADDR))
++        self.assertEqual(reg["relay-transport"], RELAY_TP)
+         self.assertGreater(int(reg["check-back-in"]), 0)
+ 
+ #     def test_same_proxy(self):
+ #         """Test that the same proxy doesn't get the same client when asking
+ #         twice."""
+ #         self.fail()
+ #
+ #     def test_num_clients(self):
+ #         """Test that the same proxy can pick up up to five different clients but
+ #         no more. Test that a proxy ceasing to handle a client allows the proxy
+ #         to handle another, different client."""
+ #         self.fail()
+ #
+ #     def test_num_proxies(self):
+ #         """Test that a single client is handed out to five different proxies but
+ #         no more. Test that a proxy ceasing to handle a client reduces its count
+ #         so another proxy can handle it."""
+ #         self.fail()
+ #
+ #     def test_proxy_timeout(self):
+ #         """Test that a proxy ceasing to connect for some time period causes that
+ #         proxy's clients to be unhandled by that proxy."""
+ #         self.fail()
+ #
+ #     def test_localhost_only(self):
+ #         """Test that the facilitator doesn't listen on any external
+ #         addresses."""
+ #         self.fail()
+ #
+ #     def test_hostname(self):
+ #         """Test that the facilitator rejects hostnames."""
+ #         self.fail()
+ 
+ class ParseAddrSpecTest(unittest.TestCase):
+     def test_ipv4(self):
+         self.assertEqual(fac.parse_addr_spec("192.168.0.1:9999"), ("192.168.0.1", 9999))
+ 
+     def test_ipv6(self):
+         self.assertEqual(fac.parse_addr_spec("[12::34]:9999"), ("12::34", 9999))
+ 
+     def test_defhost_defport_ipv4(self):
+         self.assertEqual(fac.parse_addr_spec("192.168.0.2:8888", defhost="192.168.0.1", defport=9999), ("192.168.0.2", 8888))
+         self.assertEqual(fac.parse_addr_spec("192.168.0.2:", defhost="192.168.0.1", defport=9999), ("192.168.0.2", 9999))
+         self.assertEqual(fac.parse_addr_spec("192.168.0.2", defhost="192.168.0.1", defport=9999), ("192.168.0.2", 9999))
+         self.assertEqual(fac.parse_addr_spec(":8888", defhost="192.168.0.1", defport=9999), ("192.168.0.1", 8888))
+         self.assertEqual(fac.parse_addr_spec(":", defhost="192.168.0.1", defport=9999), ("192.168.0.1", 9999))
+         self.assertEqual(fac.parse_addr_spec("", defhost="192.168.0.1", defport=9999), ("192.168.0.1", 9999))
+ 
+     def test_defhost_defport_ipv6(self):
+         self.assertEqual(fac.parse_addr_spec("[1234::2]:8888", defhost="1234::1", defport=9999), ("1234::2", 8888))
+         self.assertEqual(fac.parse_addr_spec("[1234::2]:", defhost="1234::1", defport=9999), ("1234::2", 9999))
+         self.assertEqual(fac.parse_addr_spec("[1234::2]", defhost="1234::1", defport=9999), ("1234::2", 9999))
+         self.assertEqual(fac.parse_addr_spec(":8888", defhost="1234::1", defport=9999), ("1234::1", 8888))
+         self.assertEqual(fac.parse_addr_spec(":", defhost="1234::1", defport=9999), ("1234::1", 9999))
+         self.assertEqual(fac.parse_addr_spec("", defhost="1234::1", defport=9999), ("1234::1", 9999))
+ 
+     def test_noresolve(self):
+         """Test that parse_addr_spec does not do DNS resolution by default."""
+         self.assertRaises(ValueError, fac.parse_addr_spec, "example.com")
+ 
+     def test_noresolve_nameok(self):
+         """Test that nameok passes through a domain name without resolving it."""
+         self.assertEqual(fac.parse_addr_spec("example.com:8888", defhost="other.com", defport=9999, nameOk=True), ("example.com", 8888))
+         self.assertEqual(fac.parse_addr_spec("", defhost="other.com", defport=9999, nameOk=True), ("other.com", 9999))
+ 
+ class ParseTransactionTest(unittest.TestCase):
+     def test_empty_string(self):
+         self.assertRaises(ValueError, fac.parse_transaction, "")
+ 
+     def test_correct(self):
+         self.assertEqual(fac.parse_transaction("COMMAND"), ("COMMAND", ()))
+         self.assertEqual(fac.parse_transaction("COMMAND X=\"\""), ("COMMAND", (("X", ""),)))
+         self.assertEqual(fac.parse_transaction("COMMAND X=\"ABC\""), ("COMMAND", (("X", "ABC"),)))
+         self.assertEqual(fac.parse_transaction("COMMAND X=\"\\A\\B\\C\""), ("COMMAND", (("X", "ABC"),)))
+         self.assertEqual(fac.parse_transaction("COMMAND X=\"\\\\\\\"\""), ("COMMAND", (("X", "\\\""),)))
+         self.assertEqual(fac.parse_transaction("COMMAND X=\"ABC\" Y=\"DEF\""), ("COMMAND", (("X", "ABC"), ("Y", "DEF"))))
+         self.assertEqual(fac.parse_transaction("COMMAND KEY-NAME=\"ABC\""), ("COMMAND", (("KEY-NAME", "ABC"),)))
+         self.assertEqual(fac.parse_transaction("COMMAND KEY_NAME=\"ABC\""), ("COMMAND", (("KEY_NAME", "ABC"),)))
+ 
+     def test_missing_command(self):
+         self.assertRaises(ValueError, fac.parse_transaction, "X=\"ABC\"")
+         self.assertRaises(ValueError, fac.parse_transaction, " X=\"ABC\"")
+ 
+     def test_missing_space(self):
+         self.assertRaises(ValueError, fac.parse_transaction, "COMMAND/X=\"ABC\"")
+         self.assertRaises(ValueError, fac.parse_transaction, "COMMAND X=\"ABC\"Y=\"DEF\"")
+ 
+     def test_bad_quotes(self):
+         self.assertRaises(ValueError, fac.parse_transaction, "COMMAND X=\"")
+         self.assertRaises(ValueError, fac.parse_transaction, "COMMAND X=\"ABC")
+         self.assertRaises(ValueError, fac.parse_transaction, "COMMAND X=\"ABC\" Y=\"ABC")
+         self.assertRaises(ValueError, fac.parse_transaction, "COMMAND X=\"ABC\\")
+ 
+     def test_truncated(self):
+         self.assertRaises(ValueError, fac.parse_transaction, "COMMAND X=")
+ 
+     def test_newline(self):
+         self.assertRaises(ValueError, fac.parse_transaction, "COMMAND X=\"ABC\" \nY=\"DEF\"")
+ 
++class ReadClientRegistrationsTest(unittest.TestCase):
++    def testSingle(self):
++        l = list(fac.read_client_registrations(""))
++        self.assertEqual(len(l), 0)
++        l = list(fac.read_client_registrations("client=1.2.3.4:1111"))
++        self.assertEqual(len(l), 1)
++        self.assertEqual(l[0].addr, ("1.2.3.4", 1111))
++        l = list(fac.read_client_registrations("client=1.2.3.4:1111\n"))
++        self.assertEqual(len(l), 1)
++        self.assertEqual(l[0].addr, ("1.2.3.4", 1111))
++        l = list(fac.read_client_registrations("foo=bar&client=1.2.3.4:1111&baz=quux"))
++        self.assertEqual(len(l), 1)
++        self.assertEqual(l[0].addr, ("1.2.3.4", 1111))
++        l = list(fac.read_client_registrations("foo=b%3dar&client=1.2.3.4%3a1111"))
++        self.assertEqual(len(l), 1)
++        self.assertEqual(l[0].addr, ("1.2.3.4", 1111))
++        l = list(fac.read_client_registrations("client=%5b1::2%5d:3333"))
++        self.assertEqual(len(l), 1)
++        self.assertEqual(l[0].addr, ("1::2", 3333))
++
++    def testDefaultAddress(self):
++        l = list(fac.read_client_registrations("client=:1111&transport=websocket", defhost="1.2.3.4"))
++        self.assertEqual(l[0].addr, ("1.2.3.4", 1111))
++        l = list(fac.read_client_registrations("client=1.2.3.4:&transport=websocket", defport=1111))
++        self.assertEqual(l[0].addr, ("1.2.3.4", 1111))
++
++    def testDefaultTransport(self):
++        l = list(fac.read_client_registrations("client=1.2.3.4:1111"))
++        self.assertEqual(l[0].transport, "websocket")
++
++    def testMultiple(self):
++        l = list(fac.read_client_registrations("client=1.2.3.4:1111&foo=bar\nfoo=bar&client=5.6.7.8:2222"))
++        self.assertEqual(len(l), 2)
++        self.assertEqual(l[0].addr, ("1.2.3.4", 1111))
++        self.assertEqual(l[1].addr, ("5.6.7.8", 2222))
++        l = list(fac.read_client_registrations("client=1.2.3.4:1111&foo=bar\nfoo=bar&client=%5b1::2%5d:3333"))
++        self.assertEqual(len(l), 2)
++        self.assertEqual(l[0].addr, ("1.2.3.4", 1111))
++        self.assertEqual(l[1].addr, ("1::2", 3333))
++
++    def testInvalid(self):
++        # Missing "client".
++        with self.assertRaises(ValueError):
++            list(fac.read_client_registrations("foo=bar"))
++        # More than one "client".
++        with self.assertRaises(ValueError):
++            list(fac.read_client_registrations("client=1.2.3.4:1111&foo=bar&client=5.6.7.8:2222"))
++        # Single client with bad syntax.
++        with self.assertRaises(ValueError):
++            list(fac.read_client_registrations("client=1.2.3.4,1111"))
++
+ if __name__ == "__main__":
+     unittest.main()
diff --cc facilitator/init.d/facilitator.in
index 0000000,d6dc0ec..1a1899b
mode 000000,100755..100755
--- a/facilitator/init.d/facilitator.in
+++ b/facilitator/init.d/facilitator.in
@@@ -1,0 -1,136 +1,133 @@@
+ #! /bin/sh
+ ### BEGIN INIT INFO
+ # Provides:          facilitator
+ # Required-Start:    $remote_fs $syslog
+ # Required-Stop:     $remote_fs $syslog
+ # Default-Start:     2 3 4 5
+ # Default-Stop:      0 1 6
+ # Short-Description: Flash proxy facilitator
+ # Description:       Debian init script for the flash proxy facilitator.
+ ### END INIT INFO
+ #
+ # Author:	David Fifield <david at bamsoftware.com>
+ #
+ 
+ # Based on /etc/init.d/skeleton from Debian 6.
+ 
 -# The relay must support the websocket pluggable transport.
 -# This is the IPv4 address of tor1.bamsoftware.com.
 -RELAY=173.255.221.44:9901
 -
+ PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
+ DESC="Flash proxy facilitator"
+ NAME=facilitator
+ 
+ prefix=@prefix@
+ exec_prefix=@exec_prefix@
+ PIDFILE=@localstatedir@/run/$NAME.pid
+ LOGFILE=@localstatedir@/log/$NAME.log
+ CONFDIR=@sysconfdir@/flashproxy
++RELAYFILE=$CONFDIR/relays
+ PRIVDROP_USER=@fpfacilitatoruser@
+ DAEMON=@bindir@/$NAME
 -DAEMON_ARGS="-r $RELAY --log $LOGFILE --pidfile $PIDFILE --privdrop-user $PRIVDROP_USER"
++DAEMON_ARGS="--relay-file $RELAYFILE --log $LOGFILE --pidfile $PIDFILE --privdrop-user $PRIVDROP_USER"
+ DEFAULTSFILE=@sysconfdir@/default/$NAME
+ 
+ # Exit if the package is not installed
+ [ -x "$DAEMON" ] || exit 0
+ 
+ # Read configuration variable file if it is present
+ [ -r "$DEFAULTSFILE" ] && . "$DEFAULTSFILE"
+ 
+ . /lib/init/vars.sh
+ . /lib/lsb/init-functions
+ 
+ [ "$UNSAFE_LOGGING" = "yes" ] && DAEMON_ARGS="$DAEMON_ARGS --unsafe-logging"
+ [ -n "$PORT" ] && DAEMON_ARGS="$DAEMON_ARGS --port $PORT"
+ 
+ #
+ # Function that starts the daemon/service
+ #
+ do_start()
+ {
+ 	# Return
+ 	#   0 if daemon has been started
+ 	#   1 if daemon was already running
+ 	#   2 if daemon could not be started
+ 	start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
+ 		|| return 1
+ 	start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
+ 		$DAEMON_ARGS \
+ 		|| return 2
+ }
+ 
+ #
+ # Function that stops the daemon/service
+ #
+ do_stop()
+ {
+ 	# Return
+ 	#   0 if daemon has been stopped
+ 	#   1 if daemon was already stopped
+ 	#   2 if daemon could not be stopped
+ 	#   other if a failure occurred
+ 	start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
+ 	RETVAL="$?"
+ 	[ "$RETVAL" = 2 ] && return 2
+ 	# Wait for children to finish too if this is a daemon that forks
+ 	# and if the daemon is only ever run from this initscript.
+ 	# If the above conditions are not satisfied then add some other code
+ 	# that waits for the process to drop all resources that could be
+ 	# needed by services started subsequently.  A last resort is to
+ 	# sleep for some time.
+ 	start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
+ 	[ "$?" = 2 ] && return 2
+ 	rm -f $PIDFILE
+ 	return "$RETVAL"
+ }
+ 
+ case "$1" in
+   start)
+ 	if [ "$RUN_DAEMON" != "yes" ]; then
+ 		log_action_msg "Not starting $DESC (Disabled in $DEFAULTSFILE)."
+ 		exit 0
+ 	fi
+ 	[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+ 	do_start
+ 	case "$?" in
+ 		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ 	esac
+ 	;;
+   stop)
+ 	[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+ 	do_stop
+ 	case "$?" in
+ 		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ 	esac
+ 	;;
+   status)
+        status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
+        ;;
+   restart|force-reload)
+ 	log_daemon_msg "Restarting $DESC" "$NAME"
+ 	do_stop
+ 	case "$?" in
+ 	  0|1)
+ 		do_start
+ 		case "$?" in
+ 			0) log_end_msg 0 ;;
+ 			1) log_end_msg 1 ;; # Old process is still running
+ 			*) log_end_msg 1 ;; # Failed to start
+ 		esac
+ 		;;
+ 	  *)
+ 	  	# Failed to stop
+ 		log_end_msg 1
+ 		;;
+ 	esac
+ 	;;
+   *)
+ 	echo "Usage: $0 {start|stop|status|restart|force-reload}" >&2
+ 	exit 3
+ 	;;
+ esac
+ 
+ :





More information about the tor-commits mailing list