From 1b6060bf174499c8c3eb4934c7d63d6e32f616c2 Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 10:27:14 -0600 Subject: [PATCH 01/17] fix comment --- test/test_endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index b7539fbb..1361f31c 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -612,7 +612,7 @@ def FailTorSocksEndpointGenerator(*args, **kw): return self.assertFailure(d, ConnectionRefusedError) def test_default_generator(self): - # just ensuring the default generator doesn't blow updoesn't blow up + # just ensuring the default generator doesn't blow up default_tcp4_endpoint_generator(None, 'foo.bar', 1234) def test_no_host(self): From 3b6addd5f0df467974ff5aad400a8496bb1b8fcf Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 14:05:54 -0600 Subject: [PATCH 02/17] unit-test for StopIteration/SOCKS issue --- docs/releases.rst | 3 +++ test/test_endpoints.py | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/docs/releases.rst b/docs/releases.rst index fa09ae79..fc277595 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -15,6 +15,9 @@ unreleased commands in TorConfig * a first stealth-authentication implementation (for "normal" hidden services, not ephemeral) + * bug-fix from `david415 `_ to raise + ConnectionRefusedError instead of StopIteration when running out of + SOCKS ports. v0.14.0 diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 1361f31c..5b98346e 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -615,6 +615,18 @@ def test_default_generator(self): # just ensuring the default generator doesn't blow up default_tcp4_endpoint_generator(None, 'foo.bar', 1234) + @defer.inlineCallbacks + def test_stop_iteration_becomes_refused(self): + fake_endpoint = Mock() + proto_factory = Mock() + + TorClientEndpoint.socks_ports_to_try = [] + ep = TorClientEndpoint('a host', 0) + try: + yield ep.connect(proto_factory) + except Exception as e: + self.assertTrue(isinstance(e, ConnectionRefusedError)) + def test_no_host(self): self.assertRaises( ValueError, From 3fe2ad5aa58d4b1c47d0c880f9956dbc1573b3cd Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 14:43:12 -0600 Subject: [PATCH 03/17] pep8 naming and refactor ConnectionRefusedError --- test/test_endpoints.py | 39 ++++++++++++++++++--------------------- txtorcon/endpoints.py | 3 ++- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 5b98346e..4b2d47fe 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -39,9 +39,6 @@ import util -connectionRefusedFailure = Failure(ConnectionRefusedError()) - - class EndpointTests(unittest.TestCase): def setUp(self): @@ -566,12 +563,12 @@ def __init__(self, *args, **kw): self.transport = None self.failure = kw.get('failure', None) - self.acceptPort = kw.get('acceptPort', None) + self.accept_port = kw.get('accept_port', None) def connect(self, fac): self.factory = fac - if self.acceptPort: - if self.port != self.acceptPort: + if self.accept_port: + if self.port != self.accept_port: return defer.fail(self.failure) else: if self.failure: @@ -591,7 +588,7 @@ def test_client_connection_failed(self): TestSOCKS4ClientEndpoint.test_clientConnectionFailed """ def FailTorSocksEndpointGenerator(*args, **kw): - kw['failure'] = connectionRefusedFailure + kw['failure'] = Failure(ConnectionRefusedError()) return FakeTorSocksEndpoint(*args, **kw) endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=FailTorSocksEndpointGenerator) d = endpoint.connect(None) @@ -602,7 +599,7 @@ def test_client_connection_failed_user_password(self): Same as above, but with a username/password. """ def FailTorSocksEndpointGenerator(*args, **kw): - kw['failure'] = connectionRefusedFailure + kw['failure'] = Failure(ConnectionRefusedError()) return FakeTorSocksEndpoint(*args, **kw) endpoint = TorClientEndpoint( 'invalid host', 0, @@ -625,7 +622,7 @@ def test_stop_iteration_becomes_refused(self): try: yield ep.connect(proto_factory) except Exception as e: - self.assertTrue(isinstance(e, ConnectionRefusedError)) + self.assertIsInstance(e, ConnectionRefusedError) def test_no_host(self): self.assertRaises( @@ -663,18 +660,18 @@ def TorSocksEndpointGenerator(*args, **kw): def test_good_port_retry(self): """ This tests that our Tor client endpoint retry logic works correctly. - We create a proxy endpoint that fires a connectionRefusedFailure + We create a proxy endpoint that fires a ConnectionRefusedError unless the connecting port matches. We attempt to connect with the proxy endpoint for each port that the Tor client endpoint will try. """ success_ports = TorClientEndpoint.socks_ports_to_try for port in success_ports: - def TorSocksEndpointGenerator(*args, **kw): - kw['acceptPort'] = port - kw['failure'] = connectionRefusedFailure + def tor_socks_endpoint_generator(*args, **kw): + kw['accept_port'] = port + kw['failure'] = Failure(ConnectionRefusedError()) return FakeTorSocksEndpoint(*args, **kw) - endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=TorSocksEndpointGenerator) - endpoint.connect(None) + endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=tor_socks_endpoint_generator) + endpoint.connect(Mock) self.assertEqual(endpoint.tor_socks_endpoint.transport.value(), '\x05\x01\x00') def test_bad_port_retry(self): @@ -684,8 +681,8 @@ def test_bad_port_retry(self): fail_ports = [1984, 666] for port in fail_ports: def TorSocksEndpointGenerator(*args, **kw): - kw['acceptPort'] = port - kw['failure'] = connectionRefusedFailure + kw['accept_port'] = port + kw['failure'] = Failure(ConnectionRefusedError()) return FakeTorSocksEndpoint(*args, **kw) endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=TorSocksEndpointGenerator) d = endpoint.connect(None) @@ -697,8 +694,8 @@ def test_good_no_guess_socks_port(self): connect to that SOCKS port. """ def TorSocksEndpointGenerator(*args, **kw): - kw['acceptPort'] = 6669 - kw['failure'] = connectionRefusedFailure + kw['accept_port'] = 6669 + kw['failure'] = Failure(ConnectionRefusedError()) return FakeTorSocksEndpoint(*args, **kw) endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=TorSocksEndpointGenerator, socks_port=6669) endpoint.connect(None) @@ -711,8 +708,8 @@ def test_bad_no_guess_socks_port(self): the socks_ports_to_try list. """ def TorSocksEndpointGenerator(*args, **kw): - kw['acceptPort'] = 9050 - kw['failure'] = connectionRefusedFailure + kw['accept_port'] = 9050 + kw['failure'] = Failure(ConnectionRefusedError()) return FakeTorSocksEndpoint(*args, **kw) endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=TorSocksEndpointGenerator, socks_port=6669) d = endpoint.connect(None) diff --git a/txtorcon/endpoints.py b/txtorcon/endpoints.py index 1a572edb..45157deb 100644 --- a/txtorcon/endpoints.py +++ b/txtorcon/endpoints.py @@ -23,10 +23,11 @@ from twisted.internet.endpoints import serverFromString from twisted.internet.endpoints import clientFromString from twisted.internet.endpoints import TCP4ClientEndpoint +from twisted.internet.error import ConnectionRefusedError from twisted.internet import error from twisted.plugin import IPlugin from twisted.python.util import FancyEqMixin -from twisted.internet.error import ConnectionRefusedError +from twisted.python.failure import Failure from zope.interface import implementer from zope.interface import Interface, Attribute From 20d5d82145ac8b8125954d64e161d18494426e0a Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 14:45:05 -0600 Subject: [PATCH 04/17] behave properly if socks_port is 0 --- txtorcon/endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/txtorcon/endpoints.py b/txtorcon/endpoints.py index 45157deb..bd03cd68 100644 --- a/txtorcon/endpoints.py +++ b/txtorcon/endpoints.py @@ -663,7 +663,7 @@ def __init__(self, host, port, self.port = int(port) self._proxy_endpoint_generator = _proxy_endpoint_generator self.socks_hostname = socks_hostname - self.socks_port = int(socks_port) if socks_port else None + self.socks_port = int(socks_port) if socks_port is not None else None self.socks_username = socks_username self.socks_password = socks_password From b126bd8640014a41b7a3619e9c9d462efb5c6797 Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 14:46:12 -0600 Subject: [PATCH 05/17] refactor retry logic to use @inlineCallbacks --- txtorcon/endpoints.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/txtorcon/endpoints.py b/txtorcon/endpoints.py index bd03cd68..a1944e73 100644 --- a/txtorcon/endpoints.py +++ b/txtorcon/endpoints.py @@ -673,14 +673,27 @@ def __init__(self, host, port, else: self._socks_guessing_enabled = False + @defer.inlineCallbacks def connect(self, protocolfactory): self.protocolfactory = protocolfactory + # if this fails, not even a single port was configured, so we + # let it bubble up if self._socks_guessing_enabled: self.socks_port = self._socks_port_iter.next() - d = self._try_connect() - return d + while True: + try: + yield self._try_connect() + + except error.ConnectError as e0: + if self._socks_guessing_enabled: + try: + self.socks_port = self._socks_port_iter.next() + continue + except StopIteration: + pass # fall through and re-raise e0 + raise e0 def _try_connect(self): self.tor_socks_endpoint = self._proxy_endpoint_generator( @@ -704,18 +717,6 @@ def _try_connect(self): ) d = ep.connect(self.protocolfactory) - if self._socks_guessing_enabled: - d.addErrback(self._retry_socks_port) - return d - - def _retry_socks_port(self, failure): - failure.trap(error.ConnectError) - try: - self.socks_port = self._socks_port_iter.next() - except StopIteration: - return defer.fail(ConnectionRefusedError('tor socks port retry failed')) - d = self._try_connect() - d.addErrback(self._retry_socks_port) return d From a999f3947a7076243fd8535d855e84dca883794f Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 14:47:27 -0600 Subject: [PATCH 06/17] more pep8 naming --- test/test_endpoints.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 4b2d47fe..90a42642 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -587,10 +587,10 @@ def test_client_connection_failed(self): This test is equivalent to txsocksx's TestSOCKS4ClientEndpoint.test_clientConnectionFailed """ - def FailTorSocksEndpointGenerator(*args, **kw): + def fail_tor_socks_endpoint_generator(*args, **kw): kw['failure'] = Failure(ConnectionRefusedError()) return FakeTorSocksEndpoint(*args, **kw) - endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=FailTorSocksEndpointGenerator) + endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=fail_tor_socks_endpoint_generator) d = endpoint.connect(None) return self.assertFailure(d, ConnectionRefusedError) @@ -598,13 +598,13 @@ def test_client_connection_failed_user_password(self): """ Same as above, but with a username/password. """ - def FailTorSocksEndpointGenerator(*args, **kw): + def fail_tor_socks_endpoint_generator(*args, **kw): kw['failure'] = Failure(ConnectionRefusedError()) return FakeTorSocksEndpoint(*args, **kw) endpoint = TorClientEndpoint( 'invalid host', 0, socks_username='billy', socks_password='s333cure', - _proxy_endpoint_generator=FailTorSocksEndpointGenerator) + _proxy_endpoint_generator=fail_tor_socks_endpoint_generator) d = endpoint.connect(None) return self.assertFailure(d, ConnectionRefusedError) From 41bdf9f96ef9a0b447b1d1ff2f75699a4d9cec7a Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 17:05:37 -0600 Subject: [PATCH 07/17] only replace socks_port_to_try for single test and some pep8 naming --- test/test_endpoints.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 90a42642..f5cb86e5 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -617,12 +617,12 @@ def test_stop_iteration_becomes_refused(self): fake_endpoint = Mock() proto_factory = Mock() - TorClientEndpoint.socks_ports_to_try = [] - ep = TorClientEndpoint('a host', 0) - try: - yield ep.connect(proto_factory) - except Exception as e: - self.assertIsInstance(e, ConnectionRefusedError) + with patch.object(TorClientEndpoint, 'socks_ports_to_try', []): + ep = TorClientEndpoint('a host', 0) + try: + yield ep.connect(proto_factory) + except Exception as e: + self.assertIsInstance(e, ConnectionRefusedError) def test_no_host(self): self.assertRaises( @@ -680,11 +680,11 @@ def test_bad_port_retry(self): """ fail_ports = [1984, 666] for port in fail_ports: - def TorSocksEndpointGenerator(*args, **kw): + def tor_socks_endpoint_generator(*args, **kw): kw['accept_port'] = port kw['failure'] = Failure(ConnectionRefusedError()) return FakeTorSocksEndpoint(*args, **kw) - endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=TorSocksEndpointGenerator) + endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=tor_socks_endpoint_generator) d = endpoint.connect(None) return self.assertFailure(d, ConnectionRefusedError) @@ -693,11 +693,11 @@ def test_good_no_guess_socks_port(self): This tests that if a SOCKS port is specified, we *only* attempt to connect to that SOCKS port. """ - def TorSocksEndpointGenerator(*args, **kw): + def tor_socks_endpoint_generator(*args, **kw): kw['accept_port'] = 6669 kw['failure'] = Failure(ConnectionRefusedError()) return FakeTorSocksEndpoint(*args, **kw) - endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=TorSocksEndpointGenerator, socks_port=6669) + endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=tor_socks_endpoint_generator, socks_port=6669) endpoint.connect(None) self.assertEqual(endpoint.tor_socks_endpoint.transport.value(), '\x05\x01\x00') @@ -707,10 +707,10 @@ def test_bad_no_guess_socks_port(self): specified SOCKS port... even if there is a valid SOCKS port listening on the socks_ports_to_try list. """ - def TorSocksEndpointGenerator(*args, **kw): + def tor_socks_endpoint_generator(*args, **kw): kw['accept_port'] = 9050 kw['failure'] = Failure(ConnectionRefusedError()) return FakeTorSocksEndpoint(*args, **kw) - endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=TorSocksEndpointGenerator, socks_port=6669) + endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=tor_socks_endpoint_generator, socks_port=6669) d = endpoint.connect(None) self.assertFailure(d, ConnectionRefusedError) From e5b10fd11ee9d9b0abe92402f01b15acc24580bc Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 17:55:37 -0600 Subject: [PATCH 08/17] pep8 naming --- test/test_endpoints.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index f5cb86e5..40e80833 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -651,10 +651,10 @@ def test_default_factory(self): """ This test is equivalent to txsocksx's TestSOCKS5ClientEndpoint.test_defaultFactory """ - def TorSocksEndpointGenerator(*args, **kw): + def tor_socks_endpoint_generator(*args, **kw): return FakeTorSocksEndpoint(*args, **kw) - endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=TorSocksEndpointGenerator) - endpoint.connect(None) + endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=tor_socks_endpoint_generator) + endpoint.connect(Mock) self.assertEqual(endpoint.tor_socks_endpoint.transport.value(), '\x05\x01\x00') def test_good_port_retry(self): From 0a8ce84b1713e59434240e0117f7b33a1cb20c0c Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 17:55:58 -0600 Subject: [PATCH 09/17] test successful return of protocol --- test/test_endpoints.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 40e80833..ed4b6ee4 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -3,7 +3,7 @@ import tempfile from mock import patch -from mock import Mock +from mock import Mock, MagicMock from zope.interface import implements @@ -657,6 +657,21 @@ def tor_socks_endpoint_generator(*args, **kw): endpoint.connect(Mock) self.assertEqual(endpoint.tor_socks_endpoint.transport.value(), '\x05\x01\x00') + @patch('txtorcon.endpoints.SOCKS5ClientEndpoint') + @defer.inlineCallbacks + def test_success(self, socks5_factory): + ep = MagicMock() + gold_proto = object() + ep.connect = MagicMock(return_value=gold_proto) + socks5_factory.return_value = ep + + def tor_socks_endpoint_generator(*args, **kw): + return FakeTorSocksEndpoint(*args, **kw) + + endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=tor_socks_endpoint_generator) + other_proto = yield endpoint.connect(MagicMock()) + self.assertEqual(other_proto, gold_proto) + def test_good_port_retry(self): """ This tests that our Tor client endpoint retry logic works correctly. From adcee1743ebdf24313bb9323c4afd16cafa793d9 Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 17:56:59 -0600 Subject: [PATCH 10/17] refactor _try_connect into @inlineCallbacks method --- txtorcon/endpoints.py | 48 +++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/txtorcon/endpoints.py b/txtorcon/endpoints.py index a1944e73..7cf87e64 100644 --- a/txtorcon/endpoints.py +++ b/txtorcon/endpoints.py @@ -675,16 +675,34 @@ def __init__(self, host, port, @defer.inlineCallbacks def connect(self, protocolfactory): - self.protocolfactory = protocolfactory - # if this fails, not even a single port was configured, so we # let it bubble up if self._socks_guessing_enabled: self.socks_port = self._socks_port_iter.next() while True: + self.tor_socks_endpoint = self._proxy_endpoint_generator( + reactor, + self.socks_hostname, + self.socks_port, + ) + if self.socks_username is None or self.socks_password is None: + ep = SOCKS5ClientEndpoint( + self.host, + self.port, + self.tor_socks_endpoint + ) + else: + ep = SOCKS5ClientEndpoint( + self.host, + self.port, + self.tor_socks_endpoint, + methods=dict(login=(self.socks_username, self.socks_password)) + ) + try: - yield self._try_connect() + proto = yield ep.connect(protocolfactory) + defer.returnValue(proto) except error.ConnectError as e0: if self._socks_guessing_enabled: @@ -695,30 +713,6 @@ def connect(self, protocolfactory): pass # fall through and re-raise e0 raise e0 - def _try_connect(self): - self.tor_socks_endpoint = self._proxy_endpoint_generator( - reactor, - self.socks_hostname, - self.socks_port - ) - - if self.socks_username is None or self.socks_password is None: - ep = SOCKS5ClientEndpoint( - self.host, - self.port, - self.tor_socks_endpoint - ) - else: - ep = SOCKS5ClientEndpoint( - self.host, - self.port, - self.tor_socks_endpoint, - methods=dict(login=(self.socks_username, self.socks_password)) - ) - - d = ep.connect(self.protocolfactory) - return d - @implementer(IPlugin, IStreamClientEndpointStringParser) class TorClientEndpointStringParser(object): From 66435d755fd64c00515e226793d19112bf587849 Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 17:58:54 -0600 Subject: [PATCH 11/17] more refactoring --- txtorcon/endpoints.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/txtorcon/endpoints.py b/txtorcon/endpoints.py index 7cf87e64..edaa5bd0 100644 --- a/txtorcon/endpoints.py +++ b/txtorcon/endpoints.py @@ -686,20 +686,19 @@ def connect(self, protocolfactory): self.socks_hostname, self.socks_port, ) - if self.socks_username is None or self.socks_password is None: - ep = SOCKS5ClientEndpoint( - self.host, - self.port, - self.tor_socks_endpoint - ) - else: - ep = SOCKS5ClientEndpoint( - self.host, - self.port, - self.tor_socks_endpoint, - methods=dict(login=(self.socks_username, self.socks_password)) + kw = dict() + if self.socks_username is not None and self.socks_password is not None: + kw['methods'] = dict( + login=(self.socks_username, self.socks_password), ) + ep = SOCKS5ClientEndpoint( + self.host, + self.port, + self.tor_socks_endpoint, + **kw + ) + try: proto = yield ep.connect(protocolfactory) defer.returnValue(proto) From 8eb3530081ad505fe45307a53cc1022777073036 Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 18:19:51 -0600 Subject: [PATCH 12/17] refactor: real loop + eliminate state --- txtorcon/endpoints.py | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/txtorcon/endpoints.py b/txtorcon/endpoints.py index edaa5bd0..31508610 100644 --- a/txtorcon/endpoints.py +++ b/txtorcon/endpoints.py @@ -671,46 +671,37 @@ def __init__(self, host, port, self._socks_port_iter = iter(self.socks_ports_to_try) self._socks_guessing_enabled = True else: + self._socks_port_iter = [socks_port] self._socks_guessing_enabled = False @defer.inlineCallbacks def connect(self, protocolfactory): - # if this fails, not even a single port was configured, so we - # let it bubble up - if self._socks_guessing_enabled: - self.socks_port = self._socks_port_iter.next() - - while True: - self.tor_socks_endpoint = self._proxy_endpoint_generator( + last_error = None + for socks_port in self._socks_port_iter: + self.socks_port = socks_port + tor_ep = self._proxy_endpoint_generator( reactor, self.socks_hostname, self.socks_port, ) - kw = dict() + + args = (self.host, self.port, tor_ep) + kwargs = dict() if self.socks_username is not None and self.socks_password is not None: - kw['methods'] = dict( + kwargs['methods'] = dict( login=(self.socks_username, self.socks_password), ) - ep = SOCKS5ClientEndpoint( - self.host, - self.port, - self.tor_socks_endpoint, - **kw - ) + socks_ep = SOCKS5ClientEndpoint(*args, **kwargs) try: - proto = yield ep.connect(protocolfactory) + proto = yield socks_ep.connect(protocolfactory) defer.returnValue(proto) except error.ConnectError as e0: - if self._socks_guessing_enabled: - try: - self.socks_port = self._socks_port_iter.next() - continue - except StopIteration: - pass # fall through and re-raise e0 - raise e0 + last_error = e0 + if last_error is not None: + raise last_error @implementer(IPlugin, IStreamClientEndpointStringParser) From ee8918fd879bcb3489c43bed5b987eabc99a6d24 Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 18:20:01 -0600 Subject: [PATCH 13/17] refactor unit-tests to not depend on endpoint state --- test/test_endpoints.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index ed4b6ee4..f01c476b 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -651,11 +651,14 @@ def test_default_factory(self): """ This test is equivalent to txsocksx's TestSOCKS5ClientEndpoint.test_defaultFactory """ + endpoints = [] def tor_socks_endpoint_generator(*args, **kw): - return FakeTorSocksEndpoint(*args, **kw) + endpoints.append(FakeTorSocksEndpoint(*args, **kw)) + return endpoints[-1] endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=tor_socks_endpoint_generator) endpoint.connect(Mock) - self.assertEqual(endpoint.tor_socks_endpoint.transport.value(), '\x05\x01\x00') + self.assertEqual(1, len(endpoints)) + self.assertEqual(endpoints[0].transport.value(), '\x05\x01\x00') @patch('txtorcon.endpoints.SOCKS5ClientEndpoint') @defer.inlineCallbacks @@ -680,14 +683,16 @@ def test_good_port_retry(self): proxy endpoint for each port that the Tor client endpoint will try. """ success_ports = TorClientEndpoint.socks_ports_to_try + endpoints = [] for port in success_ports: def tor_socks_endpoint_generator(*args, **kw): kw['accept_port'] = port kw['failure'] = Failure(ConnectionRefusedError()) - return FakeTorSocksEndpoint(*args, **kw) + endpoints.append(FakeTorSocksEndpoint(*args, **kw)) + return endpoints[-1] endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=tor_socks_endpoint_generator) - endpoint.connect(Mock) - self.assertEqual(endpoint.tor_socks_endpoint.transport.value(), '\x05\x01\x00') + endpoint.connect(None) + self.assertEqual(endpoints[-1].transport.value(), '\x05\x01\x00') def test_bad_port_retry(self): """ @@ -708,13 +713,16 @@ def test_good_no_guess_socks_port(self): This tests that if a SOCKS port is specified, we *only* attempt to connect to that SOCKS port. """ + endpoints = [] def tor_socks_endpoint_generator(*args, **kw): kw['accept_port'] = 6669 kw['failure'] = Failure(ConnectionRefusedError()) - return FakeTorSocksEndpoint(*args, **kw) + endpoints.append(FakeTorSocksEndpoint(*args, **kw)) + return endpoints[-1] endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=tor_socks_endpoint_generator, socks_port=6669) endpoint.connect(None) - self.assertEqual(endpoint.tor_socks_endpoint.transport.value(), '\x05\x01\x00') + self.assertEqual(1, len(endpoints)) + self.assertEqual(endpoints[-1].transport.value(), '\x05\x01\x00') def test_bad_no_guess_socks_port(self): """ From 34e8bb8222844e4d578f88a11f8ddbce807fe30d Mon Sep 17 00:00:00 2001 From: meejah Date: Fri, 9 Oct 2015 18:27:33 -0600 Subject: [PATCH 14/17] remove redundant test --- test/test_endpoints.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index f01c476b..e39e0a43 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -612,18 +612,6 @@ def test_default_generator(self): # just ensuring the default generator doesn't blow up default_tcp4_endpoint_generator(None, 'foo.bar', 1234) - @defer.inlineCallbacks - def test_stop_iteration_becomes_refused(self): - fake_endpoint = Mock() - proto_factory = Mock() - - with patch.object(TorClientEndpoint, 'socks_ports_to_try', []): - ep = TorClientEndpoint('a host', 0) - try: - yield ep.connect(proto_factory) - except Exception as e: - self.assertIsInstance(e, ConnectionRefusedError) - def test_no_host(self): self.assertRaises( ValueError, From b93e72cbecfd8c04cd1ab89bebb119be8dbcb04e Mon Sep 17 00:00:00 2001 From: David Stainton Date: Sat, 14 Nov 2015 14:34:56 +0100 Subject: [PATCH 15/17] Fix key reload bug for ephemeral onions --- txtorcon/torconfig.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/txtorcon/torconfig.py b/txtorcon/torconfig.py index 36215cf2..b78f77da 100644 --- a/txtorcon/torconfig.py +++ b/txtorcon/torconfig.py @@ -861,7 +861,8 @@ def add_to_tor(self, protocol): ans = yield protocol.queue_command(cmd) ans = find_keywords(ans.split('\n')) self.hostname = ans['ServiceID'] + '.onion' - self.private_key = ans['PrivateKey'] + if self._key_blob == 'NEW:BEST': + self.private_key = ans['PrivateKey'] log.msg('Created hidden-service at', self.hostname) From 75b97dfb56749500693c2348181a76ea1743ecda Mon Sep 17 00:00:00 2001 From: David Stainton Date: Sat, 14 Nov 2015 22:02:55 +0100 Subject: [PATCH 16/17] Fix EphemeralHiddenService key blob size constrain --- txtorcon/torconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/txtorcon/torconfig.py b/txtorcon/torconfig.py index 36215cf2..463ea510 100644 --- a/txtorcon/torconfig.py +++ b/txtorcon/torconfig.py @@ -846,8 +846,8 @@ def __init__(self, ports, key_blob_or_type='NEW:BEST', auth=[], ver=2): # FIXME nicer than assert, plz assert ' ' not in self._key_blob assert type(ports) is types.ListType - if not key_blob_or_type.startswith('NEW:') and len(key_blob_or_type) != (812 + 8): - raise RuntimeError('Wrong size key-blob') + if not key_blob_or_type.startswith('NEW:') and len(key_blob_or_type) > (825): + raise RuntimeError('Wrong key-blob size too big') @defer.inlineCallbacks def add_to_tor(self, protocol): From 61dfea27101b5ff5d4a58bb03098c8f9290a439c Mon Sep 17 00:00:00 2001 From: David Stainton Date: Sun, 15 Nov 2015 09:29:42 +0100 Subject: [PATCH 17/17] Fix EphemeralHiddenService key blob size constraints so the test passes --- txtorcon/torconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/txtorcon/torconfig.py b/txtorcon/torconfig.py index 463ea510..5544e7a8 100644 --- a/txtorcon/torconfig.py +++ b/txtorcon/torconfig.py @@ -846,7 +846,7 @@ def __init__(self, ports, key_blob_or_type='NEW:BEST', auth=[], ver=2): # FIXME nicer than assert, plz assert ' ' not in self._key_blob assert type(ports) is types.ListType - if not key_blob_or_type.startswith('NEW:') and len(key_blob_or_type) > (825): + if not key_blob_or_type.startswith('NEW:') and (len(key_blob_or_type) > (825) or len(key_blob_or_type) < (820)): raise RuntimeError('Wrong key-blob size too big') @defer.inlineCallbacks