[Pkg-privacy-commits] [txtorcon] 17/49: Cleanups and more tests for "tor:" endpoint parser
Ximin Luo
infinity0 at debian.org
Mon Oct 19 13:49:51 UTC 2015
This is an automated email from the git hooks/post-receive script.
infinity0 pushed a commit to branch master
in repository txtorcon.
commit 57bcbc41e44b1ce34f02675ebcdbfdd212984d21
Author: meejah <meejah at meejah.ca>
Date: Thu Feb 12 21:49:01 2015 -0700
Cleanups and more tests for "tor:" endpoint parser
still a few FIXMEs before this is "done".
---
docs/releases.rst | 6 ++
requirements.txt | 1 +
test/test_endpoints.py | 67 ++++++++++++++----
twisted/plugins/txtorcon_endpoint_parser.py | 1 +
txtorcon/endpoints.py | 103 +++++++++++++++++++---------
txtorcon/torstate.py | 3 +-
6 files changed, 137 insertions(+), 44 deletions(-)
diff --git a/docs/releases.rst b/docs/releases.rst
index d660359..c9925ce 100644
--- a/docs/releases.rst
+++ b/docs/releases.rst
@@ -14,6 +14,12 @@ unreleased
* :class:`txtorcon.interface.IStreamAttacher` handling was missing ``None`` and ``DO_NOT_ATTACH`` cases if a Deferred was returned.
* add ``.is_built`` Deferred to :class:`txtorcon.Circuit` that get `callback()`d when the circuit becomes BUILT
+ * `david415 <https://github.com/david415>`_ ported his ``tor:``
+ endpoint parser so now both client and server endpoints are
+ supported. This means **any** Twisted program using endpoints can
+ use Tor as a client. For example, to connect to txtorcon's Web site:
+ ``ep = clientFromString("tor:timaq4ygg2iegci7.onion:80")``.
+ (In the future, I'd like to automatically launch Tor if required, too).
v0.13.0
diff --git a/requirements.txt b/requirements.txt
index 80a733f..58935e7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,3 +4,4 @@
Twisted>=11.1.0
ipaddr>=2.1.10
zope.interface>=3.6.1
+txsocksx>=1.13.0
diff --git a/test/test_endpoints.py b/test/test_endpoints.py
index 45ced81..2a730c9 100644
--- a/test/test_endpoints.py
+++ b/test/test_endpoints.py
@@ -34,6 +34,7 @@ from txtorcon import IProgressProvider
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
@@ -562,9 +563,10 @@ class FakeTorSocksEndpoint(object):
class TestTorClientEndpoint(unittest.TestCase):
- def test_clientConnectionFailed(self):
+ def test_client_connection_failed(self):
"""
- This test is equivalent to txsocksx's TestSOCKS4ClientEndpoint.test_clientConnectionFailed
+ This test is equivalent to txsocksx's
+ TestSOCKS4ClientEndpoint.test_clientConnectionFailed
"""
def FailTorSocksEndpointGenerator(*args, **kw):
kw['failure'] = connectionRefusedFailure
@@ -573,7 +575,48 @@ class TestTorClientEndpoint(unittest.TestCase):
d = endpoint.connect(None)
return self.assertFailure(d, ConnectionRefusedError)
- def test_defaultFactory(self):
+ def test_client_connection_failed_user_password(self):
+ """
+ Same as above, but with a username/password.
+ """
+ def FailTorSocksEndpointGenerator(*args, **kw):
+ kw['failure'] = connectionRefusedFailure
+ return FakeTorSocksEndpoint(*args, **kw)
+ endpoint = TorClientEndpoint(
+ 'invalid host', 0,
+ socks_username='billy', socks_password='s333cure',
+ proxyEndpointGenerator=FailTorSocksEndpointGenerator)
+ d = endpoint.connect(None)
+ return self.assertFailure(d, ConnectionRefusedError)
+
+ def test_default_generator(self):
+ # just ensuring the default generator doesn't blow updoesn't blow up
+ default_tcp4_endpoint_generator(None, 'foo.bar', 1234)
+
+ def test_no_host(self):
+ self.assertRaises(
+ ValueError,
+ TorClientEndpoint, None, None
+ )
+
+ def test_parser_basic(self):
+ ep = clientFromString(None, 'tor:host=timaq4ygg2iegci7.onion:port=80:socksPort=9050')
+
+ self.assertEqual(ep.host, 'timaq4ygg2iegci7.onion')
+ self.assertEqual(ep.port, 80)
+ self.assertEqual(ep.socks_port, 9050)
+
+ def test_parser_user_password(self):
+ epstring = 'tor:host=torproject.org:port=443' + \
+ ':socksUsername=foo:socksPassword=bar'
+ ep = clientFromString(None, epstring)
+
+ self.assertEqual(ep.host, 'torproject.org')
+ self.assertEqual(ep.port, 443)
+ self.assertEqual(ep.socks_username, 'foo')
+ self.assertEqual(ep.socks_password, 'bar')
+
+ def test_default_factory(self):
"""
This test is equivalent to txsocksx's TestSOCKS5ClientEndpoint.test_defaultFactory
"""
@@ -583,7 +626,7 @@ class TestTorClientEndpoint(unittest.TestCase):
endpoint.connect(None)
self.assertEqual(endpoint.torSocksEndpoint.transport.value(), '\x05\x01\x00')
- def test_goodPortRetry(self):
+ 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
@@ -600,11 +643,11 @@ class TestTorClientEndpoint(unittest.TestCase):
endpoint.connect(None)
self.assertEqual(endpoint.torSocksEndpoint.transport.value(), '\x05\x01\x00')
- def test_badPortRetry(self):
+ def test_bad_port_retry(self):
"""
This tests failure to connect to the ports on the "try" list.
"""
- fail_ports = [1984, 666]
+ fail_ports = [1984, 666]
for port in fail_ports:
def TorSocksEndpointGenerator(*args, **kw):
kw['acceptPort'] = port
@@ -614,20 +657,20 @@ class TestTorClientEndpoint(unittest.TestCase):
d = endpoint.connect(None)
return self.assertFailure(d, ConnectionRefusedError)
- def test_goodNoGuessSocksPort(self):
+ 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.
+ This tests that if a SOCKS port is specified, we *only* attempt to
+ connect to that SOCKS port.
"""
def TorSocksEndpointGenerator(*args, **kw):
kw['acceptPort'] = 6669
kw['failure'] = connectionRefusedFailure
return FakeTorSocksEndpoint(*args, **kw)
- endpoint = TorClientEndpoint('', 0, proxyEndpointGenerator=TorSocksEndpointGenerator, socksPort=6669)
+ endpoint = TorClientEndpoint('', 0, proxyEndpointGenerator=TorSocksEndpointGenerator, socks_port=6669)
endpoint.connect(None)
self.assertEqual(endpoint.torSocksEndpoint.transport.value(), '\x05\x01\x00')
- def test_badNoGuessSocksPort(self):
+ def test_bad_no_guess_socks_port(self):
"""
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
@@ -637,6 +680,6 @@ class TestTorClientEndpoint(unittest.TestCase):
kw['acceptPort'] = 9050
kw['failure'] = connectionRefusedFailure
return FakeTorSocksEndpoint(*args, **kw)
- endpoint = TorClientEndpoint('', 0, proxyEndpointGenerator=TorSocksEndpointGenerator, socksPort=6669)
+ endpoint = TorClientEndpoint('', 0, proxyEndpointGenerator=TorSocksEndpointGenerator, socks_port=6669)
d = endpoint.connect(None)
self.assertFailure(d, ConnectionRefusedError)
diff --git a/twisted/plugins/txtorcon_endpoint_parser.py b/twisted/plugins/txtorcon_endpoint_parser.py
index ec320e0..cbad80e 100644
--- a/twisted/plugins/txtorcon_endpoint_parser.py
+++ b/twisted/plugins/txtorcon_endpoint_parser.py
@@ -1,2 +1,3 @@
import txtorcon
tcpHiddenServiceEndpointParser = txtorcon.TCPHiddenServiceEndpointParser()
+tcpTorClientEndpointParser = txtorcon.TorClientEndpointStringParser()
diff --git a/txtorcon/endpoints.py b/txtorcon/endpoints.py
index 2545961..583d608 100644
--- a/txtorcon/endpoints.py
+++ b/txtorcon/endpoints.py
@@ -581,45 +581,55 @@ class TCPHiddenServiceEndpointParser(object):
local_port=localPort,
control_port=controlPort)
-def DefaultTCP4EndpointGenerator(*args, **kw):
+
+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...
+ 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):
- """I am an endpoint class who attempts to establish a SOCKS5 connection
- with the system tor process. Either the user must pass a SOCKS port into my
- constructor OR I will attempt to guess the Tor SOCKS port by iterating over a list of ports
- that tor is likely to be listening on.
+ """
+ I am an endpoint class who attempts to establish a SOCKS5
+ connection with the system tor process. Either the user must pass
+ a SOCKS port into my constructor OR I will attempt to guess the
+ Tor SOCKS port by iterating over a list of ports that tor is
+ likely to be listening on.
- :param host: The hostname to connect to.
- This of course can be a Tor Hidden Service onion address.
+ :param host:
+ The hostname to connect to. This of course can be a Tor Hidden
+ Service onion address.
:param port: The tcp port or Tor Hidden Service port.
:param proxyEndpointGenerator: This is used for unit tests.
- :param socksPort: This optional argument lets the user specify which Tor SOCKS port should be used.
+ :param socksPort:
+ This optional argument lets the user specify which Tor SOCKS
+ port should be used.
"""
socks_ports_to_try = [9050, 9150]
- def __init__(self, host, port, proxyEndpointGenerator=DefaultTCP4EndpointGenerator, socksHostname=None, socksPort=None, socksUsername=None, socksPassword=None):
+ def __init__(self, host, port,
+ socks_hostname=None, socks_port=None,
+ socks_username=None, socks_password=None,
+ proxyEndpointGenerator=default_tcp4_endpoint_generator):
if host is None or port is None:
raise ValueError('host and port must be specified')
self.host = host
self.port = port
self.proxyEndpointGenerator = proxyEndpointGenerator
- self.socksHostname = socksHostname
- self.socksPort = socksPort
- self.socksUsername = socksUsername
- self.socksPassword = socksPassword
+ self.socks_hostname = socks_hostname
+ self.socks_port = socks_port
+ self.socks_username = socks_username
+ self.socks_password = socks_password
- if self.socksPort is None:
- self.socksPortIter = iter(self.socks_ports_to_try)
+ if self.socks_port is None:
+ self.socks_portIter = iter(self.socks_ports_to_try)
self.socksGuessingEnabled = True
else:
self.socksGuessingEnabled = False
@@ -628,20 +638,31 @@ class TorClientEndpoint(object):
self.protocolfactory = protocolfactory
if self.socksGuessingEnabled:
- self.socksPort = self.socksPortIter.next()
+ self.socks_port = self.socks_portIter.next()
d = self._try_connect()
return d
def _try_connect(self):
- self.torSocksEndpoint = self.proxyEndpointGenerator(reactor, self.socksHostname, self.socksPort)
-
- if self.socksUsername is None or self.socksPassword is None:
- socks5ClientEndpoint = SOCKS5ClientEndpoint(self.host, self.port, self.torSocksEndpoint)
+ self.torSocksEndpoint = self.proxyEndpointGenerator(
+ reactor,
+ self.socks_hostname,
+ self.socks_port
+ )
+
+ if self.socks_username is None or self.socks_password is None:
+ socks5ClientEndpoint = SOCKS5ClientEndpoint(
+ self.host,
+ self.port,
+ self.torSocksEndpoint
+ )
else:
- socks5ClientEndpoint = SOCKS5ClientEndpoint(self.host, self.port, self.torSocksEndpoint,
- methods={ 'login': (self.socksUsername, self.socksPassword) })
-
+ socks5ClientEndpoint = SOCKS5ClientEndpoint(
+ self.host,
+ self.port,
+ self.torSocksEndpoint,
+ methods=dict(login=(self.socks_username, self.socks_password))
+ )
d = socks5ClientEndpoint.connect(self.protocolfactory)
if self.socksGuessingEnabled:
@@ -651,7 +672,7 @@ class TorClientEndpoint(object):
def _retry_socks_port(self, failure):
failure.trap(error.ConnectError)
try:
- self.socksPort = self.socksPortIter.next()
+ self.socks_port = self.socks_portIter.next()
except StopIteration:
return failure
d = self._try_connect()
@@ -673,13 +694,29 @@ class TorClientEndpointStringParser(object):
``tor:host=timaq4ygg2iegci7.onion:port=80``
- If ``socksPort`` is specified, it means only use that port to attempt to
- proxy through Tor. If unspecified then try some likely socksPorts
- such as [9050, 9150].
+ You may also include a username + password. By default, Tor will
+ not put two streams that provided different authentication on the
+ same circuit.
+
+ ``tor:host=torproject.org:port=443:socksUsername=foo:socksPassword=bar``
+
+ If ``socksPort`` is specified, it means only use that port to
+ attempt to proxy through Tor. If unspecified then try some likely
+ socksPorts such as [9050, 9150].
+
+ NOTE that I'm using camelCase variable names in the endpoint
+ string to be consistent with the rest of Twisted's naming (and
+ their endpoint parsers).
+
+ XXX FIXME if there is no Tor instance found at socksPort, we
+ should launch one. Perhaps a separate option? (Should be on by
+ default, though, I think).
"""
prefix = "tor"
- def _parseClient(self, host=None, port=None, socksHostname=None, socksPort=None, socksUsername=None, socksPassword=None):
+ def _parseClient(self, host=None, port=None,
+ socksHostname=None, socksPort=None,
+ socksUsername=None, socksPassword=None):
if port is not None:
port = int(port)
if socksHostname is None:
@@ -687,7 +724,11 @@ class TorClientEndpointStringParser(object):
if socksPort is not None:
socksPort = int(socksPort)
- return TorClientEndpoint(host, port, socksHostname=socksHostname, socksPort=socksPort, socksUsername=socksUsername, socksPassword=socksPassword)
+ return TorClientEndpoint(
+ host, port,
+ socks_hostname=socksHostname, socks_port=socksPort,
+ socks_username=socksUsername, socks_password=socksPassword
+ )
def parseStreamClient(self, *args, **kwargs):
return self._parseClient(*args, **kwargs)
diff --git a/txtorcon/torstate.py b/txtorcon/torstate.py
index 8b0bb05..fd73992 100644
--- a/txtorcon/torstate.py
+++ b/txtorcon/torstate.py
@@ -188,7 +188,8 @@ class TorState(object):
@classmethod
def from_protocol(cls, protocol, **kw):
'''
- Create a new, boot-strapped TorState from a TorControlProtocol instance.
+ Create a new, boot-strapped TorState from a TorControlProtocol
+ instance.
:return: a Deferred that fires with a TorState instance
'''
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/txtorcon.git
More information about the Pkg-privacy-commits
mailing list