[Pkg-privacy-commits] [txtorcon] 01/03: New upstream version 0.17.0
Iain R. Learmonth
irl at moszumanska.debian.org
Sun Oct 30 13:38:47 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 9b6cfa550c790bec84c495a981e894aff57cadc8
Author: Iain R. Learmonth <irl at fsfe.org>
Date: Sun Oct 30 13:27:29 2016 +0000
New upstream version 0.17.0
---
Makefile | 2 +-
PKG-INFO | 9 +-
README.rst | 7 ++
docs/README.rst | 233 +++++++++++++++++++++++++++++++++++++++-
docs/release-checklist.rst | 11 +-
docs/releases.rst | 13 ++-
examples/ephemeral_endpoint.py | 28 ++---
examples/gui-boom.py | 39 +++++++
examples/gui-cairo.py | 94 ++++++++++++++++
examples/redirect_streams.py | 55 ----------
test/test_circuit.py | 2 +-
test/test_endpoints.py | 2 +-
test/test_stream.py | 25 +++++
test/test_torconfig.py | 36 +++++++
test/test_torstate.py | 8 +-
txtorcon.egg-info/PKG-INFO | 9 +-
txtorcon.egg-info/SOURCES.txt | 3 +-
txtorcon.egg-info/top_level.txt | 2 +-
txtorcon/_metadata.py | 2 +-
txtorcon/addrmap.py | 2 +
txtorcon/endpoints.py | 10 +-
txtorcon/stream.py | 39 +++++--
txtorcon/torconfig.py | 38 ++++---
txtorcon/torstate.py | 5 +-
24 files changed, 567 insertions(+), 107 deletions(-)
diff --git a/Makefile b/Makefile
index 30162a8..ee3b63c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
.PHONY: test html counts coverage sdist clean install doc integration
default: test
-VERSION = 0.16.1
+VERSION = 0.17.0
test:
trial --reporter=text test
diff --git a/PKG-INFO b/PKG-INFO
index 55a3f2d..f537578 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: txtorcon
-Version: 0.16.1
+Version: 0.17.0
Summary:
Twisted-based Tor controller client, with state-tracking and
configuration abstractions.
@@ -16,16 +16,23 @@ Description: README
.. image:: https://travis-ci.org/meejah/txtorcon.png?branch=master
:target: https://www.travis-ci.org/meejah/txtorcon
+ :alt: travis
.. image:: https://coveralls.io/repos/meejah/txtorcon/badge.png
:target: https://coveralls.io/r/meejah/txtorcon
+ :alt: coveralls
.. image:: http://codecov.io/github/meejah/txtorcon/coverage.svg?branch=master
:target: http://codecov.io/github/meejah/txtorcon?branch=master
+ :alt: codecov
.. image:: http://api.flattr.com/button/flattr-badge-large.png
:target: http://flattr.com/thing/1689502/meejahtxtorcon-on-GitHub
+ :alt: flattr
+ .. image:: https://landscape.io/github/meejah/txtorcon/master/landscape.svg?style=flat
+ :target: https://landscape.io/github/meejah/txtorcon/master
+ :alt: Code Health
quick start
-----------
diff --git a/README.rst b/README.rst
index fca84b2..24760cf 100644
--- a/README.rst
+++ b/README.rst
@@ -5,16 +5,23 @@ Documentation at https://txtorcon.readthedocs.org
.. image:: https://travis-ci.org/meejah/txtorcon.png?branch=master
:target: https://www.travis-ci.org/meejah/txtorcon
+ :alt: travis
.. image:: https://coveralls.io/repos/meejah/txtorcon/badge.png
:target: https://coveralls.io/r/meejah/txtorcon
+ :alt: coveralls
.. image:: http://codecov.io/github/meejah/txtorcon/coverage.svg?branch=master
:target: http://codecov.io/github/meejah/txtorcon?branch=master
+ :alt: codecov
.. image:: http://api.flattr.com/button/flattr-badge-large.png
:target: http://flattr.com/thing/1689502/meejahtxtorcon-on-GitHub
+ :alt: flattr
+.. image:: https://landscape.io/github/meejah/txtorcon/master/landscape.svg?style=flat
+ :target: https://landscape.io/github/meejah/txtorcon/master
+ :alt: Code Health
quick start
-----------
diff --git a/docs/README.rst b/docs/README.rst
deleted file mode 120000
index 89a0106..0000000
--- a/docs/README.rst
+++ /dev/null
@@ -1 +0,0 @@
-../README.rst
\ No newline at end of file
diff --git a/docs/README.rst b/docs/README.rst
new file mode 100644
index 0000000..24760cf
--- /dev/null
+++ b/docs/README.rst
@@ -0,0 +1,232 @@
+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
+ :alt: travis
+
+.. image:: https://coveralls.io/repos/meejah/txtorcon/badge.png
+ :target: https://coveralls.io/r/meejah/txtorcon
+ :alt: coveralls
+
+.. image:: http://codecov.io/github/meejah/txtorcon/coverage.svg?branch=master
+ :target: http://codecov.io/github/meejah/txtorcon?branch=master
+ :alt: codecov
+
+.. image:: http://api.flattr.com/button/flattr-badge-large.png
+ :target: http://flattr.com/thing/1689502/meejahtxtorcon-on-GitHub
+ :alt: flattr
+
+.. image:: https://landscape.io/github/meejah/txtorcon/master/landscape.svg?style=flat
+ :target: https://landscape.io/github/meejah/txtorcon/master
+ :alt: Code Health
+
+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/release-checklist.rst b/docs/release-checklist.rst
index e70bc99..d1df98c 100644
--- a/docs/release-checklist.rst
+++ b/docs/release-checklist.rst
@@ -30,9 +30,13 @@ Release Checklist
* (if not on signing machine) do "make dist"
* scp dist/txtorcon-${VERSION}.tar.gz dist/txtorcon-${VERSION}-py2-none-any.whl signingmachine:
- * sign both, with .asc detached signatures (see Makefile for command)
+ * sign both, with .asc detached signatures
+ * gpg --no-version --detach-sign --armor --local-user meejah at meejah.ca txtorcon-${VERSION}-py2-none-any.whl
+ * gpg --no-version --detach-sign --armor --local-user meejah at meejah.ca txtorcon-${VERSION}.tar.gz
* copy signatures back to build machine, in dist/
* double-check that they validate
+ * gpg --verify dist/txtorcon-${VERSION}-py2-none-any.whl
+ * gpg --verify dist/txtorcon-${VERSION}.tar.gz
* generate sha256sum for each:
sha256sum dist/txtorcon-${VERSION}.tar.gz dist/txtorcon-${VERSION}-py2-none-any.whl
@@ -79,9 +83,12 @@ Release Checklist
meejah
* copy release announcement to signing machine, update code
+ * (from dev machine: "git push pangea")
+ * git checkout master
+ * git pull
* create signed tag
- * git tag -s -u meejah at meejah.ca -F path/to/release-announcement-X-Y-Z v${VERSION}
+ * git tag -s -u meejah at meejah.ca -F release-announce-${VERSION} v${VERSION}
* copy dist/* files + signatures to hidden-service machine
* copy them to the HTML build directory! (docs/_build/html/)
diff --git a/docs/releases.rst b/docs/releases.rst
index 459a84b..ac889e7 100644
--- a/docs/releases.rst
+++ b/docs/releases.rst
@@ -19,7 +19,18 @@ Rendered docs on `txtorcon.readthedocs <http://txtorcon.readthedocs.io/en/releas
unreleased
----------
-`git master <https://github.com/meejah/txtorcon>`_ *will likely become v0.17.0*
+`git master <https://github.com/meejah/txtorcon>`_ *will likely become v0.18.0*
+
+
+v0.17.0
+-------
+
+*October 4, 2016*
+
+ * `txtorcon-0.17.0.tar.gz <http://timaq4ygg2iegci7.onion/txtorcon-0.17.0.tar.gz>`_ (`PyPI <https://pypi.python.org/pypi/txtorcon/0.17.0>`_ (:download:`local-sig </../signatues/txtorcon-0.17.0.tar.gz.asc>` or `github-sig <https://github.com/meejah/txtorcon/blob/master/signatues/txtorcon-0.17.0.tar.gz.asc?raw=true>`_) (`source <https://github.com/meejah/txtorcon/archive/v0.17.0.tar.gz>`_)
+ * `issue 187 <https://github.com/meejah/txtorcon/issues/187>`_: fix unix-socket control endpoints
+ * sometimes mapping streams to hostnames wasn't working properly
+ * backwards-compatibility API for `socks_hostname` was incorrectly named
v0.16.1
diff --git a/examples/ephemeral_endpoint.py b/examples/ephemeral_endpoint.py
index a5b75b0..4d8aa5b 100644
--- a/examples/ephemeral_endpoint.py
+++ b/examples/ephemeral_endpoint.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+from __future__ import print_function
# This connects to the system Tor (by default on control port 9151)
# and adds a new hidden service configuration to it.
@@ -34,28 +34,30 @@ def main(reactor):
)
def on_progress(percent, tag, msg):
- print('%03d: %s' % (percent, msg))
+ print('{:3.0f}%: {}'.format(percent, msg))
txtorcon.IProgressProvider(ep).add_progress_listener(on_progress)
- print "Starting site"
+ print("Starting site")
port = yield ep.listen(server.Site(Simple()))
host = port.getHost()
- print "Site started. Available at http://{}:{}".format(host.onion_uri, host.onion_port)
- print "Private key:\n{}".format(host.onion_key)
+ print("Site started. Available at http://{}:{}".format(host.onion_uri, host.onion_port))
+ print("Private key:\n{}".format(host.onion_key))
- # XXX how to get the HiddenService instance? or the
- # IOnionBlahFoo-implementing thing, I mean
- print("port", dir(port))
- print("port", type(port))
- print("port", port.__provides__)
- print("host", dir(host))
+ # if you have need of the actual IOnionService object (e.g. an
+ # EphemeralHiddenService or FilesystemHiddenService instance) then
+ # you can get it via the port (which itself is a
+ # TorOnionListeningPort instance)
+ service = port.onion_service
- print("ports", host.public_ports)
+ print("Ports:", service.ports)
+ print("Hostname:", service.hostname)
+ print("Private key:", service.private_key)
# wait forever; obviously you could do other work here
d = defer.Deferred()
yield d
-react(main)
+if __name__ == '__main__':
+ react(main)
diff --git a/examples/gui-boom.py b/examples/gui-boom.py
new file mode 100644
index 0000000..08f46d3
--- /dev/null
+++ b/examples/gui-boom.py
@@ -0,0 +1,39 @@
+import sys
+
+from gi.repository import Clutter
+
+
+# highlight handler
+def hover(source, event):
+ source.set_background_color(Clutter.Color.new(96, 224, 96, 255))
+
+def unhover(source, event):
+ source.set_background_color(Clutter.Color.new(128, 128, 128, 255))
+
+
+Clutter.init(sys.argv)
+
+# init stage
+stage = Clutter.Stage()
+
+# issue occurs only in fullscreen
+stage.set_fullscreen(True)
+
+stage.set_layout_manager(Clutter.BoxLayout())
+stage.connect("destroy", lambda _: Clutter.main_quit())
+
+# create vertical stripes
+for _ in range(8):
+ actor = Clutter.Actor()
+
+ actor.set_x_expand(True)
+ actor.set_background_color(Clutter.Color.new(128, 128, 128, 255))
+ actor.set_reactive(True)
+ actor.connect("enter-event", hover)
+ actor.connect("leave-event", unhover)
+
+ stage.add_child(actor)
+
+# start the app
+stage.show_all()
+Clutter.main()
diff --git a/examples/gui-cairo.py b/examples/gui-cairo.py
new file mode 100644
index 0000000..e4eec38
--- /dev/null
+++ b/examples/gui-cairo.py
@@ -0,0 +1,94 @@
+# coding: utf-8
+# 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, Gdk
+
+def draw_area(widget, cr, state):
+
+ w, h = widget.get_allocation().width, widget.get_allocation().height
+ cr.set_source_rgb(0, 0, 0)
+ cr.set_line_width(1.0)
+
+ if False:
+ 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()
+
+
+ cr.set_source_rgb(0, 0, 0)
+ cr.select_font_face('Source Code Pro')
+ cr.set_font_size(22.0)
+ size = cr.font_extents()
+ height = size[2]
+
+ y = height
+ x = 50
+ for circ in state.circuits.values():
+ cr.move_to(x, y)
+ msg = '{circuit.id}: {path}'.format(
+ circuit=circ,
+ path='→'.join([r.location.countrycode for r in circ.path]),
+ )
+ cr.show_text(msg)
+ y += height
+ x = 50
+
+
+def create_win(tor):
+ win = Gtk.Window()
+ win.connect("delete-event", Gtk.main_quit)
+
+ area = Gtk.DrawingArea()
+ area.set_size_request(320, 240)
+ area.connect('draw', draw_area, tor)
+# win.connect("motion-notify-event", motion)
+ 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)
+ state = yield tor.create_state()
+
+ win = create_win(state)
+ win.show_all()
+ app.add_window(win)
+
+ def redraw():
+ print("redraw")
+ win.queue_draw()
+ reactor.callLater(1, redraw)
+ redraw()
+
+ yield Deferred()
+react(main)
diff --git a/examples/redirect_streams.py b/examples/redirect_streams.py
deleted file mode 100644
index 97040e1..0000000
--- a/examples/redirect_streams.py
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env python
-
-from twisted.internet import reactor, defer
-from zope.interface import implements
-
-import txtorcon
-
-
-class MyAttacher(object):
- implements(txtorcon.IStreamAttacher)
-
- def __init__(self, state):
- # pointer to our TorState object
- self.state = state
-
- @defer.inlineCallbacks
- def attach_stream(self, stream, circuits):
- """
- IStreamAttacher API
- """
-
- print "XXXX", stream
- x = yield self.state.protocol.queue_command('REDIRECTSTREAM %s timaq4ygg2iegci7.onion' % stream.id)
- print "X", x
-
- defer.returnValue(None)
-# x = yield self.state.protocol.queue_command('ATTACHSTREAM %s 0' % stream.id)
-# print "X", x
-
-
-def do_setup(state):
- print "Connected to a Tor version", state.protocol.version
-
- attacher = MyAttacher(state)
- state.set_attacher(attacher, reactor)
-
- print "Existing state when we connected:"
- print "Streams:"
- for s in state.streams.values():
- print ' ', s
-
- print
- print "General-purpose circuits:"
- for c in filter(lambda x: x.purpose == 'GENERAL', state.circuits.values()):
- print ' ', c.id, '->'.join(map(lambda x: x.location.countrycode,
- c.path))
-
-
-def setup_failed(arg):
- print "SETUP FAILED", arg
- reactor.stop()
-
-d = txtorcon.build_local_tor_connection(reactor)
-d.addCallback(do_setup).addErrback(setup_failed)
-reactor.run()
diff --git a/test/test_circuit.py b/test/test_circuit.py
index 341e140..e5b3753 100644
--- a/test/test_circuit.py
+++ b/test/test_circuit.py
@@ -100,7 +100,7 @@ class CircuitTests(unittest.TestCase):
circuit = Circuit(tor)
now = datetime.datetime.now()
- update = '1 LAUNCHED PURPOSE=GENERAL TIME_CREATED=%s' % time.strftime('%Y-%m-%dT%H:%M:%S')
+ update = '1 LAUNCHED PURPOSE=GENERAL TIME_CREATED=%s' % now.strftime('%Y-%m-%dT%H:%M:%S')
circuit.update(update.split())
diff = circuit.age(now=now)
self.assertEquals(diff, 0)
diff --git a/test/test_endpoints.py b/test/test_endpoints.py
index d1eb0fb..5ad0fae 100644
--- a/test/test_endpoints.py
+++ b/test/test_endpoints.py
@@ -807,7 +807,7 @@ class TestTorClientEndpoint(unittest.TestCase):
endpoint = TorClientEndpoint(
'torproject.org', 0,
- socks_host='localhost',
+ socks_hostname='localhost',
socks_port=9050,
)
self.assertTrue(isinstance(endpoint.socks_endpoint, TCP4ClientEndpoint))
diff --git a/test/test_stream.py b/test/test_stream.py
index 5ab1ac9..b726ba2 100644
--- a/test/test_stream.py
+++ b/test/test_stream.py
@@ -7,6 +7,7 @@ from txtorcon import Stream
from txtorcon import IStreamListener
from txtorcon import ICircuitContainer
from txtorcon import StreamListenerMixin
+from txtorcon import AddrMap
class FakeCircuit:
@@ -119,6 +120,30 @@ class StreamTests(unittest.TestCase):
args = [None] * len(desc.positional)
method(*args)
+ def test_listener_exception(self):
+ """A listener throws an exception during notify"""
+
+ exc = Exception("the bad stuff happened")
+ class Bad(StreamListenerMixin):
+ def stream_new(*args, **kw):
+ raise exc
+ listener = Bad()
+
+ stream = Stream(self)
+ stream.listen(listener)
+ stream.update("1 NEW 0 94.23.164.42.$43ED8310EB968746970896E8835C2F1991E50B69.exit:9001 SOURCE_ADDR=(Tor_internal):0 PURPOSE=DIR_FETCH".split())
+
+ errors = self.flushLoggedErrors()
+ self.assertEqual(1, len(errors))
+ self.assertEqual(errors[0].value, exc)
+
+ def test_stream_addrmap_remap(self):
+ addrmap = AddrMap()
+ addrmap.update('meejah.ca 1.2.3.4 never')
+ stream = Stream(self, addrmap)
+ stream.update("1604 NEW 0 1.2.3.4:0 PURPOSE=DNS_REQUEST".split())
+ self.assertEqual(stream.target_host, "meejah.ca")
+
def test_circuit_already_valid_in_new(self):
stream = Stream(self)
stream.circuit = FakeCircuit(1)
diff --git a/test/test_torconfig.py b/test/test_torconfig.py
index 5869860..a9668b6 100644
--- a/test/test_torconfig.py
+++ b/test/test_torconfig.py
@@ -1203,6 +1203,42 @@ class LaunchTorTests(unittest.TestCase):
# cancel the errback chain, we wanted this
return None
+ @defer.inlineCallbacks
+ def test_launch_tor_unix_controlport(self):
+ config = TorConfig()
+ config.ControlPort = "unix:/dev/null"
+ trans = FakeProcessTransport()
+ trans.protocol = self.protocol
+ fakeout = StringIO()
+ fakeerr = StringIO()
+
+ def connector(proto, trans):
+ proto._set_valid_events('STATUS_CLIENT')
+ proto.makeConnection(trans)
+ proto.post_bootstrap.callback(proto)
+ return proto.post_bootstrap
+
+ def on_protocol(proto):
+ proto.outReceived('Bootstrapped 90%\n')
+
+ reactor = FakeReactor(self, trans, on_protocol)
+ reactor.connectUNIX = Mock()
+ try:
+ yield launch_tor(
+ config,
+ reactor,
+ tor_binary='/bin/echo',
+ stdout=fakeout,
+ stderr=fakeerr
+ )
+ except Exception:
+ pass
+ self.assertTrue(reactor.connectUNIX.called)
+ self.assertEqual(
+ '/dev/null',
+ reactor.connectUNIX.mock_calls[0][1][0],
+ )
+
def test_launch_tor_fails(self):
config = TorConfig()
config.OrPort = 1234
diff --git a/test/test_torstate.py b/test/test_torstate.py
index a00edd0..002e5d0 100644
--- a/test/test_torstate.py
+++ b/test/test_torstate.py
@@ -438,9 +438,15 @@ class StateTests(unittest.TestCase):
self.send("250 OK")
- self.assertEqual(len(self.state.addrmap.addr), 2)
+ self.assertEqual(len(self.state.addrmap.addr), 4)
self.assertTrue('www.example.com' in self.state.addrmap.addr)
self.assertTrue('subdomain.example.com' in self.state.addrmap.addr)
+ self.assertTrue('10.0.0.0' in self.state.addrmap.addr)
+ self.assertTrue('127.0.0.1' in self.state.addrmap.addr)
+ self.assertEqual('127.0.0.1', self.state.addrmap.find('www.example.com').ip)
+ self.assertEqual('www.example.com', self.state.addrmap.find('127.0.0.1').name)
+ self.assertEqual('10.0.0.0', self.state.addrmap.find('subdomain.example.com').ip)
+ self.assertEqual('subdomain.example.com', self.state.addrmap.find('10.0.0.0').name)
return d
diff --git a/txtorcon.egg-info/PKG-INFO b/txtorcon.egg-info/PKG-INFO
index 55a3f2d..f537578 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.16.1
+Version: 0.17.0
Summary:
Twisted-based Tor controller client, with state-tracking and
configuration abstractions.
@@ -16,16 +16,23 @@ Description: README
.. image:: https://travis-ci.org/meejah/txtorcon.png?branch=master
:target: https://www.travis-ci.org/meejah/txtorcon
+ :alt: travis
.. image:: https://coveralls.io/repos/meejah/txtorcon/badge.png
:target: https://coveralls.io/r/meejah/txtorcon
+ :alt: coveralls
.. image:: http://codecov.io/github/meejah/txtorcon/coverage.svg?branch=master
:target: http://codecov.io/github/meejah/txtorcon?branch=master
+ :alt: codecov
.. image:: http://api.flattr.com/button/flattr-badge-large.png
:target: http://flattr.com/thing/1689502/meejahtxtorcon-on-GitHub
+ :alt: flattr
+ .. image:: https://landscape.io/github/meejah/txtorcon/master/landscape.svg?style=flat
+ :target: https://landscape.io/github/meejah/txtorcon/master
+ :alt: Code Health
quick start
-----------
diff --git a/txtorcon.egg-info/SOURCES.txt b/txtorcon.egg-info/SOURCES.txt
index 50ee52a..a882602 100644
--- a/txtorcon.egg-info/SOURCES.txt
+++ b/txtorcon.egg-info/SOURCES.txt
@@ -50,6 +50,8 @@ examples/circuit_for_next_stream.py
examples/disallow_streams_by_port.py
examples/dump_config.py
examples/ephemeral_endpoint.py
+examples/gui-boom.py
+examples/gui-cairo.py
examples/gui-map.py
examples/gui.py
examples/gui2.py
@@ -63,7 +65,6 @@ examples/launch_tor_with_hiddenservice.py
examples/launch_tor_with_simplehttpd.py
examples/minimal_endpoint.py
examples/monitor.py
-examples/redirect_streams.py
examples/schedule_bandwidth.py
examples/stem_relay_descriptor.py
examples/stream_circuit_logger.py
diff --git a/txtorcon.egg-info/top_level.txt b/txtorcon.egg-info/top_level.txt
index 1ba11e2..dc51759 100644
--- a/txtorcon.egg-info/top_level.txt
+++ b/txtorcon.egg-info/top_level.txt
@@ -1,2 +1,2 @@
-txtorcon
twisted
+txtorcon
diff --git a/txtorcon/_metadata.py b/txtorcon/_metadata.py
index c7f7af5..a436b35 100644
--- a/txtorcon/_metadata.py
+++ b/txtorcon/_metadata.py
@@ -1,4 +1,4 @@
-__version__ = '0.16.1'
+__version__ = '0.17.0'
__author__ = 'meejah'
__contact__ = 'meejah at meejah.ca'
__url__ = 'https://github.com/meejah/txtorcon'
diff --git a/txtorcon/addrmap.py b/txtorcon/addrmap.py
index 6bc266a..fea4293 100644
--- a/txtorcon/addrmap.py
+++ b/txtorcon/addrmap.py
@@ -124,7 +124,9 @@ class AddrMap(object):
else:
a = Addr(self)
+ # add both name and IP address
self.addr[params[0]] = a
+ self.addr[params[1]] = a
a.update(*params)
self.notify("addrmap_added", *[a], **{})
diff --git a/txtorcon/endpoints.py b/txtorcon/endpoints.py
index e36e217..ef30007 100644
--- a/txtorcon/endpoints.py
+++ b/txtorcon/endpoints.py
@@ -680,17 +680,21 @@ class TorClientEndpoint(object):
)
# backwards-compatibility: you used to specify a TCP SOCKS
- # endpoint via socks_host= and socks_port= kwargs
+ # endpoint via socks_hostname= and socks_port= kwargs
if self.socks_endpoint is None:
try:
- self.socks_endpoint = TCP4ClientEndpoint(reactor, kw['socks_host'], kw['socks_port'])
+ self.socks_endpoint = TCP4ClientEndpoint(
+ reactor,
+ kw['socks_hostname'],
+ 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
+ # socks_hostname/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)
diff --git a/txtorcon/stream.py b/txtorcon/stream.py
index 84e87b5..1d45904 100644
--- a/txtorcon/stream.py
+++ b/txtorcon/stream.py
@@ -57,7 +57,7 @@ class Stream(object):
The ID of this stream, a number (or None if unset).
"""
- def __init__(self, circuitcontainer):
+ def __init__(self, circuitcontainer, addrmap=None):
"""
:param circuitcontainer: an object which implements
:class:`interface.ICircuitContainer`
@@ -94,7 +94,7 @@ class Stream(object):
self.listeners = []
"""A list of all connected
- :class:`txtorcon.interface.ICircuitListener` instances."""
+ :class:`txtorcon.interface.IStreamListener` instances."""
self.source_addr = None
"""If available, the address from which this Stream originated
@@ -111,6 +111,8 @@ class Stream(object):
"""Internal. Holds Deferred that will callback when this
stream is CLOSED, FAILED (or DETACHED??)"""
+ self._addrmap = addrmap
+
def listen(self, listen):
"""
Attach an :class:`txtorcon.interface.IStreamListener` to this stream.
@@ -190,14 +192,23 @@ class Stream(object):
last_colon = args[3].rfind(':')
self.target_host = args[3][:last_colon]
self.target_port = int(args[3][last_colon + 1:])
+ # target_host is often an IP address (newer tors? did
+ # this change?) so we attempt to look it up in our
+ # AddrMap and make it a name no matter what.
+ if self._addrmap:
+ try:
+ h = self._addrmap.find(self.target_host)
+ self.target_host = h.name
+ except KeyError:
+ pass
self.target_port = int(self.target_port)
if self.state == 'NEW':
if self.circuit is not None:
log.err(RuntimeError("Weird: circuit valid in NEW"))
- [x.stream_new(self) for x in self.listeners]
+ self._notify('stream_new', self)
else:
- [x.stream_succeeded(self) for x in self.listeners]
+ self._notify('stream_succeeded', self)
elif self.state == 'REMAP':
self.target_addr = maybe_ip_addr(args[3][:args[3].rfind(':')])
@@ -208,7 +219,7 @@ class Stream(object):
self.circuit = None
self.maybe_call_closing_deferred()
flags = self._create_flags(kw)
- [x.stream_closed(self, **flags) for x in self.listeners]
+ self._notify('stream_closed', self, **flags)
elif self.state == 'FAILED':
if self.circuit:
@@ -217,7 +228,7 @@ class Stream(object):
self.maybe_call_closing_deferred()
# build lower-case version of all flags
flags = self._create_flags(kw)
- [x.stream_failed(self, **flags) for x in self.listeners]
+ self._notify('stream_failed', self, **flags)
elif self.state == 'SENTCONNECT':
pass # print 'SENTCONNECT',self,args
@@ -230,7 +241,7 @@ class Stream(object):
# FIXME does this count as closed?
# self.maybe_call_closing_deferred()
flags = self._create_flags(kw)
- [x.stream_detach(self, **flags) for x in self.listeners]
+ self._notify('stream_detach', self, **flags)
elif self.state in ['NEWRESOLVE', 'SENTRESOLVE']:
pass # print self.state, self, args
@@ -253,8 +264,7 @@ class Stream(object):
self.circuit = self.circuit_container.find_circuit(cid)
if self not in self.circuit.streams:
self.circuit.streams.append(self)
- for x in self.listeners:
- x.stream_attach(self, self.circuit)
+ self._notify('stream_attach', self, self.circuit)
else:
if self.circuit.id != cid:
@@ -265,6 +275,17 @@ class Stream(object):
)
)
+ def _notify(self, func, *args, **kw):
+ """
+ Internal helper. Calls the IStreamListener function 'func' with
+ the given args, guarding around errors.
+ """
+ for x in self.listeners:
+ try:
+ getattr(x, func)(*args, **kw)
+ except Exception:
+ log.err()
+
def maybe_call_closing_deferred(self):
"""
Used internally to callback on the _closing_deferred if it
diff --git a/txtorcon/torconfig.py b/txtorcon/torconfig.py
index bfec9a8..66b0f31 100644
--- a/txtorcon/torconfig.py
+++ b/txtorcon/torconfig.py
@@ -17,6 +17,7 @@ from twisted.python import log
from twisted.internet import defer, error, protocol
from twisted.internet.interfaces import IReactorTime
from twisted.internet.endpoints import TCP4ClientEndpoint
+from twisted.internet.endpoints import UNIXClientEndpoint
from txtorcon.torcontrolprotocol import parse_keywords, TorProtocolFactory, DEFAULT_VALUE
from txtorcon.util import delete_file_or_tree, find_keywords, find_tor_binary
@@ -442,10 +443,16 @@ def launch_tor(config, reactor,
config.CookieAuthentication = 1
config.__OwningControllerProcess = os.getpid()
if connection_creator is None:
- connection_creator = functools.partial(
- TCP4ClientEndpoint(reactor, 'localhost', control_port).connect,
- TorProtocolFactory()
- )
+ if str(control_port).startswith('unix:'):
+ connection_creator = functools.partial(
+ UNIXClientEndpoint(reactor, control_port[5:]).connect,
+ TorProtocolFactory()
+ )
+ else:
+ connection_creator = functools.partial(
+ TCP4ClientEndpoint(reactor, 'localhost', control_port).connect,
+ TorProtocolFactory()
+ )
else:
connection_creator = None
@@ -1130,7 +1137,7 @@ class TorConfig(object):
if control is None:
self._protocol = None
- self.__dict__['_slutty_'] = None
+ self.__dict__['_accept_all_'] = None
else:
self._protocol = ITorControlProtocol(control)
@@ -1188,7 +1195,7 @@ class TorConfig(object):
self.__dict__['_protocol'] = proto
# FIXME some of this is duplicated from ctor
- del self.__dict__['_slutty_']
+ del self.__dict__['_accept_all_']
self.__dict__['post_bootstrap'] = defer.Deferred()
if proto.post_bootstrap:
proto.post_bootstrap.addCallback(self.bootstrap)
@@ -1210,13 +1217,20 @@ class TorConfig(object):
attributes we need in the constructor without uusing __dict__
all over the place.
"""
- has_setup_attr = lambda o: '_setup_' in o.__dict__
- has_slutty_attr = lambda o: '_slutty_' in o.__dict__
- is_hidden_services = lambda s: s.lower() == "hiddenservices"
+
+ # appease flake8's hatred of lambda :/
+ def has_setup_attr(o):
+ return '_setup_' in o.__dict__
+
+ def has_accept_all_attr(o):
+ return '_accept_all_' in o.__dict__
+
+ def is_hidden_services(s):
+ return s.lower() == "hiddenservices"
if has_setup_attr(self):
name = self._find_real_name(name)
- if not has_slutty_attr(self) and not is_hidden_services(name):
+ if not has_accept_all_attr(self) and not is_hidden_services(name):
value = self.parsers[name].validate(value, self, name)
if isinstance(value, list):
value = _ListWrapper(
@@ -1241,13 +1255,13 @@ class TorConfig(object):
to be called''
"""
rn = self._find_real_name(name)
- if '_slutty_' in self.__dict__ and rn in self.unsaved:
+ if '_accept_all_' in self.__dict__ and rn in self.unsaved:
return self.unsaved[rn]
self._maybe_create_listwrapper(rn)
return self.config[rn]
def __contains__(self, item):
- if item in self.unsaved and '_slutty_' in self.__dict__:
+ if item in self.unsaved and '_accept_all_' in self.__dict__:
return True
return item in self.config
diff --git a/txtorcon/torstate.py b/txtorcon/torstate.py
index 59d6d4b..c18a577 100644
--- a/txtorcon/torstate.py
+++ b/txtorcon/torstate.py
@@ -788,10 +788,11 @@ class TorState(object):
stream_id = int(args[0])
wasnew = False
if stream_id not in self.streams:
- stream = self.stream_factory(self)
+ stream = self.stream_factory(self, self.addrmap)
self.streams[stream_id] = stream
stream.listen(self)
- [stream.listen(x) for x in self.stream_listeners]
+ for x in self.stream_listeners:
+ stream.listen(x)
wasnew = True
self.streams[stream_id].update(args)
--
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