commit 3fe2221ae2bad6d826c05fd0ce96e5c09a21fc01 Author: Damian Johnson atagar@torproject.org Date: Sat Feb 3 14:13:27 2018 -0800
Initial ORPort integ tests
Nothing too interesting yet. --- stem/relay.py | 54 +++++++++++++++++++++++++++++----------- test/integ/__init__.py | 1 + test/integ/client/__init__.py | 7 ++++++ test/integ/client/connection.py | 49 ++++++++++++++++++++++++++++++++++++ test/integ/control/controller.py | 4 +-- test/runner.py | 4 ++- test/settings.cfg | 1 + 7 files changed, 103 insertions(+), 17 deletions(-)
diff --git a/stem/relay.py b/stem/relay.py index 7d1e8282..20341721 100644 --- a/stem/relay.py +++ b/stem/relay.py @@ -15,30 +15,34 @@ a wrapper for :class:`~stem.socket.RelaySocket`, much the same way as | +- connect - Establishes a connection with a relay. """
+import stem import stem.client import stem.client.cell import stem.socket import stem.util.connection
-DEFAULT_LINK_VERSIONS = (3, 4, 5) +DEFAULT_LINK_PROTOCOLS = (3, 4, 5)
class Relay(object): """ Connection with a Tor relay's ORPort. + + :var int link_protocol: link protocol version we established """
- def __init__(self, orport): + def __init__(self, orport, link_protocol): + self.link_protocol = link_protocol self._orport = orport
@staticmethod - def connect(address, port, link_versions = DEFAULT_LINK_VERSIONS): + def connect(address, port, link_protocols = DEFAULT_LINK_PROTOCOLS): """ Establishes a connection with the given ORPort.
:param str address: ip address of the relay :param int port: ORPort of the relay - :param tuple link_versions: acceptable link protocol versions + :param tuple link_protocols: acceptable link protocol versions
:raises: * **ValueError** if address or port are invalid @@ -52,20 +56,42 @@ class Relay(object): else: raise ValueError("'%s' isn't an IPv4 or IPv6 address" % address)
- if not stem.util.connection.is_port(port): + if not stem.util.connection.is_valid_port(port): raise ValueError("'%s' isn't a valid port" % port) + elif not link_protocols: + raise ValueError("Connection can't be established without a link protocol.") + + try: + conn = stem.socket.RelaySocket(address, port) + except stem.SocketError as exc: + if 'Connection refused' in str(exc): + raise stem.SocketError("Failed to connect to %s:%i. Maybe it isn't an ORPort?" % (address, port)) + elif 'SSL: UNKNOWN_PROTOCOL' in str(exc): + raise stem.SocketError("Failed to SSL authenticate to %s:%i. Maybe it isn't an ORPort?" % (address, port)) + else: + raise + + conn.send(stem.client.cell.VersionsCell(link_protocols).pack()) + response = conn.recv() + + # Link negotiation ends right away if we lack a common protocol + # version. (#25139)
- conn = stem.socket.RelaySocket(address, port) - conn.send(stem.client.cell.VersionsCell(link_versions).pack()) - versions_reply = stem.client.cell.Cell.pop(conn.recv(), 2)[0] + if not response: + conn.close() + raise stem.SocketError('Unable to establish a common link protocol with %s:%i' % (address, port)) + + versions_reply = stem.client.cell.Cell.pop(response, 2)[0] + common_protocols = set(link_protocols).intersection(versions_reply.versions) + + if not common_protocols: + conn.close() + raise stem.SocketError('Unable to find a common link protocol. We support %s but %s:%i supports %s.' % (', '.join(link_protocols), address, port, ', '.join(versions_reply.versions)))
- # TODO: determine the highest common link versions # TODO: we should fill in our address, right? # TODO: what happens if we skip the NETINFO?
- link_version = 3 - conn.send(stem.client.cell.NetinfoCell(stem.client.Address(address, addr_type), []).pack(link_version)) - - # TODO: what if no link protocol versions are acceptable? + link_protocol = max(common_protocols) + conn.send(stem.client.cell.NetinfoCell(stem.client.Address(address, addr_type), []).pack(link_protocol))
- return Relay(conn) + return Relay(conn, link_protocol) diff --git a/test/integ/__init__.py b/test/integ/__init__.py index 886d1c89..31e6084f 100644 --- a/test/integ/__init__.py +++ b/test/integ/__init__.py @@ -3,6 +3,7 @@ Integration tests for the stem library. """
__all__ = [ + 'client', 'connection', 'control', 'descriptor', diff --git a/test/integ/client/__init__.py b/test/integ/client/__init__.py new file mode 100644 index 00000000..8d77a653 --- /dev/null +++ b/test/integ/client/__init__.py @@ -0,0 +1,7 @@ +""" +Integration tests for tor's ORPort (stem.relay and stem.client). +""" + +__all__ = [ + 'connection', +] diff --git a/test/integ/client/connection.py b/test/integ/client/connection.py new file mode 100644 index 00000000..bb6ae762 --- /dev/null +++ b/test/integ/client/connection.py @@ -0,0 +1,49 @@ +""" +Integration tests for establishing a connection with tor's ORPort. +""" + +import unittest + +import stem +import test.runner + +from stem.relay import Relay + + +class TestConnection(unittest.TestCase): + def test_invalid_arguments(self): + """ + Provide invalid arguments to Relay.connect(). + """ + + self.assertRaisesRegexp(ValueError, "'nope' isn't an IPv4 or IPv6 address", Relay.connect, 'nope', 80) + self.assertRaisesRegexp(ValueError, "'-54' isn't a valid port", Relay.connect, '127.0.0.1', -54) + self.assertRaisesRegexp(ValueError, "Connection can't be established without a link protocol.", Relay.connect, '127.0.0.1', 54, []) + + def test_not_orport(self): + """ + Attempt to connect to an ORPort that doesn't exist. + """ + + self.assertRaisesRegexp(stem.SocketError, "Failed to connect to 127.0.0.1:1587. Maybe it isn't an ORPort?", Relay.connect, '127.0.0.1', 1587) + + # connect to our ControlPort like it's an ORPort + + if test.runner.Torrc.PORT in test.runner.get_runner().get_options(): + self.assertRaisesRegexp(stem.SocketError, "Failed to SSL authenticate to 127.0.0.1:1111. Maybe it isn't an ORPort?", Relay.connect, '127.0.0.1', test.runner.CONTROL_PORT) + + def test_no_common_link_protocol(self): + """ + Connection without a commonly accepted link protocol version. + """ + + for link_protocol in (1, 2, 6, 20): + self.assertRaisesRegexp(stem.SocketError, 'Unable to establish a common link protocol with 127.0.0.1:1113', Relay.connect, '127.0.0.1', test.runner.ORPORT, [link_protocol]) + + def test_established(self): + """ + Successfully establish ORPort connection. + """ + + conn = Relay.connect('127.0.0.1', test.runner.ORPORT) + self.assertEqual(5, conn.link_protocol) diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py index b2ba77a5..16061eee 100644 --- a/test/integ/control/controller.py +++ b/test/integ/control/controller.py @@ -845,7 +845,7 @@ class TestController(unittest.TestCase): runner = test.runner.get_runner()
with runner.get_tor_controller() as controller: - self.assertEqual([], controller.get_ports(Listener.OR)) + self.assertEqual([test.runner.ORPORT], controller.get_ports(Listener.OR)) self.assertEqual([], controller.get_ports(Listener.DIR)) self.assertEqual([test.runner.SOCKS_PORT], controller.get_ports(Listener.SOCKS)) self.assertEqual([], controller.get_ports(Listener.TRANS)) @@ -866,7 +866,7 @@ class TestController(unittest.TestCase): runner = test.runner.get_runner()
with runner.get_tor_controller() as controller: - self.assertEqual([], controller.get_listeners(Listener.OR)) + self.assertEqual([('0.0.0.0', test.runner.ORPORT)], controller.get_listeners(Listener.OR)) self.assertEqual([], controller.get_listeners(Listener.DIR)) self.assertEqual([('127.0.0.1', test.runner.SOCKS_PORT)], controller.get_listeners(Listener.SOCKS)) self.assertEqual([], controller.get_listeners(Listener.TRANS)) diff --git a/test/runner.py b/test/runner.py index 0a307c87..fe01f74f 100644 --- a/test/runner.py +++ b/test/runner.py @@ -58,14 +58,16 @@ CONFIG = stem.util.conf.config_dict('test', { })
SOCKS_PORT = 1112 +ORPORT = 1113
BASE_TORRC = """# configuration for stem integration tests DataDirectory %%s SocksPort %i +ORPort %i DownloadExtraInfo 1 Log notice stdout Log notice file %%s/tor_log -""" % SOCKS_PORT +""" % (SOCKS_PORT, ORPORT)
# singleton Runner instance INTEG_RUNNER = None diff --git a/test/settings.cfg b/test/settings.cfg index 4080ca2a..6d543bac 100644 --- a/test/settings.cfg +++ b/test/settings.cfg @@ -252,6 +252,7 @@ test.integ_tests |test.integ.interpreter.TestInterpreter |test.integ.version.TestVersion |test.integ.manual.TestManual +|test.integ.client.connection.TestConnection |test.integ.response.protocolinfo.TestProtocolInfo |test.integ.socket.control_socket.TestControlSocket |test.integ.socket.control_message.TestControlMessage
tor-commits@lists.torproject.org