[tor-commits] [stem/master] Resume fallback for 'GETINFO exit-policy/full'

atagar at torproject.org atagar at torproject.org
Tue Sep 25 21:45:57 UTC 2018


commit aa176374e1b7be652e2fbf0aa4240f59409d76ea
Author: Damian Johnson <atagar at torproject.org>
Date:   Tue Sep 25 14:39:26 2018 -0700

    Resume fallback for 'GETINFO exit-policy/full'
    
    Turns out there's several situations where 'GETINFO exit-policy/full' provides
    an error response. Most visibly, our integ tests occasionally fail due to
    calling the method too soon after tor first starts up...
    
      https://trac.torproject.org/projects/tor/ticket/27306
    
    I hoped this would replace our need to infer exit policies from the torrc but
    seems that's not to be. Resuming the fallback we used prior to the GETINFO
    option's addition.
    
    In effect this reverts commit 542fa1f.
---
 docs/change_log.rst             |  1 -
 stem/control.py                 | 40 ++++++++++++++++++++++++-------
 test/unit/control/controller.py | 52 ++++++++++++++++++++++++++++++++++++++---
 3 files changed, 81 insertions(+), 12 deletions(-)

diff --git a/docs/change_log.rst b/docs/change_log.rst
index b91f69b3..c32af5df 100644
--- a/docs/change_log.rst
+++ b/docs/change_log.rst
@@ -57,7 +57,6 @@ The following are only available within Stem's `git repository
   * :func:`~stem.control.COntroller.create_hidden_service` failed when creating services with v2 options (:trac:`27446`)
   * :func:`~stem.control.Controller.get_info` commonly raised :class:`stem.ProtocolError` when it should provide :class:`stem.OperationFailed`
   * :func:`~stem.control.Controller.get_microdescriptors` reads descriptors from the control port if available (:spec:`b5396d5`)
-  * :func:`~stem.control.Controller.get_exit_policy` now provides None if not configured to be a relay (:trac:`25853`, :spec:`c5453a0`)
   * 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 5687a4b2..8982d2d1 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -1281,13 +1281,10 @@ class Controller(BaseController):
        parsing the user's torrc entries. This should be more reliable for
        some edge cases. (:trac:`25739`)
 
-    .. versionchanged:: 1.7.0
-       Returning **None** if not contigured to be a relay.
-
     :param object default: response if the query fails
 
     :returns: :class:`~stem.exit_policy.ExitPolicy` of the tor instance that
-      we're connected to, this is **None** if not configured to be a relay
+      we're connected to
 
     :raises:
       * :class:`stem.ControllerError` if unable to query the policy
@@ -1302,11 +1299,38 @@ class Controller(BaseController):
       try:
         policy = stem.exit_policy.ExitPolicy(*self.get_info('exit-policy/full').splitlines())
         self._set_cache({'exit_policy': policy})
-      except stem.OperationFailed as exc:
-        if exc.code == '552':
-          return None  # not configured to be a relay
+      except stem.OperationFailed:
+        # There's a few situations where 'GETINFO exit-policy/full' will fail,
+        # most commonly...
+        #
+        #   * Error 551: Descriptor still rebuilding - not ready yet
+        #
+        #     Tor hasn't yet finished making our server descriptor. This often
+        #     arises when tor has first started.
+        #
+        #   * Error 552: Not running in server mode
+        #
+        #     We're not configured to be a relay (no ORPort), or haven't yet
+        #     been able to determine our externally facing IP address.
+        #
+        # When these arise best we can do is infer our policy from the torrc.
+        # Skipping caching so we'll retry GETINFO policy resolution next time
+        # we're called.
 
-        raise
+        rules = []
+
+        if self.get_conf('ExitRelay') == '0':
+          rules.append('reject *:*')
+
+        if self.get_conf('ExitPolicyRejectPrivate') == '1':
+          rules.append('reject private:*')
+
+        for policy_line in self.get_conf('ExitPolicy', multiple = True):
+          rules += policy_line.split(',')
+
+        rules += self.get_info('exit-policy/default').split(',')
+
+        policy = stem.exit_policy.get_config_policy(rules, self.get_info('address', None))
 
     return policy
 
diff --git a/test/unit/control/controller.py b/test/unit/control/controller.py
index ba55208a..0ae9fafd 100644
--- a/test/unit/control/controller.py
+++ b/test/unit/control/controller.py
@@ -199,9 +199,55 @@ class TestControl(unittest.TestCase):
     self.assertEqual(expected, self.controller.get_exit_policy())
 
   @patch('stem.control.Controller.get_info')
-  def test_get_exit_policy_if_not_relaying(self, get_info_mock):
-    get_info_mock.side_effect = stem.OperationFailed('552', 'Not running in server mode')
-    self.assertEqual(None, self.controller.get_exit_policy())
+  @patch('stem.control.Controller.get_conf')
+  def test_get_exit_policy_if_not_relaying(self, get_conf_mock, get_info_mock):
+    # If tor lacks an ORPort, resolved extrnal address, hasn't finished making
+    # our server descriptor (ie. tor just started), etc 'GETINFO
+    # exit-policy/full' will fail.
+
+    get_conf_mock.side_effect = lambda param, **kwargs: {
+      'ExitRelay': '1',
+      'ExitPolicyRejectPrivate': '1',
+      'ExitPolicy': ['accept *:80,   accept *:443', 'accept 43.5.5.5,reject *:22'],
+    }[param]
+
+    expected = ExitPolicy(
+      'reject 0.0.0.0/8:*',
+      'reject 169.254.0.0/16:*',
+      'reject 127.0.0.0/8:*',
+      'reject 192.168.0.0/16:*',
+      'reject 10.0.0.0/8:*',
+      'reject 172.16.0.0/12:*',
+      'reject 127.0.1.1:*',
+      'accept *:80',
+      'accept *:443',
+      'accept 43.5.5.5:*',
+      'reject *:22',
+    )
+
+    # Unfortunate it's a bit tricky to have a mock that raises exceptions in
+    # response to some arguments, and returns a response for others. As such
+    # mapping it to the following function.
+
+    exit_policy_exception = None
+
+    def getinfo_response(param, default = None):
+      if param == 'address':
+        return default
+      elif param == 'exit-policy/default':
+        return ''
+      elif param == 'exit-policy/full' and exit_policy_exception:
+        raise exit_policy_exception
+      else:
+        raise ValueError("Unmocked request for 'GETINFO %s'" % param)
+
+    get_info_mock.side_effect = getinfo_response
+
+    exit_policy_exception = stem.OperationFailed('552', 'Not running in server mode')
+    self.assertEqual(expected, self.controller.get_exit_policy())
+
+    exit_policy_exception = stem.OperationFailed('551', 'Descriptor still rebuilding - not ready yet')
+    self.assertEqual(expected, self.controller.get_exit_policy())
 
   @patch('stem.control.Controller.get_info')
   @patch('stem.control.Controller.get_conf')



More information about the tor-commits mailing list