[tor-commits] [stem/master] Controller method to get the exit policy
atagar at torproject.org
atagar at torproject.org
Mon Jan 14 01:39:16 UTC 2013
commit 716f8a693e9b814da5e2c9df551dbe6768f4f324
Author: Damian Johnson <atagar at torproject.org>
Date: Sun Jan 13 17:19:33 2013 -0800
Controller method to get the exit policy
What started out with 'we should have a method to get our exit policy' turned
out to not be as simple as I thought. A relay's exit policy is really the
combination of three things:
* 'reject private:*, reject [public address]:*' if ExitPolicyRejectPrivate is
set.
* Our ExitPolicy. Tor provides the torrc value rather than a proper policy
spec, so I added a get_config_policy() function to account for the
differences.
* The default exit policy (as per 'GETINFO exit-policy/default').
---
stem/control.py | 51 +++++++++++++++++++++++++++++++++++++-
stem/exit_policy.py | 2 +
test/integ/control/controller.py | 35 ++++++++++++++++++++++++++
test/unit/control/controller.py | 43 ++++++++++++++++++++++++++++++++
4 files changed, 130 insertions(+), 1 deletions(-)
diff --git a/stem/control.py b/stem/control.py
index 4d3bb52..2090983 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -16,7 +16,8 @@ providing its own for interacting at a higher level.
|- authenticate - authenticates this controller with tor
|
|- get_info - issues a GETINFO query for a parameter
- |- get_version - convenience method to get tor version
+ |- get_version - provides our tor version
+ |- get_exit_policy - provides our exit policy
|- get_socks_listeners - provides where tor is listening for SOCKS connections
|- get_protocolinfo - information about the controller interface
|
@@ -135,6 +136,7 @@ import time
import stem.descriptor.router_status_entry
import stem.descriptor.server_descriptor
+import stem.exit_policy
import stem.response
import stem.socket
import stem.util.connection
@@ -813,6 +815,50 @@ class Controller(BaseController):
else:
return default
+ def get_exit_policy(self, default = UNDEFINED):
+ """
+ Effective ExitPolicy for our relay. This accounts for
+ ExitPolicyRejectPrivate and default policies.
+
+ :param object default: response if the query fails
+
+ :returns: :class:`~stem.exit_policy.ExitPolicy` of the tor instance that
+ we're connected to
+
+ :raises:
+ * :class:`stem.ControllerError` if unable to query the policy
+ * **ValueError** if unable to parse the policy
+
+ An exception is only raised if we weren't provided a default response.
+ """
+
+ with self._msg_lock:
+ try:
+ if not "exit_policy" in self._request_cache:
+ policy = []
+
+ 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(",")
+
+ self._request_cache["exit_policy"] = stem.exit_policy.get_config_policy(policy)
+
+ return self._request_cache["exit_policy"]
+ except Exception, exc:
+ if default == UNDEFINED:
+ raise exc
+ else:
+ return default
+
def get_socks_listeners(self, default = UNDEFINED):
"""
Provides the SOCKS **(address, port)** tuples that tor has open.
@@ -1312,6 +1358,9 @@ class Controller(BaseController):
self._request_cache[cache_key] = [value]
else:
self._request_cache[cache_key] = value
+
+ if param.lower() == "exitpolicy" and "exit_policy" in self._request_cache:
+ del self._request_cache["exit_policy"]
else:
log.debug("%s (failed, code: %s, message: %s)" % (query, response.code, response.message))
diff --git a/stem/exit_policy.py b/stem/exit_policy.py
index 2809e5e..e568deb 100644
--- a/stem/exit_policy.py
+++ b/stem/exit_policy.py
@@ -38,6 +38,8 @@ exiting to a destination is permissible or not. For instance...
|- get_masked_bits - provides the bit representation of our mask
+- __str__ - string representation for this rule
+ get_config_policy - provides the ExitPolicy based on torrc rules
+
.. data:: AddressType (enum)
Enumerations for IP address types that can be in an exit policy.
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index bf6b229..8b75c6f 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -23,6 +23,7 @@ import test.runner
import test.util
from stem.control import EventType
+from stem.exit_policy import ExitPolicy
from stem.version import Requirement
@@ -247,6 +248,40 @@ class TestController(unittest.TestCase):
self.assertTrue(isinstance(version, stem.version.Version))
self.assertEqual(version, runner.get_tor_version())
+ def test_get_exit_policy(self):
+ """
+ Sanity test for get_exit_policy(). We have the default policy (no
+ ExitPolicy set) which is a little... long due to the boilerplate.
+ """
+
+ if test.runner.require_control(self):
+ return
+
+ 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 *:25',
+ 'reject *:119',
+ 'reject *:135-139',
+ 'reject *:445',
+ 'reject *:563',
+ 'reject *:1214',
+ 'reject *:4661-4666',
+ 'reject *:6346-6429',
+ 'reject *:6699',
+ 'reject *:6881-6999',
+ 'accept *:*',
+ )
+
+ runner = test.runner.get_runner()
+
+ with runner.get_tor_controller() as controller:
+ self.assertEqual(expected, controller.get_exit_policy())
+
def test_authenticate(self):
"""
Test that the convenient method authenticate() works.
diff --git a/test/unit/control/controller.py b/test/unit/control/controller.py
index 09b0684..77c8f84 100644
--- a/test/unit/control/controller.py
+++ b/test/unit/control/controller.py
@@ -11,6 +11,7 @@ import stem.version
from stem import InvalidArguments, InvalidRequest, ProtocolError
from stem.control import _parse_circ_path, Controller, EventType
+from stem.exit_policy import ExitPolicy
from test import mocking
@@ -69,6 +70,48 @@ class TestControl(unittest.TestCase):
# Turn caching back on before we leave.
self.controller._is_caching_enabled = True
+ def test_get_exit_policy(self):
+ """
+ Exercises the get_exit_policy() method.
+ """
+
+ mocking.mock_method(Controller, "get_conf", mocking.return_for_args({
+ ("ExitPolicyRejectPrivate",): "1",
+ ("ExitPolicy", "multiple=True"): ["accept *:80, accept *:443", "accept 43.5.5.5,reject *:22"]
+ }, is_method = True))
+
+ mocking.mock_method(Controller, "get_info", mocking.return_for_args({
+ ("address", None): "123.45.67.89",
+ ("exit-policy/default",): "reject *:25,reject *:119,reject *:135-139,reject *:445,reject *:563,reject *:1214,reject *:4661-4666,reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*"
+ }, is_method = True))
+
+ expected = ExitPolicy(
+ 'reject 0.0.0.0/8:*', # private entries
+ '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 123.45.67.89:*', # relay's public address
+ 'accept *:80', # finally we get to our ExitPolicy
+ 'accept *:443',
+ 'accept 43.5.5.5:*',
+ 'reject *:22',
+ 'reject *:25', # default policy
+ 'reject *:119',
+ 'reject *:135-139',
+ 'reject *:445',
+ 'reject *:563',
+ 'reject *:1214',
+ 'reject *:4661-4666',
+ 'reject *:6346-6429',
+ 'reject *:6699',
+ 'reject *:6881-6999',
+ 'accept *:*',
+ )
+
+ self.assertEqual(expected, self.controller.get_exit_policy())
+
def test_get_socks_listeners_old(self):
"""
Exercises the get_socks_listeners() method as though talking to an old tor
More information about the tor-commits
mailing list