コード例 #1
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
コード例 #2
0
ファイル: __init__.py プロジェクト: goddess5321/stem
  async def _msg(self, cell: 'stem.client.cell.Cell') -> AsyncIterator['stem.client.cell.Cell']:
    """
    Sends a cell on the ORPort and provides the response we receive in reply.

    Unfortunately unlike control sockets, ORPorts don't have generalized rules
    for predictable message IO. With control sockets...

      * Each message we send receives a single reply.
      * We may also receive asynchronous events marked with a 650 status.

    ORPorts by contrast receive variable length cells with differing rules on
    their arrival. As such making a best effort attempt at a send-and-receive
    method in which we do the following...

      * Discard any existing unread data from the socket.
      * Send our request.
      * Await up to a second for a reply.

    It's quite possible this is a stupid approach. If so, patches welcome.

    :param cell: cell to be sent

    :returns: **generator** with the cells received in reply
    """

    # TODO: why is this an iterator?

    await self._orport.recv(timeout = 0)  # discard unread data
    await self._orport.send(cell.pack(self.link_protocol))
    response = await self._orport.recv(timeout = 1)
    yield stem.client.cell.Cell.pop(response, self.link_protocol)[0]