/usr/lib/python2.7/dist-packages/flashproxy/fac.py is in flashproxy-common 1.7-4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | import socket
import subprocess
import urlparse
from flashproxy import reg
from flashproxy.util import parse_addr_spec, format_addr
DEFAULT_CLIENT_TRANSPORT = "websocket"
def read_client_registrations(body, defhost=None, defport=None):
"""Yield client registrations (as Endpoints) from an encoded registration
message body. The message format is one registration per line, with each
line being encoded as application/x-www-form-urlencoded. The key "client" is
required and contains the client address and port (perhaps filled in by
defhost and defport). The key "client-transport" is optional and defaults to
"websocket".
Example:
client=1.2.3.4:9000&client-transport=websocket
client=1.2.3.4:9090&client-transport=obfs3|websocket
"""
for line in body.splitlines():
qs = urlparse.parse_qs(line, keep_blank_values=True, strict_parsing=True)
# Get the unique value associated with the given key in qs. If the key
# is absent or appears more than once, raise ValueError.
def get_unique(key, default=None):
try:
vals = qs[key]
except KeyError:
if default is None:
raise ValueError("missing %r key" % key)
vals = (default,)
if len(vals) != 1:
raise ValueError("more than one %r key" % key)
return vals[0]
addr = parse_addr_spec(get_unique("client"), defhost, defport)
transport = get_unique("client-transport", DEFAULT_CLIENT_TRANSPORT)
yield reg.Endpoint(addr, transport)
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):
"""Search 'params' for 'key' and return the first value that
occurs. If 'key' was not found, return None."""
for k, v in params:
if key == k:
return v
return None
def param_getlist(key, params):
"""Search 'params' for 'key' and return a list with its values. If
'key' did not appear in 'params', return the empty list."""
result = []
for k, v in params:
if key == k:
result.append(v)
return result
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)
def fac_socket(facilitator_addr):
return socket.create_connection(facilitator_addr, 1.0).makefile()
def transact(f, command, *params):
transaction = 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 parse_transaction(line[:-1])
def put_reg(facilitator_addr, client_addr, transport):
"""Send a registration to the facilitator using a one-time socket. Returns
true iff the command was successful. transport is a transport string such as
"websocket" or "obfs3|websocket"."""
f = fac_socket(facilitator_addr)
params = [("CLIENT", format_addr(client_addr))]
params.append(("TRANSPORT", transport))
try:
command, params = transact(f, "PUT", *params)
finally:
f.close()
return command == "OK"
def get_reg(facilitator_addr, proxy_addr, proxy_transport_list):
"""
Get a client registration for proxy proxy_addr from the
facilitator at facilitator_addr using a one-time
socket. proxy_transport_list is a list containing the transport names that
the flashproxy supports.
Returns a dict with keys "client", "client-transport", "relay",
and "relay-transport" if successful, or a dict with the key "client"
mapped to the value "" if there are no registrations available for
proxy_addr. Raises an exception otherwise."""
f = fac_socket(facilitator_addr)
# Form a list (in transact() format) with the transports that we
# should send to the facilitator. Then pass that list to the
# transact() function.
# For example, PROXY-TRANSPORT=obfs2 PROXY-TRANSPORT=obfs3.
transports = [("PROXY-TRANSPORT", tp) for tp in proxy_transport_list]
try:
command, params = transact(f, "GET", ("FROM", format_addr(proxy_addr)), *transports)
finally:
f.close()
response = {}
check_back_in = param_first("CHECK-BACK-IN", params)
if check_back_in is not None:
try:
float(check_back_in)
except ValueError:
raise ValueError("Facilitator returned non-numeric polling interval.")
response["check-back-in"] = check_back_in
if command == "NONE":
response["client"] = ""
return response
elif command == "OK":
client_spec = param_first("CLIENT", params)
client_transport = param_first("CLIENT-TRANSPORT", params)
relay_spec = param_first("RELAY", params)
relay_transport = param_first("RELAY-TRANSPORT", params)
if not client_spec:
raise ValueError("Facilitator did not return CLIENT")
if not client_transport:
raise ValueError("Facilitator did not return CLIENT-TRANSPORT")
if not relay_spec:
raise ValueError("Facilitator did not return RELAY")
if not relay_transport:
raise ValueError("Facilitator did not return RELAY-TRANSPORT")
# Check the syntax returned by the facilitator.
client = parse_addr_spec(client_spec)
relay = parse_addr_spec(relay_spec)
response["client"] = format_addr(client)
response["client-transport"] = client_transport
response["relay"] = format_addr(relay)
response["relay-transport"] = relay_transport
return response
else:
raise ValueError("Facilitator response was not \"OK\"")
def put_reg_proc(args, data):
"""Attempt to add a registration by running a program."""
p = subprocess.Popen(args, stdin=subprocess.PIPE)
stdout, stderr = p.communicate(data)
return p.returncode == 0
|