def findpath(controller): """ Generate Tor paths. "controller": authenticated Tor Controller from stem.control. """ # Validate input parameters. assert isinstance(controller, Controller), \ 'Controller has wrong type: %s.' % type(controller) assert controller.get_version() > Version('0.2.3'), \ ('Your tor version (%s) is too old. ' % controller.get_version() + 'Tor version 0.2.3.x is required.') assert controller.get_version() < Version('0.2.4'), \ ('Your tor version (%s) is too new. ' % controller.get_version() + 'Tor version 0.2.3.x is required.') # Change guard nodes for every path. msg = controller.msg('DUMPGUARDS') assert msg.is_ok(), ("DUMPGUARDS command failed with error " + "'%s'. Is your tor client patched?\n" % str(msg)) # Get a path from tor. msg = controller.msg('FINDPATH') assert msg.is_ok(), ("FINDPATH command failed with error " + "'%s'. Is your tor client patched?\n" % str(msg)) sys.stdout.write("%s\n" % findall('[A-Z0-9]{40}', str(msg)))
def assert_version_is_equal(self, first_version, second_version): """ Asserts that the parsed version of the first version equals the second. """ version1 = Version(first_version) version2 = Version(second_version) self.assertEqual(version1, version2)
def assert_version_is_greater(self, first_version, second_version): """ Asserts that the parsed version of the first version is greate than the second (also checking the inverse). """ version1 = Version(first_version) version2 = Version(second_version) self.assertEqual(version1 > version2, True) self.assertEqual(version1 < version2, False)
def test_requirements_multiple_rules(self): """ Checks a VersionRequirements is the logical 'or' when it has multiple rules. """ # rule to say 'anything but the 0.2.2.x series' requirements = stem.version._VersionRequirements() requirements.greater_than(Version('0.2.3.0')) requirements.less_than(Version('0.2.2.0'), False) self.assertTrue(Version('0.2.3.0') >= requirements) self.assertFalse(Version('0.2.2.0') >= requirements) for index in range(0, 100): self.assertFalse(Version('0.2.2.%i' % index) >= requirements)
def test_without_ed25519(self): """ Parses a router status entry without a ed25519 value. """ microdescriptor_hashes = [ ([13, 14, 15], {'sha256': 'uaAYTOVuYRqUwJpNfP2WizjzO0FiNQB4U97xSQu+vMc'}), ([16, 17], {'sha256': 'G6FmPe/ehgfb6tsRzFKDCwvvae+RICeP1MaP0vWDGyI'}), ([18, 19, 20, 21], {'sha256': '/XhIMOnhElo2UiKjL2S10uRka/fhg1CFfNd+9wgUwEE'}), ] entry = RouterStatusEntryV3(ENTRY_WITHOUT_ED25519, document = vote_document(), validate = True) self.assertEqual('seele', entry.nickname) self.assertEqual('000A10D43011EA4928A35F610405F92B4433B4DC', entry.fingerprint) self.assertEqual(datetime.datetime(2015, 8, 23, 0, 26, 35), entry.published) self.assertEqual('73.15.150.172', entry.address) self.assertEqual(9001, entry.or_port) self.assertEqual(None, entry.dir_port) self.assertEqual(set([Flag.RUNNING, Flag.STABLE, Flag.VALID]), set(entry.flags)) self.assertEqual('Tor 0.2.6.10', entry.version_line) self.assertEqual(Version('0.2.6.10'), entry.version) self.assertEqual(102, entry.bandwidth) self.assertEqual(31, entry.measured) self.assertEqual(False, entry.is_unmeasured) self.assertEqual([], entry.unrecognized_bandwidth_entries) self.assertEqual(MicroExitPolicy('reject 1-65535'), entry.exit_policy) self.assertEqual(microdescriptor_hashes, entry.microdescriptor_hashes) self.assertEqual('ed25519', entry.identifier_type) self.assertEqual('none', entry.identifier) self.assertEqual('9B4CA73EEC3349EC6DCEC897609600D0771EF82B', entry.digest) self.assertEqual([], entry.get_unrecognized_lines())
def test_with_ed25519(self): """ Parses a router status entry with a ed25519 value. """ microdescriptor_hashes = [ ([13], {'sha256': 'PTSHzE7RKnRGZMRmBddSzDiZio254FUhv9+V4F5zq8s'}), ([14, 15], {'sha256': '0wsEwBbxJ8RtPmGYwilHQTVEw2pWzUBEVlSgEO77OyU'}), ([16, 17], {'sha256': 'JK2xhYr/VsCF60px+LsT990BCpfKfQTeMxRbD63o2vE'}), ([18, 19, 20], {'sha256': 'AkZH3gIvz3wunsroqh5izBJizdYuR7kn2oVbsvqgML8'}), ([21], {'sha256': 'AVp41YVxKEJCaoEf0+77Cdvyw5YgpyDXdob0+LSv/pE'}), ] entry = RouterStatusEntryV3(ENTRY_WITH_ED25519, document = vote_document(), validate = True) self.assertEqual('PDrelay1', entry.nickname) self.assertEqual('000149E6EF7102AACA9690D6E8DD2932124B94AB', entry.fingerprint) self.assertEqual(datetime.datetime(2015, 8, 23, 16, 52, 37), entry.published) self.assertEqual('95.215.44.189', entry.address) self.assertEqual(8080, entry.or_port) self.assertEqual(None, entry.dir_port) self.assertEqual(set([Flag.FAST, Flag.RUNNING, Flag.STABLE, Flag.VALID]), set(entry.flags)) self.assertEqual('Tor 0.2.7.2-alpha-dev', entry.version_line) self.assertEqual(Version('0.2.7.2-alpha-dev'), entry.version) self.assertEqual(608, entry.bandwidth) self.assertEqual(472, entry.measured) self.assertEqual(False, entry.is_unmeasured) self.assertEqual([], entry.unrecognized_bandwidth_entries) self.assertEqual(MicroExitPolicy('reject 1-65535'), entry.exit_policy) self.assertEqual(microdescriptor_hashes, entry.microdescriptor_hashes) self.assertEqual('ed25519', entry.identifier_type) self.assertEqual('8RH34kO07Pp+XYwzdoATVyCibIvmbslUjRkAm7J4IA8', entry.identifier) self.assertEqual('CAB27A6FFEF7A661C18B0B11120C3E8A77FC585C', entry.digest) self.assertEqual([], entry.get_unrecognized_lines())
def checkOutdateRelays(self, torVersion='0.2.3.0'): print "Checking relays with TOR version equals or less to %s " % ( torVersion) for torNode in self.torNodes: if torNode.torVersion < Version(torVersion): print "[*] Older version of TOR detected: %s Nickname of the Relay %s IP Address reported %s" % ( torNode.torVersion, torNode.nickName, torNode.host)
def assert_string_matches(self, version): """ Parses the given version string then checks that its string representation matches the input. """ self.assertEqual(version, str(Version(version)))
def test_with_multiple_extra(self): """ Parse a version with multiple 'extra' fields. """ version = Version('0.1.2 (release) (git-73ff13ab3cc9570d)') self.assert_versions_match(version, 0, 1, 2, None, None, 'release') self.assertEqual(['release', 'git-73ff13ab3cc9570d'], version.all_extra) self.assertEqual('73ff13ab3cc9570d', version.git_commit)
def test_versions(self): """ Handles a variety of version inputs. """ test_values = { 'Tor 0.2.2.35': Version('0.2.2.35'), 'Tor 0.1.2': Version('0.1.2'), 'Torr new_stuff': None, 'new_stuff and stuff': None, } for v_line, expected in test_values.items(): entry = RouterStatusEntryV3.create({'v': v_line}) self.assertEqual(expected, entry.version) self.assertEqual(v_line, entry.version_line) # tries an invalid input expect_invalid_attr(self, {'v': 'Tor ugabuga'}, 'version')
def test_parsing(self): """ Tests parsing by the Version class constructor. """ # valid versions with various number of compontents to the version version = Version("0.1.2.3-tag") self.assert_versions_match(version, 0, 1, 2, 3, "tag", None) version = Version("0.1.2.3") self.assert_versions_match(version, 0, 1, 2, 3, None, None) version = Version("0.1.2-tag") self.assert_versions_match(version, 0, 1, 2, None, "tag", None) version = Version("0.1.2") self.assert_versions_match(version, 0, 1, 2, None, None, None) # checks an empty tag version = Version("0.1.2.3-") self.assert_versions_match(version, 0, 1, 2, 3, "", None) version = Version("0.1.2-") self.assert_versions_match(version, 0, 1, 2, None, "", None) # check with extra informaton version = Version("0.1.2.3-tag (git-73ff13ab3cc9570d)") self.assert_versions_match(version, 0, 1, 2, 3, "tag", "git-73ff13ab3cc9570d") self.assertEqual("73ff13ab3cc9570d", version.git_commit) version = Version("0.1.2.3-tag ()") self.assert_versions_match(version, 0, 1, 2, 3, "tag", "") version = Version("0.1.2 (git-73ff13ab3cc9570d)") self.assert_versions_match(version, 0, 1, 2, None, None, "git-73ff13ab3cc9570d") # checks invalid version strings self.assertRaises(ValueError, stem.version.Version, "") self.assertRaises(ValueError, stem.version.Version, "1.2.3.4nodash") self.assertRaises(ValueError, stem.version.Version, "1.2.3.a") self.assertRaises(ValueError, stem.version.Version, "1.2.a.4") self.assertRaises(ValueError, stem.version.Version, "1x2x3x4") self.assertRaises(ValueError, stem.version.Version, "12.3") self.assertRaises(ValueError, stem.version.Version, "1.-2.3")
def test_parsing(self): """ Tests parsing by the Version class constructor. """ # valid versions with various number of compontents to the version version = Version('0.1.2.3-tag') self.assert_versions_match(version, 0, 1, 2, 3, 'tag', None) version = Version('0.1.2.3') self.assert_versions_match(version, 0, 1, 2, 3, None, None) version = Version('0.1.2-tag') self.assert_versions_match(version, 0, 1, 2, None, 'tag', None) version = Version('0.1.2') self.assert_versions_match(version, 0, 1, 2, None, None, None) # checks an empty tag version = Version('0.1.2.3-') self.assert_versions_match(version, 0, 1, 2, 3, '', None) version = Version('0.1.2-') self.assert_versions_match(version, 0, 1, 2, None, '', None) # check with extra informaton version = Version('0.1.2.3-tag (git-73ff13ab3cc9570d)') self.assert_versions_match(version, 0, 1, 2, 3, 'tag', 'git-73ff13ab3cc9570d') self.assertEqual('73ff13ab3cc9570d', version.git_commit) version = Version('0.1.2.3-tag ()') self.assert_versions_match(version, 0, 1, 2, 3, 'tag', '') version = Version('0.1.2 (git-73ff13ab3cc9570d)') self.assert_versions_match(version, 0, 1, 2, None, None, 'git-73ff13ab3cc9570d') # checks invalid version strings self.assertRaises(ValueError, stem.version.Version, '') self.assertRaises(ValueError, stem.version.Version, '1.2.3.4nodash') self.assertRaises(ValueError, stem.version.Version, '1.2.3.a') self.assertRaises(ValueError, stem.version.Version, '1.2.a.4') self.assertRaises(ValueError, stem.version.Version, '1x2x3x4') self.assertRaises(ValueError, stem.version.Version, '12.3') self.assertRaises(ValueError, stem.version.Version, '1.-2.3')
def test_nonversion_comparison(self): """ Checks that we can be compared with other types. """ test_version = Version("0.1.2.3") self.assertNotEqual(test_version, None) self.assertTrue(test_version > None) self.assertNotEqual(test_version, 5) self.assertTrue(test_version > 5)
def test_requirements_greater_than(self): """ Checks a VersionRequirements with a single greater_than rule. """ requirements = stem.version._VersionRequirements() requirements.greater_than(Version("0.2.2.36")) self.assertTrue(Version("0.2.2.36") >= requirements) self.assertTrue(Version("0.2.2.37") >= requirements) self.assertTrue(Version("0.2.3.36") >= requirements) self.assertFalse(Version("0.2.2.35") >= requirements) self.assertFalse(Version("0.2.1.38") >= requirements) requirements = stem.version._VersionRequirements() requirements.greater_than(Version("0.2.2.36"), False) self.assertFalse(Version("0.2.2.35") >= requirements) self.assertFalse(Version("0.2.2.36") >= requirements) self.assertTrue(Version("0.2.2.37") >= requirements)
def test_requirements_less_than(self): """ Checks a VersionRequirements with a single less_than rule. """ requirements = stem.version._VersionRequirements() requirements.less_than(Version('0.2.2.36')) self.assertTrue(Version('0.2.2.36') >= requirements) self.assertTrue(Version('0.2.2.35') >= requirements) self.assertTrue(Version('0.2.1.38') >= requirements) self.assertFalse(Version('0.2.2.37') >= requirements) self.assertFalse(Version('0.2.3.36') >= requirements) requirements = stem.version._VersionRequirements() requirements.less_than(Version('0.2.2.36'), False) self.assertFalse(Version('0.2.2.37') >= requirements) self.assertFalse(Version('0.2.2.36') >= requirements) self.assertTrue(Version('0.2.2.35') >= requirements)
def test_versions(self): """ Handles a variety of version inputs. """ test_values = { 'Tor 0.2.2.35': Version('0.2.2.35'), 'Tor 0.1.2': Version('0.1.2'), 'Torr new_stuff': None, 'new_stuff and stuff': None, } for v_line, expected in test_values.items(): entry = get_router_status_entry_v3({'v': v_line}) self.assertEqual(expected, entry.version) self.assertEqual(v_line, entry.version_line) # tries an invalid input content = get_router_status_entry_v3({'v': 'Tor ugabuga'}, content = True) self._expect_invalid_attr(content, 'version')
def test_requirements_less_than(self): """ Checks a VersionRequirements with a single less_than rule. """ requirements = stem.version.VersionRequirements() requirements.less_than(Version("0.2.2.36")) self.assertTrue(Version("0.2.2.36").meets_requirements(requirements)) self.assertTrue(Version("0.2.2.35").meets_requirements(requirements)) self.assertTrue(Version("0.2.1.38").meets_requirements(requirements)) self.assertFalse(Version("0.2.2.37").meets_requirements(requirements)) self.assertFalse(Version("0.2.3.36").meets_requirements(requirements)) requirements = stem.version.VersionRequirements() requirements.less_than(Version("0.2.2.36"), False) self.assertFalse(Version("0.2.2.37").meets_requirements(requirements)) self.assertFalse(Version("0.2.2.36").meets_requirements(requirements)) self.assertTrue(Version("0.2.2.35").meets_requirements(requirements))
def test_versions(self): """ Handles a variety of version inputs. """ test_values = { "Tor 0.2.2.35": Version("0.2.2.35"), "Tor 0.1.2": Version("0.1.2"), "Torr new_stuff": None, "new_stuff and stuff": None, } for v_line, expected in test_values.items(): entry = get_router_status_entry_v3({'v': v_line}) self.assertEquals(expected, entry.version) self.assertEquals(v_line, entry.version_line) # tries an invalid input content = get_router_status_entry_v3({'v': "Tor ugabuga"}, content = True) self._expect_invalid_attr(content, "version")
def test_nonversion_comparison(self): """ Checks that we can be compared with other types. In python 3 on only equality comparisons work, greater than and less than comparisons result in a TypeError. """ test_version = Version('0.1.2.3') self.assertNotEqual(test_version, None) self.assertNotEqual(test_version, 5)
def test_requirements_in_range(self): """ Checks a VersionRequirements with a single in_range rule. """ requirements = stem.version._VersionRequirements() requirements.in_range(Version('0.2.2.36'), Version('0.2.2.38')) self.assertFalse(Version('0.2.2.35') >= requirements) self.assertTrue(Version('0.2.2.36') >= requirements) self.assertTrue(Version('0.2.2.37') >= requirements) self.assertFalse(Version('0.2.2.38') >= requirements) # rule for 'anything in the 0.2.2.x series' requirements = stem.version._VersionRequirements() requirements.in_range(Version('0.2.2.0'), Version('0.2.3.0')) for index in range(0, 100): self.assertTrue(Version('0.2.2.%i' % index) >= requirements)
def test_requirements_in_range(self): """ Checks a VersionRequirements with a single in_range rule. """ requirements = stem.version.VersionRequirements() requirements.in_range(Version("0.2.2.36"), Version("0.2.2.38")) self.assertFalse(Version("0.2.2.35").meets_requirements(requirements)) self.assertTrue(Version("0.2.2.36").meets_requirements(requirements)) self.assertTrue(Version("0.2.2.37").meets_requirements(requirements)) self.assertFalse(Version("0.2.2.38").meets_requirements(requirements)) # rule for 'anything in the 0.2.2.x series' requirements = stem.version.VersionRequirements() requirements.in_range(Version("0.2.2.0"), Version("0.2.3.0")) for index in xrange(0, 100): self.assertTrue( Version("0.2.2.%i" % index).meets_requirements(requirements))
def test_with_ipv6(self): """ Parse a router status entry with an IPv6 address. """ expected_protocols = OrderedDict(( ('Cons', [1]), ('Desc', [1]), ('DirCache', [1]), ('HSDir', [1]), ('HSIntro', [3]), ('HSRend', [1]), ('Link', [1, 2, 3, 4]), ('LinkAuth', [1]), ('Microdesc', [1]), ('Relay', [1, 2]), )) entry = RouterStatusEntryMicroV3(ENTRY_WITH_IPV6, validate=True) self.assertEqual('MYLEX', entry.nickname) self.assertEqual('010B7728454411F485CE29D4C79A14534151C2C4', entry.fingerprint) self.assertEqual(datetime.datetime(2018, 7, 15, 16, 38, 10), entry.published) self.assertEqual('77.123.42.148', entry.address) self.assertEqual(444, entry.or_port) self.assertEqual(800, entry.dir_port) self.assertEqual( set([ Flag.FAST, Flag.GUARD, Flag.HSDIR, Flag.RUNNING, Flag.STABLE, Flag.V2DIR, Flag.VALID ]), set(entry.flags)) self.assertEqual('Tor 0.2.5.16', entry.version_line) self.assertEqual(Version('0.2.5.16'), entry.version) self.assertEqual([('2001:470:71:9b9:f66d:4ff:fee7:954c', 444, True)], entry.or_addresses) self.assertEqual(4950, entry.bandwidth) self.assertEqual(None, entry.measured) self.assertEqual(False, entry.is_unmeasured) self.assertEqual([], entry.unrecognized_bandwidth_entries) self.assertEqual(expected_protocols, entry.protocols) self.assertEqual( '1966FEC636AFD1FB2EC0FC0F36752F5BD43522F9399F3F26D4C70408CE0A63C0', entry.digest) self.assertEqual([], entry.get_unrecognized_lines())
def tutorial_example(): from stem.descriptor.remote import DescriptorDownloader from stem.version import Version downloader = DescriptorDownloader() count, with_contact = 0, 0 print("Checking for outdated relays...\n") for desc in downloader.get_server_descriptors(): if desc.tor_version < Version('0.2.3.0'): count += 1 if desc.contact: print(' %-15s %s' % (desc.tor_version, desc.contact.decode("utf-8", "replace"))) with_contact += 1 print("\n%i outdated relays found, %i had contact information" % (count, with_contact))
def run(self, do_onion=True, do_inet=True, tgen_model=None, tgen_client_conf=None, tgen_server_conf=None): ''' only `tgen_server_conf.listen_port` are "public" and need to be opened on the firewall. if `tgen_client_conf.connect_port` != `tgen_server_conf.listen_port`, then you should have installed a forwarding rule in the firewall. all ports need to be unique though, and unique among multiple onionperf instances. here are some sane defaults: tgen_client_conf.listen_port=58888, tgen_client_conf.connect_port=8080, tgen_client_conf.tor_ctl_port=59050, tgen_client_conf.tor_socks_port=59000, tgen_server_conf.listen_port=8080, tgen_server_conf.tor_ctl_port=59051, tgen_server_conf.tor_socks_port=59001 ''' self.threads = [] self.done_event = threading.Event() if tgen_client_conf is None: tgen_client_conf = TGenConf(listen_port=58888, connect_ip='0.0.0.0', connect_port=8080, tor_ctl_port=59050, tor_socks_port=59000) if tgen_server_conf is None: tgen_server_conf = TGenConf(listen_port=8080, tor_ctl_port=59051, tor_socks_port=59001) # if ctrl-c is pressed, shutdown child processes properly try: # make sure stem and Tor supports ephemeral HS (version >= 0.2.7.1-alpha) # and also the NEWNYM mode that clears descriptor cache (version >= 0.2.7.3-rc) if do_onion: try: tor_version = get_system_tor_version(self.tor_bin_path) if tor_version < Requirement.ADD_ONION or tor_version < Version( '0.2.7.3-rc'): # ADD_ONION is a stem 1.4.0 feature logging.warning( "OnionPerf in onion mode requires Tor version >= 0.2.7.3-rc, you have {0}, aborting" .format(tor_version)) return except: logging.warning( "OnionPerf in onion mode requires stem version >= 1.4.0, you have {0}, aborting" .format(stem_version)) return logging.info("Bootstrapping started...") logging.info( "Log files for the client and server processes will be placed in {0}" .format(self.datadir_path)) general_writables = [] tgen_client_writable, torctl_client_writable = None, None if do_onion or do_inet: tgen_model.port = tgen_server_conf.listen_port general_writables.append(self.__start_tgen_server(tgen_model)) if do_onion: logging.info( "Onion Service private keys will be placed in {0}".format( self.privatedir_path)) # one must not have an open socks port when running a single # onion service. see tor's man page for more information. if self.single_onion: tgen_server_conf.tor_socks_port = 0 tor_writable, torctl_writable = self.__start_tor_server( tgen_server_conf.tor_ctl_port, tgen_server_conf.tor_socks_port, { tgen_client_conf.connect_port: tgen_server_conf.listen_port }) general_writables.append(tor_writable) general_writables.append(torctl_writable) if do_onion or do_inet: tor_writable, torctl_client_writable = self.__start_tor_client( tgen_client_conf.tor_ctl_port, tgen_client_conf.tor_socks_port) general_writables.append(tor_writable) server_urls = [] if do_onion and self.hs_v3_service_id is not None: server_urls.append("{0}.onion:{1}".format( self.hs_v3_service_id, tgen_client_conf.connect_port)) if do_inet: connect_ip = tgen_client_conf.connect_ip if tgen_client_conf.connect_ip != '0.0.0.0' else util.get_ip_address( ) server_urls.append("{0}:{1}".format( connect_ip, tgen_client_conf.connect_port)) tgen_model.servers = server_urls if do_onion or do_inet: assert len(server_urls) > 0 tgen_model.port = tgen_client_conf.listen_port tgen_model.socks_port = tgen_client_conf.tor_socks_port tgen_client_writable = self.__start_tgen_client(tgen_model) self.__start_log_processors(general_writables, tgen_client_writable, torctl_client_writable) logging.info("Bootstrapping finished, entering heartbeat loop") time.sleep(1) while True: if tgen_model.num_transfers: # This function blocks until our TGen client process # terminated on its own. self.__wait_for_tgen_client() break if self.__is_alive(): logging.info( "All helper processes seem to be alive :)") else: logging.warning( "Some parallel components failed too many times or have died :(" ) logging.info( "We are in a broken state, giving up and exiting now" ) break logging.info( "Next main process heartbeat is in 1 hour (helper processes run on their own schedule)" ) logging.info("press CTRL-C for graceful shutdown...") time.sleep(3600) else: logging.info("No measurement mode set, nothing to do") except KeyboardInterrupt: logging.info( "Interrupt received, please wait for graceful shutdown") self.__is_alive() finally: logging.info("Cleaning up child processes now...") if self.hs_v3_service_id is not None: try: with Controller.from_port( port=self.hs_v3_control_port) as torctl: torctl.authenticate() torctl.remove_ephemeral_hidden_service( self.hs_v3_service_id) except: pass # this fails to authenticate if tor proc is dead # logging.disable(logging.INFO) self.done_event.set() for t in self.threads: logging.info("Joining {0} thread...".format(t.getName())) t.join() time.sleep(1) # logging.disable(logging.NOTSET) logging.info("Child processes terminated") logging.info("Child process cleanup complete!") logging.info("Exiting")
def run(self, do_onion=True, do_inet=True, client_tgen_listen_port=58888, client_tgen_connect_ip='0.0.0.0', client_tgen_connect_port=8080, client_tor_ctl_port=59050, client_tor_socks_port=59000, server_tgen_listen_port=8080, server_tor_ctl_port=59051, server_tor_socks_port=59001): ''' only `server_tgen_listen_port` are "public" and need to be opened on the firewall. if `client_tgen_connect_port` != `server_tgen_listen_port`, then you should have installed a forwarding rule in the firewall. all ports need to be unique though, and unique among multiple onionperf instances. here are some sane defaults: client_tgen_listen_port=58888, client_tgen_connect_port=8080, client_tor_ctl_port=59050, client_tor_socks_port=59000, server_tgen_listen_port=8080, server_tor_ctl_port=59051, server_tor_socks_port=59001 ''' self.threads = [] self.done_event = threading.Event() # if ctrl-c is pressed, shutdown child processes properly try: # make sure stem and Tor supports ephemeral HS (version >= 0.2.7.1-alpha) # and also the NEWNYM mode that clears descriptor cache (version >= 0.2.7.3-rc) if do_onion: try: tor_version = get_system_tor_version(self.tor_bin_path) if tor_version < Requirement.ADD_ONION or tor_version < Version( '0.2.7.3-rc'): # ADD_ONION is a stem 1.4.0 feature logging.warning( "OnionPerf in onion mode requires Tor version >= 0.2.7.3-rc, you have {0}, aborting" .format(tor_version)) return except: logging.warning( "OnionPerf in onion mode requires stem version >= 1.4.0, you have {0}, aborting" .format(stem_version)) return logging.info("Bootstrapping started...") logging.info( "Log files for the client and server processes will be placed in {0}" .format(self.datadir_path)) general_writables = [] tgen_client_writable, torctl_client_writable = None, None if do_onion or do_inet: general_writables.append( self.__start_tgen_server(server_tgen_listen_port)) if do_onion: logging.info( "Onion Service private keys will be placed in {0}".format( self.privatedir_path)) tor_writable, torctl_writable = self.__start_tor_server( server_tor_ctl_port, server_tor_socks_port, {client_tgen_connect_port: server_tgen_listen_port}) general_writables.append(tor_writable) general_writables.append(torctl_writable) if do_onion or do_inet: tor_writable, torctl_client_writable = self.__start_tor_client( client_tor_ctl_port, client_tor_socks_port) general_writables.append(tor_writable) server_urls = [] if do_onion and self.hs_service_id is not None and self.hs_v3_service_id is not None: server_urls.append("{0}.onion:{1}".format( self.hs_service_id, client_tgen_connect_port)) server_urls.append("{0}.onion:{1}".format( self.hs_v3_service_id, client_tgen_connect_port)) if do_inet: connect_ip = client_tgen_connect_ip if client_tgen_connect_ip != '0.0.0.0' else util.get_ip_address( ) server_urls.append("{0}:{1}".format(connect_ip, client_tgen_connect_port)) if do_onion or do_inet: assert len(server_urls) > 0 tgen_client_writable = self.__start_tgen_client( server_urls, client_tgen_listen_port, client_tor_socks_port) self.__start_log_processors(general_writables, tgen_client_writable, torctl_client_writable) logging.info("Bootstrapping finished, entering heartbeat loop") time.sleep(1) if self.oneshot: logging.info( "Onionperf is running in Oneshot mode. It will download a 5M file and shut down gracefully..." ) while True: # TODO add status update of some kind? maybe the number of files in the www directory? # logging.info("Heartbeat: {0} downloads have completed successfully".format(self.__get_download_count(tgen_client_writable.filename))) if self.oneshot: downloads = 0 while True: downloads = self.__get_download_count( tgen_client_writable.filename) if downloads >= 1: logging.info( "Onionperf has downloaded a 5M file in oneshot mode, and will now shut down." ) break else: continue break if self.__is_alive(): logging.info( "All helper processes seem to be alive :)") else: logging.warning( "Some parallel components failed too many times or have died :(" ) logging.info( "We are in a broken state, giving up and exiting now" ) break logging.info( "Next main process heartbeat is in 1 hour (helper processes run on their own schedule)" ) logging.info("press CTRL-C for graceful shutdown...") time.sleep(3600) else: logging.info("No measurement mode set, nothing to do") except KeyboardInterrupt: logging.info( "Interrupt received, please wait for graceful shutdown") self.__is_alive() finally: logging.info("Cleaning up child processes now...") if self.hs_service_id is not None: try: with Controller.from_port( port=self.hs_control_port) as torctl: torctl.authenticate() torctl.remove_ephemeral_hidden_service( self.hs_service_id) except: pass # this fails to authenticate if tor proc is dead if self.hs_v3_service_id is not None: try: with Controller.from_port( port=self.hs_v3_control_port) as torctl: torctl.authenticate() torctl.remove_ephemeral_hidden_service( self.hs_v3_service_id) except: pass # this fails to authenticate if tor proc is dead # logging.disable(logging.INFO) self.done_event.set() for t in self.threads: logging.info("Joining {0} thread...".format(t.getName())) t.join() time.sleep(1) # logging.disable(logging.NOTSET) logging.info("Child processes terminated") logging.info("Child process cleanup complete!") logging.info("Exiting")
def NavigaTor(controller, num_circuits=1, num_rttprobes=1, num_ttfbprobes=1, num_bwprobes=1, probesleep=0, num_threads=1, output='probe_', network_protection=True): """ Configure Tor client and start threads for probing the RTT and/or TTFB of Tor circuits. "controller": authenticated Tor Controller from stem.control. "num_circuits": number of circuits to be probed. "num_rttprobes": number of RTT probes to be taken for each circuit. "num_ttfbprobes": number of TTFB probes to be taken for each circuit. "num_bwprobes": number of bw probes to be taken for each circuit. "probesleep": number of seconds to wait between probes. "num_threads": number of threads to start that actually do the probing. "output": prefix for output file(s). "network_protection": Anti-Hammering protection for the Tor network. """ # RouterStatusEntryV3 support in Stem assert get_distribution('stem').version > '1.4.0', \ 'Stem module version must be greater then 1.4.0.' # socks5 + hostname support has been added in 7.21.7 assert pycurl.version_info()[1] >= '7.21.7', \ 'pycurl version (%s) must be >= 7.21.7' % pycurl.version_info()[1] # Validate input parameters. assert isinstance(controller, Controller), \ 'Controller has wrong type: %s.' % type(controller) for i in num_circuits, num_rttprobes, num_ttfbprobes, num_bwprobes,\ num_threads: assert isinstance(i, int), '%s has wrong type: %s.' % (i, type(i)) # Maximum number of circuits that can be probed is limited by # the unique destination IP calculation. Currently there is no need to # raise this limit. max_circuits = 255 + 255 * 256 + 255 * pow(256, 2) - 1 assert num_circuits in range(1, max_circuits), \ 'num_circuits is out of range: %d.' % (num_circuits) assert controller.get_version() > Version('0.2.3'), \ ('Your tor version (%s) is too old. ' % controller.get_version() + 'Tor version 0.2.3.x is required.') assert controller.get_version() < Version('0.2.4'), \ ('Your tor version (%s) is too new. ' % controller.get_version() + 'Tor version 0.2.3.x is required.') try: # Configure tor client controller.set_conf("__DisablePredictedCircuits", "1") controller.set_conf("__LeaveStreamsUnattached", "1") controller.set_conf("MaxClientCircuitsPending", "1024") # Workaround ticket 9543. 10s average for each RTT probe and # 10s for each TTFB probe should be enough. max_dirtiness = (num_rttprobes + num_ttfbprobes) * 10 if int(controller.get_conf("MaxCircuitDirtiness")) < max_dirtiness: controller.set_conf("MaxCircuitDirtiness", str(max_dirtiness)) # Close all non-internal circuits. for circ in controller.get_circuits(): if not circ.build_flags or 'IS_INTERNAL' not in circ.build_flags: controller.close_circuit(circ.id) manager = _Manager(controller, num_circuits, num_rttprobes, num_ttfbprobes, num_bwprobes, probesleep, num_threads, output, network_protection) while True: manager.join(1) if not manager.is_alive(): break except KeyboardInterrupt: pass finally: controller.reset_conf("__DisablePredictedCircuits") controller.reset_conf("__LeaveStreamsUnattached") controller.reset_conf("MaxCircuitDirtiness") controller.reset_conf("MaxClientCircuitsPending") controller.close()
from stem.descriptor.remote import DescriptorDownloader from stem.version import Version downloader = DescriptorDownloader() count, with_contact = 0, 0 print("Checking for outdated relays...") print("") for desc in downloader.get_server_descriptors(): if desc.tor_version < Version('0.2.3.0'): count += 1 if desc.contact: print(' %-15s %s' % (desc.tor_version, desc.contact.decode("utf-8", "replace"))) with_contact += 1 print("") print("%i outdated relays found, %i had contact information" % (count, with_contact))