def _parse_file(descriptor_file, validate=False, **kwargs):
    """
  Iterates over the hidden service descriptors in a file.

  :param file descriptor_file: file with descriptor content
  :param bool validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param dict kwargs: additional arguments for the descriptor constructor

  :returns: iterator for :class:`~stem.descriptor.hidden_service_descriptor.HiddenServiceDescriptor`
    instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is **True**
    * **IOError** if the file can't be read
  """

    while True:
        descriptor_content = _read_until_keywords('signature', descriptor_file)

        # we've reached the 'signature', now include the pgp style block
        block_end_prefix = PGP_BLOCK_END.split(' ', 1)[0]
        descriptor_content += _read_until_keywords(block_end_prefix,
                                                   descriptor_file, True)

        if descriptor_content:
            if descriptor_content[0].startswith(b'@type'):
                descriptor_content = descriptor_content[1:]

            yield HiddenServiceDescriptor(bytes.join(b'', descriptor_content),
                                          validate, **kwargs)
        else:
            break  # done parsing file
def _parse_file(descriptor_file, validate = False, **kwargs):
  """
  Iterates over the hidden service descriptors in a file.

  :param file descriptor_file: file with descriptor content
  :param bool validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param dict kwargs: additional arguments for the descriptor constructor

  :returns: iterator for :class:`~stem.descriptor.hidden_service_descriptor.HiddenServiceDescriptor`
    instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is **True**
    * **IOError** if the file can't be read
  """

  while True:
    descriptor_content = _read_until_keywords('signature', descriptor_file)

    # we've reached the 'signature', now include the pgp style block
    block_end_prefix = PGP_BLOCK_END.split(' ', 1)[0]
    descriptor_content += _read_until_keywords(block_end_prefix, descriptor_file, True)

    if descriptor_content:
      if descriptor_content[0].startswith(b'@type'):
        descriptor_content = descriptor_content[1:]

      yield HiddenServiceDescriptor(bytes.join(b'', descriptor_content), validate, **kwargs)
    else:
      break  # done parsing file
示例#3
0
def _parse_file(
        tordnsel_file: BinaryIO,
        validate: bool = False,
        **kwargs: Any) -> Iterator['stem.descriptor.tordnsel.TorDNSEL']:
    """
  Iterates over a tordnsel file.

  :returns: iterator for :class:`~stem.descriptor.tordnsel.TorDNSEL`
    instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is **True**
    * **OSError** if the file can't be read
  """

    if kwargs:
        raise ValueError("TorDNSEL doesn't support additional arguments: %s" %
                         kwargs)

    # skip content prior to the first ExitNode
    _read_until_keywords('ExitNode', tordnsel_file, skip=True)

    while True:
        contents = _read_until_keywords('ExitAddress', tordnsel_file)
        contents += _read_until_keywords('ExitNode', tordnsel_file)

        if contents:
            yield TorDNSEL(bytes.join(b'', contents), validate)
        else:
            break  # done parsing file
示例#4
0
def _parse_file(descriptor_file, is_bridge = False, validate = True, **kwargs):
  """
  Iterates over the extra-info descriptors in a file.

  :param file descriptor_file: file with descriptor content
  :param bool is_bridge: parses the file as being a bridge descriptor
  :param bool validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param dict kwargs: additional arguments for the descriptor constructor

  :returns: iterator for :class:`~stem.descriptor.extrainfo_descriptor.ExtraInfoDescriptor`
    instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is **True**
    * **IOError** if the file can't be read
  """

  while True:
    extrainfo_content = _read_until_keywords("router-signature", descriptor_file)

    # we've reached the 'router-signature', now include the pgp style block
    block_end_prefix = PGP_BLOCK_END.split(' ', 1)[0]
    extrainfo_content += _read_until_keywords(block_end_prefix, descriptor_file, True)

    if extrainfo_content:
      if is_bridge:
        yield BridgeExtraInfoDescriptor(bytes.join(b"", extrainfo_content), validate, **kwargs)
      else:
        yield RelayExtraInfoDescriptor(bytes.join(b"", extrainfo_content), validate, **kwargs)
    else:
      break  # done parsing file
def _parse_file(descriptor_file, is_bridge = False, validate = True, **kwargs):
  """
  Iterates over the extra-info descriptors in a file.

  :param file descriptor_file: file with descriptor content
  :param bool is_bridge: parses the file as being a bridge descriptor
  :param bool validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param dict kwargs: additional arguments for the descriptor constructor

  :returns: iterator for :class:`~stem.descriptor.extrainfo_descriptor.ExtraInfoDescriptor`
    instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is **True**
    * **IOError** if the file can't be read
  """

  while True:
    extrainfo_content = _read_until_keywords('router-signature', descriptor_file)

    # we've reached the 'router-signature', now include the pgp style block
    block_end_prefix = PGP_BLOCK_END.split(' ', 1)[0]
    extrainfo_content += _read_until_keywords(block_end_prefix, descriptor_file, True)

    if extrainfo_content:
      if extrainfo_content[0].startswith(b'@type'):
        extrainfo_content = extrainfo_content[1:]

      if is_bridge:
        yield BridgeExtraInfoDescriptor(bytes.join(b'', extrainfo_content), validate, **kwargs)
      else:
        yield RelayExtraInfoDescriptor(bytes.join(b'', extrainfo_content), validate, **kwargs)
    else:
      break  # done parsing file
示例#6
0
def _parse_file(descriptor_file: BinaryIO, validate: bool = False, **kwargs: Any) -> Iterator['stem.descriptor.microdescriptor.Microdescriptor']:
  """
  Iterates over the microdescriptors in a file.

  :param descriptor_file: file with descriptor content
  :param validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param kwargs: additional arguments for the descriptor constructor

  :returns: iterator for Microdescriptor instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is True
    * **IOError** if the file can't be read
  """

  if kwargs:
    raise ValueError('BUG: keyword arguments unused by microdescriptors')

  while True:
    annotations = _read_until_keywords('onion-key', descriptor_file)

    # read until we reach an annotation or onion-key line
    descriptor_lines = []

    # read the onion-key line, done if we're at the end of the document

    onion_key_line = descriptor_file.readline()

    if onion_key_line:
      descriptor_lines.append(onion_key_line)
    else:
      break

    while True:
      last_position = descriptor_file.tell()
      line = descriptor_file.readline()

      if not line:
        break  # EOF
      elif line.startswith(b'@') or line.startswith(b'onion-key'):
        descriptor_file.seek(last_position)
        break
      else:
        descriptor_lines.append(line)

    if descriptor_lines:
      if descriptor_lines[0].startswith(b'@type'):
        descriptor_lines = descriptor_lines[1:]

      # strip newlines from annotations
      annotations = list(map(bytes.strip, annotations))

      descriptor_text = bytes.join(b'', descriptor_lines)

      yield Microdescriptor(descriptor_text, validate, annotations)
    else:
      break  # done parsing descriptors
示例#7
0
def _parse_file(descriptor_file, validate=False, onion_address=None, **kwargs):
    while True:
        descriptor_content = _read_until_keywords('signature', descriptor_file)

        # we've reached the 'signature', now include the pgp style block
        block_end_prefix = PGP_BLOCK_END.split(' ', 1)[0]
        descriptor_content += _read_until_keywords(block_end_prefix,
                                                   descriptor_file, True)

        if descriptor_content:
            if descriptor_content[0].startswith(b'@type'):
                descriptor_content = descriptor_content[1:]

            yield Hsv3Descriptor(bytes.join(b'', descriptor_content),
                                 validate,
                                 onion_address=onion_address,
                                 **kwargs)
        else:
            break  # done parsing file
def _parse_file(descriptor_file, validate=True, **kwargs):
    """
  Iterates over the microdescriptors in a file.

  :param file descriptor_file: file with descriptor content
  :param bool validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param dict kwargs: additional arguments for the descriptor constructor

  :returns: iterator for Microdescriptor instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is True
    * **IOError** if the file can't be read
  """

    while True:
        annotations = _read_until_keywords("onion-key", descriptor_file)

        # read until we reach an annotation or onion-key line
        descriptor_lines = []

        # read the onion-key line, done if we're at the end of the document

        onion_key_line = descriptor_file.readline()

        if onion_key_line:
            descriptor_lines.append(onion_key_line)
        else:
            break

        while True:
            last_position = descriptor_file.tell()
            line = descriptor_file.readline()

            if not line:
                break  # EOF
            elif line.startswith(b"@") or line.startswith(b"onion-key"):
                descriptor_file.seek(last_position)
                break
            else:
                descriptor_lines.append(line)

        if descriptor_lines:
            if descriptor_lines[0].startswith(b"@type"):
                descriptor_lines = descriptor_lines[1:]

            # strip newlines from annotations
            annotations = map(bytes.strip, annotations)

            descriptor_text = bytes.join(b"", descriptor_lines)

            yield Microdescriptor(descriptor_text, validate, annotations, **kwargs)
        else:
            break  # done parsing descriptors
示例#9
0
def _parse_file(descriptor_file: BinaryIO, is_bridge = False, validate = False, **kwargs: Any) -> Iterator['stem.descriptor.extrainfo_descriptor.ExtraInfoDescriptor']:
  """
  Iterates over the extra-info descriptors in a file.

  :param descriptor_file: file with descriptor content
  :param is_bridge: parses the file as being a bridge descriptor
  :param validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param kwargs: additional arguments for the descriptor constructor

  :returns: iterator for :class:`~stem.descriptor.extrainfo_descriptor.ExtraInfoDescriptor`
    instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is **True**
    * **IOError** if the file can't be read
  """

  if kwargs:
    raise ValueError('BUG: keyword arguments unused by extrainfo descriptors')

  while True:
    if not is_bridge:
      extrainfo_content = _read_until_keywords('router-signature', descriptor_file)

      # we've reached the 'router-signature', now include the pgp style block

      block_end_prefix = PGP_BLOCK_END.split(' ', 1)[0]
      extrainfo_content += _read_until_keywords(block_end_prefix, descriptor_file, True)
    else:
      extrainfo_content = _read_until_keywords('router-digest', descriptor_file, True)

    if extrainfo_content:
      if extrainfo_content[0].startswith(b'@type'):
        extrainfo_content = extrainfo_content[1:]

      if is_bridge:
        yield BridgeExtraInfoDescriptor(bytes.join(b'', extrainfo_content), validate)
      else:
        yield RelayExtraInfoDescriptor(bytes.join(b'', extrainfo_content), validate)
    else:
      break  # done parsing file
def _parse_file(descriptor_file, desc_type=None, validate=False, **kwargs):
    """
  Iterates over the hidden service descriptors in a file.

  :param file descriptor_file: file with descriptor content
  :param class desc_type: BaseHiddenServiceDescriptor subclass
  :param bool validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param dict kwargs: additional arguments for the descriptor constructor

  :returns: iterator for :class:`~stem.descriptor.hidden_service.HiddenServiceDescriptorV2`
    instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is **True**
    * **IOError** if the file can't be read
  """

    if desc_type is None:
        desc_type = HiddenServiceDescriptorV2

    # Hidden service v3 ends with a signature line, whereas v2 has a pgp style
    # block following it.

    while True:
        descriptor_content = _read_until_keywords('signature', descriptor_file,
                                                  True)

        if desc_type == HiddenServiceDescriptorV2:
            block_end_prefix = PGP_BLOCK_END.split(' ', 1)[0]
            descriptor_content += _read_until_keywords(block_end_prefix,
                                                       descriptor_file, True)

        if descriptor_content:
            if descriptor_content[0].startswith(b'@type'):
                descriptor_content = descriptor_content[1:]

            yield desc_type(bytes.join(b'', descriptor_content), validate,
                            **kwargs)
        else:
            break  # done parsing file
示例#11
0
def _parse_file(descriptor_file, validate=True, **kwargs):
    """
  Iterates over the microdescriptors in a file.

  :param file descriptor_file: file with descriptor content
  :param bool validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param dict kwargs: additional arguments for the descriptor constructor

  :returns: iterator for Microdescriptor instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is True
    * **IOError** if the file can't be read
  """

    while True:
        annotations = _read_until_keywords("onion-key", descriptor_file)

        # read until we reach an annotation or onion-key line
        descriptor_lines = []

        # read the onion-key line, done if we're at the end of the document

        onion_key_line = descriptor_file.readline()

        if onion_key_line:
            descriptor_lines.append(onion_key_line)
        else:
            break

        while True:
            last_position = descriptor_file.tell()
            line = descriptor_file.readline()

            if not line:
                break  # EOF
            elif line.startswith(b"@") or line.startswith(b"onion-key"):
                descriptor_file.seek(last_position)
                break
            else:
                descriptor_lines.append(line)

        if descriptor_lines:
            # strip newlines from annotations
            annotations = map(bytes.strip, annotations)

            descriptor_text = bytes.join(b"", descriptor_lines)

            yield Microdescriptor(descriptor_text, validate, annotations,
                                  **kwargs)
        else:
            break  # done parsing descriptors
示例#12
0
  def _parse_introduction_points(content):
    """
    Provides the parsed list of IntroductionPoints for the unencrypted content.
    """

    introduction_points = []
    content_io = io.BytesIO(content)

    while True:
      content = b''.join(_read_until_keywords('introduction-point', content_io, ignore_first = True))

      if not content:
        break  # reached the end

      attr = dict(INTRODUCTION_POINTS_ATTR)
      entries = _get_descriptor_components(content, False)

      for keyword, values in list(entries.items()):
        value, block_type, block_contents = values[0]

        if keyword in SINGLE_INTRODUCTION_POINT_FIELDS and len(values) > 1:
          raise ValueError("'%s' can only appear once in an introduction-point block, but appeared %i times" % (keyword, len(values)))

        if keyword == 'introduction-point':
          attr['identifier'] = value
        elif keyword == 'ip-address':
          if not stem.util.connection.is_valid_ipv4_address(value):
            raise ValueError("'%s' is an invalid IPv4 address" % value)

          attr['address'] = value
        elif keyword == 'onion-port':
          if not stem.util.connection.is_valid_port(value):
            raise ValueError("'%s' is an invalid port" % value)

          attr['port'] = int(value)
        elif keyword == 'onion-key':
          attr['onion_key'] = block_contents
        elif keyword == 'service-key':
          attr['service_key'] = block_contents
        elif keyword == 'intro-authentication':
          auth_entries = []

          for auth_value, _, _ in values:
            if ' ' not in auth_value:
              raise ValueError("We expected 'intro-authentication [auth_type] [auth_data]', but had '%s'" % auth_value)

            auth_type, auth_data = auth_value.split(' ')[:2]
            auth_entries.append((auth_type, auth_data))

      introduction_points.append(IntroductionPoints(**attr))

    return introduction_points
示例#13
0
  def _parse_introduction_points(content):
    """
    Provides the parsed list of IntroductionPoints for the unencrypted content.
    """

    introduction_points = []
    content_io = io.BytesIO(content)

    while True:
      content = b''.join(_read_until_keywords('introduction-point', content_io, ignore_first = True))

      if not content:
        break  # reached the end

      attr = dict(INTRODUCTION_POINTS_ATTR)
      entries = _descriptor_components(content, False)

      for keyword, values in list(entries.items()):
        value, block_type, block_contents = values[0]

        if keyword in SINGLE_INTRODUCTION_POINT_FIELDS and len(values) > 1:
          raise ValueError("'%s' can only appear once in an introduction-point block, but appeared %i times" % (keyword, len(values)))

        if keyword == 'introduction-point':
          attr['identifier'] = value
        elif keyword == 'ip-address':
          if not stem.util.connection.is_valid_ipv4_address(value):
            raise ValueError("'%s' is an invalid IPv4 address" % value)

          attr['address'] = value
        elif keyword == 'onion-port':
          if not stem.util.connection.is_valid_port(value):
            raise ValueError("'%s' is an invalid port" % value)

          attr['port'] = int(value)
        elif keyword == 'onion-key':
          attr['onion_key'] = block_contents
        elif keyword == 'service-key':
          attr['service_key'] = block_contents
        elif keyword == 'intro-authentication':
          auth_entries = []

          for auth_value, _, _ in values:
            if ' ' not in auth_value:
              raise ValueError("We expected 'intro-authentication [auth_type] [auth_data]', but had '%s'" % auth_value)

            auth_type, auth_data = auth_value.split(' ')[:2]
            auth_entries.append((auth_type, auth_data))

      introduction_points.append(IntroductionPoints(**attr))

    return introduction_points
def _parse_file(tordnsel_file, validate = True, **kwargs):
  """
  Iterates over a tordnsel file.

  :returns: iterator for :class:`~stem.descriptor.tordnsel.TorDNSEL`
    instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is **True**
    * **IOError** if the file can't be read
  """

  # skip content prior to the first ExitNode
  _read_until_keywords('ExitNode', tordnsel_file, skip = True)

  while True:
    contents = _read_until_keywords('ExitAddress', tordnsel_file)
    contents += _read_until_keywords('ExitNode', tordnsel_file)

    if contents:
      yield TorDNSEL(bytes.join(b'', contents), validate, **kwargs)
    else:
      break  # done parsing file
示例#15
0
def _parse_file(tordnsel_file, validate=True, **kwargs):
    """
  Iterates over a tordnsel file.

  :returns: iterator for :class:`~stem.descriptor.tordnsel.TorDNSEL`
    instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is **True**
    * **IOError** if the file can't be read
  """

    # skip content prior to the first ExitNode
    _read_until_keywords('ExitNode', tordnsel_file, skip=True)

    while True:
        contents = _read_until_keywords('ExitAddress', tordnsel_file)
        contents += _read_until_keywords('ExitNode', tordnsel_file)

        if contents:
            yield TorDNSEL(bytes.join(b'', contents), validate, **kwargs)
        else:
            break  # done parsing file
示例#16
0
    def _parse_introduction_points(content):
        """
    Provides the parsed list of IntroductionPoints for the unencrypted content.
    """

        introduction_points = []
        content_io = io.BytesIO(content)

        while True:
            content = b''.join(
                _read_until_keywords('introduction-point',
                                     content_io,
                                     ignore_first=True))

            if not content:
                break  # reached the end

            attr = dict(INTRODUCTION_POINTS_ATTR)
            entries = _descriptor_components(content, False)

            for keyword, values in list(entries.items()):
                value, block_type, block_contents = values[0]
                if keyword in SINGLE_INTRODUCTION_POINT_FIELDS and len(
                        values) > 1:
                    raise ValueError(
                        "'%s' can only appear once in an introduction-point block, but appeared %i times"
                        % (keyword, len(values)))
                elif keyword == 'introduction-point':
                    attr['link_specifier'] = value
                elif keyword == 'onion-key':
                    attr['onion_key'] = value
                elif keyword == 'auth-key':
                    attr[
                        'auth_key'] = stem.descriptor.certificate.Ed25519Certificate.parse(
                            ''.join(block_contents.splitlines()[1:-1]))
                elif keyword == 'enc-key':
                    attr['enc_key'] = value
                elif keyword == 'enc-key-cert':
                    attr[
                        'enc_key_cert'] = stem.descriptor.certificate.Ed25519Certificate.parse(
                            ''.join(block_contents.splitlines()[1:-1]))
                elif keyword == 'legacy-key':
                    attr['legacy_key'] = block_contents
                elif keyword == 'legacy-key-cert':
                    attr['legacy_key_cert'] = block_contents

            introduction_points.append(IntroductionPoints(**attr))

        return introduction_points
示例#17
0
def _parse_file(descriptor_file, is_bridge=False, validate=True, **kwargs):
    """
  Iterates over the server descriptors in a file.

  :param file descriptor_file: file with descriptor content
  :param bool is_bridge: parses the file as being a bridge descriptor
  :param bool validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param dict kwargs: additional arguments for the descriptor constructor

  :returns: iterator for ServerDescriptor instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is True
    * **IOError** if the file can't be read
  """

    # Handler for relay descriptors
    #
    # Cached descriptors consist of annotations followed by the descriptor
    # itself. For instance...
    #
    #   @downloaded-at 2012-03-14 16:31:05
    #   @source "145.53.65.130"
    #   router caerSidi 71.35.143.157 9001 0 0
    #   platform Tor 0.2.1.30 on Linux x86_64
    #   <rest of the descriptor content>
    #   router-signature
    #   -----BEGIN SIGNATURE-----
    #   <signature for the above descriptor>
    #   -----END SIGNATURE-----
    #
    # Metrics descriptor files are the same, but lack any annotations. The
    # following simply does the following...
    #
    #   - parse as annotations until we get to "router"
    #   - parse as descriptor content until we get to "router-signature" followed
    #     by the end of the signature block
    #   - construct a descriptor and provide it back to the caller
    #
    # Any annotations after the last server descriptor is ignored (never provided
    # to the caller).

    while True:
        annotations = _read_until_keywords("router", descriptor_file)
        descriptor_content = _read_until_keywords("router-signature",
                                                  descriptor_file)

        # we've reached the 'router-signature', now include the pgp style block
        block_end_prefix = PGP_BLOCK_END.split(' ', 1)[0]
        descriptor_content += _read_until_keywords(block_end_prefix,
                                                   descriptor_file, True)

        if descriptor_content:
            # strip newlines from annotations
            annotations = map(bytes.strip, annotations)

            descriptor_text = bytes.join(b"", descriptor_content)

            if is_bridge:
                yield BridgeDescriptor(descriptor_text, validate, annotations,
                                       **kwargs)
            else:
                yield RelayDescriptor(descriptor_text, validate, annotations,
                                      **kwargs)
        else:
            if validate and annotations:
                orphaned_annotations = stem.util.str_tools._to_unicode(
                    b'\n'.join(annotations))
                raise ValueError(
                    'Content conform to being a server descriptor:\n%s' %
                    orphaned_annotations)

            break  # done parsing descriptors
示例#18
0
def _parse_file(document_file,
                validate,
                entry_class,
                entry_keyword='r',
                start_position=None,
                end_position=None,
                section_end_keywords=(),
                extra_args=()):
    """
  Reads a range of the document_file containing some number of entry_class
  instances. We deliminate the entry_class entries by the keyword on their
  first line (entry_keyword). When finished the document is left at the
  end_position.

  Either an end_position or section_end_keywords must be provided.

  :param file document_file: file with network status document content
  :param bool validate: checks the validity of the document's contents if
    **True**, skips these checks otherwise
  :param class entry_class: class to construct instance for
  :param str entry_keyword: first keyword for the entry instances
  :param int start_position: start of the section, default is the current position
  :param int end_position: end of the section
  :param tuple section_end_keywords: keyword(s) that deliminate the end of the
    section if no end_position was provided
  :param tuple extra_args: extra arguments for the entry_class (after the
    content and validate flag)

  :returns: iterator over entry_class instances

  :raises:
    * **ValueError** if the contents is malformed and validate is **True**
    * **IOError** if the file can't be read
  """

    if start_position:
        document_file.seek(start_position)
    else:
        start_position = document_file.tell()

    # check if we're starting at the end of the section (ie, there's no entries to read)
    if section_end_keywords:
        first_keyword = None
        line_match = KEYWORD_LINE.match(
            stem.util.str_tools._to_unicode(document_file.readline()))

        if line_match:
            first_keyword = line_match.groups()[0]

        document_file.seek(start_position)

        if first_keyword in section_end_keywords:
            return

    while end_position is None or document_file.tell() < end_position:
        desc_lines, ending_keyword = _read_until_keywords(
            (entry_keyword, ) + section_end_keywords,
            document_file,
            ignore_first=True,
            end_position=end_position,
            include_ending_keyword=True)

        desc_content = bytes.join(b'', desc_lines)

        if desc_content:
            yield entry_class(desc_content, validate, *extra_args)

            # check if we stopped at the end of the section
            if ending_keyword in section_end_keywords:
                break
        else:
            break
示例#19
0
def _parse_file(document_file, validate, entry_class, entry_keyword = "r", start_position = None, end_position = None, section_end_keywords = (), extra_args = ()):
  """
  Reads a range of the document_file containing some number of entry_class
  instances. We deliminate the entry_class entries by the keyword on their
  first line (entry_keyword). When finished the document is left at the
  end_position.

  Either an end_position or section_end_keywords must be provided.

  :param file document_file: file with network status document content
  :param bool validate: checks the validity of the document's contents if
    **True**, skips these checks otherwise
  :param class entry_class: class to construct instance for
  :param str entry_keyword: first keyword for the entry instances
  :param int start_position: start of the section, default is the current position
  :param int end_position: end of the section
  :param tuple section_end_keywords: keyword(s) that deliminate the end of the
    section if no end_position was provided
  :param tuple extra_args: extra arguments for the entry_class (after the
    content and validate flag)

  :returns: iterator over entry_class instances

  :raises:
    * **ValueError** if the contents is malformed and validate is **True**
    * **IOError** if the file can't be read
  """

  if start_position:
    document_file.seek(start_position)
  else:
    start_position = document_file.tell()

  # check if we're starting at the end of the section (ie, there's no entries to read)
  if section_end_keywords:
    first_keyword = None
    line_match = KEYWORD_LINE.match(stem.util.str_tools._to_unicode(document_file.readline()))

    if line_match:
      first_keyword = line_match.groups()[0]

    document_file.seek(start_position)

    if first_keyword in section_end_keywords:
      return

  while end_position is None or document_file.tell() < end_position:
    desc_lines, ending_keyword = _read_until_keywords(
      (entry_keyword,) + section_end_keywords,
      document_file,
      ignore_first = True,
      end_position = end_position,
      include_ending_keyword = True
    )

    desc_content = bytes.join(b"", desc_lines)

    if desc_content:
      yield entry_class(desc_content, validate, *extra_args)

      # check if we stopped at the end of the section
      if ending_keyword in section_end_keywords:
        break
    else:
      break
def _parse_file(descriptor_file, is_bridge = False, validate = True, **kwargs):
  """
  Iterates over the server descriptors in a file.

  :param file descriptor_file: file with descriptor content
  :param bool is_bridge: parses the file as being a bridge descriptor
  :param bool validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param dict kwargs: additional arguments for the descriptor constructor

  :returns: iterator for ServerDescriptor instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is True
    * **IOError** if the file can't be read
  """

  # Handler for relay descriptors
  #
  # Cached descriptors consist of annotations followed by the descriptor
  # itself. For instance...
  #
  #   @downloaded-at 2012-03-14 16:31:05
  #   @source "145.53.65.130"
  #   router caerSidi 71.35.143.157 9001 0 0
  #   platform Tor 0.2.1.30 on Linux x86_64
  #   <rest of the descriptor content>
  #   router-signature
  #   -----BEGIN SIGNATURE-----
  #   <signature for the above descriptor>
  #   -----END SIGNATURE-----
  #
  # Metrics descriptor files are the same, but lack any annotations. The
  # following simply does the following...
  #
  #   - parse as annotations until we get to 'router'
  #   - parse as descriptor content until we get to 'router-signature' followed
  #     by the end of the signature block
  #   - construct a descriptor and provide it back to the caller
  #
  # Any annotations after the last server descriptor is ignored (never provided
  # to the caller).

  while True:
    annotations = _read_until_keywords('router', descriptor_file)
    descriptor_content = _read_until_keywords('router-signature', descriptor_file)

    # we've reached the 'router-signature', now include the pgp style block
    block_end_prefix = PGP_BLOCK_END.split(' ', 1)[0]
    descriptor_content += _read_until_keywords(block_end_prefix, descriptor_file, True)

    if descriptor_content:
      if descriptor_content[0].startswith(b'@type'):
        descriptor_content = descriptor_content[1:]

      # strip newlines from annotations
      annotations = map(bytes.strip, annotations)

      descriptor_text = bytes.join(b'', descriptor_content)

      if is_bridge:
        yield BridgeDescriptor(descriptor_text, validate, annotations, **kwargs)
      else:
        yield RelayDescriptor(descriptor_text, validate, annotations, **kwargs)
    else:
      if validate and annotations:
        orphaned_annotations = stem.util.str_tools._to_unicode(b'\n'.join(annotations))
        raise ValueError('Content conform to being a server descriptor:\n%s' % orphaned_annotations)

      break  # done parsing descriptors
示例#21
0
def _parse_file(
    descriptor_file: BinaryIO,
    is_bridge: bool = False,
    validate: bool = False,
    **kwargs: Any
) -> Iterator['stem.descriptor.server_descriptor.ServerDescriptor']:
    """
  Iterates over the server descriptors in a file.

  :param descriptor_file: file with descriptor content
  :param is_bridge: parses the file as being a bridge descriptor
  :param validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param kwargs: additional arguments for the descriptor constructor

  :returns: iterator for ServerDescriptor instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is True
    * **IOError** if the file can't be read
  """

    # Handler for relay descriptors
    #
    # Cached descriptors consist of annotations followed by the descriptor
    # itself. For instance...
    #
    #   @downloaded-at 2012-03-14 16:31:05
    #   @source "145.53.65.130"
    #   router caerSidi 71.35.143.157 9001 0 0
    #   platform Tor 0.2.1.30 on Linux x86_64
    #   <rest of the descriptor content>
    #   router-signature
    #   -----BEGIN SIGNATURE-----
    #   <signature for the above descriptor>
    #   -----END SIGNATURE-----
    #
    # Metrics descriptor files are the same, but lack any annotations. The
    # following simply does the following...
    #
    #   - parse as annotations until we get to 'router'
    #   - parse as descriptor content until we get to 'router-signature' followed
    #     by the end of the signature block
    #   - construct a descriptor and provide it back to the caller
    #
    # Any annotations after the last server descriptor is ignored (never provided
    # to the caller).

    while True:
        # skip annotations

        while True:
            pos = descriptor_file.tell()

            if not descriptor_file.readline().startswith(b'@'):
                descriptor_file.seek(pos)
                break

        if not is_bridge:
            descriptor_content = _read_until_keywords('router-signature',
                                                      descriptor_file)

            # we've reached the 'router-signature', now include the pgp style block

            block_end_prefix = PGP_BLOCK_END.split(' ', 1)[0]
            descriptor_content += _read_until_keywords(block_end_prefix,
                                                       descriptor_file, True)
        else:
            descriptor_content = _read_until_keywords('router-digest',
                                                      descriptor_file, True)

        if descriptor_content:
            if descriptor_content[0].startswith(b'@type'):
                descriptor_content = descriptor_content[1:]

            descriptor_text = bytes.join(b'', descriptor_content)

            if is_bridge:
                if kwargs:
                    raise ValueError(
                        'BUG: keyword arguments unused by bridge descriptors')

                yield BridgeDescriptor(descriptor_text, validate)
            else:
                yield RelayDescriptor(descriptor_text, validate, **kwargs)
        else:
            break  # done parsing descriptors