2016-06-04 06:07:50 +00:00
|
|
|
from __future__ import print_function, unicode_literals
|
2018-04-21 07:30:08 +00:00
|
|
|
|
2017-05-23 07:45:02 +00:00
|
|
|
import sys
|
2018-04-21 07:30:08 +00:00
|
|
|
|
|
|
|
from attr import attrib, attrs
|
2016-03-03 20:24:07 +00:00
|
|
|
from twisted.internet.defer import inlineCallbacks, returnValue
|
2017-09-14 22:48:35 +00:00
|
|
|
from twisted.internet.endpoints import clientFromString
|
2018-04-21 07:30:08 +00:00
|
|
|
from zope.interface.declarations import directlyProvides
|
|
|
|
|
|
|
|
from . import _interfaces, errors
|
|
|
|
from .timing import DebugTiming
|
|
|
|
|
rewrite Tor support (py2 only)
The new TorManager adds --launch-tor and --tor-control-port= arguments
(requiring the user to explicitly request a new Tor process, if that's what
they want). The default (when --tor is enabled) looks for a control port in
the usual places (/var/run/tor/control, localhost:9051, localhost:9151), then
falls back to hoping there's a SOCKS port in the usual
place (localhost:9050). (closes #64)
The ssh utilities should now accept the same tor arguments as ordinary
send/receive commands. There are now full tests for TorManager, and basic
tests for how send/receive use it. (closes #97)
Note that Tor is only supported on python2.7 for now, since txsocksx (and
therefore txtorcon) doesn't work on py3. You need to do "pip install
magic-wormhole[tor]" to get Tor support, and that will get you an inscrutable
error on py3 (referencing vcversioner, "install_requires must be a string or
list of strings", and "int object not iterable").
To run tests, you must install with the [dev] extra (to get "mock" and other
libraries). Our setup.py only includes "txtorcon" in the [dev] extra when on
py2, not on py3. Unit tests tolerate the lack of txtorcon (they mock out
everything txtorcon would provide), so they should provide the same coverage
on both py2 and py3.
2017-01-16 03:24:23 +00:00
|
|
|
try:
|
2017-05-23 07:45:02 +00:00
|
|
|
import txtorcon
|
rewrite Tor support (py2 only)
The new TorManager adds --launch-tor and --tor-control-port= arguments
(requiring the user to explicitly request a new Tor process, if that's what
they want). The default (when --tor is enabled) looks for a control port in
the usual places (/var/run/tor/control, localhost:9051, localhost:9151), then
falls back to hoping there's a SOCKS port in the usual
place (localhost:9050). (closes #64)
The ssh utilities should now accept the same tor arguments as ordinary
send/receive commands. There are now full tests for TorManager, and basic
tests for how send/receive use it. (closes #97)
Note that Tor is only supported on python2.7 for now, since txsocksx (and
therefore txtorcon) doesn't work on py3. You need to do "pip install
magic-wormhole[tor]" to get Tor support, and that will get you an inscrutable
error on py3 (referencing vcversioner, "install_requires must be a string or
list of strings", and "int object not iterable").
To run tests, you must install with the [dev] extra (to get "mock" and other
libraries). Our setup.py only includes "txtorcon" in the [dev] extra when on
py2, not on py3. Unit tests tolerate the lack of txtorcon (they mock out
everything txtorcon would provide), so they should provide the same coverage
on both py2 and py3.
2017-01-16 03:24:23 +00:00
|
|
|
except ImportError:
|
2017-05-23 07:45:02 +00:00
|
|
|
txtorcon = None
|
2018-04-21 07:30:08 +00:00
|
|
|
|
2016-03-03 20:24:07 +00:00
|
|
|
|
2017-05-23 22:01:57 +00:00
|
|
|
@attrs
|
|
|
|
class SocksOnlyTor(object):
|
|
|
|
_reactor = attrib()
|
|
|
|
|
|
|
|
def stream_via(self, host, port, tls=False):
|
|
|
|
return txtorcon.TorClientEndpoint(
|
2018-04-21 07:30:08 +00:00
|
|
|
host,
|
|
|
|
port,
|
|
|
|
socks_endpoint=None, # tries localhost:9050 and 9150
|
2017-05-23 22:01:57 +00:00
|
|
|
tls=tls,
|
|
|
|
reactor=self._reactor,
|
|
|
|
)
|
|
|
|
|
2018-04-21 07:30:08 +00:00
|
|
|
|
2017-05-23 07:45:02 +00:00
|
|
|
@inlineCallbacks
|
2018-04-21 07:30:08 +00:00
|
|
|
def get_tor(reactor,
|
|
|
|
launch_tor=False,
|
|
|
|
tor_control_port=None,
|
|
|
|
timing=None,
|
|
|
|
stderr=sys.stderr):
|
2017-05-23 07:45:02 +00:00
|
|
|
"""
|
|
|
|
If launch_tor=True, I will try to launch a new Tor process, ask it
|
|
|
|
for its SOCKS and control ports, and use those for outbound
|
|
|
|
connections (and inbound onion-service listeners, if necessary).
|
|
|
|
|
|
|
|
Otherwise if tor_control_port is provided, I will attempt to connect
|
2017-05-23 22:01:57 +00:00
|
|
|
to an existing Tor's control port at the endpoint it specifies. I'll
|
2017-05-23 07:45:02 +00:00
|
|
|
ask that Tor for its SOCKS port.
|
|
|
|
|
|
|
|
With no arguments, I will try to connect to an existing Tor's control
|
|
|
|
port at the usual places: [unix:/var/run/tor/control,
|
2017-05-23 22:01:57 +00:00
|
|
|
tcp:127.0.0.1:9051, tcp:127.0.0.1:9151]. If any are successful, I'll
|
|
|
|
ask that Tor for its SOCKS port. If none are successful, I'll
|
|
|
|
attempt to do SOCKS to the usual places: [tcp:127.0.0.1:9050,
|
|
|
|
tcp:127.0.0.1:9150].
|
2017-05-23 07:45:02 +00:00
|
|
|
|
|
|
|
If I am unable to make a SOCKS connection, the initial connection to
|
|
|
|
the Rendezvous Server will fail, and the program will terminate.
|
|
|
|
|
|
|
|
Control-port connections can only succeed if I can authenticate (by
|
|
|
|
reading a cookie file named by the Tor process), so the current user
|
|
|
|
must have permission to read that file (either they started Tor, e.g.
|
|
|
|
TorBrowser, or they are in a unix group that's been given access,
|
|
|
|
e.g. debian-tor).
|
|
|
|
"""
|
|
|
|
# rationale: launching a new Tor takes a long time, so only do it if
|
|
|
|
# the user specifically asks for it with --launch-tor. Using an
|
|
|
|
# existing Tor should be much faster, but still requires general
|
|
|
|
# permission via --tor.
|
|
|
|
|
|
|
|
if not txtorcon:
|
|
|
|
raise errors.NoTorError()
|
|
|
|
|
2018-04-21 07:30:08 +00:00
|
|
|
if not isinstance(launch_tor, bool): # note: False is int
|
2017-05-23 07:45:02 +00:00
|
|
|
raise TypeError("launch_tor= must be boolean")
|
|
|
|
if not isinstance(tor_control_port, (type(""), type(None))):
|
|
|
|
raise TypeError("tor_control_port= must be str or None")
|
|
|
|
assert tor_control_port != ""
|
|
|
|
if launch_tor and tor_control_port is not None:
|
|
|
|
raise ValueError("cannot combine --launch-tor and --tor-control-port=")
|
|
|
|
timing = timing or DebugTiming()
|
|
|
|
|
|
|
|
# Connect to an existing Tor, or create a new one. If we need to
|
|
|
|
# launch an onion service, then we need a working control port (and
|
|
|
|
# authentication cookie). If we're only acting as a client, we don't
|
|
|
|
# need the control port.
|
|
|
|
|
|
|
|
if launch_tor:
|
2018-04-21 07:30:08 +00:00
|
|
|
print(
|
|
|
|
" launching a new Tor process, this may take a while..",
|
|
|
|
file=stderr)
|
2017-05-23 07:45:02 +00:00
|
|
|
with timing.add("launch tor"):
|
|
|
|
tor = yield txtorcon.launch(reactor,
|
2018-04-21 07:30:08 +00:00
|
|
|
# data_directory=,
|
|
|
|
# tor_binary=,
|
2017-05-23 07:45:02 +00:00
|
|
|
)
|
2017-09-14 23:46:26 +00:00
|
|
|
elif tor_control_port:
|
|
|
|
with timing.add("find tor"):
|
|
|
|
control_ep = clientFromString(reactor, tor_control_port)
|
2018-04-21 07:30:08 +00:00
|
|
|
tor = yield txtorcon.connect(reactor, control_ep) # might raise
|
|
|
|
print(
|
|
|
|
" using Tor via control port at %s" % tor_control_port,
|
|
|
|
file=stderr)
|
2017-05-23 07:45:02 +00:00
|
|
|
else:
|
2017-09-14 23:46:26 +00:00
|
|
|
# Let txtorcon look through a list of usual places. If that fails,
|
|
|
|
# we'll arrange to attempt the default SOCKS port
|
2017-05-23 07:45:02 +00:00
|
|
|
with timing.add("find tor"):
|
|
|
|
try:
|
2017-09-14 23:46:26 +00:00
|
|
|
tor = yield txtorcon.connect(reactor)
|
|
|
|
print(" using Tor via default control port", file=stderr)
|
2017-05-23 07:45:02 +00:00
|
|
|
except Exception:
|
2017-05-23 22:01:57 +00:00
|
|
|
# TODO: make this more specific. I think connect() is
|
|
|
|
# likely to throw a reactor.connectTCP -type error, like
|
|
|
|
# ConnectionFailed or ConnectionRefused or something
|
2018-04-21 07:30:08 +00:00
|
|
|
print(
|
|
|
|
" unable to find default Tor control port, using SOCKS",
|
|
|
|
file=stderr)
|
2017-05-23 22:01:57 +00:00
|
|
|
tor = SocksOnlyTor(reactor)
|
2017-05-23 07:45:02 +00:00
|
|
|
directlyProvides(tor, _interfaces.ITorManager)
|
|
|
|
returnValue(tor)
|