def _process_buffer(self): while self.buffer: length, raw = variant.read_variant(self.buffer) if len(raw) < length: break data = raw[:length] self.buffer = raw[length:] if self.chacha: log_binary(_LOGGER, "ENC Phone->ATV", Encrypted=data) data = self.chacha.decrypt(data) message = protobuf.ProtocolMessage() message.ParseFromString(data) _LOGGER.info("(DEC Phone->ATV): %s", message) try: if message.type == protobuf.DEVICE_INFO_MESSAGE: self.handle_device_info(message, message.inner()) elif message.type == protobuf.CRYPTO_PAIRING_MESSAGE: self.handle_crypto_pairing(message, message.inner()) else: self.connection.send_raw(data) except Exception: # pylint: disable=broad-except _LOGGER.exception("Error while dispatching message")
def data_received(self, data): """Message received from iOS app/client.""" self.buffer += data while self.buffer: length, raw = variant.read_variant(self.buffer) if len(raw) < length: break data = raw[:length] self.buffer = raw[length:] if self.chacha: log_binary(_LOGGER, 'ENC Phone->ATV', Encrypted=data) data = self.chacha.decrypt(data) parsed = protobuf.ProtocolMessage() parsed.ParseFromString(data) _LOGGER.info('(DEC Phone->ATV): %s', parsed) try: def unhandled_message(_, raw): self.connection.send_raw(raw) self.mapping.get(parsed.type, unhandled_message)(parsed, data) except Exception: # pylint: disable=broad-except _LOGGER.exception('Error while dispatching message')
def data_received(self, data): self.buffer += data while self.buffer: length, raw = variant.read_variant(self.buffer) if len(raw) < length: return data = raw[:length] self.buffer = raw[length:] if self.chacha: data = self.chacha.decrypt(data) parsed = protobuf.ProtocolMessage() parsed.ParseFromString(data) _LOGGER.info('Incoming message: %s', parsed) try: name = parsed.Type.Name( parsed.type).lower().replace('_message', '') _LOGGER.debug('Received %s message', name) getattr(self, 'handle_' + name)(parsed, parsed.inner()) except AttributeError: _LOGGER.exception('No message handler for ' + str(parsed)) except Exception: _LOGGER.exception('Error while dispatching message')
def data_received(self, data): self.buffer += data while self.buffer: length, raw = variant.read_variant(self.buffer) if len(raw) < length: return data = raw[:length] self.buffer = raw[length:] if self.chacha: data = self.chacha.decrypt(data) parsed = protobuf.ProtocolMessage() parsed.ParseFromString(data) log_protobuf(_LOGGER, "<< Receive: Protobuf", parsed) try: name = parsed.Type.Name(parsed.type).lower().replace("_message", "") _LOGGER.debug("Received %s message", name) getattr(self, "handle_" + name)(parsed, parsed.inner()) except AttributeError: _LOGGER.exception("No message handler for " + str(parsed)) except Exception: _LOGGER.exception("Error while dispatching message")
def data_received(self, data): """Message was received from device.""" # A message might be split over several reads, so we store a buffer and # try to decode messages from that buffer self._buffer += data log_binary(_LOGGER, self._log_str + "<< Receive", Data=data) while self._buffer: # The variant tells us how much data must follow length, raw = read_variant(self._buffer) if len(raw) < length: _LOGGER.debug( self._log_str + "Require %d bytes but only %d in buffer", length, len(raw), ) break data = raw[:length] # Incoming message (might be encrypted) self._buffer = raw[length:] # Buffer, might contain more messages try: self._handle_message(data) except Exception: # pylint: disable=broad-except _LOGGER.exception(self._log_str + "Failed to handle message")
def decode_and_print_message(args): """Decode and print protobuf messages.""" buf = binascii.unhexlify(args.message) if not args.stream: buf = variant.write_variant(len(buf)) + buf while buf: length, raw = variant.read_variant(buf) data = raw[:length] buf = raw[length:] _print_single_message(data, args.unknown_fields) return 0
def decode_and_print_message(args): """Decode and print protobuf messages.""" # Import here to allow other parts of script, e.g. message generation to run # without having pyatv installed from pyatv.mrp import variant # pylint: disable=import-outside-toplevel buf = binascii.unhexlify(args.message) if not args.stream: buf = variant.write_variant(len(buf)) + buf while buf: length, raw = variant.read_variant(buf) data = raw[:length] buf = raw[length:] _print_single_message(data, args.unknown_fields) return 0
def data_received(self, data): self.buffer += data length, raw = variant.read_variant(self.buffer) if len(raw) < length: return data = raw[:length] self.buffer = raw[length:] parsed = protobuf.ProtocolMessage() parsed.ParseFromString(data) _LOGGER.info('Incoming message: %s', parsed) try: def unhandled_message(message): _LOGGER.warning('No message handler for %s', message) self.mapping.get(parsed.type, unhandled_message)(parsed) except Exception: _LOGGER.exception('Error while dispatching message')
def test_read_multiple_bytes(self): self.assertEqual(read_variant(b"\xb5\x44")[0], 8757) self.assertEqual(read_variant(b"\xc5\x92\x01")[0], 18757)
#!/usr/bin/env python3 """Raw decode sequence of messages with protoc. Pass hex string (including variant length). Multiple messages are supported. """ import os import sys import binascii from pyatv.mrp import variant if __name__ == '__main__': buf = binascii.unhexlify(sys.argv[1]) while buf: length, raw = variant.read_variant(buf) data = raw[:length] buf = raw[length:] hexdata = binascii.hexlify(data).decode('ascii') print('Raw:', hexdata, '\n') print('Decoded') os.system('echo ' + hexdata + ' | xxd -r -p | protoc --decode_raw') print(40 * '-')
def test_read_single_byte(self): self.assertEqual(read_variant(b"\x00")[0], 0x00) self.assertEqual(read_variant(b"\x35")[0], 0x35)
def test_read_invalid_variant(self): with self.assertRaises(Exception): read_variant(b"\x80")
def test_read_and_return_remaining_data(self): value, remaining = read_variant(b"\xb5\x44\xca\xfe") self.assertEqual(value, 8757) self.assertEqual(remaining, b"\xca\xfe")
def test_read_single_byte(): assert read_variant(b"\x00")[0] == 0x00 assert read_variant(b"\x35")[0] == 0x35
def test_read_invalid_variant(): with pytest.raises(Exception): read_variant(b"\x80")
def test_read_and_return_remaining_data(): value, remaining = read_variant(b"\xb5\x44\xca\xfe") assert value == 8757 assert remaining == b"\xca\xfe"
def test_read_multiple_bytes(): assert read_variant(b"\xb5\x44")[0] == 8757 assert read_variant(b"\xc5\x92\x01")[0] == 18757