From 5cae26e22b51e75691fb9f01dcb447a85df2c075 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 23 Jan 2026 10:52:42 -0800 Subject: [PATCH] [coap] simplify `Coap::Message` implementation (#12313) This change simplifies the `Coap::Message` implementation and removes the fragile `HelpData` struct which was used to cache header information within reserved portion of message. The `Coap::Msg` class is updated to hold the parsed CoAP header information (type, code, message ID, token). It now inherits from a new `HeaderInfo` class which contains the parsed fields. This change helps to simplify many of the call sites which previously had to parse the header information themselves. The key changes are: - The `HelpData` struct is removed from `Coap::Message`. - `Coap::Msg` is updated to track parsed header info in `HeaderInfo`. - `otCoapMessageInit()` and `otCoapMessageInitResponse()` now return an `otError`. - Methods are renamed to harmonize their names. - A new unit test `test_coap_message.cpp` is added to verify the `Coap::Message` implementation. --- include/openthread/coap.h | 31 +- include/openthread/instance.h | 2 +- src/cli/cli_coap.cpp | 11 +- src/cli/cli_coap_secure.cpp | 2 +- src/core/api/coap_api.cpp | 22 +- src/core/backbone_router/bbr_manager.cpp | 12 +- src/core/coap/coap.cpp | 208 +++++--- src/core/coap/coap.hpp | 52 +- src/core/coap/coap_message.cpp | 425 +++++++++------ src/core/coap/coap_message.hpp | 567 +++++++++++---------- src/core/coap/coap_secure.cpp | 2 +- src/core/meshcop/announce_begin_client.cpp | 2 +- src/core/meshcop/border_agent.cpp | 36 +- src/core/meshcop/border_agent.hpp | 4 +- src/core/meshcop/commissioner.cpp | 12 +- src/core/meshcop/energy_scan_client.cpp | 4 +- src/core/meshcop/joiner.cpp | 9 +- src/core/meshcop/joiner_router.cpp | 4 +- src/core/meshcop/panid_query_client.cpp | 4 +- src/core/meshcop/tcat_agent.cpp | 2 +- src/core/thread/address_resolver.cpp | 13 +- src/core/thread/announce_begin_server.cpp | 4 +- src/core/thread/anycast_locator.cpp | 2 +- src/core/thread/dua_manager.cpp | 8 +- src/core/thread/energy_scan_server.cpp | 4 +- src/core/thread/mle.hpp | 2 +- src/core/thread/mle_ftd.cpp | 18 +- src/core/thread/mlr_manager.cpp | 2 +- src/core/thread/network_diagnostic.cpp | 14 +- src/core/thread/panid_query_server.cpp | 4 +- src/core/utils/history_tracker_client.cpp | 2 +- src/core/utils/history_tracker_server.cpp | 6 +- src/core/utils/otns.cpp | 10 +- tests/unit/CMakeLists.txt | 1 + tests/unit/test_coap_message.cpp | 429 ++++++++++++++++ 35 files changed, 1294 insertions(+), 636 deletions(-) create mode 100644 tests/unit/test_coap_message.cpp diff --git a/include/openthread/coap.h b/include/openthread/coap.h index 15adca24d..ec928581a 100644 --- a/include/openthread/coap.h +++ b/include/openthread/coap.h @@ -312,26 +312,33 @@ typedef struct otCoapTxParameters // `otCoapMessage*` APIs - Constructing or parsing a CoAP message. /** - * Initializes the CoAP header. + * Initializes a CoAP message. * - * @param[in,out] aMessage A pointer to the CoAP message to initialize. - * @param[in] aType CoAP message type. - * @param[in] aCode CoAP message code. + * This function initializes the CoAP header, erasing any previously written content in the message. The Message ID is + * set to zero, and the token is empty (zero-length). + * + * @param[in,out] aMessage A pointer to the CoAP message to initialize. + * @param[in] aType The CoAP Type. + * @param[in] aCode The CoAP Code. + * + * @retval OT_ERROR_NONE Successfully initialized the message. + * @retval OT_ERROR_NO_BUFS Insufficient message buffers available to write the CoAP header. */ -void otCoapMessageInit(otMessage *aMessage, otCoapType aType, otCoapCode aCode); +otError otCoapMessageInit(otMessage *aMessage, otCoapType aType, otCoapCode aCode); /** - * Initializes a response message. + * Initializes a CoAP message as a response to a request. * - * @note Both message ID and token are set according to @p aRequest. + * This function initializes the CoAP header, erasing any previously written content in the message. The Message ID and + * Token are copied from the request message. * - * @param[in,out] aResponse A pointer to the CoAP response message. - * @param[in] aRequest A pointer to the CoAP request message. - * @param[in] aType CoAP message type. - * @param[in] aCode CoAP message code. + * @param[in,out] aResponse A pointer to the CoAP response message to initialize. + * @param[in] aRequest A pointer to the CoAP request message. + * @param[in] aType The CoAP Type for the response. + * @param[in] aCode The CoAP Code for the response. * * @retval OT_ERROR_NONE Successfully initialized the response message. - * @retval OT_ERROR_NO_BUFS Insufficient message buffers available to initialize the response message. + * @retval OT_ERROR_NO_BUFS Insufficient message buffers available to write the CoAP header. */ otError otCoapMessageInitResponse(otMessage *aResponse, const otMessage *aRequest, otCoapType aType, otCoapCode aCode); diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 66b98ac32..9ba8a3b5f 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (573) +#define OPENTHREAD_API_VERSION (574) /** * @addtogroup api-instance diff --git a/src/cli/cli_coap.cpp b/src/cli/cli_coap.cpp index 5aba2116e..eec0bf96c 100644 --- a/src/cli/cli_coap.cpp +++ b/src/cli/cli_coap.cpp @@ -89,7 +89,7 @@ otError Coap::CancelResourceSubscription(bool aSendCancelMessage) message = otCoapNewMessage(GetInstancePtr(), nullptr); VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); - otCoapMessageInit(message, OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_GET); + SuccessOrExit(error = otCoapMessageInit(message, OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_GET)); SuccessOrExit(error = otCoapMessageWriteToken(message, &mRequestToken)); SuccessOrExit(error = otCoapMessageAppendObserveOption(message, 1)); @@ -268,9 +268,10 @@ template <> otError Coap::Process(Arg aArgs[]) notificationMessage = otCoapNewMessage(GetInstancePtr(), nullptr); VerifyOrExit(notificationMessage != nullptr, error = OT_ERROR_NO_BUFS); - otCoapMessageInit(notificationMessage, - (isConNotification ? OT_COAP_TYPE_CONFIRMABLE : OT_COAP_TYPE_NON_CONFIRMABLE), - OT_COAP_CODE_CONTENT); + SuccessOrExit( + error = otCoapMessageInit(notificationMessage, + (isConNotification ? OT_COAP_TYPE_CONFIRMABLE : OT_COAP_TYPE_NON_CONFIRMABLE), + OT_COAP_CODE_CONTENT)); SuccessOrExit(error = otCoapMessageWriteToken(notificationMessage, &mSubscriberToken)); SuccessOrExit(error = otCoapMessageAppendObserveOption(notificationMessage, mObserveSerial++)); @@ -704,7 +705,7 @@ otError Coap::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode) message = otCoapNewMessage(GetInstancePtr(), nullptr); VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); - otCoapMessageInit(message, coapType, aCoapCode); + SuccessOrExit(error = otCoapMessageInit(message, coapType, aCoapCode)); otCoapMessageGenerateToken(message, OT_COAP_DEFAULT_TOKEN_LENGTH); #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE diff --git a/src/cli/cli_coap_secure.cpp b/src/cli/cli_coap_secure.cpp index 8b32c83c7..348b35499 100644 --- a/src/cli/cli_coap_secure.cpp +++ b/src/cli/cli_coap_secure.cpp @@ -525,7 +525,7 @@ otError CoapSecure::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode) message = otCoapNewMessage(GetInstancePtr(), nullptr); VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); - otCoapMessageInit(message, coapType, aCoapCode); + SuccessOrExit(error = otCoapMessageInit(message, coapType, aCoapCode)); otCoapMessageGenerateToken(message, OT_COAP_DEFAULT_TOKEN_LENGTH); SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri)); diff --git a/src/core/api/coap_api.cpp b/src/core/api/coap_api.cpp index 2322c31fa..72eedb9ad 100644 --- a/src/core/api/coap_api.cpp +++ b/src/core/api/coap_api.cpp @@ -44,20 +44,14 @@ otMessage *otCoapNewMessage(otInstance *aInstance, const otMessageSettings *aSet return AsCoreType(aInstance).Get().NewMessage(Message::Settings::From(aSettings)); } -void otCoapMessageInit(otMessage *aMessage, otCoapType aType, otCoapCode aCode) +otError otCoapMessageInit(otMessage *aMessage, otCoapType aType, otCoapCode aCode) { - AsCoapMessage(aMessage).Init(MapEnum(aType), MapEnum(aCode)); + return AsCoapMessage(aMessage).Init(MapEnum(aType), MapEnum(aCode)); } otError otCoapMessageInitResponse(otMessage *aResponse, const otMessage *aRequest, otCoapType aType, otCoapCode aCode) { - Coap::Message &response = AsCoapMessage(aResponse); - const Coap::Message &request = AsCoapMessage(aRequest); - - response.Init(MapEnum(aType), MapEnum(aCode)); - response.SetMessageId(request.GetMessageId()); - - return response.WriteTokenFromMessage(request); + return AsCoapMessage(aResponse).InitAsResponse(MapEnum(aType), MapEnum(aCode), AsCoapMessage(aRequest)); } otError otCoapMessageWriteToken(otMessage *aMessage, const otCoapToken *aToken) @@ -151,23 +145,23 @@ otError otCoapMessageAppendUriQueryOption(otMessage *aMessage, const char *aUriQ return AsCoapMessage(aMessage).AppendUriQueryOption(aUriQuery); } -otError otCoapMessageSetPayloadMarker(otMessage *aMessage) { return AsCoapMessage(aMessage).SetPayloadMarker(); } +otError otCoapMessageSetPayloadMarker(otMessage *aMessage) { return AsCoapMessage(aMessage).AppendPayloadMarker(); } otCoapType otCoapMessageGetType(const otMessage *aMessage) { - return static_cast(AsCoapMessage(aMessage).GetType()); + return static_cast(AsCoapMessage(aMessage).ReadType()); } otCoapCode otCoapMessageGetCode(const otMessage *aMessage) { - return static_cast(AsCoapMessage(aMessage).GetCode()); + return static_cast(AsCoapMessage(aMessage).ReadCode()); } -void otCoapMessageSetCode(otMessage *aMessage, otCoapCode aCode) { AsCoapMessage(aMessage).SetCode(MapEnum(aCode)); } +void otCoapMessageSetCode(otMessage *aMessage, otCoapCode aCode) { AsCoapMessage(aMessage).WriteCode(MapEnum(aCode)); } const char *otCoapMessageCodeToString(const otMessage *aMessage) { return AsCoapMessage(aMessage).CodeToString(); } -uint16_t otCoapMessageGetMessageId(const otMessage *aMessage) { return AsCoapMessage(aMessage).GetMessageId(); } +uint16_t otCoapMessageGetMessageId(const otMessage *aMessage) { return AsCoapMessage(aMessage).ReadMessageId(); } otError otCoapMessageReadToken(const otMessage *aMessage, otCoapToken *aToken) { diff --git a/src/core/backbone_router/bbr_manager.cpp b/src/core/backbone_router/bbr_manager.cpp index 63b5378e9..3b7620aed 100644 --- a/src/core/backbone_router/bbr_manager.cpp +++ b/src/core/backbone_router/bbr_manager.cpp @@ -149,7 +149,7 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Msg &aMsg) bool hasCommissionerSessionIdTlv = false; bool processTimeoutTlv = false; - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest(), error = kErrorParse); + VerifyOrExit(aMsg.IsConfirmablePostRequest(), error = kErrorParse); #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE // Required by Test Specification 5.10.22 DUA-TC-26, only for certification purpose @@ -366,7 +366,7 @@ void Manager::HandleDuaRegistration(const Coap::Msg &aMsg) #endif VerifyOrExit(aMsg.mMessageInfo.GetPeerAddr().GetIid().IsRoutingLocator(), error = kErrorDrop); - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest(), error = kErrorParse); + VerifyOrExit(aMsg.IsConfirmablePostRequest(), error = kErrorParse); SuccessOrExit(error = Tlv::Find(aMsg.mMessage, target)); SuccessOrExit(error = Tlv::Find(aMsg.mMessage, meshLocalIid)); @@ -542,7 +542,7 @@ template <> void Manager::HandleTmf(Coap::Msg &aMsg) VerifyOrExit(aMsg.mMessageInfo.IsHostInterface(), error = kErrorDrop); VerifyOrExit(Get().IsPrimary(), error = kErrorInvalidState); - VerifyOrExit(aMsg.mMessage.IsNonConfirmablePostRequest(), error = kErrorParse); + VerifyOrExit(aMsg.IsNonConfirmablePostRequest(), error = kErrorParse); SuccessOrExit(error = Tlv::Find(aMsg.mMessage, dua)); @@ -574,9 +574,9 @@ template <> void Manager::HandleTmf(Coap::Msg &aMsg) VerifyOrExit(aMsg.mMessageInfo.IsHostInterface(), error = kErrorDrop); VerifyOrExit(Get().IsPrimary(), error = kErrorInvalidState); - VerifyOrExit(aMsg.mMessage.IsPostRequest(), error = kErrorParse); + VerifyOrExit(aMsg.IsPostRequest(), error = kErrorParse); - proactive = !aMsg.mMessage.IsConfirmable(); + proactive = !aMsg.IsConfirmable(); SuccessOrExit(error = Tlv::Find(aMsg.mMessage, dua)); SuccessOrExit(error = Tlv::Find(aMsg.mMessage, meshLocalIid)); @@ -637,7 +637,7 @@ Error Manager::SendBackboneAnswer(const Ip6::Address &aDstAddr, SuccessOrExit(error = message->Init(proactive ? Coap::kTypeNonConfirmable : Coap::kTypeConfirmable, Coap::kCodePost, kUriBackboneAnswer)); - SuccessOrExit(error = message->SetPayloadMarker()); + SuccessOrExit(error = message->AppendPayloadMarker()); SuccessOrExit(error = Tlv::Append(*message, aDua)); diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp index c28d577ab..8f5d80fd7 100644 --- a/src/core/coap/coap.cpp +++ b/src/core/coap/coap.cpp @@ -40,6 +40,81 @@ namespace Coap { RegisterLogModule("Coap"); +//--------------------------------------------------------------------------------------------------------------------- +// Msg + +Error Msg::ParseHeaderAndOptions(PayloadMarkerMode aPayloadMarkerMode) +{ + // Parses and validates CoAP headers and options. Assumes + // `GetHeaderOffset()` points to the header start. + // `aPayloadMarkerMode` determines behavior regarding "payload + // marker". Either rejects the message if payload marker is present + // with no payload (used on a received message), or removes the + // payload marker in such a situation (used on a message to be + // sent). Upon completion, `GetOffset()` is updated to the start of + // the payload. + + Error error; + Option::Iterator iterator; + uint16_t payloadOffset; + bool emptyPayload; + + SuccessOrExit(error = mMessage.ParseHeaderInfo(*this)); + + SuccessOrExit(error = iterator.Init(mMessage)); + + while (!iterator.IsDone()) + { + SuccessOrExit(error = iterator.Advance()); + } + + payloadOffset = iterator.GetPayloadMessageOffset(); + emptyPayload = (payloadOffset == mMessage.GetLength()); + + if (iterator.HasPayloadMarker()) + { + switch (aPayloadMarkerMode) + { + case kRejectIfNoPayloadWithPayloadMarker: + VerifyOrExit(!emptyPayload, error = kErrorParse); + break; + + case kRemovePayloadMarkerIfNoPayload: + if (emptyPayload) + { + mMessage.RemoveFooter(sizeof(uint8_t)); + payloadOffset--; + } + break; + } + } + + mMessage.SetOffset(payloadOffset); + +exit: + return error; +} + +uint16_t Msg::GetHeaderSize(void) const +{ + // Determines the size of the CoAP header including the token + // but excluding any appended Options. + + return sizeof(Message::Header) + GetToken().GetLength(); +} + +void Msg::UpdateType(Type aType) +{ + mType = aType; + mMessage.WriteType(aType); +} + +void Msg::UpdateMessageId(uint16_t aMessageId) +{ + mMessageId = aMessageId; + mMessage.WriteMessageId(aMessageId); +} + //--------------------------------------------------------------------------------------------------------------------- // CoapBase @@ -137,7 +212,7 @@ Message *CoapBase::InitMessage(Message *aMessage, Type aType, Uri aUri) VerifyOrExit(aMessage != nullptr); SuccessOrExit(error = aMessage->Init(aType, kCodePost, aUri)); - SuccessOrExit(error = aMessage->SetPayloadMarker()); + SuccessOrExit(error = aMessage->AppendPayloadMarker()); exit: FreeAndNullMessageOnError(aMessage, error); @@ -150,8 +225,8 @@ Message *CoapBase::InitResponse(Message *aMessage, const Message &aRequest) VerifyOrExit(aMessage != nullptr); - SuccessOrExit(error = aMessage->SetDefaultResponseHeader(aRequest)); - SuccessOrExit(error = aMessage->SetPayloadMarker()); + SuccessOrExit(error = aMessage->InitAsResponse(kTypeAck, kCodeChanged, aRequest)); + SuccessOrExit(error = aMessage->AppendPayloadMarker()); exit: FreeAndNullMessageOnError(aMessage, error); @@ -199,46 +274,46 @@ Error CoapBase::SendMessage(Message &aMessage, Msg txMsg(aMessage, aMessageInfo); Metadata metadata; + SuccessOrExit(error = txMsg.ParseHeaderAndOptions(Msg::kRemovePayloadMarkerIfNoPayload)); + if (aTxParameters == nullptr) { aTxParameters = &TxParameters::GetDefault(); } else { - SuccessOrExit(error = aTxParameters->ValidateFor(txMsg.mMessage)); + SuccessOrExit(error = aTxParameters->ValidateFor(txMsg)); } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE metadata.mBlockwiseReceiveHook = aReceiveHook; metadata.mBlockwiseTransmitHook = aTransmitHook; - SuccessOrExit(error = ProcessBlockwiseSend(txMsg.mMessage, aTransmitHook, aContext)); + SuccessOrExit(error = ProcessBlockwiseSend(txMsg, aTransmitHook, aContext)); #endif - switch (txMsg.mMessage.GetType()) + switch (txMsg.GetType()) { case kTypeAck: mResponseCache.Add(txMsg, aTxParameters->CalculateExchangeLifetime()); break; case kTypeReset: - OT_ASSERT(txMsg.mMessage.GetCode() == kCodeEmpty); + OT_ASSERT(txMsg.GetCode() == kCodeEmpty); break; default: - txMsg.mMessage.SetMessageId(mMessageId++); + txMsg.UpdateMessageId(mMessageId++); break; } - txMsg.mMessage.Finish(); - - if (txMsg.mMessage.IsConfirmable()) + if (txMsg.IsConfirmable()) { copyLength = txMsg.mMessage.GetLength(); } - else if (txMsg.mMessage.IsNonConfirmable() && (aHandler != nullptr)) + else if (txMsg.IsNonConfirmable() && (aHandler != nullptr)) { // As we do not retransmit non confirmable messages, create a // copy of header only, for token information. - copyLength = txMsg.mMessage.GetOptionStart(); + copyLength = txMsg.GetHeaderSize(); } if (copyLength > 0) @@ -248,7 +323,8 @@ Error CoapBase::SendMessage(Message &aMessage, bool shouldObserve = false; SuccessOrExit(error = ProcessObserveSend(txMsg, shouldObserve)); - metadata.mObserve = shouldObserve; + metadata.mObserve = shouldObserve; + metadata.mIsRequest = txMsg.IsRequest(); } #endif @@ -261,7 +337,7 @@ Error CoapBase::SendMessage(Message &aMessage, metadata.mRetransmissionsRemaining = aTxParameters->mMaxRetransmit; metadata.mRetransmissionTimeout = aTxParameters->CalculateInitialRetransmissionTimeout(); metadata.mAcknowledged = false; - metadata.mConfirmable = txMsg.mMessage.IsConfirmable(); + metadata.mConfirmable = txMsg.IsConfirmable(); #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE metadata.mHopLimit = txMsg.mMessageInfo.GetHopLimit(); metadata.mIsHostInterface = txMsg.mMessageInfo.IsHostInterface(); @@ -339,7 +415,7 @@ Error CoapBase::SendAck(const Msg &aRxMsg) { return SendEmptyMessage(kTypeAck, a Error CoapBase::SendEmptyAck(const Msg &aRxMsg, Code aCode) { - return (aRxMsg.mMessage.IsConfirmable() ? SendHeaderResponse(aCode, aRxMsg) : kErrorInvalidArgs); + return (aRxMsg.IsConfirmable() ? SendHeaderResponse(aCode, aRxMsg) : kErrorInvalidArgs); } Error CoapBase::SendEmptyAck(const Msg &aRxMsg) { return SendEmptyAck(aRxMsg, kCodeChanged); } @@ -351,14 +427,11 @@ Error CoapBase::SendEmptyMessage(Type aType, const Msg &aRxMsg) Error error = kErrorNone; Message *message = nullptr; - VerifyOrExit(aRxMsg.mMessage.IsConfirmable(), error = kErrorInvalidArgs); + VerifyOrExit(aRxMsg.IsConfirmable(), error = kErrorInvalidArgs); VerifyOrExit((message = NewMessage()) != nullptr, error = kErrorNoBufs); - message->Init(aType, kCodeEmpty); - message->SetMessageId(aRxMsg.mMessage.GetMessageId()); - - message->Finish(); + SuccessOrExit(error = message->Init(aType, kCodeEmpty, aRxMsg.GetMessageId())); SuccessOrExit(error = Send(*message, aRxMsg.mMessageInfo)); exit: @@ -371,18 +444,17 @@ Error CoapBase::SendHeaderResponse(Message::Code aCode, const Msg &aRxMsg) Error error = kErrorNone; Message *message = nullptr; - VerifyOrExit(aRxMsg.mMessage.IsRequest(), error = kErrorInvalidArgs); + VerifyOrExit(aRxMsg.IsRequest(), error = kErrorInvalidArgs); VerifyOrExit((message = NewMessage()) != nullptr, error = kErrorNoBufs); - switch (aRxMsg.mMessage.GetType()) + switch (aRxMsg.GetType()) { case kTypeConfirmable: - message->Init(kTypeAck, aCode); - message->SetMessageId(aRxMsg.mMessage.GetMessageId()); + SuccessOrExit(error = message->Init(kTypeAck, aCode, aRxMsg.GetMessageId())); break; case kTypeNonConfirmable: - message->Init(kTypeNonConfirmable, aCode); + SuccessOrExit(error = message->Init(kTypeNonConfirmable, aCode)); break; default: @@ -408,7 +480,7 @@ void CoapBase::ScheduleRetransmissionTimer(void) metadata.ReadFrom(message); #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE - if (IsObserveSubscription(message, metadata)) + if (IsObserveSubscription(metadata)) { // This is an RFC7641 subscription which is already acknowledged. // We do not time it out, so skip it when determining the next @@ -441,7 +513,7 @@ void CoapBase::HandleRetransmissionTimer(void) if (now >= metadata.mNextTimerShot) { #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE - if (IsObserveSubscription(message, metadata)) + if (IsObserveSubscription(metadata)) { continue; } @@ -570,11 +642,11 @@ Message *CoapBase::FindRelatedRequest(const Msg &aMsg, Metadata &aMetadata) aMetadata.mDestinationPort == aMsg.mMessageInfo.GetPeerPort()) || aMetadata.mDestinationAddress.IsMulticast() || aMetadata.mDestinationAddress.GetIid().IsAnycastLocator())) { - switch (aMsg.mMessage.GetType()) + switch (aMsg.GetType()) { case kTypeReset: case kTypeAck: - if (aMsg.mMessage.GetMessageId() == message.GetMessageId()) + if (aMsg.GetMessageId() == message.ReadMessageId()) { request = &message; ExitNow(); @@ -603,16 +675,18 @@ void CoapBase::Receive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageIn { Msg rxMsg(AsCoapMessage(&aMessage), aMessageInfo); - if (rxMsg.mMessage.ParseHeader() != kErrorNone) + rxMsg.mMessage.SetHeaderOffset(rxMsg.mMessage.GetOffset()); + + if (rxMsg.ParseHeaderAndOptions(Msg::kRejectIfNoPayloadWithPayloadMarker) != kErrorNone) { LogDebg("Failed to parse CoAP header"); - if (!aMessageInfo.GetSockAddr().IsMulticast() && rxMsg.mMessage.IsConfirmable()) + if (!aMessageInfo.GetSockAddr().IsMulticast() && rxMsg.IsConfirmable()) { IgnoreError(SendReset(rxMsg)); } } - else if (rxMsg.mMessage.IsRequest()) + else if (rxMsg.IsRequest()) { ProcessReceivedRequest(rxMsg); } @@ -643,7 +717,7 @@ void CoapBase::ProcessReceivedResponse(Msg &aRxMsg) // response, and we have a response handler; then we're dealing // with RFC7641 rules here. If there is no response handler, then // we're wasting our time! - if (metadata.mObserve && request->IsRequest() && (metadata.mResponseHandler != nullptr)) + if (metadata.mObserve && metadata.mIsRequest && (metadata.mResponseHandler != nullptr)) { Option::Iterator iterator; @@ -652,10 +726,10 @@ void CoapBase::ProcessReceivedResponse(Msg &aRxMsg) } #endif - switch (aRxMsg.mMessage.GetType()) + switch (aRxMsg.GetType()) { case kTypeReset: - if (aRxMsg.mMessage.IsEmpty()) + if (aRxMsg.IsEmpty()) { FinalizeCoapTransaction(*request, metadata, nullptr, kErrorAbort); } @@ -664,12 +738,12 @@ void CoapBase::ProcessReceivedResponse(Msg &aRxMsg) break; case kTypeAck: - if (aRxMsg.mMessage.IsEmpty()) + if (aRxMsg.IsEmpty()) { // Empty acknowledgment. #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE - if (metadata.mObserve && !request->IsRequest()) + if (metadata.mObserve && !metadata.mIsRequest) { // This is the ACK to our RFC7641 CON notification. // There will be no "separate" response so pass it back @@ -697,7 +771,7 @@ void CoapBase::ProcessReceivedResponse(Msg &aRxMsg) } } } - else if (aRxMsg.mMessage.IsResponse() && aRxMsg.mMessage.HasSameTokenAs(*request)) + else if (aRxMsg.IsResponse() && aRxMsg.mMessage.HasSameTokenAs(*request)) { // Piggybacked response. @@ -777,7 +851,7 @@ exit: { bool didHandle = InvokeResponseFallback(aRxMsg); - if (!didHandle && aRxMsg.mMessage.RequireResetOnError()) + if (!didHandle && aRxMsg.RequireResetOnError()) { // Successfully parsed a header but no matching request was // found - reject the message by sending reset. @@ -893,10 +967,10 @@ Error CoapBase::SendMessage(Message &aMessage, return SendMessage(aMessage, aMessageInfo, aTxParameters, aHandler, aContext, nullptr, nullptr); } -Error CoapBase::ProcessBlockwiseSend(Message &aMessage, BlockwiseTransmitHook aTransmitHook, void *aContext) +Error CoapBase::ProcessBlockwiseSend(Msg &aMsg, BlockwiseTransmitHook aTransmitHook, void *aContext) { Error error = kErrorNone; - uint8_t type = aMessage.GetType(); + uint8_t type = aMsg.GetType(); bool moreBlocks = false; uint16_t blockSize; uint8_t buf[kMaxBlockSize]; @@ -906,7 +980,7 @@ Error CoapBase::ProcessBlockwiseSend(Message &aMessage, BlockwiseTransmitHook aT VerifyOrExit(aTransmitHook != nullptr); - SuccessOrExit(aMessage.ReadBlockOptionValues(type == kTypeAck ? kOptionBlock2 : kOptionBlock1, blockInfo)); + SuccessOrExit(aMsg.mMessage.ReadBlockOptionValues(type == kTypeAck ? kOptionBlock2 : kOptionBlock1, blockInfo)); VerifyOrExit(blockInfo.mBlockNumber == 0); @@ -914,17 +988,17 @@ Error CoapBase::ProcessBlockwiseSend(Message &aMessage, BlockwiseTransmitHook aT VerifyOrExit(blockSize <= kMaxBlockSize, error = kErrorNoBufs); SuccessOrExit(error = aTransmitHook(aContext, buf, 0, &blockSize, &moreBlocks)); - SuccessOrExit(error = aMessage.AppendBytes(buf, blockSize)); + SuccessOrExit(error = aMsg.mMessage.AppendBytes(buf, blockSize)); switch (type) { case kTypeAck: - SuccessOrExit(error = CacheLastBlockResponse(&aMessage)); + SuccessOrExit(error = CacheLastBlockResponse(&aMsg.mMessage)); break; case kTypeNonConfirmable: // Block-Wise messages always have to be confirmable - aMessage.SetType(kTypeConfirmable); + aMsg.UpdateType(kTypeConfirmable); break; default: @@ -980,31 +1054,29 @@ Error CoapBase::ProcessBlockwiseResponse(Msg &aRxMsg, Message &aRequest, const M FinalizeCoapTransaction(aRequest, aMetadata, &aRxMsg, kErrorNone); break; case 1: // Block1 option - if (aRxMsg.mMessage.GetCode() == kCodeContinue && aMetadata.mBlockwiseTransmitHook != nullptr) + if (aRxMsg.GetCode() == kCodeContinue && aMetadata.mBlockwiseTransmitHook != nullptr) { error = SendNextBlock1Request(aRequest, aRxMsg, aMetadata); } - if (aRxMsg.mMessage.GetCode() != kCodeContinue || aMetadata.mBlockwiseTransmitHook == nullptr || - error != kErrorNone) + if (aRxMsg.GetCode() != kCodeContinue || aMetadata.mBlockwiseTransmitHook == nullptr || error != kErrorNone) { FinalizeCoapTransaction(aRequest, aMetadata, &aRxMsg, error); } break; case 2: // Block2 option - if (aRxMsg.mMessage.GetCode() < kCodeBadRequest && aMetadata.mBlockwiseReceiveHook != nullptr) + if (aRxMsg.GetCode() < kCodeBadRequest && aMetadata.mBlockwiseReceiveHook != nullptr) { error = SendNextBlock2Request(aRequest, aRxMsg, aMetadata, totalTransferSize, false); } - if (aRxMsg.mMessage.GetCode() >= kCodeBadRequest || aMetadata.mBlockwiseReceiveHook == nullptr || - error != kErrorNone) + if (aRxMsg.GetCode() >= kCodeBadRequest || aMetadata.mBlockwiseReceiveHook == nullptr || error != kErrorNone) { FinalizeCoapTransaction(aRequest, aMetadata, &aRxMsg, error); } break; case 3: // Block1 & Block2 option - if (aRxMsg.mMessage.GetCode() < kCodeBadRequest && aMetadata.mBlockwiseReceiveHook != nullptr) + if (aRxMsg.GetCode() < kCodeBadRequest && aMetadata.mBlockwiseReceiveHook != nullptr) { error = SendNextBlock2Request(aRequest, aRxMsg, aMetadata, totalTransferSize, true); } @@ -1162,12 +1234,12 @@ Error CoapBase::PrepareNextBlockRequest(uint16_t aBlockOptionNumber, Message &aRequest, const BlockInfo &aBlockInfo) { - Error error = kErrorNone; + Error error; bool isOptionSet = false; Option::Iterator iterator; Metadata metadata; - aRequest.Init(kTypeConfirmable, static_cast(aRequestOld.GetCode())); + SuccessOrExit(error = aRequest.Init(kTypeConfirmable, static_cast(aRequestOld.ReadCode()))); metadata.ReadFrom(aRequestOld); metadata.RemoveFrom(aRequestOld); @@ -1253,7 +1325,7 @@ Error CoapBase::SendNextBlock1Request(Message &aRequest, Msg &aRxMsg, const Meta SuccessOrExit(error = PrepareNextBlockRequest(kOptionBlock1, aRequest, *request, requestBlockInfo)); - SuccessOrExit(error = request->SetPayloadMarker()); + SuccessOrExit(error = request->AppendPayloadMarker()); SuccessOrExit(error = request->AppendBytes(buf, blockSize)); @@ -1352,8 +1424,7 @@ Error CoapBase::ProcessBlock1Request(Msg &aRxMsg, const ResourceBlockWise &aReso { // Set up next response VerifyOrExit((response = NewMessage()) != nullptr, error = kErrorFailed); - response->Init(kTypeAck, kCodeContinue); - response->SetMessageId(aRxMsg.mMessage.GetMessageId()); + SuccessOrExit(error = response->Init(kTypeAck, kCodeContinue, aRxMsg.GetMessageId())); SuccessOrExit(error = response->WriteTokenFromMessage(aRxMsg.mMessage)); SuccessOrExit(error = response->AppendBlockOption(kOptionBlock1, msgBlockInfo)); @@ -1405,9 +1476,8 @@ Error CoapBase::ProcessBlock2Request(Msg &aRxMsg, const ResourceBlockWise &aReso } VerifyOrExit((response = NewMessage()) != nullptr, error = kErrorNoBufs); - response->Init(kTypeAck, kCodeContent); - response->SetMessageId(aRxMsg.mMessage.GetMessageId()); + SuccessOrExit(error = response->Init(kTypeAck, kCodeContent, aRxMsg.GetMessageId())); SuccessOrExit(error = response->WriteTokenFromMessage(aRxMsg.mMessage)); responseBlockInfo.mMoreBlocks = false; @@ -1448,7 +1518,7 @@ Error CoapBase::ProcessBlock2Request(Msg &aRxMsg, const ResourceBlockWise &aReso SuccessOrExit(error = iterator.Advance()); } - SuccessOrExit(error = response->SetPayloadMarker()); + SuccessOrExit(error = response->AppendPayloadMarker()); SuccessOrExit(error = response->AppendBytes(buf, blockSize)); if (responseBlockInfo.mMoreBlocks) @@ -1512,7 +1582,7 @@ Error CoapBase::ProcessObserveSend(Msg &aTxMsg, bool &aShouldObserve) // Special case, if we're sending a GET with Observe=1, that is a // cancellation. - if (aShouldObserve && aTxMsg.mMessage.IsGetRequest()) + if (aShouldObserve && aTxMsg.IsGetRequest()) { uint64_t value = 0; @@ -1540,12 +1610,12 @@ exit: return error; } -bool CoapBase::IsObserveSubscription(const Message &aMessage, const Metadata &aMetadata) +bool CoapBase::IsObserveSubscription(const Metadata &aMetadata) { // Indicate whether the message is an RFC7641 subscription which // is already acknowledged. - return aMessage.IsRequest() && aMetadata.mObserve && aMetadata.mAcknowledged; + return aMetadata.mIsRequest && aMetadata.mObserve && aMetadata.mAcknowledged; } #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE @@ -1588,8 +1658,6 @@ Error CoapBase::ResponseCache::SendCachedResponse(const Msg &aRxMsg, CoapBase &a response = match->Clone(match->GetLength() - sizeof(ResponseMetadata)); VerifyOrExit(response != nullptr, error = kErrorNoBufs); - response->Finish(); - error = aCoapBase.Send(*response, aRxMsg.mMessageInfo); exit: @@ -1600,11 +1668,11 @@ exit: const Message *CoapBase::ResponseCache::FindMatching(const Msg &aRxMsg) const { const Message *match = nullptr; - uint16_t requestMsgId = aRxMsg.mMessage.GetMessageId(); + uint16_t requestMsgId = aRxMsg.GetMessageId(); for (const Message &response : mResponses) { - if (response.GetMessageId() == requestMsgId) + if (response.ReadMessageId() == requestMsgId) { ResponseMetadata metadata; @@ -1745,7 +1813,7 @@ const TxParameters &TxParameters::GetDefault(void) return AsCoreType(&kDefaultTxParameters); } -Error TxParameters::ValidateFor(const Message &aMessage) const +Error TxParameters::ValidateFor(const Msg &aMsg) const { Error error = kErrorInvalidArgs; uint32_t duration; @@ -1754,7 +1822,7 @@ Error TxParameters::ValidateFor(const Message &aMessage) const if (mAckTimeout == 0) { // Fire and forget is only allowed for non-confirmable messages. - VerifyOrExit(aMessage.IsNonConfirmable()); + VerifyOrExit(aMsg.IsNonConfirmable()); error = kErrorNone; ExitNow(); } diff --git a/src/core/coap/coap.hpp b/src/core/coap/coap.hpp index 6be0c50fd..78fb74c98 100644 --- a/src/core/coap/coap.hpp +++ b/src/core/coap/coap.hpp @@ -55,6 +55,8 @@ namespace ot { +class UnitTester; + namespace Coap { /** @@ -63,6 +65,7 @@ namespace Coap { * @{ */ +class Msg; class CoapBase; /** @@ -112,7 +115,7 @@ private: static constexpr uint8_t kMaxRetransmit = OT_COAP_MAX_RETRANSMIT; static constexpr uint32_t kMinAckTimeout = OT_COAP_MIN_ACK_TIMEOUT; - Error ValidateFor(const Message &aMessage) const; + Error ValidateFor(const Msg &aMsg) const; uint32_t CalculateInitialRetransmissionTimeout(void) const; uint32_t CalculateExchangeLifetime(void) const; uint32_t CalculateMaxTransmitWait(void) const; @@ -122,11 +125,12 @@ private: }; /** - * Represents a CoAP message and its associated `Ip6::MessageInfo`. + * Represents a CoAP message and its associated `Ip6::MessageInfo` along with parse CoAP Header information. */ -class Msg +class Msg : public HeaderInfo { friend class CoapBase; + friend class ot::UnitTester; public: Message &mMessage; ///< The CoAP message. @@ -138,6 +142,17 @@ private: , mMessageInfo(aMessageInfo) { } + + enum PayloadMarkerMode : uint8_t + { + kRejectIfNoPayloadWithPayloadMarker, + kRemovePayloadMarkerIfNoPayload, + }; + + Error ParseHeaderAndOptions(PayloadMarkerMode aPayloadMarkerMode); + uint16_t GetHeaderSize(void) const; + void UpdateType(Type aType); + void UpdateMessageId(uint16_t aMessageId); }; /** @@ -342,9 +357,9 @@ public: * Allocates and initializes a new CoAP Confirmable Post message with Network Control priority level. * * The CoAP header is initialized as `kTypeConfirmable` and `kCodePost` with a given URI path and a randomly - * generated token (of default length). This method also sets the payload marker (`SetPayloadMarker()` on message. - * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and - * remove the payload marker when there is no payload. + * generated token (of default length). This method also sets the payload marker (`AppendPayloadMarker()` on + * message. Even if message has no payload, calling `AppendPayloadMarker()` is harmless, since `SendMessage()` will + * check and remove the payload marker when there is no payload. * * @param[in] aUri The URI. * @@ -356,8 +371,8 @@ public: * Allocates and initializes a new CoAP Confirmable Post message with normal priority level. * * The CoAP header is initialized as `kTypeConfirmable` and `kCodePost` with a given URI and a randomly - * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`). - * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and + * generated token (of default length). This method also sets the payload marker (calling `AppendPayloadMarker()`). + * Even if message has no payload, calling `AppendPayloadMarker()` is harmless, since `SendMessage()` will check and * remove the payload marker when there is no payload. * * @param[in] aUri The URI. @@ -371,8 +386,8 @@ public: * level. * * The CoAP header is initialized as `kTypeNonConfirmable` and `kCodePost` with a given URI and a randomly - * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`). - * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and + * generated token (of default length). This method also sets the payload marker (calling `AppendPayloadMarker()`). + * Even if message has no payload, calling `AppendPayloadMarker()` is harmless, since `SendMessage()` will check and * remove the payload marker when there is no payload. * * @param[in] aUri The URI. @@ -385,8 +400,8 @@ public: * Allocates and initializes a new CoAP Non-confirmable Post message with normal priority level. * * The CoAP header is initialized as `kTypeNonConfirmable` and `kCodePost` with a given URI and a randomly - * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`). - * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and + * generated token (of default length). This method also sets the payload marker (calling `AppendPayloadMarker()`). + * Even if message has no payload, calling `AppendPayloadMarker()` is harmless, since `SendMessage()` will check and * remove the payload marker when there is no payload. * * @param[in] aUri The URI. @@ -400,8 +415,8 @@ public: * given request message. * * The CoAP header is initialized as `kTypeAck` with `kCodeChanged`. The token and message ID is copied from - * @p aRequest. This method also sets the payload marker (calling `SetPayloadMarker()`). Even if message has - * no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and remove the payload + * @p aRequest. This method also sets the payload marker (calling `AppendPayloadMarker()`). Even if message has + * no payload, calling `AppendPayloadMarker()` is harmless, since `SendMessage()` will check and remove the payload * marker when there is no payload. * * @returns A pointer to the message or `nullptr` if failed to allocate message. @@ -413,8 +428,8 @@ public: * request message. * * The CoAP header is initialized as `kTypeAck` with `kCodeChanged`. The token and message ID is copied from - * @p aRequest. This method also sets the payload marker (calling `SetPayloadMarker()`). Even if message has - * no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and remove the payload + * @p aRequest. This method also sets the payload marker (calling `AppendPayloadMarker()`). Even if message has + * no payload, calling `AppendPayloadMarker()` is harmless, since `SendMessage()` will check and remove the payload * marker when there is no payload. * * @returns A pointer to the message or `nullptr` if failed to allocate message. @@ -754,6 +769,7 @@ private: #endif #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE bool mObserve : 1; // Information that this request involves Observations. + bool mIsRequest : 1; #endif #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE BlockwiseReceiveHook mBlockwiseReceiveHook; // Function pointer called on Block2 response reception. @@ -810,7 +826,7 @@ private: #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE - Error ProcessBlockwiseSend(Message &aMessage, BlockwiseTransmitHook aTransmitHook, void *aContext); + Error ProcessBlockwiseSend(Msg &aMsg, BlockwiseTransmitHook aTransmitHook, void *aContext); Error ProcessBlockwiseResponse(Msg &aRxMsg, Message &aRequest, const Metadata &aMetadata); Error ProcessBlockwiseRequest(Msg &aRxMsg, Message::UriPathStringBuffer &aUriPath, bool &aDidHandle); void FreeLastBlockResponse(void); @@ -835,7 +851,7 @@ private: #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE Error ProcessObserveSend(Msg &aTxMsg, bool &aShouldObserve); - static bool IsObserveSubscription(const Message &aMessage, const Metadata &aMetadata); + static bool IsObserveSubscription(const Metadata &aMetadata); #endif MessageQueue mPendingRequests; diff --git a/src/core/coap/coap_message.cpp b/src/core/coap/coap_message.cpp index 41bab3ce2..7ff6f94f8 100644 --- a/src/core/coap/coap_message.cpp +++ b/src/core/coap/coap_message.cpp @@ -86,31 +86,44 @@ exit: return error; } +//--------------------------------------------------------------------------------------------------------------------- +// `HeaderInfo` + +bool HeaderInfo::IsRequest(void) const { return IsValueInRange(mCode, kCodeGet, kCodeDelete); } + +bool HeaderInfo::IsConfirmablePostRequest(void) const { return IsConfirmable() && IsPostRequest(); } + +bool HeaderInfo::IsNonConfirmablePostRequest(void) const { return IsNonConfirmable() && IsPostRequest(); } + //--------------------------------------------------------------------------------------------------------------------- // `Message` -void Message::Init(void) +Error Message::Init(Type aType, Code aCode) { return Init(aType, aCode, 0); } + +Error Message::Init(Type aType, Code aCode, uint16_t aMessageId) { - GetHelpData().Clear(); - SetVersion(Header::kVersion1); + Header header; + + // Erase any previously written content in the message. + IgnoreError(SetLength(0)); + SetOffset(0); - GetHelpData().mHeaderLength = kMinHeaderLength; + SetHeaderOffset(0); - IgnoreError(SetLength(GetHelpData().mHeaderLength)); -} + header.Clear(); + header.SetVersion(Header::kVersion1); + header.SetType(aType); + header.SetCode(aCode); + header.SetMessageId(aMessageId); -void Message::Init(Type aType, Code aCode) -{ - Init(); - SetType(aType); - SetCode(aCode); + return Append(header); } Error Message::Init(Type aType, Code aCode, Uri aUri) { Error error; - Init(aType, aCode); + SuccessOrExit(error = Init(aType, aCode)); SuccessOrExit(error = WriteRandomToken(Token::kDefaultLength)); SuccessOrExit(error = AppendUriPathOptions(PathForUri(aUri))); @@ -123,23 +136,120 @@ Error Message::InitAsPost(const Ip6::Address &aDestination, Uri aUri) return Init(aDestination.IsMulticast() ? kTypeNonConfirmable : kTypeConfirmable, kCodePost, aUri); } -bool Message::IsConfirmablePostRequest(void) const { return IsConfirmable() && IsPostRequest(); } - -bool Message::IsNonConfirmablePostRequest(void) const { return IsNonConfirmable() && IsPostRequest(); } - -void Message::Finish(void) +Error Message::InitAsResponse(Type aType, Code aCode, const Message &aRequest) { - // If the payload marker is set but the message contains no - // payload, we remove the payload marker from the message. Note - // that the presence of a marker followed by a zero-length payload - // will be processed as a message format error on the receiver. + Error error; - if (GetHelpData().mPayloadMarkerSet && (GetHelpData().mHeaderLength == GetLength())) - { - RemoveFooter(sizeof(uint8_t)); - } + SuccessOrExit(error = Init(aType, aCode, aRequest.ReadMessageId())); + error = WriteTokenFromMessage(aRequest); - WriteBytes(0, &GetHelpData().mHeader, GetOptionStart()); +exit: + return error; +} + +Error Message::ReadHeader(Header &aHeader) const +{ + Error error; + + SuccessOrExit(error = Read(GetHeaderOffset(), aHeader)); + VerifyOrExit(aHeader.GetVersion() == Header::kVersion1, error = kErrorParse); + VerifyOrExit(aHeader.GetTokenLength() <= Token::kMaxLength, error = kErrorParse); + +exit: + return error; +} + +void Message::WriteHeader(const Header &aHeader) { Write(GetHeaderOffset(), aHeader); } + +Error Message::ParseHeaderInfo(HeaderInfo &aInfo) const +{ + Error error; + Header header; + + aInfo.Clear(); + + SuccessOrExit(error = ReadHeader(header)); + + aInfo.mType = header.GetType(); + aInfo.mCode = header.GetCode(); + aInfo.mMessageId = header.GetMessageId(); + + error = ReadToken(header, aInfo.mToken); + +exit: + return error; +} + +uint8_t Message::ReadType(void) const +{ + uint8_t type = 0; + Header header; + + SuccessOrExit(ReadHeader(header)); + type = header.GetType(); + +exit: + return type; +} + +void Message::WriteType(Type aType) +{ + Header header; + + SuccessOrExit(ReadHeader(header)); + header.SetType(aType); + WriteHeader(header); + +exit: + return; +} + +uint8_t Message::ReadCode(void) const +{ + uint8_t code = 0; + Header header; + + SuccessOrExit(ReadHeader(header)); + code = header.GetCode(); + +exit: + return code; +} + +void Message::WriteCode(Code aCode) +{ + Header header; + + SuccessOrExit(ReadHeader(header)); + header.SetCode(aCode); + WriteHeader(header); + +exit: + return; +} + +uint16_t Message::ReadMessageId(void) const +{ + uint16_t messageId = 0; + Header header; + + SuccessOrExit(ReadHeader(header)); + messageId = header.GetMessageId(); + +exit: + return messageId; +} + +void Message::WriteMessageId(uint16_t aMessageId) +{ + Header header; + + SuccessOrExit(ReadHeader(header)); + header.SetMessageId(aMessageId); + WriteHeader(header); + +exit: + return; } uint8_t Message::WriteExtendedOptionField(uint16_t aValue, uint8_t *&aBuffer) @@ -186,18 +296,34 @@ uint8_t Message::WriteExtendedOptionField(uint16_t aValue, uint8_t *&aBuffer) Error Message::AppendOptionHeader(uint16_t aNumber, uint16_t aLength) { - /* - * Appends a CoAP Option header field (Option Delta/Length) per RFC 7252. - */ + // Appends a CoAP Option header field (Option Delta/Length) per RFC 7252. - Error error = kErrorNone; - uint16_t delta; - uint8_t header[kMaxOptionHeaderSize]; - uint16_t headerLength; - uint8_t *cur; + Error error; + Option::Iterator iterator; + uint16_t lastNumber; + uint16_t delta; + uint8_t header[kMaxOptionHeaderSize]; + uint16_t headerLength; + uint8_t *cur; - VerifyOrExit(aNumber >= GetHelpData().mOptionLast, error = kErrorInvalidArgs); - delta = aNumber - GetHelpData().mOptionLast; + // Parses the already appended options in the message + // to determine the last option number. Also ensures + // that "payload marker" is not appended. + + SuccessOrExit(error = iterator.Init(*this)); + + lastNumber = 0; + + while (!iterator.IsDone()) + { + lastNumber = iterator.GetOption()->GetNumber(); + SuccessOrExit(error = iterator.Advance()); + } + + VerifyOrExit(!iterator.HasPayloadMarker(), error = kErrorParse); + + VerifyOrExit(aNumber >= lastNumber, error = kErrorInvalidArgs); + delta = aNumber - lastNumber; cur = &header[1]; @@ -206,12 +332,8 @@ Error Message::AppendOptionHeader(uint16_t aNumber, uint16_t aLength) headerLength = static_cast(cur - header); - VerifyOrExit(static_cast(GetLength()) + headerLength + aLength < kMaxHeaderLength, error = kErrorNoBufs); - SuccessOrExit(error = AppendBytes(header, headerLength)); - GetHelpData().mOptionLast = aNumber; - exit: return error; } @@ -223,8 +345,6 @@ Error Message::AppendOption(uint16_t aNumber, uint16_t aLength, const void *aVal SuccessOrExit(error = AppendOptionHeader(aNumber, aLength)); SuccessOrExit(error = AppendBytes(aValue, aLength)); - GetHelpData().mHeaderLength = GetLength(); - exit: return error; } @@ -236,8 +356,6 @@ Error Message::AppendOptionFromMessage(uint16_t aNumber, uint16_t aLength, const SuccessOrExit(error = AppendOptionHeader(aNumber, aLength)); SuccessOrExit(error = AppendBytesFromMessage(aMessage, aOffset, aLength)); - GetHelpData().mHeaderLength = GetLength(); - exit: return error; } @@ -407,38 +525,12 @@ exit: #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -Error Message::SetPayloadMarker(void) +Error Message::AppendPayloadMarker(void) { - Error error = kErrorNone; - uint8_t marker = kPayloadMarker; - - VerifyOrExit(GetLength() < kMaxHeaderLength, error = kErrorNoBufs); - SuccessOrExit(error = Append(marker)); - GetHelpData().mPayloadMarkerSet = true; - GetHelpData().mHeaderLength = GetLength(); - - // Set offset to the start of payload. - SetOffset(GetHelpData().mHeaderLength); - -exit: - return error; -} - -Error Message::ParseHeader(void) -{ - Error error = kErrorNone; - uint16_t offset = GetOffset(); + Error error; + uint8_t marker = kPayloadMarker; Option::Iterator iterator; - OT_ASSERT(GetReserved() >= - sizeof(HelpData) + static_cast((reinterpret_cast(&GetHelpData()) - GetFirstData()))); - - GetHelpData().Clear(); - - GetHelpData().mHeaderOffset = offset; - - SuccessOrExit(error = GetHelpData().mHeader.ParseFrom(*this)); - SuccessOrExit(error = iterator.Init(*this)); while (!iterator.IsDone()) @@ -446,8 +538,51 @@ Error Message::ParseHeader(void) SuccessOrExit(error = iterator.Advance()); } - GetHelpData().mHeaderLength = iterator.GetPayloadMessageOffset() - GetHelpData().mHeaderOffset; - MoveOffset(GetHelpData().mHeaderLength); + VerifyOrExit(!iterator.HasPayloadMarker()); + + SuccessOrExit(error = Append(marker)); + + SetOffset(GetLength()); + +exit: + return error; +} + +uint16_t Message::DetermineTokenOffset(void) const +{ + uint16_t offset; + + if (CanAddSafely(GetHeaderOffset(), sizeof(Header))) + { + offset = GetHeaderOffset() + sizeof(Header); + } + else + { + SetToUintMax(offset); + } + + return offset; +} + +Error Message::DetermineOptionStartOffset(uint16_t &aOffset) const +{ + Error error; + uint8_t tokenLength; + uint16_t offset; + + SuccessOrExit(error = ReadTokenLength(tokenLength)); + offset = DetermineTokenOffset(); + + if (CanAddSafely(offset, tokenLength)) + { + offset += tokenLength; + } + else + { + SetToUintMax(offset); + } + + aOffset = offset; exit: return error; @@ -455,10 +590,11 @@ exit: Error Message::ReadTokenLength(uint8_t &aLength) const { - Error error = kErrorNone; + Error error = kErrorNone; + Header header; - VerifyOrExit(GetHelpData().mHeader.IsValid(), error = kErrorParse); - aLength = GetHelpData().mHeader.GetTokenLength(); + SuccessOrExit(error = ReadHeader(header)); + aLength = header.GetTokenLength(); exit: return error; @@ -466,16 +602,51 @@ exit: Error Message::ReadToken(Token &aToken) const { - return aToken.SetToken(GetHelpData().mHeader.GetToken(), GetHelpData().mHeader.GetTokenLength()); + Error error; + Header header; + + SuccessOrExit(error = ReadHeader(header)); + error = ReadToken(header, aToken); +exit: + return error; +} + +Error Message::ReadToken(const Header &aHeader, Token &aToken) const +{ + aToken.mLength = aHeader.GetTokenLength(); + + return Read(DetermineTokenOffset(), aToken.m8, aToken.mLength); } Error Message::WriteToken(const Token &aToken) { - Error error; + Error error; + Header header; + uint16_t tokenOffset = DetermineTokenOffset(); - SuccessOrExit(error = GetHelpData().mHeader.SetToken(aToken)); - GetHelpData().mHeaderLength += aToken.GetLength(); - error = SetLength(GetHelpData().mHeaderLength); + VerifyOrExit(aToken.IsValid(), error = kErrorInvalidArgs); + + SuccessOrExit(error = ReadHeader(header)); + + if (tokenOffset == GetLength()) + { + // A token has not been written yet, so grow the message to make + // space for it. + + SuccessOrExit(error = IncreaseLength(aToken.GetLength())); + + header.SetTokenLength(aToken.GetLength()); + WriteHeader(header); + } + else + { + // If a token was previously written, we only allow it to be + // overwritten by a new token of the same length. + + VerifyOrExit(header.GetTokenLength() == aToken.GetLength(), error = kErrorInvalidArgs); + } + + WriteBytes(tokenOffset, aToken.GetBytes(), aToken.GetLength()); exit: return error; @@ -519,22 +690,13 @@ exit: return hasSame; } -Error Message::SetDefaultResponseHeader(const Message &aRequest) -{ - Init(kTypeAck, kCodeChanged); - - SetMessageId(aRequest.GetMessageId()); - - return WriteTokenFromMessage(aRequest); -} - Message *Message::Clone(uint16_t aLength) const { Message *message = static_cast(ot::Message::Clone(aLength)); VerifyOrExit(message != nullptr); - message->GetHelpData() = GetHelpData(); + message->SetHeaderOffset(GetHeaderOffset()); exit: return message; @@ -576,45 +738,10 @@ const char *Message::CodeToString(void) const static_assert(Stringify::IsSorted(kCodeTable), "kCodeTable is not sorted"); - return Stringify::Lookup(GetCode(), kCodeTable, "Unknown"); + return Stringify::Lookup(ReadCode(), kCodeTable, "Unknown"); } #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE -//--------------------------------------------------------------------------------------------------------------------- -// `Message::Header` - -bool Message::Header::IsValid(void) const -{ - return (GetVersion() == kVersion1) && (GetTokenLength() <= Token::kMaxLength); -} - -Error Message::Header::ParseFrom(const Message &aMessage) -{ - Error error; - uint16_t offset = aMessage.GetOffset(); - - SuccessOrExit(error = aMessage.Read(offset, this, kMinSize)); - VerifyOrExit(IsValid(), error = kErrorParse); - - SuccessOrExit(error = aMessage.Read(offset + kMinSize, mToken, GetTokenLength())); - -exit: - return error; -} - -Error Message::Header::SetToken(const Token &aToken) -{ - Error error = kErrorNone; - - VerifyOrExit(aToken.IsValid(), error = kErrorInvalidArgs); - - SetTokenLength(aToken.mLength); - memcpy(mToken, aToken.GetBytes(), aToken.GetLength()); - -exit: - return error; -} - //--------------------------------------------------------------------------------------------------------------------- // `Message::Iterator` @@ -627,23 +754,30 @@ Message::ConstIterator MessageQueue::begin(void) const { return Message::ConstIt Error Option::Iterator::Init(const Message &aMessage) { - Error error = kErrorParse; - uint32_t offset = static_cast(aMessage.GetHelpData().mHeaderOffset) + aMessage.GetOptionStart(); + Error error; + uint16_t offset; + + SuccessOrExit(error = aMessage.DetermineOptionStartOffset(offset)); // Note that the case where `offset == aMessage.GetLength())` is // valid and indicates an empty payload (no CoAP Option and no // Payload Marker). - VerifyOrExit(offset <= aMessage.GetLength(), MarkAsParseErrored()); + VerifyOrExit(offset <= aMessage.GetLength(), error = kErrorParse); mOption.mNumber = 0; mOption.mLength = 0; mMessage = &aMessage; - mNextOptionOffset = static_cast(offset); + mNextOptionOffset = offset; error = Advance(); exit: + if (error != kErrorNone) + { + MarkAsDone(); + } + return error; } @@ -658,22 +792,23 @@ Error Option::Iterator::Advance(void) error = Read(sizeof(uint8_t), &headerByte); - if ((error != kErrorNone) || (headerByte == Message::kPayloadMarker)) + if (error != kErrorNone) + { + // Reached the end without seeing the payload marker. + + MarkAsDone(); + SetHasPayloadMarker(false); + ExitNow(error = kErrorNone); + } + + if (headerByte == Message::kPayloadMarker) { // Payload Marker indicates end of options and start of payload. // Absence of a Payload Marker indicates a zero-length payload. MarkAsDone(); - - if (error == kErrorNone) - { - // The presence of a marker followed by a zero-length payload - // MUST be processed as a message format error. - - VerifyOrExit(mNextOptionOffset < GetMessage().GetLength(), error = kErrorParse); - } - - ExitNow(error = kErrorNone); + SetHasPayloadMarker(true); + ExitNow(); } optionDelta = (headerByte & Message::kOptionDeltaMask) >> Message::kOptionDeltaOffset; @@ -691,7 +826,7 @@ Error Option::Iterator::Advance(void) exit: if (error != kErrorNone) { - MarkAsParseErrored(); + MarkAsDone(); } return error; diff --git a/src/core/coap/coap_message.hpp b/src/core/coap/coap_message.hpp index 305766b45..d2bfd5557 100644 --- a/src/core/coap/coap_message.hpp +++ b/src/core/coap/coap_message.hpp @@ -69,8 +69,10 @@ namespace Coap { * @{ */ +class Msg; class Message; class Option; +class CoapBase; /** * CoAP Type values. @@ -263,18 +265,177 @@ private: Error GenerateRandom(uint8_t aLength); }; +/** + * Represents information from a parsed CoAP Header in a CoAP message. + */ +class HeaderInfo : public Clearable +{ + friend class Message; + friend class Msg; + +public: + /** + * Returns the Type value. + * + * @returns The Type value. + */ + uint8_t GetType(void) const { return mType; } + + /** + * Returns the Code value. + * + * @returns The Code value. + */ + uint8_t GetCode(void) const { return mCode; } + + /** + * Returns the Message ID value. + * + * @returns The Message ID value. + */ + uint16_t GetMessageId(void) const { return mMessageId; } + + /** + * Returns the Token. + * + * @returns The Token. + */ + const Token &GetToken(void) const { return mToken; } + + /** + * Checks if a header is an empty message header (`kCodeEmpty`). + * + * @retval TRUE Message is an empty message header. + * @retval FALSE Message is not an empty message header. + */ + bool IsEmpty(void) const { return (mCode == kCodeEmpty); } + + /** + * Checks if a header is a request header. + * + * @retval TRUE Message is a request header. + * @retval FALSE Message is not a request header. + */ + bool IsRequest(void) const; + + /** + * Indicates whether or not the CoAP code in header is "Get" request. + * + * @retval TRUE Message is a Get request. + * @retval FALSE Message is not a Get request. + */ + bool IsGetRequest(void) const { return (mCode == kCodeGet); } + + /** + * Indicates whether or not the CoAP code in header is "Post" request. + * + * @retval TRUE Message is a Post request. + * @retval FALSE Message is not a Post request. + */ + bool IsPostRequest(void) const { return (mCode == kCodePost); } + + /** + * Indicates whether or not the CoAP code in header is "Put" request. + * + * @retval TRUE Message is a Put request. + * @retval FALSE Message is not a Put request. + */ + bool IsPutRequest(void) const { return (mCode == kCodePut); } + + /** + * Indicates whether or not the CoAP code in header is "Delete" request. + * + * @retval TRUE Message is a Delete request. + * @retval FALSE Message is not a Delete request. + */ + bool IsDeleteRequest(void) const { return (mCode == kCodeDelete); } + + /** + * Checks if a header is a response header. + * + * @retval TRUE Message is a response header. + * @retval FALSE Message is not a response header. + */ + bool IsResponse(void) const { return mCode >= kCodeResponseMin; } + + /** + * Checks if a header is a CON message header. + * + * @retval TRUE Message is a CON message header. + * @retval FALSE Message is not is a CON message header. + */ + bool IsConfirmable(void) const { return (GetType() == kTypeConfirmable); } + + /** + * Checks if a header is a NON message header. + * + * @retval TRUE Message is a NON message header. + * @retval FALSE Message is not is a NON message header. + */ + bool IsNonConfirmable(void) const { return (mType == kTypeNonConfirmable); } + + /** + * Checks if a header is a ACK message header. + * + * @retval TRUE Message is a ACK message header. + * @retval FALSE Message is not is a ACK message header. + */ + bool IsAck(void) const { return (mType == kTypeAck); } + + /** + * Checks if a header is a RST message header. + * + * @retval TRUE Message is a RST message header. + * @retval FALSE Message is not is a RST message header. + */ + bool IsReset(void) const { return (mType == kTypeReset); } + + /** + * Indicates whether or not the header is a confirmable Post request (`kTypeConfirmable` with`kCodePost`). + * + * @retval TRUE Message is a confirmable Post request. + * @retval FALSE Message is not a confirmable Post request. + */ + bool IsConfirmablePostRequest(void) const; + + /** + * Indicates whether the message is a non-confirmable Post request (`kTypeNonConfirmable` with `kCodePost`). + * + * @retval TRUE Message is a non-confirmable Post request. + * @retval FALSE Message is not a non-confirmable Post request. + */ + bool IsNonConfirmablePostRequest(void) const; + + /** + * Checks if the message requires a reset response if an error during low level CoAP processing occurred. + * + * A reset message is expected to be sent for NON and CON messages if the message can not be processed or a + * duplicated message has been received. + * + * @retval TRUE Expect to respond with CoAP reset message on error. + * @retval FALSE No CoAP reset message should be sent on error. + */ + bool RequireResetOnError(void) { return IsConfirmable() || IsNonConfirmable(); } + +private: + uint8_t mType; + uint8_t mCode; + uint16_t mMessageId; + Token mToken; +}; + /** * Implements CoAP message generation and parsing. */ class Message : public ot::Message { + friend class Msg; friend class Option; friend class MessageQueue; + friend class CoapBase; public: - static constexpr uint8_t kDefaultTokenLength = OT_COAP_DEFAULT_TOKEN_LENGTH; ///< Default token length. - static constexpr uint8_t kMaxReceivedUriPath = 32; ///< Max URI path length on rx msgs. - static constexpr uint8_t kMaxTokenLength = OT_COAP_MAX_TOKEN_LENGTH; ///< Maximum token length. + static constexpr uint8_t kMaxReceivedUriPath = 32; ///< Max URI path length on rx msgs. typedef ot::Coap::Type Type; ///< CoAP Type. typedef ot::Coap::Code Code; ///< CoAP Code. @@ -282,91 +443,121 @@ public: typedef char UriPathStringBuffer[kMaxReceivedUriPath + 1]; ///< Buffer to store a received URI Path string. /** - * Initializes the CoAP header. + * Initializes the CoAP message with a given Type and Code. + * + * This method erases any previously written content in the message. The Message ID is set to zero, and the token + * is empty (zero-length). + * + * @param[in] aType The CoAP Type value. + * @param[in] aCode The CoAP Code value. + * + * @retval kErrorNone Successfully initialized the message. + * @retval kErrorNoBufs Could not grow the message to write the CoAP header. */ - void Init(void); + Error Init(Type aType, Code aCode); /** - * Initializes the CoAP header with specific Type and Code. + * Initializes the CoAP message with a given Type, Code, and Message ID. * - * @param[in] aType The Type value. - * @param[in] aCode The Code value. + * This method erases any previously written content in the message. The token is empty (zero-length). + * + * @param[in] aType The CoAP Type value. + * @param[in] aCode The CoAP Code value. + * @param[in] aMessageId The CoAP Message ID value. + * + * @retval kErrorNone Successfully initialized the message. + * @retval kErrorNoBufs Could not grow the message to write the CoAP header. */ - void Init(Type aType, Code aCode); + Error Init(Type aType, Code aCode, uint16_t aMessageId); /** - * Initializes the CoAP header with specific Type and Code. + * Initializes the message with a Type, Code, adds a random token, and appends a URI-path option. * - * @param[in] aType The Type value. - * @param[in] aCode The Code value. - * @param[in] aUri The URI. + * This method erases any previously written content in the message. The Message ID is set to zero. A random token + * of default length (`Token::kDefaultLength`) is generated and added to the message. * - * @retval kErrorNone Successfully appended the option. - * @retval kErrorNoBufs The option length exceeds the buffer size. + * @param[in] aType The CoAP Type value. + * @param[in] aCode The CoAP Code value. + * @param[in] aUri The URI string. + * + * @retval kErrorNone Successfully initialized the message and appended the URI-path option. + * @retval kErrorNoBufs Could not grow the message to append the option. */ Error Init(Type aType, Code aCode, Uri aUri); /** - * Initializes the CoAP header as `kCodePost` with a given URI Path with its type determined from a - * given destination IPv6 address. + * Initializes a CoAP POST message, appends a URI Path, and adds a random token. * - * @param[in] aDestination The message destination IPv6 address used to determine the CoAP type, - * `kTypeNonConfirmable` if multicast address, `kTypeConfirmable` otherwise. - * @param[in] aUri The URI. + * This method erases any previously written content in the message. * - * @retval kErrorNone Successfully appended the option. - * @retval kErrorNoBufs The option length exceeds the buffer size. + * The CoAP Type is determined from the destination IPv6 address: `kTypeNonConfirmable` for multicast and + * `kTypeConfirmable` otherwise. The Message ID is set to zero. A random token of default length + * (`Token::kDefaultLength`) is generated and added. + * + * @param[in] aDestination The message destination IPv6 address, used to determine the CoAP Type. + * @param[in] aUri The URI string. + * + * @retval kErrorNone Successfully initialized the message and appended the URI-path option. + * @retval kErrorNoBufs Could not grow the message to append the option. */ Error InitAsPost(const Ip6::Address &aDestination, Uri aUri); /** - * Writes header to the message. This must be called before sending the message. + * Initializes a CoAP message as a response to a request message. * - * Also checks whether the payload marker is set (`SetPayloadMarker()`) but the message contains no - * payload, and if so it removes the payload marker from the message. + * This method erases any previously written content in the message. The Message ID and Token are copied from the + * request message. + * + * @param[in] aType The CoAP Type value. + * @param[in] aCode The CoAP Code value. + * @param[in] aRequest The request message to respond to. + * + * @retval kErrorNone Successfully initialized the message. + * @retval kErrorNoBufs Could not grow the message to write the CoAP header. */ - void Finish(void); + Error InitAsResponse(Type aType, Code aCode, const Message &aRequest); /** - * Returns the Version value. + * Parses the CoAP header and token. * - * @returns The Version value. + * @param[out] aInfo A reference to `HeaderInfo` to populate. + * + * @retval kErrorNone Successfully parsed the CoAP header. @p aInfo is updated. + * @retval kErrorParse Failed to parse. */ - uint8_t GetVersion(void) const { return GetHelpData().mHeader.GetVersion(); } + Error ParseHeaderInfo(HeaderInfo &aInfo) const; /** - * Sets the Version value. + * Reads the Type value. * - * @param[in] aVersion The Version value. + * @returns The Type value, or zero if the CoAP header is invalid or cannot be parsed. */ - void SetVersion(uint8_t aVersion) { GetHelpData().mHeader.SetVersion(aVersion); } + uint8_t ReadType(void) const; /** - * Returns the Type value. + * Writes the Type value in CoAP header. * - * @returns The Type value. - */ - uint8_t GetType(void) const { return GetHelpData().mHeader.GetType(); } - /** - * Sets the Type value. + * This method requires that the message contains a valid CoAP header. Otherwise, no change is made. * * @param[in] aType The Type value. */ - void SetType(Type aType) { GetHelpData().mHeader.SetType(aType); } + void WriteType(Type aType); /** - * Returns the Code value. + * Reads the Code value. * - * @returns The Code value. + * @returns The Code value, or zero if the CoAP header is invalid or cannot be parsed. */ - uint8_t GetCode(void) const { return GetHelpData().mHeader.GetCode(); } + uint8_t ReadCode(void) const; /** - * Sets the Code value. + * Writes the Code value. + * + * This method requires that the message contains a valid CoAP header. Otherwise, no change is made. * * @param[in] aCode The Code value. */ - void SetCode(Code aCode) { GetHelpData().mHeader.SetCode(aCode); } + void WriteCode(Code aCode); #if OPENTHREAD_CONFIG_COAP_API_ENABLE /** @@ -378,18 +569,20 @@ public: #endif /** - * Returns the Message ID value. + * Reads the Message ID value. * - * @returns The Message ID value. + * @returns The Message ID value, or zero if the CoAP header is invalid or cannot be parsed. */ - uint16_t GetMessageId(void) const { return GetHelpData().mHeader.GetMessageId(); } + uint16_t ReadMessageId(void) const; /** - * Sets the Message ID value. + * Writes the Message ID value in CoAP header. + * + * This method requires that the message contains a valid CoAP header. Otherwise, no change is made. * * @param[in] aMessageId The Message ID value. */ - void SetMessageId(uint16_t aMessageId) { GetHelpData().mHeader.SetMessageId(aMessageId); } + void WriteMessageId(uint16_t aMessageId); /** * Reads the Token length from the message @@ -623,164 +816,14 @@ public: Error AppendUriQueryOption(const char *aUriQuery) { return AppendStringOption(kOptionUriQuery, aUriQuery); } /** - * Reads and reassembles the URI path string and fills it into @p aUriPath. + * Appends a Payload Marker indicating the beginning of the payload. * - * @retval kErrorNone URI path string has been reassembled. - * @retval kErrorNoBufs URI path string is too long. + * It also sets the offset to the start of the payload. + * + * @retval kErrorNone Payload Marker was successfully added. + * @retval kErrorNoBufs Could not grow the message to append the payload marker. */ - Error GetUriPath(char *aUriPath) const; - - /** - * Adds Payload Marker indicating beginning of the payload to the CoAP header. - * - * It also set offset to the start of payload. - * - * @retval kErrorNone Payload Marker successfully added. - * @retval kErrorNoBufs Message Payload Marker exceeds the buffer size. - */ - Error SetPayloadMarker(void); - - /** - * Returns the offset of the first CoAP option. - * - * @returns The offset of the first CoAP option. - */ - uint16_t GetOptionStart(void) const { return kMinHeaderLength + GetHelpData().mHeader.GetTokenLength(); } - - /** - * Parses CoAP header and moves offset end of CoAP header. - * - * @retval kErrorNone Successfully parsed CoAP header from the message. - * @retval kErrorParse Failed to parse the CoAP header. - */ - Error ParseHeader(void); - - /** - * Sets a default response header based on request header. - * - * @param[in] aRequest The request message. - * - * @retval kErrorNone Successfully set the default response header. - * @retval kErrorNoBufs Insufficient message buffers available to set the default response header. - */ - Error SetDefaultResponseHeader(const Message &aRequest); - - /** - * Checks if a header is an empty message header. - * - * @retval TRUE Message is an empty message header. - * @retval FALSE Message is not an empty message header. - */ - bool IsEmpty(void) const { return (GetCode() == kCodeEmpty); } - - /** - * Checks if a header is a request header. - * - * @retval TRUE Message is a request header. - * @retval FALSE Message is not a request header. - */ - bool IsRequest(void) const { return (GetCode() >= kCodeGet) && (GetCode() <= kCodeDelete); } - - /** - * Indicates whether or not the CoAP code in header is "Get" request. - * - * @retval TRUE Message is a Get request. - * @retval FALSE Message is not a Get request. - */ - bool IsGetRequest(void) const { return GetCode() == kCodeGet; } - - /** - * Indicates whether or not the CoAP code in header is "Post" request. - * - * @retval TRUE Message is a Post request. - * @retval FALSE Message is not a Post request. - */ - bool IsPostRequest(void) const { return GetCode() == kCodePost; } - - /** - * Indicates whether or not the CoAP code in header is "Put" request. - * - * @retval TRUE Message is a Put request. - * @retval FALSE Message is not a Put request. - */ - bool IsPutRequest(void) const { return GetCode() == kCodePut; } - - /** - * Indicates whether or not the CoAP code in header is "Delete" request. - * - * @retval TRUE Message is a Delete request. - * @retval FALSE Message is not a Delete request. - */ - bool IsDeleteRequest(void) const { return GetCode() == kCodeDelete; } - - /** - * Checks if a header is a response header. - * - * @retval TRUE Message is a response header. - * @retval FALSE Message is not a response header. - */ - bool IsResponse(void) const { return GetCode() >= OT_COAP_CODE_RESPONSE_MIN; } - - /** - * Checks if a header is a CON message header. - * - * @retval TRUE Message is a CON message header. - * @retval FALSE Message is not is a CON message header. - */ - bool IsConfirmable(void) const { return (GetType() == kTypeConfirmable); } - - /** - * Checks if a header is a NON message header. - * - * @retval TRUE Message is a NON message header. - * @retval FALSE Message is not is a NON message header. - */ - bool IsNonConfirmable(void) const { return (GetType() == kTypeNonConfirmable); } - - /** - * Checks if a header is a ACK message header. - * - * @retval TRUE Message is a ACK message header. - * @retval FALSE Message is not is a ACK message header. - */ - bool IsAck(void) const { return (GetType() == kTypeAck); } - - /** - * Checks if a header is a RST message header. - * - * @retval TRUE Message is a RST message header. - * @retval FALSE Message is not is a RST message header. - */ - bool IsReset(void) const { return (GetType() == kTypeReset); } - - /** - * Indicates whether or not the header is a confirmable Post request (i.e, `kTypeConfirmable` with - * `kCodePost`). - * - * @retval TRUE Message is a confirmable Post request. - * @retval FALSE Message is not a confirmable Post request. - */ - bool IsConfirmablePostRequest(void) const; - - /** - * Indicates whether or not the header is a non-confirmable Post request (i.e, `kTypeNonConfirmable` with - * `kCodePost`). - * - * @retval TRUE Message is a non-confirmable Post request. - * @retval FALSE Message is not a non-confirmable Post request. - */ - bool IsNonConfirmablePostRequest(void) const; - - /** - * Checks if the message requires an reset response if an error during low level CoAP processing occurred. - * - * A reset message is expected to be sent for NON and CON messages if the message can not be processed or a - * duplicated message has been received. - * - * @retval TRUE Expect to respond with CoAP reset message on error. - * @retval FALSE No CoAP reset message should be sent on error. - */ - bool RequireResetOnError(void) { return IsConfirmable() || IsNonConfirmable(); } + Error AppendPayloadMarker(void); /** * Creates a copy of this CoAP message. @@ -806,11 +849,6 @@ public: */ Message *Clone(void) const { return Clone(GetLength()); } - /** - * Returns the minimal reserved bytes required for CoAP message. - */ - static uint16_t GetHelpDataReserved(void) { return sizeof(HelpData) + kHelpDataAlignment; } - /** * Returns a pointer to the next message after this as a `Coap::Message`. * @@ -862,11 +900,6 @@ private: static constexpr uint8_t kPayloadMarker = 0xff; - static constexpr uint8_t kHelpDataAlignment = sizeof(uint16_t); // Alignment of help data. - - static constexpr uint16_t kMinHeaderLength = 4; - static constexpr uint16_t kMaxHeaderLength = 512; - static constexpr uint16_t kOption1ByteExtensionOffset = 13; // Delta/Length offset as specified (RFC 7252). static constexpr uint16_t kOption2ByteExtensionOffset = 269; // Delta/Length offset as specified (RFC 7252). @@ -878,25 +911,21 @@ private: static constexpr uint32_t kBlockNumMax = 0xffff; OT_TOOL_PACKED_BEGIN - class Header + class Header : public Clearable
{ public: static constexpr uint8_t kVersion1 = 1; - uint8_t GetSize(void) const { return kMinSize + GetTokenLength(); } - bool IsValid(void) const; - Error ParseFrom(const Message &aMessage); - uint8_t GetVersion(void) const { return ReadBits(mVersionTypeToken); } - void SetVersion(uint8_t aVersion) { WriteBits(mVersionTypeToken, aVersion); } - uint8_t GetType(void) const { return ReadBits(mVersionTypeToken); } - void SetType(Type aType) { WriteBits(mVersionTypeToken, aType); } - uint8_t GetCode(void) const { return mCode; } - void SetCode(Code aCode) { mCode = aCode; } - uint16_t GetMessageId(void) const { return BigEndian::HostSwap16(mMessageId); } - void SetMessageId(uint16_t aMessageId) { mMessageId = BigEndian::HostSwap16(aMessageId); } - const uint8_t *GetToken(void) const { return mToken; } - uint8_t GetTokenLength(void) const { return ReadBits(mVersionTypeToken); } - Error SetToken(const Token &aToken); + uint8_t GetVersion(void) const { return ReadBits(mVersionTypeToken); } + void SetVersion(uint8_t aVersion) { WriteBits(mVersionTypeToken, aVersion); } + uint8_t GetType(void) const { return ReadBits(mVersionTypeToken); } + void SetType(Type aType) { WriteBits(mVersionTypeToken, aType); } + uint8_t GetCode(void) const { return mCode; } + void SetCode(Code aCode) { mCode = aCode; } + uint16_t GetMessageId(void) const { return BigEndian::HostSwap16(mMessageId); } + void SetMessageId(uint16_t aMessageId) { mMessageId = BigEndian::HostSwap16(aMessageId); } + uint8_t GetTokenLength(void) const { return ReadBits(mVersionTypeToken); } + void SetTokenLength(uint8_t aLength) { WriteBits(mVersionTypeToken, aLength); } private: /* @@ -907,31 +936,15 @@ private: * |Ver| T | TKL | (Version, Type and Token Length). * +-+-+-+-+-+-+-+-+ */ - static constexpr uint8_t kVersionMask = 0x3 << 6; - static constexpr uint8_t kTypeMask = 0x3 << 4; - static constexpr uint8_t kTokenLengthMask = 0xf << 0; - static constexpr uint16_t kMinSize = 4; - - void SetTokenLength(uint8_t aLength) { WriteBits(mVersionTypeToken, aLength); } + static constexpr uint8_t kVersionMask = 0x3 << 6; + static constexpr uint8_t kTypeMask = 0x3 << 4; + static constexpr uint8_t kTokenLengthMask = 0xf << 0; uint8_t mVersionTypeToken; // The CoAP Version, Type, and Token Length uint8_t mCode; uint16_t mMessageId; - uint8_t mToken[kMaxTokenLength]; } OT_TOOL_PACKED_END; - /** - * Represents a HelpData used by this CoAP message. - */ - struct HelpData : public Clearable - { - Header mHeader; - uint16_t mOptionLast; - uint16_t mHeaderOffset; ///< The byte offset for the CoAP Header - uint16_t mHeaderLength; - bool mPayloadMarkerSet; - }; - class ConstIterator : public ot::Message::ConstIterator { public: @@ -953,23 +966,15 @@ private: Message *operator->(void) { return static_cast(ot::Message::Iterator::operator->()); } }; - static_assert(sizeof(HelpData) <= sizeof(Ip6::Header) + sizeof(Ip6::HopByHopHeader) + sizeof(Ip6::MplOption) + - sizeof(Ip6::Udp::Header), - "HelpData size exceeds the size of the reserved region in the message"); - - const HelpData &GetHelpData(void) const - { - static_assert(sizeof(HelpData) + kHelpDataAlignment <= kHeadBufferDataSize, - "Insufficient buffer size for CoAP processing! Increase OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE."); - - return *static_cast(OT_ALIGN(GetFirstData(), kHelpDataAlignment)); - } - - HelpData &GetHelpData(void) { return AsNonConst(AsConst(this)->GetHelpData()); } - - uint8_t WriteExtendedOptionField(uint16_t aValue, uint8_t *&aBuffer); - - Error AppendOptionHeader(uint16_t aNumber, uint16_t aLength); + uint16_t GetHeaderOffset(void) const { return GetMeshDest(); } + void SetHeaderOffset(uint16_t aOffset) { SetMeshDest(aOffset); } + uint16_t DetermineTokenOffset(void) const; + Error DetermineOptionStartOffset(uint16_t &aOffset) const; + Error ReadHeader(Header &aHeader) const; + void WriteHeader(const Header &aHeader); + Error ReadToken(const Header &aHeader, Token &aToken) const; + uint8_t WriteExtendedOptionField(uint16_t aValue, uint8_t *&aBuffer); + Error AppendOptionHeader(uint16_t aNumber, uint16_t aLength); }; /** @@ -1091,16 +1096,6 @@ public: */ bool IsDone(void) const { return mOption.mLength == kIteratorDoneLength; } - /** - * Indicates whether or not there was a earlier parse error (i.e., whether the iterator is valid). - * - * After a parse errors, iterator would also be marked as done. - * - * @retval TRUE There was an earlier parse error and the iterator is not valid. - * @retval FALSE There was no earlier parse error and the iterator is valid. - */ - bool HasParseErrored(void) const { return mNextOptionOffset == kNextOptionOffsetParseError; } - /** * Advances the iterator to the next CoAP Option in the header. * @@ -1164,12 +1159,22 @@ public: /** * Gets the offset of beginning of the CoAP message payload (after the CoAP header). * - * MUST be used after the iterator is done (i.e. iterated through all options). + * MUST be used after the iterator is done (i.e. successfully iterated through all options). * * @returns The offset of beginning of the CoAP message payload */ uint16_t GetPayloadMessageOffset(void) const { return mNextOptionOffset; } + /** + * Inidcated whether or not the option ended with a payload marker. + * + * MUST be used after the iterator is done (i.e. successfully iterated through all options). + * + * @retval TRUE The message contains a payload marker. + * @retval FALAE The message does not contain a payload marker. + */ + bool HasPayloadMarker(void) const { return IsDone() && (mOption.mNumber != 0); } + /** * Gets the offset of beginning of the CoAP Option Value. * @@ -1183,12 +1188,8 @@ public: // `mOption.mLength` value to indicate iterator is done. static constexpr uint16_t kIteratorDoneLength = 0xffff; - // Special `mNextOptionOffset` value to indicate a parse error. - static constexpr uint16_t kNextOptionOffsetParseError = 0; - - void MarkAsDone(void) { mOption.mLength = kIteratorDoneLength; } - void MarkAsParseErrored(void) { MarkAsDone(), mNextOptionOffset = kNextOptionOffsetParseError; } - + void MarkAsDone(void) { mOption.mLength = kIteratorDoneLength; } + void SetHasPayloadMarker(bool aHasPayloadMarker) { mOption.mNumber = aHasPayloadMarker; } Error Read(uint16_t aLength, void *aBuffer); Error ReadExtendedOptionField(uint16_t &aValue); Error InitOrAdvance(const Message *aMessage, uint16_t aNumber); diff --git a/src/core/coap/coap_secure.cpp b/src/core/coap/coap_secure.cpp index 55bb6ba12..516a204d1 100644 --- a/src/core/coap/coap_secure.cpp +++ b/src/core/coap/coap_secure.cpp @@ -125,7 +125,7 @@ void SecureSession::HandleDtlsReceive(uint8_t *aBuf, uint16_t aLength) { ot::Message *message = nullptr; - VerifyOrExit((message = Get().Allocate(Message::kTypeIp6, Message::GetHelpDataReserved())) != nullptr); + VerifyOrExit((message = Get().Allocate(Message::kTypeIp6)) != nullptr); SuccessOrExit(message->AppendBytes(aBuf, aLength)); CoapBase::Receive(*message, GetMessageInfo()); diff --git a/src/core/meshcop/announce_begin_client.cpp b/src/core/meshcop/announce_begin_client.cpp index e6626f042..077f60fa4 100644 --- a/src/core/meshcop/announce_begin_client.cpp +++ b/src/core/meshcop/announce_begin_client.cpp @@ -59,7 +59,7 @@ Error AnnounceBeginClient::SendRequest(uint32_t aChannelMask, VerifyOrExit((message = Get().NewPriorityMessage()) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->InitAsPost(aAddress, kUriAnnounceBegin)); - SuccessOrExit(error = message->SetPayloadMarker()); + SuccessOrExit(error = message->AppendPayloadMarker()); SuccessOrExit( error = Tlv::Append(*message, Get().GetSessionId())); diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index e0b498a6a..181c6c869 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -360,7 +360,7 @@ template <> void Manager::HandleTmf(Coap::Msg &aMsg) VerifyOrExit(mIsRunning); - VerifyOrExit(aMsg.mMessage.IsNonConfirmablePostRequest()); + VerifyOrExit(aMsg.IsNonConfirmablePostRequest()); LogInfo("Received %s from %s", UriToString(), aMsg.mMessageInfo.GetPeerAddr().ToString().AsCString()); @@ -597,7 +597,7 @@ bool Manager::CoapDtlsSession::HandleResource(const char *aUriPath, Coap::Msg &a HandleTmfCommissionerKeepAlive(aMsg); break; case kUriRelayTx: - HandleTmfRelayTx(aMsg.mMessage); + HandleTmfRelayTx(aMsg); break; case kUriCommissionerGet: case kUriActiveGet: @@ -605,7 +605,7 @@ bool Manager::CoapDtlsSession::HandleResource(const char *aUriPath, Coap::Msg &a HandleTmfDatasetGet(aMsg.mMessage, uri); break; case kUriProxyTx: - HandleTmfProxyTx(aMsg.mMessage); + HandleTmfProxyTx(aMsg); break; default: didHandle = false; @@ -756,7 +756,7 @@ void Manager::CoapDtlsSession::HandleLeaderResponseToFwdTmf(const ForwardContext forwardMessage.Reset(NewPriorityMessage()); VerifyOrExit(forwardMessage != nullptr, error = kErrorNoBufs); - if (aResponse->GetCode() == Coap::kCodeChanged) + if (aResponse->ReadCode() == Coap::kCodeChanged) { uint8_t state; @@ -784,13 +784,13 @@ void Manager::CoapDtlsSession::HandleLeaderResponseToFwdTmf(const ForwardContext } } - forwardMessage->Init(Coap::kTypeNonConfirmable, static_cast(aResponse->GetCode())); - + SuccessOrExit(error = + forwardMessage->Init(Coap::kTypeNonConfirmable, static_cast(aResponse->ReadCode()))); SuccessOrExit(error = forwardMessage->WriteToken(aForwardContext.mToken)); if (aResponse->GetLength() > aResponse->GetOffset()) { - SuccessOrExit(error = forwardMessage->SetPayloadMarker()); + SuccessOrExit(error = forwardMessage->AppendPayloadMarker()); } SuccessOrExit(error = ForwardToCommissioner(forwardMessage.PassOwnership(), *aResponse)); @@ -897,7 +897,7 @@ void Manager::CoapDtlsSession::SendErrorMessage(Error aError, const Coap::Token code = (aError == kErrorParse) ? Coap::kCodeBadRequest : Coap::kCodeInternalError; - message->Init(Coap::kTypeNonConfirmable, code); + SuccessOrExit(error = message->Init(Coap::kTypeNonConfirmable, code)); SuccessOrExit(error = message->WriteToken(aToken)); SuccessOrExit(error = SendMessage(message.PassOwnership())); @@ -906,7 +906,7 @@ exit: LogWarnOnError(error, "send error CoAP message"); } -void Manager::CoapDtlsSession::HandleTmfProxyTx(Coap::Message &aMessage) +void Manager::CoapDtlsSession::HandleTmfProxyTx(Coap::Msg &aMsg) { Error error = kErrorNone; OwnedPtr message; @@ -918,9 +918,9 @@ void Manager::CoapDtlsSession::HandleTmfProxyTx(Coap::Message &aMessage) VerifyOrExit(IsActiveCommissioner(), error = kErrorInvalidState); - SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Tlv::kUdpEncapsulation, offsetRange)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMsg.mMessage, Tlv::kUdpEncapsulation, offsetRange)); - SuccessOrExit(error = aMessage.Read(offsetRange, udpEncapHeader)); + SuccessOrExit(error = aMsg.mMessage.Read(offsetRange, udpEncapHeader)); offsetRange.AdvanceOffset(sizeof(UdpEncapsulationTlvHeader)); VerifyOrExit(udpEncapHeader.GetSourcePort() > 0 && udpEncapHeader.GetDestinationPort() > 0, error = kErrorDrop); @@ -928,13 +928,13 @@ void Manager::CoapDtlsSession::HandleTmfProxyTx(Coap::Message &aMessage) message.Reset(Get().NewMessage()); VerifyOrExit(message != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange)); + SuccessOrExit(error = message->AppendBytesFromMessage(aMsg.mMessage, offsetRange)); messageInfo.SetSockPort(udpEncapHeader.GetSourcePort()); messageInfo.SetSockAddr(Get().GetCommissionerAloc()); messageInfo.SetPeerPort(udpEncapHeader.GetDestinationPort()); - SuccessOrExit(error = Tlv::Find(aMessage, messageInfo.GetPeerAddr())); + SuccessOrExit(error = Tlv::Find(aMsg.mMessage, messageInfo.GetPeerAddr())); // On success the message ownership is transferred. SuccessOrExit(error = Get().SendDatagram(*message, messageInfo)); @@ -946,7 +946,7 @@ exit: LogWarnOnError(error, "send proxy stream"); } -void Manager::CoapDtlsSession::HandleTmfRelayTx(Coap::Message &aMessage) +void Manager::CoapDtlsSession::HandleTmfRelayTx(Coap::Msg &aMsg) { Error error = kErrorNone; uint16_t joinerRouterRloc; @@ -954,19 +954,19 @@ void Manager::CoapDtlsSession::HandleTmfRelayTx(Coap::Message &aMessage) Tmf::MessageInfo messageInfo(GetInstance()); OffsetRange offsetRange; - VerifyOrExit(aMessage.IsNonConfirmablePostRequest()); + VerifyOrExit(aMsg.IsNonConfirmablePostRequest()); VerifyOrExit(IsActiveCommissioner(), error = kErrorInvalidState); Log(kReceive); - SuccessOrExit(error = Tlv::Find(aMessage, joinerRouterRloc)); + SuccessOrExit(error = Tlv::Find(aMsg.mMessage, joinerRouterRloc)); message.Reset(Get().NewPriorityNonConfirmablePostMessage(kUriRelayTx)); VerifyOrExit(message != nullptr, error = kErrorNoBufs); - offsetRange.InitFromMessageOffsetToEnd(aMessage); - SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange)); + offsetRange.InitFromMessageOffsetToEnd(aMsg.mMessage); + SuccessOrExit(error = message->AppendBytesFromMessage(aMsg.mMessage, offsetRange)); messageInfo.SetSockAddrToRlocPeerAddrTo(joinerRouterRloc); messageInfo.SetSockPortToTmf(); diff --git a/src/core/meshcop/border_agent.hpp b/src/core/meshcop/border_agent.hpp index d2d930c11..bfd8b5456 100644 --- a/src/core/meshcop/border_agent.hpp +++ b/src/core/meshcop/border_agent.hpp @@ -304,8 +304,8 @@ private: Error ForwardToCommissioner(OwnedPtr aForwardMessage, const Message &aMessage); void HandleTmfCommissionerKeepAlive(Coap::Msg &aMsg); - void HandleTmfRelayTx(Coap::Message &aMessage); - void HandleTmfProxyTx(Coap::Message &aMessage); + void HandleTmfRelayTx(Coap::Msg &aMsg); + void HandleTmfProxyTx(Coap::Msg &aMsg); void HandleTmfDatasetGet(Coap::Message &aMessage, Uri aUri); Error ForwardToLeader(const Coap::Msg &aMsg, Uri aUri); void SendErrorMessage(Error aError, const Coap::Token &aToken); diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp index 73b62e749..ea4ea68c6 100644 --- a/src/core/meshcop/commissioner.cpp +++ b/src/core/meshcop/commissioner.cpp @@ -627,7 +627,7 @@ exit: void Commissioner::HandleMgmtCommissionerGetResponse(Coap::Message *aMessage, Error aResult) { - VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged); + VerifyOrExit(aResult == kErrorNone && aMessage->ReadCode() == Coap::kCodeChanged); LogInfo("Received %s response", UriToString()); exit: @@ -688,7 +688,7 @@ void Commissioner::HandleMgmtCommissionerSetResponse(Coap::Message *aMessage, Er uint8_t state; SuccessOrExit(error = aResult); - VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged && Tlv::Find(*aMessage, state) == kErrorNone && + VerifyOrExit(aMessage->ReadCode() == Coap::kCodeChanged && Tlv::Find(*aMessage, state) == kErrorNone && state != StateTlv::kPending, error = kErrorParse); @@ -727,7 +727,7 @@ void Commissioner::HandleLeaderPetitionResponse(Coap::Message *aMessage, Error a bool retransmit = false; VerifyOrExit(mState != kStateActive); - VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged, + VerifyOrExit(aResult == kErrorNone && aMessage->ReadCode() == Coap::kCodeChanged, retransmit = (mState == kStatePetition)); LogInfo("Received %s response", UriToString()); @@ -799,7 +799,7 @@ void Commissioner::HandleLeaderKeepAliveResponse(Coap::Message *aMessage, Error uint8_t state; VerifyOrExit(mState == kStateActive); - VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged, + VerifyOrExit(aResult == kErrorNone && aMessage->ReadCode() == Coap::kCodeChanged, IgnoreError(Stop(kDoNotSendKeepAlive))); LogInfo("Received %s response", UriToString()); @@ -824,7 +824,7 @@ template <> void Commissioner::HandleTmf(Coap::Msg &aMsg) VerifyOrExit(mState == kStateActive, error = kErrorInvalidState); - VerifyOrExit(aMsg.mMessage.IsNonConfirmablePostRequest()); + VerifyOrExit(aMsg.IsNonConfirmablePostRequest()); SuccessOrExit(error = Tlv::Find(aMsg.mMessage, joinerPort)); SuccessOrExit(error = Tlv::Find(aMsg.mMessage, joinerIid)); @@ -893,7 +893,7 @@ void Commissioner::HandleJoinerSessionTimer(void) template <> void Commissioner::HandleTmf(Coap::Msg &aMsg) { VerifyOrExit(mState == kStateActive); - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest()); + VerifyOrExit(aMsg.IsConfirmablePostRequest()); LogInfo("Received %s", UriToString()); diff --git a/src/core/meshcop/energy_scan_client.cpp b/src/core/meshcop/energy_scan_client.cpp index 8611ae0ba..a653849e5 100644 --- a/src/core/meshcop/energy_scan_client.cpp +++ b/src/core/meshcop/energy_scan_client.cpp @@ -62,7 +62,7 @@ Error EnergyScanClient::SendQuery(uint32_t aChannelMas VerifyOrExit((message = Get().NewPriorityMessage()) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->InitAsPost(aAddress, kUriEnergyScan)); - SuccessOrExit(error = message->SetPayloadMarker()); + SuccessOrExit(error = message->AppendPayloadMarker()); SuccessOrExit( error = Tlv::Append(*message, Get().GetSessionId())); @@ -90,7 +90,7 @@ template <> void EnergyScanClient::HandleTmf(Coap::Msg &aMsg) uint32_t mask; MeshCoP::EnergyListTlv energyListTlv; - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest()); + VerifyOrExit(aMsg.IsConfirmablePostRequest()); LogInfo("Received %s", UriToString()); diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp index 67c2208da..e98887136 100644 --- a/src/core/meshcop/joiner.cpp +++ b/src/core/meshcop/joiner.cpp @@ -471,12 +471,15 @@ exit: void Joiner::HandleJoinerFinalizeResponse(Coap::Message *aMessage, Error aResult) { - uint8_t state; + uint8_t state; + Coap::HeaderInfo header; VerifyOrExit(mState == kStateConnected && aResult == kErrorNone); OT_ASSERT(aMessage != nullptr); - VerifyOrExit(aMessage->IsAck() && aMessage->GetCode() == Coap::kCodeChanged); + SuccessOrExit(aMessage->ParseHeaderInfo(header)); + + VerifyOrExit(header.IsAck() && header.GetCode() == Coap::kCodeChanged); SuccessOrExit(Tlv::Find(*aMessage, state)); @@ -499,7 +502,7 @@ template <> void Joiner::HandleTmf(Coap::Msg &aMsg) Error error; Dataset::Info datasetInfo; - VerifyOrExit(mState == kStateEntrust && aMsg.mMessage.IsConfirmablePostRequest(), error = kErrorDrop); + VerifyOrExit(mState == kStateEntrust && aMsg.IsConfirmablePostRequest(), error = kErrorDrop); LogInfo("Received %s", UriToString()); LogCert("[THCI] direction=recv | type=JOIN_ENT.ntf"); diff --git a/src/core/meshcop/joiner_router.cpp b/src/core/meshcop/joiner_router.cpp index 1230ca2e8..a9ca147a8 100644 --- a/src/core/meshcop/joiner_router.cpp +++ b/src/core/meshcop/joiner_router.cpp @@ -162,7 +162,7 @@ template <> void JoinerRouter::HandleTmf(Coap::Msg &aMsg) Message::Settings settings(kNoLinkSecurity, Message::kPriorityNet); Ip6::MessageInfo messageInfo; - VerifyOrExit(aMsg.mMessage.IsNonConfirmablePostRequest(), error = kErrorDrop); + VerifyOrExit(aMsg.IsNonConfirmablePostRequest(), error = kErrorDrop); LogInfo("Received %s", UriToString()); @@ -310,7 +310,7 @@ void JoinerRouter::HandleJoinerEntrustResponse(Coap::Message *aMessage, Error aR VerifyOrExit(aResult == kErrorNone && aMessage != nullptr); - VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged); + VerifyOrExit(aMessage->ReadCode() == Coap::kCodeChanged); LogInfo("Receive %s response", UriToString()); LogCert("[THCI] direction=recv | type=JOIN_ENT.rsp"); diff --git a/src/core/meshcop/panid_query_client.cpp b/src/core/meshcop/panid_query_client.cpp index 4f27013bb..104c96b5d 100644 --- a/src/core/meshcop/panid_query_client.cpp +++ b/src/core/meshcop/panid_query_client.cpp @@ -60,7 +60,7 @@ Error PanIdQueryClient::SendQuery(uint16_t aPanId, VerifyOrExit((message = Get().NewPriorityMessage()) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->InitAsPost(aAddress, kUriPanIdQuery)); - SuccessOrExit(error = message->SetPayloadMarker()); + SuccessOrExit(error = message->AppendPayloadMarker()); SuccessOrExit( error = Tlv::Append(*message, Get().GetSessionId())); @@ -86,7 +86,7 @@ template <> void PanIdQueryClient::HandleTmf(Coap::Msg &aMsg) uint16_t panId; uint32_t mask; - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest()); + VerifyOrExit(aMsg.IsConfirmablePostRequest()); LogInfo("Received %s", UriToString()); diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index a5bb07dbd..7f9c6cbaf 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -1119,7 +1119,7 @@ template <> void TcatAgent::HandleTmf(Coap::Msg &aMsg) uint16_t durationSec = 0; uint32_t durationMs; - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest()); + VerifyOrExit(aMsg.IsConfirmablePostRequest()); LogInfo("Received %s from %s", UriToString(), aMsg.mMessageInfo.GetPeerAddr().ToString().AsCString()); message = Get().NewResponseMessage(aMsg.mMessage); diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index d21263ad1..ea47fcf23 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -655,7 +655,7 @@ template <> void AddressResolver::HandleTmf(Coap::Msg &aMsg) CacheEntry *entry; CacheEntry *prev; - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest()); + VerifyOrExit(aMsg.IsConfirmablePostRequest()); SuccessOrExit(Tlv::Find(aMsg.mMessage, target)); SuccessOrExit(Tlv::Find(aMsg.mMessage, meshLocalIid)); @@ -725,9 +725,10 @@ void AddressResolver::SendAddressError(const Ip6::Address &aTarget, VerifyOrExit((message = Get().NewMessage()) != nullptr, error = kErrorNoBufs); - message->Init(aDestination == nullptr ? Coap::kTypeNonConfirmable : Coap::kTypeConfirmable, Coap::kCodePost); + SuccessOrExit(error = message->Init(aDestination == nullptr ? Coap::kTypeNonConfirmable : Coap::kTypeConfirmable, + Coap::kCodePost)); SuccessOrExit(error = message->AppendUriPathOptions(PathForUri(kUriAddressError))); - SuccessOrExit(error = message->SetPayloadMarker()); + SuccessOrExit(error = message->AppendPayloadMarker()); SuccessOrExit(error = Tlv::Append(*message, aTarget)); SuccessOrExit(error = Tlv::Append(*message, aMeshLocalIid)); @@ -766,11 +767,11 @@ template <> void AddressResolver::HandleTmf(Coap::Msg &aMsg) Ip6::Address destination; #endif - VerifyOrExit(aMsg.mMessage.IsPostRequest(), error = kErrorDrop); + VerifyOrExit(aMsg.IsPostRequest(), error = kErrorDrop); LogInfo("Received %s", UriToString()); - if (aMsg.mMessage.IsConfirmable() && !aMsg.mMessageInfo.GetSockAddr().IsMulticast()) + if (aMsg.IsConfirmable() && !aMsg.mMessageInfo.GetSockAddr().IsMulticast()) { if (Get().SendEmptyAck(aMsg) == kErrorNone) { @@ -842,7 +843,7 @@ template <> void AddressResolver::HandleTmf(Coap::Msg &aMsg) Ip6::Address target; uint32_t lastTransactionTime; - VerifyOrExit(aMsg.mMessage.IsNonConfirmablePostRequest()); + VerifyOrExit(aMsg.IsNonConfirmablePostRequest()); SuccessOrExit(Tlv::Find(aMsg.mMessage, target)); diff --git a/src/core/thread/announce_begin_server.cpp b/src/core/thread/announce_begin_server.cpp index cb7dc2032..52bb560c4 100644 --- a/src/core/thread/announce_begin_server.cpp +++ b/src/core/thread/announce_begin_server.cpp @@ -58,7 +58,7 @@ template <> void AnnounceBeginServer::HandleTmf(Coap::Msg &aM uint8_t count; uint16_t period; - VerifyOrExit(aMsg.mMessage.IsPostRequest()); + VerifyOrExit(aMsg.IsPostRequest()); SuccessOrExit(MeshCoP::ChannelMaskTlv::FindIn(aMsg.mMessage, mask)); SuccessOrExit(Tlv::Find(aMsg.mMessage, count)); @@ -66,7 +66,7 @@ template <> void AnnounceBeginServer::HandleTmf(Coap::Msg &aM SendAnnounce(mask, count, period); - if (aMsg.mMessage.IsConfirmable() && !aMsg.mMessageInfo.GetSockAddr().IsMulticast()) + if (aMsg.IsConfirmable() && !aMsg.mMessageInfo.GetSockAddr().IsMulticast()) { SuccessOrExit(Get().SendEmptyAck(aMsg)); LogInfo("Sent %s response", UriToString()); diff --git a/src/core/thread/anycast_locator.cpp b/src/core/thread/anycast_locator.cpp index e06e716e4..f24dd7e7b 100644 --- a/src/core/thread/anycast_locator.cpp +++ b/src/core/thread/anycast_locator.cpp @@ -113,7 +113,7 @@ template <> void AnycastLocator::HandleTmf(Coap::Msg &aMsg) { Coap::Message *message = nullptr; - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest()); + VerifyOrExit(aMsg.IsConfirmablePostRequest()); message = Get().NewResponseMessage(aMsg.mMessage); VerifyOrExit(message != nullptr); diff --git a/src/core/thread/dua_manager.cpp b/src/core/thread/dua_manager.cpp index 343312e8b..7ad3681e1 100644 --- a/src/core/thread/dua_manager.cpp +++ b/src/core/thread/dua_manager.cpp @@ -557,7 +557,7 @@ void DuaManager::HandleDuaResponse(Coap::Message *aMessage, Error aResult) VerifyOrExit(aResult == kErrorNone, error = kErrorParse); OT_ASSERT(aMessage != nullptr); - VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged || aMessage->GetCode() >= Coap::kCodeBadRequest, + VerifyOrExit(aMessage->ReadCode() == Coap::kCodeChanged || aMessage->ReadCode() >= Coap::kCodeBadRequest, error = kErrorParse); error = ProcessDuaResponse(*aMessage); @@ -575,9 +575,9 @@ template <> void DuaManager::HandleTmf(Coap::Msg &aMs { Error error; - VerifyOrExit(aMsg.mMessage.IsPostRequest(), error = kErrorParse); + VerifyOrExit(aMsg.IsPostRequest(), error = kErrorParse); - if (aMsg.mMessage.IsConfirmable() && Get().SendEmptyAck(aMsg) == kErrorNone) + if (aMsg.IsConfirmable() && Get().SendEmptyAck(aMsg) == kErrorNone) { LogInfo("Sent %s ack", UriToString()); } @@ -595,7 +595,7 @@ Error DuaManager::ProcessDuaResponse(Coap::Message &aMessage) Ip6::Address target; uint8_t status; - if (aMessage.GetCode() >= Coap::kCodeBadRequest) + if (aMessage.ReadCode() >= Coap::kCodeBadRequest) { status = kDuaGeneralFailure; target = mRegisteringDua; diff --git a/src/core/thread/energy_scan_server.cpp b/src/core/thread/energy_scan_server.cpp index 0d1f94392..50bf0aff5 100644 --- a/src/core/thread/energy_scan_server.cpp +++ b/src/core/thread/energy_scan_server.cpp @@ -58,7 +58,7 @@ template <> void EnergyScanServer::HandleTmf(Coap::Msg &aMsg) uint32_t mask; MeshCoP::Tlv tlv; - VerifyOrExit(aMsg.mMessage.IsPostRequest()); + VerifyOrExit(aMsg.IsPostRequest()); SuccessOrExit(Tlv::Find(aMsg.mMessage, count)); count = Clamp(count, kMinCount, kMaxCount); @@ -87,7 +87,7 @@ template <> void EnergyScanServer::HandleTmf(Coap::Msg &aMsg) mCommissioner = aMsg.mMessageInfo.GetPeerAddr(); - if (aMsg.mMessage.IsConfirmable() && !aMsg.mMessageInfo.GetSockAddr().IsMulticast()) + if (aMsg.IsConfirmable() && !aMsg.mMessageInfo.GetSockAddr().IsMulticast()) { SuccessOrExit(Get().SendEmptyAck(aMsg)); LogInfo("Sent %s ack", UriToString()); diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 810c8dfb6..73389233d 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -1683,7 +1683,7 @@ private: struct AddrSolicitInfo { - Error ParseFrom(const Coap::Message &aMessage); + Error ParseFrom(const Coap::Msg &aMsg); Mac::ExtAddress mExtAddress; uint16_t mRequestedRloc16; diff --git a/src/core/thread/mle_ftd.cpp b/src/core/thread/mle_ftd.cpp index 32e056dd0..e3d6e549e 100644 --- a/src/core/thread/mle_ftd.cpp +++ b/src/core/thread/mle_ftd.cpp @@ -3333,7 +3333,7 @@ void Mle::HandleAddressSolicitResponse(Coap::Message *aMessage, const Ip6::Messa VerifyOrExit(aResult == kErrorNone && aMessage != nullptr && aMessageInfo != nullptr); - VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged); + VerifyOrExit(aMessage->ReadCode() == Coap::kCodeChanged); Log(kMessageReceive, kTypeAddressReply, aMessageInfo->GetPeerAddr()); @@ -3459,18 +3459,18 @@ exit: return willBecomeRouter; } -Error Mle::AddrSolicitInfo::ParseFrom(const Coap::Message &aMessage) +Error Mle::AddrSolicitInfo::ParseFrom(const Coap::Msg &aMsg) { // Parses a `kUriAddressSolicit` request message. Error error; - VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorParse); + VerifyOrExit(aMsg.IsConfirmablePostRequest(), error = kErrorParse); - SuccessOrExit(error = Tlv::Find(aMessage, mExtAddress)); - SuccessOrExit(error = Tlv::Find(aMessage, mReason)); + SuccessOrExit(error = Tlv::Find(aMsg.mMessage, mExtAddress)); + SuccessOrExit(error = Tlv::Find(aMsg.mMessage, mReason)); - switch (Tlv::Find(aMessage, mRequestedRloc16)) + switch (Tlv::Find(aMsg.mMessage, mRequestedRloc16)) { case kErrorNone: break; @@ -3482,7 +3482,7 @@ Error Mle::AddrSolicitInfo::ParseFrom(const Coap::Message &aMessage) } #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE - switch (Tlv::Find(aMessage, mXtalAccuracy)) + switch (Tlv::Find(aMsg.mMessage, mXtalAccuracy)) { case kErrorNone: break; @@ -3576,7 +3576,7 @@ template <> void Mle::HandleTmf(Coap::Msg &aMsg) Log(kMessageReceive, kTypeAddressSolicit, aMsg.mMessageInfo.GetPeerAddr()); - SuccessOrExit(info.ParseFrom(aMsg.mMessage)); + SuccessOrExit(info.ParseFrom(aMsg)); ProcessAddressSolicit(info); @@ -3637,7 +3637,7 @@ template <> void Mle::HandleTmf(Coap::Msg &aMsg) VerifyOrExit(mRole == kRoleLeader); - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest()); + VerifyOrExit(aMsg.IsConfirmablePostRequest()); Log(kMessageReceive, kTypeAddressRelease, aMsg.mMessageInfo.GetPeerAddr()); diff --git a/src/core/thread/mlr_manager.cpp b/src/core/thread/mlr_manager.cpp index 6c6fffab5..200620f54 100644 --- a/src/core/thread/mlr_manager.cpp +++ b/src/core/thread/mlr_manager.cpp @@ -468,7 +468,7 @@ Error MlrManager::ParseMlrResponse(Error aResult, aStatus = kMlrGeneralFailure; VerifyOrExit(aResult == kErrorNone && aMessage != nullptr, error = kErrorParse); - VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, error = kErrorParse); + VerifyOrExit(aMessage->ReadCode() == Coap::kCodeChanged, error = kErrorParse); SuccessOrExit(error = Tlv::Find(*aMessage, aStatus)); diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp index d074cceec..92674fae6 100644 --- a/src/core/thread/network_diagnostic.cpp +++ b/src/core/thread/network_diagnostic.cpp @@ -630,13 +630,13 @@ exit: template <> void Server::HandleTmf(Coap::Msg &aMsg) { - VerifyOrExit(aMsg.mMessage.IsPostRequest()); + VerifyOrExit(aMsg.IsPostRequest()); LogInfo("Received %s from %s", UriToString(), aMsg.mMessageInfo.GetPeerAddr().ToString().AsCString()); // DIAG_GET.qry may be sent as a confirmable request. - if (aMsg.mMessage.IsConfirmable()) + if (aMsg.IsConfirmable()) { IgnoreError(Get().SendEmptyAck(aMsg)); } @@ -886,7 +886,7 @@ void Server::HandleAnswerResponse(Coap::Message &aNextAnswer, SuccessOrExit(error); VerifyOrExit(aResponse != nullptr && aMessageInfo != nullptr, error = kErrorDrop); - VerifyOrExit(aResponse->GetCode() == Coap::kCodeChanged, error = kErrorDrop); + VerifyOrExit(aResponse->ReadCode() == Coap::kCodeChanged, error = kErrorDrop); SendNextAnswer(aNextAnswer, aMessageInfo->GetPeerAddr()); @@ -999,7 +999,7 @@ template <> void Server::HandleTmf(Coap::Msg &aMsg) Error error = kErrorNone; Coap::Message *response = nullptr; - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest(), error = kErrorDrop); + VerifyOrExit(aMsg.IsConfirmablePostRequest(), error = kErrorDrop); LogInfo("Received %s from %s", UriToString(), aMsg.mMessageInfo.GetPeerAddr().ToString().AsCString()); @@ -1021,7 +1021,7 @@ template <> void Server::HandleTmf(Coap::Msg &aMsg) uint8_t type; Tlv tlv; - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest()); + VerifyOrExit(aMsg.IsConfirmablePostRequest()); LogInfo("Received %s from %s", UriToString(), aMsg.mMessageInfo.GetPeerAddr().ToString().AsCString()); @@ -1152,7 +1152,7 @@ exit: void Client::HandleGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) { SuccessOrExit(aResult); - VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, aResult = kErrorFailed); + VerifyOrExit(aMessage->ReadCode() == Coap::kCodeChanged, aResult = kErrorFailed); exit: mGetCallback.InvokeIfSet(aResult, aMessage, aMessageInfo); @@ -1160,7 +1160,7 @@ exit: template <> void Client::HandleTmf(Coap::Msg &aMsg) { - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest()); + VerifyOrExit(aMsg.IsConfirmablePostRequest()); LogInfo("Received %s from %s", ot::UriToString(), aMsg.mMessageInfo.GetPeerAddr().ToString().AsCString()); diff --git a/src/core/thread/panid_query_server.cpp b/src/core/thread/panid_query_server.cpp index 5d3f24dc6..074351f6f 100644 --- a/src/core/thread/panid_query_server.cpp +++ b/src/core/thread/panid_query_server.cpp @@ -52,7 +52,7 @@ template <> void PanIdQueryServer::HandleTmf(Coap::Msg &aMsg) uint16_t panId; uint32_t mask; - VerifyOrExit(aMsg.mMessage.IsPostRequest()); + VerifyOrExit(aMsg.IsPostRequest()); SuccessOrExit(MeshCoP::ChannelMaskTlv::FindIn(aMsg.mMessage, mask)); SuccessOrExit(Tlv::Find(aMsg.mMessage, panId)); @@ -62,7 +62,7 @@ template <> void PanIdQueryServer::HandleTmf(Coap::Msg &aMsg) mPanId = panId; mTimer.Start(kScanDelay); - if (aMsg.mMessage.IsConfirmable() && !aMsg.mMessageInfo.GetSockAddr().IsMulticast()) + if (aMsg.IsConfirmable() && !aMsg.mMessageInfo.GetSockAddr().IsMulticast()) { SuccessOrExit(Get().SendEmptyAck(aMsg)); LogInfo("Sent %s ack", UriToString()); diff --git a/src/core/utils/history_tracker_client.cpp b/src/core/utils/history_tracker_client.cpp index bb3ed9829..50e7ee7f6 100644 --- a/src/core/utils/history_tracker_client.cpp +++ b/src/core/utils/history_tracker_client.cpp @@ -114,7 +114,7 @@ exit: template <> void Client::HandleTmf(Coap::Msg &aMsg) { - VerifyOrExit(aMsg.mMessage.IsConfirmablePostRequest()); + VerifyOrExit(aMsg.IsConfirmablePostRequest()); IgnoreError(Get().SendEmptyAck(aMsg)); LogInfo("Received %s from %s", ot::UriToString(), diff --git a/src/core/utils/history_tracker_server.cpp b/src/core/utils/history_tracker_server.cpp index b60ed3592..db899880e 100644 --- a/src/core/utils/history_tracker_server.cpp +++ b/src/core/utils/history_tracker_server.cpp @@ -49,12 +49,12 @@ Server::Server(Instance &aInstance) template <> void Server::HandleTmf(Coap::Msg &aMsg) { - VerifyOrExit(aMsg.mMessage.IsPostRequest()); + VerifyOrExit(aMsg.IsPostRequest()); LogInfo("Received %s from %s", UriToString(), aMsg.mMessageInfo.GetPeerAddr().ToString().AsCString()); - if (aMsg.mMessage.IsConfirmable()) + if (aMsg.IsConfirmable()) { IgnoreError(Get().SendEmptyAck(aMsg)); } @@ -282,7 +282,7 @@ void Server::HandleAnswerResponse(Coap::Message &aNextAnswer, SuccessOrExit(error); VerifyOrExit(aResponse != nullptr && aMessageInfo != nullptr, error = kErrorDrop); - VerifyOrExit(aResponse->GetCode() == Coap::kCodeChanged, error = kErrorDrop); + VerifyOrExit(aResponse->ReadCode() == Coap::kCodeChanged, error = kErrorDrop); SendNextAnswer(aNextAnswer, aMessageInfo->GetPeerAddr()); diff --git a/src/core/utils/otns.cpp b/src/core/utils/otns.cpp index 4ddecf6df..16099c551 100644 --- a/src/core/utils/otns.cpp +++ b/src/core/utils/otns.cpp @@ -199,13 +199,15 @@ void Otns::EmitCoapStatus(const char *aAction, const Ip6::MessageInfo &aMessageInfo, Error *aError) const { - Error error; - char uriPath[Coap::Message::kMaxReceivedUriPath + 1]; - StatusString string; + Error error; + char uriPath[Coap::Message::kMaxReceivedUriPath + 1]; + StatusString string; + Coap::HeaderInfo header; + SuccessOrExit(error = aMessage.ParseHeaderInfo(header)); SuccessOrExit(error = aMessage.ReadUriPathOptions(uriPath)); - string.Append("coap=%s,%d,%d,%d,%s,%s,%d", aAction, aMessage.GetMessageId(), aMessage.GetType(), aMessage.GetCode(), + string.Append("coap=%s,%d,%d,%d,%s,%s,%d", aAction, header.GetMessageId(), header.GetType(), header.GetCode(), uriPath, aMessageInfo.GetPeerAddr().ToString().AsCString(), aMessageInfo.GetPeerPort()); if (aError != nullptr) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 3dfa921ec..228994cd5 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -206,6 +206,7 @@ ot_unit_test(checksum) ot_unit_test(child) ot_unit_test(child_table) ot_unit_test(cmd_line_parser) +ot_unit_test(coap_message) ot_unit_test(crc) ot_unit_test(data) ot_unit_test(dataset) diff --git a/tests/unit/test_coap_message.cpp b/tests/unit/test_coap_message.cpp new file mode 100644 index 000000000..cfcc04968 --- /dev/null +++ b/tests/unit/test_coap_message.cpp @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2026, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "instance/instance.hpp" + +#include "test_platform.h" +#include "test_util.hpp" + +namespace ot { + +class UnitTester +{ +public: + static void TestCoapMessage(void) + { + Instance *instance; + Coap::Message *message; + Coap::HeaderInfo headerInfo; + Coap::Token readToken; + Coap::Token token; + Coap::Option::Iterator iterator; + uint8_t tokenLength; + uint16_t length; + Ip6::MessageInfo messageInfo; + Coap::Message::UriPathStringBuffer uriBuffer; + + printf("TestCoapMessage()\n"); + + instance = static_cast(testInitInstance()); + VerifyOrQuit(instance != nullptr); + + message = AsCoapMessagePtr(instance->Get().Allocate(Message::kTypeOther)); + VerifyOrQuit(message != nullptr); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + SuccessOrQuit(message->Init(Coap::kTypeNonConfirmable, Coap::kCodePut)); + + SuccessOrQuit(message->ParseHeaderInfo(headerInfo)); + VerifyOrQuit(headerInfo.GetType() == Coap::kTypeNonConfirmable); + VerifyOrQuit(headerInfo.GetCode() == Coap::kCodePut); + VerifyOrQuit(headerInfo.GetMessageId() == 0); + VerifyOrQuit(headerInfo.GetToken().GetLength() == 0); + + VerifyOrQuit(message->ReadType() == Coap::kTypeNonConfirmable); + VerifyOrQuit(message->ReadCode() == Coap::kCodePut); + VerifyOrQuit(message->ReadMessageId() == 0); + + SuccessOrQuit(message->ReadTokenLength(tokenLength)); + VerifyOrQuit(tokenLength == 0); + + SuccessOrQuit(message->ReadToken(readToken)); + VerifyOrQuit(readToken.GetLength() == 0); + + SuccessOrQuit(iterator.Init(*message)); + VerifyOrQuit(iterator.IsDone()); + VerifyOrQuit(!iterator.HasPayloadMarker()); + VerifyOrQuit(iterator.GetPayloadMessageOffset() == message->GetLength()); + + { + Coap::Msg msg(*message, messageInfo); + + SuccessOrQuit(msg.ParseHeaderAndOptions(Coap::Msg::kRemovePayloadMarkerIfNoPayload)); + + VerifyOrQuit(msg.GetType() == Coap::kTypeNonConfirmable); + VerifyOrQuit(msg.GetCode() == Coap::kCodePut); + VerifyOrQuit(msg.GetMessageId() == 0); + VerifyOrQuit(msg.GetToken().GetLength() == 0); + + VerifyOrQuit(msg.mMessage.GetOffset() == msg.mMessage.GetLength()); + } + + // AppendPayloadMaker + + SuccessOrQuit(message->AppendPayloadMarker()); + VerifyOrQuit(message->GetOffset() == message->GetLength()); + + SuccessOrQuit(iterator.Init(*message)); + VerifyOrQuit(iterator.IsDone()); + VerifyOrQuit(iterator.HasPayloadMarker()); + VerifyOrQuit(iterator.GetPayloadMessageOffset() == message->GetLength()); + + { + Coap::Msg msg(*message, messageInfo); + + VerifyOrQuit(msg.ParseHeaderAndOptions(Coap::Msg::kRejectIfNoPayloadWithPayloadMarker) != kErrorNone); + } + + // AppendPayloadMaker again should not make any changes + + length = message->GetLength(); + SuccessOrQuit(message->AppendPayloadMarker()); + VerifyOrQuit(message->GetLength() == length); + VerifyOrQuit(message->GetOffset() == length); + + // Ensure payload marker is removed since we have no payload + + { + Coap::Msg msg(*message, messageInfo); + + SuccessOrQuit(msg.ParseHeaderAndOptions(Coap::Msg::kRemovePayloadMarkerIfNoPayload)); + + VerifyOrQuit(msg.GetType() == Coap::kTypeNonConfirmable); + VerifyOrQuit(msg.GetCode() == Coap::kCodePut); + VerifyOrQuit(msg.GetMessageId() == 0); + VerifyOrQuit(msg.GetToken().GetLength() == 0); + + VerifyOrQuit(msg.mMessage.GetOffset() == msg.mMessage.GetLength()); + } + + SuccessOrQuit(iterator.Init(*message)); + VerifyOrQuit(iterator.IsDone()); + VerifyOrQuit(!iterator.HasPayloadMarker()); + VerifyOrQuit(iterator.GetPayloadMessageOffset() == message->GetLength()); + + SuccessOrQuit(message->Append(0xaa)); + VerifyOrQuit(iterator.Init(*message) != kErrorNone); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + SuccessOrQuit(message->Init(Coap::kTypeConfirmable, Coap::kCodePost, 0x1234)); + + SuccessOrQuit(message->ParseHeaderInfo(headerInfo)); + VerifyOrQuit(headerInfo.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(headerInfo.GetCode() == Coap::kCodePost); + VerifyOrQuit(headerInfo.GetMessageId() == 0x1234); + VerifyOrQuit(headerInfo.GetToken().GetLength() == 0); + + VerifyOrQuit(message->ReadType() == Coap::kTypeConfirmable); + VerifyOrQuit(message->ReadCode() == Coap::kCodePost); + VerifyOrQuit(message->ReadMessageId() == 0x1234); + + SuccessOrQuit(message->ReadTokenLength(tokenLength)); + VerifyOrQuit(tokenLength == 0); + + SuccessOrQuit(message->ReadToken(readToken)); + VerifyOrQuit(readToken.GetLength() == 0); + + SuccessOrQuit(iterator.Init(*message)); + VerifyOrQuit(iterator.IsDone()); + VerifyOrQuit(!iterator.HasPayloadMarker()); + VerifyOrQuit(iterator.GetPayloadMessageOffset() == message->GetLength()); + + { + Coap::Msg msg(*message, messageInfo); + + SuccessOrQuit(msg.ParseHeaderAndOptions(Coap::Msg::kRemovePayloadMarkerIfNoPayload)); + + VerifyOrQuit(msg.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(msg.GetCode() == Coap::kCodePost); + VerifyOrQuit(msg.GetMessageId() == 0x1234); + VerifyOrQuit(msg.GetToken().GetLength() == 0); + + VerifyOrQuit(msg.mMessage.GetOffset() == msg.mMessage.GetLength()); + } + + // Write Token + + token.mLength = 2; + token.m8[0] = 0x11; + token.m8[1] = 0x22; + + SuccessOrQuit(message->WriteToken(token)); + + SuccessOrQuit(message->ParseHeaderInfo(headerInfo)); + VerifyOrQuit(headerInfo.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(headerInfo.GetCode() == Coap::kCodePost); + VerifyOrQuit(headerInfo.GetMessageId() == 0x1234); + VerifyOrQuit(headerInfo.GetToken().GetLength() == 2); + + SuccessOrQuit(message->ReadToken(readToken)); + VerifyOrQuit(readToken.GetLength() == 2); + VerifyOrQuit(readToken == token); + + SuccessOrQuit(iterator.Init(*message)); + VerifyOrQuit(iterator.IsDone()); + VerifyOrQuit(!iterator.HasPayloadMarker()); + VerifyOrQuit(iterator.GetPayloadMessageOffset() == message->GetLength()); + + { + Coap::Msg msg(*message, messageInfo); + + SuccessOrQuit(msg.ParseHeaderAndOptions(Coap::Msg::kRemovePayloadMarkerIfNoPayload)); + + VerifyOrQuit(msg.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(msg.GetCode() == Coap::kCodePost); + VerifyOrQuit(msg.GetMessageId() == 0x1234); + VerifyOrQuit(msg.GetToken().GetLength() == 2); + VerifyOrQuit(msg.GetToken() == token); + + VerifyOrQuit(msg.mMessage.GetOffset() == msg.mMessage.GetLength()); + } + + // Append URI-Path Option + + SuccessOrQuit(message->AppendUriPathOptions("uri")); + + SuccessOrQuit(message->ParseHeaderInfo(headerInfo)); + VerifyOrQuit(headerInfo.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(headerInfo.GetCode() == Coap::kCodePost); + VerifyOrQuit(headerInfo.GetMessageId() == 0x1234); + VerifyOrQuit(headerInfo.GetToken().GetLength() == 2); + + SuccessOrQuit(iterator.Init(*message)); + + VerifyOrQuit(!iterator.IsDone()); + VerifyOrQuit(iterator.GetOption() != nullptr); + VerifyOrQuit(iterator.GetOption()->GetNumber() == Coap::kOptionUriPath); + VerifyOrQuit(iterator.GetOption()->GetLength() == 3); + + SuccessOrQuit(iterator.Advance()); + VerifyOrQuit(iterator.IsDone()); + VerifyOrQuit(!iterator.HasPayloadMarker()); + VerifyOrQuit(iterator.GetPayloadMessageOffset() == message->GetLength()); + + SuccessOrQuit(message->AppendPayloadMarker()); + + SuccessOrQuit(iterator.Init(*message)); + VerifyOrQuit(!iterator.IsDone()); + SuccessOrQuit(iterator.Advance()); + VerifyOrQuit(iterator.IsDone()); + VerifyOrQuit(iterator.HasPayloadMarker()); + VerifyOrQuit(iterator.GetPayloadMessageOffset() == message->GetLength()); + + // Append some payload after marker + + length = message->GetLength(); + SuccessOrQuit(message->Append(0xef)); + + SuccessOrQuit(iterator.Init(*message)); + VerifyOrQuit(!iterator.IsDone()); + SuccessOrQuit(iterator.Advance()); + VerifyOrQuit(iterator.IsDone()); + VerifyOrQuit(iterator.HasPayloadMarker()); + VerifyOrQuit(iterator.GetPayloadMessageOffset() == length); + + { + Coap::Msg msg(*message, messageInfo); + + SuccessOrQuit(msg.ParseHeaderAndOptions(Coap::Msg::kRemovePayloadMarkerIfNoPayload)); + + VerifyOrQuit(msg.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(msg.GetCode() == Coap::kCodePost); + VerifyOrQuit(msg.GetMessageId() == 0x1234); + VerifyOrQuit(msg.GetToken().GetLength() == 2); + VerifyOrQuit(msg.GetToken() == token); + + VerifyOrQuit(msg.mMessage.GetOffset() == length); + } + + // Re-write Token + + token.mLength = 2; + token.m8[0] = 0x33; + token.m8[1] = 0x44; + + SuccessOrQuit(message->WriteToken(token)); + + SuccessOrQuit(message->ParseHeaderInfo(headerInfo)); + VerifyOrQuit(headerInfo.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(headerInfo.GetCode() == Coap::kCodePost); + VerifyOrQuit(headerInfo.GetMessageId() == 0x1234); + VerifyOrQuit(headerInfo.GetToken().GetLength() == 2); + + SuccessOrQuit(message->ReadToken(readToken)); + VerifyOrQuit(readToken.GetLength() == 2); + VerifyOrQuit(readToken == token); + + { + Coap::Msg msg(*message, messageInfo); + + SuccessOrQuit(msg.ParseHeaderAndOptions(Coap::Msg::kRemovePayloadMarkerIfNoPayload)); + + VerifyOrQuit(msg.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(msg.GetCode() == Coap::kCodePost); + VerifyOrQuit(msg.GetMessageId() == 0x1234); + VerifyOrQuit(msg.GetToken().GetLength() == 2); + VerifyOrQuit(msg.GetToken() == token); + + VerifyOrQuit(msg.mMessage.GetOffset() == length); + } + + // Re-write token - changing token length afterwards is not allowed + + token.mLength = 3; + token.m8[2] = 0x55; + + VerifyOrQuit(message->WriteToken(token) != kErrorNone); + + SuccessOrQuit(message->ParseHeaderInfo(headerInfo)); + VerifyOrQuit(headerInfo.GetToken().GetLength() == 2); + + token.mLength = 2; + + SuccessOrQuit(message->ReadToken(readToken)); + VerifyOrQuit(readToken.GetLength() == 2); + VerifyOrQuit(readToken == token); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + SuccessOrQuit(message->Init(Coap::kTypeConfirmable, Coap::kCodeGet, kUriCommissionerSet)); + + SuccessOrQuit(message->ParseHeaderInfo(headerInfo)); + VerifyOrQuit(headerInfo.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(headerInfo.GetCode() == Coap::kCodeGet); + VerifyOrQuit(headerInfo.GetMessageId() == 0); + VerifyOrQuit(headerInfo.GetToken().GetLength() == Coap::Token::kDefaultLength); + + VerifyOrQuit(message->ReadType() == Coap::kTypeConfirmable); + VerifyOrQuit(message->ReadCode() == Coap::kCodeGet); + VerifyOrQuit(message->ReadMessageId() == 0); + + SuccessOrQuit(message->ReadTokenLength(tokenLength)); + VerifyOrQuit(tokenLength == Coap::Token::kDefaultLength); + + SuccessOrQuit(iterator.Init(*message)); + + VerifyOrQuit(!iterator.IsDone()); + VerifyOrQuit(iterator.GetOption() != nullptr); + VerifyOrQuit(iterator.GetOption()->GetNumber() == Coap::kOptionUriPath); + + SuccessOrQuit(iterator.Advance()); + VerifyOrQuit(!iterator.IsDone()); + VerifyOrQuit(iterator.GetOption() != nullptr); + VerifyOrQuit(iterator.GetOption()->GetNumber() == Coap::kOptionUriPath); + + SuccessOrQuit(iterator.Advance()); + VerifyOrQuit(iterator.IsDone()); + VerifyOrQuit(!iterator.HasPayloadMarker()); + VerifyOrQuit(iterator.GetPayloadMessageOffset() == message->GetLength()); + + SuccessOrQuit(message->ReadUriPathOptions(uriBuffer)); + VerifyOrQuit(StringMatch(uriBuffer, PathForUri(kUriCommissionerSet))); + + { + Coap::Msg msg(*message, messageInfo); + + SuccessOrQuit(msg.ParseHeaderAndOptions(Coap::Msg::kRemovePayloadMarkerIfNoPayload)); + + VerifyOrQuit(msg.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(msg.GetCode() == Coap::kCodeGet); + VerifyOrQuit(msg.GetMessageId() == 0); + VerifyOrQuit(msg.GetToken().GetLength() == 2); + + VerifyOrQuit(msg.mMessage.GetOffset() == msg.mMessage.GetLength()); + } + + // Re-write code, type, and message ID + + message->WriteType(Coap::kTypeNonConfirmable); + message->WriteCode(Coap::kCodePost); + message->WriteMessageId(0x9876); + + SuccessOrQuit(message->ParseHeaderInfo(headerInfo)); + VerifyOrQuit(headerInfo.GetType() == Coap::kTypeNonConfirmable); + VerifyOrQuit(headerInfo.GetCode() == Coap::kCodePost); + VerifyOrQuit(headerInfo.GetMessageId() == 0x9876); + + { + Coap::Msg msg(*message, messageInfo); + + SuccessOrQuit(msg.ParseHeaderAndOptions(Coap::Msg::kRemovePayloadMarkerIfNoPayload)); + + VerifyOrQuit(msg.GetType() == Coap::kTypeNonConfirmable); + VerifyOrQuit(msg.GetCode() == Coap::kCodePost); + VerifyOrQuit(msg.GetMessageId() == 0x9876); + VerifyOrQuit(msg.GetToken().GetLength() == 2); + + VerifyOrQuit(msg.mMessage.GetOffset() == msg.mMessage.GetLength()); + + // Msg::UpdateType() + + msg.UpdateType(Coap::kTypeConfirmable); + + VerifyOrQuit(msg.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(msg.GetCode() == Coap::kCodePost); + VerifyOrQuit(msg.GetMessageId() == 0x9876); + + VerifyOrQuit(message->ReadType() == Coap::kTypeConfirmable); + + // Msg::UpdateMessageId() + + msg.UpdateMessageId(0xabcd); + + VerifyOrQuit(msg.GetType() == Coap::kTypeConfirmable); + VerifyOrQuit(msg.GetCode() == Coap::kCodePost); + VerifyOrQuit(msg.GetMessageId() == 0xabcd); + + VerifyOrQuit(message->ReadMessageId() == 0xabcd); + } + + message->Free(); + testFreeInstance(instance); + } +}; + +} // namespace ot + +int main(void) +{ + ot::UnitTester::TestCoapMessage(); + printf("All tests passed\n"); + return 0; +}