magic-wormhole/src/wormhole/tor_manager.py

119 lines
4.5 KiB
Python
Raw Normal View History

from __future__ import print_function, unicode_literals
2018-04-21 07:30:08 +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
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
try:
import txtorcon
except ImportError:
txtorcon = None
2018-04-21 07:30:08 +00:00
2016-03-03 20:24:07 +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
tls=tls,
reactor=self._reactor,
)
2018-04-21 07:30:08 +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):
"""
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
to an existing Tor's control port at the endpoint it specifies. I'll
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,
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].
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
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)
with timing.add("launch tor"):
tor = yield txtorcon.launch(reactor,
2018-04-21 07:30:08 +00:00
# data_directory=,
# tor_binary=,
)
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)
else:
# Let txtorcon look through a list of usual places. If that fails,
# we'll arrange to attempt the default SOCKS port
with timing.add("find tor"):
try:
tor = yield txtorcon.connect(reactor)
print(" using Tor via default control port", file=stderr)
except Exception:
# 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)
tor = SocksOnlyTor(reactor)
directlyProvides(tor, _interfaces.ITorManager)
returnValue(tor)