Exemplo n.º 1
0
    async def read_any(
            self, expected_wire_types: Iterable[int]) -> protobuf.MessageType:
        if __debug__:
            log.debug(
                __name__,
                "%s:%x expect: %s",
                self.iface.iface_num(),
                self.sid,
                expected_wire_types,
            )

        # Load the full message into a buffer, parse out type and data payload
        msg = await self.read_from_wire()

        # If we got a message with unexpected type, raise the message via
        # `UnexpectedMessageError` and let the session handler deal with it.
        if msg.type not in expected_wire_types:
            raise UnexpectedMessageError(msg)

        # find the protobuf type
        exptype = messages.get_type(msg.type)

        if __debug__:
            log.debug(__name__, "%s:%x read: %s", self.iface.iface_num(),
                      self.sid, exptype)

        workflow.idle_timer.touch()

        # parse the message and return it
        return protobuf.load_message(msg.data, exptype)
Exemplo n.º 2
0
    def test_validate_enum(self):
        # ok message:
        msg = Message(-42, 5)
        writer = ByteArrayWriter()
        await_result(protobuf.dump_message(writer, msg))
        reader = ByteReader(bytes(writer.buf))
        nmsg = await_result(protobuf.load_message(reader, Message))

        self.assertEqual(msg.sint_field, nmsg.sint_field)
        self.assertEqual(msg.enum_field, nmsg.enum_field)

        # bad enum value:
        msg = Message(-42, 42)
        writer = ByteArrayWriter()
        await_result(protobuf.dump_message(writer, msg))
        reader = ByteReader(bytes(writer.buf))
        with self.assertRaises(TypeError):
            await_result(protobuf.load_message(reader, Message))
Exemplo n.º 3
0
def get() -> protobuf.MessageType | None:
    stored_auth_type = storage.cache.get(
        storage.cache.APP_COMMON_AUTHORIZATION_TYPE)
    if not stored_auth_type:
        return None

    msg_wire_type = int.from_bytes(stored_auth_type, "big")
    msg_type = messages.get_type(msg_wire_type)
    buffer = storage.cache.get(storage.cache.APP_COMMON_AUTHORIZATION_DATA)
    reader = utils.BufferReader(buffer)

    return protobuf.load_message(reader, msg_type)
Exemplo n.º 4
0
def _wrap_protobuf_load(
    reader: protobuf.Reader,
    expected_type: Type[protobuf.LoadedMessageType],
    field_cache: protobuf.FieldCache = None,
) -> protobuf.LoadedMessageType:
    try:
        return protobuf.load_message(reader, expected_type, field_cache)
    except Exception as e:
        if e.args:
            raise DataError("Failed to decode message: {}".format(e.args[0]))
        else:
            raise DataError("Failed to decode message")
    def test_validate_enum(self):
        # ok message:
        msg = Message(-42, 5)
        length = protobuf.count_message(msg)
        buffer_writer = BufferWriter(bytearray(length))
        protobuf.dump_message(buffer_writer, msg)

        buffer_reader = BufferReader(buffer_writer.buffer)
        nmsg = protobuf.load_message(buffer_reader, Message)

        self.assertEqual(msg.sint_field, nmsg.sint_field)
        self.assertEqual(msg.enum_field, nmsg.enum_field)

        # bad enum value:
        buffer_writer.seek(0)
        msg = Message(-42, 42)
        # XXX this assumes the message will have equal size
        protobuf.dump_message(buffer_writer, msg)
        buffer_reader.seek(0)
        with self.assertRaises(TypeError):
            protobuf.load_message(buffer_reader, Message)
Exemplo n.º 6
0
    async def read(
        self,
        expected_type: Type[protobuf.LoadedMessageType],
        field_cache: protobuf.FieldCache = None,
    ) -> protobuf.LoadedMessageType:
        if __debug__:
            log.debug(
                __name__,
                "%s:%x expect: %s",
                self.iface.iface_num(),
                self.sid,
                expected_type,
            )

        # Load the full message into a buffer, parse out type and data payload
        msg = await self.read_from_wire()

        # If we got a message with unexpected type, raise the message via
        # `UnexpectedMessageError` and let the session handler deal with it.
        if msg.type != expected_type.MESSAGE_WIRE_TYPE:
            raise UnexpectedMessageError(msg)

        if __debug__:
            log.debug(
                __name__,
                "%s:%x read: %s",
                self.iface.iface_num(),
                self.sid,
                expected_type,
            )

        workflow.idle_timer.touch()

        # look up the protobuf class and parse the message
        pbtype = messages.get_type(msg.type)
        return protobuf.load_message(msg.data, pbtype,
                                     field_cache)  # type: ignore
Exemplo n.º 7
0
def load_message(msg_type, buffer: bytearray) -> protobuf.MessageType:
    return protobuf.load_message(BufferReader(buffer), msg_type)
Exemplo n.º 8
0
async def handle_session(iface: WireInterface,
                         session_id: int,
                         use_workflow: bool = True) -> None:
    ctx = Context(iface, session_id)
    next_msg = None  # type: Optional[codec_v1.Message]
    res_msg = None  # type: Optional[protobuf.MessageType]
    req_type = None
    req_msg = None
    while True:
        try:
            if next_msg is None:
                # We are not currently reading a message, so let's wait for one.
                # If the decoding fails, exception is raised and we try again
                # (with the same `Reader` instance, it's OK).  Even in case of
                # de-synchronized wire communication, report with a message
                # header is eventually received, after a couple of tries.
                msg = await ctx.read_from_wire()

                if __debug__:
                    try:
                        msg_type = messages.get_type(msg.type).__name__
                    except KeyError:
                        msg_type = "%d - unknown message type" % msg.type
                    log.debug(
                        __name__,
                        "%s:%x receive: <%s>",
                        iface.iface_num(),
                        session_id,
                        msg_type,
                    )
            else:
                # We have a reader left over from earlier.  We should process
                # this message instead of waiting for new one.
                msg = next_msg
                next_msg = None

            # Now we are in a middle of reading a message and we need to decide
            # what to do with it, based on its type from the message header.
            # From this point on, we should take care to read it in full and
            # send a response.

            # Take a mark of modules that are imported at this point, so we can
            # roll back and un-import any others.  Should not raise.
            modules = utils.unimport_begin()

            # We need to find a handler for this message type.  Should not
            # raise.
            handler = find_handler(iface, msg.type)

            if handler is None:
                # If no handler is found, we can skip decoding and directly
                # respond with failure.  Should not raise.
                res_msg = unexpected_message()

            else:
                # We found a valid handler for this message type.

                # Workflow task, declared for the finally block
                wf_task = None  # type: Optional[HandlerTask]

                # Here we make sure we always respond with a Failure response
                # in case of any errors.
                try:
                    # Find a protobuf.MessageType subclass that describes this
                    # message.  Raises if the type is not found.
                    req_type = messages.get_type(msg.type)

                    # Try to decode the message according to schema from
                    # `req_type`. Raises if the message is malformed.
                    req_msg = protobuf.load_message(msg.data, req_type)

                    # At this point, message reports are all processed and
                    # correctly parsed into `req_msg`.

                    # Create the workflow task.
                    wf_task = handler(ctx, req_msg)

                    # Run the workflow task.  Workflow can do more on-the-wire
                    # communication inside, but it should eventually return a
                    # response message, or raise an exception (a rather common
                    # thing to do).  Exceptions are handled in the code below.
                    if use_workflow:
                        res_msg = await workflow.spawn(wf_task)
                    else:
                        res_msg = await wf_task

                except UnexpectedMessageError as exc:
                    # Workflow was trying to read a message from the wire, and
                    # something unexpected came in.  See Context.read() for
                    # example, which expects some particular message and raises
                    # UnexpectedMessageError if another one comes in.
                    # In order not to lose the message, we pass on the reader
                    # to get picked up by the workflow logic in the beginning of
                    # the cycle, which processes it in the usual manner.
                    # TODO:
                    # We might handle only the few common cases here, like
                    # Initialize and Cancel.
                    next_msg = exc.msg
                    res_msg = None

                except Exception as exc:
                    # Either:
                    # - the first workflow message had a type that has a
                    #   registered handler, but does not have a protobuf class
                    # - the first workflow message was not a valid protobuf
                    # - workflow raised some kind of an exception while running
                    # - something canceled the workflow from the outside
                    if __debug__:
                        if isinstance(exc, ActionCancelled):
                            log.debug(__name__,
                                      "cancelled: {}".format(exc.message))
                        elif isinstance(exc, loop.TaskClosed):
                            log.debug(__name__,
                                      "cancelled: loop task was closed")
                        else:
                            log.exception(__name__, exc)
                    res_msg = failure(exc)

                finally:
                    # If we ran a workflow task, and a default workflow is on, make sure
                    # we do not race against the layout that is inside.
                    # TODO: this is very hacky and complects wire with the ui
                    if wf_task is not None and workflow.default_task is not None:
                        await ui.wait_until_layout_is_running()

            if res_msg is not None:
                # Either the workflow returned a response, or we created one.
                # Write it on the wire.  Possibly, the incoming message haven't
                # been read in full.  We ignore this case here and let the rest
                # of the reports get processed while waiting for the message
                # header.
                # TODO: if the write fails, we do not unimport the loaded modules
                await ctx.write(res_msg)

            # Cleanup, so garbage collection triggered after un-importing can
            # pick up the trash.
            req_type = None
            req_msg = None
            res_msg = None
            handler = None
            wf_task = None

            # Unload modules imported by the workflow.  Should not raise.
            utils.unimport_end(modules)

        except Exception as exc:
            # The session handling should never exit, just log and continue.
            if __debug__:
                log.exception(__name__, exc)