[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [arm/master] Using stem's ExitPolicy
commit 3232ce527da8b95cb78f3a52b19ad0634e1cd9da
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sun Jan 13 20:24:08 2013 -0800
Using stem's ExitPolicy
Dropping our ExitPolicy class in favor of stem's. Besides the class replacement
this also clears out a fair bit of our custom Controller. Stem provides caching
and an easy method to fetch a policy from descriptors.
The attempt by getRelayExitPolicy() to use router status entries to get an exit
policy has always been broken. 'GETINFO ns/*' gives v2 router status entries,
and the exit policy wasn't added until v3.
---
src/cli/connections/connEntry.py | 2 +-
src/cli/connections/connPanel.py | 5 +-
src/util/torTools.py | 358 ++------------------------------------
3 files changed, 21 insertions(+), 344 deletions(-)
diff --git a/src/cli/connections/connEntry.py b/src/cli/connections/connEntry.py
index bec5aa9..5b2eda5 100644
--- a/src/cli/connections/connEntry.py
+++ b/src/cli/connections/connEntry.py
@@ -714,7 +714,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
exitPolicy = conn.getRelayExitPolicy(fingerprint)
- if exitPolicy: policyLabel = exitPolicy.getSummary()
+ if exitPolicy: policyLabel = exitPolicy.summary()
else: policyLabel = "unknown"
dirPortLabel = "" if dirPort == "0" else "dirport: %s" % dirPort
diff --git a/src/cli/connections/connPanel.py b/src/cli/connections/connPanel.py
index 9f3a5ed..79bc3b4 100644
--- a/src/cli/connections/connPanel.py
+++ b/src/cli/connections/connPanel.py
@@ -207,8 +207,11 @@ class ConnectionPanel(panel.Panel, threading.Thread):
True if exit connections are permissable, false otherwise.
"""
+ if not torTools.getOption("ORPort", None):
+ return False # no ORPort
+
policy = torTools.getConn().getExitPolicy()
- return policy and policy.isExitingAllowed()
+ return policy and policy.is_exiting_allowed()
def showSortDialog(self):
"""
diff --git a/src/util/torTools.py b/src/util/torTools.py
index c050ea6..c8d12c5 100644
--- a/src/util/torTools.py
+++ b/src/util/torTools.py
@@ -58,9 +58,6 @@ REQ_EVENTS = {"NOTICE": "this will be unable to detect when tor is shut down",
"NS": "information related to the consensus will grow stale",
"NEWCONSENSUS": "information related to the consensus will grow stale"}
-# ip address ranges substituted by the 'private' keyword
-PRIVATE_IP_RANGES = ("0.0.0.0/8", "169.254.0.0/16", "127.0.0.0/8", "192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12")
-
# This prevents controllers from spawning worker threads (and by extension
# notifying status listeners). This is important when shutting down to prevent
# rogue threads from being alive during shutdown.
@@ -286,10 +283,6 @@ class Controller:
# is.
self._notificationQueue = Queue.Queue()
- self._exitPolicyChecker = None
- self._isExitingAllowed = False
- self._exitPolicyLookupCache = {} # mappings of ip/port tuples to if they were accepted by the policy or not
-
# Logs issues and notices when fetching the path prefix if true. This is
# only done once for the duration of the application to avoid pointless
# messages.
@@ -333,10 +326,6 @@ class Controller:
self._consensusLookupCache = {}
self._descriptorLookupCache = {}
- self._exitPolicyChecker = self.getExitPolicy()
- self._isExitingAllowed = self._exitPolicyChecker.isExitingAllowed()
- self._exitPolicyLookupCache = {}
-
self._status = State.INIT
self._statusTime = time.time()
@@ -495,12 +484,6 @@ class Controller:
if not self.isAlive():
raise stem.SocketClosed()
- # clears our exit policy chache if it's changing
- if "exitpolicy" in [k.lower() for (k, v) in paramList]:
- self._exitPolicyChecker = self.getExitPolicy()
- self._isExitingAllowed = self._exitPolicyChecker.isExitingAllowed()
- self._exitPolicyLookupCache = {}
-
self.controller.set_options(paramList, isReset)
except stem.SocketClosed, exc:
self.close()
@@ -790,19 +773,16 @@ class Controller:
result = False
if self.isAlive():
- # query the policy if it isn't yet cached
- if not (ipAddress, port) in self._exitPolicyLookupCache:
- # If we allow any exiting then this could be relayed DNS queries,
- # otherwise the policy is checked. Tor still makes DNS connections to
- # test when exiting isn't allowed, but nothing is relayed over them.
- # I'm registering these as non-exiting to avoid likely user confusion:
- # https://trac.torproject.org/projects/tor/ticket/965
-
- if self._isExitingAllowed and port == "53": isAccepted = True
- else: isAccepted = self._exitPolicyChecker.check(ipAddress, port)
- self._exitPolicyLookupCache[(ipAddress, port)] = isAccepted
+ # If we allow any exiting then this could be relayed DNS queries,
+ # otherwise the policy is checked. Tor still makes DNS connections to
+ # test when exiting isn't allowed, but nothing is relayed over them.
+ # I'm registering these as non-exiting to avoid likely user confusion:
+ # https://trac.torproject.org/projects/tor/ticket/965
+
+ our_policy = self.getExitPolicy()
- result = self._exitPolicyLookupCache[(ipAddress, port)]
+ if our_policy and our_policy.is_exiting_allowed() and port == "53": result = True
+ else: result = our_policy and our_policy.can_exit_to(ipAddress, port)
self.connLock.release()
@@ -818,35 +798,10 @@ class Controller:
result = None
if self.isAlive():
- if self.getOption("ORPort", None):
- policyEntries = []
- for exitPolicy in self.getOption("ExitPolicy", [], True):
- policyEntries += [policy.strip() for policy in exitPolicy.split(",")]
-
- # appends the default exit policy
- defaultExitPolicy = self.getInfo("exit-policy/default", None)
-
- if defaultExitPolicy:
- policyEntries += defaultExitPolicy.split(",")
-
- # construct the policy chain backwards
- policyEntries.reverse()
-
- for entry in policyEntries:
- result = ExitPolicy(entry, result)
-
- # Checks if we are rejecting private connections. If set, this appends
- # 'reject private' and 'reject <my ip>' to the start of our policy chain.
- isPrivateRejected = self.getOption("ExitPolicyRejectPrivate", True)
-
- if isPrivateRejected:
- myAddress = self.getInfo("address", None)
- if myAddress: result = ExitPolicy("reject %s" % myAddress, result)
-
- result = ExitPolicy("reject private", result)
- else:
- # no ORPort is set so all relaying is disabled
- result = ExitPolicy("reject *:*", None)
+ try:
+ result = self.controller.get_exit_policy(param)
+ except:
+ pass
self.connLock.release()
@@ -973,7 +928,7 @@ class Controller:
return result
- def getRelayExitPolicy(self, relayFingerprint, allowImprecision = True):
+ def getRelayExitPolicy(self, relayFingerprint):
"""
Provides the ExitPolicy instance associated with the given relay. The tor
consensus entries don't indicate if private addresses are rejected or
@@ -983,7 +938,6 @@ class Controller:
Arguments:
relayFingerprint - fingerprint of the relay
- allowImprecision - make use of consensus policies as a fallback
"""
self.connLock.acquire()
@@ -991,55 +945,10 @@ class Controller:
result = None
if self.isAlive():
# attempts to fetch the policy via the descriptor
- descriptor = self.getDescriptorEntry(relayFingerprint)
+ descriptor = self.controller.get_server_descriptor(relayFingerprint, None)
if descriptor:
- exitPolicyEntries = []
- for line in descriptor.split("\n"):
- if line.startswith("accept ") or line.startswith("reject "):
- exitPolicyEntries.append(line)
-
- # construct the policy chain
- for entry in reversed(exitPolicyEntries):
- result = ExitPolicy(entry, result)
- elif allowImprecision:
- # Falls back to using the consensus entry, which is both less precise
- # and unavailable with older tor versions. This assumes that the relay
- # has ExitPolicyRejectPrivate set and won't include address-specific
- # policies.
-
- consensusLine, relayAddress = None, None
-
- nsEntry = self.getConsensusEntry(relayFingerprint)
- if nsEntry:
- for line in nsEntry.split("\n"):
- if line.startswith("r "):
- # fetch the relay's public address, which we'll need for the
- # ExitPolicyRejectPrivate policy entry
-
- lineComp = line.split(" ")
- if len(lineComp) >= 7 and connections.isValidIpAddress(lineComp[6]):
- relayAddress = lineComp[6]
- elif line.startswith("p "):
- consensusLine = line
- break
-
- if consensusLine:
- acceptance, ports = consensusLine.split(" ")[1:]
-
- # starts with a reject-all for whitelists and accept-all for blacklists
- if acceptance == "accept":
- result = ExitPolicy("reject *:*", None)
- else:
- result = ExitPolicy("accept *:*", None)
-
- # adds each of the ports listed in the consensus
- for port in reversed(ports.split(",")):
- result = ExitPolicy("%s *:%s" % (acceptance, port), result)
-
- # adds ExitPolicyRejectPrivate since this is the default
- if relayAddress: result = ExitPolicy("reject %s" % relayAddress, result)
- result = ExitPolicy("reject private", result)
+ result = descriptor.exit_policy
self.connLock.release()
@@ -1855,238 +1764,3 @@ class Controller:
self.connLock.release()
-class ExitPolicyIterator:
- """
- Basic iterator for cycling through ExitPolicy entries.
- """
-
- def __init__(self, head):
- self.head = head
-
- def next(self):
- if self.head:
- lastHead = self.head
- self.head = self.head.nextRule
- return lastHead
- else: raise StopIteration
-
-class ExitPolicy:
- """
- Single rule from the user's exit policy. These are chained together to form
- complete policies.
- """
-
- def __init__(self, ruleEntry, nextRule):
- """
- Exit policy rule constructor.
-
- Arguments:
- ruleEntry - tor exit policy rule (for instance, "reject *:135-139")
- nextRule - next rule to be checked when queries don't match this policy
- """
-
- # cached summary string
- self.summaryStr = None
-
- # sanitize the input a bit, cleaning up tabs and stripping quotes
- ruleEntry = ruleEntry.replace("\\t", " ").replace("\"", "")
-
- self.ruleEntry = ruleEntry
- self.nextRule = nextRule
- self.isAccept = ruleEntry.startswith("accept")
-
- # strips off "accept " or "reject " and extra spaces
- ruleEntry = ruleEntry[7:].replace(" ", "")
-
- # split ip address (with mask if provided) and port
- if ":" in ruleEntry: entryIp, entryPort = ruleEntry.split(":", 1)
- else: entryIp, entryPort = ruleEntry, "*"
-
- # sets the ip address component
- self.isIpWildcard = entryIp == "*" or entryIp.endswith("/0")
-
- # checks for the private alias (which expands this to a chain of entries)
- if entryIp.lower() == "private":
- entryIp = PRIVATE_IP_RANGES[0]
-
- # constructs the chain backwards (last first)
- lastHop = self.nextRule
- prefix = "accept " if self.isAccept else "reject "
- suffix = ":" + entryPort
- for addr in PRIVATE_IP_RANGES[-1:0:-1]:
- lastHop = ExitPolicy(prefix + addr + suffix, lastHop)
-
- self.nextRule = lastHop # our next hop is the start of the chain
-
- if "/" in entryIp:
- ipComp = entryIp.split("/", 1)
- self.ipAddress = ipComp[0]
- self.ipMask = int(ipComp[1])
- else:
- self.ipAddress = entryIp
- self.ipMask = 32
-
- # constructs the binary address just in case of comparison with a mask
- if self.ipAddress != "*":
- self.ipAddressBin = ""
- for octet in self.ipAddress.split("."):
- # Converts the int to a binary string, padded with zeros. Source:
- # http://www.daniweb.com/code/snippet216539.html
- self.ipAddressBin += "".join([str((int(octet) >> y) & 1) for y in range(7, -1, -1)])
- else:
- self.ipAddressBin = "0" * 32
-
- # sets the port component
- self.minPort, self.maxPort = 0, 0
- self.isPortWildcard = entryPort == "*"
-
- if entryPort != "*":
- if "-" in entryPort:
- portComp = entryPort.split("-", 1)
- self.minPort = int(portComp[0])
- self.maxPort = int(portComp[1])
- else:
- self.minPort = int(entryPort)
- self.maxPort = int(entryPort)
-
- # if both the address and port are wildcards then we're effectively the
- # last entry so cut off the remaining chain
- if self.isIpWildcard and self.isPortWildcard:
- self.nextRule = None
-
- def isExitingAllowed(self):
- """
- Provides true if the policy allows exiting whatsoever, false otherwise.
- """
-
- if self.isAccept: return True
- elif self.isIpWildcard and self.isPortWildcard: return False
- elif not self.nextRule: return False # fell off policy (shouldn't happen)
- else: return self.nextRule.isExitingAllowed()
-
- def check(self, ipAddress, port):
- """
- Checks if the rule chain allows exiting to this address, returning true if
- so and false otherwise.
- """
-
- port = int(port)
-
- # does the port check first since comparing ip masks is more work
- isPortMatch = self.isPortWildcard or (port >= self.minPort and port <= self.maxPort)
-
- if isPortMatch:
- isIpMatch = self.isIpWildcard or self.ipAddress == ipAddress
-
- # expands the check to include the mask if it has one
- if not isIpMatch and self.ipMask != 32:
- inputAddressBin = ""
- for octet in ipAddress.split("."):
- inputAddressBin += "".join([str((int(octet) >> y) & 1) for y in range(7, -1, -1)])
-
- isIpMatch = self.ipAddressBin[:self.ipMask] == inputAddressBin[:self.ipMask]
-
- if isIpMatch: return self.isAccept
-
- # our policy doesn't concern this address, move on to the next one
- if self.nextRule: return self.nextRule.check(ipAddress, port)
- else: return True # fell off the chain without a conclusion (shouldn't happen...)
-
- def getSummary(self):
- """
- Provides a summary description of the policy chain similar to the
- consensus. This excludes entries that don't cover all ips, and is either
- a whitelist or blacklist policy based on the final entry. For instance...
- accept 80, 443 # just accepts ports 80/443
- reject 1-1024, 5555 # just accepts non-privilaged ports, excluding 5555
- """
-
- if not self.summaryStr:
- # determines if we're a whitelist or blacklist
- isWhitelist = False # default in case we don't have a catch-all policy at the end
-
- for rule in self:
- if rule.isIpWildcard and rule.isPortWildcard:
- isWhitelist = not rule.isAccept
- break
-
- # Iterates over the rules and adds the the ports we'll return (ie, allows
- # if a whitelist and rejects if a blacklist). Reguardless of a port's
- # allow/reject policy, all further entries with that port are ignored since
- # policies respect the first matching rule.
-
- displayPorts, skipPorts = [], []
-
- for rule in self:
- if not rule.isIpWildcard: continue
-
- if rule.minPort == rule.maxPort:
- portRange = [rule.minPort]
- else:
- portRange = range(rule.minPort, rule.maxPort + 1)
-
- for port in portRange:
- if port in skipPorts: continue
-
- # if accept + whitelist or reject + blacklist then add
- if rule.isAccept == isWhitelist:
- displayPorts.append(port)
-
- # all further entries with this port are to be ignored
- skipPorts.append(port)
-
- # gets a list of the port ranges
- if displayPorts:
- displayRanges, tmpRange = [], []
- displayPorts.sort()
- displayPorts.append(None) # ending item to include last range in loop
-
- for port in displayPorts:
- if not tmpRange or tmpRange[-1] + 1 == port:
- tmpRange.append(port)
- else:
- if len(tmpRange) > 1:
- displayRanges.append("%i-%i" % (tmpRange[0], tmpRange[-1]))
- else:
- displayRanges.append(str(tmpRange[0]))
-
- tmpRange = [port]
- else:
- # everything for the inverse
- isWhitelist = not isWhitelist
- displayRanges = ["1-65535"]
-
- # constructs the summary string
- labelPrefix = "accept " if isWhitelist else "reject "
-
- self.summaryStr = (labelPrefix + ", ".join(displayRanges)).strip()
-
- return self.summaryStr
-
- def __iter__(self):
- return ExitPolicyIterator(self)
-
- def __str__(self):
- # This provides the actual policy rather than the entry used to construct
- # it so the 'private' keyword is expanded.
-
- acceptanceLabel = "accept" if self.isAccept else "reject"
-
- if self.isIpWildcard:
- ipLabel = "*"
- elif self.ipMask != 32:
- ipLabel = "%s/%i" % (self.ipAddress, self.ipMask)
- else: ipLabel = self.ipAddress
-
- if self.isPortWildcard:
- portLabel = "*"
- elif self.minPort != self.maxPort:
- portLabel = "%i-%i" % (self.minPort, self.maxPort)
- else: portLabel = str(self.minPort)
-
- myPolicy = "%s %s:%s" % (acceptanceLabel, ipLabel, portLabel)
-
- if self.nextRule:
- return myPolicy + ", " + str(self.nextRule)
- else: return myPolicy
-
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits