Author: atagar Date: 2011-03-01 03:42:13 +0000 (Tue, 01 Mar 2011) New Revision: 24278
Modified: arm/trunk/README arm/trunk/src/interface/connections/connPanel.py arm/trunk/src/interface/connections/listings.py arm/trunk/src/util/torTools.py Log: Using circuit-status results to identify directory and client circuits. This is using a far better implementation than the previous connection panel from a perspective of performance, accuracy, and code cleanliness. Other notes... - The previous function for fetching used guards is replaced with a getter function for the circuits. - I'm no longer using our authorities to identify directory connections (this was a stupid hack for several reasons, foremost being that it didn't identify mirrors, the harded listings get stale, and authorities can act as a normal relay too). I'm keeping that code for now in case it comes in handy later.
Modified: arm/trunk/README =================================================================== --- arm/trunk/README 2011-02-28 17:39:55 UTC (rev 24277) +++ arm/trunk/README 2011-03-01 03:42:13 UTC (rev 24278) @@ -111,7 +111,6 @@ ChangeLog - revision history LICENSE - copy of the gpl v3 README - um... guess you figured this one out - TODO - known issues, future plans, etc setup.py - distutils installation script for arm
src/
Modified: arm/trunk/src/interface/connections/connPanel.py =================================================================== --- arm/trunk/src/interface/connections/connPanel.py 2011-02-28 17:39:55 UTC (rev 24277) +++ arm/trunk/src/interface/connections/connPanel.py 2011-03-01 03:42:13 UTC (rev 24278) @@ -187,6 +187,10 @@ if self._lastResourceFetch != currentResolutionCount: self.valsLock.acquire() currentConnections = connResolver.getConnections() + + # Replacement listing of connections. We first populate it with any of + # our old entries in currentConnections, then add new ConnectionEntries + # for whatever remains. newConnections = []
# preserves any ConnectionEntries they already exist
Modified: arm/trunk/src/interface/connections/listings.py =================================================================== --- arm/trunk/src/interface/connections/listings.py 2011-02-28 17:39:55 UTC (rev 24277) +++ arm/trunk/src/interface/connections/listings.py 2011-03-01 03:42:13 UTC (rev 24278) @@ -17,10 +17,10 @@
# TODO: add recognizing of CLIENT connection type DestAttr = enum.Enum("NONE", "LOCALE", "HOSTNAME") -Category = enum.Enum("INBOUND", "OUTBOUND", "EXIT", "SOCKS", "CLIENT", "DIRECTORY", "CONTROL") -CATEGORY_COLOR = {Category.INBOUND: "green", Category.OUTBOUND: "blue", - Category.EXIT: "red", Category.SOCKS: "cyan", - Category.CLIENT: "cyan", Category.DIRECTORY: "magenta", +Category = enum.Enum("INBOUND", "OUTBOUND", "EXIT", "CLIENT", "APPLICATION", "DIRECTORY", "CONTROL") +CATEGORY_COLOR = {Category.INBOUND: "green", Category.OUTBOUND: "blue", + Category.EXIT: "red", Category.CLIENT: "cyan", + Category.APPLICATION: "yellow", Category.DIRECTORY: "magenta", Category.CONTROL: "red"}
# static data for listing format @@ -121,6 +121,15 @@ self._labelCache = "" self._labelCacheArgs = (None, None)
+ # True if the connection has matched the properties of a client/directory + # connection every time we've checked. The criteria we check is... + # client - first hop in an established circuit + # directory - matches an established single-hop circuit (probably a + # directory mirror) + + self._possibleClient = True + self._possibleDirectory = True + conn = torTools.getConn() myOrPort = conn.getOption("ORPort") myDirPort = conn.getOption("DirPort") @@ -137,7 +146,7 @@ self.baseType = Category.INBOUND self.local.isORPort = True elif lPort == mySocksPort: - self.baseType = Category.SOCKS + self.baseType = Category.APPLICATION elif lPort == myCtlPort: self.baseType = Category.CONTROL elif (fIpAddr, fPort) in myAuthorities: @@ -153,18 +162,53 @@ """
if self.baseType == Category.OUTBOUND: - # Currently the only non-static categories are OUTBOUND vs EXIT (since - # this depends on the current consensus). The exitability and - # fingerprints are both cached by the torTools util making this a quick - # lookup. + # Currently the only non-static categories are OUTBOUND vs... + # - EXIT since this depends on the current consensus + # - CLIENT if this is likely to belong to our guard usage + # - DIRECTORY if this is a single-hop circuit (directory mirror?) + # + # The exitability, circuits, and fingerprints are all cached by the + # torTools util keeping this a quick lookup.
conn = torTools.getConn() - isKnownRelay = self.foreign.getFingerprint() != "UNKNOWN" - isExitingAllowed = conn.isExitingAllowed(self.foreign.getIpAddr(), self.foreign.getPort()) - isExitConnection = isExitingAllowed and not isKnownRelay + destFingerprint = self.foreign.getFingerprint()
- return Category.EXIT if isExitConnection else Category.OUTBOUND - else: return self.baseType + if destFingerprint == "UNKNOWN": + # Not a known relay. This might be an exit connection. + + if conn.isExitingAllowed(self.foreign.getIpAddr(), self.foreign.getPort()): + return Category.EXIT + elif self._possibleClient or self._possibleDirectory: + # This belongs to a known relay. If we haven't eliminated ourselves as + # a possible client or directory connection then check if it still + # holds true. + + myCircuits = conn.getCircuits() + + if self._possibleClient: + # Checks that this belongs to the first hop in a circuit that's + # either unestablished or longer than a single hop (ie, anything but + # a built 1-hop connection since those are most likely a directory + # mirror). + + for status, _, path in myCircuits: + if path[0] == destFingerprint and (status != "BUILT" or len(path) > 1): + return Category.CLIENT # matched a probable guard connection + + # fell through, we can eliminate ourselves as a guard in the future + self._possibleClient = False + + if self._possibleDirectory: + # Checks if we match a built, single hop circuit. + + for status, _, path in myCircuits: + if path[0] == destFingerprint and status == "BUILT" and len(path) == 1: + return Category.DIRECTORY + + # fell through, eliminate ourselves as a directory connection + self._possibleDirectory = False + + return self.baseType
def getDestinationLabel(self, maxLength, extraAttr=DestAttr.NONE): """
Modified: arm/trunk/src/util/torTools.py =================================================================== --- arm/trunk/src/util/torTools.py 2011-02-28 17:39:55 UTC (rev 24277) +++ arm/trunk/src/util/torTools.py 2011-03-01 03:42:13 UTC (rev 24278) @@ -55,7 +55,7 @@ "config/names", "info/names", "features/names", "events/names", "nsEntry", "descEntry", "address", "bwRate", "bwBurst", "bwObserved", "bwMeasured", "flags", "pid", "pathPrefix", - "startTime", "authorities", "usedGuards") + "startTime", "authorities", "circuits")
# Tor has a couple messages (in or/router.c) for when our ip address changes: # "Our IP Address has changed from <previous> to <current>; rebuilding @@ -603,6 +603,17 @@
if raisedExc: raise raisedExc
+ def getCircuits(self, default = []): + """ + This provides a list with tuples of the form: + (status, purpose, (fingerprint1, fingerprint2...)) + + Arguments: + default - value provided back if unable to query the circuit-status + """ + + return self._getRelayAttr("circuits", default) + def getMyNetworkStatus(self, default = None): """ Provides the network status entry for this relay if available. This is @@ -691,16 +702,6 @@
return self._getRelayAttr("flags", default)
- def getMyUsedGuards(self, default = None): - """ - Provides the guards that we're currently using. - - Arguments: - default - results if the query fails - """ - - return self._getRelayAttr("usedGuards", default) - def getMyPid(self): """ Provides the pid of the attached tor process (None if no controller exists @@ -1263,7 +1264,7 @@ # since it uses circuit-status results. self._fingerprintsAttachedCache = None
- self._cachedParam["usedGuards"] = None + self._cachedParam["circuits"] = None
def buildtimeout_set_event(self, event): self._updateHeartbeat() @@ -1586,23 +1587,28 @@ locationComp = entry.split()[-2] # address:port component result.append(tuple(locationComp.split(":", 1))) else: result = list(DIR_SERVERS) - elif key == "usedGuards": - # Checks our circuit-status entry and provides the first hops. Results - # tend to be one or three hops, for instance: - # 91 BUILT $E4AE6E2FE320FBBD31924E8577F3289D4BE0B4AD=Qwerty PURPOSE=GENERAL + elif key == "circuits": + # Parses our circuit-status results, for instance + # 91 BUILT $E4AE6E2FE320FBBD31924E8577F3289D4BE0B4AD=Qwerty PURPOSE=GENERAL + # would belong to a single hop circuit, most likely fetching the + # consensus via a directory mirror. circStatusResults = self.getInfo("circuit-status")
if circStatusResults == "": - result = [] # we don't have any client circuits + result = [] # we don't have any circuits elif circStatusResults != None: result = []
for line in circStatusResults.split("\n"): - fpStart = line.find("$") - fpEnd = line.find("=", fpStart) - guardFp = line[fpStart + 1:fpEnd] + # appends a tuple with the (status, purpose, path) + lineComp = line.split(" ")
- if not guardFp in result: result.append(guardFp) + # skips blank lines and circuits without a path, for instance: + # 5 LAUNCHED PURPOSE=TESTING + if len(lineComp) < 4: continue + + path = tuple([hopEntry[1:41] for hopEntry in lineComp[2].split(",")]) + result.append((lineComp[1], lineComp[3], path))
# cache value if result != None: self._cachedParam[key] = result