[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Function to query PROTOCOLINFO via control socket
commit 56c2ea8c33e63456dc1316f3936439478e12cc03
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Mon Nov 21 09:21:21 2011 -0800
Function to query PROTOCOLINFO via control socket
Same as before, implementation and integ sanity check for making a PROTOCOLINFO
query via a control socket. This has the common bits between it, the control
port function, and a bit of the PROTOCOLINFO response parsing delegated to
helper functions.
---
stem/connection.py | 122 +++++++++++++++++++++++----------
test/integ/connection/protocolinfo.py | 29 ++++++--
test/unit/connection/protocolinfo.py | 12 ++--
3 files changed, 114 insertions(+), 49 deletions(-)
diff --git a/stem/connection.py b/stem/connection.py
index 3e97e9e..476c9c3 100644
--- a/stem/connection.py
+++ b/stem/connection.py
@@ -1,5 +1,17 @@
"""
Functions for connecting and authenticating to the tor process.
+
+get_protocolinfo_by_port - PROTOCOLINFO query via a control port.
+get_protocolinfo_by_socket - PROTOCOLINFO query via a control socket.
+ProtocolInfoResponse - Reply from a PROTOCOLINFO query.
+ |- Attributes:
+ | |- protocol_version
+ | |- tor_version
+ | |- auth_methods
+ | |- unknown_auth_methods
+ | |- cookie_path
+ | +- socket
+ +- convert - parses a ControlMessage, turning it into a ProtocolInfoResponse
"""
import Queue
@@ -28,7 +40,7 @@ LOGGER = logging.getLogger("stem")
AuthMethod = stem.util.enum.Enum("NONE", "PASSWORD", "COOKIE", "UNKNOWN")
-def get_protocolinfo_port(control_addr = "127.0.0.1", control_port = 9051, keep_alive = False):
+def get_protocolinfo_by_port(control_addr = "127.0.0.1", control_port = 9051, keep_alive = False):
"""
Issues a PROTOCOLINFO query to a control port, getting information about the
tor process running on it.
@@ -49,12 +61,50 @@ def get_protocolinfo_port(control_addr = "127.0.0.1", control_port = 9051, keep_
"""
control_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ connection_args = (control_addr, control_port)
+ protocolinfo_response = _get_protocolinfo_impl(control_socket, connection_args, keep_alive)
+
+ # attempt to expand relative cookie paths using our port to infer the pid
+ protocolinfo_response.cookie_path = _expand_cookie_path(protocolinfo_response.cookie_path, stem.util.system.get_pid_by_port, control_port)
+
+ return protocolinfo_response
+
+def get_protocolinfo_by_socket(socket_path = "/var/run/tor/control", keep_alive = False):
+ """
+ Issues a PROTOCOLINFO query to a control socket, getting information about
+ the tor process running on it.
+
+ Arguments:
+ socket_path (str) - path where the control socket is located
+ keep_alive (bool) - keeps the socket used to issue the PROTOCOLINFO query
+ open if True, closes otherwise
+
+ Raises:
+ stem.types.ProtocolError if the PROTOCOLINFO response is malformed
+ stem.types.SocketError if problems arise in establishing or using the
+ socket
+ """
+
+ control_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ protocolinfo_response = _get_protocolinfo_impl(control_socket, socket_path, keep_alive)
+
+ # attempt to expand relative cookie paths using our socket to infer the pid
+ protocolinfo_response.cookie_path = _expand_cookie_path(protocolinfo_response.cookie_path, stem.util.system.get_pid_by_open_file, socket_path)
+
+ return protocolinfo_response
+
+def _get_protocolinfo_impl(control_socket, connection_args, keep_alive):
+ """
+ Common implementation behind the get_protocolinfo_by_* functions. This
+ connects the given socket and issues a PROTOCOLINFO query with it.
+ """
+
control_socket_file = control_socket.makefile()
protocolinfo_response, raised_exc = None, None
try:
# initiates connection
- control_socket.connect((control_addr, control_port))
+ control_socket.connect(connection_args)
# issues the PROTOCOLINFO query
control_socket_file.write("PROTOCOLINFO 1\r\n")
@@ -79,26 +129,36 @@ def get_protocolinfo_port(control_addr = "127.0.0.1", control_port = 9051, keep_
# if we're keeping the socket open then attach it to the response
protocolinfo_response.socket = control_socket
- if raised_exc:
- raise raised_exc
- else:
- # if we have an unresolved cookie path then try to expand it again, using
- # our port to infer a pid
-
- cookie_path = protocolinfo_response.cookie_file
- if cookie_path and control_addr == "127.0.0.1" and stem.util.system.is_relative_path(cookie_path):
- try:
- tor_pid = stem.util.system.get_pid_by_port(control_port)
- if not tor_pid: raise IOError("pid lookup failed")
-
- tor_cwd = stem.util.system.get_cwd(tor_pid)
- if not tor_cwd: raise IOError("cwd lookup failed")
-
- protocolinfo_response.cookie_file = stem.util.system.expand_path(cookie_path, tor_cwd)
- except IOError, exc:
- LOGGER.debug("unable to expand relative tor cookie path by port: %s" % exc)
-
- return protocolinfo_response
+ if raised_exc: raise raised_exc
+ else: return protocolinfo_response
+
+def _expand_cookie_path(cookie_path, pid_resolver, pid_resolution_arg):
+ """
+ Attempts to expand a relative cookie path with the given pid resolver. This
+ returns the input path if it's already absolute, None, or the system calls
+ fail.
+ """
+
+ if cookie_path and stem.util.system.is_relative_path(cookie_path):
+ try:
+ tor_pid = pid_resolver(pid_resolution_arg)
+ if not tor_pid: raise IOError("pid lookup failed")
+
+ tor_cwd = stem.util.system.get_cwd(tor_pid)
+ if not tor_cwd: raise IOError("cwd lookup failed")
+
+ cookie_path = stem.util.system.expand_path(cookie_path, tor_cwd)
+ except IOError, exc:
+ resolver_labels = {
+ stem.util.system.get_pid_by_name: " by name",
+ stem.util.system.get_pid_by_port: " by port",
+ stem.util.system.get_pid_by_open_file: " by socket file",
+ }
+
+ pid_resolver_label = resolver_labels.get(pid_resolver, "")
+ LOGGER.debug("unable to expand relative tor cookie path%s: %s" % (pid_resolver_label, exc))
+
+ return cookie_path
class ProtocolInfoResponse(stem.types.ControlMessage):
"""
@@ -119,7 +179,7 @@ class ProtocolInfoResponse(stem.types.ControlMessage):
tor_version (stem.types.Version) - version of the tor process
auth_methods (tuple) - AuthMethod types that tor will accept
unknown_auth_methods (tuple) - strings of unrecognized auth methods
- cookie_file (str) - path of tor's authentication cookie
+ cookie_path (str) - path of tor's authentication cookie
socket (socket.socket) - socket used to make the query
"""
@@ -155,7 +215,7 @@ class ProtocolInfoResponse(stem.types.ControlMessage):
self.protocol_version = None
self.tor_version = None
- self.cookie_file = None
+ self.cookie_path = None
self.socket = None
auth_methods, unknown_auth_methods = [], []
@@ -225,20 +285,10 @@ class ProtocolInfoResponse(stem.types.ControlMessage):
# parse optional COOKIEFILE mapping (quoted and can have escapes)
if line.is_next_mapping("COOKIEFILE", True, True):
- self.cookie_file = line.pop_mapping(True, True)[1]
+ self.cookie_path = line.pop_mapping(True, True)[1]
# attempt to expand relative cookie paths
- if stem.util.system.is_relative_path(self.cookie_file):
- try:
- tor_pid = stem.util.system.get_pid_by_name("tor")
- if not tor_pid: raise IOError("pid lookup failed")
-
- tor_cwd = stem.util.system.get_cwd(tor_pid)
- if not tor_cwd: raise IOError("cwd lookup failed")
-
- self.cookie_file = stem.util.system.expand_path(self.cookie_file, tor_cwd)
- except IOError, exc:
- LOGGER.debug("unable to expand relative tor cookie path by name: %s" % exc)
+ self.cookie_path = _expand_cookie_path(self.cookie_path, stem.util.system.get_pid_by_name, "tor")
elif line_type == "VERSION":
# Line format:
# VersionLine = "250-VERSION" SP "Tor=" TorVersion OptArguments CRLF
diff --git a/test/integ/connection/protocolinfo.py b/test/integ/connection/protocolinfo.py
index 73a7884..037f6dd 100644
--- a/test/integ/connection/protocolinfo.py
+++ b/test/integ/connection/protocolinfo.py
@@ -46,20 +46,35 @@ class TestProtocolInfo(unittest.TestCase):
self.assertEqual(None, protocolinfo_response.socket)
self.assert_protocolinfo_attr(protocolinfo_response, connection_type)
- def test_get_protocolinfo_port(self):
+ def test_get_protocolinfo_by_port(self):
"""
- Exercises the stem.connection.get_protocolinfo_port function.
+ Exercises the stem.connection.get_protocolinfo_by_port function.
"""
connection_type = test.runner.get_runner().get_connection_type()
if test.runner.OPT_PORT in test.runner.CONNECTION_OPTS[connection_type]:
- protocolinfo_response = stem.connection.get_protocolinfo_port(control_port = test.runner.CONTROL_PORT)
+ protocolinfo_response = stem.connection.get_protocolinfo_by_port(control_port = test.runner.CONTROL_PORT)
self.assertEqual(None, protocolinfo_response.socket)
self.assert_protocolinfo_attr(protocolinfo_response, connection_type)
else:
# we don't have a control port
- self.assertRaises(stem.types.SocketError, stem.connection.get_protocolinfo_port, "127.0.0.1", test.runner.CONTROL_PORT)
+ self.assertRaises(stem.types.SocketError, stem.connection.get_protocolinfo_by_port, "127.0.0.1", test.runner.CONTROL_PORT)
+
+ def test_get_protocolinfo_by_socket(self):
+ """
+ Exercises the stem.connection.get_protocolinfo_by_socket function.
+ """
+
+ connection_type = test.runner.get_runner().get_connection_type()
+
+ if test.runner.OPT_SOCKET in test.runner.CONNECTION_OPTS[connection_type]:
+ protocolinfo_response = stem.connection.get_protocolinfo_by_socket(socket_path = test.runner.CONTROL_SOCKET_PATH)
+ self.assertEqual(None, protocolinfo_response.socket)
+ self.assert_protocolinfo_attr(protocolinfo_response, connection_type)
+ else:
+ # we don't have a control socket
+ self.assertRaises(stem.types.SocketError, stem.connection.get_protocolinfo_by_socket, test.runner.CONTROL_SOCKET_PATH)
def assert_protocolinfo_attr(self, protocolinfo_response, connection_type):
"""
@@ -86,9 +101,9 @@ class TestProtocolInfo(unittest.TestCase):
self.assertEqual((), protocolinfo_response.unknown_auth_methods)
self.assertEqual(auth_methods, protocolinfo_response.auth_methods)
+ auth_cookie_path = None
if test.runner.OPT_COOKIE in test.runner.CONNECTION_OPTS[connection_type]:
auth_cookie_path = test.runner.get_runner().get_auth_cookie_path()
- self.assertEqual(auth_cookie_path, protocolinfo_response.cookie_file)
- else:
- self.assertEqual(None, protocolinfo_response.cookie_file)
+
+ self.assertEqual(auth_cookie_path, protocolinfo_response.cookie_path)
diff --git a/test/unit/connection/protocolinfo.py b/test/unit/connection/protocolinfo.py
index fc9ab64..3014cbb 100644
--- a/test/unit/connection/protocolinfo.py
+++ b/test/unit/connection/protocolinfo.py
@@ -89,7 +89,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
self.assertEquals(stem.types.Version("0.2.1.30"), control_message.tor_version)
self.assertEquals((stem.connection.AuthMethod.NONE, ), control_message.auth_methods)
self.assertEquals((), control_message.unknown_auth_methods)
- self.assertEquals(None, control_message.cookie_file)
+ self.assertEquals(None, control_message.cookie_path)
self.assertEquals(None, control_message.socket)
def test_password_auth(self):
@@ -110,7 +110,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
control_message = stem.types.read_message(StringIO.StringIO(COOKIE_AUTH))
stem.connection.ProtocolInfoResponse.convert(control_message)
self.assertEquals((stem.connection.AuthMethod.COOKIE, ), control_message.auth_methods)
- self.assertEquals("/tmp/my data\\\"dir//control_auth_cookie", control_message.cookie_file)
+ self.assertEquals("/tmp/my data\\\"dir//control_auth_cookie", control_message.cookie_path)
def test_multiple_auth(self):
"""
@@ -120,7 +120,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
control_message = stem.types.read_message(StringIO.StringIO(MULTIPLE_AUTH))
stem.connection.ProtocolInfoResponse.convert(control_message)
self.assertEquals((stem.connection.AuthMethod.COOKIE, stem.connection.AuthMethod.PASSWORD), control_message.auth_methods)
- self.assertEquals("/home/atagar/.tor/control_auth_cookie", control_message.cookie_file)
+ self.assertEquals("/home/atagar/.tor/control_auth_cookie", control_message.cookie_path)
def test_unknown_auth(self):
"""
@@ -145,7 +145,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
self.assertEquals(None , control_message.tor_version)
self.assertEquals((), control_message.auth_methods)
self.assertEquals((), control_message.unknown_auth_methods)
- self.assertEquals(None, control_message.cookie_file)
+ self.assertEquals(None, control_message.cookie_path)
self.assertEquals(None, control_message.socket)
def test_relative_cookie(self):
@@ -169,7 +169,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
control_message = stem.types.read_message(StringIO.StringIO(RELATIVE_COOKIE_PATH))
stem.connection.ProtocolInfoResponse.convert(control_message)
- self.assertEquals("/tmp/foo/tor-browser_en-US/Data/control_auth_cookie", control_message.cookie_file)
+ self.assertEquals("/tmp/foo/tor-browser_en-US/Data/control_auth_cookie", control_message.cookie_path)
# exercise cookie expansion where both calls fail (should work, just
# leaving the path unexpanded)
@@ -177,7 +177,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
stem.util.system.CALL_MOCKING = lambda cmd: None
control_message = stem.types.read_message(StringIO.StringIO(RELATIVE_COOKIE_PATH))
stem.connection.ProtocolInfoResponse.convert(control_message)
- self.assertEquals("./tor-browser_en-US/Data/control_auth_cookie", control_message.cookie_file)
+ self.assertEquals("./tor-browser_en-US/Data/control_auth_cookie", control_message.cookie_path)
# reset system call mocking
stem.util.system.CALL_MOCKING = None
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits