Skip to content

Commit

Permalink
Merge 31a46dd into 24d4aa2
Browse files Browse the repository at this point in the history
  • Loading branch information
david415 committed Aug 29, 2016
2 parents 24d4aa2 + 31a46dd commit 26f77b8
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 104 deletions.
135 changes: 70 additions & 65 deletions test/test_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
from txtorcon import TorOnionAddress
from txtorcon.util import NoOpProtocolFactory
from txtorcon.endpoints import get_global_tor # FIXME
from txtorcon.endpoints import default_tcp4_endpoint_generator

import util

Expand Down Expand Up @@ -592,31 +591,29 @@ def test_client_connection_failed(self):
This test is equivalent to txsocksx's
TestSOCKS4ClientEndpoint.test_clientConnectionFailed
"""
def fail_tor_socks_endpoint_generator(*args, **kw):
kw['failure'] = Failure(ConnectionRefusedError())
return FakeTorSocksEndpoint(*args, **kw)
endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=fail_tor_socks_endpoint_generator)
args = "host123"
kw = dict()
kw['failure'] = Failure(ConnectionRefusedError())
tor_endpoint = FakeTorSocksEndpoint(*args, **kw)
endpoint = TorClientEndpoint('', 0, socks_endpoint=tor_endpoint)
d = endpoint.connect(None)
return self.assertFailure(d, ConnectionRefusedError)

def test_client_connection_failed_user_password(self):
"""
Same as above, but with a username/password.
"""
def fail_tor_socks_endpoint_generator(*args, **kw):
kw['failure'] = Failure(ConnectionRefusedError())
return FakeTorSocksEndpoint(*args, **kw)
args = "fakehost"
kw = dict()
kw['failure'] = Failure(ConnectionRefusedError())
tor_endpoint = FakeTorSocksEndpoint(*args, **kw)
endpoint = TorClientEndpoint(
'invalid host', 0,
socks_username='billy', socks_password='s333cure',
_proxy_endpoint_generator=fail_tor_socks_endpoint_generator)
socks_endpoint = tor_endpoint)
d = endpoint.connect(None)
return self.assertFailure(d, ConnectionRefusedError)

def test_default_generator(self):
# just ensuring the default generator doesn't blow up
default_tcp4_endpoint_generator(None, 'foo.bar', 1234)

def test_no_host(self):
self.assertRaises(
ValueError,
Expand All @@ -628,7 +625,8 @@ def test_parser_basic(self):

self.assertEqual(ep.host, 'timaq4ygg2iegci7.onion')
self.assertEqual(ep.port, 80)
self.assertEqual(ep.socks_port, 9050)
# XXX what's "the Twisted way" to get the port out here?
self.assertEqual(ep.socks_endpoint._port, 9050)

def test_parser_user_password(self):
epstring = 'tor:host=torproject.org:port=443' + \
Expand All @@ -644,15 +642,13 @@ def test_default_factory(self):
"""
This test is equivalent to txsocksx's TestSOCKS5ClientEndpoint.test_defaultFactory
"""
endpoints = []

def tor_socks_endpoint_generator(*args, **kw):
endpoints.append(FakeTorSocksEndpoint(*args, **kw))
return endpoints[-1]
endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=tor_socks_endpoint_generator)
args = "fakehost"
kw = dict()
tor_endpoint = FakeTorSocksEndpoint(*args, **kw)
endpoint = TorClientEndpoint('', 0, socks_endpoint=tor_endpoint)
endpoint.connect(Mock)
self.assertEqual(1, len(endpoints))
self.assertEqual(endpoints[0].transport.value(), '\x05\x01\x00')
self.assertEqual(tor_endpoint.transport.value(), '\x05\x01\x00')

@patch('txtorcon.endpoints.SOCKS5ClientEndpoint')
@defer.inlineCallbacks
Expand All @@ -661,11 +657,10 @@ def test_success(self, socks5_factory):
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)
args = "fakehost"
kw = dict()
tor_endpoint = FakeTorSocksEndpoint(*args, **kw)
endpoint = TorClientEndpoint('', 0, socks_endpoint = tor_endpoint)
other_proto = yield endpoint.connect(MagicMock())
self.assertEqual(other_proto, gold_proto)

Expand All @@ -677,58 +672,68 @@ 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())
endpoints.append(FakeTorSocksEndpoint(*args, **kw))
return endpoints[-1]
endpoint = TorClientEndpoint('', 0, _proxy_endpoint_generator=tor_socks_endpoint_generator)
tor_endpoint = FakeTorSocksEndpoint(
"fakehost", "127.0.0.1", port,
accept_port=port,
failure=Failure(ConnectionRefusedError()),
)

endpoint = TorClientEndpoint('', 0, socks_endpoint=tor_endpoint)
endpoint.connect(None)
self.assertEqual(endpoints[-1].transport.value(), '\x05\x01\x00')
self.assertEqual(tor_endpoint.transport.value(), '\x05\x01\x00')

def test_bad_port_retry(self):
"""
This tests failure to connect to the ports on the "try" list.
"""
fail_ports = [1984, 666]
for port in fail_ports:
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=tor_socks_endpoint_generator)
ep = FakeTorSocksEndpoint(
'', '', 0,
accept_port=port,
failure=Failure(ConnectionRefusedError()),
)
endpoint = TorClientEndpoint('', 0, socks_endpoint=ep)
d = endpoint.connect(None)
return self.assertFailure(d, ConnectionRefusedError)

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())
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(1, len(endpoints))
self.assertEqual(endpoints[-1].transport.value(), '\x05\x01\x00')

def test_bad_no_guess_socks_port(self):
@patch('txtorcon.endpoints.SOCKS5ClientEndpoint')
def test_default_socks_ports_fails(self, ep_mock):
"""
This tests that are connection fails if we try to connect to an unavailable
specified SOCKS port... even if there is a valid SOCKS port listening on
the socks_ports_to_try list.
Ensure we iterate over the default socks ports
"""
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=tor_socks_endpoint_generator, socks_port=6669)

class FakeSocks5(object):

def __init__(self, *args, **kw):
pass

def connect(self, *args, **kw):
raise ConnectionRefusedError()

ep_mock.side_effect = FakeSocks5
endpoint = TorClientEndpoint('', 0)#, socks_endpoint=ep)
d = endpoint.connect(None)
self.assertFailure(d, ConnectionRefusedError)

@patch('txtorcon.endpoints.SOCKS5ClientEndpoint')
@defer.inlineCallbacks
def test_default_socks_ports_happy(self, ep_mock):
"""
Ensure we iterate over the default socks ports
"""

proto = object()
class FakeSocks5(object):

def __init__(self, *args, **kw):
pass

def connect(self, *args, **kw):
return proto

ep_mock.side_effect = FakeSocks5
endpoint = TorClientEndpoint('', 0)
p2 = yield endpoint.connect(None)
self.assertTrue(proto is p2)
82 changes: 43 additions & 39 deletions txtorcon/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,14 +633,6 @@ def parseStreamServer(self, reactor, public_port, localPort=None,
control_port=controlPort)


def default_tcp4_endpoint_generator(*args, **kw):
"""
Default generator used to create client-side TCP4ClientEndpoint
instances. We do this to make the unit tests work...
"""
return TCP4ClientEndpoint(*args, **kw)


@implementer(IStreamClientEndpoint)
class TorClientEndpoint(object):
"""
Expand All @@ -667,55 +659,63 @@ class TorClientEndpoint(object):
socks_ports_to_try = [9050, 9150]

def __init__(self, host, port,
socks_hostname=None, socks_port=None,
socks_username=None, socks_password=None,
_proxy_endpoint_generator=default_tcp4_endpoint_generator):
socks_endpoint=None,
socks_username=None, socks_password=None, **kw):
if host is None or port is None:
raise ValueError('host and port must be specified')

self.host = host
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 is not None else None
self.socks_endpoint = socks_endpoint
self.socks_username = socks_username
self.socks_password = socks_password

if self.socks_port is None:
# backwards-compatibility: you used to specify a TCP SOCKS
# endpoint via socks_host= and socks_port= kwargs
if self.socks_endpoint is None:
try:
self.socks_endpoint = TCP4ClientEndpoint(reactor, kw['socks_host'], kw['socks_port'])
# XXX should deprecation-warn here
except KeyError:
pass

if self.socks_endpoint is None:
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):
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,
kwargs = dict()
if self.socks_username is not None and self.socks_password is not None:
kwargs['methods'] = dict(
login=(self.socks_username, self.socks_password),
)

args = (self.host, self.port, tor_ep)
kwargs = dict()
if self.socks_username is not None and self.socks_password is not None:
kwargs['methods'] = dict(
login=(self.socks_username, self.socks_password),
)

if self.socks_endpoint is not None:
args = (self.host, self.port, self.socks_endpoint)
socks_ep = SOCKS5ClientEndpoint(*args, **kwargs)
proto = yield socks_ep.connect(protocolfactory)
defer.returnValue(proto)
else:
for socks_port in self._socks_port_iter:
tor_ep = TCP4ClientEndpoint(
reactor,
"127.0.0.1",
socks_port,
)
args = (self.host, self.port, tor_ep)
socks_ep = SOCKS5ClientEndpoint(*args, **kwargs)

try:
proto = yield socks_ep.connect(protocolfactory)
defer.returnValue(proto)
try:
proto = yield socks_ep.connect(protocolfactory)
defer.returnValue(proto)

except error.ConnectError as e0:
last_error = e0
if last_error is not None:
raise last_error
except error.ConnectError as e0:
last_error = e0
if last_error is not None:
raise last_error


@implementer(IPlugin, IStreamClientEndpointStringParserWithReactor)
Expand Down Expand Up @@ -762,10 +762,14 @@ def _parseClient(self, host=None, port=None,
if socksPort is not None:
socksPort = int(socksPort)

ep = None
if socksPort is not None:
ep = TCP4ClientEndpoint(reactor, socksHostname, socksPort)
return TorClientEndpoint(
host, port,
socks_hostname=socksHostname, socks_port=socksPort,
socks_username=socksUsername, socks_password=socksPassword
socks_endpoint=ep,
socks_username=socksUsername,
socks_password=socksPassword,
)

def parseStreamClient(self, *args, **kwargs):
Expand Down

0 comments on commit 26f77b8

Please sign in to comment.