[tor-commits] r24278: {arm} Using circuit-status results to identify directory and clien (in arm/trunk: . src/interface/connections src/util)

Damian Johnson atagar1 at gmail.com
Tue Mar 1 03:42:14 UTC 2011


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



More information about the tor-commits mailing list