예제 #1
0
파일: events.py 프로젝트: Foxboron/stem
    def _parse(self):
        if self.destination == '<error>':
            self.destination = None

        if self.expiry is not None:
            if self.expiry == 'NEVER':
                self.expiry = None
            else:
                try:
                    self.expiry = stem.util.str_tools._parse_timestamp(
                        self.expiry)
                except ValueError:
                    raise stem.ProtocolError(
                        'Unable to parse date in ADDRMAP event: %s' % self)

        if self.utc_expiry is not None:
            self.utc_expiry = stem.util.str_tools._parse_timestamp(
                self.utc_expiry)

        if self.cached is not None:
            if self.cached == 'YES':
                self.cached = True
            elif self.cached == 'NO':
                self.cached = False
            else:
                raise stem.ProtocolError(
                    "An ADDRMAP event's CACHED mapping can only be 'YES' or 'NO': %s"
                    % self)
예제 #2
0
파일: __init__.py 프로젝트: dmr-x/stem
  def send(self, command, data = '', stream_id = 0):
    """
    Sends a message over the circuit.

    :param stem.client.RelayCommand command: command to be issued
    :param bytes data: message payload
    :param int stream_id: specific stream this concerns

    :returns: **list** of :class:`~stem.client.cell.RelayCell` responses
    """

    with self.relay._orport_lock:
      orig_digest = self.forward_digest.copy()
      orig_key = copy.copy(self.forward_key)

      # Digests and such are computed using the RELAY cell payload. This
      # doesn't include the initial circuit id and cell type fields.
      # Circuit ids vary in length depending on the protocol version.

      header_size = self.relay.link_protocol.circ_id_size.size + 1

      try:
        cell = stem.client.cell.RelayCell(self.id, command, data, 0, stream_id)
        payload_without_digest = cell.pack(self.relay.link_protocol)[header_size:]
        self.forward_digest.update(payload_without_digest)

        cell = stem.client.cell.RelayCell(self.id, command, data, self.forward_digest, stream_id)
        header, payload = split(cell.pack(self.relay.link_protocol), header_size)
        encrypted_payload = header + self.forward_key.update(payload)

        reply_cells = []
        self.relay._orport.send(encrypted_payload)
        reply = self.relay._orport.recv()

        # Check that we got the correct number of bytes for a series of RELAY cells

        relay_cell_size = header_size + stem.client.cell.FIXED_PAYLOAD_LEN
        relay_cell_cmd = stem.client.cell.RelayCell.VALUE

        if len(reply) % relay_cell_size != 0:
          raise stem.ProtocolError('Circuit response should be a series of RELAY cells, but received an unexpected size for a response: %i' % len(reply))

        while reply:
          circ_id, reply = self.relay.link_protocol.circ_id_size.pop(reply)
          command, reply = Size.CHAR.pop(reply)
          payload, reply = split(reply, stem.client.cell.FIXED_PAYLOAD_LEN)

          if command != relay_cell_cmd:
            raise stem.ProtocolError('RELAY cell responses should be %i but was %i' % (relay_cell_cmd, command))
          elif circ_id != self.id:
            raise stem.ProtocolError('Response should be for circuit id %i, not %i' % (self.id, circ_id))

          decrypted = self.backward_key.update(payload)
          reply_cells.append(stem.client.cell.RelayCell._unpack(decrypted, self.id, self.relay.link_protocol))

        return reply_cells
      except:
        self.forward_digest = orig_digest
        self.forward_key = orig_key
        raise
예제 #3
0
    def _parse(self):
        if self.destination == '<error>':
            self.destination = None

        if self.expiry is not None:
            if self.expiry == 'NEVER':
                self.expiry = None
            else:
                try:
                    self.expiry = datetime.datetime.strptime(
                        self.expiry, '%Y-%m-%d %H:%M:%S')
                except ValueError:
                    raise stem.ProtocolError(
                        'Unable to parse date in ADDRMAP event: %s' % self)

        if self.utc_expiry is not None:
            self.utc_expiry = datetime.datetime.strptime(
                self.utc_expiry, '%Y-%m-%d %H:%M:%S')

        if self.cached is not None:
            if self.cached == 'YES':
                self.cached = True
            elif self.cached == 'NO':
                self.cached = False
            else:
                raise stem.ProtocolError(
                    "An ADDRMAP event's CACHED mapping can only be 'YES' or 'NO': %s"
                    % self)
예제 #4
0
    def _parse(self):
        self.endpoint_fingerprint = None
        self.endpoint_nickname = None
        self.endpoint_address = None
        self.endpoint_port = None

        try:
            self.endpoint_fingerprint, self.endpoint_nickname = \
              stem.control._parse_circ_entry(self.endpoint)
        except stem.ProtocolError:
            if not ':' in self.endpoint:
                raise stem.ProtocolError(
                    "ORCONN endpoint is neither a relay nor 'address:port': %s"
                    % self)

            address, port = self.endpoint.split(':', 1)

            if not connection.is_valid_port(port):
                raise stem.ProtocolError(
                    "ORCONN's endpoint location's port is invalid: %s" % self)

            self.endpoint_address = address
            self.endpoint_port = int(port)

        if self.circ_count is not None:
            if not self.circ_count.isdigit():
                raise stem.ProtocolError(
                    "ORCONN event got a non-numeric circuit count (%s): %s" %
                    (self.circ_count, self))

            self.circ_count = int(self.circ_count)

        self._log_if_unrecognized('status', stem.ORStatus)
        self._log_if_unrecognized('reason', stem.ORClosureReason)
예제 #5
0
파일: events.py 프로젝트: Foxboron/stem
    def _parse(self):
        if self.id and not tor_tools.is_valid_circuit_id(self.id):
            raise stem.ProtocolError(
                "Circuit IDs must be one to sixteen alphanumeric characters, got '%s': %s"
                % (self.id, self))
        elif self.inbound_queue and not tor_tools.is_valid_circuit_id(
                self.inbound_queue):
            raise stem.ProtocolError(
                "Queue IDs must be one to sixteen alphanumeric characters, got '%s': %s"
                % (self.inbound_queue, self))
        elif self.inbound_connection and not tor_tools.is_valid_connection_id(
                self.inbound_connection):
            raise stem.ProtocolError(
                "Connection IDs must be one to sixteen alphanumeric characters, got '%s': %s"
                % (self.inbound_connection, self))
        elif self.outbound_queue and not tor_tools.is_valid_circuit_id(
                self.outbound_queue):
            raise stem.ProtocolError(
                "Queue IDs must be one to sixteen alphanumeric characters, got '%s': %s"
                % (self.outbound_queue, self))
        elif self.outbound_connection and not tor_tools.is_valid_connection_id(
                self.outbound_connection):
            raise stem.ProtocolError(
                "Connection IDs must be one to sixteen alphanumeric characters, got '%s': %s"
                % (self.outbound_connection, self))

        self.inbound_added = _parse_cell_type_mapping(self.inbound_added)
        self.inbound_removed = _parse_cell_type_mapping(self.inbound_removed)
        self.inbound_time = _parse_cell_type_mapping(self.inbound_time)
        self.outbound_added = _parse_cell_type_mapping(self.outbound_added)
        self.outbound_removed = _parse_cell_type_mapping(self.outbound_removed)
        self.outbound_time = _parse_cell_type_mapping(self.outbound_time)
예제 #6
0
파일: events.py 프로젝트: sreenatha123/stem
  def _parse_standard_attr(self):
    """
    Most events are of the form...
    650 *( positional_args ) *( key "=" value )

    This parses this standard format, populating our **positional_args** and
    **keyword_args** attributes and creating attributes if it's in our event's
    **_POSITIONAL_ARGS** and **_KEYWORD_ARGS**.
    """

    # Tor events contain some number of positional arguments followed by
    # key/value mappings. Parsing keyword arguments from the end until we hit
    # something that isn't a key/value mapping. The rest are positional.

    content = str(self)

    while True:
      match = QUOTED_KW_ARG.match(content)

      if not match:
        match = KW_ARG.match(content)

      if match:
        content, keyword, value = match.groups()
        self.keyword_args[keyword] = value
      else:
        break

    # Setting attributes for the fields that we recognize.

    self.positional_args = content.split()[1:]
    positional = list(self.positional_args)

    for attr_name in self._POSITIONAL_ARGS:
      attr_value = None

      if positional:
        if attr_name in self._QUOTED or (attr_name in self._OPTIONALLY_QUOTED and positional[0].startswith('"')):
          attr_values = [positional.pop(0)]

          if not attr_values[0].startswith('"'):
            raise stem.ProtocolError("The %s value should be quoted, but didn't have a starting quote: %s" % (attr_name, self))

          while True:
            if not positional:
              raise stem.ProtocolError("The %s value should be quoted, but didn't have an ending quote: %s" % (attr_name, self))

            attr_values.append(positional.pop(0))

            if attr_values[-1].endswith('"'):
              break

          attr_value = " ".join(attr_values)[1:-1]
        else:
          attr_value = positional.pop(0)

      setattr(self, attr_name, attr_value)

    for controller_attr_name, attr_name in self._KEYWORD_ARGS.items():
      setattr(self, attr_name, self.keyword_args.get(controller_attr_name))
예제 #7
0
    def _parse(self):
        # convert our integer and float parameters

        for param in ('total_times', 'timeout', 'xm', 'close_timeout'):
            param_value = getattr(self, param)

            if param_value is not None:
                try:
                    setattr(self, param, int(param_value))
                except ValueError:
                    raise stem.ProtocolError(
                        "The %s of a BUILDTIMEOUT_SET should be an integer: %s"
                        % (param, self))

        for param in ('alpha', 'quantile', 'timeout_rate', 'close_rate'):
            param_value = getattr(self, param)

            if param_value is not None:
                try:
                    setattr(self, param, float(param_value))
                except ValueError:
                    raise stem.ProtocolError(
                        "The %s of a BUILDTIMEOUT_SET should be a float: %s" %
                        (param, self))

        self._log_if_unrecognized('set_type', stem.TimeoutSetType)
예제 #8
0
파일: remote.py 프로젝트: momor666/stem
def _download_from_orport(endpoint, compression, resource):
  """
  Downloads descriptors from the given orport. Payload is just like an http
  response (headers and all)...

  ::

    HTTP/1.0 200 OK
    Date: Mon, 23 Apr 2018 18:43:47 GMT
    Content-Type: text/plain
    X-Your-Address-Is: 216.161.254.25
    Content-Encoding: identity
    Expires: Wed, 25 Apr 2018 18:43:47 GMT

    router dannenberg 193.23.244.244 443 0 80
    identity-ed25519
    ... rest of the descriptor content...

  :param stem.ORPort endpoint: endpoint to download from
  :param list compression: compression methods for the request
  :param str resource: descriptor resource to download

  :returns: two value tuple of the form (data, reply_headers)

  :raises:
    * :class:`stem.ProtocolError` if not a valid descriptor response
    * :class:`stem.SocketError` if unable to establish a connection
  """

  link_protocols = endpoint.link_protocols if endpoint.link_protocols else [3]

  with stem.client.Relay.connect(endpoint.address, endpoint.port, link_protocols) as relay:
    with relay.create_circuit() as circ:
      request = '\r\n'.join((
        'GET %s HTTP/1.0' % resource,
        'Accept-Encoding: %s' % ', '.join(compression),
        'User-Agent: %s' % stem.USER_AGENT,
      )) + '\r\n\r\n'

      circ.send(RelayCommand.BEGIN_DIR, stream_id = 1)
      response = b''.join([cell.data for cell in circ.send(RelayCommand.DATA, request, stream_id = 1)])
      first_line, data = response.split(b'\r\n', 1)
      header_data, body_data = data.split(b'\r\n\r\n', 1)

      if not first_line.startswith(b'HTTP/1.0 2'):
        raise stem.ProtocolError("Response should begin with HTTP success, but was '%s'" % str_tools._to_unicode(first_line))

      headers = {}

      for line in str_tools._to_unicode(header_data).splitlines():
        if ': ' not in line:
          raise stem.ProtocolError("'%s' is not a HTTP header:\n\n%s" % line)

        key, value = line.split(': ', 1)
        headers[key] = value

      return _decompress(body_data, headers.get('Content-Encoding')), headers
예제 #9
0
파일: remote.py 프로젝트: zmchu/stem
def _http_body_and_headers(data: bytes) -> Tuple[bytes, Dict[str, str]]:
    """
  Parse the headers and decompressed body from a HTTP response, such as...

  ::

    HTTP/1.0 200 OK
    Date: Mon, 23 Apr 2018 18:43:47 GMT
    Content-Type: text/plain
    X-Your-Address-Is: 216.161.254.25
    Content-Encoding: identity
    Expires: Wed, 25 Apr 2018 18:43:47 GMT

    router dannenberg 193.23.244.244 443 0 80
    identity-ed25519
    ... rest of the descriptor content...

  :param data: HTTP response

  :returns: **tuple** with the decompressed data and headers

  :raises:
    * **stem.ProtocolError** if response was unsuccessful or malformed
    * **ValueError** if encoding is unrecognized
    * **ImportError** if missing the decompression module
  """

    first_line, data = data.split(b'\r\n', 1)
    header_data, body_data = data.split(b'\r\n\r\n', 1)

    if not first_line.startswith(b'HTTP/1.0 2'):
        raise stem.ProtocolError(
            "Response should begin with HTTP success, but was '%s'" %
            str_tools._to_unicode(first_line))

    headers = {}

    for line in str_tools._to_unicode(header_data).splitlines():
        if ': ' not in line:
            raise stem.ProtocolError("'%s' is not a HTTP header:\n\n%s" %
                                     (line, header_data.decode('utf-8')))

        key, value = line.split(': ', 1)
        headers[key] = value

    encoding = headers.get('Content-Encoding')

    if encoding == 'deflate':
        return stem.descriptor.Compression.GZIP.decompress(
            body_data).rstrip(), headers

    for compression in stem.descriptor.Compression:
        if encoding == compression.encoding:
            return compression.decompress(body_data).rstrip(), headers

    raise ValueError("'%s' is an unrecognized encoding" % encoding)
예제 #10
0
파일: events.py 프로젝트: sreenatha123/stem
  def _parse(self):
    if not self.read:
      raise stem.ProtocolError("BW event is missing its read value")
    elif not self.written:
      raise stem.ProtocolError("BW event is missing its written value")
    elif not self.read.isdigit() or not self.written.isdigit():
      raise stem.ProtocolError("A BW event's bytes sent and received should be a positive numeric value, received: %s" % self)

    self.read = long(self.read)
    self.written = long(self.written)
예제 #11
0
파일: events.py 프로젝트: sreenatha123/stem
  def _parse(self):
    lines = str(self).split('\n')

    if len(lines) < 5:
      raise stem.ProtocolError("AUTHDIR_NEWDESCS events must contain lines for at least the type, action, message, descriptor, and terminating 'OK'")
    elif not lines[-1] == "OK":
      raise stem.ProtocolError("AUTHDIR_NEWDESCS doesn't end with an 'OK'")

    self.action = lines[1]
    self.message = lines[2]
    self.descriptor = '\n'.join(lines[3:-1])
예제 #12
0
파일: events.py 프로젝트: sreenatha123/stem
  def _parse(self):
    if not tor_tools.is_valid_stream_id(self.id):
      raise stem.ProtocolError("Stream IDs must be one to sixteen alphanumeric characters, got '%s': %s" % (self.id, self))
    elif not self.written:
      raise stem.ProtocolError("STREAM_BW event is missing its written value")
    elif not self.read:
      raise stem.ProtocolError("STREAM_BW event is missing its read value")
    elif not self.read.isdigit() or not self.written.isdigit():
      raise stem.ProtocolError("A STREAM_BW event's bytes sent and received should be a positive numeric value, received: %s" % self)

    self.read = long(self.read)
    self.written = long(self.written)
예제 #13
0
  async def directory(self, request: str, stream_id: int = 0) -> bytes:
    """
    Request descriptors from the relay.

    :param request: directory request to make
    :param stream_id: specific stream this concerns

    :returns: **str** with the requested descriptor data
    """

    async with self.relay._orport_lock:
      await self._send(RelayCommand.BEGIN_DIR, stream_id = stream_id)
      await self._send(RelayCommand.DATA, request, stream_id = stream_id)

      response = []  # type: List[stem.client.cell.RelayCell]

      while True:
        # Decrypt relay cells received in response. Our digest/key only
        # updates when handled successfully.

        encrypted_cell = await self.relay._recv_bytes()

        decrypted_cell, backward_key, backward_digest = stem.client.cell.RelayCell.decrypt(self.relay.link_protocol, encrypted_cell, self.backward_key, self.backward_digest)

        if self.id != decrypted_cell.circ_id:
          raise stem.ProtocolError('Response should be for circuit id %i, not %i' % (self.id, decrypted_cell.circ_id))

        self.backward_digest = backward_digest
        self.backward_key = backward_key

        if decrypted_cell.command == RelayCommand.END:
          return b''.join([cell.data for cell in response])
        else:
          response.append(decrypted_cell)
예제 #14
0
def negotiate_socks(sock, host, port):
    """
  Negotiate with a socks4a server. Closes the socket and raises an exception on
  failure.

  :param socket sock: socket connected to socks4a server
  :param str host: hostname/IP to connect to
  :param int port: port to connect to

  :raises: :class:`stem.ProtocolError` if the socks server doesn't grant our request

  :returns: a list with the IP address and the port that the proxy connected to
  """

    # SOCKS4a request here - http://en.wikipedia.org/wiki/SOCKS#Protocol
    request = b'\x04\x01' + struct.pack(
        '!H',
        port) + b'\x00\x00\x00\x01' + b'\x00' + stem.util.str_tools._to_bytes(
            host) + b'\x00'
    sock.sendall(request)
    response = sock.recv(8)

    if len(response) != 8 or response[0:2] != b'\x00\x5a':
        sock.close()
        raise stem.ProtocolError(
            ERROR_MSG.get(response[1],
                          'SOCKS server returned unrecognized error code'))

    return [
        socket.inet_ntoa(response[4:]),
        struct.unpack('!H', response[2:4])[0]
    ]
예제 #15
0
class CircuitEvent(Event):
    """
  Event that indicates that a circuit has changed.

  The fingerprint or nickname values in our 'path' may be **None** if the
  VERBOSE_NAMES feature isn't enabled. The option was first introduced in tor
  version 0.1.2.2, and on by default after 0.2.2.1.

  The CIRC event was one of the first Control Protocol V1 events and was
  introduced in tor version 0.1.1.1-alpha.

  :var str id: circuit identifier
  :var stem.CircStatus status: reported status for the circuit
  :var tuple path: relays involved in the circuit, these are
    **(fingerprint, nickname)** tuples
  :var tuple build_flags: :data:`~stem.CircBuildFlag` attributes
    governing how the circuit is built
  :var stem.CircPurpose purpose: purpose that the circuit is intended for
  :var stem.HiddenServiceState hs_state: status if this is a hidden service circuit
  :var str rend_query: circuit's rendezvous-point if this is hidden service related
  :var datetime created: time when the circuit was created or cannibalized
  :var stem.CircClosureReason reason: reason for the circuit to be closed
  :var stem.CircClosureReason remote_reason: remote side's reason for the circuit to be closed
  """

    _POSITIONAL_ARGS = ("id", "status", "path")
    _KEYWORD_ARGS = {
        "BUILD_FLAGS": "build_flags",
        "PURPOSE": "purpose",
        "HS_STATE": "hs_state",
        "REND_QUERY": "rend_query",
        "TIME_CREATED": "created",
        "REASON": "reason",
        "REMOTE_REASON": "remote_reason",
    }

    def _parse(self):
        self.path = tuple(stem.control._parse_circ_path(self.path))

        if self.build_flags is not None:
            self.build_flags = tuple(self.build_flags.split(','))

        if self.created is not None:
            try:
                self.created = str_tools._parse_iso_timestamp(self.created)
            except ValueError, exc:
                raise stem.ProtocolError(
                    "Unable to parse create date (%s): %s" % (exc, self))

        if not tor_tools.is_valid_circuit_id(self.id):
            raise stem.ProtocolError(
                "Circuit IDs must be one to sixteen alphanumeric characters, got '%s': %s"
                % (self.id, self))

        self._log_if_unrecognized('status', stem.CircStatus)
        self._log_if_unrecognized('build_flags', stem.CircBuildFlag)
        self._log_if_unrecognized('purpose', stem.CircPurpose)
        self._log_if_unrecognized('hs_state', stem.HiddenServiceState)
        self._log_if_unrecognized('reason', stem.CircClosureReason)
        self._log_if_unrecognized('remote_reason', stem.CircClosureReason)
예제 #16
0
def upload_descriptor(controller, signed_descriptor, hsdirs=None):
    """
    Upload descriptor via the Tor control port

    If no HSDirs are specified, Tor will upload to what it thinks are the
    responsible directories
    """
    logger.debug("Beginning service descriptor upload.")

    # Provide server fingerprints to control command if HSDirs are specified.
    if hsdirs:
        server_args = ' '.join([("SERVER={}".format(hsdir))
                                for hsdir in hsdirs])
    else:
        server_args = ""

    # Stem will insert the leading + and trailing '\r\n.\r\n'
    response = controller.msg("HSPOST %s\n%s" %
                              (server_args, signed_descriptor))

    (response_code, divider, response_content) = response.content()[0]
    if not response.is_ok():
        if response_code == "552":
            raise stem.InvalidRequest(response_code, response_content)
        else:
            raise stem.ProtocolError("HSPOST returned unexpected response "
                                     "code: %s\n%s" %
                                     (response_code, response_content))
예제 #17
0
파일: events.py 프로젝트: Foxboron/stem
    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)
예제 #18
0
    def _parse(self):
        if self.target is None:
            raise stem.ProtocolError("STREAM event didn't have a target: %s" %
                                     self)
        else:
            if not ':' in self.target:
                raise stem.ProtocolError(
                    "Target location must be of the form 'address:port': %s" %
                    self)

            address, port = self.target.split(':', 1)

            if not connection.is_valid_port(port, allow_zero=True):
                raise stem.ProtocolError(
                    "Target location's port is invalid: %s" % self)

            self.target_address = address
            self.target_port = int(port)

        if self.source_addr is None:
            self.source_address = None
            self.source_port = None
        else:
            if not ':' in self.source_addr:
                raise stem.ProtocolError(
                    "Source location must be of the form 'address:port': %s" %
                    self)

            address, port = self.source_addr.split(':', 1)

            if not connection.is_valid_port(port, allow_zero=True):
                raise stem.ProtocolError(
                    "Source location's port is invalid: %s" % self)

            self.source_address = address
            self.source_port = int(port)

        # spec specifies a circ_id of zero if the stream is unattached

        if self.circ_id == "0":
            self.circ_id = None

        self._log_if_unrecognized('reason', stem.StreamClosureReason)
        self._log_if_unrecognized('remote_reason', stem.StreamClosureReason)
        self._log_if_unrecognized('purpose', stem.StreamPurpose)
예제 #19
0
class CircMinorEvent(Event):
    """
  Event providing information about minor changes in our circuits. This was
  first added in tor version 0.2.3.11.

  The CIRC_MINOR event was introduced in tor version 0.2.3.11-alpha.

  :var str id: circuit identifier
  :var stem.CircEvent event: type of change in the circuit
  :var tuple path: relays involved in the circuit, these are
    **(fingerprint, nickname)** tuples
  :var tuple build_flags: :data:`~stem.CircBuildFlag` attributes
    governing how the circuit is built
  :var stem.CircPurpose purpose: purpose that the circuit is intended for
  :var stem.HiddenServiceState hs_state: status if this is a hidden service circuit
  :var str rend_query: circuit's rendezvous-point if this is hidden service related
  :var datetime created: time when the circuit was created or cannibalized
  :var stem.CircPurpose old_purpose: prior purpose for the circuit
  :var stem.HiddenServiceState old_hs_state: prior status as a hidden service circuit
  """

    _POSITIONAL_ARGS = ("id", "event", "path")
    _KEYWORD_ARGS = {
        "BUILD_FLAGS": "build_flags",
        "PURPOSE": "purpose",
        "HS_STATE": "hs_state",
        "REND_QUERY": "rend_query",
        "TIME_CREATED": "created",
        "OLD_PURPOSE": "old_purpose",
        "OLD_HS_STATE": "old_hs_state",
    }
    _VERSION_ADDED = stem.version.Requirement.EVENT_CIRC_MINOR

    def _parse(self):
        self.path = tuple(stem.control._parse_circ_path(self.path))

        if self.build_flags is not None:
            self.build_flags = tuple(self.build_flags.split(','))

        if self.created is not None:
            try:
                self.created = str_tools._parse_iso_timestamp(self.created)
            except ValueError, exc:
                raise stem.ProtocolError(
                    "Unable to parse create date (%s): %s" % (exc, self))

        if not tor_tools.is_valid_circuit_id(self.id):
            raise stem.ProtocolError(
                "Circuit IDs must be one to sixteen alphanumeric characters, got '%s': %s"
                % (self.id, self))

        self._log_if_unrecognized('event', stem.CircEvent)
        self._log_if_unrecognized('build_flags', stem.CircBuildFlag)
        self._log_if_unrecognized('purpose', stem.CircPurpose)
        self._log_if_unrecognized('hs_state', stem.HiddenServiceState)
        self._log_if_unrecognized('old_purpose', stem.CircPurpose)
        self._log_if_unrecognized('old_hs_state', stem.HiddenServiceState)
예제 #20
0
  def send(self, command, data = '', stream_id = 0):
    """
    Sends a message over the circuit.

    :param stem.client.datatype.RelayCommand command: command to be issued
    :param bytes data: message payload
    :param int stream_id: specific stream this concerns

    :returns: **list** of :class:`~stem.client.cell.RelayCell` responses
    """

    with self.relay._orport_lock:
      # Encrypt and send the cell. Our digest/key only updates if the cell is
      # successfully sent.

      cell = stem.client.cell.RelayCell(self.id, command, data, stream_id = stream_id)
      payload, forward_key, forward_digest = cell.encrypt(self.relay.link_protocol, self.forward_key, self.forward_digest)
      self.relay._orport.send(payload)

      self.forward_digest = forward_digest
      self.forward_key = forward_key

      # Decrypt relay cells received in response. Again, our digest/key only
      # updates when handled successfully.

      reply = self.relay._orport.recv()
      reply_cells = []

      if len(reply) % self.relay.link_protocol.fixed_cell_length != 0:
        raise stem.ProtocolError('Circuit response should be a series of RELAY cells, but received an unexpected size for a response: %i' % len(reply))

      while reply:
        encrypted_cell, reply = split(reply, self.relay.link_protocol.fixed_cell_length)
        decrypted_cell, backward_key, backward_digest = stem.client.cell.RelayCell.decrypt(self.relay.link_protocol, encrypted_cell, self.backward_key, self.backward_digest)

        if self.id != decrypted_cell.circ_id:
          raise stem.ProtocolError('Response should be for circuit id %i, not %i' % (self.id, decrypted_cell.circ_id))

        self.backward_digest = backward_digest
        self.backward_key = backward_key

        reply_cells.append(decrypted_cell)

      return reply_cells
예제 #21
0
파일: events.py 프로젝트: Foxboron/stem
    def _parse(self):
        if not self.id:
            raise stem.ProtocolError('CIRC_BW event is missing its id')
        elif not self.read:
            raise stem.ProtocolError('CIRC_BW event is missing its read value')
        elif not self.written:
            raise stem.ProtocolError(
                'CIRC_BW event is missing its written value')
        elif not self.read.isdigit() or not self.written.isdigit():
            raise stem.ProtocolError(
                "A CIRC_BW event's bytes sent and received should be a positive numeric value, received: %s"
                % self)
        elif not tor_tools.is_valid_circuit_id(self.id):
            raise stem.ProtocolError(
                "Circuit IDs must be one to sixteen alphanumeric characters, got '%s': %s"
                % (self.id, self))

        self.read = int_type(self.read)
        self.written = int_type(self.written)
예제 #22
0
파일: events.py 프로젝트: sreenatha123/stem
  def _parse(self):
    self.path = tuple(stem.control._parse_circ_path(self.path))

    if self.build_flags is not None:
      self.build_flags = tuple(self.build_flags.split(','))

    if self.created is not None:
      try:
        self.created = str_tools._parse_iso_timestamp(self.created)
      except ValueError, exc:
        raise stem.ProtocolError("Unable to parse create date (%s): %s" % (exc, self))
예제 #23
0
파일: events.py 프로젝트: sreenatha123/stem
  def _parse(self):
    self.endpoint_fingerprint = None
    self.endpoint_nickname = None

    try:
      self.endpoint_fingerprint, self.endpoint_nickname = \
        stem.control._parse_circ_entry(self.endpoint)
    except stem.ProtocolError:
      raise stem.ProtocolError("ORCONN's endpoint doesn't match a ServerSpec: %s" % self)

    self._log_if_unrecognized('guard_type', stem.GuardType)
    self._log_if_unrecognized('status', stem.GuardStatus)
예제 #24
0
파일: events.py 프로젝트: Foxboron/stem
def _parse_cell_type_mapping(mapping):
    """
  Parses a mapping of the form...

    key1:value1,key2:value2...

  ... in which keys are strings and values are integers.

  :param str mapping: value to be parsed

  :returns: dict of **str => int** mappings

  :rasies: **stem.ProtocolError** if unable to parse the mapping
  """

    if mapping is None:
        return None

    results = {}

    for entry in mapping.split(','):
        if ':' not in entry:
            raise stem.ProtocolError(
                "Mappings are expected to be of the form 'key:value', got '%s': %s"
                % (entry, mapping))

        key, value = entry.split(':', 1)

        if not CELL_TYPE.match(key):
            raise stem.ProtocolError(
                "Key had invalid characters, got '%s': %s" % (key, mapping))
        elif not value.isdigit():
            raise stem.ProtocolError(
                "Values should just be integers, got '%s': %s" %
                (value, mapping))

        results[key] = int(value)

    return results
예제 #25
0
파일: events.py 프로젝트: Foxboron/stem
    def _parse(self):
        self.directory_fingerprint = None
        self.directory_nickname = None

        try:
            self.directory_fingerprint, self.directory_nickname = \
              stem.control._parse_circ_entry(self.directory)
        except stem.ProtocolError:
            raise stem.ProtocolError(
                "HS_DESC's directory doesn't match a ServerSpec: %s" % self)

        self._log_if_unrecognized('action', stem.HSDescAction)
        self._log_if_unrecognized('authentication', stem.HSAuth)
예제 #26
0
파일: events.py 프로젝트: Foxboron/stem
    def _parse(self):
        if self.id and not tor_tools.is_valid_connection_id(self.id):
            raise stem.ProtocolError(
                "Connection IDs must be one to sixteen alphanumeric characters, got '%s': %s"
                % (self.id, self))
        elif not self.read.isdigit():
            raise stem.ProtocolError(
                "A TB_EMPTY's READ value should be a positive numeric value, received: %s"
                % self)
        elif not self.written.isdigit():
            raise stem.ProtocolError(
                "A TB_EMPTY's WRITTEN value should be a positive numeric value, received: %s"
                % self)
        elif not self.last_refill.isdigit():
            raise stem.ProtocolError(
                "A TB_EMPTY's LAST value should be a positive numeric value, received: %s"
                % self)

        self.read = int(self.read)
        self.written = int(self.written)
        self.last_refill = int(self.last_refill)

        self._log_if_unrecognized('bucket', stem.TokenBucket)
예제 #27
0
파일: events.py 프로젝트: Foxboron/stem
    def _parse(self):
        self.path = tuple(stem.control._parse_circ_path(self.path))

        if self.build_flags is not None:
            self.build_flags = tuple(self.build_flags.split(','))

        if self.created is not None:
            try:
                self.created = str_tools._parse_iso_timestamp(self.created)
            except ValueError as exc:
                raise stem.ProtocolError(
                    'Unable to parse create date (%s): %s' % (exc, self))

        if not tor_tools.is_valid_circuit_id(self.id):
            raise stem.ProtocolError(
                "Circuit IDs must be one to sixteen alphanumeric characters, got '%s': %s"
                % (self.id, self))

        self._log_if_unrecognized('event', stem.CircEvent)
        self._log_if_unrecognized('build_flags', stem.CircBuildFlag)
        self._log_if_unrecognized('purpose', stem.CircPurpose)
        self._log_if_unrecognized('hs_state', stem.HiddenServiceState)
        self._log_if_unrecognized('old_purpose', stem.CircPurpose)
        self._log_if_unrecognized('old_hs_state', stem.HiddenServiceState)
예제 #28
0
    def _parse(self):
        if self.destination == "<error>":
            self.destination = None

        if self.expiry is not None:
            if self.expiry == "NEVER":
                self.expiry = None
            else:
                try:
                    self.expiry = datetime.datetime.strptime(
                        self.expiry, "%Y-%m-%d %H:%M:%S")
                except ValueError:
                    raise stem.ProtocolError(
                        "Unable to parse date in ADDRMAP event: %s" % self)

        if self.utc_expiry is not None:
            self.utc_expiry = datetime.datetime.strptime(
                self.utc_expiry, "%Y-%m-%d %H:%M:%S")
예제 #29
0
    def _parse(self):
        if self.start_time is not None:
            self.start_time = datetime.datetime.strptime(
                self.start_time, "%Y-%m-%d %H:%M:%S")

        if self.locales is not None:
            locale_to_count = {}

            for entry in self.locales.split(','):
                if not '=' in entry:
                    raise stem.ProtocolError(
                        "The CLIENTS_SEEN's CountrySummary should be a comma separated listing of '<locale>=<count>' mappings: %s"
                        % self)

                locale, count = entry.split('=', 1)

                if len(locale) != 2:
                    raise stem.ProtocolError(
                        "Locales should be a two character code, got '%s': %s"
                        % (locale, self))
                elif not count.isdigit():
                    raise stem.ProtocolError(
                        "Locale count was non-numeric (%s): %s" %
                        (count, self))
                elif locale in locale_to_count:
                    raise stem.ProtocolError(
                        "CountrySummary had multiple mappings for '%s': %s" %
                        (locale, self))

                locale_to_count[locale] = int(count)

            self.locales = locale_to_count

        if self.ip_versions is not None:
            protocol_to_count = {}

            for entry in self.ip_versions.split(','):
                if not '=' in entry:
                    raise stem.ProtocolError(
                        "The CLIENTS_SEEN's IPVersions should be a comma separated listing of '<protocol>=<count>' mappings: %s"
                        % self)

                protocol, count = entry.split('=', 1)

                if not count.isdigit():
                    raise stem.ProtocolError(
                        "IP protocol count was non-numeric (%s): %s" %
                        (count, self))

                protocol_to_count[protocol] = int(count)

            self.ip_versions = protocol_to_count
예제 #30
0
    def _parse_message(self, arrived_at):
        if not str(self).strip():
            raise stem.ProtocolError(
                "Received a blank tor event. Events must at the very least have a type."
            )

        self.type = str(self).split().pop(0)
        self.arrived_at = arrived_at

        # if we're a recognized event type then translate ourselves into that subclass

        if self.type in EVENT_TYPE_TO_CLASS:
            self.__class__ = EVENT_TYPE_TO_CLASS[self.type]

        self.positional_args = []
        self.keyword_args = {}

        if not self._SKIP_PARSING:
            self._parse_standard_attr()

        self._parse()