[tor-commits] [stem/master] Adding get_microdescriptors() method to the Controller

atagar at torproject.org atagar at torproject.org
Sun Mar 3 06:22:22 UTC 2013


commit 7f3f8722d99b2747cafd2247b14e362c743d5290
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Mar 2 22:16:20 2013 -0800

    Adding get_microdescriptors() method to the Controller
    
    Controller method to fetch all microdescriptors. This is modeled after its
    counterparts for server descriptors and network status documents. However, as
    mentioned in 'https://trac.torproject.org/8323', the controller interface
    presently lacks a method to get them.
    
    In the meantime we're reading them from disk.
---
 stem/control.py                  |   52 ++++++++++++++++++++++++++++++++++++++
 test/integ/control/controller.py |   20 ++++++++++++++
 2 files changed, 72 insertions(+), 0 deletions(-)

diff --git a/stem/control.py b/stem/control.py
index d1025b8..a342fd0 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -26,6 +26,7 @@ providing its own for interacting at a higher level.
     |- get_protocolinfo - information about the controller interface
     |
     |- get_microdescriptor - querying the microdescriptor for a relay
+    |- get_microdescriptors - provides all presently available microdescriptors
     |- get_server_descriptor - querying the server descriptor for a relay
     |- get_server_descriptors - provides all presently available server descriptors
     |- get_network_status - querying the router status entry for a relay
@@ -141,6 +142,7 @@ import threading
 import time
 
 import stem.descriptor.microdescriptor
+import stem.descriptor.reader
 import stem.descriptor.router_status_entry
 import stem.descriptor.server_descriptor
 import stem.exit_policy
@@ -999,6 +1001,56 @@ class Controller(BaseController):
       else:
         return default
 
+  def get_microdescriptors(self, default = UNDEFINED):
+    """
+    Provides an iterator for all of the microdescriptors that tor presently
+    knows about.
+
+    **Tor does not expose this information via the control protocol (`ticket
+    <https://trac.torproject.org/8323>`_).** Until it does this reads the
+    microdescriptors from disk, and hence won't work remotely or if we lack
+    read permissions.
+
+    :param list default: items to provide if the query fails
+
+    :returns: iterates over
+      :class:`~stem.descriptor.microdescriptor.Microdescriptor` for relays in
+      the tor network
+
+    :raises: :class:`stem.ControllerError` if unable to query tor and no
+      default was provided
+    """
+
+    try:
+      try:
+        data_directory = self.get_conf("DataDirectory")
+      except stem.ControllerError, exc:
+        raise stem.OperationFailed(message = "Unable to determine the data directory (%s)" % exc)
+
+      cached_descriptor_path = os.path.join(data_directory, "cached-microdescs")
+
+      if not os.path.exists(data_directory):
+        raise stem.OperationFailed(message = "Data directory reported by tor doesn't exist (%s)" % data_directory)
+      elif not os.path.exists(cached_descriptor_path):
+        raise stem.OperationFailed(message = "Data directory doens't contain cached microescriptors (%s)" % cached_descriptor_path)
+
+      with stem.descriptor.reader.DescriptorReader([cached_descriptor_path]) as reader:
+        for desc in reader:
+          # It shouldn't be possible for these to be something other than
+          # microdescriptors but as the saying goes: trust but verify.
+
+          if not isinstance(desc, stem.descriptor.microdescriptor.Microdescriptor):
+            raise stem.OperationFailed(message = "BUG: Descriptor reader provided non-microdescriptor content (%s)" % type(desc))
+
+          yield desc
+    except Exception, exc:
+      if default == UNDEFINED:
+        raise exc
+      else:
+        if default is not None:
+          for entry in default:
+            yield entry
+
   def get_server_descriptor(self, relay, default = UNDEFINED):
     """
     Provides the server descriptor for the relay with the given fingerprint or
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index 42006c4..b93b802 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -827,6 +827,26 @@ class TestController(unittest.TestCase):
 
       self.assertEqual(md_by_fingerprint, md_by_nickname)
 
+  def test_get_microdescriptors(self):
+    """
+    Fetches a few descriptors via the get_microdescriptors() method.
+    """
+
+    runner = test.runner.get_runner()
+
+    if test.runner.require_control(self):
+      return
+
+    with runner.get_tor_controller() as controller:
+      count = 0
+
+      for desc in controller.get_microdescriptors():
+        self.assertTrue(desc.onion_key is not None)
+
+        count += 1
+        if count > 10:
+          break
+
   def test_get_server_descriptor(self):
     """
     Basic checks for get_server_descriptor().



More information about the tor-commits mailing list