#!/usr/bin/env python # Tor Glass - Developed by acidvegas in Python (https://git.acid.vegas/proxytools) ''' A simple script to pull a list of all the Tor relays / exit nodes & generate a json database. The example below will generate a map of all the Tor relays / exit nodes using the ipinfo.io API. ''' import datetime try: import stem.descriptor.remote except ImportError: raise SystemExit('missing required library \'stem\' (https://pypi.org/project/stem/)') def get_descriptors(start_time = None) -> dict: ''' Generate a json database of all Tor relays & exit nodes. :param start_time: (optional) datetime object to start from ''' tor_map = { 'relay': [], 'exit': [] } source = stem.descriptor.collector.get_server_descriptors(start = start_time) if start_time else stem.descriptor.remote.get_server_descriptors() for relay in source: data = { 'nickname' : relay.nickname, 'fingerprint' : relay.fingerprint, 'or_addresses' : relay.or_addresses, 'published' : str(relay.published) if relay.published else None, 'address' : relay.address, 'or_port' : relay.or_port, 'socks_port' : relay.socks_port, 'dir_port' : relay.dir_port, 'platform' : str(relay.platform) if relay.platform else None, 'tor_version' : str(relay.tor_version), 'operating_system' : relay.operating_system, 'uptime' : relay.uptime, 'contact' : relay.contact.decode('utf-8') if relay.contact else None, 'exit_policy' : str(relay.exit_policy) if relay.exit_policy else None, 'exit_policy_v6' : str(relay.exit_policy_v6) if relay.exit_policy_v6 else None, 'bridge_distribution' : relay.bridge_distribution, 'family' : list(relay.family) if relay.family else None, 'average_bandwidth' : relay.average_bandwidth, 'burst_bandwidth' : relay.burst_bandwidth, 'observed_bandwidth' : relay.observed_bandwidth, 'link_protocols' : relay.link_protocols, 'circuit_protocols' : relay.circuit_protocols, 'is_hidden_service_dir' : relay.is_hidden_service_dir, 'hibernating' : relay.hibernating, 'allow_single_hop_exits' : relay.allow_single_hop_exits, 'allow_tunneled_dir_requests' : relay.allow_tunneled_dir_requests, 'extra_info_cache' : relay.extra_info_cache, 'extra_info_digest' : relay.extra_info_digest, 'extra_info_sha256_digest' : relay.extra_info_sha256_digest, 'eventdns' : relay.eventdns, 'ntor_onion_key' : relay.ntor_onion_key, 'protocols' : relay.protocols } if relay.exit_policy.is_exiting_allowed(): tor_map['exit'].append(data) else: tor_map['relay'].append(data) return tor_map if __name__ == '__main__': import json print('loading Tor descriptors... (this could take a while)') now = datetime.datetime.now(datetime.timezone.utc) way_back = now.replace(year=now.year-1) tor_data = get_descriptors() #tor_data = get_descriptors(way_back) with open('tor.json', 'w') as fd: json.dump(tor_data['relay'], fd) with open('tor.exit.json', 'w') as fd: json.dump(tor_data['exit'], fd) print('Relays: {0:,}'.format(len(tor_data['relay']))) print('Exits : {0:,}'.format(len(tor_data['exit']))) try: import ipinfo except ImportError: raise ImportError('missing optional library \'ipinfo\' (https://pypi.org/project/ipinfo/) for map visualization') try: handler = ipinfo.getHandler('changeme') # put your ipinfo.io API key here print('Relay Map: ' + handler.getMap([ip['address'] for ip in tor_data['relay']])) print('Exit Map: ' + handler.getMap([ip['address'] for ip in tor_data['exit']])) except ipinfo.errors.AuthorizationError: print('error: invalid ipinfo.io API key (https://ipinfo.io/signup)') except Exception as ex: print(f'error generating ipinfo map ({ex})')