commit 12c2a7ec541fb11c5e00a0c67a47176f40606076 Author: Damian Johnson atagar@torproject.org Date: Fri Mar 4 08:39:53 2016 -0800
ExitPolicy support for accept6 and reject6 rules
Thanks to teor for explaining these! Adding support for these exit policy rule types...
https://trac.torproject.org/projects/tor/ticket/16103#comment:5 --- docs/change_log.rst | 1 + stem/exit_policy.py | 37 +++++++++++++++++++++++++++---------- test/unit/exit_policy/rule.py | 19 +++++++++++++++++++ 3 files changed, 47 insertions(+), 10 deletions(-)
diff --git a/docs/change_log.rst b/docs/change_log.rst index 57667e1..8a92788 100644 --- a/docs/change_log.rst +++ b/docs/change_log.rst @@ -47,6 +47,7 @@ The following are only available within Stem's `git repository * Dramatic, `300x performance improvement https://github.com/DonnchaC/stem/pull/1`_ for reading from the control port with python 3 * Added `stem.manual <api/manual.html>`_, which provides information available about Tor from `its manual https://www.torproject.org/docs/tor-manual.html.en`_ (:trac:`8251`) * :func:`~stem.connection.connect` and :func:`~stem.control.Controller.from_port` now connect to both port 9051 (relay's default) and 9151 (Tor Browser's default) (:trac:`16075`) + * :class:`~stem.exit_policy.ExitPolicy` support for *accept6* and *reject6* rules (:trac:`16103`) * Added `support for NETWORK_LIVENESS events <api/response.html#stem.response.events.NetworkLivenessEvent>`_ (:spec:`44aac63`) * Added :func:`~stem.control.Controller.is_set` to the :class:`~stem.control.Controller` * Added :func:`~stem.control.Controller.is_user_traffic_allowed` to the :class:`~stem.control.Controller` diff --git a/stem/exit_policy.py b/stem/exit_policy.py index 293095b..0f80032 100644 --- a/stem/exit_policy.py +++ b/stem/exit_policy.py @@ -632,7 +632,12 @@ class ExitPolicyRule(object):
This should be treated as an immutable object.
+ .. versionchanged:: 1.5.0 + Support for 'accept6/reject6' entries and our **is_ipv6_only** attribute. + :var bool is_accept: indicates if exiting is allowed or disallowed + :var bool is_ipv6_only: indicates if this is an accept6 or reject6 rule, only + matching ipv6 addresses
:var str address: address that this rule is for
@@ -645,17 +650,18 @@ class ExitPolicyRule(object): """
def __init__(self, rule): - # policy ::= "accept" exitpattern | "reject" exitpattern + # policy ::= "accept[6]" exitpattern | "reject[6]" exitpattern # exitpattern ::= addrspec ":" portspec
- if rule.startswith('accept'): - self.is_accept = True - elif rule.startswith('reject'): - self.is_accept = False - else: - raise ValueError("An exit policy must start with either 'accept' or 'reject': %s" % rule) + self.is_accept = rule.startswith('accept') + self.is_ipv6_only = rule.startswith('accept6') or rule.startswith('reject6')
- exitpattern = rule[6:] + if rule.startswith('accept6') or rule.startswith('reject6'): + exitpattern = rule[7:] + elif rule.startswith('accept') or rule.startswith('reject'): + exitpattern = rule[6:] + else: + raise ValueError("An exit policy must start with either 'accept[6]' or 'reject[6]': %s" % rule)
if not exitpattern.startswith(' '): raise ValueError('An exit policy should have a space separating its accept/reject from the exit pattern: %s' % rule) @@ -738,6 +744,9 @@ class ExitPolicyRule(object): # validate our input and check if the argument doesn't match our address type
if address is not None: + if self.is_ipv6_only and stem.util.connection.is_valid_ipv4_address(address): + return False # accept6/reject6 don't match ipv4 + address_type = self.get_address_type()
if stem.util.connection.is_valid_ipv4_address(address): @@ -868,7 +877,10 @@ class ExitPolicyRule(object): to re-create this rule. """
- label = 'accept ' if self.is_accept else 'reject ' + if self.is_ipv6_only: + label = 'accept6 ' if self.is_accept else 'reject6 ' + else: + label = 'accept ' if self.is_accept else 'reject '
if self.is_address_wildcard(): label += '*:' @@ -906,7 +918,7 @@ class ExitPolicyRule(object): if self._hash is None: my_hash = 0
- for attr in ('is_accept', 'address', 'min_port', 'max_port'): + for attr in ('is_accept', 'is_ipv6_only', 'address', 'min_port', 'max_port'): my_hash *= 1024
attr_value = getattr(self, attr) @@ -951,6 +963,10 @@ class ExitPolicyRule(object): # ip4mask ::= an IPv4 mask in dotted-quad format # num_ip4_bits ::= an integer between 0 and 32
+ if self.is_ipv6_only: + rule_start = 'accept6' if self.is_accept else 'reject6' + raise ValueError("'%s' rules should have an IPv6 address, not IPv4 (%s)" % (rule_start, self.address)) + self._address_type = _address_type_to_int(AddressType.IPv4)
if addr_extra is None: @@ -1054,6 +1070,7 @@ class MicroExitPolicyRule(ExitPolicyRule):
def __init__(self, is_accept, min_port, max_port): self.is_accept = is_accept + self.is_ipv6_only = False self.address = None # wildcard address self.min_port = min_port self.max_port = max_port diff --git a/test/unit/exit_policy/rule.py b/test/unit/exit_policy/rule.py index ecd96a8..9ff0181 100644 --- a/test/unit/exit_policy/rule.py +++ b/test/unit/exit_policy/rule.py @@ -42,6 +42,8 @@ class TestExitPolicyRule(unittest.TestCase): test_inputs = ( 'accept *:*', 'reject *:*', + 'accept6 *:*', + 'reject6 *:*', 'accept *:80', 'accept *:80-443', 'accept 127.0.0.1:80', @@ -83,6 +85,10 @@ class TestExitPolicyRule(unittest.TestCase): 'reject [0000:0000:0000:0000:0000:0000:0000:0000]/64:80': (False, False), 'reject [0000:0000:0000:0000:0000:0000:0000:0000]/128:80': (False, False),
+ 'reject6 *:*': (True, True), + 'reject6 *:80': (True, False), + 'reject6 [0000:0000:0000:0000:0000:0000:0000:0000]/128:80': (False, False), + 'accept 192.168.0.1:0-65535': (False, True), 'accept 192.168.0.1:1-65535': (False, True), 'accept 192.168.0.1:2-65535': (False, False), @@ -352,3 +358,16 @@ class TestExitPolicyRule(unittest.TestCase):
for match_args, expected_result in matches.items(): self.assertEqual(expected_result, rule.is_match(*match_args)) + + def test_ipv6_only_entries(self): + # accept6/reject6 shouldn't allow ipv4 addresses + + self.assertRaises(ValueError, ExitPolicyRule, 'accept6 192.168.0.1:*') + self.assertRaises(ValueError, ExitPolicyRule, 'reject6 192.168.0.1:*') + + # wildcards match all ipv6 but *not* ipv4 + + rule = ExitPolicyRule('accept6 *:*') + self.assertTrue(rule.is_ipv6_only) + self.assertTrue(rule.is_match('FE80:0000:0000:0000:0202:B3FF:FE1E:8329', 443)) + self.assertFalse(rule.is_match('192.168.0.1', 443))
tor-commits@lists.torproject.org