diff --git a/src/wormhole/scripts/cli_args.py b/src/wormhole/scripts/cli_args.py index 3a4d898..90169d4 100644 --- a/src/wormhole/scripts/cli_args.py +++ b/src/wormhole/scripts/cli_args.py @@ -31,6 +31,8 @@ g.add_argument("--twisted", action="store_true", help="use Twisted-based implementations, for testing") g.add_argument("--no-listen", action="store_true", help="(debug) don't open a listening socket for Transit") +g.add_argument("--tor", action="store_true", + help="use Tor when connecting") parser.set_defaults(timing=None) subparsers = parser.add_subparsers(title="subcommands", dest="subcommand") diff --git a/src/wormhole/scripts/cmd_receive_twisted.py b/src/wormhole/scripts/cmd_receive_twisted.py index 1dadf8a..e863be9 100644 --- a/src/wormhole/scripts/cmd_receive_twisted.py +++ b/src/wormhole/scripts/cmd_receive_twisted.py @@ -39,14 +39,25 @@ class TwistedReceiver(BlockingReceiver): # TODO: @handle_server_error @inlineCallbacks def go(self): - w = Wormhole(APPID, self.args.relay_url, timing=self.args.timing) + tor_manager = None + if self.args.tor: + from ..twisted.tor_manager import TorManager + tor_manager = TorManager(reactor) + # 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 + yield tor_manager.start() - rc = yield self._go(w) + w = Wormhole(APPID, self.args.relay_url, tor_manager, + timing=self.args.timing) + + rc = yield self._go(w, tor_manager) yield w.close() returnValue(rc) @inlineCallbacks - def _go(self, w): + def _go(self, w, tor_manager): self.handle_code(w) verifier = yield w.get_verifier() self.show_verifier(verifier) @@ -57,13 +68,13 @@ class TwistedReceiver(BlockingReceiver): returnValue(0) if "file" in them_d: f = self.handle_file(them_d) - rp = yield self.establish_transit(w, them_d) + rp = yield self.establish_transit(w, them_d, tor_manager) yield self.transfer_data(rp, f) self.write_file(f) yield self.close_transit(rp) elif "directory" in them_d: f = self.handle_directory(them_d) - rp = yield self.establish_transit(w, them_d) + rp = yield self.establish_transit(w, them_d, tor_manager) yield self.transfer_data(rp, f) self.write_directory(f) yield self.close_transit(rp) @@ -96,10 +107,11 @@ class TwistedReceiver(BlockingReceiver): yield w.send_data(data) @inlineCallbacks - def establish_transit(self, w, them_d): + def establish_transit(self, w, them_d, tor_manager): transit_key = w.derive_key(APPID+u"/transit-key") transit_receiver = TransitReceiver(self.args.transit_helper, no_listen=self.args.no_listen, + tor_manager=tor_manager, timing=self.args.timing) transit_receiver.set_transit_key(transit_key) direct_hints = yield transit_receiver.get_direct_hints() diff --git a/src/wormhole/scripts/cmd_send_twisted.py b/src/wormhole/scripts/cmd_send_twisted.py index f6d04ed..e2e8142 100644 --- a/src/wormhole/scripts/cmd_send_twisted.py +++ b/src/wormhole/scripts/cmd_send_twisted.py @@ -42,11 +42,22 @@ def send_twisted(args): print(u"On the other computer, please run: %s" % other_cmd, file=args.stdout) - w = Wormhole(APPID, args.relay_url, timing=args.timing) + tor_manager = None + if args.tor: + from ..twisted.tor_manager import TorManager + tor_manager = TorManager(reactor) + # 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 + yield tor_manager.start() + + w = Wormhole(APPID, args.relay_url, tor_manager, timing=args.timing) if fd_to_send: transit_sender = TransitSender(args.transit_helper, no_listen=args.no_listen, + tor_manager=tor_manager, timing=args.timing) phase1["transit"] = transit_data = {} transit_data["relay_connection_hints"] = transit_sender.get_relay_hints() diff --git a/src/wormhole/scripts/runner.py b/src/wormhole/scripts/runner.py index f89f01e..5cddf33 100644 --- a/src/wormhole/scripts/runner.py +++ b/src/wormhole/scripts/runner.py @@ -21,6 +21,8 @@ def dispatch(args): from ..servers import cmd_usage return cmd_usage.tail_usage(args) + if args.tor: + args.twisted = True if args.func == "send/send": if args.twisted: from . import cmd_send_twisted diff --git a/src/wormhole/twisted/transcribe.py b/src/wormhole/twisted/transcribe.py index 96d7179..bb31cd9 100644 --- a/src/wormhole/twisted/transcribe.py +++ b/src/wormhole/twisted/transcribe.py @@ -181,15 +181,23 @@ class Channel: return d class ChannelManager: - def __init__(self, relay, appid, side, handle_welcome, timing=None): + def __init__(self, relay, appid, side, handle_welcome, tor_manager=None, + timing=None): assert isinstance(relay, type(u"")) self._relay = relay self._appid = appid self._side = side self._handle_welcome = handle_welcome - self._timing = timing or DebugTiming() self._pool = web_client.HTTPConnectionPool(reactor, True) # persistent - self._agent = web_client.Agent(reactor, pool=self._pool) + if tor_manager: + print("ChannelManager using tor") + epf = tor_manager.get_web_agent_endpoint_factory() + agent = web_client.Agent.usingEndpointFactory(reactor, epf, + pool=self._pool) + else: + agent = web_client.Agent(reactor, pool=self._pool) + self._agent = agent + self._timing = timing or DebugTiming() @inlineCallbacks def allocate(self): @@ -250,13 +258,14 @@ class Wormhole: version_warning_displayed = False _send_confirm = True - def __init__(self, appid, relay_url, timing=None): + def __init__(self, appid, relay_url, tor_manager=None, timing=None): if not isinstance(appid, type(u"")): raise TypeError(type(appid)) if not isinstance(relay_url, type(u"")): raise TypeError(type(relay_url)) if not relay_url.endswith(u"/"): raise UsageError self._appid = appid self._relay_url = relay_url + self._tor_manager = tor_manager self._timing = timing or DebugTiming() self._set_side(hexlify(os.urandom(5)).decode("ascii")) self.code = None @@ -272,6 +281,7 @@ class Wormhole: self._side = side self._channel_manager = ChannelManager(self._relay_url, self._appid, self._side, self.handle_welcome, + self._tor_manager, self._timing) self._channel = None diff --git a/src/wormhole/twisted/transit.py b/src/wormhole/twisted/transit.py index 5b0e88d..cc8bed8 100644 --- a/src/wormhole/twisted/transit.py +++ b/src/wormhole/twisted/transit.py @@ -469,7 +469,7 @@ def there_can_be_only_one(contenders): class Common: RELAY_DELAY = 2.0 - def __init__(self, transit_relay, no_listen=False, + def __init__(self, transit_relay, no_listen=False, tor_manager=None, reactor=reactor, timing=None): if transit_relay: if not isinstance(transit_relay, type(u"")): @@ -477,6 +477,7 @@ class Common: self._transit_relays = [transit_relay] else: self._transit_relays = [] + self._tor_manager = tor_manager self._transit_key = None self._no_listen = no_listen self._waiting_for_transit_key = [] @@ -487,7 +488,7 @@ class Common: self._timing_started = self._timing.add_event("transit") def _build_listener(self): - if self._no_listen: + if self._no_listen or self._tor_manager: return ([], None) portnum = allocate_tcp_port() direct_hints = [u"tcp:%s:%d" % (addr, portnum) @@ -686,10 +687,17 @@ class Common: # TODO: use transit_common.parse_hint_tcp if ":" not in hint: return None + pieces = hint.split(":") hint_type = hint.split(":")[0] + if hint_type == "tor" and self._tor_manager: + return self._tor_manager.get_endpoint_for(pieces[1], int(pieces[2])) if hint_type != "tcp": return None pieces = hint.split(":") + if self._tor_manager: + # our TorManager will return None for non-public IPv4 addresses + # and any IPv6 address + return self._tor_manager.get_endpoint_for(pieces[1], int(pieces[2])) return endpoints.HostnameEndpoint(self._reactor, pieces[1], int(pieces[2]))