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