[tor-commits] [stem/master] ExitPolicy's 'private' keyword handling didn't account for our public address

atagar at torproject.org atagar at torproject.org
Sun Sep 7 19:31:10 UTC 2014


commit c5e5d1ac2df52631d542d03d2f5c35db6a84f225
Author: Damian Johnson <atagar at torproject.org>
Date:   Fri Sep 5 08:34:43 2014 -0700

    ExitPolicy's 'private' keyword handling didn't account for our public address
    
    Our Controller included a rule for our public ip address, but the exit_policy
    module didn't. Moving this functionality down to that.
---
 stem/control.py                 |    7 +------
 stem/exit_policy.py             |   35 +++++++++++++++++++++++++++--------
 test/unit/exit_policy/policy.py |    3 ++-
 3 files changed, 30 insertions(+), 15 deletions(-)

diff --git a/stem/control.py b/stem/control.py
index 58dd119..1b33659 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -1033,17 +1033,12 @@ class Controller(BaseController):
           if self.get_conf('ExitPolicyRejectPrivate') == '1':
             policy.append('reject private:*')
 
-            public_addr = self.get_info('address', None)
-
-            if public_addr:
-              policy.append('reject %s:*' % public_addr)
-
           for policy_line in self.get_conf('ExitPolicy', multiple = True):
             policy += policy_line.split(',')
 
           policy += self.get_info('exit-policy/default').split(',')
 
-          config_policy = stem.exit_policy.get_config_policy(policy)
+          config_policy = stem.exit_policy.get_config_policy(policy, self.get_info('address'))
           self._set_cache({'exit_policy': config_policy})
 
         return config_policy
diff --git a/stem/exit_policy.py b/stem/exit_policy.py
index 2cade1d..86722af 100644
--- a/stem/exit_policy.py
+++ b/stem/exit_policy.py
@@ -61,6 +61,9 @@ exiting to a destination is permissible or not. For instance...
   ============ ===========
 """
 
+from __future__ import absolute_import
+
+import socket
 import zlib
 
 import stem.prereq
@@ -92,7 +95,7 @@ PRIVATE_ADDRESSES = (
 )
 
 
-def get_config_policy(rules):
+def get_config_policy(rules, ip_address = None):
   """
   Converts an ExitPolicy found in a torrc to a proper exit pattern. This
   accounts for...
@@ -101,12 +104,17 @@ def get_config_policy(rules):
   * the 'private' keyword
 
   :param str,list rules: comma separated rules or list to be converted
+  :param str ip_address: this relay's IP address for the 'private' policy if
+    it's present, this defaults to the local address
 
   :returns: :class:`~stem.exit_policy.ExitPolicy` reflected by the rules
 
   :raises: **ValueError** if input isn't a valid tor exit policy
   """
 
+  if ip_address and not (stem.util.connection.is_valid_ipv4_address(ip_address) or stem.util.connection.is_valid_ipv6_address(ip_address)):
+    raise ValueError("%s isn't a valid IP address" % ip_address)
+
   if isinstance(rules, (bytes, unicode)):
     rules = rules.split(',')
 
@@ -125,7 +133,10 @@ def get_config_policy(rules):
       acceptance = rule.split(' ', 1)[0]
       port = rule.split(':', 1)[1]
 
-      for private_addr in PRIVATE_ADDRESSES:
+      if ip_address is None:
+        ip_address = socket.gethostbyname(socket.gethostname())
+
+      for private_addr in PRIVATE_ADDRESSES + (ip_address,):
         result.append(ExitPolicyRule("%s %s:%s" % (acceptance, private_addr, port)))
     else:
       result.append(ExitPolicyRule(rule))
@@ -149,7 +160,7 @@ def _flag_private_rules(rules):
   matches = []
 
   for i, rule in enumerate(rules):
-    if i + len(PRIVATE_ADDRESSES) > len(rules):
+    if i + len(PRIVATE_ADDRESSES) + 1 > len(rules):
       break
 
     rule_str = '%s/%s' % (rule.address, rule.get_masked_bits())
@@ -161,21 +172,29 @@ def _flag_private_rules(rules):
     # To match the private policy the following must all be true...
     #
     #   * series of addresses and bit masks match PRIVATE_ADDRESSES
-    #   * all these rules have the same port range and acceptance
-    #   * all these rules must be either accept or reject entries
+    #   * all rules have the same port range and acceptance
+    #   * all rules have the same acceptance (all accept or reject entries)
+
+    rule_set = rules[start_index:start_index + len(PRIVATE_ADDRESSES) + 1]
+    is_match = True
 
-    rule_set = rules[start_index:start_index + len(PRIVATE_ADDRESSES)]
     min_port, max_port = rule_set[0].min_port, rule_set[0].max_port
     is_accept = rule_set[0].is_accept
-    is_match = True
 
-    for i, rule in enumerate(rule_set):
+    for i, rule in enumerate(rule_set[:-1]):
       rule_str = '%s/%s' % (rule.address, rule.get_masked_bits())
 
       if rule_str != PRIVATE_ADDRESSES[i] or rule.min_port != min_port or rule.max_port != max_port or rule.is_accept != is_accept:
         is_match = False
         break
 
+    # The last rule is for the relay's public address, so it's dynamic.
+
+    last_rule = rule_set[-1]
+
+    if last_rule.is_address_wildcard() or last_rule.min_port != min_port or last_rule.max_port != max_port or last_rule.is_accept != is_accept:
+      is_match = False
+
     if is_match:
       for rule in rule_set:
         rule._is_private = True
diff --git a/test/unit/exit_policy/policy.py b/test/unit/exit_policy/policy.py
index d00954d..b4a35e5 100644
--- a/test/unit/exit_policy/policy.py
+++ b/test/unit/exit_policy/policy.py
@@ -225,6 +225,7 @@ class TestExitPolicy(unittest.TestCase):
         'reject 192.168.0.0/16:*',
         'reject 10.0.0.0/8:*',
         'reject 172.16.0.0/12:*',
+        'reject 12.34.56.78:*',
       ),
       'accept *:80, reject *': ExitPolicy(
         'accept *:80',
@@ -237,7 +238,7 @@ class TestExitPolicy(unittest.TestCase):
     }
 
     for test_input, expected in test_inputs.items():
-      self.assertEqual(expected, get_config_policy(test_input))
+      self.assertEqual(expected, get_config_policy(test_input, '12.34.56.78'))
 
     test_inputs = (
       'blarg',





More information about the tor-commits mailing list