Exemple #1
0
    def use_directory_mirrors(
            self) -> stem.descriptor.networkstatus.NetworkStatusDocumentV3:
        """
    Downloads the present consensus and configures ourselves to use directory
    mirrors, in addition to authorities.

    :returns: :class:`~stem.descriptor.networkstatus.NetworkStatusDocumentV3`
      from which we got the directory mirrors

    :raises: **Exception** if unable to determine the directory mirrors
    """

        directories = [
            auth for auth in stem.directory.Authority.from_cache().values()
            if auth.nickname not in DIR_PORT_BLACKLIST
        ]
        new_endpoints = set([
            stem.DirPort(directory.address, directory.dir_port)
            for directory in directories
        ])

        consensus = list(
            self.get_consensus(
                document_handler=stem.descriptor.DocumentHandler.DOCUMENT).run(
                ))[0]  # type: ignore

        for desc in consensus.routers.values():
            if stem.Flag.V2DIR in desc.flags and desc.dir_port:
                new_endpoints.add(stem.DirPort(desc.address, desc.dir_port))

        # we need our endpoints to be a list rather than set for random.choice()

        self._endpoints = list(new_endpoints)

        return consensus
Exemple #2
0
    def test_query_download(self):
        """
    Check Query functionality when we successfully download a descriptor.
    """

        query = stem.descriptor.remote.Query(
            TEST_RESOURCE,
            'server-descriptor 1.0',
            endpoints=[stem.DirPort('128.31.0.39', 9131)],
            compression=Compression.PLAINTEXT,
            validate=True,
            skip_crypto_validation=not test.require.CRYPTOGRAPHY_AVAILABLE,
        )

        self.assertEqual(stem.DirPort('128.31.0.39', 9131),
                         query._pick_endpoint())

        descriptors = list(query)
        self.assertEqual(1, len(descriptors))
        desc = descriptors[0]

        self.assertEqual('moria1', desc.nickname)
        self.assertEqual('128.31.0.34', desc.address)
        self.assertEqual('9695DFC35FFEB861329B9F1AB04C46397020CE31',
                         desc.fingerprint)
        self.assertEqual(TEST_DESCRIPTOR, desc.get_bytes())
Exemple #3
0
    def test_equality(self):
        self.assertTrue(
            stem.ORPort('12.34.56.78', 80) == stem.ORPort('12.34.56.78', 80))
        self.assertTrue(
            stem.ORPort('12.34.56.78', 80, [1, 2, 3]) == stem.ORPort(
                '12.34.56.78', 80, [1, 2, 3]))
        self.assertFalse(
            stem.ORPort('12.34.56.78', 80) == stem.ORPort('12.34.56.88', 80))
        self.assertFalse(
            stem.ORPort('12.34.56.78', 80) == stem.ORPort('12.34.56.78', 443))
        self.assertFalse(
            stem.ORPort('12.34.56.78', 80, [2, 3]) == stem.ORPort(
                '12.34.56.78', 80, [1, 2, 3]))

        self.assertTrue(
            stem.DirPort('12.34.56.78', 80) == stem.DirPort('12.34.56.78', 80))
        self.assertFalse(
            stem.DirPort('12.34.56.78', 80) == stem.DirPort('12.34.56.88', 80))
        self.assertFalse(
            stem.DirPort('12.34.56.78', 80) == stem.DirPort(
                '12.34.56.78', 443))

        self.assertFalse(
            stem.ORPort('12.34.56.78', 80) == stem.DirPort('12.34.56.78', 80))
        self.assertFalse(
            stem.DirPort('12.34.56.78', 80) == stem.ORPort('12.34.56.78', 80))
Exemple #4
0
  def test_malformed_content(self):
    """
    Query with malformed descriptor content.
    """

    query = stem.descriptor.remote.Query(
      TEST_RESOURCE,
      'server-descriptor 1.0',
      endpoints = [stem.DirPort('128.31.0.39', 9131)],
      compression = Compression.PLAINTEXT,
      validate = True,
    )

    # checking via the iterator

    descriptors = list(query)
    self.assertEqual(0, len(descriptors))
    self.assertEqual(ValueError, type(query.error))
    self.assertEqual("Descriptor must have a 'router' entry", str(query.error))

    # check via the run() method

    self.assertRaises(ValueError, query.run)

    query.stop()
Exemple #5
0
  def test_using_dirport(self):
    """
    Download a descriptor through the DirPort.
    """

    reply = stem.descriptor.remote.their_server_descriptor(
      endpoints = [stem.DirPort('12.34.56.78', 1100)],
      validate = True,
    )

    self.assertEqual(1, len(list(reply)))
    self.assertEqual('moria1', list(reply)[0].nickname)
    self.assertEqual(5, len(reply.reply_headers))
Exemple #6
0
    def test_downloading_via_dirport(self):
        moria1 = stem.directory.Authority.from_cache()['moria1']

        desc = list(
            stem.descriptor.remote.their_server_descriptor(
                endpoints=[stem.DirPort(moria1.address, moria1.dir_port)],
                fall_back_to_authority=False,
            ).run())[0]

        self.assertEqual('moria1', desc.nickname)
        self.assertTrue(
            isinstance(desc,
                       stem.descriptor.server_descriptor.ServerDescriptor))
Exemple #7
0
    def test_constructor(self):
        endpoint = stem.ORPort('12.34.56.78', 80)
        self.assertEqual('12.34.56.78', endpoint.address)
        self.assertEqual(80, endpoint.port)
        self.assertEqual(None, endpoint.link_protocols)

        endpoint = stem.ORPort('12.34.56.78', 80, [3])
        self.assertEqual('12.34.56.78', endpoint.address)
        self.assertEqual(80, endpoint.port)
        self.assertEqual([3], endpoint.link_protocols)

        endpoint = stem.DirPort('12.34.56.78', 80)
        self.assertEqual('12.34.56.78', endpoint.address)
        self.assertEqual(80, endpoint.port)
Exemple #8
0
    def test_can_iterate_multiple_times(self):
        query = stem.descriptor.remote.Query(
            TEST_RESOURCE,
            'server-descriptor 1.0',
            endpoints=[stem.DirPort('128.31.0.39', 9131)],
            compression=Compression.PLAINTEXT,
            validate=True,
            skip_crypto_validation=not test.require.CRYPTOGRAPHY_AVAILABLE,
        )

        # check that iterating over the query provides the descriptors each time

        self.assertEqual(1, len(list(query)))
        self.assertEqual(1, len(list(query)))
        self.assertEqual(1, len(list(query)))
Exemple #9
0
  def _pick_endpoint(self, use_authority = False):
    """
    Provides an endpoint to query. If we have multiple endpoints then one
    is picked at random.

    :param bool use_authority: ignores our endpoints and uses a directory
      authority instead

    :returns: **str** for the url being queried by this request
    """

    if use_authority or not self.endpoints:
      picked = random.choice([auth for auth in stem.directory.Authority.from_cache().values() if auth.nickname not in ('tor26', 'Bifroest')])
      return stem.DirPort(picked.address, picked.dir_port)
    else:
      return random.choice(self.endpoints)
Exemple #10
0
  def _pick_endpoint(self, use_authority = False):
    """
    Provides an endpoint to query. If we have multiple endpoints then one
    is picked at random.

    :param bool use_authority: ignores our endpoints and uses a directory
      authority instead

    :returns: :class:`stem.Endpoint` for the location to be downloaded
      from by this request
    """

    if use_authority or not self.endpoints:
      picked = random.choice([auth for auth in stem.directory.Authority.from_cache().values() if auth.nickname not in DIR_PORT_BLACKLIST])
      return stem.DirPort(picked.address, picked.dir_port)
    else:
      return random.choice(self.endpoints)
Exemple #11
0
    def test_using_authorities(self):
        """
    Fetches a descriptor from each of the directory authorities. This is
    intended to check that DIRECTORY_AUTHORITIES is still up to date (that
    addresses and ports haven't changed).

    This is hardcoded to fetch moria1's descriptor. If its fingerprint changes
    then this test will need to be updated.
    """

        queries = []

        for nickname, authority in stem.directory.Authority.from_cache().items(
        ):
            if nickname in stem.descriptor.remote.DIR_PORT_BLACKLIST:
                continue

            queries.append((stem.descriptor.remote.Query(
                '/tor/server/fp/9695DFC35FFEB861329B9F1AB04C46397020CE31',
                'server-descriptor 1.0',
                endpoints=[
                    stem.DirPort(authority.address, authority.dir_port)
                ],
                timeout=30,
                validate=True,
            ), authority))

        for query, authority in queries:
            try:
                descriptors = list(query.run())
            except Exception as exc:
                for query, _ in queries:
                    query.stop()

                self.fail('Unable to use %s (%s:%s, %s): %s' %
                          (authority.nickname, authority.address,
                           authority.dir_port, type(exc), exc))
            finally:
                query.stop()

            self.assertEqual(1, len(descriptors))
            self.assertEqual('moria1', descriptors[0].nickname)
Exemple #12
0
    def get_vote(self, authority, **query_args):
        """
    Provides the present vote for a given directory authority.

    :param stem.directory.Authority authority: authority for which to retrieve a vote for
    :param query_args: additional arguments for the
      :class:`~stem.descriptor.remote.Query` constructor

    :returns: :class:`~stem.descriptor.remote.Query` for the router status
      entries
    """

        resource = '/tor/status-vote/current/authority'

        if 'endpoint' not in query_args:
            query_args['endpoints'] = [
                stem.DirPort(authority.address, authority.dir_port)
            ]

        return self.query(resource, **query_args)
Exemple #13
0
    def test_query_with_timeout(self, dirport_mock):
        def urlopen_call(*args, **kwargs):
            time.sleep(0.06)
            raise socket.timeout('connection timed out')

        dirport_mock.side_effect = urlopen_call

        query = stem.descriptor.remote.Query(
            TEST_RESOURCE,
            'server-descriptor 1.0',
            endpoints=[stem.DirPort('128.31.0.39', 9131)],
            fall_back_to_authority=False,
            timeout=0.1,
            validate=True,
        )

        # After two requests we'll have reached our total permissable timeout.
        # It would be nice to check that we don't make a third, but this
        # assertion has proved unreliable so only checking for the exception.

        self.assertRaises(stem.DownloadTimeout, query.run)
Exemple #14
0
  def __init__(self, resource, descriptor_type = None, endpoints = None, compression = None, retries = 2, fall_back_to_authority = False, timeout = None, start = True, block = False, validate = False, document_handler = stem.descriptor.DocumentHandler.ENTRIES, **kwargs):
    if not resource.startswith('/'):
      raise ValueError("Resources should start with a '/': %s" % resource)

    if resource.endswith('.z'):
      compression = [Compression.GZIP]
      resource = resource[:-2]
    elif compression is None:
      compression = [Compression.PLAINTEXT]
    else:
      if isinstance(compression, str):
        compression = [compression]  # caller provided only a single option

      if Compression.ZSTD in compression and not stem.prereq.is_zstd_available():
        compression.remove(Compression.ZSTD)

      if Compression.LZMA in compression and not stem.prereq.is_lzma_available():
        compression.remove(Compression.LZMA)

      if not compression:
        compression = [Compression.PLAINTEXT]

    if descriptor_type:
      self.descriptor_type = descriptor_type
    else:
      self.descriptor_type = _guess_descriptor_type(resource)

    self.endpoints = []

    if endpoints:
      for endpoint in endpoints:
        if isinstance(endpoint, tuple) and len(endpoint) == 2:
          self.endpoints.append(stem.DirPort(endpoint[0], endpoint[1]))  # TODO: remove this in stem 2.0
        elif isinstance(endpoint, (stem.ORPort, stem.DirPort)):
          self.endpoints.append(endpoint)
        else:
          raise ValueError("Endpoints must be an stem.ORPort, stem.DirPort, or two value tuple. '%s' is a %s." % (endpoint, type(endpoint).__name__))

    self.resource = resource
    self.compression = compression
    self.retries = retries
    self.fall_back_to_authority = fall_back_to_authority

    self.content = None
    self.error = None
    self.is_done = False
    self.download_url = None

    self.start_time = None
    self.timeout = timeout
    self.runtime = None

    self.validate = validate
    self.document_handler = document_handler
    self.reply_headers = None
    self.kwargs = kwargs

    self._downloader_thread = None
    self._downloader_thread_lock = threading.RLock()

    if start:
      self.start()

    if block:
      self.run(True)
Exemple #15
0
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)