Esempio n. 1
0
  def __init__(self, address: str, or_port: Union[int, str], dir_port: Union[int, str], fingerprint: str, nickname: str, orport_v6: Tuple[str, int]) -> None:
    identifier = '%s (%s)' % (fingerprint, nickname) if nickname else fingerprint

    if not connection.is_valid_ipv4_address(address):
      raise ValueError('%s has an invalid IPv4 address: %s' % (identifier, address))
    elif not connection.is_valid_port(or_port):
      raise ValueError('%s has an invalid ORPort: %s' % (identifier, or_port))
    elif not connection.is_valid_port(dir_port):
      raise ValueError('%s has an invalid DirPort: %s' % (identifier, dir_port))
    elif not tor_tools.is_valid_fingerprint(fingerprint):
      raise ValueError('%s has an invalid fingerprint: %s' % (identifier, fingerprint))
    elif nickname and not tor_tools.is_valid_nickname(nickname):
      raise ValueError('%s has an invalid nickname: %s' % (fingerprint, nickname))

    if orport_v6:
      if not isinstance(orport_v6, tuple) or len(orport_v6) != 2:
        raise ValueError('%s orport_v6 should be a two value tuple: %s' % (identifier, str(orport_v6)))
      elif not connection.is_valid_ipv6_address(orport_v6[0]):
        raise ValueError('%s has an invalid IPv6 address: %s' % (identifier, orport_v6[0]))
      elif not connection.is_valid_port(orport_v6[1]):
        raise ValueError('%s has an invalid IPv6 port: %s' % (identifier, orport_v6[1]))

    self.address = address
    self.or_port = int(or_port)
    self.dir_port = int(dir_port)
    self.fingerprint = fingerprint
    self.nickname = nickname
    self.orport_v6 = (orport_v6[0], int(orport_v6[1])) if orport_v6 else None
Esempio n. 2
0
    def exits_can_exit_to(self, host, port):
        '''
        Return exits that can MOST LIKELY exit to the given host:port. **host**
        can be a hostname, but be warned that we will resolve it locally and
        use the first (arbitrary/unknown order) result when checking exit
        policies, which is different than what other parts of the code may do
        (leaving it up to the exit to resolve the name).

        An exit can only MOST LIKELY not just because of the above DNS
        disconnect, but also because fundamentally our Tor client is most
        likely using microdescriptors which do not have full information about
        exit policies.
        '''
        c = self._controller
        if not is_valid_ipv4_address(host) and not is_valid_ipv6_address(host):
            # It certainly isn't perfect trying to guess if an exit can connect
            # to an ipv4/6 address based on the DNS result we got locally. But
            # it's the best we can do.
            #
            # Also, only use the first ipv4/6 we get even if there is more than
            # one.
            host = resolve(host)[0]
        assert is_valid_ipv4_address(host) or is_valid_ipv6_address(host)
        exits = []
        for exit in self.exits:
            # If we have the exit policy already, easy
            if exit.exit_policy:
                policy = exit.exit_policy
            else:
                # Otherwise ask Tor for the microdescriptor and assume the exit
                # won't work if the desc isn't available
                try:
                    fp = exit.fingerprint
                    policy = c.get_microdescriptor(fp).exit_policy
                except DescriptorUnavailable as e:
                    log.debug(e)
                    continue
            # There's a weird KeyError we sometimes hit when checking
            # policy.can_exit_to()... so catch that and log about it. Maybe
            # someday it can be fixed?
            try:
                if policy is not None and policy.can_exit_to(port=port):
                    exits.append(exit)
            except KeyError as e:
                log.exception('Got that KeyError in stem again...: %s', e)
                continue
        return exits
  def from_cache():
    """
    Provides fallback directory information cached with Stem. Unlike
    :func:`~stem.descriptor.remote.FallbackDirectory.from_remote` this doesn't
    have any system requirements, and is faster too. Only drawback is that
    these fallback directories are only as up to date as the Stem release we're
    using.

    :returns: **dict** of **str** fingerprints to their
      :class:`~stem.descriptor.remote.FallbackDirectory`
    """

    conf = stem.util.conf.Config()
    conf.load(CACHE_PATH)

    results = {}

    for fingerprint in set([key.split('.')[0] for key in conf.keys()]):
      if fingerprint in ('tor_commit', 'stem_commit'):
        continue

      attr = {}

      for attr_name in ('address', 'or_port', 'dir_port', 'orport6_address', 'orport6_port'):
        key = '%s.%s' % (fingerprint, attr_name)
        attr[attr_name] = conf.get(key)

        if not attr[attr_name] and not attr_name.startswith('orport6_'):
          raise IOError("'%s' is missing from %s" % (key, CACHE_PATH))

      if not connection.is_valid_ipv4_address(attr['address']):
        raise IOError("'%s.address' was an invalid IPv4 address (%s)" % (fingerprint, attr['address']))
      elif not connection.is_valid_port(attr['or_port']):
        raise IOError("'%s.or_port' was an invalid port (%s)" % (fingerprint, attr['or_port']))
      elif not connection.is_valid_port(attr['dir_port']):
        raise IOError("'%s.dir_port' was an invalid port (%s)" % (fingerprint, attr['dir_port']))
      elif attr['orport6_address'] and not connection.is_valid_ipv6_address(attr['orport6_address']):
        raise IOError("'%s.orport6_address' was an invalid IPv6 address (%s)" % (fingerprint, attr['orport6_address']))
      elif attr['orport6_port'] and not connection.is_valid_port(attr['orport6_port']):
        raise IOError("'%s.orport6_port' was an invalid port (%s)" % (fingerprint, attr['orport6_port']))

      if attr['orport6_address'] and attr['orport6_port']:
        orport_v6 = (attr['orport6_address'], int(attr['orport6_port']))
      else:
        orport_v6 = None

      results[fingerprint] = FallbackDirectory(
        address = attr['address'],
        or_port = int(attr['or_port']),
        dir_port = int(attr['dir_port']),
        fingerprint = fingerprint,
        orport_v6 = orport_v6,
      )

    return results
Esempio n. 4
0
  def from_cache():
    """
    Provides fallback directory information cached with Stem. Unlike
    :func:`~stem.descriptor.remote.FallbackDirectory.from_remote` this doesn't
    have any system requirements, and is faster too. Only drawback is that
    these fallback directories are only as up to date as the Stem release we're
    using.

    :returns: **dict** of **str** fingerprints to their
      :class:`~stem.descriptor.remote.FallbackDirectory`
    """

    conf = stem.util.conf.Config()
    conf.load(CACHE_PATH)

    results = {}

    for fingerprint in set([key.split('.')[0] for key in conf.keys()]):
      if fingerprint in ('tor_commit', 'stem_commit'):
        continue

      attr = {}

      for attr_name in ('address', 'or_port', 'dir_port', 'orport6_address', 'orport6_port'):
        key = '%s.%s' % (fingerprint, attr_name)
        attr[attr_name] = conf.get(key)

        if not attr[attr_name] and not attr_name.startswith('orport6_'):
          raise IOError("'%s' is missing from %s" % (key, CACHE_PATH))

      if not connection.is_valid_ipv4_address(attr['address']):
        raise IOError("'%s.address' was an invalid IPv4 address (%s)" % (fingerprint, attr['address']))
      elif not connection.is_valid_port(attr['or_port']):
        raise IOError("'%s.or_port' was an invalid port (%s)" % (fingerprint, attr['or_port']))
      elif not connection.is_valid_port(attr['dir_port']):
        raise IOError("'%s.dir_port' was an invalid port (%s)" % (fingerprint, attr['dir_port']))
      elif attr['orport6_address'] and not connection.is_valid_ipv6_address(attr['orport6_address']):
        raise IOError("'%s.orport6_address' was an invalid IPv6 address (%s)" % (fingerprint, attr['orport6_address']))
      elif attr['orport6_port'] and not connection.is_valid_port(attr['orport6_port']):
        raise IOError("'%s.orport6_port' was an invalid port (%s)" % (fingerprint, attr['orport6_port']))

      if attr['orport6_address'] and attr['orport6_port']:
        orport_v6 = (attr['orport6_address'], int(attr['orport6_port']))
      else:
        orport_v6 = None

      results[fingerprint] = FallbackDirectory(
        address = attr['address'],
        or_port = int(attr['or_port']),
        dir_port = int(attr['dir_port']),
        fingerprint = fingerprint,
        orport_v6 = orport_v6,
      )

    return results
Esempio n. 5
0
  def _parse(self):
    if self.type not in ('server', 'client'):
      raise stem.ProtocolError("Transport type should either be 'server' or 'client': %s" % self)

    if not connection.is_valid_ipv4_address(self.address) and \
       not connection.is_valid_ipv6_address(self.address):
      raise stem.ProtocolError("Transport address isn't a valid IPv4 or IPv6 address: %s" % self)

    if not connection.is_valid_port(self.port):
      raise stem.ProtocolError('Transport port is invalid: %s' % self)

    self.port = int(self.port)
Esempio n. 6
0
 def can_exit_to(self, host, port):
     '''
     Returns if this relay can MOST LIKELY exit to the given host:port.
     **host** can be a hostname, but be warned that we will resolve it
     locally and use the first (arbitrary/unknown order) result when
     checking exit policies, which is different than what other parts of the
     code may do (leaving it up to the exit to resolve the name).
     '''
     if not self.exit_policy:
         return False
     assert isinstance(host, str)
     assert isinstance(port, int)
     if not is_valid_ipv4_address(host) and not is_valid_ipv6_address(host):
         # It certainly isn't perfect trying to guess if an exit can connect
         # to an ipv4/6 address based on the DNS result we got locally. But
         # it's the best we can do.
         #
         # Also, only use the first ipv4/6 we get even if there is more than
         # one.
         host = resolve(host)[0]
     assert is_valid_ipv4_address(host) or is_valid_ipv6_address(host)
     return self.exit_policy.can_exit_to(host, port)
Esempio n. 7
0
    def _parse(self):
        if self.type not in ('server', 'client'):
            raise stem.ProtocolError(
                "Transport type should either be 'server' or 'client': %s" %
                self)

        if not connection.is_valid_ipv4_address(self.address) and \
           not connection.is_valid_ipv6_address(self.address):
            raise stem.ProtocolError(
                "Transport address isn't a valid IPv4 or IPv6 address: %s" %
                self)

        if not connection.is_valid_port(self.port):
            raise stem.ProtocolError('Transport port is invalid: %s' % self)

        self.port = int(self.port)
Esempio n. 8
0
    def from_remote(timeout=60):
        """
    Reads and parses tor's latest fallback directories `from
    gitweb.torproject.org
    <https://gitweb.torproject.org/tor.git/plain/src/or/fallback_dirs.inc>`_.
    Note that while convenient, this reliance on GitWeb means you should alway
    call with a fallback, such as...

    ::

      try:
        fallback_directories = stem.descriptor.remote.from_remote()
      except IOError:
        fallback_directories = stem.descriptor.remote.from_cache()

    :param int timeout: seconds to wait before timing out the request

    :returns: **dict** of **str** fingerprints to their
      :class:`~stem.descriptor.remote.FallbackDirectory`

    :raises: **IOError** if unable to retrieve the fallback directories
    """

        try:
            fallback_dir_page = str_tools._to_unicode(
                urllib.urlopen(GITWEB_FALLBACK_DIR_URL,
                               timeout=timeout).read())
        except:
            exc = sys.exc_info()[1]
            raise IOError(
                "Unable to download tor's fallback directories from %s: %s" %
                (GITWEB_FALLBACK_DIR_URL, exc))

        # Example of an entry...
        #
        #   "5.175.233.86:80 orport=443 id=5525D0429BFE5DC4F1B0E9DE47A4CFA169661E33"
        #   " ipv6=[2a03:b0c0:0:1010::a4:b001]:9001"
        #   " weight=43680",

        results, attr = {}, {}

        for line in fallback_dir_page.splitlines():
            if line.startswith('"'):
                addr_line_match = re.match(
                    '"([\d\.]+):(\d+) orport=(\d+) id=([\dA-F]{40}).*', line)
                ipv6_line_match = re.match('" ipv6=\[([\da-f:]+)\]:(\d+)"',
                                           line)

                if addr_line_match:
                    address, dir_port, or_port, fingerprint = addr_line_match.groups(
                    )

                    if not connection.is_valid_ipv4_address(address):
                        raise IOError('%s has an invalid IPv4 address: %s' %
                                      (fingerprint, address))
                    elif not connection.is_valid_port(or_port):
                        raise IOError('%s has an invalid or_port: %s' %
                                      (fingerprint, or_port))
                    elif not connection.is_valid_port(dir_port):
                        raise IOError('%s has an invalid dir_port: %s' %
                                      (fingerprint, dir_port))
                    elif not tor_tools.is_valid_fingerprint(fingerprint):
                        raise IOError('%s has an invalid fingerprint: %s' %
                                      (fingerprint, fingerprint))

                    attr = {
                        'address': address,
                        'or_port': int(or_port),
                        'dir_port': int(dir_port),
                        'fingerprint': fingerprint,
                    }
                elif ipv6_line_match:
                    address, port = ipv6_line_match.groups()

                    if not connection.is_valid_ipv6_address(address):
                        raise IOError('%s has an invalid IPv6 address: %s' %
                                      (fingerprint, address))
                    elif not connection.is_valid_port(port):
                        raise IOError(
                            '%s has an invalid ORPort for its IPv6 endpoint: %s'
                            % (fingerprint, port))

                    attr['orport_v6'] = (address, int(port))
                elif line.startswith('" weight=') and 'fingerprint' in attr:
                    results[attr.get('fingerprint')] = FallbackDirectory(
                        address=attr.get('address'),
                        or_port=attr.get('or_port'),
                        dir_port=attr.get('dir_port'),
                        fingerprint=attr.get('fingerprint'),
                        orport_v6=attr.get('orport_v6'),
                    )

                    attr = {}

        return results
Esempio n. 9
0
File: remote.py Progetto: E3V3A/stem
    def _parse_v2(fallback_dir_page):
        # Example of an entry...
        #
        #   "5.9.110.236:9030 orport=9001 id=0756B7CD4DFC8182BE23143FAC0642F515182CEB"
        #   " ipv6=[2a01:4f8:162:51e2::2]:9001"
        #   /* nickname=rueckgrat */
        #   /* extrainfo=1 */

        results, attr = {}, {}

        for line in fallback_dir_page.splitlines():
            addr_line_match = re.match(
                '"([\d\.]+):(\d+) orport=(\d+) id=([\dA-F]{40}).*', line)
            nickname_match = re.match('/\* nickname=(\S+) \*/', line)
            has_extrainfo_match = re.match('/\* extrainfo=([0-1]) \*/', line)
            ipv6_line_match = re.match('" ipv6=\[([\da-f:]+)\]:(\d+)"', line)

            if addr_line_match:
                address, dir_port, or_port, fingerprint = addr_line_match.groups(
                )

                if not connection.is_valid_ipv4_address(address):
                    raise IOError('%s has an invalid IPv4 address: %s' %
                                  (fingerprint, address))
                elif not connection.is_valid_port(or_port):
                    raise IOError('%s has an invalid or_port: %s' %
                                  (fingerprint, or_port))
                elif not connection.is_valid_port(dir_port):
                    raise IOError('%s has an invalid dir_port: %s' %
                                  (fingerprint, dir_port))
                elif not tor_tools.is_valid_fingerprint(fingerprint):
                    raise IOError('%s has an invalid fingerprint: %s' %
                                  (fingerprint, fingerprint))

                attr = {
                    'address': address,
                    'or_port': int(or_port),
                    'dir_port': int(dir_port),
                    'fingerprint': fingerprint,
                }
            elif ipv6_line_match:
                address, port = ipv6_line_match.groups()

                if not connection.is_valid_ipv6_address(address):
                    raise IOError('%s has an invalid IPv6 address: %s' %
                                  (fingerprint, address))
                elif not connection.is_valid_port(port):
                    raise IOError(
                        '%s has an invalid ORPort for its IPv6 endpoint: %s' %
                        (fingerprint, port))

                attr['orport_v6'] = (address, int(port))
            elif nickname_match:
                nickname = nickname_match.group(1)

                if not tor_tools.is_valid_nickname(nickname):
                    raise IOError('%s has an invalid nickname: %s' %
                                  (fingerprint, nickname))

                attr['nickname'] = nickname
            elif has_extrainfo_match:
                attr['has_extrainfo'] = has_extrainfo_match.group(1) == '1'

                results[attr.get('fingerprint')] = FallbackDirectory(
                    address=attr.get('address'),
                    or_port=attr.get('or_port'),
                    dir_port=attr.get('dir_port'),
                    fingerprint=attr.get('fingerprint'),
                    nickname=attr.get('nickname'),
                    has_extrainfo=attr.get('has_extrainfo', False),
                    orport_v6=attr.get('orport_v6'),
                )

                attr = {}

        return results
Esempio n. 10
0
File: remote.py Progetto: E3V3A/stem
    def _parse_v1(fallback_dir_page):
        # Example of an entry...
        #
        #   "5.175.233.86:80 orport=443 id=5525D0429BFE5DC4F1B0E9DE47A4CFA169661E33"
        #   " ipv6=[2a03:b0c0:0:1010::a4:b001]:9001"
        #   " weight=43680",

        # TODO: this method can be removed once gitweb provides a v2 formatted document

        results, attr = {}, {}

        for line in fallback_dir_page.splitlines():
            if line.startswith('"'):
                addr_line_match = re.match(
                    '"([\d\.]+):(\d+) orport=(\d+) id=([\dA-F]{40}).*', line)
                ipv6_line_match = re.match('" ipv6=\[([\da-f:]+)\]:(\d+)"',
                                           line)

                if addr_line_match:
                    address, dir_port, or_port, fingerprint = addr_line_match.groups(
                    )

                    if not connection.is_valid_ipv4_address(address):
                        raise IOError('%s has an invalid IPv4 address: %s' %
                                      (fingerprint, address))
                    elif not connection.is_valid_port(or_port):
                        raise IOError('%s has an invalid or_port: %s' %
                                      (fingerprint, or_port))
                    elif not connection.is_valid_port(dir_port):
                        raise IOError('%s has an invalid dir_port: %s' %
                                      (fingerprint, dir_port))
                    elif not tor_tools.is_valid_fingerprint(fingerprint):
                        raise IOError('%s has an invalid fingerprint: %s' %
                                      (fingerprint, fingerprint))

                    attr = {
                        'address': address,
                        'or_port': int(or_port),
                        'dir_port': int(dir_port),
                        'fingerprint': fingerprint,
                    }
                elif ipv6_line_match:
                    address, port = ipv6_line_match.groups()

                    if not connection.is_valid_ipv6_address(address):
                        raise IOError('%s has an invalid IPv6 address: %s' %
                                      (fingerprint, address))
                    elif not connection.is_valid_port(port):
                        raise IOError(
                            '%s has an invalid ORPort for its IPv6 endpoint: %s'
                            % (fingerprint, port))

                    attr['orport_v6'] = (address, int(port))
                elif line.startswith('" weight=') and 'fingerprint' in attr:
                    results[attr.get('fingerprint')] = FallbackDirectory(
                        address=attr.get('address'),
                        or_port=attr.get('or_port'),
                        dir_port=attr.get('dir_port'),
                        fingerprint=attr.get('fingerprint'),
                        orport_v6=attr.get('orport_v6'),
                    )

                    attr = {}

        return results
Esempio n. 11
0
  def from_remote(timeout = 60):
    """
    Reads and parses tor's latest fallback directories `from
    gitweb.torproject.org
    <https://gitweb.torproject.org/tor.git/plain/src/or/fallback_dirs.inc>`_.
    Note that while convenient, this reliance on GitWeb means you should alway
    call with a fallback, such as...

    ::

      try:
        fallback_directories = stem.descriptor.remote.from_remote()
      except IOError:
        fallback_directories = stem.descriptor.remote.from_cache()

    :param int timeout: seconds to wait before timing out the request

    :returns: **dict** of **str** fingerprints to their
      :class:`~stem.descriptor.remote.FallbackDirectory`

    :raises: **IOError** if unable to retrieve the fallback directories
    """

    try:
      fallback_dir_page = str_tools._to_unicode(urllib.urlopen(GITWEB_FALLBACK_DIR_URL, timeout = timeout).read())
    except:
      exc = sys.exc_info()[1]
      raise IOError("Unable to download tor's fallback directories from %s: %s" % (GITWEB_FALLBACK_DIR_URL, exc))

    # Example of an entry...
    #
    #   "5.175.233.86:80 orport=443 id=5525D0429BFE5DC4F1B0E9DE47A4CFA169661E33"
    #   " ipv6=[2a03:b0c0:0:1010::a4:b001]:9001"
    #   " weight=43680",

    results, attr = {}, {}

    for line in fallback_dir_page.splitlines():
      if line.startswith('"'):
        addr_line_match = re.match('"([\d\.]+):(\d+) orport=(\d+) id=([\dA-F]{40}).*', line)
        ipv6_line_match = re.match('" ipv6=\[([\da-f:]+)\]:(\d+)"', line)

        if addr_line_match:
          address, dir_port, or_port, fingerprint = addr_line_match.groups()

          if not connection.is_valid_ipv4_address(address):
            raise IOError('%s has an invalid IPv4 address: %s' % (fingerprint, address))
          elif not connection.is_valid_port(or_port):
            raise IOError('%s has an invalid or_port: %s' % (fingerprint, or_port))
          elif not connection.is_valid_port(dir_port):
            raise IOError('%s has an invalid dir_port: %s' % (fingerprint, dir_port))
          elif not tor_tools.is_valid_fingerprint(fingerprint):
            raise IOError('%s has an invalid fingerprint: %s' % (fingerprint, fingerprint))

          attr = {
            'address': address,
            'or_port': int(or_port),
            'dir_port': int(dir_port),
            'fingerprint': fingerprint,
          }
        elif ipv6_line_match:
          address, port = ipv6_line_match.groups()

          if not connection.is_valid_ipv6_address(address):
            raise IOError('%s has an invalid IPv6 address: %s' % (fingerprint, address))
          elif not connection.is_valid_port(port):
            raise IOError('%s has an invalid ORPort for its IPv6 endpoint: %s' % (fingerprint, port))

          attr['orport_v6'] = (address, int(port))
        elif line.startswith('" weight=') and 'fingerprint' in attr:
          results[attr.get('fingerprint')] = FallbackDirectory(
            address = attr.get('address'),
            or_port = attr.get('or_port'),
            dir_port = attr.get('dir_port'),
            fingerprint = attr.get('fingerprint'),
            orport_v6 = attr.get('orport_v6'),
          )

          attr = {}

    return results