| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef LIB_FIDL_LLCPP_MESSAGE_H_ |
| #define LIB_FIDL_LLCPP_MESSAGE_H_ |
| |
| #include <lib/fidl/llcpp/message_storage.h> |
| #include <lib/fidl/llcpp/result.h> |
| #include <lib/fidl/txn_header.h> |
| #include <lib/fit/nullable.h> |
| #include <lib/stdcompat/span.h> |
| #include <lib/stdcompat/variant.h> |
| #include <zircon/assert.h> |
| #include <zircon/fidl.h> |
| |
| #include <memory> |
| #include <vector> |
| |
| #ifdef __Fuchsia__ |
| #include <lib/fidl/llcpp/client_end.h> |
| #include <lib/fidl/llcpp/server_end.h> |
| #include <lib/zx/channel.h> |
| #endif // __Fuchsia__ |
| |
| namespace fidl { |
| |
| namespace internal { |
| |
| #ifdef __Fuchsia__ |
| class ClientBase; |
| class ResponseContext; |
| #endif // __Fuchsia__ |
| |
| // Create a type for the 1-element iovec buffer used as the output of fidl_encode_iovec. |
| // TODO(fxbug.dev/66977) Replace this with a resizable buffer type. |
| constexpr size_t IovecBufferSize = 1; |
| using IovecBuffer = zx_channel_iovec_t[IovecBufferSize]; |
| |
| } // namespace internal |
| |
| // Class representing a FIDL message on the write path. |
| class OutgoingMessage : public ::fidl::Result { |
| public: |
| struct ConstructorArgs { |
| zx_channel_iovec_t* iovecs; |
| uint32_t iovec_capacity; |
| zx_handle_disposition_t* handles; |
| uint32_t handle_capacity; |
| uint8_t* backing_buffer; |
| uint32_t backing_buffer_capacity; |
| }; |
| // Creates an object which can manage a FIDL message. |
| // |args.iovecs|, |args.handles| and |args.backing_buffer| contain undefined data that will be |
| // populated during |Encode|. |
| explicit OutgoingMessage(ConstructorArgs args); |
| |
| // Copy and move is disabled for the sake of avoiding double handle close. |
| // It is possible to implement the move operations with correct semantics if they are |
| // ever needed. |
| OutgoingMessage(const OutgoingMessage&) = delete; |
| OutgoingMessage(OutgoingMessage&&) = delete; |
| OutgoingMessage& operator=(const OutgoingMessage&) = delete; |
| OutgoingMessage& operator=(OutgoingMessage&&) = delete; |
| OutgoingMessage() = delete; |
| ~OutgoingMessage(); |
| |
| // Creates an object which can manage a FIDL message. |
| // |c_msg| must contain an already-encoded message. |
| static OutgoingMessage FromEncodedCMessage(const fidl_outgoing_msg_t* c_msg); |
| |
| // Set the txid in the message header. |
| // Requires that there are sufficient bytes to store the header in the buffer. |
| void set_txid(zx_txid_t txid) { |
| ZX_ASSERT(iovec_actual() >= 1 && iovecs()[0].capacity >= sizeof(fidl_message_header_t)); |
| // The byte buffer is const becuase the kernel only reads the bytes. |
| // const_cast is needed to populate it here. |
| reinterpret_cast<fidl_message_header_t*>(const_cast<void*>(iovecs()[0].buffer))->txid = txid; |
| } |
| |
| zx_channel_iovec_t* iovecs() const { return iovec_message().iovecs; } |
| uint32_t iovec_actual() const { return iovec_message().num_iovecs; } |
| zx_handle_disposition_t* handles() const { return iovec_message().handles; } |
| uint32_t handle_actual() const { return iovec_message().num_handles; } |
| fidl_outgoing_msg_t* message() { return &message_; } |
| const fidl_outgoing_msg_t* message() const { return &message_; } |
| |
| // Returns true iff the bytes in this message are identical to the bytes in the argument. |
| bool BytesMatch(const OutgoingMessage& other) const; |
| |
| // Holds a heap-allocated continguous copy of the bytes in this message. |
| // |
| // This owns the allocated buffer and frees it when the object goes out of scope. |
| // To create a |CopiedBytes|, use |CopyBytes|. |
| class CopiedBytes { |
| public: |
| CopiedBytes() = default; |
| CopiedBytes(CopiedBytes&&) = default; |
| CopiedBytes& operator=(CopiedBytes&&) = default; |
| CopiedBytes(const CopiedBytes&) = delete; |
| CopiedBytes& operator=(const CopiedBytes&) = delete; |
| |
| uint8_t* data() { return bytes_.data(); } |
| size_t size() const { return bytes_.size(); } |
| |
| private: |
| explicit CopiedBytes(const OutgoingMessage& msg); |
| |
| std::vector<uint8_t> bytes_; |
| |
| friend class OutgoingMessage; |
| }; |
| |
| // Create a heap-allocated contiguous copy of the bytes in this message. |
| CopiedBytes CopyBytes() const { return CopiedBytes(*this); } |
| |
| // Release the handles to prevent them to be closed by CloseHandles. This method is only useful |
| // when interfacing with low-level channel operations which consume the handles. |
| void ReleaseHandles() { iovec_message().num_handles = 0; } |
| |
| // Encodes the data. |
| template <typename FidlType> |
| void Encode(FidlType* data) { |
| EncodeImpl(FidlType::Type, data); |
| } |
| |
| #ifdef __Fuchsia__ |
| // Uses |zx_channel_write_etc| to write the message. |
| // The message must be in an encoded state before calling |Write|. |
| void Write(zx_handle_t channel) { WriteImpl(channel); } |
| |
| // Various helper functions for writing to other channel-like types. |
| |
| void Write(const ::zx::channel& channel) { Write(channel.get()); } |
| |
| void Write(const ::zx::unowned_channel& channel) { Write(channel->get()); } |
| |
| template <typename Protocol> |
| void Write(::fidl::UnownedClientEnd<Protocol> client_end) { |
| Write(client_end.channel()); |
| } |
| |
| template <typename Protocol> |
| void Write(const ::fidl::ServerEnd<Protocol>& server_end) { |
| Write(server_end.channel().get()); |
| } |
| |
| // For requests with a response, uses zx_channel_call_etc to write the message. |
| // Before calling Call, Encode must be called. |
| // If the call succeed, |result_bytes| contains the decoded result. |
| template <typename FidlType> |
| void Call(zx_handle_t channel, uint8_t* result_bytes, uint32_t result_capacity, |
| zx_time_t deadline = ZX_TIME_INFINITE) { |
| CallImpl(FidlType::Type, channel, result_bytes, result_capacity, deadline); |
| } |
| |
| // Helper function for making a call over other channel-like types. |
| template <typename FidlType, typename Protocol> |
| void Call(::fidl::UnownedClientEnd<Protocol> client_end, uint8_t* result_bytes, |
| uint32_t result_capacity, zx_time_t deadline = ZX_TIME_INFINITE) { |
| CallImpl(FidlType::Type, client_end.handle(), result_bytes, result_capacity, deadline); |
| } |
| |
| // For asynchronous clients, writes a request. |
| ::fidl::Result Write(::fidl::internal::ClientBase* client, |
| ::fidl::internal::ResponseContext* context); |
| #endif |
| |
| protected: |
| OutgoingMessage(fidl_outgoing_msg_t msg, uint32_t handle_capacity) |
| : ::fidl::Result(::fidl::Result::Ok()), message_(msg), handle_capacity_(handle_capacity) {} |
| |
| void EncodeImpl(const fidl_type_t* message_type, void* data); |
| |
| #ifdef __Fuchsia__ |
| void WriteImpl(zx_handle_t channel); |
| void CallImpl(const fidl_type_t* response_type, zx_handle_t channel, uint8_t* result_bytes, |
| uint32_t result_capacity, zx_time_t deadline); |
| #endif |
| |
| uint32_t iovec_capacity() const { return iovec_capacity_; } |
| uint32_t handle_capacity() const { return handle_capacity_; } |
| uint32_t backing_buffer_capacity() const { return backing_buffer_capacity_; } |
| uint8_t* backing_buffer() const { return backing_buffer_; } |
| |
| private: |
| explicit OutgoingMessage(const fidl_outgoing_msg_t* msg); |
| |
| fidl_outgoing_msg_iovec_t& iovec_message() { |
| ZX_DEBUG_ASSERT(message_.type == FIDL_OUTGOING_MSG_TYPE_IOVEC); |
| return message_.iovec; |
| } |
| const fidl_outgoing_msg_iovec_t& iovec_message() const { |
| ZX_DEBUG_ASSERT(message_.type == FIDL_OUTGOING_MSG_TYPE_IOVEC); |
| return message_.iovec; |
| } |
| |
| using Result::SetResult; |
| |
| fidl_outgoing_msg_t message_ = {}; |
| uint32_t iovec_capacity_ = 0; |
| uint32_t handle_capacity_ = 0; |
| uint32_t backing_buffer_capacity_ = 0; |
| uint8_t* backing_buffer_ = nullptr; |
| |
| // If OutgoingMessage is constructed with a fidl_outgoing_msg_t* that contains bytes |
| // rather than iovec, it is converted to a single-element iovec pointing to the bytes. |
| zx_channel_iovec_t converted_byte_message_iovec_ = {}; |
| }; |
| |
| namespace internal { |
| |
| template <typename T> |
| class DecodedMessageBase; |
| |
| } // namespace internal |
| |
| // |IncomingMessage| represents a FIDL message on the read path. |
| // Each instantiation of the class should only be used for one message. |
| // |
| // |IncomingMessage|s are created with the results from reading from a channel. |
| // By default, it assumes it is a transactional message, and automatically |
| // performs necessary validation on the message header - users may opt out |
| // via the |kSkipMessageHeaderValidation| constructor overload in the case of |
| // regular FIDL type encoding/decoding. |
| // |
| // |IncomingMessage| relinquishes the ownership of the handles after decoding. |
| // Instead, callers must adopt the decoded content into another RAII class, such |
| // as |fidl::DecodedMessage<FidlType>|. |
| // |
| // Functions that take |IncomingMessage&| conditionally take ownership of the |
| // message. For functions in the public API, they must then indicate through |
| // their return value if they took ownership. For functions in the binding |
| // internals, it is sufficient to only document the conditions where minimum |
| // overhead is desired. |
| // |
| // Functions that take |IncomingMessage&&| always take ownership of the message. |
| // In practice, this means that they must either decode the message, or close |
| // the handles, or move the message into a deeper function that takes |
| // |IncomingMessage&&|. |
| // |
| // For efficiency, errors are stored inside this object. Callers must check for |
| // errors after construction, and after performing each operation on the object. |
| // |
| // An |IncomingMessage| may be created from |fidl::ChannelReadEtc|: |
| // |
| // fidl::IncomingMessage msg = fidl::ChannelReadEtc(handle, 0, byte_span, handle_span); |
| // if (!msg.ok()) { /* ... error handling ... */ } |
| // |
| class IncomingMessage : public ::fidl::Result { |
| public: |
| // Creates an object which can manage a FIDL message. Allocated memory is not owned by |
| // the |IncomingMessage|, but handles are owned by it and cleaned up when the |
| // |IncomingMessage| is destructed. |
| // |
| // The bytes must represent a transactional message. See |
| // https://fuchsia.dev/fuchsia-src/reference/fidl/language/wire-format?hl=en#transactional-messages |
| IncomingMessage(uint8_t* bytes, uint32_t byte_actual, zx_handle_info_t* handles, |
| uint32_t handle_actual); |
| |
| // Creates an |IncomingMessage| from a C |fidl_incoming_msg_t| already in |
| // encoded form. This should only be used when interfacing with C APIs. |
| // The handles in |c_msg| are owned by the returned |IncomingMessage| object. |
| // |
| // The bytes must represent a transactional message. |
| static IncomingMessage FromEncodedCMessage(const fidl_incoming_msg_t* c_msg); |
| |
| struct SkipMessageHeaderValidationTag {}; |
| |
| // A marker that instructs the constructor of |IncomingMessage| to skip |
| // validating the message header. This is useful when the message is not a |
| // transactional message. |
| constexpr inline static auto kSkipMessageHeaderValidation = SkipMessageHeaderValidationTag{}; |
| |
| // An overload for when the bytes do not represent a transactional message. |
| // |
| // This constructor should be rarely used in practice. When decoding |
| // FIDL types that are not transactional messages (e.g. tables), consider |
| // using the constructor in |FidlType::DecodedMessage|, which delegates |
| // here appropriately. |
| IncomingMessage(uint8_t* bytes, uint32_t byte_actual, zx_handle_info_t* handles, |
| uint32_t handle_actual, SkipMessageHeaderValidationTag); |
| |
| // Creates an empty incoming message representing an error (e.g. failed to read from |
| // a channel). |
| // |
| // |failure| must contain an error result. |
| explicit IncomingMessage(const ::fidl::Result& failure); |
| |
| IncomingMessage(const IncomingMessage&) = delete; |
| IncomingMessage& operator=(const IncomingMessage&) = delete; |
| |
| IncomingMessage(IncomingMessage&& other) noexcept : ::fidl::Result(other) { |
| MoveImpl(std::move(other)); |
| } |
| IncomingMessage& operator=(IncomingMessage&& other) noexcept { |
| ::fidl::Result::operator=(other); |
| if (this != &other) { |
| MoveImpl(std::move(other)); |
| } |
| return *this; |
| } |
| |
| ~IncomingMessage(); |
| |
| fidl_message_header_t* header() const { |
| ZX_DEBUG_ASSERT(ok()); |
| return reinterpret_cast<fidl_message_header_t*>(bytes()); |
| } |
| |
| // If the message is an epitaph, returns a pointer to the epitaph structure. |
| // Otherwise, returns null. |
| fit::nullable<fidl_epitaph_t*> maybe_epitaph() const { |
| ZX_DEBUG_ASSERT(ok()); |
| if (unlikely(header()->ordinal == kFidlOrdinalEpitaph)) { |
| return fit::nullable(reinterpret_cast<fidl_epitaph_t*>(bytes())); |
| } |
| return fit::nullable<fidl_epitaph_t*>{}; |
| } |
| |
| uint8_t* bytes() const { return reinterpret_cast<uint8_t*>(message_.bytes); } |
| uint32_t byte_actual() const { return message_.num_bytes; } |
| |
| zx_handle_info_t* handles() const { return message_.handles; } |
| uint32_t handle_actual() const { return message_.num_handles; } |
| |
| // Convert the incoming message to its C API counterpart, releasing the |
| // ownership of handles to the caller in the process. This consumes the |
| // |IncomingMessage|. |
| // |
| // This should only be called while the message is in its encoded form. |
| fidl_incoming_msg_t ReleaseToEncodedCMessage() &&; |
| |
| // Closes the handles managed by this message. This may be used when the |
| // code would like to consume a |IncomingMessage&&| and close its handles, |
| // but does not want to incur the overhead of moving it into a regular |
| // |IncomingMessage| object, and running the destructor. |
| // |
| // This consumes the |IncomingMessage|. |
| void CloseHandles() &&; |
| |
| private: |
| // Only |fidl::DecodedMessage<T>| instances may decode this message. |
| template <typename T> |
| friend class internal::DecodedMessageBase; |
| |
| // Decodes the message using |FidlType|. If this operation succeed, |status()| is ok and |
| // |bytes()| contains the decoded object. |
| // |
| // On success, the handles owned by |IncomingMessage| are transferred to the decoded bytes. |
| // |
| // This method should be used after a read. |
| template <typename FidlType> |
| void Decode() { |
| Decode(FidlType::Type); |
| } |
| |
| // Release the handle ownership after the message has been converted to its |
| // decoded form. When used standalone and not as part of a |Decode|, this |
| // method is only useful when interfacing with C APIs. |
| void ReleaseHandles() { message_.num_handles = 0; } |
| |
| void MoveImpl(IncomingMessage&& other) noexcept { |
| message_ = other.message_; |
| other.ReleaseHandles(); |
| } |
| |
| // Decodes the message using |message_type|. If this operation succeed, |status()| is ok and |
| // |bytes()| contains the decoded object. |
| // |
| // On success, the handles owned by |IncomingMessage| are transferred to the decoded bytes. |
| // |
| // This method should be used after a read. |
| void Decode(const fidl_type_t* message_type); |
| |
| // Performs basic transactional message header validation and sets the |fidl::Result| fields |
| // accordingly. |
| void Validate(); |
| |
| fidl_incoming_msg_t message_; |
| }; |
| |
| #ifdef __Fuchsia__ |
| |
| // Wrapper around |zx_channel_read_etc| that instantiates an |IncomingMessage| |
| // with the contents read from the |channel|, referencing the |bytes_storage| |
| // and |handles_storage| buffers. The channel should contain transactional FIDL |
| // messages, which the instantiated |IncomingMessage| will automatically validate. |
| // |
| // Error information is embedded in the returned |IncomingMessage| when applicable. |
| IncomingMessage ChannelReadEtc(zx_handle_t channel, uint32_t options, |
| ::fidl::BufferSpan bytes_storage, |
| cpp20::span<zx_handle_info_t> handles_storage); |
| |
| #endif // __Fuchsia__ |
| |
| namespace internal { |
| |
| // DecodedMessageBase implements the common behavior to all |
| // |fidl::DecodedMessage<T>| subclasses. They may be created from an incoming |
| // message in encoded form, in which case they would perform the necessary |
| // decoding and own the decoded handles via RAII. |
| // |
| // |DecodedMessageBase| should never be instantiated directly. Rather, a |
| // subclass should be defined which adds the FIDL type-specific handle RAII |
| // behavior. |
| template <typename FidlType> |
| class DecodedMessageBase : public ::fidl::Result { |
| public: |
| // Creates an |DecodedMessageBase| by decoding the incoming message |msg|. |
| // Consumes |msg|. |
| explicit DecodedMessageBase(::fidl::IncomingMessage&& msg) { |
| bytes_ = msg.bytes(); |
| msg.Decode<FidlType>(); |
| SetResult(msg); |
| } |
| |
| protected: |
| DecodedMessageBase(const DecodedMessageBase&) = delete; |
| DecodedMessageBase(DecodedMessageBase&&) = delete; |
| DecodedMessageBase& operator=(const DecodedMessageBase&) = delete; |
| DecodedMessageBase& operator=(DecodedMessageBase&&) = delete; |
| |
| ~DecodedMessageBase() = default; |
| |
| uint8_t* bytes() const { return bytes_; } |
| |
| void ResetBytes() { bytes_ = nullptr; } |
| |
| private: |
| uint8_t* bytes_ = nullptr; |
| }; |
| |
| } // namespace internal |
| |
| // This class owns a message of |FidlType| and encodes the message automatically upon construction |
| // into a byte buffer. |
| template <typename FidlType> |
| using OwnedEncodedMessage = typename FidlType::OwnedEncodedMessage; |
| |
| // This class manages the handles within |FidlType| and encodes the message automatically upon |
| // construction. Different from |OwnedEncodedMessage|, it takes in a caller-allocated buffer and |
| // uses that as the backing storage for the message. The buffer must outlive instances of this |
| // class. |
| template <typename FidlType> |
| using UnownedEncodedMessage = typename FidlType::UnownedEncodedMessage; |
| |
| // This class manages the handles within |FidlType| and decodes the message automatically upon |
| // construction. It always borrows external buffers for the backing storage of the message. |
| // This class should mostly be used for tests. |
| template <typename FidlType> |
| using DecodedMessage = typename FidlType::DecodedMessage; |
| |
| // Holds the result of converting an outgoing message to an incoming message. |
| // |
| // |OutgoingToIncomingMessage| objects own the bytes and handles resulting from |
| // conversion. |
| class OutgoingToIncomingMessage { |
| public: |
| // Converts an outgoing message to an incoming message. |
| // |
| // In doing so, it will make syscalls to fetch rights and type information |
| // of any provided handles. The caller is responsible for ensuring that |
| // returned handle rights and object types are checked appropriately. |
| // |
| // The constructed |OutgoingToIncomingMessage| will take ownership over |
| // handles from the input |OutgoingMessage|. |
| explicit OutgoingToIncomingMessage(OutgoingMessage& input); |
| |
| ~OutgoingToIncomingMessage() = default; |
| |
| fidl::IncomingMessage& incoming_message() & { |
| ZX_DEBUG_ASSERT(ok()); |
| return incoming_message_; |
| } |
| |
| [[nodiscard]] zx_status_t status() const { return incoming_message_.status(); } |
| [[nodiscard]] bool ok() const { return incoming_message_.ok(); } |
| [[nodiscard]] const char* error_message() const { return incoming_message_.error_message(); } |
| |
| private: |
| static fidl::IncomingMessage ConversionImpl(OutgoingMessage& input, |
| OutgoingMessage::CopiedBytes& buf_bytes, |
| std::unique_ptr<zx_handle_info_t[]>& buf_handles); |
| |
| OutgoingMessage::CopiedBytes buf_bytes_; |
| std::unique_ptr<zx_handle_info_t[]> buf_handles_ = {}; |
| fidl::IncomingMessage incoming_message_; |
| }; |
| |
| } // namespace fidl |
| |
| #endif // LIB_FIDL_LLCPP_MESSAGE_H_ |