Commit Graph

4025 Commits

Author SHA1 Message Date
Damian Johnson
4164c7a620 Replace all IOErrors with OSErrors
PEP 3151 deprecated IOError...

  https://www.python.org/dev/peps/pep-3151/#confusing-set-of-os-related-exceptions

Python 3.3 turned IOError into an OSError alias, so this commit shouldn't
impact our users...

  >>> raise OSError('boom')
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  OSError: boom

  >>> raise IOError('boom')
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  OSError: boom
2020-09-01 13:57:23 -07:00
Damian Johnson
8b09cbd778 BandwidthFile tor_version attribute
Adding our bandwidth file's tor_version. The spec claims that it's a version
1.5.0 attribute...

  https://gitweb.torproject.org/torspec.git/commit/?id=7d8b4bc

However, our most recent bandwidth files have this attribute yet claim to be
1.4.0. Maybe our scanners added the attribute without bumping their version?

Working under that assumption and reached out to juga.
2020-08-30 16:03:40 -07:00
Damian Johnson
120715e4af Support resetting circuit timeouts
According to Mike and Karsten, OnionPerf and Shadow require the ability to
reset circuit timeouts when they change tor's guards. Adding this as an
argument to drop_guards().

  https://github.com/torproject/stem/issues/73
2020-08-29 18:38:15 -07:00
Damian Johnson
559fc6c55c Generalize assertion for invalid ONION_CLIENT_AUTH_ADD addresses
Caught thanks to George...

11:42 <+asn> atagar: hello
11:42 <+asn> atagar: seems like the stem integration tests are failing travis:
  https://travis-ci.org/github/torproject/tor/jobs/720852838
11:43 <+asn> atagar: is there a ticket for this?
11:44 <+asn> atagar: if u want i can open one!
23:56 <+atagar> asn: 'seems like the stem integration tests are failing
  travis' => I just compiled tor's master branch (commit d4f3cfe) and our
  integ tests pass. The problem looks to be that tor is testing its commit
  7915b651, which predates  https://gitlab.torproject.org/tpo/core/tor/-/issues/40005.
  Stem added its test after tor changed its response
  (https://gitweb.torproject.org/stem.git/commit/?id=0dba06fd).
23:56 <+atagar> I'd be happy to merge a patch if you'd care to generalize
  the assertion.
2020-08-25 17:33:08 -07:00
Damian Johnson
1f90729003 Decode cookie path with filesystem encoding first
Oops, I iterated over a set to deduplicate if our filesystem encoding is utf-8
or latin-1. However, there's no harm in decoding twice and doing so
inconsistently broke our unit test because set order is non-deterministic
(so we sometimes decoded our unicode path as latin-1).

  ======================================================================
  FAIL: test_unicode_cookie
  ----------------------------------------------------------------------
  Traceback (most recent call last):
    File "/usr/lib/python3.7/unittest/mock.py", line 1204, in patched
      return func(*args, **keywargs)
    File "/srv/jenkins-workspace/workspace/stem-tor-ci/test/unit/response/protocolinfo.py", line 160, in test_unicode_cookie
      self.assertEqual(EXPECTED_UNICODE_PATH, control_message.cookie_path)
  AssertionError: '/home/user/文档/tor-browser_en-US/Browser/TorBrowser/D[23 chars]okie' != '/home/user/æ\x96\x87æ¡£/tor-browser_en-US/Browser/To[33 chars]okie'
  - /home/user/文档/tor-browser_en-US/Browser/TorBrowser/Data/Tor/control_auth_cookie
  ?            ^^
  + /home/user/文档/tor-browser_en-US/Browser/TorBrowser/Data/Tor/control_auth_cookie
  ?            ^^^^^^
2020-08-25 16:22:16 -07:00
Damian Johnson
5696286d8a Download links for tutorial examples
Five years ago we dropped download links from our tutorial examples because
Debian didn't have Sphinx 1.3...

  https://gitweb.torproject.org/stem.git/commit/?id=0541194
  https://trac.torproject.org/projects/tor/ticket/16214

That should now be moot so restoring the links.

This reverts commit 0541194, updated to cover our current tutorials.
2020-08-23 17:17:41 -07:00
Damian Johnson
a331ee334b Timeout stalled queries and installation
Couple good timeout suggestions from...

  https://github.com/torproject/stem/issues/61
2020-08-23 16:49:25 -07:00
Damian Johnson
fbf1682612 Incorrect filesystem encoding broke latin-1 cookie path
When a PROTOCOLINFO's authentication cookie path mismatches our filesystem
encoding falling back to a couple common encodings (unicode and latin-1).

  https://github.com/torproject/stem/issues/57
2020-08-22 15:50:57 -07:00
Damian Johnson
3f67d82f84 New circuit purposes
Added by https://gitweb.torproject.org/torspec.git/commit/?id=a9fee22
2020-08-20 18:19:25 -07:00
Damian Johnson
71ef9ebc55 IP_NOW_REDUNDANT circuit closure reason
Added by https://gitweb.torproject.org/torspec.git/commit/?id=b418155
2020-08-20 18:18:21 -07:00
Damian Johnson
12bf19f59f OR_CONN_CLOSED changed to CHANNEL_CLOSED
This value's description didn't change, just its name.

  https://gitweb.torproject.org/torspec.git/commit/?id=693f5d4
2020-08-20 18:12:59 -07:00
Damian Johnson
1916c39a37 Restore changelog ticket links
Oops! I thought that GitLab only had tickets for components that migrated, but
on reflection everything else is available under the 'legacy/trac' component.

Thanks to Roger for pointing this out!
2020-08-17 18:27:55 -07:00
Damian Johnson
2f3efa9e55 Remove all trac links
Trac is read-only and will be discontinued at some point. I moved our open
tickets to GitHub but we'll lose closed tickets when it's gone. Definitely a
loss, but not too bad - our commit history archives their most useful
information.
2020-08-16 17:51:32 -07:00
Damian Johnson
f490844a07 Add a 'ticket' sphinx role
Adding a role so we can cite stem and tor tickets on their new bug trackers.
2020-08-16 15:31:30 -07:00
Damian Johnson
ced78f5803 Simplify Sphinx roles
Originally we added roles based on a tutorial from...

  https://trac.torproject.org/projects/tor/ticket/8671

This was a lot more verbose than it needs to be. Trimming these to the simplest
functions I can come up with.
2020-08-16 00:38:53 -07:00
Damian Johnson
dd495b2673 Fix malformed table in convert() pydoc
Oops, while adjusting the indentation we broke this table for Sphinx...

  /home/atagar/Desktop/stem/stem/response/__init__.py:docstring of stem.response.convert:15: WARNING: Malformed table.
  Text in column margin in table line 10.
2020-08-15 17:57:46 -07:00
Damian Johnson
2e66a4846e Support mixed MAPADDRESS response
Recently tor documented that MAPADDRESS can contain a mixture of successful and
failed responses...

  https://gitweb.torproject.org/torspec.git/commit/?id=1417af05de247d9f858863849d16a7185577d369

Such a response didn't break us, our map_address() method simply ignored the
failures and returned a dictionary with whatever mappings succeeded.

However, we should provide our caller with failures as well for completeness.
This breaks backward compatibility in a couple ways...

  1. Our map_address() method now returns a MapAddressResponse rather
     than a dictionary.

  2. Renamed MapAddressResponse's "entries" attribute to "mappings".

Personally I'd prefer if MAPADDRESS was all-or-nothing (that is to say, if any
entry is malformed then reject the request). That would produce a simpler API
for our callers. But I don't control that so this is the method response we'll
need to supply.
2020-08-15 17:16:54 -07:00
Damian Johnson
cba4c3fbeb Unassign MAPADDRESS when destination is None
Integration tests should undo any configuration changes they make to avoid
unintentionally impacting later tests. Initially it was unclear to me how to
unassign MAPADDRESS, but I filed the following to clarify this...

  https://gitlab.torproject.org/tpo/core/tor/-/issues/40104
2020-08-15 14:49:47 -07:00
Damian Johnson
092ddb1df7 Drop unused mapaddress integ test
Nick provided an alternate test rather than adjusting the existing one...

  https://trac.torproject.org/projects/tor/ticket/25611

I'm not entirely clear what the issue was but that's an old issue so simply
dropping the obsolete test.
2020-08-14 16:29:58 -07:00
Damian Johnson
8d1effca8f Accept all 2xx response codes
Tor uses a similar response code scheme as HTTP (2xx = ok, 4xx = error,
5xx = fatal). Most successful responses are 250, but better to accept
all 2xx codes.
2020-08-14 16:29:54 -07:00
Damian Johnson
1b301bd651 Missing changelog entries 2020-08-13 17:14:38 -07:00
Damian Johnson
224b202ee3 Add CONTROLLER_WAIT stream status
Stream event status added in...

  https://gitweb.torproject.org/torspec.git/commit/?id=8761aa0
2020-08-12 18:02:53 -07:00
Damian Johnson
daed7bca9f Present a safer pkill command
pkill's '-x' flag picks a process via an exact match rather than a regex.
Caught thanks to uokf...

  17:16 < uokf> I'd like to make a suggestion to change https://stem.torproject.org/faq.html#how-do-i-reload-my-torrc
  17:17 < uokf> it says pkill -sighup tor
  17:17 < uokf> but it should say pkill -x -sighup tor
  17:17 < uokf> pkill sends the signal to all matching pids
  17:18 <@arma> so if you have a process called 'extractor' then it'll hit that one too?
  17:18 <@arma> that seems like a good bug. can you open a ticket for stem? i think atagar likes his tickets in github but i'm not sure.
2020-08-12 15:41:46 -07:00
Damian Johnson
1731720fb8 Update microdescriptor pydocs
This documentation dates back to the dawn of Stem. Thanks to Barkin for
pointing out that it is no longer accurate...

  https://github.com/torproject/stem/issues/68
2020-08-11 17:03:58 -07:00
Damian Johnson
e3c21a6b3e Generator methods uncallable when synchronous
Our Synchronous mixin failed to mock any async method that yields because
inspect.iscoroutinefunction() does not recognize them...

============================================================

import asyncio
import inspect

class Demo(object):
  async def async_method(self):
    return 'hi'

  async def async_generator(self):
    yield 'hi'

def print_awaitability(func):
  print('')
  print('asyncio.iscoroutinefunction: %s' % asyncio.iscoroutinefunction(func))
  print('inspect.iscoroutinefunction: %s' % inspect.iscoroutinefunction(func))
  print('inspect.isawaitable: %s' % inspect.isawaitable(func))
  print('inspect.isasyncgenfunction: %s' % inspect.isasyncgenfunction(func))
  print('')

print('-' * 60)
print('Asynchronous method')
print('-' * 60)

print_awaitability(Demo.async_method)

print('-' * 60)
print('Asynchronous generator')
print('-' * 60)

print_awaitability(Demo.async_generator)

============================================================

% python3.7 demo.py
------------------------------------------------------------
Asynchronous method
------------------------------------------------------------

asyncio.iscoroutinefunction: True
inspect.iscoroutinefunction: True
inspect.isawaitable: False
inspect.isasyncgenfunction: False

------------------------------------------------------------
Asynchronous generator
------------------------------------------------------------

asyncio.iscoroutinefunction: False
inspect.iscoroutinefunction: False
inspect.isawaitable: False
inspect.isasyncgenfunction: True
2020-08-10 17:55:59 -07:00
Damian Johnson
d7eb8827c3 Fix tor-prompt initialization
Tor's present commit (67fc69c) isn't providing a bootstrap message with
progress above 0% so dropping that requirement. Also fixing...

  Traceback (most recent call last):
    File "./tor-prompt", line 8, in <module>
      stem.interpreter.main()
    File "/home/atagar/Desktop/stem/stem/interpreter/__init__.py", line 109, in main
      password_prompt = True,
    File "/home/atagar/Desktop/stem/stem/connection.py", line 285, in connect
      connection = asyncio.run_coroutine_threadsafe(connect_async(control_port, control_socket, password, password_prompt, chroot_path, controller), loop).result()
    File "/home/atagar/Python-3.7.0/Lib/concurrent/futures/_base.py", line 432, in result
      return self.__get_result()
    File "/home/atagar/Python-3.7.0/Lib/concurrent/futures/_base.py", line 384, in __get_result
      raise self._exception
    File "/home/atagar/Desktop/stem/stem/connection.py", line 363, in connect_async
      raise ValueError("'%s' isn't a valid port" % control_port[1])
  ValueError: 'None' isn't a valid port
2020-08-10 17:55:53 -07:00
Damian Johnson
2dde4f69ed get_circuit() failed with integer circuit ids
Our get_circuit() method is documented as taking an integer circuit id, but our
CircuitEvent class uses string id attributes. As a result calling with an int
argument would always fail with 'Tor currently does not have a circuit with the
id of x' error.

Caught thanks to Joel.
2020-08-07 17:38:09 -07:00
Damian Johnson
b31551f37e Unit tests fail on big-endian systems
Thanks to Juan for the catch. On big-endian systems such as CentOS our unit
tests failed because we don't mock our IS_LITTLE_ENDIAN constant (so our
assertions are based on being little-endian).

By mocking the constant as 'False' we fail in the same way that Juan
reports...

  https://github.com/torproject/stem/issues/71
2020-08-07 17:27:09 -07:00
Kevin
1da1a17874 Fix open call during hash verification.
"open" needs one of create/read/write/append

https://github.com/torproject/stem/pull/72
2020-08-07 17:13:49 -07:00
Damian Johnson
0be767b714 get_listener() integ test broke for older tor versions
Oops, adjusting this assertion in commit 435b980c broke our tests for prior tor
versions. Caught thanks to asn.

  12:42 <+asn> AssertionError: Lists differ: [('0.0.0.0', 1113), ('::', 1113)]
    != [('0.0.0.0', 1113)]
  ...
  20:06 <+atagar> asn, nickm: Thanks for pointing out the stem test assertion
    error. I'd like to approach this via a conditional. What was the tor
    version where the address behavior changed?
  21:38 <+nickm> atagar: somewhere in 0.4.5.x
  21:38 <+nickm> I believe that ">= 0.4.5.0" will do fine
  21:42 <+nickm> (since 0.4.5.1 isn't out yet)
  21:48 <+atagar> perfect, thanks
2020-08-06 17:24:47 -07:00
Damian Johnson
31d25caffc Client side authentication for HSv3
Thanks to mig5 adding support for tor's new ONION_CLIENT_AUTH_ADD,
ONION_CLIENT_AUTH_REMOVE, and ONION_CLIENT_AUTH_VIEW features. These
add support for authenticating *to* a version 3 hidden service.

  https://github.com/torproject/stem/issues/66

Implementation uncovered some rough edges within tor...

  * Controllers cannot create authenticated services

    https://gitlab.torproject.org/tpo/core/tor/-/issues/40084

  * Client names apparently don't work

    https://gitlab.torproject.org/tpo/core/tor/-/issues/40089

  * Credential persistence either doesn't work or vague error response

    https://gitlab.torproject.org/tpo/core/tor/-/issues/40090
2020-08-06 16:59:44 -07:00
Damian Johnson
6af714a4bd Rewrite ONION_CLIENT_AUTH_VIEW parsing
Mig5's parser was a fine proof of concept but stem parses everything within the
spec. Our list_hidden_service_auth() method now returns either a credential or
dictionary of credentials based on if we're requesting a single service or
everything.
2020-08-06 16:56:39 -07:00
Damian Johnson
5a104e84de Rearrange hidden service auth test
No significant changes. These tests were great, just rearranging things a tad.
2020-08-05 16:50:45 -07:00
Damian Johnson
e302e46be5 Rename new authentication methods
These method names were based on the controller commands which is fine, but we
have some conventions of our own. Renaming these methods for a couple
reasons...

  * For consitency Stem still calls these 'hidden services', and will continue
    to do so until...

    https://trac.torproject.org/projects/tor/ticket/25918

  * We prefix getter methods like this with 'list_'.
2020-08-05 16:34:19 -07:00
Damian Johnson
0d60896e0d Minor whitespace fixes
Address minor issues cited by pycodestyle...

  STATIC CHECKS
  * /home/atagar/Desktop/stem/stem/response/__init__.py
    line 164  - E302 expected 2 blank lines, found 1     | def _convert_to_onion_client_auth_add(message: 'stem.response.ControlMessage', **kwargs: Any) -> 'stem.response.onion_client_auth.OnionClientAuthAddResponse':
    line 168  - E302 expected 2 blank lines, found 1     | def _convert_to_onion_client_auth_remove(message: 'stem.response.ControlMessage', **kwargs: Any) -> 'stem.response.onion_client_auth.OnionClientAuthRemoveResponse':
    line 172  - E302 expected 2 blank lines, found 1     | def _convert_to_onion_client_auth_view(message: 'stem.response.ControlMessage', **kwargs: Any) -> 'stem.response.onion_client_auth.OnionClientAuthViewResponse':
    line 176  - E302 expected 2 blank lines, found 1     | def _convert_to_mapaddress(message: 'stem.response.ControlMessage', **kwargs: Any) -> 'stem.response.mapaddress.MapAddressResponse':

  * /home/atagar/Desktop/stem/stem/response/onion_client_auth.py
    line 7    - E302 expected 2 blank lines, found 1     | class OnionClientAuthAddResponse(stem.response.ControlMessage):
    line 23   - E302 expected 2 blank lines, found 1     | class OnionClientAuthRemoveResponse(stem.response.ControlMessage):
    line 30   - W291 trailing whitespace                 | # '250 OK',
    line 37   - E302 expected 2 blank lines, found 1     | class OnionClientAuthViewResponse(stem.response.ControlMessage):
2020-08-04 17:02:09 -07:00
Miguel Jacq
0dba06fd54 Adds support for ONION_CLIENT_AUTH_ADD, ONION_CLIENT_AUTH_REMOVE and ONION_CLIENT_AUTH_VIEW 2020-08-04 16:43:56 -07:00
Damian Johnson
2946016e8b Fix control socket integration tests
Our tests had a few failures when exercising a ControlSocket rather than a
ControlPort. Finally our tests now pass with the RUN_ALL target.
2020-07-31 18:31:23 -07:00
Damian Johnson
50a5e9a3bc Fix test_authenticate_general_example
This test failed when running with a control socket (the RUN_SOCKET test
target) because our constructor no longer implicity connects. Fixing this
tests' "am I using a control port" check.

The test failure message was...

  ======================================================================
  FAIL: test_authenticate_general_example
  ----------------------------------------------------------------------
  Traceback (most recent call last):
    File "/home/atagar/Desktop/stem/stem/socket.py", line 456, in _open_connection
      return await asyncio.open_connection(self.address, self.port)
    File "/home/atagar/Python-3.7.0/Lib/asyncio/streams.py", line 77, in open_connection
      lambda: protocol, host, port, **kwds)
    File "/home/atagar/Python-3.7.0/Lib/asyncio/base_events.py", line 943, in create_connection
      raise exceptions[0]
    File "/home/atagar/Python-3.7.0/Lib/asyncio/base_events.py", line 930, in create_connection
      await self.sock_connect(sock, address)
  ConnectionRefusedError: [Errno 111] Connect call failed ('127.0.0.1', 1111)

  During handling of the above exception, another exception occurred:

  Traceback (most recent call last):
    File "/home/atagar/Desktop/stem/stem/connection.py", line 583, in authenticate
      protocolinfo_response = await get_protocolinfo(controller)
    File "/home/atagar/Desktop/stem/stem/connection.py", line 1077, in get_protocolinfo
      await controller.connect()
    File "/home/atagar/Desktop/stem/stem/socket.py", line 182, in connect
      self._reader, self._writer = await self._open_connection()
    File "/home/atagar/Desktop/stem/stem/socket.py", line 458, in _open_connection
      raise stem.SocketError(exc)
  stem.SocketError: [Errno 111] Connect call failed ('127.0.0.1', 1111)

  During handling of the above exception, another exception occurred:

  Traceback (most recent call last):
    File "/home/atagar/Desktop/stem/test/integ/connection/authentication.py", line 146, in test_authenticate_general_example
      await stem.connection.authenticate(control_socket, chroot_path = runner.get_chroot())
    File "/home/atagar/Desktop/stem/stem/connection.py", line 587, in authenticate
      raise AuthenticationFailure('socket connection failed (%s)' % exc)
  stem.connection.AuthenticationFailure: socket connection failed ([Errno 111] Connect call failed ('127.0.0.1', 1111))

  During handling of the above exception, another exception occurred:

  Traceback (most recent call last):
    File "/home/atagar/Desktop/stem/test/require.py", line 75, in wrapped
      return func(self, *args, **kwargs)
    File "/home/atagar/Desktop/stem/stem/util/test_tools.py", line 701, in wrapper
      result = loop.run_until_complete(func(*args, **kwargs))
    File "/home/atagar/Python-3.7.0/Lib/asyncio/base_events.py", line 568, in run_until_complete
      return future.result()
    File "/home/atagar/Desktop/stem/test/integ/connection/authentication.py", line 160, in test_authenticate_general_example
      self.fail()
  AssertionError: None

  ----------------------------------------------------------------------
2020-07-31 18:22:44 -07:00
Damian Johnson
b489fda9a2 Recognize socket disconnections
Unfortunately we can't differentiate socket disconnections from errors except
by its message. Asyncio sockets use a different message so revising our check.

This fixes the following RUN_SOCKET test...

  ======================================================================
  ERROR: test_send_disconnected
  ----------------------------------------------------------------------
  Traceback (most recent call last):
    File "/home/atagar/Desktop/stem/stem/socket.py", line 536, in _write_to_socket
      await writer.drain()
    File "/home/atagar/Python-3.7.0/Lib/asyncio/streams.py", line 348, in drain
      await self._protocol._drain_helper()
    File "/home/atagar/Python-3.7.0/Lib/asyncio/streams.py", line 202, in _drain_helper
      raise ConnectionResetError('Connection lost')
  ConnectionResetError: Connection lost

  During handling of the above exception, another exception occurred:

  Traceback (most recent call last):
    File "/home/atagar/Desktop/stem/test/require.py", line 75, in wrapped
      return func(self, *args, **kwargs)
    File "/home/atagar/Desktop/stem/stem/util/test_tools.py", line 701, in wrapper
      result = loop.run_until_complete(func(*args, **kwargs))
    File "/home/atagar/Python-3.7.0/Lib/asyncio/base_events.py", line 568, in run_until_complete
      return future.result()
    File "/home/atagar/Desktop/stem/test/integ/socket/control_socket.py", line 118, in test_send_disconnected
      await control_socket.send('blarg')
    File "/home/atagar/Desktop/stem/stem/socket.py", line 413, in send
      await self._send(message, send_message)
    File "/home/atagar/Desktop/stem/stem/socket.py", line 238, in _send
      await handler(self._writer, message)
    File "/home/atagar/Desktop/stem/stem/socket.py", line 525, in send_message
      await _write_to_socket(writer, message)
    File "/home/atagar/Desktop/stem/stem/socket.py", line 547, in _write_to_socket
      raise stem.SocketError(exc)
  stem.SocketError: Connection lost
2020-07-31 18:11:48 -07:00
Damian Johnson
dc051cee91 Catch BrokenPipeError when closing unix sockets
I spent a few hours investigating the root cause but no luck. When closing a
unix socket that has been terminated by the other end our closed_wait() method
raises a BrokenPipeError. In the following test this causes us to fail to
reconnect the socket (because reconnection first closes us).

This only happens with a ControlSocket (ie. our RUN_SOCKET test target).

  ======================================================================
  ERROR: test_pre_disconnected_query
  ----------------------------------------------------------------------
  Traceback (most recent call last):
    File "/home/atagar/Desktop/stem/test/require.py", line 75, in wrapped
      return func(self, *args, **kwargs)
    File "/home/atagar/Desktop/stem/stem/util/test_tools.py", line 701, in wrapper
      result = loop.run_until_complete(func(*args, **kwargs))
    File "/home/atagar/Python-3.7.0/Lib/asyncio/base_events.py", line 568, in run_until_complete
      return future.result()
    File "/home/atagar/Desktop/stem/test/integ/response/protocolinfo.py", line 122, in test_pre_disconnected_query
      self.assert_matches_test_config(protocolinfo_response)
    File "/home/atagar/Desktop/stem/stem/socket.py", line 293, in __aexit__
      await self.close()
    File "/home/atagar/Desktop/stem/stem/socket.py", line 203, in close
      await self._close_wo_send_lock()
    File "/home/atagar/Desktop/stem/stem/socket.py", line 215, in _close_wo_send_lock
      await self._writer.wait_closed()
    File "/home/atagar/Python-3.7.0/Lib/asyncio/streams.py", line 323, in wait_closed
      await self._protocol._closed
    File "/home/atagar/Desktop/stem/test/integ/response/protocolinfo.py", line 121, in test_pre_disconnected_query
      protocolinfo_response = await stem.connection.get_protocolinfo(control_socket)
    File "/home/atagar/Desktop/stem/stem/connection.py", line 1077, in get_protocolinfo
      await controller.connect()
    File "/home/atagar/Desktop/stem/stem/socket.py", line 181, in connect
      await self._close_wo_send_lock()
    File "/home/atagar/Desktop/stem/stem/socket.py", line 215, in _close_wo_send_lock
      await self._writer.wait_closed()
    File "/home/atagar/Python-3.7.0/Lib/asyncio/streams.py", line 323, in wait_closed
      await self._protocol._closed
    File "/home/atagar/Python-3.7.0/Lib/asyncio/selector_events.py", line 868, in write
      n = self._sock.send(data)
  BrokenPipeError: [Errno 32] Broken pipe
2020-07-31 17:34:29 -07:00
Damian Johnson
a77ff0225a Move controller connecting to aenter
Static methods such as from_port() and from_socket_file() cannot invoke
asynchronous methods. Fundimentally this is the same problem as our ainit -
when a loop is transtively running us we cannot join any futures we create.

Luckily in this case we can simply sidestep the headache. from_port() and
from_socket_file() are designed for 'with' statements so we can simply move the
act of connecting into our context management (which is already asynchronous).

I encountered this problem when I ran the following...

  import asyncio

  from stem.control import Controller

  async def print_version_async():
    async with Controller.from_port() as controller:
      await controller.authenticate()
      print('[with asyncio] tor is version %s' % await controller.get_version())

  def print_version_sync():
    with Controller.from_port() as controller:
      controller.authenticate()
      print('[without asyncio] tor is version %s' % controller.get_version())

  print_version_sync()
  asyncio.run(print_version_async())

Before:

  % python3.7 demo.py
  [without asyncio] tor is version 0.4.5.0-alpha-dev (git-9d922b8eaae54242)
  /home/atagar/Desktop/stem/stem/control.py:1059: RuntimeWarning: coroutine 'BaseController.connect' was never awaited
    controller.connect()
  [with asyncio] tor is version 0.4.5.0-alpha-dev (git-9d922b8eaae54242)

After:

  % python3.7 demo.py
  [without asyncio] tor is version 0.4.5.0-alpha-dev (git-9d922b8eaae54242)
  [with asyncio] tor is version 0.4.5.0-alpha-dev (git-9d922b8eaae54242)
2020-07-29 16:56:52 -07:00
Damian Johnson
ffb8d33ea9 Fix authentication test
Oops, we only exercise this line when using authentication (which isn't the
default). Our tests now pass when using the RUN_ALL target.

  ======================================================================
  ERROR: test_authenticate_general_cookie
  ----------------------------------------------------------------------
  Traceback (most recent call last):
    File "/srv/jenkins-workspace/workspace/stem-tor-ci/test/require.py", line 75, in wrapped
      return func(self, *args, **kwargs)
    File "/srv/jenkins-workspace/workspace/stem-tor-ci/stem/util/test_tools.py", line 701, in wrapper
      result = loop.run_until_complete(func(*args, **kwargs))
    File "/usr/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
      return future.result()
    File "/srv/jenkins-workspace/workspace/stem-tor-ci/test/integ/connection/authentication.py", line 221, in test_authenticate_general_cookie
      if method in protocolinfo_response.auth_methods:
  AttributeError: 'coroutine' object has no attribute 'auth_methods'
2020-07-27 19:28:29 -07:00
Damian Johnson
c1bad9e9b2 Move Synchronous to its own module
This class has grown sophisticated enough that it deserves its own module.
Also, I'd like to discuss this with the wider python community and this will
make it easier to cite.
2020-07-25 16:08:59 -07:00
Damian Johnson
435decc082 Follow-up asyncio test fixes
Our asyncio branch was a large overhaul, and though the tests seemed to pass
locally it introduced several regressions...

  * Python 3.6 support broke due to usage of asyncio.get_running_loop().

  * Interpreter broke. This test was skipped locally because I can't run
    python's readline module without segfaulting.

  * Our ONLINE target had multiple failures. We don't run this target often so
    some of the regressions predated this branch. Fixing the ONLINE target is
    the significant bulk of these fixes.
2020-07-24 16:23:33 -07:00
Damian Johnson
561b9e6eb9 Fix interpreter integ test
Our integration tests are failing with...

  ======================================================================
  FAIL: test_running_command
  ----------------------------------------------------------------------
  Traceback (most recent call last):
    File "/home/atagar/Desktop/stem/test/require.py", line 75, in wrapped
      return func(self, *args, **kwargs)
    File "/home/atagar/Desktop/stem/test/integ/interpreter.py", line 35, in test_running_command
      self.assertEqual(expected, _run_prompt('--run', 'GETINFO config-file'))
  AssertionError: Lists differ: ['250-config-file=/home/atagar/Desktop/stem/test/data/torrc', '250 OK'] != []

  First list contains 2 additional elements.
  First extra element 0:
  '250-config-file=/home/atagar/Desktop/stem/test/data/torrc'

  - ['250-config-file=/home/atagar/Desktop/stem/test/data/torrc', '250 OK']
  + []

Our actual reason surfaces in tor-prompt's stderr...

  % python3.7 tor-prompt --run 'GETINFO config-file' --interface 9051
  /home/atagar/Desktop/stem/stem/interpreter/__init__.py:115: RuntimeWarning: coroutine 'BaseController.__aenter__' was never awaited
    with controller:
  /home/atagar/Desktop/stem/stem/interpreter/commands.py:366: RuntimeWarning: coroutine 'BaseController.msg' was never awaited
    output = format(str(self._controller.msg(command).raw_content()).strip(), *STANDARD_OUTPUT)
  /home/atagar/Desktop/stem/stem/interpreter/__init__.py:182: RuntimeWarning: coroutine 'BaseController.__aexit__' was never awaited
    break

The problem is that stem.connection.connect() returns asynchronous controllers,
whereas callers such as the interpreter require the class to be synchronous.
This workaround is pretty gross hackery but in the long run I expect to
completely replace the module prior to Stem 2.x.
2020-07-24 16:14:15 -07:00
Damian Johnson
9e68863c6a Only requre readline for interactive interpreter
For reasons I don't grok python 3.7's readline module segfaults whenever I use
it, so I've been unable to run our tor-prompt tests.

We only need readline for an interactive interpreter so narrowing it to that
scope so I can once again run its other tests.
2020-07-23 15:58:33 -07:00
Damian Johnson
dd623ddeb7 Unit test broken by get_port() type change
Oops, just ran the integ tests prior to pushing. Type change broke a unit test.

  ======================================================================
  FAIL: test_get_ports
  ----------------------------------------------------------------------
  Traceback (most recent call last):
    File "/home/atagar/Python-3.7.0/Lib/unittest/mock.py", line 1191, in patched
      return func(*args, **keywargs)
    File "/home/atagar/Desktop/stem/test/unit/control/controller.py", line 231, in test_get_ports
      self.assertEqual([9050], self.controller.get_ports(Listener.CONTROL))
  AssertionError: [9050] != {9050}

  ----------------------------------------------------------------------
2020-07-23 15:49:28 -07:00
Damian Johnson
435b980c62 Tor added default IPv6 ORPort
Tor recently changed its ORPort behavior so it provides both an IPv4 and IPv6
endpoint by default...

  https://github.com/torproject/stem/issues/70

Adjusting our tests. Our get_ports() method now provides a set rather than a
list so we don't return duplicate values.
2020-07-23 15:37:07 -07:00
Damian Johnson
774876798b Close integ query threads
Our Query instances now must be manually closed. This resolves the following
when running our ONLINE target...

  Threads lingering after test run:
    <_MainThread(MainThread, started 139802875361024)>
    <Thread(Query asyncio, started daemon 139802728457984)>
    <Thread(Query asyncio, started daemon 139802586375936)>
    <Thread(Query asyncio, started daemon 139802594768640)>
    <Thread(Query asyncio, started daemon 139802544412416)>
    <Thread(Query asyncio, started daemon 139801990788864)>
    <Thread(Query asyncio, started daemon 139801982396160)>
    <Thread(Query asyncio, started daemon 139801974003456)>
2020-07-23 15:26:58 -07:00
Damian Johnson
b30d82e049 Synchronize fallback cache
Tor merged a bulk fallback cache update today. Again since I'm actively working
on our ONLINE test target pulling the revisions in.
2020-07-23 15:26:54 -07:00