[tor-commits] [stem/master] Using 'GETINFO md/all' to get microdescriptors if available

atagar at torproject.org atagar at torproject.org
Tue Jul 31 20:14:24 UTC 2018


commit ac2b277a9b09451c0270d4397ab96087ce2cbaad
Author: Damian Johnson <atagar at torproject.org>
Date:   Tue Jul 31 12:45:27 2018 -0700

    Using 'GETINFO md/all' to get microdescriptors if available
    
    Thanks to rl1987 tor now has a GETINFO method to retrieve microdescriptors
    (similar to 'GETINFO desc/all-recent', 'GETINFO ns/all', etc).
    
      https://gitweb.torproject.org/torspec.git/commit/?id=b5396d5
      https://trac.torproject.org/projects/tor/ticket/26210
---
 docs/change_log.rst |  1 +
 stem/control.py     | 50 +++++++++++++++++++++++++++++++-------------------
 stem/version.py     |  2 ++
 3 files changed, 34 insertions(+), 19 deletions(-)

diff --git a/docs/change_log.rst b/docs/change_log.rst
index aa8231ba..ca8b6805 100644
--- a/docs/change_log.rst
+++ b/docs/change_log.rst
@@ -52,6 +52,7 @@ The following are only available within Stem's `git repository
   * Stacktrace if :func:`stem.connection.connect` had a string port argument
   * More reliable ExitPolicy resolution (:trac:`25739`)
   * More reliable caching during configuration changes, especially in multiple-controller situations (:trac:`25821`)
+  * :func:`~stem.control.Controller.get_microdescriptors` reads descriptors from the control port if available (:spec:`b5396d5`)
   * Added the delivered_read, delivered_written, overhead_read, and overhead_written attributes to :class:`~stem.response.events.CircuitBandwidthEvent` (:spec:`fbb38ec`)
   * The *config* attribute of :class:`~stem.response.events.ConfChangedEvent` couldn't represent tor configuration options with multiple values. It has been replaced with new *changed* and *unset* attributes.
   * Replaced socket's :func:`~stem.socket.ControlPort.get_address`, :func:`~stem.socket.ControlPort.get_port`, and :func:`~stem.socket.ControlSocketFile.get_socket_path` with attributes
diff --git a/stem/control.py b/stem/control.py
index b1918ce9..77e6140e 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -1746,9 +1746,10 @@ class Controller(BaseController):
     Provides an iterator for all of the microdescriptors that tor currently
     knows about.
 
-    **Tor does not expose this information via the control protocol**
-    (:trac:`8323`). Until it does this reads the microdescriptors from disk,
-    and hence won't work remotely or if we lack read permissions.
+    Prior to Tor 0.3.5.1 this information was not available via the control
+    protocol. When connected to prior versions we read the microdescriptors
+    directly from disk instead, which will not work remotely or if our process
+    lacks read permissions.
 
     :param list default: items to provide if the query fails
 
@@ -1760,27 +1761,38 @@ class Controller(BaseController):
       default was provided
     """
 
-    try:
-      data_directory = self.get_conf('DataDirectory')
-    except stem.ControllerError as exc:
-      raise stem.OperationFailed(message = 'Unable to determine the data directory (%s)' % exc)
+    if self.get_version() >= stem.version.Requirement.GETINFO_MICRODESCRIPTORS:
+      desc_content = self.get_info('md/all', get_bytes = True)
+
+      if not desc_content:
+        raise stem.DescriptorUnavailable('Descriptor information is unavailable, tor might still be downloading it')
+
+      for desc in stem.descriptor.microdescriptor._parse_file(io.BytesIO(desc_content)):
+        yield desc
+    else:
+      # TODO: remove when tor versions that require this are obsolete
+
+      try:
+        data_directory = self.get_conf('DataDirectory')
+      except stem.ControllerError as exc:
+        raise stem.OperationFailed(message = 'Unable to determine the data directory (%s)' % exc)
 
-    cached_descriptor_path = os.path.join(data_directory, 'cached-microdescs')
+      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 doesn't contain cached microdescriptors (%s)" % cached_descriptor_path)
+      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 doesn't contain cached microdescriptors (%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.
+      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))
+          if not isinstance(desc, stem.descriptor.microdescriptor.Microdescriptor):
+            raise stem.OperationFailed(message = 'BUG: Descriptor reader provided non-microdescriptor content (%s)' % type(desc))
 
-        yield desc
+          yield desc
 
   @with_default()
   def get_server_descriptor(self, relay = None, default = UNDEFINED):
diff --git a/stem/version.py b/stem/version.py
index 7e15fe51..b6a866f0 100644
--- a/stem/version.py
+++ b/stem/version.py
@@ -62,6 +62,7 @@ easily parsed and compared, for instance...
   **FEATURE_VERBOSE_NAMES**             'VERBOSE_NAMES' optional feature
   **GETINFO_CONFIG_TEXT**               'GETINFO config-text' query
   **GETINFO_GEOIP_AVAILABLE**           'GETINFO ip-to-country/ipv4-available' query and its ipv6 counterpart
+  **GETINFO_MICRODESCRIPTORS**          'GETINFO md/all' query
   **HIDDEN_SERVICE_V3**                 Support for v3 hidden services
   **HSFETCH**                           HSFETCH requests
   **HSPOST**                            HSPOST requests
@@ -380,6 +381,7 @@ Requirement = stem.util.enum.Enum(
   ('FEATURE_VERBOSE_NAMES', Version('0.2.2.1-alpha')),
   ('GETINFO_CONFIG_TEXT', Version('0.2.2.7-alpha')),
   ('GETINFO_GEOIP_AVAILABLE', Version('0.3.2.1-alpha')),
+  ('GETINFO_MICRODESCRIPTORS', Version('0.3.5.1-alpha')),
   ('HIDDEN_SERVICE_V3', Version('0.3.3.1-alpha')),
   ('HSFETCH', Version('0.2.7.1-alpha')),
   ('HSPOST', Version('0.2.7.1-alpha')),



More information about the tor-commits mailing list