[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