Download Tor Descriptors
************************

[image]

Tor relays provide a mirror for the tor relay descriptors it has
cached. These are available from its ORPort using Tor’s wire protocol,
and optionally with http as well from a DirPort.

   """
   Simple script to dowload a descriptor from Tor's ORPort or DirPort.
   """

   import collections
   import getopt
   import sys

   import stem
   import stem.descriptor.remote
   import stem.util.connection
   import stem.util.tor_tools

   # By default downloading moria1's server descriptor from itself.

   DEFAULT_ARGS = {
     'descriptor_type': 'server',
     'fingerprint': '9695DFC35FFEB861329B9F1AB04C46397020CE31',
     'download_from': stem.DirPort('128.31.0.34', 9131),
     'print_help': False,
   }

   VALID_TYPES = ('server', 'extrainfo', 'consensus')

   HELP_TEXT = """\
   Downloads a descriptor through Tor's ORPort or DirPort.

     -t, --type TYPE                 descriptor type to download, options are:
                                       %s
     -f, --fingerprint FP            relay to download the descriptor of
         --orport ADDRESS:PORT       ORPort to download from
         --dirport ADDRESS:PORT      DirPort to download from
     -h, --help                      presents this help
   """ % ', '.join(VALID_TYPES)


   def parse(argv):
     """
     Parses our arguments, providing a named tuple with their values.

     :param list argv: input arguments to be parsed

     :returns: a **named tuple** with our parsed arguments

     :raises: **ValueError** if we got an invalid argument
     """

     args = dict(DEFAULT_ARGS)

     try:
       recognized_args, unrecognized_args = getopt.getopt(argv, 't:f:h', ['type=', 'fingerprint=', 'orport=', 'dirport=', 'help'])

       if unrecognized_args:
         raise getopt.GetoptError("'%s' aren't recognized arguments" % "', '".join(unrecognized_args))
     except Exception as exc:
       raise ValueError('%s (for usage provide --help)' % exc)

     for opt, arg in recognized_args:
       if opt in ('-t', '--type'):
         if arg not in VALID_TYPES:
           raise ValueError("'%s' isn't a recognized decriptor type, options are: %s" % (arg, ', '.join(VALID_TYPES)))

         args['descriptor_type'] = arg
       elif opt in ('-f', '--fingerprint'):
         if not stem.util.tor_tools.is_valid_fingerprint(arg):
           raise ValueError("'%s' isn't a relay fingerprint" % arg)

         args['fingerprint'] = arg
       elif opt in ('--orport', '--dirport'):
         if ':' not in arg:
           raise ValueError("'%s' should be of the form 'address:port'" % arg)

         address, port = arg.rsplit(':', 1)

         if not stem.util.connection.is_valid_ipv4_address(address):
           raise ValueError("'%s' isn't a valid IPv4 address" % address)
         elif not stem.util.connection.is_valid_port(port):
           raise ValueError("'%s' isn't a valid port number" % port)

         endpoint_class = stem.ORPort if opt == '--orport' else stem.DirPort
         args['download_from'] = endpoint_class(address, port)
       elif opt in ('-h', '--help'):
         args['print_help'] = True

     # translates our args dict into a named tuple

     Args = collections.namedtuple('Args', args.keys())
     return Args(**args)


   def main():
     try:
       args = parse(sys.argv[1:])
     except ValueError as exc:
       print(exc)
       sys.exit(1)

     if args.print_help:
       print(HELP_TEXT)
       sys.exit()

     print('Downloading %s descriptor from %s:%s...\n' % (args.descriptor_type, args.download_from.address, args.download_from.port))
     desc = None

     if args.descriptor_type in ('server', 'extrainfo'):
       if args.descriptor_type == 'server':
         download_func = stem.descriptor.remote.get_server_descriptors
       else:
         download_func = stem.descriptor.remote.get_extrainfo_descriptors

       desc = download_func(
         fingerprints = [args.fingerprint],
         endpoints = [args.download_from],
       ).run()[0]
     elif args.descriptor_type == 'consensus':
       for consensus_desc in stem.descriptor.remote.get_consensus(endpoints = [args.download_from]):
         if consensus_desc.fingerprint == args.fingerprint:
           desc = consensus_desc
           break

       if not desc:
         print('Unable to find a descriptor for %s in the consensus' % args.fingerprint)
         sys.exit(1)
     else:
       print("'%s' is not a recognized descriptor type, options are: %s" % (args.descriptor_type, ', '.join(VALID_TYPES)))
       sys.exit(1)

     print(desc)

   if __name__ == '__main__':
     main()

   % python download_descriptor.py --type consensus --dirport 128.31.0.34:9131
   Downloading consensus descriptor from 128.31.0.34:9131...

   r moria1 lpXfw1/+uGEym58asExGOXAgzjE IpcU7dolas8+Q+oAzwgvZIWx7PA 2018-05-23 02:41:25 128.31.0.34 9101 9131
   s Authority Fast Running Stable V2Dir Valid
   v Tor 0.3.3.5-rc-dev
   pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2
   w Bandwidth=20 Unmeasured=1
   p reject 1-65535
