47007273ec
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.
128 lines
4.1 KiB
Python
128 lines
4.1 KiB
Python
import json
|
|
from twisted.internet.defer import inlineCallbacks, returnValue
|
|
|
|
from .wormhole import wormhole
|
|
from .tor_manager import TorManager
|
|
from .errors import NoTorError
|
|
|
|
@inlineCallbacks
|
|
def receive(reactor, appid, relay_url, code,
|
|
use_tor=False, launch_tor=False, tor_control_port=None,
|
|
on_code=None):
|
|
"""
|
|
This is a convenience API which returns a Deferred that callbacks
|
|
with a single chunk of data from another wormhole (and then closes
|
|
the wormhole). Under the hood, it's just using an instance
|
|
returned from :func:`wormhole.wormhole`. This is similar to the
|
|
`wormhole receive` command.
|
|
|
|
:param unicode appid: our application ID
|
|
|
|
:param unicode relay_url: the relay URL to use
|
|
|
|
:param unicode code: a pre-existing code to use, or None
|
|
|
|
:param bool use_tor: True if we should use Tor, False to not use it (None for default)
|
|
|
|
:param on_code: if not None, this is called when we have a code (even if you passed in one explicitly)
|
|
:type on_code: single-argument callable
|
|
"""
|
|
tm = None
|
|
if use_tor:
|
|
tm = TorManager(reactor, launch_tor, tor_control_port)
|
|
# For now, block everything until Tor has started. Soon: launch
|
|
# tor in parallel with everything else, make sure the TorManager
|
|
# can lazy-provide an endpoint, and overlap the startup process
|
|
# with the user handing off the wormhole code
|
|
if not tm.tor_available():
|
|
raise NoTorError()
|
|
yield tm.start()
|
|
|
|
wh = wormhole(appid, relay_url, reactor, tor_manager=tm)
|
|
if code is None:
|
|
code = yield wh.get_code()
|
|
else:
|
|
wh.set_code(code)
|
|
# we'll call this no matter what, even if you passed in a code --
|
|
# maybe it should be only in the 'if' block above?
|
|
if on_code:
|
|
on_code(code)
|
|
data = yield wh.get()
|
|
data = json.loads(data.decode("utf-8"))
|
|
offer = data.get('offer', None)
|
|
if not offer:
|
|
raise Exception(
|
|
"Do not understand response: {}".format(data)
|
|
)
|
|
msg = None
|
|
if 'message' in offer:
|
|
msg = offer['message']
|
|
wh.send(json.dumps({"answer": {"message_ack": "ok"}}).encode("utf-8"))
|
|
|
|
else:
|
|
raise Exception(
|
|
"Unknown offer type: {}".format(offer.keys())
|
|
)
|
|
|
|
yield wh.close()
|
|
returnValue(msg)
|
|
|
|
|
|
@inlineCallbacks
|
|
def send(reactor, appid, relay_url, data, code,
|
|
use_tor=False, launch_tor=False, tor_control_port=None,
|
|
on_code=None):
|
|
"""
|
|
This is a convenience API which returns a Deferred that callbacks
|
|
after a single chunk of data has been sent to another
|
|
wormhole. Under the hood, it's just using an instance returned
|
|
from :func:`wormhole.wormhole`. This is similar to the `wormhole
|
|
send` command.
|
|
|
|
:param unicode appid: the application ID
|
|
|
|
:param unicode relay_url: the relay URL to use
|
|
|
|
:param unicode code: a pre-existing code to use, or None
|
|
|
|
:param bool use_tor: True if we should use Tor, False to not use it (None for default)
|
|
|
|
:param on_code: if not None, this is called when we have a code (even if you passed in one explicitly)
|
|
:type on_code: single-argument callable
|
|
"""
|
|
tm = None
|
|
if use_tor:
|
|
tm = TorManager(reactor, launch_tor, tor_control_port)
|
|
# For now, block everything until Tor has started. Soon: launch
|
|
# tor in parallel with everything else, make sure the TorManager
|
|
# can lazy-provide an endpoint, and overlap the startup process
|
|
# with the user handing off the wormhole code
|
|
if not tm.tor_available():
|
|
raise NoTorError()
|
|
yield tm.start()
|
|
wh = wormhole(appid, relay_url, reactor, tor_manager=tm)
|
|
if code is None:
|
|
code = yield wh.get_code()
|
|
else:
|
|
wh.set_code(code)
|
|
if on_code:
|
|
on_code(code)
|
|
|
|
wh.send(
|
|
json.dumps({
|
|
"offer": {
|
|
"message": data
|
|
}
|
|
}).encode("utf-8")
|
|
)
|
|
data = yield wh.get()
|
|
data = json.loads(data.decode("utf-8"))
|
|
answer = data.get('answer', None)
|
|
yield wh.close()
|
|
if answer:
|
|
returnValue(None)
|
|
else:
|
|
raise Exception(
|
|
"Unknown answer: {}".format(data)
|
|
)
|