[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