Пример #1
0
def _parse_dirreq_line(keyword, recognized_counts_attr,
                       unrecognized_counts_attr, descriptor, entries):
    value = _value(keyword, entries)

    recognized_counts = {}
    unrecognized_counts = {}

    is_response_stats = keyword in ('dirreq-v2-resp', 'dirreq-v3-resp')
    key_set = DirResponse if is_response_stats else DirStat

    key_type = 'STATUS' if is_response_stats else 'STAT'

    for status, count in _mappings_for(keyword, value, divider=','):
        if not count.isdigit():
            raise ValueError(
                '%s lines should contain %s=COUNT mappings: %s %s' %
                (keyword, key_type, keyword, value))

        if status in key_set:
            recognized_counts[status] = int(count)
        else:
            unrecognized_counts[status] = int(count)

    setattr(descriptor, recognized_counts_attr, recognized_counts)
    setattr(descriptor, unrecognized_counts_attr, unrecognized_counts)
Пример #2
0
def _parse_hs_stats(keyword, stat_attribute, extra_attribute, descriptor,
                    entries):
    # "<keyword>" num key=val key=val...

    value, stat, extra = _value(keyword, entries), None, {}

    if value is None:
        pass  # not in the descriptor
    elif value == '':
        raise ValueError("'%s' line was blank" % keyword)
    else:
        if ' ' in value:
            stat_value, remainder = value.split(' ', 1)
        else:
            stat_value, remainder = value, None

        try:
            stat = int(stat_value)
        except ValueError:
            raise ValueError("'%s' stat was non-numeric (%s): %s %s" %
                             (keyword, stat_value, keyword, value))

        for key, val in _mappings_for(keyword, remainder):
            extra[key] = val

    setattr(descriptor, stat_attribute, stat)
    setattr(descriptor, extra_attribute, extra)
Пример #3
0
def _parse_body(descriptor: 'stem.descriptor.Descriptor',
                entries: ENTRY_TYPE) -> None:
    # In version 1.0.0 the body is everything after the first line. Otherwise
    # it's everything after the header's divider.

    content = io.BytesIO(descriptor.get_bytes())

    if descriptor.version == '1.0.0':
        content.readline()  # skip the first line
    else:
        while content.readline().strip() not in ('', HEADER_DIV,
                                                 HEADER_DIV_ALT):
            pass  # skip the header

    measurements = {}

    for line_bytes in content.readlines():
        line = stem.util.str_tools._to_unicode(line_bytes.strip())
        attr = dict(_mappings_for('measurement', line))
        fingerprint = attr.get('node_id', '').lstrip(
            '$')  # bwauths prefix fingerprints with '$'

        if not fingerprint:
            raise ValueError("Every meaurement must include 'node_id': %s" %
                             stem.util.str_tools._to_unicode(line))
        elif fingerprint in measurements:
            raise ValueError(
                'Relay %s is listed multiple times. It should only be present once.'
                % fingerprint)

        measurements[fingerprint] = attr

    descriptor.measurements = measurements
Пример #4
0
def _parse_bridge_ip_transports_line(descriptor, entries):
  value, ip_transports = _value('bridge-ip-transports', entries), {}

  for protocol, count in _mappings_for('bridge-ip-transports', value, divider = ','):
    if not count.isdigit():
      raise stem.ProtocolError('Transport count was non-numeric (%s): bridge-ip-transports %s' % (count, value))

    ip_transports[protocol] = int(count)

  descriptor.ip_transports = ip_transports
Пример #5
0
def _parse_bridge_ip_versions_line(descriptor: 'stem.descriptor.Descriptor', entries: ENTRY_TYPE) -> None:
  value, ip_versions = _value('bridge-ip-versions', entries), {}

  for protocol, count in _mappings_for('bridge-ip-versions', value, divider = ','):
    if not count.isdigit():
      raise stem.ProtocolError('IP protocol count was non-numeric (%s): bridge-ip-versions %s' % (count, value))

    ip_versions[protocol] = int(count)

  descriptor.ip_versions = ip_versions
Пример #6
0
def _parse_port_count_line(keyword, attribute, descriptor, entries):
  # "<keyword>" port=N,port=N,...

  value, port_mappings = _value(keyword, entries), {}

  for port, stat in _mappings_for(keyword, value, divider = ','):
    if (port != 'other' and not stem.util.connection.is_valid_port(port)) or not stat.isdigit():
      raise ValueError('Entries in %s line should only be PORT=N entries: %s %s' % (keyword, keyword, value))

    port = int(port) if port.isdigit() else port
    port_mappings[port] = int(stat)

  setattr(descriptor, attribute, port_mappings)
Пример #7
0
def _parse_padding_counts_line(descriptor, entries):
  # "padding-counts" YYYY-MM-DD HH:MM:SS (NSEC s) key=val key=val...

  value = _value('padding-counts', entries)
  timestamp, interval, remainder = _parse_timestamp_and_interval('padding-counts', value)
  counts = {}

  for k, v in _mappings_for('padding-counts', remainder, require_value = True):
    counts[k] = int(v) if v.isdigit() else v

  setattr(descriptor, 'padding_counts_end', timestamp)
  setattr(descriptor, 'padding_counts_interval', interval)
  setattr(descriptor, 'padding_counts', counts)
Пример #8
0
def _parse_port_count_line(keyword: str, attribute: str, descriptor: 'stem.descriptor.Descriptor', entries: ENTRY_TYPE) -> None:
  # "<keyword>" port=N,port=N,...

  value, port_mappings = _value(keyword, entries), {}

  for port, stat in _mappings_for(keyword, value, divider = ','):
    if (port != 'other' and not stem.util.connection.is_valid_port(port)) or not stat.isdigit():
      raise ValueError('Entries in %s line should only be PORT=N entries: %s %s' % (keyword, keyword, value))

    port = int(port) if port.isdigit() else port  # type: ignore # this can be an int or 'other'
    port_mappings[port] = int(stat)

  setattr(descriptor, attribute, port_mappings)
Пример #9
0
def _parse_geoip_to_count_line(keyword, attribute, descriptor, entries):
  # "<keyword>" CC=N,CC=N,...
  #
  # The maxmind geoip (https://www.maxmind.com/app/iso3166) has numeric
  # locale codes for some special values, for instance...
  #   A1,"Anonymous Proxy"
  #   A2,"Satellite Provider"
  #   ??,"Unknown"

  value, locale_usage = _value(keyword, entries), {}

  for locale, count in _mappings_for(keyword, value, divider = ','):
    if not _locale_re.match(locale) or not count.isdigit():
      raise ValueError('Entries in %s line should only be CC=N entries: %s %s' % (keyword, keyword, value))

    locale_usage[locale] = int(count)

  setattr(descriptor, attribute, locale_usage)