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('Bootstrapped 90%\n') proto.outReceived('Bootstrapped 100%\n') trans = FakeProcessTransport() trans.protocol = self.protocol self.othertrans = trans creator = functools.partial(Connector(), self.protocol, self.transport) d = launch_tor(config, FakeReactor(self, trans, on_protocol), connection_creator=creator) def check_control_port(proto, tester): ## we just want to ensure launch_tor() 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_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('Bootstrapped 90%\n') proto.outReceived('Bootstrapped 100%\n') my_dir = tempfile.mkdtemp(prefix='tortmp') config.DataDirectory = my_dir trans = FakeProcessTransport() trans.protocol = self.protocol self.othertrans = trans creator = functools.partial(Connector(), self.protocol, self.transport) d = launch_tor(config, FakeReactor(self, trans, on_protocol), connection_creator=creator) def still_have_data_dir(proto, tester): proto.cleanup() # FIXME? not really unit-testy as this is sort of internal function tester.assertTrue(os.path.exists(my_dir)) delete_file_or_tree(my_dir) d.addCallback(still_have_data_dir, self) d.addErrback(self.fail) return d
def test_parse_relative_path(self): # this makes sure we convert a relative path to absolute # hiddenServiceDir args. see Issue #77 # make sure we have a valid thing from get_global_tor without # actually launching tor config = TorConfig() config.post_bootstrap = defer.succeed(config) from txtorcon import torconfig torconfig._global_tor_config = None get_global_tor( self.reactor, _tor_launcher=lambda react, config, prog: defer.succeed(config) ) orig = os.path.realpath('.') try: with util.TempDir() as t: t = str(t) os.chdir(t) os.mkdir(os.path.join(t, 'foo')) hsdir = os.path.join(t, 'foo', 'blam') os.mkdir(hsdir) ep = serverFromString( self.reactor, 'onion:88:localPort=1234:hiddenServiceDir=foo/blam' ) self.assertEqual( os.path.realpath(hsdir), ep.hidden_service_dir ) finally: os.chdir(orig)
def test_parse_user_path(self): # this makes sure we expand users and symlinks in # hiddenServiceDir args. see Issue #77 # make sure we have a valid thing from get_global_tor without # actually launching tor config = TorConfig() config.post_bootstrap = defer.succeed(config) from txtorcon import torconfig torconfig._global_tor_config = None get_global_tor( self.reactor, _tor_launcher=lambda react, config, prog: defer.succeed(config) ) ep = serverFromString( self.reactor, 'onion:88:localPort=1234:hiddenServiceDir=~/blam/blarg' ) # would be nice to have a fixed path here, but then would have # to run as a known user :/ # maybe using the docker stuff to run integration tests better here? self.assertEqual( os.path.expanduser('~/blam/blarg'), ep.hidden_service_dir )
def test_options_hidden(self): self.protocol.answers.append( 'HiddenServiceDir=/fake/path\nHiddenServicePort=80 ' '127.0.0.1:1234\nHiddenServiceDirGroupReadable=1\n' ) conf = TorConfig(self.protocol) yield conf.post_bootstrap self.assertTrue(conf.post_bootstrap.called) self.assertTrue('HiddenServiceOptions' not in conf.config) self.assertTrue('HiddenServices' in conf.config) self.assertEqual(len(conf.HiddenServices), 1) self.assertTrue(not conf.needs_save()) conf.hiddenservices.append( HiddenService(conf, '/some/dir', '80 127.0.0.1:2345', 'auth', 2, True) ) conf.hiddenservices[0].ports.append('443 127.0.0.1:443') self.assertTrue(conf.needs_save()) conf.save() self.assertEqual(len(self.protocol.sets), 9) self.assertEqual(self.protocol.sets[0], ('HiddenServiceDir', '/fake/path')) self.assertEqual(self.protocol.sets[1], ('HiddenServiceDirGroupReadable', '1')) self.assertEqual(self.protocol.sets[2], ('HiddenServicePort', '80 127.0.0.1:1234')) self.assertEqual(self.protocol.sets[3], ('HiddenServicePort', '443 127.0.0.1:443')) self.assertEqual(self.protocol.sets[4], ('HiddenServiceDir', '/some/dir')) self.assertEqual(self.protocol.sets[5], ('HiddenServiceDirGroupReadable', '1')) self.assertEqual(self.protocol.sets[6], ('HiddenServicePort', '80 127.0.0.1:2345')) self.assertEqual(self.protocol.sets[7], ('HiddenServiceVersion', '2')) self.assertEqual(self.protocol.sets[8], ('HiddenServiceAuthorizeClient', 'auth'))
def test_add_hidden_service_to_empty_config(self): conf = TorConfig() h = HiddenService(conf, '/fake/path', ['80 127.0.0.1:1234'], '', 3) conf.hiddenservices.append(h) self.assertEqual(len(conf.hiddenservices), 1) self.assertEqual(h, conf.hiddenservices[0]) self.assertTrue(conf.needs_save())
def test_tor_connection_fails(self): """ We fail to connect once, and then successfully connect -- testing whether we're retrying properly on each Bootstrapped line from stdout. """ config = TorConfig() config.OrPort = 1234 config.SocksPort = 9999 class Connector: count = 0 def __call__(self, proto, trans): self.count += 1 if self.count < 2: return defer.fail(error.CannotListenError(None, None, None)) proto._set_valid_events('STATUS_CLIENT') proto.makeConnection(trans) proto.post_bootstrap.callback(proto) return proto.post_bootstrap def on_protocol(proto): proto.outReceived('Bootstrapped 90%\n') proto.outReceived('Bootstrapped 100%\n') trans = FakeProcessTransport() trans.protocol = self.protocol self.othertrans = trans creator = functools.partial(Connector(), self.protocol, self.transport) d = launch_tor(config, FakeReactor(self, trans, on_protocol), connection_creator=creator, tor_binary='/bin/echo') d.addCallback(self.setup_complete_fails) return self.assertFailure(d, Exception)
def test_tor_produces_stderr_output(self): config = TorConfig() config.OrPort = 1234 config.SocksPort = 9999 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 = self.protocol self.othertrans = trans fakeout = StringIO() fakeerr = StringIO() creator = functools.partial(connector, self.protocol, self.transport) d = launch_tor(config, FakeReactor(self, trans, on_protocol), connection_creator=creator, tor_binary='/bin/echo', stdout=fakeout, stderr=fakeerr) d.addCallback(self.fail) # should't get callback d.addErrback(self.setup_fails_stderr, fakeout, fakeerr) self.assertFalse(self.protocol.on_disconnect) return d
def test_basic_launch(self): config = TorConfig() config.OrPort = 1234 config.SocksPort = 9999 def connector(proto, trans): proto._set_valid_events('STATUS_CLIENT') proto.makeConnection(trans) proto.post_bootstrap.callback(proto) return proto.post_bootstrap class OnProgress: def __init__(self, test, expected): self.test = test self.expected = expected def __call__(self, percent, tag, summary): self.test.assertEqual(self.expected[0], (percent, tag, summary)) self.expected = self.expected[1:] self.test.assertTrue('"' not in summary) self.test.assertTrue(percent >= 0 and percent <= 100) def on_protocol(proto): proto.outReceived('Bootstrapped 100%\n') proto.progress = OnProgress(self, [(90, 'circuit_create', 'Establishing a Tor circuit'), (100, 'done', 'Done')]) trans = FakeProcessTransport() trans.protocol = self.protocol self.othertrans = trans creator = functools.partial(connector, self.protocol, self.transport) d = launch_tor(config, FakeReactor(self, trans, on_protocol), connection_creator=creator) d.addCallback(self.setup_complete_no_errors, config) return d
def test_hidden_service_parse_error(self): conf = TorConfig(FakeControlProtocol(['config/names='])) try: conf._setup_hidden_services('''FakeHiddenServiceKey=foo''') self.fail() except RuntimeError, e: self.assertTrue('parse' in str(e))
def _do_launch_tor(self): tconfig = TorConfig() #tconfig.ControlPort = allocate_tcp_port() # defaults to 9052 tconfig.SocksPort = allocate_tcp_port() socks_desc = "tcp:127.0.0.1:%d" % tconfig.SocksPort # this could take tor_binary= tproto = yield launch_tor(tconfig, self._reactor) returnValue((tproto, tconfig, socks_desc))
def test_set_wrong_object(self): conf = TorConfig(self.protocol) try: conf.log = ('this', 'is', 'a', 'tuple') self.fail() except ValueError, e: self.assertTrue('Not valid' in str(e))
def test_log_set_slice(self): conf = TorConfig(self.protocol) conf.log[0:1] = ['info file /tmp/foo.log'] self.assertTrue(conf.needs_save()) conf.save() self.assertEqual(self.protocol.sets[0], ('Log', 'info file /tmp/foo.log'))
def test_log_set_index(self): conf = TorConfig(self.protocol) conf.log[0] = 'info file /tmp/foo.log' self.assertTrue(conf.needs_save()) conf.save() self.assertTrue(self.protocol.sets[0] == ('Log', 'info file /tmp/foo.log'))
def test_iterate_torconfig(self): cfg = TorConfig() cfg.FooBar = 'quux' cfg.save() cfg.Quux = 'blimblam' keys = sorted([k for k in cfg]) self.assertEqual(['FooBar', 'Quux'], keys)
def test_boolean_parse_error(self): self.protocol.answers.append('config/names=\nfoo Boolean') self.protocol.answers.append({'foo': 'bar'}) cfg = TorConfig(self.protocol) self.assertEqual(cfg.get_type('foo'), torconfig.Boolean) errs = self.flushLoggedErrors(ValueError) self.assertEqual(len(errs), 1) ## dunno if asserting strings in messages is a good idea... self.assertTrue('invalid literal' in errs[0].getErrorMessage())
def test_log_set_capital(self): conf = TorConfig(self.protocol) conf.Log.append('info file /tmp/foo.log') self.assertTrue(conf.needs_save()) conf.save() self.assertEqual(self.protocol.sets[0], ('Log', 'notice file /var/log/tor/notices.log')) self.assertEqual(self.protocol.sets[1], ('Log', 'info file /tmp/foo.log'))
def test_multiple_orports(self): self.protocol.post_bootstrap = None self.protocol.answers.append('config/names=\nOrPort CommaList') self.protocol.answers.append({'OrPort': '1234'}) conf = TorConfig(self.protocol) conf.OrPort = ['1234', '4321'] conf.save() self.assertEqual(self.protocol.sets, [('OrPort', '1234'), ('OrPort', '4321')])
def test_log_set_remove(self): conf = TorConfig(self.protocol) self.assertEqual(len(conf.log), 1) conf.log.remove('notice file /var/log/tor/notices.log') self.assertTrue(conf.needs_save()) conf.save() self.assertEqual(len(conf.log), 0) self.assertEqual(len(self.protocol.sets), 0)
def test_multiple_append(self): conf = TorConfig() h0 = HiddenService(conf, '/fake/path', ['80 127.0.0.1:1234'], '', 3) h1 = HiddenService(conf, '/fake/path', ['90 127.0.0.1:4321'], '', 3) conf.hiddenservices.append(h0) conf.hiddenservices.append(h1) self.assertEqual(len(conf.hiddenservices), 2) self.assertEqual(h0, conf.hiddenservices[0]) self.assertEqual(h1, conf.hiddenservices[1]) self.assertTrue(conf.needs_save())
def test_get_type(self): self.protocol.answers.append( 'config/names=\nSomethingExciting CommaList\nHiddenServices Dependant' ) self.protocol.answers.append({'SomethingExciting': 'a,b'}) conf = TorConfig(self.protocol) from txtorcon.torconfig import HiddenService self.assertEqual(conf.get_type('SomethingExciting'), CommaList) self.assertEqual(conf.get_type('HiddenServices'), HiddenService)
def test_log_set_pop(self): conf = TorConfig(self.protocol) self.assertEqual(len(conf.log), 1) conf.log.pop() self.assertTrue(conf.needs_save()) conf.save() self.assertEqual(len(conf.log), 0) self.assertEqual(len(self.protocol.sets), 0)
def get_tor_config(): tor_config = TorConfig() if config.tor.control_port is None: config.tor.control_port = int(randomFreePort()) if config.tor.socks_port is None: config.tor.socks_port = int(randomFreePort()) tor_config.ControlPort = config.tor.control_port tor_config.SocksPort = config.tor.socks_port if config.tor.data_dir: data_dir = os.path.expanduser(config.tor.data_dir) # We only use the Tor data dir specified in the config file if # 1. It is not locked (i.e. another process is using it) # 2. We have write permissions to it data_dir_usable = is_tor_data_dir_usable(data_dir) try: mkdir_p(data_dir) except OSError as ose: if ose.errno == errno.EACCESS: data_dir_usable = False else: raise if data_dir_usable: tor_config.DataDirectory = data_dir if config.tor.bridges: tor_config.UseBridges = 1 if config.advanced.obfsproxy_binary: tor_config.ClientTransportPlugin = ( 'obfs2,obfs3 exec %s managed' % config.advanced.obfsproxy_binary ) bridges = [] with open(config.tor.bridges) as f: for bridge in f: if 'obfs' in bridge: if config.advanced.obfsproxy_binary: bridges.append(bridge.strip()) else: bridges.append(bridge.strip()) tor_config.Bridge = bridges if config.tor.torrc: for i in config.tor.torrc.keys(): setattr(tor_config, i, config.tor.torrc[i]) if os.geteuid() == 0: tor_config.User = pwd.getpwuid(os.geteuid()).pw_name tor_config.save() log.debug("Setting control port as %s" % tor_config.ControlPort) log.debug("Setting SOCKS port as %s" % tor_config.SocksPort) return tor_config
def startTor(self): """ Starts Tor Launches a Tor with :param: socks_port :param: control_port :param: tor_binary set in ooniprobe.conf """ log.msg("Starting Tor...") from txtorcon import TorConfig tor_config = TorConfig() if config.tor.control_port is None: config.tor.control_port = int(randomFreePort()) if config.tor.socks_port is None: config.tor.socks_port = int(randomFreePort()) tor_config.ControlPort = config.tor.control_port tor_config.SocksPort = config.tor.socks_port if config.tor.data_dir: data_dir = os.path.expanduser(config.tor.data_dir) if not os.path.exists(data_dir): log.msg("%s does not exist. Creating it." % data_dir) os.makedirs(data_dir) tor_config.DataDirectory = data_dir if config.tor.bridges: tor_config.UseBridges = 1 if config.advanced.obfsproxy_binary: tor_config.ClientTransportPlugin = ( 'obfs2,obfs3 exec %s managed' % config.advanced.obfsproxy_binary ) bridges = [] with open(config.tor.bridges) as f: for bridge in f: if 'obfs' in bridge: if config.advanced.obfsproxy_binary: bridges.append(bridge.strip()) else: bridges.append(bridge.strip()) tor_config.Bridge = bridges if config.tor.torrc: for i in config.tor.torrc.keys(): setattr(tor_config, i, config.tor.torrc[i]) if os.geteuid() == 0: tor_config.User = pwd.getpwuid(os.geteuid()).pw_name tor_config.save() log.debug("Setting control port as %s" % tor_config.ControlPort) log.debug("Setting SOCKS port as %s" % tor_config.SocksPort) return start_tor(tor_config)
class CreateSocksEndpointTests(unittest.TestCase): def setUp(self): self.reactor = Mock() self.config = TorConfig() self.config.SocksPort = [] self.config.bootstrap = defer.succeed(self.config) @defer.inlineCallbacks def test_create_default_no_ports(self): with self.assertRaises(Exception) as ctx: yield self.config.create_socks_endpoint(self.reactor, None) self.assertTrue('no SocksPorts configured' in str(ctx.exception)) @defer.inlineCallbacks def test_create_default(self): self.config.SocksPort = ['9150'] ep = yield self.config.create_socks_endpoint(self.reactor, None) factory = Mock() ep.connect(factory) self.assertEqual(1, len(self.reactor.mock_calls)) call = self.reactor.mock_calls[0] self.assertEqual('connectTCP', call[0]) self.assertEqual('127.0.0.1', call[1][0]) self.assertEqual(9150, call[1][1]) @defer.inlineCallbacks def test_create_tcp(self): ep = yield self.config.create_socks_endpoint( self.reactor, "9050", ) factory = Mock() ep.connect(factory) self.assertEqual(1, len(self.reactor.mock_calls)) call = self.reactor.mock_calls[0] self.assertEqual('connectTCP', call[0]) self.assertEqual('127.0.0.1', call[1][0]) self.assertEqual(9050, call[1][1]) @defer.inlineCallbacks def test_create_error_on_save(self): self.config.SocksPort = [] def boom(*args, **kw): raise TorProtocolError(551, "Something bad happened") with patch.object(TorConfig, 'save', boom): with self.assertRaises(Exception) as ctx: yield self.config.create_socks_endpoint(self.reactor, 'unix:/foo') err = str(ctx.exception) self.assertTrue('error from Tor' in err) self.assertTrue('specific ownership/permissions requirements' in err)
def test_proper_sets(self): self.protocol.answers.append('config/names=\nLog LineList') self.protocol.answers.append({'Log': 'foo'}) conf = TorConfig(self.protocol) conf.log.append('bar') conf.save() self.assertEqual(len(self.protocol.sets), 2) self.assertEqual(self.protocol.sets[0], ('Log', 'foo')) self.assertEqual(self.protocol.sets[1], ('Log', 'bar'))
def test_parse_via_plugin(self): # make sure we have a valid thing from get_global_tor without actually launching tor config = TorConfig() config.post_bootstrap = defer.succeed(config) from txtorcon import torconfig torconfig._global_tor_config = None get_global_tor(self.reactor, _tor_launcher=lambda react, config, prog: defer.succeed(config)) ep = serverFromString(self.reactor, 'onion:88:localPort=1234:hiddenServiceDir=/foo/bar') self.assertEqual(ep.public_port, 88) self.assertEqual(ep.local_port, 1234) self.assertEqual(ep.hidden_service_dir, '/foo/bar')
def test_save_boolean_with_strange_values(self): self.protocol.answers.append('config/names=\nfoo Boolean\nbar Boolean') self.protocol.answers.append({'foo': '0'}) self.protocol.answers.append({'bar': '1'}) conf = TorConfig(self.protocol) # save some non-boolean value conf.foo = "Something True" conf.bar = 0 conf.save() self.assertEqual(set(self.protocol.sets), set([('foo', 1), ('bar', 0)]))
def test_log_set_insert(self): conf = TorConfig(self.protocol) self.assertEqual(len(conf.log), 1) conf.log.insert(0, 'info file /tmp/foo') self.assertTrue(conf.needs_save()) conf.save() self.assertEqual(len(conf.log), 2) self.assertEqual(len(self.protocol.sets), 2) self.assertEqual(self.protocol.sets[1], ('Log', 'notice file /var/log/tor/notices.log')) self.assertEqual(self.protocol.sets[0], ('Log', 'info file /tmp/foo'))
def test_log_set_extend(self): conf = TorConfig(self.protocol) self.assertTrue(len(conf.log) == 1) conf.log.extend(['info file /tmp/foo']) self.assertTrue(conf.needs_save()) conf.save() self.assertTrue(len(conf.log) == 2) self.assertTrue(len(self.protocol.sets) == 2) self.assertTrue(self.protocol.sets[0] == ('Log', 'notice file /var/log/tor/notices.log')) self.assertTrue(self.protocol.sets[1] == ('Log', 'info file /tmp/foo'))
def test_ephemeral_extra_kwargs(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) with self.assertRaises(ValueError) as ctx: EphemeralOnionService( config, ports=["80 127.0.0.1:80"], ver=2, something_funny="foo", ) self.assertIn( "Unknown kwarg", str(ctx.exception), )
def test_ephemeral_auth_stealth(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) with self.assertRaises(ValueError) as ctx: yield EphemeralAuthenticatedOnionService.create( Mock(), config, ports=["80 127.0.0.1:80"], auth=AuthStealth(["steve", "carol"]), ) self.assertIn( "Tor does not yet support", str(ctx.exception), )
def test_ephemeral_auth_unknown(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) with self.assertRaises(ValueError) as ctx: yield EphemeralAuthenticatedOnionService.create( Mock(), config, ports=["80 127.0.0.1:80"], auth=["carol", "steve"], ) self.assertIn( "'auth' should be an AuthBasic or AuthStealth instance", str(ctx.exception), )
def test_filesystem_wrong_ports(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) with self.assertRaises(ValueError) as ctx: yield FilesystemOnionService.create( Mock(), config, "/dev/null", ports="80 127.0.0.1:80", ) self.assertIn( "'ports' must be a list of strings", str(ctx.exception) )
def test_explicit_data_dir(self, ftb): with util.TempDir() as tmp: d = str(tmp) with open(os.path.join(d, 'hostname'), 'w') as f: f.write('public') config = TorConfig(self.protocol) ep = TCPHiddenServiceEndpoint(self.reactor, config, 123, d) # make sure listen() correctly configures our hidden-serivce # with the explicit directory we passed in above yield ep.listen(NoOpProtocolFactory()) self.assertEqual(1, len(config.HiddenServices)) self.assertEqual(config.HiddenServices[0].dir, d) self.assertEqual(config.HiddenServices[0].hostname, 'public')
def test_ephemeral_v3_no_key(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) # returns a Deferred we're ignoring EphemeralOnionService.create( Mock(), config, ports=["80 127.0.0.1:80"], detach=True, version=3, ) cmd, d = protocol.commands[0] self.assertEqual(u"ADD_ONION NEW:ED25519-V3 Port=80,127.0.0.1:80 Flags=Detach", cmd) d.callback("PrivateKey={}\nServiceID={}".format(_test_private_key_blob, _test_onion_id))
def test_ephemeral_ports_no_spaces(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) privkey = 'a' * 32 with self.assertRaises(ValueError) as ctx: yield EphemeralOnionService.create( Mock(), config, ports=["80:127.0.0.1:80"], private_key=privkey, ) self.assertIn( "exactly one space", str(ctx.exception) )
def test_ephemeral_ports_not_a_list(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) privkey = 'a' * 32 with self.assertRaises(ValueError) as ctx: yield EphemeralOnionService.create( Mock(), config, ports="80 127.0.0.1:80", private_key=privkey, ) self.assertIn( "'ports' must be a list of strings", str(ctx.exception) )
def test_ephemeral_ports_not_an_int(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) privkey = 'a' * 32 with self.assertRaises(ValueError) as ctx: yield EphemeralOnionService.create( Mock(), config, ports=["web 127.0.0.1:80"], private_key=privkey, ) self.assertIn( "external port isn't an int", str(ctx.exception) )
def test_ephemeral_ports_non_local(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) privkey = 'a' * 32 with self.assertRaises(ValueError) as ctx: yield EphemeralOnionService.create( Mock(), config, ports=["80 8.8.8.8:80"], private_key=privkey, ) self.assertIn( "should be a local address", str(ctx.exception) )
def test_create_torrc(self): config = TorConfig() config.SocksPort = 1234 config.hiddenservices = [ HiddenService(config, '/some/dir', '80 127.0.0.1:1234', 'auth', 2) ] config.Log = ['80 127.0.0.1:80', '90 127.0.0.1:90'] config.save() torrc = config.create_torrc() self.assertEqual( torrc, '''HiddenServiceDir /some/dir HiddenServicePort 80 127.0.0.1:1234 HiddenServiceVersion 2 HiddenServiceAuthorizeClient auth Log 80 127.0.0.1:80 Log 90 127.0.0.1:90 SocksPort 1234 ''')
def test_log_set_index(self): conf = TorConfig(self.protocol) conf.log[0] = 'info file /tmp/foo.log' self.assertTrue(conf.needs_save()) conf.save() self.assertEqual( self.protocol.sets[0], ('Log', 'info file /tmp/foo.log') )
def test_old_tor_version(self): protocol = FakeControlProtocol([]) protocol.version = "0.1.2.3" config = TorConfig(protocol) hsdir = self.mktemp() def my_progress(a, b, c): pass eph_d = FilesystemOnionService.create( Mock(), config, hsdir, ports=["80 127.0.0.1:80"], progress=my_progress, ) yield eph_d
def test_set_multiple(self): self.protocol.answers.append('config/names=\nAwesomeKey String\nOK') self.protocol.answers.append({'AwesomeKey': 'foo'}) conf = TorConfig(self.protocol) conf.awesomekey conf.awesomekey = 'baz' self.assertTrue(conf.needs_save()) conf.awesomekey = 'nybble' conf.awesomekey = 'pac man' conf.save() self.assertEqual(len(self.protocol.sets), 1) self.assertEqual(self.protocol.sets[0], ('AwesomeKey', 'pac man'))
def test_log_set_slice(self): conf = TorConfig(self.protocol) conf.log[0:1] = ['info file /tmp/foo.log'] self.assertTrue(conf.needs_save()) conf.save() self.assertEqual(1, len(self.protocol.sets)) self.assertEqual( self.protocol.sets[0], ('Log', 'info file /tmp/foo.log') )
def test_unknown_version(self): protocol = FakeControlProtocol([]) protocol.version = "0.1.1.1" config = TorConfig(protocol) hsdir = self.mktemp() os.mkdir(hsdir) hs = yield FilesystemOnionService.create( Mock(), config, hsdir=hsdir, ports=["80 127.0.0.1:4321"], version=99, ) with self.assertRaises(RuntimeError) as ctx: hs.private_key self.assertIn("Don't know how to load", str(ctx.exception))
def test_ephemeral_v3_wrong_key_type(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) privkey = 'RSA1024:{}'.format('a' * 32) with self.assertRaises(ValueError) as ctx: yield EphemeralOnionService.create( Mock(), config, ports=["80 127.0.0.1:80"], detach=True, version=3, private_key=privkey, ) self.assertIn( "but private key isn't", str(ctx.exception), )
def test_single_client(self): # FIXME test without crapping on filesystem self.protocol.answers.append('HiddenServiceDir=/fake/path\n') d = tempfile.mkdtemp() try: with open(os.path.join(d, 'hostname'), 'w') as f: f.write('gobledegook\n') conf = TorConfig(self.protocol) hs = HiddenService(conf, d, []) self.assertEqual(1, len(hs.clients)) self.assertEqual('default', hs.clients[0][0]) self.assertEqual('gobledegook', hs.clients[0][1]) finally: shutil.rmtree(d, ignore_errors=True)
def test_onion_keys(self): self.protocol.answers.append('HiddenServiceDir=/fake/path\n') d = tempfile.mkdtemp() try: with open(os.path.join(d, 'hostname'), 'w') as f: f.write('public') with open(os.path.join(d, 'private_key'), 'w') as f: f.write('private') conf = TorConfig(self.protocol) hs = HiddenService(conf, d, []) self.assertEqual(hs.hostname, 'public') self.assertEqual(hs.private_key, 'private') finally: shutil.rmtree(d, ignore_errors=True)
def test_save_boolean(self): self.protocol.answers.append('config/names=\nfoo Boolean\nbar Boolean') self.protocol.answers.append({'foo': '0'}) self.protocol.answers.append({'bar': '1'}) conf = TorConfig(self.protocol) # save some boolean value conf.foo = True conf.bar = False conf.save() self.assertEqual(set(self.protocol.sets), set([('foo', 1), ('bar', 0)]))
def test_create_torrc(self): config = TorConfig() config.SocksPort = 1234 config.hiddenservices = [ HiddenService(config, '/some/dir', '80 127.0.0.1:1234', 'auth', 2, True) ] config.Log = ['80 127.0.0.1:80', '90 127.0.0.1:90'] config.save() torrc = config.create_torrc() lines = torrc.split('\n') lines.sort() torrc = '\n'.join(lines).strip() self.assertEqual(torrc, '''HiddenServiceAuthorizeClient auth HiddenServiceDir /some/dir HiddenServicePort 80 127.0.0.1:1234 HiddenServiceVersion 2 Log 80 127.0.0.1:80 Log 90 127.0.0.1:90 SocksPort 1234''')
def test_read_boolean_after_save(self): self.protocol.answers.append('config/names=\nfoo Boolean\nbar Boolean') self.protocol.answers.append({'foo': '0'}) self.protocol.answers.append({'bar': '1'}) conf = TorConfig(self.protocol) # save some boolean value conf.foo = True conf.bar = False conf.save() self.assertTrue(conf.foo is True, msg="foo not True: %s" % conf.foo) self.assertTrue(conf.bar is False, msg="bar not False: %s" % conf.bar)
def test_explicit_data_dir(self): d = tempfile.mkdtemp() try: with open(os.path.join(d, 'hostname'), 'w') as f: f.write('public') config = TorConfig(self.protocol) ep = TCPHiddenServiceEndpoint(self.reactor, config, 123, d) # make sure listen() correctly configures our hidden-serivce # with the explicit directory we passed in above port = yield ep.listen(NoOpProtocolFactory()) self.assertEqual(1, len(config.HiddenServices)) self.assertEqual(config.HiddenServices[0].dir, d) self.assertEqual(config.HiddenServices[0].hostname, 'public') finally: shutil.rmtree(d, ignore_errors=True)
def test_conf_changed_parsed(self): ''' Create a configuration which holds boolean types. These types have to be parsed as booleans. ''' protocol = FakeControlProtocol([]) protocol.answers.append('config/names=\nFoo Boolean\nBar Boolean') protocol.answers.append({'Foo': '0'}) protocol.answers.append({'Bar': '1'}) config = TorConfig(protocol) # Initial value is not tested here protocol.events['CONF_CHANGED']('Foo=1\nBar=0') msg = "Foo is not True: %r" % config.Foo self.assertTrue(config.Foo is True, msg=msg) msg = "Foo is not False: %r" % config.Bar self.assertTrue(config.Bar is False, msg=msg)
def startTor(): def updates(prog, tag, summary): print("%d%%: %s" % (prog, summary)) torconfig = TorConfig() torconfig.SocksPort = config.main.socks_port if config.main.tor2webmode: torconfig.Tor2webMode = 1 torconfig.CircuitBuildTimeout = 60 torconfig.save() if config.main.tor_binary is not None: d = launch_tor(torconfig, reactor, tor_binary=config.main.tor_binary, progress_updates=updates) else: d = launch_tor(torconfig, reactor, progress_updates=updates) d.addCallback(setupCollector) d.addErrback(txSetupFailed)
def test_ephemeral_v3_non_anonymous(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) # returns a Deferred we're ignoring EphemeralOnionService.create( Mock(), config, ports=[(80, "192.168.1.2:80")], version=3, detach=True, single_hop=True, ) cmd, d = protocol.commands[0] self.assertEqual( u"ADD_ONION NEW:ED25519-V3 Port=80,192.168.1.2:80 Flags=Detach,NonAnonymous", cmd) d.callback("PrivateKey={}\nServiceID={}".format( _test_private_key_blob, _test_onion_id))
def _try_control_port(self, control_port): NOPE = (None, None, None) ep = clientFromString(self._reactor, control_port) try: tproto = yield build_tor_connection(ep, build_state=False) # now wait for bootstrap tconfig = yield TorConfig.from_protocol(tproto) except (ValueError, ConnectError): returnValue(NOPE) socks_ports = list(tconfig.SocksPort) socks_port = socks_ports[0] # TODO: when might there be multiple? # I've seen "9050", and "unix:/var/run/tor/socks WorldWritable" pieces = socks_port.split() p = pieces[0] if p == DEFAULT_VALUE: socks_desc = "tcp:127.0.0.1:9050" elif re.search('^\d+$', p): socks_desc = "tcp:127.0.0.1:%s" % p else: socks_desc = p returnValue((tproto, tconfig, socks_desc))
def test_dir_ioerror(self): protocol = FakeControlProtocol([]) config = TorConfig(protocol) hsdir = self.mktemp() os.mkdir(hsdir) with open(join(hsdir, "hostname"), "w") as f: f.write("{}.onion".format(_test_onion_id)) hs_d = FilesystemOnionService.create( Mock(), config, hsdir=hsdir, ports=["80 127.0.0.1:4321"], ) # arrange HS_DESC callbacks so we get the hs instance back cb = protocol.events['HS_DESC'] cb('UPLOAD {} UNKNOWN hsdir0'.format(_test_onion_id)) cb('UPLOADED {} UNKNOWN hsdir0'.format(_test_onion_id)) hs = yield hs_d self.assertIs(None, hs.private_key)
def setupCollector(tor_process_protocol, datadir): def setup_complete(port): #XXX: drop some other noise about what API are available on this machine print("Exposed collector Tor hidden service on httpo://%s" % port.onion_uri) torconfig = TorConfig(tor_process_protocol.tor_protocol) public_port = 80 # XXX there is currently a bug in txtorcon that prevents data_dir from # being passed properly. Details on the bug can be found here: # https://github.com/meejah/txtorcon/pull/22 #XXX: set up the various API endpoints, if configured and enabled #XXX: also set up a separate keyed hidden service for collectors to push their status to, if the bouncer is enabled hs_endpoint = TCPHiddenServiceEndpoint(reactor, torconfig, public_port, data_dir=os.path.join(datadir, 'collector')) d = hs_endpoint.listen(ooniBackend) d.addCallback(setup_complete) d.addErrback(txSetupFailed) return tor_process_protocol