[tor-commits] r24526: {arm} Identifying connections we're providing a hidden service on (in arm/trunk/src: interface/connections util)

Damian Johnson atagar1 at gmail.com
Fri Apr 1 16:18:16 UTC 2011


Author: atagar
Date: 2011-04-01 16:18:15 +0000 (Fri, 01 Apr 2011)
New Revision: 24526

Modified:
   arm/trunk/src/interface/connections/connEntry.py
   arm/trunk/src/interface/connections/connPanel.py
   arm/trunk/src/util/connections.py
   arm/trunk/src/util/enum.py
   arm/trunk/src/util/torTools.py
Log:
Identifying connections we're providing a hidden service on and application it's attached to. This was tested by running a local HS via thttpd and using tor2web to download files from it.



Modified: arm/trunk/src/interface/connections/connEntry.py
===================================================================
--- arm/trunk/src/interface/connections/connEntry.py	2011-04-01 04:46:33 UTC (rev 24525)
+++ arm/trunk/src/interface/connections/connEntry.py	2011-04-01 16:18:15 UTC (rev 24526)
@@ -18,11 +18,11 @@
 #   Directory    Fetching tor consensus information.
 #   Control      Tor controller (arm, vidalia, etc).
 
-Category = enum.Enum("INBOUND", "OUTBOUND", "EXIT", "CIRCUIT", "DIRECTORY", "SOCKS", "CONTROL")
+Category = enum.Enum("INBOUND", "OUTBOUND", "EXIT", "CIRCUIT", "DIRECTORY", "SOCKS", "HIDDEN", "CONTROL")
 CATEGORY_COLOR = {Category.INBOUND: "green",      Category.OUTBOUND: "blue",
                   Category.EXIT: "red",           Category.CIRCUIT: "cyan",
-                  Category.SOCKS: "yellow",     Category.DIRECTORY: "magenta",
-                  Category.CONTROL: "red"}
+                  Category.DIRECTORY: "magenta",  Category.SOCKS: "yellow",
+                  Category.HIDDEN: "magenta",     Category.CONTROL: "red"}
 
 # static data for listing format
 # <src>  -->  <dst>  <etc><padding>
@@ -199,7 +199,7 @@
     self._possibleClient = True
     self._possibleDirectory = True
     
-    # attributes for SOCKS and CONTROL connections
+    # attributes for SOCKS, HIDDEN, and CONTROL connections
     self.appName = None
     self.appPid = None
     self.isAppResolving = False
@@ -209,6 +209,7 @@
     myDirPort = conn.getOption("DirPort")
     mySocksPort = conn.getOption("SocksPort", "9050")
     myCtlPort = conn.getOption("ControlPort")
+    myHiddenServicePorts = conn.getHiddenServicePorts()
     
     # the ORListenAddress can overwrite the ORPort
     listenAddr = conn.getOption("ORListenAddress")
@@ -220,6 +221,8 @@
       self.local.isORPort = True
     elif lPort == mySocksPort:
       self.baseType = Category.SOCKS
+    elif fPort in myHiddenServicePorts:
+      self.baseType = Category.HIDDEN
     elif lPort == myCtlPort:
       self.baseType = Category.CONTROL
     else:
@@ -288,7 +291,7 @@
     True if our display uses application information that hasn't yet been resolved.
     """
     
-    return self.appName == None and self.getType() in (Category.SOCKS, Category.CONTROL)
+    return self.appName == None and self.getType() in (Category.SOCKS, Category.HIDDEN, Category.CONTROL)
   
   def _getListingEntry(self, width, currentTime, listingType):
     entryType = self.getType()
@@ -428,7 +431,7 @@
     """
     
     # for applications show the command/pid
-    if self.getType() in (Category.SOCKS, Category.CONTROL):
+    if self.getType() in (Category.SOCKS, Category.HIDDEN, Category.CONTROL):
       displayLabel = ""
       
       if self.appName:
@@ -538,13 +541,23 @@
       # the source and destination addresses are both private, but that might
       # not be perfectly reliable either.
       
-      isExpansionType = not myType in (Category.SOCKS, Category.CONTROL)
+      isExpansionType = not myType in (Category.SOCKS, Category.HIDDEN, Category.CONTROL)
       
       if isExpansionType: srcAddress = myExternalIpAddr + localPort
       else: srcAddress = self.local.getIpAddr() + localPort
-      src = "%-21s" % srcAddress # ip:port = max of 21 characters
-      dst = "%-26s" % dstAddress # ip:port (xx) = max of 26 characters
       
+      if myType in (Category.SOCKS, Category.CONTROL):
+        # These categories are reordered later so the it's dst -> src rather
+        # than src -> dst. They are local connections so the dst lacks locale
+        # information, so swapping their respective widths to match column
+        # alignments.
+        
+        src = "%-26s" % srcAddress
+        dst = "%-21s" % dstAddress
+      else:
+        src = "%-21s" % srcAddress # ip:port = max of 21 characters
+        dst = "%-26s" % dstAddress # ip:port (xx) = max of 26 characters
+      
       usedSpace += len(src) + len(dst) # base data requires 47 characters
       
       # Showing the fingerprint (which has the width of 42) has priority over

Modified: arm/trunk/src/interface/connections/connPanel.py
===================================================================
--- arm/trunk/src/interface/connections/connPanel.py	2011-04-01 04:46:33 UTC (rev 24525)
+++ arm/trunk/src/interface/connections/connPanel.py	2011-04-01 16:18:15 UTC (rev 24526)
@@ -64,14 +64,14 @@
     # it changes.
     self._lastResourceFetch = -1
     
-    # resolver for the command/pid associated with SOCKS and CONTROL connections
+    # resolver for the command/pid associated with SOCKS, HIDDEN, and CONTROL connections
     self._appResolver = connections.AppResolver("arm")
     
     # rate limits appResolver queries to once per update
     self.appResolveSinceUpdate = False
     
     self._update()            # populates initial entries
-    self._resolveApps(False)  # resolves initial SOCKS and CONTROL applications
+    self._resolveApps(False)  # resolves initial applications
     
     # mark the initially exitsing connection uptimes as being estimates
     for entry in self._entries:
@@ -203,7 +203,7 @@
     for lineNum in range(scrollLoc, len(self._entryLines)):
       entryLine = self._entryLines[lineNum]
       
-      # if this is an unresolved SOCKS or CONTROL entry then queue up
+      # if this is an unresolved SOCKS, HIDDEN, or CONTROL entry then queue up
       # resolution for the applicaitions they belong to
       if isinstance(entryLine, connEntry.ConnectionLine) and entryLine.isUnresolvedApp():
         self._resolveApps()
@@ -325,8 +325,8 @@
   
   def _resolveApps(self, flagQuery = True):
     """
-    Triggers an asynchronous query for all unresolved SOCKS and CONTROL
-    entries.
+    Triggers an asynchronous query for all unresolved SOCKS, HIDDEN, and
+    CONTROL entries.
     
     Arguments:
       flagQuery - sets a flag to prevent further call from being respected
@@ -334,42 +334,46 @@
     """
     
     if self.appResolveSinceUpdate or not self._config["features.connection.resolveApps"]: return
+    unresolvedLines = [l for l in self._entryLines if isinstance(l, connEntry.ConnectionLine) and l.isUnresolvedApp()]
     
-    # fetch the unresolved SOCKS and CONTROL lines
-    unresolvedLines = []
+    # get the ports used for unresolved applications
+    appPorts = []
     
-    for line in self._entryLines:
-      if isinstance(line, connEntry.ConnectionLine) and line.isUnresolvedApp():
-        unresolvedLines.append(line)
+    for line in unresolvedLines:
+      appConn = line.local if line.getType() == connEntry.Category.HIDDEN else line.foreign
+      appPorts.append(appConn.getPort())
     
     # Queue up resolution for the unresolved ports (skips if it's still working
     # on the last query).
-    if not self._appResolver.isResolving:
-      self._appResolver.resolve([line.foreign.getPort() for line in unresolvedLines])
+    if appPorts and not self._appResolver.isResolving:
+      self._appResolver.resolve(appPorts)
     
+    # Fetches results. If the query finishes quickly then this is what we just
+    # asked for, otherwise these belong to an earlier resolution.
+    #
     # The application resolver might have given up querying (for instance, if
     # the lsof lookups aren't working on this platform or lacks permissions).
     # The isAppResolving flag lets the unresolved entries indicate if there's
     # a lookup in progress for them or not.
     
-    for line in unresolvedLines:
-      line.isAppResolving = self._appResolver.isResolving
-    
-    # Fetches results. If the query finishes quickly then this is what we just
-    # asked for, otherwise these belong to the last resolution.
     appResults = self._appResolver.getResults(0.2)
     
     for line in unresolvedLines:
-      linePort = line.foreign.getPort()
+      isLocal = line.getType() == connEntry.Category.HIDDEN
+      linePort = line.local.getPort() if isLocal else line.foreign.getPort()
       
       if linePort in appResults:
         # sets application attributes if there's a result with this as the
         # inbound port
         for inboundPort, outboundPort, cmd, pid in appResults[linePort]:
-          if linePort == inboundPort:
+          appPort = outboundPort if isLocal else inboundPort
+          
+          if linePort == appPort:
             line.appName = cmd
             line.appPid = pid
             line.isAppResolving = False
+      else:
+        line.isAppResolving = self._appResolver.isResolving
     
     if flagQuery:
       self.appResolveSinceUpdate = True

Modified: arm/trunk/src/util/connections.py
===================================================================
--- arm/trunk/src/util/connections.py	2011-04-01 04:46:33 UTC (rev 24525)
+++ arm/trunk/src/util/connections.py	2011-04-01 16:18:15 UTC (rev 24526)
@@ -659,7 +659,7 @@
       else: lsofArgs.append("-i tcp:%s" % port)
     
     if lsofArgs:
-      lsofResults = sysTools.call("lsof -n " + " ".join(lsofArgs))
+      lsofResults = sysTools.call("lsof -nP " + " ".join(lsofArgs))
     else: lsofResults = None
     
     if not lsofResults and self.failureCount != -1:

Modified: arm/trunk/src/util/enum.py
===================================================================
--- arm/trunk/src/util/enum.py	2011-04-01 04:46:33 UTC (rev 24525)
+++ arm/trunk/src/util/enum.py	2011-04-01 16:18:15 UTC (rev 24526)
@@ -32,7 +32,8 @@
   
   words = []
   for entry in label.split("_"):
-    if len(entry) == 1: words.append(entry.upper())
+    if len(entry) == 0: words.append("")
+    elif len(entry) == 1: words.append(entry.upper())
     else: words.append(entry[0].upper() + entry[1:].lower())
   
   return " ".join(words)

Modified: arm/trunk/src/util/torTools.py
===================================================================
--- arm/trunk/src/util/torTools.py	2011-04-01 04:46:33 UTC (rev 24525)
+++ arm/trunk/src/util/torTools.py	2011-04-01 16:18:15 UTC (rev 24526)
@@ -55,7 +55,7 @@
               "config/names", "info/names", "features/names", "events/names",
               "nsEntry", "descEntry", "address", "bwRate", "bwBurst",
               "bwObserved", "bwMeasured", "flags", "parsedVersion", "pid",
-              "pathPrefix", "startTime", "authorities", "circuits")
+              "pathPrefix", "startTime", "authorities", "circuits", "hsPorts")
 
 # 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
@@ -649,6 +649,16 @@
     
     return self._getRelayAttr("circuits", default)
   
+  def getHiddenServicePorts(self, default = []):
+    """
+    Provides the target ports hidden services are configured to use.
+    
+    Arguments:
+      default - value provided back if unable to query the hidden service ports
+    """
+    
+    return self._getRelayAttr("hsPorts", default)
+  
   def getMyNetworkStatus(self, default = None):
     """
     Provides the network status entry for this relay if available. This is
@@ -1691,6 +1701,31 @@
             
             path = tuple([hopEntry[1:41] for hopEntry in lineComp[2].split(",")])
             result.append((int(lineComp[0]), lineComp[1], lineComp[3][8:], path))
+      elif key == "hsPorts":
+        result = []
+        hsOptions = self.getOptionMap("HiddenServiceOptions")
+        
+        if hsOptions and "HiddenServicePort" in hsOptions:
+          for hsEntry in hsOptions["HiddenServicePort"]:
+            # hidden service port entries are of the form:
+            # VIRTPORT [TARGET]
+            # with the TARGET being an IP, port, or IP:Port. If the target port
+            # isn't defined then uses the VIRTPORT.
+            
+            hsPort = None
+            
+            if " " in hsEntry:
+              # parses the target, checking if it's a port or IP:Port combination
+              hsTarget = hsEntry.split(" ")[1]
+              
+              if ":" in hsTarget:
+                hsPort = hsTarget.split(":")[1] # target is the IP:Port
+              elif hsTarget.isdigit():
+                hsPort = hsTarget # target is just the port
+            else: hsPort = hsEntry # just has the virtual port
+            
+            if hsPort.isdigit():
+              result.append(hsPort)
       
       # cache value
       if result != None: self._cachedParam[key] = result



More information about the tor-commits mailing list