[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Handling chroot in stem.connection and integ tests
commit 636fd3704d6ddcba6a2a780119570fc2e25dd77f
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sun Apr 22 15:05:17 2012 -0700
Handling chroot in stem.connection and integ tests
On ticket 4896 [1] I proposed a couple ideas for how we could handle chroot
setups, but on reflection neither of them were very good.
At a low level we can't reliably expand paths, nor should we try. If the user
makes a raw 'GETINFO config-file' query then we should simply return what tor
gives us, not try to 'fix' it by expanding the path.
Rather, we should correct for chroot prefixes at a higher level like the
controller. My current plan is...
* The Controller class will have an optional chroot_path constructor argument,
and a get_chroot() method. All of the Controller's methods and those of
subclasses should take it into account for tor resource paths.
* The stem.connection functions now accept a chroot_path argument. We need this
since they will construct Controller instances, and also do the initial
authentication (we need to know about chroots for cookie authentication).
[1] https://trac.torproject.org/projects/tor/ticket/4896
---
stem/connection.py | 25 +++++++++++-----
test/integ/connection/authentication.py | 16 ++++++-----
test/integ/connection/connect.py | 5 +++-
test/integ/connection/protocolinfo.py | 9 +++++-
test/integ/control/base_controller.py | 6 ++--
test/integ/socket/control_message.py | 4 +++
test/runner.py | 45 +++++++++++++++++++++++-------
test/unit/connection/authentication.py | 4 +-
8 files changed, 80 insertions(+), 34 deletions(-)
diff --git a/stem/connection.py b/stem/connection.py
index 419dfa3..d950049 100644
--- a/stem/connection.py
+++ b/stem/connection.py
@@ -194,7 +194,7 @@ AUTHENTICATE_EXCEPTIONS = (
AuthenticationFailure,
)
-def connect_port(control_addr = "127.0.0.1", control_port = 9051, password = None, controller = Controller.NONE):
+def connect_port(control_addr = "127.0.0.1", control_port = 9051, password = None, chroot_path = None, controller = Controller.NONE):
"""
Convenience function for quickly getting a control connection. This is very
handy for debugging or CLI setup, handling setup and prompting for a password
@@ -205,6 +205,7 @@ def connect_port(control_addr = "127.0.0.1", control_port = 9051, password = Non
control_addr (str) - ip address of the controller
control_port (int) - port number of the controller
password (str) - passphrase to authenticate to the socket
+ chroot_path (str) - path prefix if in a chroot environment
controller (Controller) - controller type to be returned
Returns:
@@ -220,9 +221,9 @@ def connect_port(control_addr = "127.0.0.1", control_port = 9051, password = Non
print exc
return None
- return _connect(control_port, password, controller)
+ return _connect(control_port, password, chroot_path, controller)
-def connect_socket_file(socket_path = "/var/run/tor/control", password = None, controller = Controller.NONE):
+def connect_socket_file(socket_path = "/var/run/tor/control", password = None, chroot_path = None, controller = Controller.NONE):
"""
Convenience function for quickly getting a control connection. For more
information see the connect_port function.
@@ -230,6 +231,7 @@ def connect_socket_file(socket_path = "/var/run/tor/control", password = None, c
Arguments:
socket_path (str) - path where the control socket is located
password (str) - passphrase to authenticate to the socket
+ chroot_path (str) - path prefix if in a chroot environment
controller (Controller) - controller type to be returned
Returns:
@@ -242,15 +244,16 @@ def connect_socket_file(socket_path = "/var/run/tor/control", password = None, c
print exc
return None
- return _connect(control_socket, password, controller)
+ return _connect(control_socket, password, chroot_path, controller)
-def _connect(control_socket, password, controller):
+def _connect(control_socket, password, chroot_path, controller):
"""
Common implementation for the connect_* functions.
Arguments:
control_socket (stem.socket.ControlSocket) - socket being authenticated to
password (str) - passphrase to authenticate to the socket
+ chroot_path (str) - path prefix if in a chroot environment
controller (Controller) - controller type to be returned
Returns:
@@ -258,7 +261,7 @@ def _connect(control_socket, password, controller):
"""
try:
- authenticate(control_socket, password)
+ authenticate(control_socket, password, chroot_path)
if controller == Controller.NONE:
return control_socket
@@ -274,7 +277,7 @@ def _connect(control_socket, password, controller):
print "Unable to authenticate: %s" % exc
return None
-def authenticate(control_socket, password = None, protocolinfo_response = None):
+def authenticate(control_socket, password = None, chroot_path = None, protocolinfo_response = None):
"""
Authenticates to a control socket using the information provided by a
PROTOCOLINFO response. In practice this will often be all we need to
@@ -288,6 +291,7 @@ def authenticate(control_socket, password = None, protocolinfo_response = None):
control_socket (stem.socket.ControlSocket) - socket to be authenticated
password (str) - passphrase to present to the socket if it uses password
authentication (skips password auth if None)
+ chroot_path (str) - path prefix if in a chroot environment
protocolinfo_response (stem.connection.ProtocolInfoResponse) -
tor protocolinfo response, this is retrieved on our own if None
@@ -397,7 +401,12 @@ def authenticate(control_socket, password = None, protocolinfo_response = None):
elif auth_type == AuthMethod.PASSWORD:
authenticate_password(control_socket, password, False)
elif auth_type == AuthMethod.COOKIE:
- authenticate_cookie(control_socket, protocolinfo_response.cookie_path, False)
+ cookie_path = protocolinfo_response.cookie_path
+
+ if chroot_path:
+ cookie_path = os.path.join(chroot_path, cookie_path.lstrip(os.path.sep))
+
+ authenticate_cookie(control_socket, cookie_path, False)
return # success!
except OpenAuthRejected, exc:
diff --git a/test/integ/connection/authentication.py b/test/integ/connection/authentication.py
index aafde5d..f80b848 100644
--- a/test/integ/connection/authentication.py
+++ b/test/integ/connection/authentication.py
@@ -85,8 +85,9 @@ class TestAuthenticate(unittest.TestCase):
Tests that the authenticate function can authenticate to our socket.
"""
- with test.runner.get_runner().get_tor_socket(False) as control_socket:
- stem.connection.authenticate(control_socket, test.runner.CONTROL_PASSWORD)
+ runner = test.runner.get_runner()
+ with runner.get_tor_socket(False) as control_socket:
+ stem.connection.authenticate(control_socket, test.runner.CONTROL_PASSWORD, runner.get_chroot())
test.runner.exercise_socket(self, control_socket)
def test_authenticate_general_example(self):
@@ -94,7 +95,8 @@ class TestAuthenticate(unittest.TestCase):
Tests the authenticate function with something like its pydoc example.
"""
- tor_options = test.runner.get_runner().get_options()
+ runner = test.runner.get_runner()
+ tor_options = runner.get_options()
try:
control_socket = stem.socket.ControlPort(control_port = test.runner.CONTROL_PORT)
@@ -105,7 +107,7 @@ class TestAuthenticate(unittest.TestCase):
try:
# this authenticate call should work for everything but password-only auth
- stem.connection.authenticate(control_socket)
+ stem.connection.authenticate(control_socket, chroot_path = runner.get_chroot())
test.runner.exercise_socket(self, control_socket)
except stem.connection.IncorrectSocketType:
self.fail()
@@ -141,7 +143,7 @@ class TestAuthenticate(unittest.TestCase):
if is_password_only:
self.assertRaises(stem.connection.MissingPassword, stem.connection.authenticate, control_socket)
else:
- stem.connection.authenticate(control_socket)
+ stem.connection.authenticate(control_socket, chroot_path = runner.get_chroot())
test.runner.exercise_socket(self, control_socket)
# tests with the incorrect password
@@ -149,12 +151,12 @@ class TestAuthenticate(unittest.TestCase):
if is_password_only:
self.assertRaises(stem.connection.IncorrectPassword, stem.connection.authenticate, control_socket, "blarg")
else:
- stem.connection.authenticate(control_socket, "blarg")
+ stem.connection.authenticate(control_socket, "blarg", runner.get_chroot())
test.runner.exercise_socket(self, control_socket)
# tests with the right password
with runner.get_tor_socket(False) as control_socket:
- stem.connection.authenticate(control_socket, test.runner.CONTROL_PASSWORD)
+ stem.connection.authenticate(control_socket, test.runner.CONTROL_PASSWORD, runner.get_chroot())
test.runner.exercise_socket(self, control_socket)
def test_authenticate_none(self):
diff --git a/test/integ/connection/connect.py b/test/integ/connection/connect.py
index 9b6337a..d61ddf7 100644
--- a/test/integ/connection/connect.py
+++ b/test/integ/connection/connect.py
@@ -25,12 +25,15 @@ class TestConnect(unittest.TestCase):
Basic sanity checks for the connect_port function.
"""
+ runner = test.runner.get_runner()
+
control_socket = stem.connection.connect_port(
control_port = test.runner.CONTROL_PORT,
password = test.runner.CONTROL_PASSWORD,
+ chroot_path = runner.get_chroot(),
controller = stem.connection.Controller.NONE)
- if test.runner.Torrc.PORT in test.runner.get_runner().get_options():
+ if test.runner.Torrc.PORT in runner.get_options():
test.runner.exercise_socket(self, control_socket)
control_socket.close()
else:
diff --git a/test/integ/connection/protocolinfo.py b/test/integ/connection/protocolinfo.py
index a0214a2..728c746 100644
--- a/test/integ/connection/protocolinfo.py
+++ b/test/integ/connection/protocolinfo.py
@@ -114,12 +114,17 @@ class TestProtocolInfo(unittest.TestCase):
the test configuration.
"""
- tor_options = test.runner.get_runner().get_options()
+ runner = test.runner.get_runner()
+ tor_options = runner.get_options()
auth_methods, auth_cookie_path = [], None
if test.runner.Torrc.COOKIE in tor_options:
auth_methods.append(stem.connection.AuthMethod.COOKIE)
- auth_cookie_path = test.runner.get_runner().get_auth_cookie_path()
+ chroot_path = runner.get_chroot()
+ auth_cookie_path = runner.get_auth_cookie_path()
+
+ if chroot_path and auth_cookie_path.startswith(chroot_path):
+ auth_cookie_path = auth_cookie_path[len(chroot_path):]
if test.runner.Torrc.PASSWORD in tor_options:
auth_methods.append(stem.connection.AuthMethod.PASSWORD)
diff --git a/test/integ/control/base_controller.py b/test/integ/control/base_controller.py
index da4cc08..1abab93 100644
--- a/test/integ/control/base_controller.py
+++ b/test/integ/control/base_controller.py
@@ -77,10 +77,10 @@ class TestBaseController(unittest.TestCase):
runner = test.runner.get_runner()
with runner.get_tor_socket() as control_socket:
controller = stem.control.BaseController(control_socket)
- response = controller.msg("GETINFO config-file")
+ response = controller.msg("GETINFO version")
- torrc_dst = runner.get_torrc_path()
- self.assertEquals("config-file=%s\nOK" % torrc_dst, str(response))
+ tor_version = runner.get_tor_version()
+ self.assertEquals("version=%s\nOK" % tor_version, str(response))
def test_msg_invalid(self):
"""
diff --git a/test/integ/socket/control_message.py b/test/integ/socket/control_message.py
index 72f9174..255e66d 100644
--- a/test/integ/socket/control_message.py
+++ b/test/integ/socket/control_message.py
@@ -86,6 +86,10 @@ class TestControlMessage(unittest.TestCase):
runner = test.runner.get_runner()
torrc_dst = runner.get_torrc_path()
+ chroot_path = runner.get_chroot()
+
+ if chroot_path and torrc_dst.startswith(chroot_path):
+ torrc_dst = torrc_dst[len(chroot_path):]
with runner.get_tor_socket() as control_socket:
control_socket.send("GETINFO config-file")
diff --git a/test/runner.py b/test/runner.py
index 3636b44..6702b8b 100644
--- a/test/runner.py
+++ b/test/runner.py
@@ -21,6 +21,9 @@ Runner - Runtime context for our integration tests.
|- get_test_dir - testing directory path
|- get_torrc_path - path to our tor instance's torrc
|- get_torrc_contents - contents of our tor instance's torrc
+ |- get_auth_cookie_path - path for our authentication cookie if we have one
+ |- get_tor_cwd - current working directory of our tor process
+ |- get_chroot - provides the path of our emulated chroot if we have one
|- get_pid - process id of our tor process
|- get_tor_socket - provides a socket to the tor instance
|- get_tor_version - provides the version of tor we're running against
@@ -122,7 +125,12 @@ def exercise_socket(test_case, control_socket):
control_socket (stem.socket.ControlSocket) - socket to be tested
"""
- torrc_path = get_runner().get_torrc_path()
+ runner = get_runner()
+ torrc_path, chroot_path = runner.get_torrc_path(), runner.get_chroot()
+
+ if chroot_path and torrc_path.startswith(chroot_path):
+ torrc_path = torrc_path[len(chroot_path):]
+
control_socket.send("GETINFO config-file")
config_file_response = control_socket.recv()
test_case.assertEquals("config-file=%s\nOK" % torrc_path, str(config_file_response))
@@ -136,7 +144,7 @@ def get_runner():
if not INTEG_RUNNER: INTEG_RUNNER = Runner()
return INTEG_RUNNER
-class MockChrootFile:
+class _MockChrootFile:
"""
Wrapper around a file object that strips given content from readline()
responses. This is used to simulate a chroot setup by removing the restign
@@ -161,6 +169,7 @@ class Runner:
self._torrc_contents = ""
self._custom_opts = None
self._tor_process = None
+ self._chroot_path = None
# set if we monkey patch stem.socket.recv_message()
@@ -225,9 +234,10 @@ class Runner:
# need to set that too
self._original_recv_message = stem.socket.recv_message
+ self._chroot_path = data_dir_path
def _chroot_recv_message(control_file):
- return self._original_recv_message(MockChrootFile(control_file, data_dir_path))
+ return self._original_recv_message(_MockChrootFile(control_file, data_dir_path))
stem.socket.recv_message = _chroot_recv_message
@@ -356,9 +366,24 @@ class Runner:
test_dir = self._get("_test_dir")
return os.path.join(test_dir, "torrc")
+ def get_torrc_contents(self):
+ """
+ Provides the contents of our torrc.
+
+ Returns:
+ str with the contents of our torrc, lines are newline separated
+
+ Raises:
+ RunnerStopped if we aren't running
+ """
+
+ return self._get("_torrc_contents")
+
def get_auth_cookie_path(self):
"""
Provides the absolute path for our authentication cookie if we have one.
+ If running with an emulated chroot this is uneffected, still providing the
+ real path.
Returns:
str with our auth cookie path
@@ -377,18 +402,16 @@ class Runner:
return self._get("_tor_cwd")
- def get_torrc_contents(self):
+ def get_chroot(self):
"""
- Provides the contents of our torrc.
+ Provides the path we're using to emulate a chroot environment. This is None
+ if we aren't emulating a chroot setup.
Returns:
- str with the contents of our torrc, lines are newline separated
-
- Raises:
- RunnerStopped if we aren't running
+ str with the path of our emulated chroot
"""
- return self._get("_torrc_contents")
+ return self._chroot_path
def get_pid(self):
"""
@@ -425,7 +448,7 @@ class Runner:
else: raise TorInaccessable("Unable to connect to tor")
if authenticate:
- stem.connection.authenticate(control_socket, CONTROL_PASSWORD)
+ stem.connection.authenticate(control_socket, CONTROL_PASSWORD, self.get_chroot())
return control_socket
diff --git a/test/unit/connection/authentication.py b/test/unit/connection/authentication.py
index 0937439..831ca62 100644
--- a/test/unit/connection/authentication.py
+++ b/test/unit/connection/authentication.py
@@ -140,9 +140,9 @@ class TestAuthenticate(unittest.TestCase):
mocking.mock(auth_function, mocking.raise_exception(raised_exc))
if expect_success:
- stem.connection.authenticate(None, "blah", protocolinfo_arg)
+ stem.connection.authenticate(None, "blah", None, protocolinfo_arg)
else:
- self.assertRaises(stem.connection.AuthenticationFailure, stem.connection.authenticate, None, "blah", protocolinfo_arg)
+ self.assertRaises(stem.connection.AuthenticationFailure, stem.connection.authenticate, None, "blah", None, protocolinfo_arg)
# revert logging back to normal
stem_logger.setLevel(log.logging_level(log.TRACE))
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits