commit 59ced2a86af9414115a594b499f7307bc7821b69 Author: David Fifield david@bamsoftware.com Date: Thu Aug 30 12:31:50 2012 -0700
Move facilitator files into a subdirectory. --- Makefile | 3 +- fac.py | 182 ---------------------- facilitator | 330 ---------------------------------------- facilitator-test | 124 --------------- facilitator.cgi | 115 -------------- facilitator/Makefile | 17 ++ facilitator/fac.py | 182 ++++++++++++++++++++++ facilitator/facilitator | 330 ++++++++++++++++++++++++++++++++++++++++ facilitator/facilitator-test | 124 +++++++++++++++ facilitator/facilitator.cgi | 115 ++++++++++++++ facilitator/init.d/facilitator | 67 ++++++++ init.d/facilitator | 67 -------- 12 files changed, 836 insertions(+), 820 deletions(-)
diff --git a/Makefile b/Makefile index 3a15e54..78de922 100644 --- a/Makefile +++ b/Makefile @@ -10,14 +10,13 @@ all:
install: mkdir -p $(BINDIR) - cp -f flashproxy-client flashproxy-reg-http facilitator $(BINDIR) + cp -f flashproxy-client flashproxy-reg-http $(BINDIR)
clean: rm -f *.pyc rm -rf dist
test: - ./facilitator-test ./flashproxy-client-test ./flashproxy-test.js
diff --git a/fac.py b/fac.py deleted file mode 100644 index b9ad435..0000000 --- a/fac.py +++ /dev/null @@ -1,182 +0,0 @@ -import re -import socket - -def parse_addr_spec(spec, defhost = None, defport = None, resolve = False): - """Parse a host:port specification and return a 2-tuple ("host", port) as - understood by the Python socket functions. - >>> parse_addr_spec("192.168.0.1:9999") - ('192.168.0.1', 9999) - - If defhost or defport are given, those parts of the specification may be - omitted; if so, they will be filled in with defaults. - >>> parse_addr_spec("192.168.0.2:8888", defhost="192.168.0.1", defport=9999) - ('192.168.0.2', 8888) - >>> parse_addr_spec(":8888", defhost="192.168.0.1", defport=9999) - ('192.168.0.1', 9999) - >>> parse_addr_spec("192.168.0.2:", defhost="192.168.0.1", defport=9999) - ('192.168.0.2', 9999) - >>> parse_addr_spec(":", defhost="192.168.0.1", defport=9999) - ('192.168.0.1', 9999) - - If resolve is true, then the host in the specification or the defhost may be - a domain name, which will be resolved. If resolve is false, then the host - must be a numeric IPv4 or IPv6 address. - - IPv6 addresses must be enclosed in square brackets.""" - host = None - port = None - m = None - # IPv6 syntax. - if not m: - m = re.match(ur'^[(.+)]:(\d+)$', spec) - if m: - host, port = m.groups() - af = socket.AF_INET6 - if not m: - m = re.match(ur'^[(.+)]:?$', spec) - if m: - host, = m.groups() - af = socket.AF_INET6 - # IPv4 syntax. - if not m: - m = re.match(ur'^(.+):(\d+)$', spec) - if m: - host, port = m.groups() - af = socket.AF_INET - if not m: - m = re.match(ur'^:?(\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 host is None or port is None: - raise ValueError("Bad address specification "%s"" % spec) - - # Now we have split around the colon and have a guess at the address family. - # Forward-resolve the name into an addrinfo struct. Real DNS resolution is - # done only if resolve is true; otherwise the address must be numeric. - if resolve: - flags = 0 - else: - flags = socket.AI_NUMERICHOST - try: - addrs = socket.getaddrinfo(host, port, af, socket.SOCK_STREAM, socket.IPPROTO_TCP, flags) - 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)) - - # Convert the result of socket.getaddrinfo (which is a 2-tuple for IPv4 and - # a 4-tuple for IPv6) into a (host, port) 2-tuple. - host, port = socket.getnameinfo(addrs[0][4], socket.NI_NUMERICHOST | socket.NI_NUMERICSERV) - port = int(port) - return host, port - -def format_addr(addr): - host, port = addr - host_str = u"" - port_str = u"" - if host is not None: - # 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: - host_str = u"[%s]" % host - else: - host_str = u"%s" % host - if port is not None: - if not (0 < port <= 65535): - raise ValueError("port must be between 1 and 65535 (is %d)" % port) - port_str = u":%d" % port - - if not host_str and not port_str: - raise ValueError("host and port may not both be None") - return u"%s%s" % (host_str, port_str) - -def skip_space(pos, line): - """Skip a (possibly empty) sequence of space characters (the ASCII character - '\x20' exactly). Returns a pair (pos, num_skipped).""" - begin = pos - while pos < len(line) and line[pos] == "\x20": - pos += 1 - return pos, pos - begin - -TOKEN_CHARS = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-") -def get_token(pos, line): - begin = pos - while pos < len(line) and line[pos] in TOKEN_CHARS: - pos += 1 - if begin == pos: - raise ValueError("No token found at position %d" % pos) - return pos, line[begin:pos] - -def get_quoted_string(pos, line): - chars = [] - if not (pos < len(line) and line[pos] == '"'): - raise ValueError("Expected '"' at beginning of quoted string.") - pos += 1 - while pos < len(line) and line[pos] != '"': - if line[pos] == '\': - pos += 1 - if not (pos < len(line)): - raise ValueError("End of line after backslash in quoted string") - chars.append(line[pos]) - pos += 1 - if not (pos < len(line) and line[pos] == '"'): - raise ValueError("Expected '"' at end of quoted string.") - pos += 1 - return pos, "".join(chars) - -def parse_transaction(line): - """A transaction is a command followed by zero or more key-value pairs. Like so: - COMMAND KEY="VALUE" KEY=""ESCAPED" VALUE" - Values must be quoted. Any byte value may be escaped with a backslash. - Returns a pair: (COMMAND, ((KEY1, VALUE1), (KEY2, VALUE2), ...)). - """ - pos = 0 - pos, skipped = skip_space(pos, line) - pos, command = get_token(pos, line) - - pairs = [] - while True: - pos, skipped = skip_space(pos, line) - if not (pos < len(line)): - break - if skipped == 0: - raise ValueError("Expected space before key-value pair") - pos, key = get_token(pos, line) - if not (pos < len(line) and line[pos] == '='): - raise ValueError("No '=' found after key") - pos += 1 - pos, value = get_quoted_string(pos, line) - pairs.append((key, value)) - return command, tuple(pairs) - -def param_first(key, params): - for k, v in params: - if key == k: - return v - return None - -def quote_string(s): - chars = [] - for c in s: - if c == "\": - c = "\\" - elif c == """: - c = "\"" - chars.append(c) - return """ + "".join(chars) + """ - -def render_transaction(command, *params): - parts = [command] - for key, value in params: - parts.append("%s=%s" % (key, quote_string(value))) - return " ".join(parts) diff --git a/facilitator b/facilitator deleted file mode 100755 index 3695b52..0000000 --- a/facilitator +++ /dev/null @@ -1,330 +0,0 @@ -#!/usr/bin/env python - -import SocketServer -import errno -import getopt -import os -import socket -import sys -import threading -import time - -import fac - -LISTEN_ADDRESS = "127.0.0.1" -DEFAULT_LISTEN_PORT = 9002 -DEFAULT_RELAY_PORT = 9001 -DEFAULT_LOG_FILENAME = "facilitator.log" - -# Don't indulge clients for more than this many seconds. -CLIENT_TIMEOUT = 1.0 -# Buffer no many than this many bytes when trying to read a line. -READLINE_MAX_LENGTH = 10240 - -LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" - -class options(object): - listen_port = DEFAULT_LISTEN_PORT - log_filename = DEFAULT_LOG_FILENAME - log_file = sys.stdout - relay_spec = None - daemonize = True - pid_filename = None - safe_logging = True - - @staticmethod - def set_relay_spec(spec): - spec = fac.parse_addr_spec(spec, defport = DEFAULT_RELAY_PORT, resolve = True) - options.relay_spec = fac.format_addr(spec) - -def usage(f = sys.stdout): - print >> f, """\ -Usage: %(progname)s -r RELAY <OPTIONS> -Flash proxy facilitator: Register client addresses and serve them out -again. Listen on 127.0.0.1 and port PORT (by default %(port)d). - -d, --debug don't daemonize, log to stdout. - -h, --help show this help. - -l, --log FILENAME write log to FILENAME (default "%(log)s"). - -p, --port PORT listen on PORT (by default %(port)d). - --pidfile FILENAME write PID to FILENAME after daemonizing. - -r, --relay RELAY send RELAY (host:port) to proxies as the relay to use. - --unsafe-logging don't scrub IP addresses from logs.\ -""" % { - "progname": sys.argv[0], - "port": DEFAULT_LISTEN_PORT, - "log": DEFAULT_LOG_FILENAME, -} - -def safe_str(s): - """Return s if options.safe_logging is true, and "[scrubbed]" otherwise.""" - if options.safe_logging: - return "[scrubbed]" - else: - return s - -log_lock = threading.Lock() -def log(msg): - log_lock.acquire() - try: - print >> options.log_file, (u"%s %s" % (time.strftime(LOG_DATE_FORMAT), msg)).encode("UTF-8") - options.log_file.flush() - finally: - log_lock.release() - -class TCPReg(object): - def __init__(self, host, port): - self.host = host - self.port = port - - def __unicode__(self): - return fac.format_addr((self.host, self.port)) - - def __str__(self): - return unicode(self).encode("UTF-8") - - def __cmp__(self, other): - if isinstance(other, TCPReg): - return cmp((self.host, self.port), (other.host, other.port)) - else: - return False - -class Reg(object): - @staticmethod - def parse(spec, defhost = None, defport = None): - host, port = fac.parse_addr_spec(spec, defhost, defport) - return TCPReg(host, port) - -class RegSet(object): - def __init__(self): - self.set = [] - self.cv = threading.Condition() - - def add(self, reg): - self.cv.acquire() - try: - if reg not in list(self.set): - self.set.append(reg) - self.cv.notify() - return True - else: - return False - finally: - self.cv.release() - - def fetch(self): - self.cv.acquire() - try: - if not self.set: - return None - return self.set.pop(0) - finally: - self.cv.release() - - def __len__(self): - self.cv.acquire() - try: - return len(self.set) - finally: - self.cv.release() - -# A decorator to ignore "broken pipe" errors. -def catch_epipe(fn): - def ret(self, *args): - try: - return fn(self, *args) - except socket.error, e: - try: - err_num = e.errno - except AttributeError: - # Before Python 2.6, exception can be a pair. - err_num, errstr = e - except: - raise - if err_num != errno.EPIPE: - raise - return ret - -class Handler(SocketServer.StreamRequestHandler): - def __init__(self, *args, **kwargs): - self.deadline = time.time() + CLIENT_TIMEOUT - # Buffer for readline. - self.buffer = "" - SocketServer.StreamRequestHandler.__init__(self, *args, **kwargs) - - def recv(self): - timeout = self.deadline - time.time() - self.connection.settimeout(timeout) - return self.connection.recv(1024) - - def readline(self): - # A line already buffered? - i = self.buffer.find("\n") - if i >= 0: - line = self.buffer[:i+1] - self.buffer = self.buffer[i+1:] - return line - - auxbuf = [] - buflen = len(self.buffer) - while True: - data = self.recv() - if not data: - if self.buffer or auxbuf: - raise socket.error("readline: stream does not end with a newline") - else: - return "" - i = data.find("\n") - if i >= 0: - line = self.buffer + "".join(auxbuf) + data[:i+1] - self.buffer = data[i+1:] - return line - else: - auxbuf.append(data) - buflen += len(data) - if buflen >= READLINE_MAX_LENGTH: - raise socket.error("readline: refusing to buffer %d bytes (last read was %d bytes)" % (buflen, len(data))) - - @catch_epipe - def handle(self): - num_lines = 0 - while True: - try: - line = self.readline() - if not line: - break - num_lines += 1 - except socket.error, e: - log("socket error after reading %d lines: %s" % (num_lines, str(e))) - break - if not self.handle_line(line): - break - - def handle_line(self, line): - if not (len(line) > 0 and line[-1] == '\n'): - raise ValueError("No newline at end of string returned by readline") - try: - command, params = fac.parse_transaction(line[:-1]) - except ValueError, e: - log("fac.parse_transaction: %s" % e) - self.send_error() - return False - - if command == "GET": - return self.do_GET(params) - if command == "PUT": - return self.do_PUT(params) - else: - self.send_error() - return False - - def send_ok(self): - print >> self.wfile, "OK" - - def send_error(self): - print >> self.wfile, "ERROR" - - def do_GET(self, params): - reg = REGS.fetch() - if reg: - log(u"proxy gets %s, relay %s (now %d)" % - (safe_str(unicode(reg)), options.relay_spec, len(REGS))) - print >> self.wfile, fac.render_transaction("OK", ("CLIENT", str(reg)), ("RELAY", options.relay_spec)) - else: - log(u"proxy gets none") - print >> self.wfile, fac.render_transaction("NONE") - return True - - def do_PUT(self, params): - client_spec = fac.param_first("CLIENT", params) - if client_spec is None: - log(u"PUT missing CLIENT param") - self.send_error() - return False - - # FROM - - try: - reg = Reg.parse(client_spec, self.client_address[0]) - except ValueError, e: - log(u"syntax error in %s: %s" % (safe_str(repr(client_spec)), repr(str(e)))) - self.send_error() - return False - - if REGS.add(reg): - log(u"client %s (now %d)" % (safe_str(unicode(reg)), len(REGS))) - else: - log(u"client %s (already present, now %d)" % (safe_str(unicode(reg)), len(REGS))) - - self.send_ok() - return True - - finish = catch_epipe(SocketServer.StreamRequestHandler.finish) - -class Server(SocketServer.ThreadingMixIn, SocketServer.TCPServer): - allow_reuse_address = True - -REGS = RegSet() - -def main(): - opts, args = getopt.gnu_getopt(sys.argv[1:], "dhl:p:r:", - ["debug", "help", "log=", "port=", "pidfile=", "relay=", "unsafe-logging"]) - for o, a in opts: - if o == "-d" or o == "--debug": - options.daemonize = False - options.log_filename = None - elif o == "-h" or o == "--help": - usage() - sys.exit() - elif o == "-l" or o == "--log": - options.log_filename = a - elif o == "-p" or o == "--port": - options.listen_port = int(a) - elif o == "--pidfile": - options.pid_filename = a - elif o == "-r" or o == "--relay": - try: - options.set_relay_spec(a) - except socket.gaierror, e: - print >> sys.stderr, u"Can't resolve relay %s: %s" % (repr(a), str(e)) - sys.exit(1) - elif o == "--unsafe-logging": - options.safe_logging = False - - if not options.relay_spec: - print >> sys.stderr, """\ -The -r option is required. Give it the relay that will be sent to proxies. - -r HOST[:PORT]\ - """ - sys.exit(1) - - if options.log_filename: - options.log_file = open(options.log_filename, "a") - # Send error tracebacks to the log. - sys.stderr = options.log_file - else: - options.log_file = sys.stdout - - addrinfo = socket.getaddrinfo(LISTEN_ADDRESS, options.listen_port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP)[0] - - server = Server(addrinfo[4], Handler) - - log(u"start on %s" % fac.format_addr(addrinfo[4])) - log(u"using relay address %s" % options.relay_spec) - - if options.daemonize: - log(u"daemonizing") - pid = os.fork() - if pid != 0: - if options.pid_filename: - f = open(options.pid_filename, "w") - print >> f, pid - f.close() - sys.exit(0) - - try: - server.serve_forever() - except KeyboardInterrupt: - sys.exit(0) - -if __name__ == "__main__": - main() diff --git a/facilitator-test b/facilitator-test deleted file mode 100755 index b06f5d7..0000000 --- a/facilitator-test +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python - -import socket -import subprocess -import time -import unittest - -import fac - -FACILITATOR_HOST = "127.0.0.1" -FACILITATOR_PORT = 9002 - -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 FacilitatorTest(unittest.TestCase): - def gimme_socket(self): - return gimme_socket(FACILITATOR_HOST, FACILITATOR_PORT) - - def setUp(self): - self.process = subprocess.Popen(["./facilitator", "-d", "-p", str(FACILITATOR_PORT), "-r", "0.0.1.0:1", "-l", "/dev/null"]) - time.sleep(0.1) - - def tearDown(self): - 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 - self.fail("should have raised a socket error") - except socket.error: - pass - -# 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 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"") - -if __name__ == "__main__": - unittest.main() diff --git a/facilitator.cgi b/facilitator.cgi deleted file mode 100755 index 39566d3..0000000 --- a/facilitator.cgi +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env python - -import cgi -import os -import socket -import sys -import urllib - -import fac - -FACILITATOR_ADDR = ("127.0.0.1", 9002) - -def exit_error(status): - print """\ -Status: %d\r -\r""" % status - sys.exit() - -def fac_socket(): - return socket.create_connection(FACILITATOR_ADDR, 1.0).makefile() - -def transact(f, command, *params): - transaction = fac.render_transaction(command, *params) - print >> f, transaction - f.flush() - line = f.readline() - if not (len(line) > 0 and line[-1] == '\n'): - raise ValueError("No newline at end of string returned by facilitator") - return fac.parse_transaction(line[:-1]) - -def put_reg(client_addr, registrant_addr): - f = fac_socket() - try: - command, params = transact(f, "PUT", ("CLIENT", fac.format_addr(client_addr)), ("FROM", fac.format_addr(registrant_addr))) - finally: - f.close() - if command == "OK": - pass - else: - exit_error(500) - -def get_reg(proxy_addr): - f = fac_socket() - try: - command, params = transact(f, "GET", ("FROM", fac.format_addr(proxy_addr))) - finally: - f.close() - if command == "NONE": - return { - "client": "" - } - elif command == "OK": - client_spec = fac.param_first("CLIENT", params) - relay_spec = fac.param_first("RELAY", params) - if not client_spec or not relay_spec: - exit_error(500) - try: - # Check the syntax returned by the backend. - client = fac.parse_addr_spec(client_spec) - relay = fac.parse_addr_spec(relay_spec) - except ValueError: - exit_error(500) - return { - "client": fac.format_addr(client), - "relay": fac.format_addr(relay), - } - else: - exit_error(500) - -method = os.environ.get("REQUEST_METHOD") -proxy_addr = (os.environ.get("REMOTE_ADDR"), None) - -if not method or not proxy_addr[0]: - exit_error(400) - -fs = cgi.FieldStorage() - -def do_get(): - try: - reg = get_reg(proxy_addr) or "" - except: - exit_error(500) - # Allow XMLHttpRequest from any domain. http://www.w3.org/TR/cors/. - print """\ -Status: 200\r -Content-Type: application/x-www-form-urlencoded\r -Cache-Control: no-cache\r -Access-Control-Allow-Origin: *\r -\r""" - sys.stdout.write(urllib.urlencode(reg)) - -def do_post(): - client_specs = fs.getlist("client") - if len(client_specs) != 1: - exit_error(400) - client_spec = client_specs[0] - try: - client_addr = fac.parse_addr_spec(client_spec, defhost=proxy_addr[0]) - except ValueError: - exit_error(400) - try: - put_reg(client_addr, proxy_addr) - except: - raise - exit_error(500) - print """\ -Status: 200\r -\r""" - -if method == "GET": - do_get() -elif method == "POST": - do_post() -else: - exit_error(405) diff --git a/facilitator/Makefile b/facilitator/Makefile new file mode 100644 index 0000000..1d9cb54 --- /dev/null +++ b/facilitator/Makefile @@ -0,0 +1,17 @@ +PREFIX = /usr/local +BINDIR = $(PREFIX)/bin + +all: + : + +install: + mkdir -p $(BINDIR) + cp -f facilitator facilitator.cgi fac.py $(BINDIR) + +clean: + rm -f *.pyc + +test: + ./facilitator-test + +.PHONY: all install clean test diff --git a/facilitator/fac.py b/facilitator/fac.py new file mode 100644 index 0000000..b9ad435 --- /dev/null +++ b/facilitator/fac.py @@ -0,0 +1,182 @@ +import re +import socket + +def parse_addr_spec(spec, defhost = None, defport = None, resolve = False): + """Parse a host:port specification and return a 2-tuple ("host", port) as + understood by the Python socket functions. + >>> parse_addr_spec("192.168.0.1:9999") + ('192.168.0.1', 9999) + + If defhost or defport are given, those parts of the specification may be + omitted; if so, they will be filled in with defaults. + >>> parse_addr_spec("192.168.0.2:8888", defhost="192.168.0.1", defport=9999) + ('192.168.0.2', 8888) + >>> parse_addr_spec(":8888", defhost="192.168.0.1", defport=9999) + ('192.168.0.1', 9999) + >>> parse_addr_spec("192.168.0.2:", defhost="192.168.0.1", defport=9999) + ('192.168.0.2', 9999) + >>> parse_addr_spec(":", defhost="192.168.0.1", defport=9999) + ('192.168.0.1', 9999) + + If resolve is true, then the host in the specification or the defhost may be + a domain name, which will be resolved. If resolve is false, then the host + must be a numeric IPv4 or IPv6 address. + + IPv6 addresses must be enclosed in square brackets.""" + host = None + port = None + m = None + # IPv6 syntax. + if not m: + m = re.match(ur'^[(.+)]:(\d+)$', spec) + if m: + host, port = m.groups() + af = socket.AF_INET6 + if not m: + m = re.match(ur'^[(.+)]:?$', spec) + if m: + host, = m.groups() + af = socket.AF_INET6 + # IPv4 syntax. + if not m: + m = re.match(ur'^(.+):(\d+)$', spec) + if m: + host, port = m.groups() + af = socket.AF_INET + if not m: + m = re.match(ur'^:?(\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 host is None or port is None: + raise ValueError("Bad address specification "%s"" % spec) + + # Now we have split around the colon and have a guess at the address family. + # Forward-resolve the name into an addrinfo struct. Real DNS resolution is + # done only if resolve is true; otherwise the address must be numeric. + if resolve: + flags = 0 + else: + flags = socket.AI_NUMERICHOST + try: + addrs = socket.getaddrinfo(host, port, af, socket.SOCK_STREAM, socket.IPPROTO_TCP, flags) + 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)) + + # Convert the result of socket.getaddrinfo (which is a 2-tuple for IPv4 and + # a 4-tuple for IPv6) into a (host, port) 2-tuple. + host, port = socket.getnameinfo(addrs[0][4], socket.NI_NUMERICHOST | socket.NI_NUMERICSERV) + port = int(port) + return host, port + +def format_addr(addr): + host, port = addr + host_str = u"" + port_str = u"" + if host is not None: + # 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: + host_str = u"[%s]" % host + else: + host_str = u"%s" % host + if port is not None: + if not (0 < port <= 65535): + raise ValueError("port must be between 1 and 65535 (is %d)" % port) + port_str = u":%d" % port + + if not host_str and not port_str: + raise ValueError("host and port may not both be None") + return u"%s%s" % (host_str, port_str) + +def skip_space(pos, line): + """Skip a (possibly empty) sequence of space characters (the ASCII character + '\x20' exactly). Returns a pair (pos, num_skipped).""" + begin = pos + while pos < len(line) and line[pos] == "\x20": + pos += 1 + return pos, pos - begin + +TOKEN_CHARS = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-") +def get_token(pos, line): + begin = pos + while pos < len(line) and line[pos] in TOKEN_CHARS: + pos += 1 + if begin == pos: + raise ValueError("No token found at position %d" % pos) + return pos, line[begin:pos] + +def get_quoted_string(pos, line): + chars = [] + if not (pos < len(line) and line[pos] == '"'): + raise ValueError("Expected '"' at beginning of quoted string.") + pos += 1 + while pos < len(line) and line[pos] != '"': + if line[pos] == '\': + pos += 1 + if not (pos < len(line)): + raise ValueError("End of line after backslash in quoted string") + chars.append(line[pos]) + pos += 1 + if not (pos < len(line) and line[pos] == '"'): + raise ValueError("Expected '"' at end of quoted string.") + pos += 1 + return pos, "".join(chars) + +def parse_transaction(line): + """A transaction is a command followed by zero or more key-value pairs. Like so: + COMMAND KEY="VALUE" KEY=""ESCAPED" VALUE" + Values must be quoted. Any byte value may be escaped with a backslash. + Returns a pair: (COMMAND, ((KEY1, VALUE1), (KEY2, VALUE2), ...)). + """ + pos = 0 + pos, skipped = skip_space(pos, line) + pos, command = get_token(pos, line) + + pairs = [] + while True: + pos, skipped = skip_space(pos, line) + if not (pos < len(line)): + break + if skipped == 0: + raise ValueError("Expected space before key-value pair") + pos, key = get_token(pos, line) + if not (pos < len(line) and line[pos] == '='): + raise ValueError("No '=' found after key") + pos += 1 + pos, value = get_quoted_string(pos, line) + pairs.append((key, value)) + return command, tuple(pairs) + +def param_first(key, params): + for k, v in params: + if key == k: + return v + return None + +def quote_string(s): + chars = [] + for c in s: + if c == "\": + c = "\\" + elif c == """: + c = "\"" + chars.append(c) + return """ + "".join(chars) + """ + +def render_transaction(command, *params): + parts = [command] + for key, value in params: + parts.append("%s=%s" % (key, quote_string(value))) + return " ".join(parts) diff --git a/facilitator/facilitator b/facilitator/facilitator new file mode 100755 index 0000000..3695b52 --- /dev/null +++ b/facilitator/facilitator @@ -0,0 +1,330 @@ +#!/usr/bin/env python + +import SocketServer +import errno +import getopt +import os +import socket +import sys +import threading +import time + +import fac + +LISTEN_ADDRESS = "127.0.0.1" +DEFAULT_LISTEN_PORT = 9002 +DEFAULT_RELAY_PORT = 9001 +DEFAULT_LOG_FILENAME = "facilitator.log" + +# Don't indulge clients for more than this many seconds. +CLIENT_TIMEOUT = 1.0 +# Buffer no many than this many bytes when trying to read a line. +READLINE_MAX_LENGTH = 10240 + +LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" + +class options(object): + listen_port = DEFAULT_LISTEN_PORT + log_filename = DEFAULT_LOG_FILENAME + log_file = sys.stdout + relay_spec = None + daemonize = True + pid_filename = None + safe_logging = True + + @staticmethod + def set_relay_spec(spec): + spec = fac.parse_addr_spec(spec, defport = DEFAULT_RELAY_PORT, resolve = True) + options.relay_spec = fac.format_addr(spec) + +def usage(f = sys.stdout): + print >> f, """\ +Usage: %(progname)s -r RELAY <OPTIONS> +Flash proxy facilitator: Register client addresses and serve them out +again. Listen on 127.0.0.1 and port PORT (by default %(port)d). + -d, --debug don't daemonize, log to stdout. + -h, --help show this help. + -l, --log FILENAME write log to FILENAME (default "%(log)s"). + -p, --port PORT listen on PORT (by default %(port)d). + --pidfile FILENAME write PID to FILENAME after daemonizing. + -r, --relay RELAY send RELAY (host:port) to proxies as the relay to use. + --unsafe-logging don't scrub IP addresses from logs.\ +""" % { + "progname": sys.argv[0], + "port": DEFAULT_LISTEN_PORT, + "log": DEFAULT_LOG_FILENAME, +} + +def safe_str(s): + """Return s if options.safe_logging is true, and "[scrubbed]" otherwise.""" + if options.safe_logging: + return "[scrubbed]" + else: + return s + +log_lock = threading.Lock() +def log(msg): + log_lock.acquire() + try: + print >> options.log_file, (u"%s %s" % (time.strftime(LOG_DATE_FORMAT), msg)).encode("UTF-8") + options.log_file.flush() + finally: + log_lock.release() + +class TCPReg(object): + def __init__(self, host, port): + self.host = host + self.port = port + + def __unicode__(self): + return fac.format_addr((self.host, self.port)) + + def __str__(self): + return unicode(self).encode("UTF-8") + + def __cmp__(self, other): + if isinstance(other, TCPReg): + return cmp((self.host, self.port), (other.host, other.port)) + else: + return False + +class Reg(object): + @staticmethod + def parse(spec, defhost = None, defport = None): + host, port = fac.parse_addr_spec(spec, defhost, defport) + return TCPReg(host, port) + +class RegSet(object): + def __init__(self): + self.set = [] + self.cv = threading.Condition() + + def add(self, reg): + self.cv.acquire() + try: + if reg not in list(self.set): + self.set.append(reg) + self.cv.notify() + return True + else: + return False + finally: + self.cv.release() + + def fetch(self): + self.cv.acquire() + try: + if not self.set: + return None + return self.set.pop(0) + finally: + self.cv.release() + + def __len__(self): + self.cv.acquire() + try: + return len(self.set) + finally: + self.cv.release() + +# A decorator to ignore "broken pipe" errors. +def catch_epipe(fn): + def ret(self, *args): + try: + return fn(self, *args) + except socket.error, e: + try: + err_num = e.errno + except AttributeError: + # Before Python 2.6, exception can be a pair. + err_num, errstr = e + except: + raise + if err_num != errno.EPIPE: + raise + return ret + +class Handler(SocketServer.StreamRequestHandler): + def __init__(self, *args, **kwargs): + self.deadline = time.time() + CLIENT_TIMEOUT + # Buffer for readline. + self.buffer = "" + SocketServer.StreamRequestHandler.__init__(self, *args, **kwargs) + + def recv(self): + timeout = self.deadline - time.time() + self.connection.settimeout(timeout) + return self.connection.recv(1024) + + def readline(self): + # A line already buffered? + i = self.buffer.find("\n") + if i >= 0: + line = self.buffer[:i+1] + self.buffer = self.buffer[i+1:] + return line + + auxbuf = [] + buflen = len(self.buffer) + while True: + data = self.recv() + if not data: + if self.buffer or auxbuf: + raise socket.error("readline: stream does not end with a newline") + else: + return "" + i = data.find("\n") + if i >= 0: + line = self.buffer + "".join(auxbuf) + data[:i+1] + self.buffer = data[i+1:] + return line + else: + auxbuf.append(data) + buflen += len(data) + if buflen >= READLINE_MAX_LENGTH: + raise socket.error("readline: refusing to buffer %d bytes (last read was %d bytes)" % (buflen, len(data))) + + @catch_epipe + def handle(self): + num_lines = 0 + while True: + try: + line = self.readline() + if not line: + break + num_lines += 1 + except socket.error, e: + log("socket error after reading %d lines: %s" % (num_lines, str(e))) + break + if not self.handle_line(line): + break + + def handle_line(self, line): + if not (len(line) > 0 and line[-1] == '\n'): + raise ValueError("No newline at end of string returned by readline") + try: + command, params = fac.parse_transaction(line[:-1]) + except ValueError, e: + log("fac.parse_transaction: %s" % e) + self.send_error() + return False + + if command == "GET": + return self.do_GET(params) + if command == "PUT": + return self.do_PUT(params) + else: + self.send_error() + return False + + def send_ok(self): + print >> self.wfile, "OK" + + def send_error(self): + print >> self.wfile, "ERROR" + + def do_GET(self, params): + reg = REGS.fetch() + if reg: + log(u"proxy gets %s, relay %s (now %d)" % + (safe_str(unicode(reg)), options.relay_spec, len(REGS))) + print >> self.wfile, fac.render_transaction("OK", ("CLIENT", str(reg)), ("RELAY", options.relay_spec)) + else: + log(u"proxy gets none") + print >> self.wfile, fac.render_transaction("NONE") + return True + + def do_PUT(self, params): + client_spec = fac.param_first("CLIENT", params) + if client_spec is None: + log(u"PUT missing CLIENT param") + self.send_error() + return False + + # FROM + + try: + reg = Reg.parse(client_spec, self.client_address[0]) + except ValueError, e: + log(u"syntax error in %s: %s" % (safe_str(repr(client_spec)), repr(str(e)))) + self.send_error() + return False + + if REGS.add(reg): + log(u"client %s (now %d)" % (safe_str(unicode(reg)), len(REGS))) + else: + log(u"client %s (already present, now %d)" % (safe_str(unicode(reg)), len(REGS))) + + self.send_ok() + return True + + finish = catch_epipe(SocketServer.StreamRequestHandler.finish) + +class Server(SocketServer.ThreadingMixIn, SocketServer.TCPServer): + allow_reuse_address = True + +REGS = RegSet() + +def main(): + opts, args = getopt.gnu_getopt(sys.argv[1:], "dhl:p:r:", + ["debug", "help", "log=", "port=", "pidfile=", "relay=", "unsafe-logging"]) + for o, a in opts: + if o == "-d" or o == "--debug": + options.daemonize = False + options.log_filename = None + elif o == "-h" or o == "--help": + usage() + sys.exit() + elif o == "-l" or o == "--log": + options.log_filename = a + elif o == "-p" or o == "--port": + options.listen_port = int(a) + elif o == "--pidfile": + options.pid_filename = a + elif o == "-r" or o == "--relay": + try: + options.set_relay_spec(a) + except socket.gaierror, e: + print >> sys.stderr, u"Can't resolve relay %s: %s" % (repr(a), str(e)) + sys.exit(1) + elif o == "--unsafe-logging": + options.safe_logging = False + + if not options.relay_spec: + print >> sys.stderr, """\ +The -r option is required. Give it the relay that will be sent to proxies. + -r HOST[:PORT]\ + """ + sys.exit(1) + + if options.log_filename: + options.log_file = open(options.log_filename, "a") + # Send error tracebacks to the log. + sys.stderr = options.log_file + else: + options.log_file = sys.stdout + + addrinfo = socket.getaddrinfo(LISTEN_ADDRESS, options.listen_port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP)[0] + + server = Server(addrinfo[4], Handler) + + log(u"start on %s" % fac.format_addr(addrinfo[4])) + log(u"using relay address %s" % options.relay_spec) + + if options.daemonize: + log(u"daemonizing") + pid = os.fork() + if pid != 0: + if options.pid_filename: + f = open(options.pid_filename, "w") + print >> f, pid + f.close() + sys.exit(0) + + try: + server.serve_forever() + except KeyboardInterrupt: + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/facilitator/facilitator-test b/facilitator/facilitator-test new file mode 100755 index 0000000..b06f5d7 --- /dev/null +++ b/facilitator/facilitator-test @@ -0,0 +1,124 @@ +#!/usr/bin/env python + +import socket +import subprocess +import time +import unittest + +import fac + +FACILITATOR_HOST = "127.0.0.1" +FACILITATOR_PORT = 9002 + +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 FacilitatorTest(unittest.TestCase): + def gimme_socket(self): + return gimme_socket(FACILITATOR_HOST, FACILITATOR_PORT) + + def setUp(self): + self.process = subprocess.Popen(["./facilitator", "-d", "-p", str(FACILITATOR_PORT), "-r", "0.0.1.0:1", "-l", "/dev/null"]) + time.sleep(0.1) + + def tearDown(self): + 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 + self.fail("should have raised a socket error") + except socket.error: + pass + +# 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 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"") + +if __name__ == "__main__": + unittest.main() diff --git a/facilitator/facilitator.cgi b/facilitator/facilitator.cgi new file mode 100755 index 0000000..39566d3 --- /dev/null +++ b/facilitator/facilitator.cgi @@ -0,0 +1,115 @@ +#!/usr/bin/env python + +import cgi +import os +import socket +import sys +import urllib + +import fac + +FACILITATOR_ADDR = ("127.0.0.1", 9002) + +def exit_error(status): + print """\ +Status: %d\r +\r""" % status + sys.exit() + +def fac_socket(): + return socket.create_connection(FACILITATOR_ADDR, 1.0).makefile() + +def transact(f, command, *params): + transaction = fac.render_transaction(command, *params) + print >> f, transaction + f.flush() + line = f.readline() + if not (len(line) > 0 and line[-1] == '\n'): + raise ValueError("No newline at end of string returned by facilitator") + return fac.parse_transaction(line[:-1]) + +def put_reg(client_addr, registrant_addr): + f = fac_socket() + try: + command, params = transact(f, "PUT", ("CLIENT", fac.format_addr(client_addr)), ("FROM", fac.format_addr(registrant_addr))) + finally: + f.close() + if command == "OK": + pass + else: + exit_error(500) + +def get_reg(proxy_addr): + f = fac_socket() + try: + command, params = transact(f, "GET", ("FROM", fac.format_addr(proxy_addr))) + finally: + f.close() + if command == "NONE": + return { + "client": "" + } + elif command == "OK": + client_spec = fac.param_first("CLIENT", params) + relay_spec = fac.param_first("RELAY", params) + if not client_spec or not relay_spec: + exit_error(500) + try: + # Check the syntax returned by the backend. + client = fac.parse_addr_spec(client_spec) + relay = fac.parse_addr_spec(relay_spec) + except ValueError: + exit_error(500) + return { + "client": fac.format_addr(client), + "relay": fac.format_addr(relay), + } + else: + exit_error(500) + +method = os.environ.get("REQUEST_METHOD") +proxy_addr = (os.environ.get("REMOTE_ADDR"), None) + +if not method or not proxy_addr[0]: + exit_error(400) + +fs = cgi.FieldStorage() + +def do_get(): + try: + reg = get_reg(proxy_addr) or "" + except: + exit_error(500) + # Allow XMLHttpRequest from any domain. http://www.w3.org/TR/cors/. + print """\ +Status: 200\r +Content-Type: application/x-www-form-urlencoded\r +Cache-Control: no-cache\r +Access-Control-Allow-Origin: *\r +\r""" + sys.stdout.write(urllib.urlencode(reg)) + +def do_post(): + client_specs = fs.getlist("client") + if len(client_specs) != 1: + exit_error(400) + client_spec = client_specs[0] + try: + client_addr = fac.parse_addr_spec(client_spec, defhost=proxy_addr[0]) + except ValueError: + exit_error(400) + try: + put_reg(client_addr, proxy_addr) + except: + raise + exit_error(500) + print """\ +Status: 200\r +\r""" + +if method == "GET": + do_get() +elif method == "POST": + do_post() +else: + exit_error(405) diff --git a/facilitator/init.d/facilitator b/facilitator/init.d/facilitator new file mode 100755 index 0000000..5510d11 --- /dev/null +++ b/facilitator/init.d/facilitator @@ -0,0 +1,67 @@ +#!/bin/sh +# +# facilitator This shell script takes care of starting and stopping +# the flash proxy facilitator. +# +# chkconfig: 2345 90 10 +# description: Flash proxy facilitator. +# processname: facilitator +# pidfile: /var/flashproxy/facilitator.pid + +# Installation instructions: +# cp facilitator /etc/init.d/facilitator +# chkconfig --add facilitator +# service facilitator start + +# Source function library. +. /etc/rc.d/init.d/functions + +# Replace this with the address of a Tor relay with a websocket pluggable +# transport. You can use host:port syntax. +RELAY=tor1.bamsoftware.com:9901 + +BINDIR=/usr/local/bin +VARDIR=/var/flashproxy +PROG=$BINDIR/facilitator +PIDFILE=$VARDIR/facilitator.pid +USER=flashproxy + +# See how we were called. +case "$1" in + start) + [ -x $PROG ] || exit 1 + echo -n $"Starting flash proxy facilitator: " + cd $VARDIR && daemon --user $USER --pidfile $PIDFILE $PROG --pidfile $PIDFILE -r $RELAY + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/facilitator + ;; + stop) + # Stop daemon. + echo -n $"Shutting down flash proxy facilitator: " + killproc -p $PIDFILE + RETVAL=$? + echo + if [ $RETVAL -eq 0 ]; then + rm -f /var/lock/subsys/facilitator + rm -f $PIDFILE + fi + ;; + status) + status -p $PIDFILE facilitator + RETVAL=$? + ;; + restart|reload) + $0 stop + $0 start + ;; + condrestart) + [ -f /var/lock/subsys/facilitator ] && restart || : + ;; + *) + echo $"Usage: $0 {start|stop|status|restart}" + RETVAL=3 + ;; +esac + +exit $RETVAL diff --git a/init.d/facilitator b/init.d/facilitator deleted file mode 100755 index 5510d11..0000000 --- a/init.d/facilitator +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/sh -# -# facilitator This shell script takes care of starting and stopping -# the flash proxy facilitator. -# -# chkconfig: 2345 90 10 -# description: Flash proxy facilitator. -# processname: facilitator -# pidfile: /var/flashproxy/facilitator.pid - -# Installation instructions: -# cp facilitator /etc/init.d/facilitator -# chkconfig --add facilitator -# service facilitator start - -# Source function library. -. /etc/rc.d/init.d/functions - -# Replace this with the address of a Tor relay with a websocket pluggable -# transport. You can use host:port syntax. -RELAY=tor1.bamsoftware.com:9901 - -BINDIR=/usr/local/bin -VARDIR=/var/flashproxy -PROG=$BINDIR/facilitator -PIDFILE=$VARDIR/facilitator.pid -USER=flashproxy - -# See how we were called. -case "$1" in - start) - [ -x $PROG ] || exit 1 - echo -n $"Starting flash proxy facilitator: " - cd $VARDIR && daemon --user $USER --pidfile $PIDFILE $PROG --pidfile $PIDFILE -r $RELAY - RETVAL=$? - echo - [ $RETVAL -eq 0 ] && touch /var/lock/subsys/facilitator - ;; - stop) - # Stop daemon. - echo -n $"Shutting down flash proxy facilitator: " - killproc -p $PIDFILE - RETVAL=$? - echo - if [ $RETVAL -eq 0 ]; then - rm -f /var/lock/subsys/facilitator - rm -f $PIDFILE - fi - ;; - status) - status -p $PIDFILE facilitator - RETVAL=$? - ;; - restart|reload) - $0 stop - $0 start - ;; - condrestart) - [ -f /var/lock/subsys/facilitator ] && restart || : - ;; - *) - echo $"Usage: $0 {start|stop|status|restart}" - RETVAL=3 - ;; -esac - -exit $RETVAL
tor-commits@lists.torproject.org