def test_launch_root_changes_tmp_ownership(self, chown, euid, _pwd, _sys): _pwd.return_value = 1000 _sys.platform = 'linux2' euid.return_value = 0 reactor = Mock() directlyProvides(reactor, IReactorCore) # note! we're providing enough options here that we react the # "chown" before any 'yield' statements in launch, so we don't # actually have to wait for it... a little rickety, though :/ launch(reactor, tor_binary='/bin/echo', user='******', socks_port='1234') self.assertEqual(1, chown.call_count)
def _test_tor_connection_default_control_port(self): """ Confirm a default control-port is set if not user-supplied. """ class Connector: def __call__(self, proto, trans): proto._set_valid_events('STATUS_CLIENT') proto.makeConnection(trans) proto.post_bootstrap.callback(proto) return proto.post_bootstrap def on_protocol(proto): proto.outReceived(b'Bootstrapped 90%\n') proto.outReceived(b'Bootstrapped 100%\n') trans = FakeProcessTransport() trans.protocol = self.protocol creator = functools.partial(Connector(), self.protocol, self.transport) tor = yield launch( FakeReactor(self, trans, on_protocol, [9052]), connection_creator=creator, tor_binary='/bin/echo', socks_port=1234, ) self.assertEqual(tor.config.ControlPort, 9052)
def test_launch_with_timeout(self): # XXX not entirely sure what this was/is supposed to be # testing, but it covers an extra 7 lines of code?? timeout = 5 def connector(proto, trans): proto._set_valid_events('STATUS_CLIENT') proto.makeConnection(trans) proto.post_bootstrap.callback(proto) return proto.post_bootstrap def on_protocol(proto): proto.outReceived(b'Bootstrapped 100%\n') trans = FakeProcessTransportNeverBootstraps() trans.protocol = self.protocol creator = functools.partial(connector, Mock(), Mock()) react = FakeReactor(self, trans, on_protocol, [1234, 9052]) with self.assertRaises(RuntimeError) as ctx: d = launch(react, connection_creator=creator, timeout=timeout, tor_binary='/bin/echo') # FakeReactor is a task.Clock subclass and +1 just to be sure react.advance(timeout + 1) yield d self.assertTrue('timeout while launching Tor' in str(ctx.exception)) # could/should just use return from this to do asserts? self.flushLoggedErrors(RuntimeError)
def test_launch_timeout_exception(self): """ we provide a timeout, and it expires """ trans = Mock() trans.signalProcess = Mock(side_effect=error.ProcessExitedAlready) trans.loseConnection = Mock() on_proto = Mock() react = FakeReactor(self, trans, on_proto, [1234]) def creator(): return defer.succeed(Mock()) d = launch( reactor=react, tor_binary='/bin/echo', socks_port=1234, timeout=10, connection_creator=creator, ) react.advance(12) self.assertTrue(trans.loseConnection.called) with self.assertRaises(RuntimeError) as ctx: yield d self.assertTrue("timeout while launching" in str(ctx.exception))
def test_successful_launch_tcp_control(self, ftb): """ full end-to-end test of a launch, faking things out at a "lower level" than most of the other tests """ trans = FakeProcessTransport() def on_protocol(proto): pass reactor = FakeReactor(self, trans, on_protocol, [1, 2, 3]) def connect_tcp(host, port, factory, timeout=0, bindAddress=None): addr = Mock() factory.doStart() proto = factory.buildProtocol(addr) tpp = proto._wrappedProtocol tpp.add_event_listener = self._fake_event_listener tpp.queue_command = self._fake_queue proto.makeConnection(Mock()) return proto reactor.connectTCP = connect_tcp config = TorConfig() tor = yield launch(reactor, _tor_config=config, control_port='1234', timeout=30) self.assertTrue(isinstance(tor, Tor))
def test_no_tor_binary(self, ftb): self.transport = proto_helpers.StringTransport() class Connector: def __call__(self, proto, trans): proto._set_valid_events('STATUS_CLIENT') proto.makeConnection(trans) proto.post_bootstrap.callback(proto) return proto.post_bootstrap self.protocol = FakeControlProtocol([]) trans = Mock() trans.protocol = self.protocol creator = functools.partial(Connector(), self.protocol, self.transport) reactor = Mock() directlyProvides(reactor, IReactorCore) try: yield launch( reactor, connection_creator=creator ) self.fail() except TorNotFound: pass # success!
def test_tor_produces_stderr_output(self): def connector(proto, trans): proto._set_valid_events('STATUS_CLIENT') proto.makeConnection(trans) proto.post_bootstrap.callback(proto) return proto.post_bootstrap def on_protocol(proto): proto.errReceived('Something went horribly wrong!\n') trans = FakeProcessTransport() trans.protocol = Mock() fakeout = StringIO() fakeerr = StringIO() creator = functools.partial(connector, Mock(), Mock()) try: yield launch( FakeReactor(self, trans, on_protocol, [1234, 9052]), connection_creator=creator, tor_binary='/bin/echo', stdout=fakeout, stderr=fakeerr, ) self.fail() # should't get callback except RuntimeError as e: self.assertEqual('', fakeout.getvalue()) self.assertEqual('Something went horribly wrong!\n', fakeerr.getvalue()) self.assertTrue('Something went horribly wrong!' in str(e))
def test_no_tor_binary(self, ftb): self.transport = proto_helpers.StringTransport() class Connector: def __call__(self, proto, trans): proto._set_valid_events('STATUS_CLIENT') proto.makeConnection(trans) proto.post_bootstrap.callback(proto) return proto.post_bootstrap self.protocol = FakeControlProtocol([]) trans = Mock() trans.protocol = self.protocol creator = functools.partial(Connector(), self.protocol, self.transport) reactor = Mock() directlyProvides(reactor, IReactorCore) try: yield launch( reactor, connection_creator=creator ) self.fail() except TorNotFound: pass # success!
def test_tor_connection_fails(self, ftb): trans = FakeProcessTransport() def on_protocol(proto): proto.outReceived(b'Bootstrapped 100%\n') reactor = FakeReactor(self, trans, on_protocol, [1, 2, 3]) fails = ['one'] def connect_tcp(host, port, factory, timeout=0, bindAddress=None): if len(fails): fails.pop() raise error.CannotListenError('on-purpose-error', None, None) addr = Mock() factory.doStart() proto = factory.buildProtocol(addr) tpp = proto._wrappedProtocol def fake_event_listener(what, cb): if what == 'STATUS_CLIENT': # should ignore non-BOOTSTRAP messages cb('STATUS_CLIENT not-bootstrap') cb('STATUS_CLIENT BOOTSTRAP PROGRESS=100 TAG=foo SUMMARY=bar' ) return defer.succeed(None) tpp.add_event_listener = fake_event_listener def fake_queue(cmd): if cmd.split()[0] == 'PROTOCOLINFO': return defer.succeed('AUTH METHODS=NULL') elif cmd == 'GETINFO config/names': return defer.succeed('config/names=') elif cmd == 'GETINFO signal/names': return defer.succeed('signal/names=') elif cmd == 'GETINFO version': return defer.succeed('version=0.1.2.3') elif cmd == 'GETINFO events/names': return defer.succeed('events/names=STATUS_CLIENT') elif cmd == 'GETINFO config/defaults': return defer.succeed('config/defaults=') return defer.succeed(None) tpp.queue_command = fake_queue proto.makeConnection(Mock()) return proto reactor.connectTCP = connect_tcp config = TorConfig() tor = yield launch(reactor, _tor_config=config, control_port='1234', timeout=30) errs = self.flushLoggedErrors() self.assertTrue(isinstance(tor, Tor)) self.assertEqual(1, len(errs))
def test_launch_wrong_stdout(self): try: yield launch( FakeReactor(self, Mock(), Mock()), stdout=object(), tor_binary='/bin/echo', ) self.fail("Should have thrown an error") except RuntimeError as e: self.assertTrue("file-like object needed" in str(e).lower())
def test_launch_tor_unix_controlport_no_directory(self): reactor = FakeReactor(self, Mock(), None, [9050]) with self.assertRaises(ValueError) as ctx: socket_file = '/does/not/exist' yield launch( reactor, control_port="unix:{}".format(socket_file), tor_binary="/bin/echo", stdout=Mock(), stderr=Mock(), ) self.assertTrue("must exist" in str(ctx.exception))
def test_launch_tor_unix_controlport_wrong_perms(self): reactor = FakeReactor(self, Mock(), None, [9050]) with self.assertRaises(ValueError) as ctx: with TempDir() as tmp: tmpdir = str(tmp) os.chmod(tmpdir, 0o0777) socket_file = join(tmpdir, 'socket_test') yield launch( reactor, control_port="unix:{}".format(socket_file), tor_binary="/bin/echo", stdout=Mock(), stderr=Mock(), ) self.assertTrue( "must only be readable by the user" in str(ctx.exception))
def main(reactor): # note that you can pass a few options as kwargs # (e.g. data_directory=, or socks_port= ). For other torrc # changes, see below. tor = yield txtorcon.launch( reactor, data_directory="./tordata", stdout=sys.stdout, socks_port='unix:/tmp/tor2/socks', ) # tor = yield txtorcon.connect( # reactor, # clientFromString(reactor, "unix:/var/run/tor/control"), # ) print("Connected to Tor version '{}'".format(tor.protocol.version)) config = yield tor.get_config() state = yield tor.create_state() # or state = yield txtorcon.TorState.from_protocol(tor.protocol) print("This Tor has PID {}".format(state.tor_pid)) print("This Tor has the following {} Circuits:".format(len( state.circuits))) for c in state.circuits.values(): print(" {}".format(c)) endpoint_d = config.socks_endpoint(reactor, u'unix:/tmp/tor2/socks') agent = tor.web_agent(socks_endpoint=endpoint_d) uri = b'https://www.torproject.org' print("Downloading {}".format(uri)) resp = yield agent.request(b'GET', uri) print("Response has {} bytes".format(resp.length)) body = yield readBody(resp) print("received body ({} bytes)".format(len(body))) print("{}\n[...]\n{}\n".format(body[:200], body[-200:])) # SOCKSPort is 'really' a list of SOCKS ports in Tor now, so we # have to set it to a list ... :/ print("Changing our config (SOCKSPort=9876)") # config.SOCKSPort = ['unix:/tmp/foo/bar'] config.SOCKSPort = ['9876'] yield config.save() print("Querying to see it changed:") socksport = yield tor.protocol.get_conf("SOCKSPort") print("SOCKSPort", socksport)
def test_launch_timeout_process_exits(self): # cover the "one more edge case" where we get a processEnded() # but we've already "done" a timeout. trans = Mock() trans.signalProcess = Mock() trans.loseConnection = Mock() class MyFakeReactor(FakeReactor): def spawnProcess(self, processprotocol, bin, args, env, path, uid=None, gid=None, usePTY=None, childFDs=None): self.protocol = processprotocol self.protocol.makeConnection(self.transport) self.transport.process_protocol = processprotocol self.on_protocol(self.protocol) status = Mock() status.value.exitCode = None processprotocol.processEnded(status) return self.transport react = MyFakeReactor(self, trans, Mock(), [1234, 9052]) d = launch( reactor=react, tor_binary='/bin/echo', timeout=10, data_directory='/dev/null', ) react.advance(20) try: yield d except RuntimeError as e: self.assertTrue("Tor was killed" in str(e)) errs = self.flushLoggedErrors(RuntimeError) self.assertEqual(1, len(errs)) self.assertTrue("Tor was killed" in str(errs[0]))
def main(reactor): # note that you can pass a few options as kwargs # (e.g. data_directory=, or socks_port= ). For other torrc # changes, see below. tor = yield txtorcon.launch( reactor, data_directory="./tordata", stdout=sys.stdout, socks_port='unix:/tmp/tor2/socks', ) # tor = yield txtorcon.connect( # reactor, # clientFromString(reactor, "unix:/var/run/tor/control"), # ) print("Connected to Tor version '{}'".format(tor.protocol.version)) config = yield tor.get_config() state = yield tor.create_state() # or state = yield txtorcon.TorState.from_protocol(tor.protocol) print("This Tor has PID {}".format(state.tor_pid)) print("This Tor has the following {} Circuits:".format(len(state.circuits))) for c in state.circuits.values(): print(" {}".format(c)) endpoint_d = config.socks_endpoint(reactor, u'unix:/tmp/tor2/socks') agent = tor.web_agent(socks_endpoint=endpoint_d) uri = b'https://www.torproject.org' print("Downloading {}".format(uri)) resp = yield agent.request(b'GET', uri) print("Response has {} bytes".format(resp.length)) body = yield readBody(resp) print("received body ({} bytes)".format(len(body))) print("{}\n[...]\n{}\n".format(body[:200], body[-200:])) # SOCKSPort is 'really' a list of SOCKS ports in Tor now, so we # have to set it to a list ... :/ print("Changing our config (SOCKSPort=9876)") # config.SOCKSPort = ['unix:/tmp/foo/bar'] config.SOCKSPort = ['9876'] yield config.save() print("Querying to see it changed:") socksport = yield tor.protocol.get_conf("SOCKSPort") print("SOCKSPort", socksport)
def test_launch_fails(self, ftb): trans = FakeProcessTransport() def on_proto(protocol): protocol.processEnded( Failure(error.ProcessTerminated(12, None, 'statusFIXME'))) reactor = FakeReactor(self, trans, on_proto, [1234, 9052]) try: yield launch(reactor) self.fail("Should fail") except RuntimeError: pass errs = self.flushLoggedErrors(RuntimeError) self.assertEqual(1, len(errs)) self.assertTrue("Tor exited with error-code 12" in str(errs[0]))
def main(reactor): # we must have a directory owned by us with 0700 permissions to # contain our Unix sockets or Tor is sad. tmp = tempfile.mkdtemp() reactor.addSystemEventTrigger( 'after', 'shutdown', shutil.rmtree, tmp, ) control_path = join(tmp, 'control_socket') socks_path = join(tmp, 'socks') # note that you can pass a few options as kwargs # (e.g. data_directory=, or socks_port= ). For other torrc # changes, see below. tor = yield txtorcon.launch( reactor, data_directory="./tordata", stdout=sys.stdout, control_port='unix:{}'.format(control_path), socks_port='unix:{}'.format(socks_path), ) print("Connected to Tor version '{}'".format(tor.protocol.version)) state = yield tor.create_state() print("This Tor has PID {}".format(state.tor_pid)) print("This Tor has the following {} Circuits:".format(len( state.circuits))) for c in state.circuits.values(): print(" {}".format(c)) config = yield tor.get_config() socks_ep = config.create_socks_endpoint(reactor, u'unix:{}'.format(socks_path)) agent = tor.web_agent(socks_endpoint=socks_ep) uri = b'https://www.torproject.org' print("Downloading {} via Unix socket".format(uri)) resp = yield agent.request(b'GET', uri) print("Response has {} bytes".format(resp.length)) body = yield readBody(resp) print("received body ({} bytes)".format(len(body))) print("{}\n[...]\n{}\n".format(body[:200], body[-200:]))
def test_tor_connection_user_data_dir(self): """ Test that we don't delete a user-supplied data directory. """ config = TorConfig() config.OrPort = 1234 class Connector: def __call__(self, proto, trans): proto._set_valid_events('STATUS_CLIENT') proto.makeConnection(trans) proto.post_bootstrap.callback(proto) return proto.post_bootstrap def on_protocol(proto): proto.outReceived(b'Bootstrapped 90%\n') with TempDir() as tmp: my_dir = str(tmp) config.DataDirectory = my_dir trans = FakeProcessTransport() trans.protocol = self.protocol creator = functools.partial(Connector(), self.protocol, self.transport) d = launch( FakeReactor(self, trans, on_protocol, [1234, 9051]), connection_creator=creator, tor_binary='/bin/echo', data_directory=my_dir, control_port=0, ) def still_have_data_dir(tor, tester): tor._process_protocol.cleanup( ) # FIXME? not really unit-testy as this is sort of internal function tester.assertTrue(os.path.exists(my_dir)) d.addCallback(still_have_data_dir, self) d.addErrback(self.fail) return d
def main(reactor): def update(percent, tag, summary): print("{}%: {}".format(int(percent), summary)) tor = yield txtorcon.launch( reactor, progress_updates=update, data_directory='./tordata', ) print("Tor started: {}".format(tor)) # make a request via Tor resp = yield treq.get( 'https://www.torproject.org:443', agent=tor.web_agent(), ) print("Retrieving {} bytes".format(resp.length)) data = yield resp.text() print("Got {} bytes:\n{}\n[...]{}".format( len(data), data[:120], data[-120:], )) # create a new circuit print("creating circuit") state = yield tor.create_state() circ = yield state.build_circuit() yield circ.when_built() print(" path: {}".format(" -> ".join([r.ip for r in circ.path]))) # make a request via our new circuit print("Downloading meejah's public key...") resp = yield treq.get( 'https://meejah.ca/meejah.asc', agent=circ.web_agent(reactor, tor.config.socks_endpoint(reactor)), ) data = yield resp.text() print(data)
def main(reactor): # we must have a directory owned by us with 0700 permissions to # contain our Unix sockets or Tor is sad. tmp = tempfile.mkdtemp() reactor.addSystemEventTrigger( 'after', 'shutdown', shutil.rmtree, tmp, ) control_path = join(tmp, 'control_socket') socks_path = join(tmp, 'socks') # note that you can pass a few options as kwargs # (e.g. data_directory=, or socks_port= ). For other torrc # changes, see below. tor = yield txtorcon.launch( reactor, data_directory="./tordata", stdout=sys.stdout, control_port='unix:{}'.format(control_path), socks_port='unix:{}'.format(socks_path), ) print("Connected to Tor version '{}'".format(tor.protocol.version)) state = yield tor.create_state() print("This Tor has PID {}".format(state.tor_pid)) print("This Tor has the following {} Circuits:".format(len(state.circuits))) for c in state.circuits.values(): print(" {}".format(c)) config = yield tor.get_config() socks_ep = config.create_socks_endpoint(reactor, u'unix:{}'.format(socks_path)) agent = tor.web_agent(socks_endpoint=socks_ep) uri = b'https://www.torproject.org' print("Downloading {} via Unix socket".format(uri)) resp = yield agent.request(b'GET', uri) print("Response has {} bytes".format(resp.length)) body = yield readBody(resp) print("received body ({} bytes)".format(len(body))) print("{}\n[...]\n{}\n".format(body[:200], body[-200:]))
def test_launch_tor_unix_controlport(self): trans = FakeProcessTransport() trans.protocol = self.protocol self.protocol.post_bootstrap.callback(self.protocol) self.protocol._set_valid_events("STATUS_CLIENT") self.protocol.add_event_listener = self._fake_event_listener self.protocol.queue_command = self._fake_queue def on_protocol(proto): proto.outReceived(b'Bootstrapped 90%\n') # launch() auto-discovers a SOCKS port reactor = FakeReactor(self, trans, on_protocol, [9050]) reactor.connectUNIX = Mock() # prepare a suitable directory for tor unix socket with TempDir() as tmp: tmpdir = str(tmp) os.chmod(tmpdir, 0o0700) socket_file = join(tmpdir, 'test_socket_file') with patch('txtorcon.controller.UNIXClientEndpoint') as uce: endpoint = Mock() endpoint.connect = Mock( return_value=defer.succeed(self.protocol)) uce.return_value = endpoint yield launch( reactor, control_port="unix:{}".format(socket_file), tor_binary="/bin/echo", stdout=Mock(), stderr=Mock(), ) self.assertTrue(endpoint.connect.called) self.assertTrue(uce.called) self.assertEqual( socket_file, uce.mock_calls[0][1][1], )
def test_successful_launch(self, tpp, ftb): trans = FakeProcessTransport() reactor = FakeReactor(self, trans, lambda p: None, [1, 2, 3]) config = TorConfig() def boot(arg=None): config.post_bootstrap.callback(config) config.__dict__['bootstrap'] = Mock(side_effect=boot) config.__dict__['attach_protocol'] = Mock( return_value=defer.succeed(None)) def foo(*args, **kw): rtn = Mock() rtn.post_bootstrap = defer.succeed(None) rtn.when_connected = Mock(return_value=defer.succeed(rtn)) return rtn tpp.side_effect = foo tor = yield launch(reactor, _tor_config=config) self.assertTrue(isinstance(tor, Tor))
def _test_tor_connection_user_control_port(self): """ Confirm we use a user-supplied control-port properly """ config = TorConfig() config.OrPort = 1234 config.ControlPort = 4321 class Connector: def __call__(self, proto, trans): proto._set_valid_events('STATUS_CLIENT') proto.makeConnection(trans) proto.post_bootstrap.callback(proto) return proto.post_bootstrap def on_protocol(proto): proto.outReceived(b'Bootstrapped 90%\n') proto.outReceived(b'Bootstrapped 100%\n') trans = FakeProcessTransport() trans.protocol = self.protocol creator = functools.partial(Connector(), self.protocol, self.transport) d = launch( FakeReactor(self, trans, on_protocol, [9052]), connection_creator=creator, tor_binary='/bin/echo', socks_port=1234, ) def check_control_port(proto, tester): # we just want to ensure launch() didn't mess with # the controlport we set tester.assertEquals(config.ControlPort, 4321) d.addCallback(check_control_port, self) d.addErrback(self.fail) return d
def test_launch_no_control_port(self): ''' See Issue #80. This allows you to launch tor with a TorConfig with ControlPort=0 in case you don't want a control connection at all. In this case you get back a TorProcessProtocol and you own both pieces. (i.e. you have to kill it yourself). ''' trans = FakeProcessTransportNoProtocol() trans.protocol = self.protocol def creator(*args, **kw): print("Bad: connection creator called") self.fail() def on_protocol(proto): self.process_proto = proto proto.outReceived(b'Bootstrapped 90%\n') proto.outReceived(b'Bootstrapped 100%\n') reactor = FakeReactor(self, trans, on_protocol, [9052, 9999]) tor = yield launch( reactor=reactor, connection_creator=creator, tor_binary='/bin/echo', socks_port=1234, control_port=0, ) self.assertEqual(tor._process_protocol, self.process_proto) d = tor.quit() reactor.advance(0) yield d errs = self.flushLoggedErrors() self.assertEqual(1, len(errs)) self.assertTrue("Tor was killed" in str(errs[0]))
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() 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: print(" launching a new Tor process, this may take a while..", file=stderr) with timing.add("launch tor"): tor = yield txtorcon.launch(reactor, #data_directory=, #tor_binary=, ) elif tor_control_port: with timing.add("find tor"): control_ep = clientFromString(reactor, tor_control_port) 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 print(" unable to find default Tor control port, using SOCKS", file=stderr) tor = SocksOnlyTor(reactor) directlyProvides(tor, _interfaces.ITorManager) returnValue(tor)
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() 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: print(" launching a new Tor process, this may take a while..", file=stderr) with timing.add("launch tor"): tor = yield txtorcon.launch(reactor, #data_directory=, #tor_binary=, ) elif tor_control_port: with timing.add("find tor"): control_ep = clientFromString(reactor, tor_control_port) 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 print(" unable to find default Tor control port, using SOCKS", file=stderr) tor = SocksOnlyTor(reactor) directlyProvides(tor, _interfaces.ITorManager) returnValue(tor)
def test_launch_no_ireactorcore(self): try: yield launch(None) self.fail("should get exception") except ValueError as e: self.assertTrue("provide IReactorCore" in str(e))