[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [torbel/master] Specify and implement narrow exit policy check.
commit c1092dcf4b4bc5e95d2d37d69feb3a2c62cd85ab
Author: Harry Bock <hbock@xxxxxxxxxxx>
Date: Fri Sep 3 18:53:14 2010 -0400
Specify and implement narrow exit policy check.
A "narrow" port is a port that a router explicitly rejects
our active tester but could possibly accept other traffic;
e.g., from an exit enclave.
Add a NarrowPorts data field in TorBEL exports. Implement
the check, export, and import functionality.
---
doc/data-spec.txt | 5 ++++
query.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++--
router.py | 30 ++++++++++++++++++++++--
3 files changed, 93 insertions(+), 6 deletions(-)
diff --git a/doc/data-spec.txt b/doc/data-spec.txt
index 58dbb7e..c5f4014 100644
--- a/doc/data-spec.txt
+++ b/doc/data-spec.txt
@@ -43,6 +43,11 @@ Status: Draft
* FailedPorts: A list of ports that were not reachable by TorBEL, either
due to its exit policy or a temporary failure, when it
was last tested.
+ * NarrowPorts: A list of ports that were not reachable by TorBEL due to
+ a narrow exit policy that does not include the TorBEL
+ test server. Consumers should treat NarrowPorts as
+ working according to the currently published exit policy
+ for the router.
The following data are supplied for convenience to the consumer:
diff --git a/query.py b/query.py
index 2f223d7..f1d0046 100644
--- a/query.py
+++ b/query.py
@@ -44,6 +44,11 @@ class Router:
self.exit_policy = self._build_exit_policy(data["ExitPolicy"])
self.working_ports = data["WorkingPorts"]
self.failed_ports = data["FailedPorts"]
+ self.narrow_ports = data["NarrowPorts"]
+
+ @property
+ def exit_policy_string(self):
+ return "; ".join(map(str, self.exit_policy))
def _build_exit_policy(self, policy_list):
return [ExitPolicyRule(line) for line in policy_list]
@@ -58,7 +63,24 @@ class Router:
# will be accepted."
return True
- def will_exit_to(self, ip, port):
+ def is_narrow_exit(self, ip, port):
+ """ Returns True if this router accepts exit traffic to port
+ on some IP addresses but rejects traffic to ip. This can be
+ used to detect exit enclaves. """
+ can_accept = False
+ for line in self.exit_policy:
+ if line.reject and line.network == _nulladdr and (port >= line.port_low and port <= line.port_high):
+ can_accept = False
+ break
+
+ if line.accept and (port >= line.port_low and port <= line.port_high):
+ can_accept = True
+ break
+
+ match = self.exit_policy_match(ip, port)
+ return can_accept and not match
+
+ def will_exit_to(self, ip, port, check_narrow_policy = True):
# FIXME: In the case that TorBEL has not actually tested this exit,
# we will trust the router's exit policy. This may or may not be
# the Right Thing To Do.
@@ -69,7 +91,7 @@ class Router:
return True
elif port in self.failed_ports:
return False
-
+ # Treat NarrowPorts the same as not tested.
return self.exit_policy_match(ip, port)
def will_exit_to_ports(self, ip, port_list):
@@ -87,6 +109,7 @@ class ParseError(Exception):
_exitline = re.compile(r"^(accept|reject) (.+)")
_portspec = re.compile(r"^\d{1,5}|\d{1,5}-\d{1,5}$")
_addrspec = re.compile(r"^\[?([\d:.]+)\]?(/[\d:.]+)?$")
+_nulladdr = ipaddr.IPAddress("0.0.0.0")
class ExitPolicyRule:
def __init__(self, line):
self.port_low, self.port_high = -1, -1
@@ -139,6 +162,31 @@ class ExitPolicyRule:
return True
elif self.address and ipaddr.IPAddress(ip) == self.address:
return True
+
+ def __str__(self):
+ # 0.0.0.0/0.0.0.0 => *
+ if self.network == _nulladdr:# and line.netmask == 0:
+ ip = "*"
+ else:
+ # ipaddr.IPv4Network always converts to CIDR.
+ if self.network:
+ ip = str(self.network)
+ else:
+ ip = str(self.address)
+
+ # Convert 0-65535 to *
+ if self.port_low == 0 and self.port_high == 0xffff:
+ port = "*"
+ # Use 8 instead of 8-8
+ elif self.port_low == self.port_high:
+ port = str(self.port_low)
+ else:
+ port = "%d-%d" % (self.port_low, self.port_high)
+
+ if self.accept:
+ return "accept " + ip + ":" + port
+ else:
+ return "reject " + ip + ":" + port
class ExitList:
def __init__(self, filename, status_filename = None):
@@ -248,7 +296,8 @@ class ExitList:
"InConsensus": r[4] == "True",
"ExitPolicy": r[5].split(";"),
"WorkingPorts": port_list_from_string(r[6]),
- "FailedPorts": port_list_from_string(r[7])
+ "FailedPorts": port_list_from_string(r[7]),
+ "NarrowPorts": port_list_from_string(r[8]),
}
self.add_record(data)
@@ -349,6 +398,15 @@ if __name__ == "__main__":
output.close()
+ elif command == "test":
+ port = int(sys.argv[2])
+ e = ExitList("torbel_export.csv")
+ print "start"
+ count = 0
+ for r in e.cache_ip.itervalues():
+ if r.is_narrow_exit(ipaddr.IPAddress("131.128.160.242"), port):
+ count += 1
+ print count, r.nickname, r.exit_policy_string
else:
usage()
sys.exit(1)
diff --git a/router.py b/router.py
index c77e178..bc71e86 100644
--- a/router.py
+++ b/router.py
@@ -16,6 +16,7 @@ class RouterRecord(_OldRouterClass):
self.test_ports = set(ports)
self.working_ports = set()
self.failed_ports = set()
+ self.narrow_ports = set()
self.circ_failed = False
def passed(self, port):
@@ -24,6 +25,9 @@ class RouterRecord(_OldRouterClass):
def failed(self, port):
self.failed_ports.add(port)
+ def narrow(self, port):
+ self.narrow_ports.add(port)
+
def start(self):
self.start_time = time.time()
return self
@@ -33,7 +37,8 @@ class RouterRecord(_OldRouterClass):
return self
def is_complete(self):
- return self.test_ports <= (self.working_ports | self.failed_ports)
+ return self.test_ports <= \
+ (self.working_ports | self.failed_ports | self.narrow_ports)
def __init__(self, *args, **kwargs):
_OldRouterClass.__init__(self, *args, **kwargs)
@@ -68,6 +73,23 @@ class RouterRecord(_OldRouterClass):
return len(self.exitpolicy) == 1 and \
(ep.ip, ep.netmask, ep.port_low, ep.port_high) == (0, 0, 0, 0xffff)
+ def is_narrow_exit(self, ip, port):
+ """ Returns True if this router accepts exit traffic to port
+ on some IP addresses but rejects traffic to ip. This can be
+ used to detect exit enclaves. """
+ can_accept = False
+ for line in self.exitpolicy:
+ if not line.match and line.ip == 0 and \
+ (port >= line.port_low and port <= line.port_high):
+ can_accept = False
+ break
+
+ if line.match and (port >= line.port_low and port <= line.port_high):
+ can_accept = True
+ break
+
+ return can_accept and not self.will_exit_to(ip, port)
+
def should_export(self):
""" Returns True if we have found working exit ports, or if we
have not found working test ports, if this router has a non-reject-all
@@ -171,7 +193,8 @@ class RouterRecord(_OldRouterClass):
"LastTestedTimestamp": int(self.last_test.end_time),
"ExitPolicy": self.exit_policy_list(),
"WorkingPorts": list(self.last_test.working_ports),
- "FailedPorts": list(self.last_test.failed_ports) }
+ "FailedPorts": list(self.last_test.failed_ports),
+ "NarrowPorts": list(self.last_test.narrow_ports) }
def export_csv(self, out):
""" Export record in CSV format, given a Python csv.writer instance. """
@@ -186,7 +209,8 @@ class RouterRecord(_OldRouterClass):
not self.stale, # InConsensus
self.exit_policy_string(), # ExitPolicy
list(self.last_test.working_ports), # WorkingPorts
- list(self.last_test.failed_ports)]) # FailedPorts
+ list(self.last_test.failed_ports), # FailedPorts
+ list(self.last_test.narrow_ports)]) # NarrowPorts
def __str__(self):
return "%s (%s)" % (self.idhex, self.nickname)
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits