[Pkg-privacy-commits] [txtorcon] 01/03: Imported Upstream version 0.16.1
Iain R. Learmonth
irl at moszumanska.debian.org
Mon Sep 5 09:04:43 UTC 2016
This is an automated email from the git hooks/post-receive script.
irl pushed a commit to branch master
in repository txtorcon.
commit ac340d45f084f9cd8a04c63034900530aa8addfa
Author: Iain R. Learmonth <irl at fsfe.org>
Date: Mon Sep 5 09:56:57 2016 +0100
Imported Upstream version 0.16.1
---
Makefile | 2 +-
PKG-INFO | 2 +-
docs/README.rst | 226 +---------------------------------------
docs/release-checklist.rst | 6 +-
docs/releases.rst | 19 +++-
examples/gui-map.py | 66 ++++++++++++
examples/gui.py | 83 +++++++++++++++
examples/gui2.py | 52 +++++++++
test/test_endpoints.py | 217 ++++++++++++++++++++++++++------------
test/test_torconfig.py | 31 ++++++
txtorcon.egg-info/PKG-INFO | 2 +-
txtorcon.egg-info/SOURCES.txt | 3 +
txtorcon.egg-info/top_level.txt | 2 +-
txtorcon/_metadata.py | 2 +-
txtorcon/endpoints.py | 128 ++++++++++++++---------
txtorcon/torconfig.py | 29 ++++++
16 files changed, 521 insertions(+), 349 deletions(-)
diff --git a/Makefile b/Makefile
index 8a940bf..30162a8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
.PHONY: test html counts coverage sdist clean install doc integration
default: test
-VERSION = 0.15.1
+VERSION = 0.16.1
test:
trial --reporter=text test
diff --git a/PKG-INFO b/PKG-INFO
index 06fe8f9..55a3f2d 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: txtorcon
-Version: 0.15.1
+Version: 0.16.1
Summary:
Twisted-based Tor controller client, with state-tracking and
configuration abstractions.
diff --git a/docs/README.rst b/docs/README.rst
deleted file mode 100644
index fca84b2..0000000
--- a/docs/README.rst
+++ /dev/null
@@ -1,225 +0,0 @@
-README
-======
-
-Documentation at https://txtorcon.readthedocs.org
-
-.. image:: https://travis-ci.org/meejah/txtorcon.png?branch=master
- :target: https://www.travis-ci.org/meejah/txtorcon
-
-.. image:: https://coveralls.io/repos/meejah/txtorcon/badge.png
- :target: https://coveralls.io/r/meejah/txtorcon
-
-.. image:: http://codecov.io/github/meejah/txtorcon/coverage.svg?branch=master
- :target: http://codecov.io/github/meejah/txtorcon?branch=master
-
-.. image:: http://api.flattr.com/button/flattr-badge-large.png
- :target: http://flattr.com/thing/1689502/meejahtxtorcon-on-GitHub
-
-
-quick start
------------
-
-For the impatient, there are two quick ways to install this::
-
- $ pip install txtorcon
-
-... or, if you checked out or downloaded the source::
-
- $ python setup.py install
-
-... or, better yet, use a virtualenv and the dev requirements::
-
- $ virtualenv venv
- $ ./venv/bin/pip install -e .[dev]
-
-For OSX, we can install txtorcon with the help of easy_install::
-
- $ easy_install txtorcon
-
-To avoid installing, you can just add the base of the source to your
-PYTHONPATH::
-
- $ export PYTHONPATH=`pwd`:$PYTHONPATH
-
-Then, you will want to explore the examples. Try "python
-examples/stream\_circuit\_logger.py" for instance.
-
-On Debian testing (jessie), or with wheezy-backports (big thanks to
-Lunar^ for all his packaging work) you can install easily::
-
- $ apt-get install python-txtorcon
-
-You may also like `this asciinema demo <http://asciinema.org/a/5654>`_
-for an overview.
-
-Tor configuration
------------------
-
-You'll want to have the following options on in your ``torrc``::
-
- CookieAuthentication 1
- CookieAuthFileGroupReadable 1
-
-If you want to use unix sockets to speak to tor::
-
- ControlSocketsGroupWritable 1
- ControlSocket /var/run/tor/control
-
-The defaults used by py:meth:`txtorcon.build_local_tor_connection` will
-find a Tor on ``9051`` or ``/var/run/tor/control``
-
-
-overview
---------
-
-txtorcon is a Twisted-based asynchronous Tor control protocol
-implementation. Twisted is an event-driven networking engine written
-in Python and Tor is an onion-routing network designed to improve
-people's privacy and anonymity on the Internet.
-
-The main abstraction of this library is txtorcon.TorControlProtocol
-which presents an asynchronous API to speak the Tor client protocol in
-Python. txtorcon also provides abstractions to track and get updates
-about Tor's state (txtorcon.TorState) and current configuration
-(including writing it to Tor or disk) in txtorcon.TorConfig, along
-with helpers to asynchronously launch slave instances of Tor including
-Twisted endpoint support.
-
-txtorcon runs all tests cleanly on:
-
-- Debian "squeeze", "wheezy" and "jessie"
-- OS X 10.4 (naif)
-- OS X 10.8 (lukas lueg)
-- OS X 10.9 (kurt neufeld)
-- Fedora 18 (lukas lueg)
-- FreeBSD 10 (enrique fynn) (**needed to install "lsof"**)
-- RHEL6
-- Reports from other OSes appreciated.
-
-If instead you want a synchronous (threaded) Python controller
-library, check out Stem at https://stem.torproject.org/
-
-
-quick implementation overview
------------------------------
-
-txtorcon provides a class to track Tor's current state -- such as
-details about routers, circuits and streams -- called
-txtorcon.TorState and an abstraction to the configuration values via
-txtorcon.TorConfig which provides attribute-style accessors to Tor's
-state (including making changes). txtorcon.TorState provides
-txtorcon.Router, txtorcon.Circuit and txtorcon.Stream objects which
-implement a listener interface so client code may receive updates (in
-real time) including Tor events.
-
-txtorcon uses **trial for unit-tests** and has 100% test-coverage --
-which is not to say I've covered all the cases, but nearly all of the
-code is at least exercised somehow by the unit tests.
-
-Tor itself is not required to be running for any of the tests. ohcount
-claims around 2000 lines of code for the core bit; around 4000
-including tests. About 37% comments in the not-test code.
-
-There are a few simple integration tests, based on Docker. More are
-always welcome!
-
-
-dependencies / requirements
----------------------------
-
-- `twisted <http://twistedmatrix.com>`_: txtorcon should work with any
- Twisted 11.1.0 or newer. Twisted 15.4.0+ works with Python3, and so
- does txtorcon (if you find something broken on Py3 please file a bug).
-
-- `GeoIP <https://www.maxmind.com/app/python>`_: **optional** provides location
- information for ip addresses; you will want to download GeoLite City
- from `MaxMind <https://www.maxmind.com/app/geolitecity>`_ or pay them
- for more accuracy. Or use tor-geoip, which makes this sort-of
- optional, in that we'll query Tor for the IP if the GeoIP database
- doesn't have an answer. It also does ASN lookups if you installed that MaxMind database.
-
-- development: `Sphinx <http://sphinx.pocoo.org/>`_ if you want to build the
- documentation. In that case you'll also need something called
- ``python-repoze.sphinx.autointerface`` (at least in Debian) to build
- the Interface-derived docs properly.
-
-- development: `coverage <http://nedbatchelder.com/code/coverage/>`_ to
- run the code-coverage metrics, and Tox
-
-- optional: GraphViz is used in the tests (and to generate state-machine
- diagrams, if you like) but those tests are skipped if "dot" isn't
- in your path
-
-.. BEGIN_INSTALL
-
-In any case, on a `Debian <http://www.debian.org/>`_ wheezy, squeeze or
-Ubuntu system, this should work::
-
- apt-get install -y python-setuptools python-twisted python-ipaddr python-geoip graphviz tor
- apt-get install -y python-sphinx python-repoze.sphinx.autointerface python-coverage # for development
-
-.. END_INSTALL
-
-Using pip this would be::
-
- pip install Twisted ipaddr pygeoip
- pip install GeoIP Sphinx repoze.sphinx.autointerface coverage # for development
-
-or::
-
- pip install -r requirements.txt
- pip install -r dev-requirements.txt
-
-or for the bare minimum::
-
- pip install Twisted # will install zope.interface too
-
-
-documentation
--------------
-
-It is likely that you will need to read at least some of
-`control-spec.txt <https://gitweb.torproject.org/torspec.git/blob/HEAD:/control-spec.txt>`_
-from the torspec git repository so you know what's being abstracted by
-this library.
-
-Run "make doc" to build the Sphinx documentation locally, or rely on
-ReadTheDocs https://txtorcon.readthedocs.org which builds each tagged
-release and the latest master.
-
-There is also a directory of examples/ scripts, which have inline
-documentation explaining their use.
-
-
-contact information
--------------------
-
-For novelty value, the Web site (with built documentation and so forth)
-can be viewed via Tor at http://timaq4ygg2iegci7.onion although the
-code itself is hosted via git::
-
- torsocks git clone git://timaq4ygg2iegci7.onion/txtorcon.git
-
-or::
-
- git clone git://github.com/meejah/txtorcon.git
-
-You may contact me via ``meejah at meejah dot ca`` with GPG key
-`0xC2602803128069A7
-<http://pgp.mit.edu:11371/pks/lookup?op=get&search=0xC2602803128069A7>`_
-or see ``meejah.asc`` in the repository. The fingerprint is ``9D5A
-2BD5 688E CB88 9DEB CD3F C260 2803 1280 69A7``.
-
-It is often possible to contact me as ``meejah`` in #tor-dev on `OFTC
-<http://www.oftc.net/oftc/>`_ but be patient for replies (I do look at
-scrollback, so putting "meejah: " in front will alert my client).
-
-More conventionally, you may get the code at GitHub and documentation
-via ReadTheDocs:
-
-- https://github.com/meejah/txtorcon
-- https://txtorcon.readthedocs.org
-
-Please do **use the GitHub issue-tracker** to report bugs. Patches,
-pull-requests, comments and criticisms are all welcomed and
-appreciated.
diff --git a/docs/README.rst b/docs/README.rst
new file mode 120000
index 0000000..89a0106
--- /dev/null
+++ b/docs/README.rst
@@ -0,0 +1 @@
+../README.rst
\ No newline at end of file
diff --git a/docs/release-checklist.rst b/docs/release-checklist.rst
index 4373d8c..e70bc99 100644
--- a/docs/release-checklist.rst
+++ b/docs/release-checklist.rst
@@ -1,6 +1,10 @@
Release Checklist
=================
+* ensure local copy is on master, up-to-date:
+ * git checkout master
+ * git pull
+
* double-check version updated, sadly in a few places:
* Makefile
* txtorcon/_metadata.py
@@ -15,7 +19,7 @@ Release Checklist
* update heading, date
* on both signing-machine and build-machine shells:
- * export VERSION=v0.15.1
+ * export VERSION=0.16.1
* (if on signing machine) "make dist" and "make dist-sig"
* creates:
diff --git a/docs/releases.rst b/docs/releases.rst
index 3f82ace..459a84b 100644
--- a/docs/releases.rst
+++ b/docs/releases.rst
@@ -19,7 +19,24 @@ Rendered docs on `txtorcon.readthedocs <http://txtorcon.readthedocs.io/en/releas
unreleased
----------
-`git master <https://github.com/meejah/txtorcon>`_ *will likely become v0.16.0*
+`git master <https://github.com/meejah/txtorcon>`_ *will likely become v0.17.0*
+
+
+v0.16.1
+-------
+
+*August 31, 2016*
+
+ * `txtorcon-0.16.1.tar.gz <http://timaq4ygg2iegci7.onion/txtorcon-0.16.1.tar.gz>`_ (`PyPI <https://pypi.python.org/pypi/txtorcon/0.16.1>`_ (:download:`local-sig </../signatues/txtorcon-0.16.1.tar.gz.asc>` or `github-sig <https://github.com/meejah/txtorcon/blob/master/signatues/txtorcon-0.16.1.tar.gz.asc?raw=true>`_) (`source <https://github.com/meejah/txtorcon/archive/v0.16.1.tar.gz>`_)
+ * `issue 172 <https://github.com/meejah/txtorcon/issues/172>`_: give `TorProcessProtocol` a `.quit` method
+ * `issue 181 <https://github.com/meejah/txtorcon/issues/181>`_: enable SOCKS5-over-unix-sockets for TorClientEndpoint (thanks to `david415 <https://github.com/david415>`_
+
+
+v0.16.0
+-------
+
+ * there wasn't one, `because reasons <https://github.com/meejah/txtorcon/commit/e4291c01ff223d3cb7774437cafa2f06ca195bcf>`_.
+
v0.15.1
-------
diff --git a/examples/gui-map.py b/examples/gui-map.py
new file mode 100644
index 0000000..9b895d4
--- /dev/null
+++ b/examples/gui-map.py
@@ -0,0 +1,66 @@
+# fooling around w/ a GUI
+
+# need to boot up GTK first
+try:
+ import pgi
+ pgi.install_as_gi()
+except ImportError:
+ pass
+import gi
+gi.require_version('Gtk', '3.0')
+
+# ...then "really soon" (before any other twisted reactor imports) get
+# the correct reactor
+
+from twisted.internet import gtk3reactor
+gtk3reactor.install()
+
+# normal imports follow
+
+from twisted.internet import reactor
+from twisted.internet.defer import Deferred, inlineCallbacks
+from twisted.internet.task import react
+
+from twisted.internet.endpoints import TCP4ClientEndpoint
+import txtorcon
+
+from gi.repository import Gtk
+
+def draw_area(widget, cr):
+ print("ding", widget, cr)
+ for x in dir(widget):
+ print(x)
+ print("ddd", widget.get_size_request())
+ print("XXX", dir(widget.get_allocation()))
+ w, h = widget.get_allocation().width, widget.get_allocation().height
+ cr.set_source_rgb(0, 0, 0)
+ cr.set_line_width(1.0)
+
+ for lng in range(0, 360, 5):
+ for lat in range(-160, 160, 5):
+ x, y = lng * 2, (lat + 160) * 2
+ cr.rectangle(x + 10, y + 10, 6, 6)
+ cr.stroke()
+
+def create_win():
+ win = Gtk.Window()
+ win.connect("delete-event", Gtk.main_quit)
+
+ area = Gtk.DrawingArea()
+ area.set_size_request(320, 240)
+ area.connect('draw', draw_area)
+ win.add(area)
+ return win
+
+app = Gtk.Application()
+reactor.registerGApplication(app)
+
+ at inlineCallbacks
+def main(reactor):
+ ep = TCP4ClientEndpoint(reactor, "localhost", 9051)
+ tor = yield txtorcon.connect(reactor, ep)
+ win = create_win()
+ win.show_all()
+ app.add_window(win)
+ yield Deferred()
+react(main)
diff --git a/examples/gui.py b/examples/gui.py
new file mode 100644
index 0000000..7c9082f
--- /dev/null
+++ b/examples/gui.py
@@ -0,0 +1,83 @@
+# fooling around w/ a GUI
+
+# need to boot up GTK first
+try:
+ import pgi
+ pgi.install_as_gi()
+except ImportError:
+ pass
+import gi
+gi.require_version('Gtk', '3.0')
+
+# ...then "really soon" (before any other twisted reactor imports) get
+# the correct reactor
+
+from twisted.internet import gtk3reactor
+gtk3reactor.install()
+
+# normal imports follow
+
+from twisted.internet import reactor
+from twisted.internet.defer import Deferred, inlineCallbacks
+from twisted.internet.task import react
+
+from twisted.internet.endpoints import TCP4ClientEndpoint
+import txtorcon
+
+from gi.repository import Gtk
+
+def create_win():
+ #win = Gtk.Window()
+ win = Gtk.Dialog()
+ win.connect("delete-event", lambda a, b: reactor.stop())#Gtk.main_quit)
+ win.set_border_width(10)
+ win.set_default_size(320, 220)
+
+ def got_response(dialog, response_code):
+ if response_code == 12:
+ print("Cancelling; Tor not launched")
+ reactor.stop()
+ win.connect("response", got_response)
+
+# cancel = Gtk.Button.new_with_label("Cancel")
+
+ #vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+ vbox = win.get_content_area()
+ win.add(vbox)
+ win.add_button('Cancel', 12)
+ progress = Gtk.ProgressBar()
+ progress.set_property('show-text', True)
+ progress.set_show_text(True)
+ progress.set_text(None)
+ label = Gtk.Label("Launching Tor")
+ log = Gtk.Label("")
+
+ messages = []
+ def progress_updates(prog, tag, msg):
+ progress.set_fraction(prog / 100.0)
+ if len(messages) == 0 or msg != messages[0]:
+ messages.insert(0, msg)
+ markup = ''
+ for n, msg in enumerate(messages[:6]):
+ percent = n / 6.0
+ val = '%x' % int(255 * percent)
+ markup += '<span foreground="#{0}{0}{0}">{1}</span>\n'.format(val, msg)
+ #log.set_text('\n'.join(messages))
+ log.set_markup(markup)
+
+ vbox.pack_start(label, expand=False, fill=False, padding=5)
+ vbox.pack_start(progress, expand=False, fill=False, padding=0)
+ vbox.pack_start(log, expand=False, fill=False, padding=0)
+ vbox.pack_start(Gtk.Label(), expand=True, fill=True, padding=0)
+ return win, progress_updates
+
+ at inlineCallbacks
+def main(reactor):
+ ep = TCP4ClientEndpoint(reactor, "localhost", 9051)
+ win, prog = create_win()
+ win.show_all()
+ tor = yield txtorcon.launch(reactor, progress_updates=prog)
+ print("tor launched", tor)
+ win.destroy()
+ yield Deferred()
+react(main)
diff --git a/examples/gui2.py b/examples/gui2.py
new file mode 100644
index 0000000..e1c8eb6
--- /dev/null
+++ b/examples/gui2.py
@@ -0,0 +1,52 @@
+# fooling around w/ a GUI
+
+# need to boot up GTK first
+try:
+ import pgi
+ pgi.install_as_gi()
+except ImportError:
+ pass
+import gi
+gi.require_version('Gtk', '3.0')
+
+# ...then "really soon" (before any other twisted reactor imports) get
+# the correct reactor
+
+from twisted.internet import gtk3reactor
+gtk3reactor.install()
+
+# normal imports follow
+
+from twisted.internet import reactor
+from twisted.internet.defer import Deferred, inlineCallbacks
+from twisted.internet.task import react
+
+from twisted.internet.endpoints import TCP4ClientEndpoint
+import txtorcon
+
+from gi.repository import Gtk
+
+def create_win():
+ win = Gtk.Window()
+ win.connect("delete-event", Gtk.main_quit)
+
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+ win.add(vbox)
+ progress = Gtk.ProgressBar()
+ label = Gtk.Label("no progress yet")
+ vbox.pack_start(progress, True, True, 0)
+ vbox.pack_start(label, True, True, 0)
+ return win
+
+app = Gtk.Application()
+reactor.registerGApplication(app)
+
+ at inlineCallbacks
+def main(reactor):
+ ep = TCP4ClientEndpoint(reactor, "localhost", 9051)
+ tor = yield txtorcon.connect(reactor, ep)
+# win = create_main(tor)
+# win.show_all()
+# app.add_window(win)
+# yield Deferred()
+react(main)
diff --git a/test/test_endpoints.py b/test/test_endpoints.py
index ab0c68a..d1eb0fb 100644
--- a/test/test_endpoints.py
+++ b/test/test_endpoints.py
@@ -11,6 +11,7 @@ from twisted.trial import unittest
from twisted.test import proto_helpers
from twisted.internet import defer, error, task, tcp
from twisted.internet.endpoints import TCP4ServerEndpoint
+from twisted.internet.endpoints import TCP4ClientEndpoint
from twisted.internet.endpoints import serverFromString
from twisted.internet.endpoints import clientFromString
from twisted.python.failure import Failure
@@ -34,7 +35,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
+from txtorcon.endpoints import _HAVE_TLS
import util
@@ -592,10 +593,11 @@ class TestTorClientEndpoint(unittest.TestCase):
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)
@@ -603,20 +605,17 @@ class TestTorClientEndpoint(unittest.TestCase):
"""
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,
@@ -628,7 +627,8 @@ class TestTorClientEndpoint(unittest.TestCase):
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' + \
@@ -644,15 +644,13 @@ class TestTorClientEndpoint(unittest.TestCase):
"""
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
@@ -661,11 +659,10 @@ class TestTorClientEndpoint(unittest.TestCase):
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)
@@ -677,16 +674,16 @@ class TestTorClientEndpoint(unittest.TestCase):
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):
"""
@@ -694,41 +691,131 @@ class TestTorClientEndpoint(unittest.TestCase):
"""
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)
+
+ @patch('txtorcon.endpoints.SOCKS5ClientEndpoint')
+ @defer.inlineCallbacks
+ def test_tls_socks_no_endpoint(self, ep_mock):
+
+ if not _HAVE_TLS:
+ print("no TLS support")
+ return
+
+ class FakeWrappedProto(object):
+ wrappedProtocol = object()
+
+ wrap = FakeWrappedProto()
+ proto = defer.succeed(wrap)
+ class FakeSocks5(object):
+
+ def __init__(self, *args, **kw):
+ pass
+
+ def connect(self, *args, **kw):
+ return proto
+
+ ep_mock.side_effect = FakeSocks5
+ endpoint = TorClientEndpoint('torproject.org', 0, tls=True)
+ p2 = yield endpoint.connect(None)
+ self.assertTrue(wrap.wrappedProtocol is p2)
+
+ @patch('txtorcon.endpoints.SOCKS5ClientEndpoint')
+ @defer.inlineCallbacks
+ def test_tls_socks_with_endpoint(self, ep_mock):
+ """
+ Same as above, except we provide an explicit endpoint
+ """
+
+ if not _HAVE_TLS:
+ print("no TLS support")
+ return
+
+ class FakeWrappedProto(object):
+ wrappedProtocol = object()
+
+ wrap = FakeWrappedProto()
+ proto = defer.succeed(wrap)
+ class FakeSocks5(object):
+
+ def __init__(self, *args, **kw):
+ pass
+
+ def connect(self, *args, **kw):
+ return proto
+
+ ep_mock.side_effect = FakeSocks5
+ endpoint = TorClientEndpoint(
+ 'torproject.org', 0,
+ socks_endpoint=clientFromString(Mock(), "tcp:localhost:9050"),
+ tls=True,
+ )
+ p2 = yield endpoint.connect(None)
+ self.assertTrue(wrap.wrappedProtocol is p2)
+
+ @patch('txtorcon.endpoints.reactor') # FIXME should be passing reactor to TorClientEndpoint :/
+ def test_client_endpoint_old_api(self, reactor):
+ """
+ Test the old API of passing socks_host, socks_port
+ """
+
+ endpoint = TorClientEndpoint(
+ 'torproject.org', 0,
+ socks_host='localhost',
+ socks_port=9050,
+ )
+ self.assertTrue(isinstance(endpoint.socks_endpoint, TCP4ClientEndpoint))
+
+ d = endpoint.connect(Mock())
+ calls = reactor.mock_calls
+ self.assertEqual(1, len(calls))
+ name, args, kw = calls[0]
+ self.assertEqual("connectTCP", name)
+ self.assertEqual("localhost", args[0])
+ self.assertEqual(9050, args[1])
diff --git a/test/test_torconfig.py b/test/test_torconfig.py
index b0edea0..5869860 100644
--- a/test/test_torconfig.py
+++ b/test/test_torconfig.py
@@ -1611,6 +1611,37 @@ ControlPort Port''')
process.progress(10, 'tag', 'summary')
self.assertTrue(self.got_progress)
+ def test_quit_process(self):
+ process = TorProcessProtocol(None)
+ process.transport = Mock()
+
+ d = process.quit()
+ self.assertFalse(d.called)
+
+ process.processExited(Failure(error.ProcessTerminated(exitCode=15)))
+ self.assertTrue(d.called)
+ process.processEnded(Failure(error.ProcessDone(None)))
+ self.assertTrue(d.called)
+ errs = self.flushLoggedErrors()
+ self.assertEqual(1, len(errs))
+ self.assertTrue("Tor exited with error-code" in str(errs[0]))
+
+ def test_quit_process_already(self):
+ process = TorProcessProtocol(None)
+ process.transport = Mock()
+
+ def boom(sig):
+ self.assertEqual(sig, 'TERM')
+ raise error.ProcessExitedAlready()
+ process.transport.signalProcess = Mock(side_effect=boom)
+
+ d = process.quit()
+ process.processEnded(Failure(error.ProcessDone(None)))
+ self.assertTrue(d.called)
+ errs = self.flushLoggedErrors()
+ self.assertEqual(1, len(errs))
+ self.assertTrue("Tor exited with error-code" in str(errs[0]))
+
def test_status_updates(self):
process = TorProcessProtocol(None)
process.status_client("NOTICE CONSENSUS_ARRIVED")
diff --git a/txtorcon.egg-info/PKG-INFO b/txtorcon.egg-info/PKG-INFO
index 06fe8f9..55a3f2d 100644
--- a/txtorcon.egg-info/PKG-INFO
+++ b/txtorcon.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: txtorcon
-Version: 0.15.1
+Version: 0.16.1
Summary:
Twisted-based Tor controller client, with state-tracking and
configuration abstractions.
diff --git a/txtorcon.egg-info/SOURCES.txt b/txtorcon.egg-info/SOURCES.txt
index 4255aed..50ee52a 100644
--- a/txtorcon.egg-info/SOURCES.txt
+++ b/txtorcon.egg-info/SOURCES.txt
@@ -50,6 +50,9 @@ examples/circuit_for_next_stream.py
examples/disallow_streams_by_port.py
examples/dump_config.py
examples/ephemeral_endpoint.py
+examples/gui-map.py
+examples/gui.py
+examples/gui2.py
examples/hello_darkweb.py
examples/hidden-service-systemd.service
examples/launch_tor.py
diff --git a/txtorcon.egg-info/top_level.txt b/txtorcon.egg-info/top_level.txt
index dc51759..1ba11e2 100644
--- a/txtorcon.egg-info/top_level.txt
+++ b/txtorcon.egg-info/top_level.txt
@@ -1,2 +1,2 @@
-twisted
txtorcon
+twisted
diff --git a/txtorcon/_metadata.py b/txtorcon/_metadata.py
index 3e2b124..c7f7af5 100644
--- a/txtorcon/_metadata.py
+++ b/txtorcon/_metadata.py
@@ -1,4 +1,4 @@
-__version__ = '0.15.1'
+__version__ = '0.16.1'
__author__ = 'meejah'
__contact__ = 'meejah at meejah.ca'
__url__ = 'https://github.com/meejah/txtorcon'
diff --git a/txtorcon/endpoints.py b/txtorcon/endpoints.py
index a901555..e36e217 100644
--- a/txtorcon/endpoints.py
+++ b/txtorcon/endpoints.py
@@ -22,6 +22,14 @@ except ImportError:
from twisted.internet.interfaces import IStreamClientEndpointStringParser as IStreamClientEndpointStringParserWithReactor
_HAVE_TX_14 = False
+try:
+ from twisted.internet.ssl import optionsForClientTLS
+ from txsocksx.tls import TLSWrapClientEndpoint
+ _HAVE_TLS = True
+except ImportError:
+ _HAVE_TLS = False
+
+
from twisted.internet import defer, reactor
from twisted.python import log
from twisted.internet.interfaces import IStreamServerEndpointStringParser
@@ -633,89 +641,101 @@ class TCPHiddenServiceEndpointParser(object):
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):
"""
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 port: The tcp port or Tor Hidden Service port.
+ connection with the system tor process. If no socks_endpoint is
+ given, I will try TCP4 to localhost on ports 9050 then 9150.
- :param _proxy_endpoint_generator: This is used for unit tests.
+ :param socks_endpoint:
+ An IStreamClientEndpoint that will connect to a SOCKS5
+ port. Tor can speak SOCKS5 over either TCP4 or Unix sockets.
- :param socks_port:
- This optional argument lets the user specify which Tor SOCKS
- port should be used.
+ :param tls:
+ If True, we will attemp TLS negotiation after the SOCKS forwarding
+ is set up.
"""
# XXX should get these via the control connection, i.e. ask Tor
# via GETINFO net/listeners/socks or whatever
socks_ports_to_try = [9050, 9150]
def __init__(self, host, port,
- socks_hostname=None, socks_port=None,
+ socks_endpoint=None,
socks_username=None, socks_password=None,
- _proxy_endpoint_generator=default_tcp4_endpoint_generator):
+ tls=False, **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
+ self.tls = tls
- if self.socks_port is None:
+ if self.tls and not _HAVE_TLS:
+ raise ValueError(
+ "'tls=True' but we don't have TLS support"
+ )
+
+ # 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
+
+ # this is a separate "if" from above in case socks_endpoint
+ # was None but the user specified the (old)
+ # socks_host/socks_port (in which case we do NOT want
+ # guessing_enabled
+ 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)
+ if self.tls:
+ context = optionsForClientTLS(unicode(self.host))
+ socks_ep = TLSWrapClientEndpoint(context, socks_ep)
+ 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)
+ if self.tls:
+ # XXX only twisted 14+
+ context = optionsForClientTLS(unicode(self.host))
+ socks_ep = TLSWrapClientEndpoint(context, socks_ep)
- 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)
@@ -762,10 +782,14 @@ class TorClientEndpointStringParser(object):
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):
diff --git a/txtorcon/torconfig.py b/txtorcon/torconfig.py
index faf9e86..bfec9a8 100644
--- a/txtorcon/torconfig.py
+++ b/txtorcon/torconfig.py
@@ -115,6 +115,7 @@ class TorProcessProtocol(protocol.ProcessProtocol):
self._setup_complete = False
self._did_timeout = False
self._timeout_delayed_call = None
+ self._on_exit = [] # Deferred's we owe a call/errback to when we exit
if timeout:
if not ireactortime:
raise RuntimeError(
@@ -124,6 +125,30 @@ class TorProcessProtocol(protocol.ProcessProtocol):
self._timeout_delayed_call = ireactortime.callLater(
timeout, self.timeout_expired)
+ def quit(self):
+ """
+ This will terminate (with SIGTERM) the underlying Tor process.
+
+ :returns: a Deferred that callback()'s (with None) when the
+ process has actually exited.
+ """
+
+ try:
+ self.transport.signalProcess('TERM')
+ d = defer.Deferred()
+ self._on_exit.append(d)
+
+ except error.ProcessExitedAlready:
+ self.transport.loseConnection()
+ d = defer.succeed(None)
+ return d
+
+ def _signal_on_exit(self, reason):
+ to_notify = self._on_exit
+ self._on_exit = []
+ for d in to_notify:
+ d.callback(None)
+
def outReceived(self, data):
"""
:api:`twisted.internet.protocol.ProcessProtocol <ProcessProtocol>` API
@@ -179,6 +204,9 @@ class TorProcessProtocol(protocol.ProcessProtocol):
all([delete_file_or_tree(f) for f in self.to_delete])
self.to_delete = []
+ def processExited(self, reason):
+ self._signal_on_exit(reason)
+
def processEnded(self, status):
"""
:api:`twisted.internet.protocol.ProcessProtocol <ProcessProtocol>` API
@@ -201,6 +229,7 @@ class TorProcessProtocol(protocol.ProcessProtocol):
if self.connected_cb:
self.connected_cb.errback(err)
self.connected_cb = None
+ self._signal_on_exit(status)
def progress(self, percent, tag, summary):
"""
--
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