| /* |
| * Copyright (c) 2016, 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. |
| */ |
| |
| /** |
| * @file |
| * This file implements the CoAP message generation and parsing. |
| */ |
| |
| #include "coap_message.hpp" |
| |
| #include "coap/coap.hpp" |
| #include "common/code_utils.hpp" |
| #include "common/debug.hpp" |
| #include "common/encoding.hpp" |
| #include "common/instance.hpp" |
| #include "common/random.hpp" |
| |
| namespace ot { |
| namespace Coap { |
| |
| void Message::Init(void) |
| { |
| GetHelpData().Clear(); |
| SetVersion(kVersion1); |
| SetOffset(0); |
| GetHelpData().mHeaderLength = kMinHeaderLength; |
| |
| SetLength(GetHelpData().mHeaderLength); |
| } |
| |
| void Message::Init(Type aType, Code aCode) |
| { |
| Init(); |
| SetType(aType); |
| SetCode(aCode); |
| } |
| |
| otError Message::Init(Type aType, Code aCode, const char *aUriPath) |
| { |
| otError error; |
| |
| Init(aType, aCode); |
| SuccessOrExit(error = SetToken(kDefaultTokenLength)); |
| SuccessOrExit(error = AppendUriPathOptions(aUriPath)); |
| |
| exit: |
| return error; |
| } |
| |
| void Message::Finish(void) |
| { |
| Write(0, GetOptionStart(), &GetHelpData().mHeader); |
| } |
| |
| otError Message::AppendOption(uint16_t aNumber, uint16_t aLength, const void *aValue) |
| { |
| otError error = OT_ERROR_NONE; |
| uint16_t optionDelta = aNumber - GetHelpData().mOptionLast; |
| uint16_t optionLength; |
| uint8_t buf[kMaxOptionHeaderSize] = {0}; |
| uint8_t *cur = &buf[1]; |
| |
| // Assure that no option is inserted out of order. |
| VerifyOrExit(aNumber >= GetHelpData().mOptionLast, error = OT_ERROR_INVALID_ARGS); |
| |
| // Calculate the total option size and check the buffers. |
| optionLength = 1 + aLength; |
| optionLength += optionDelta < kOption1ByteExtensionOffset ? 0 : (optionDelta < kOption2ByteExtensionOffset ? 1 : 2); |
| optionLength += aLength < kOption1ByteExtensionOffset ? 0 : (aLength < kOption2ByteExtensionOffset ? 1 : 2); |
| VerifyOrExit(GetLength() + optionLength < kMaxHeaderLength, error = OT_ERROR_NO_BUFS); |
| |
| // Insert option delta. |
| if (optionDelta < kOption1ByteExtensionOffset) |
| { |
| *buf = (optionDelta << kOptionDeltaOffset) & kOptionDeltaMask; |
| } |
| else if (optionDelta < kOption2ByteExtensionOffset) |
| { |
| *buf |= kOption1ByteExtension << kOptionDeltaOffset; |
| *cur++ = (optionDelta - kOption1ByteExtensionOffset) & 0xff; |
| } |
| else |
| { |
| *buf |= kOption2ByteExtension << kOptionDeltaOffset; |
| optionDelta -= kOption2ByteExtensionOffset; |
| *cur++ = optionDelta >> 8; |
| *cur++ = optionDelta & 0xff; |
| } |
| |
| // Insert option length. |
| if (aLength < kOption1ByteExtensionOffset) |
| { |
| *buf |= aLength; |
| } |
| else if (aLength < kOption2ByteExtensionOffset) |
| { |
| *buf |= kOption1ByteExtension; |
| *cur++ = (aLength - kOption1ByteExtensionOffset) & 0xff; |
| } |
| else |
| { |
| *buf |= kOption2ByteExtension; |
| optionLength = aLength - kOption2ByteExtensionOffset; |
| *cur++ = optionLength >> 8; |
| *cur++ = optionLength & 0xff; |
| } |
| |
| SuccessOrExit(error = Append(buf, static_cast<uint16_t>(cur - buf))); |
| SuccessOrExit(error = Append(aValue, aLength)); |
| |
| GetHelpData().mOptionLast = aNumber; |
| |
| GetHelpData().mHeaderLength = GetLength(); |
| |
| exit: |
| return error; |
| } |
| |
| otError Message::AppendUintOption(uint16_t aNumber, uint32_t aValue) |
| { |
| uint16_t length = sizeof(aValue); |
| uint8_t *value; |
| |
| aValue = Encoding::BigEndian::HostSwap32(aValue); |
| value = reinterpret_cast<uint8_t *>(&aValue); |
| |
| // skip preceding zeros |
| while (value[0] == 0 && length > 0) |
| { |
| value++; |
| length--; |
| } |
| |
| return AppendOption(aNumber, length, value); |
| } |
| |
| otError Message::AppendStringOption(uint16_t aNumber, const char *aValue) |
| { |
| return AppendOption(aNumber, static_cast<uint16_t>(strlen(aValue)), aValue); |
| } |
| |
| otError Message::AppendObserveOption(uint32_t aObserve) |
| { |
| return AppendUintOption(OT_COAP_OPTION_OBSERVE, aObserve & 0xFFFFFF); |
| } |
| |
| otError Message::AppendUriPathOptions(const char *aUriPath) |
| { |
| otError error = OT_ERROR_NONE; |
| const char *cur = aUriPath; |
| const char *end; |
| |
| while ((end = strchr(cur, '/')) != NULL) |
| { |
| SuccessOrExit(error = AppendOption(OT_COAP_OPTION_URI_PATH, static_cast<uint16_t>(end - cur), cur)); |
| cur = end + 1; |
| } |
| |
| SuccessOrExit(error = AppendStringOption(OT_COAP_OPTION_URI_PATH, cur)); |
| |
| exit: |
| return error; |
| } |
| |
| otError Message::AppendProxyUriOption(const char *aProxyUri) |
| { |
| return AppendStringOption(OT_COAP_OPTION_PROXY_URI, aProxyUri); |
| } |
| |
| otError Message::AppendContentFormatOption(otCoapOptionContentFormat aContentFormat) |
| { |
| return AppendUintOption(OT_COAP_OPTION_CONTENT_FORMAT, static_cast<uint32_t>(aContentFormat)); |
| } |
| |
| otError Message::AppendMaxAgeOption(uint32_t aMaxAge) |
| { |
| return AppendUintOption(OT_COAP_OPTION_MAX_AGE, aMaxAge); |
| } |
| |
| otError Message::AppendUriQueryOption(const char *aUriQuery) |
| { |
| return AppendStringOption(OT_COAP_OPTION_URI_QUERY, aUriQuery); |
| } |
| |
| otError Message::SetPayloadMarker(void) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t marker = 0xff; |
| |
| VerifyOrExit(GetLength() < kMaxHeaderLength, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = Append(&marker, sizeof(marker))); |
| GetHelpData().mHeaderLength = GetLength(); |
| |
| // Set offset to the start of payload. |
| SetOffset(GetHelpData().mHeaderLength); |
| |
| exit: |
| return error; |
| } |
| |
| otError Message::ParseHeader(void) |
| { |
| otError error = OT_ERROR_NONE; |
| OptionIterator iterator; |
| |
| assert(mBuffer.mHead.mInfo.mReserved >= |
| sizeof(GetHelpData()) + |
| static_cast<size_t>((reinterpret_cast<uint8_t *>(&GetHelpData()) - mBuffer.mHead.mData))); |
| |
| GetHelpData().Clear(); |
| |
| GetHelpData().mHeaderOffset = GetOffset(); |
| Read(GetHelpData().mHeaderOffset, sizeof(GetHelpData().mHeader), &GetHelpData().mHeader); |
| |
| SuccessOrExit(error = iterator.Init(this)); |
| for (const otCoapOption *option = iterator.GetFirstOption(); option != NULL; option = iterator.GetNextOption()) |
| { |
| } |
| |
| VerifyOrExit(iterator.mNextOptionOffset > 0, error = OT_ERROR_PARSE); |
| GetHelpData().mHeaderLength = iterator.mNextOptionOffset - GetHelpData().mHeaderOffset; |
| MoveOffset(GetHelpData().mHeaderLength); |
| |
| exit: |
| return error; |
| } |
| |
| otError Message::SetToken(const uint8_t *aToken, uint8_t aTokenLength) |
| { |
| GetHelpData().mHeader.mVersionTypeToken = (GetHelpData().mHeader.mVersionTypeToken & ~kTokenLengthMask) | |
| ((aTokenLength << kTokenLengthOffset) & kTokenLengthMask); |
| memcpy(GetHelpData().mHeader.mToken, aToken, aTokenLength); |
| GetHelpData().mHeaderLength += aTokenLength; |
| |
| return SetLength(GetHelpData().mHeaderLength); |
| } |
| |
| otError Message::SetToken(uint8_t aTokenLength) |
| { |
| uint8_t token[kMaxTokenLength] = {0}; |
| |
| assert(aTokenLength <= sizeof(token)); |
| |
| Random::NonCrypto::FillBuffer(token, aTokenLength); |
| |
| return SetToken(token, aTokenLength); |
| } |
| |
| otError Message::SetDefaultResponseHeader(const Message &aRequest) |
| { |
| Init(OT_COAP_TYPE_ACKNOWLEDGMENT, OT_COAP_CODE_CHANGED); |
| |
| SetMessageId(aRequest.GetMessageId()); |
| |
| return SetToken(aRequest.GetToken(), aRequest.GetTokenLength()); |
| } |
| |
| Message *Message::Clone(uint16_t aLength) const |
| { |
| Message *message = static_cast<Message *>(ot::Message::Clone(aLength)); |
| |
| VerifyOrExit(message != NULL); |
| |
| memcpy(&message->GetHelpData(), &GetHelpData(), sizeof(GetHelpData())); |
| |
| exit: |
| return message; |
| } |
| |
| #if OPENTHREAD_CONFIG_COAP_API_ENABLE |
| const char *Message::CodeToString(void) const |
| { |
| const char *codeString; |
| |
| switch (GetCode()) |
| { |
| case OT_COAP_CODE_INTERNAL_ERROR: |
| codeString = "InternalError"; |
| break; |
| case OT_COAP_CODE_METHOD_NOT_ALLOWED: |
| codeString = "MethodNotAllowed"; |
| break; |
| case OT_COAP_CODE_CONTENT: |
| codeString = "Content"; |
| break; |
| case OT_COAP_CODE_EMPTY: |
| codeString = "Empty"; |
| break; |
| case OT_COAP_CODE_GET: |
| codeString = "Get"; |
| break; |
| case OT_COAP_CODE_POST: |
| codeString = "Post"; |
| break; |
| case OT_COAP_CODE_PUT: |
| codeString = "Put"; |
| break; |
| case OT_COAP_CODE_DELETE: |
| codeString = "Delete"; |
| break; |
| case OT_COAP_CODE_NOT_FOUND: |
| codeString = "NotFound"; |
| break; |
| case OT_COAP_CODE_UNSUPPORTED_FORMAT: |
| codeString = "UnsupportedFormat"; |
| break; |
| case OT_COAP_CODE_RESPONSE_MIN: |
| codeString = "ResponseMin"; |
| break; |
| case OT_COAP_CODE_CREATED: |
| codeString = "Created"; |
| break; |
| case OT_COAP_CODE_DELETED: |
| codeString = "Deleted"; |
| break; |
| case OT_COAP_CODE_VALID: |
| codeString = "Valid"; |
| break; |
| case OT_COAP_CODE_CHANGED: |
| codeString = "Changed"; |
| break; |
| case OT_COAP_CODE_BAD_REQUEST: |
| codeString = "BadRequest"; |
| break; |
| case OT_COAP_CODE_UNAUTHORIZED: |
| codeString = "Unauthorized"; |
| break; |
| case OT_COAP_CODE_BAD_OPTION: |
| codeString = "BadOption"; |
| break; |
| case OT_COAP_CODE_FORBIDDEN: |
| codeString = "Forbidden"; |
| break; |
| case OT_COAP_CODE_NOT_ACCEPTABLE: |
| codeString = "NotAcceptable"; |
| break; |
| case OT_COAP_CODE_PRECONDITION_FAILED: |
| codeString = "PreconditionFailed"; |
| break; |
| case OT_COAP_CODE_REQUEST_TOO_LARGE: |
| codeString = "RequestTooLarge"; |
| break; |
| case OT_COAP_CODE_NOT_IMPLEMENTED: |
| codeString = "NotImplemented"; |
| break; |
| case OT_COAP_CODE_BAD_GATEWAY: |
| codeString = "BadGateway"; |
| break; |
| case OT_COAP_CODE_SERVICE_UNAVAILABLE: |
| codeString = "ServiceUnavailable"; |
| break; |
| case OT_COAP_CODE_GATEWAY_TIMEOUT: |
| codeString = "GatewayTimeout"; |
| break; |
| case OT_COAP_CODE_PROXY_NOT_SUPPORTED: |
| codeString = "ProxyNotSupported"; |
| break; |
| default: |
| codeString = "Unknown"; |
| break; |
| } |
| |
| return codeString; |
| } |
| #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE |
| |
| otError OptionIterator::Init(const Message *aMessage) |
| { |
| otError err = OT_ERROR_NONE; |
| |
| /* |
| * Check that: |
| * Length - Offset: the length of the payload |
| * is greater than: |
| * Start position of options |
| * |
| * → Check options start before the message ends, or bail ::Init with |
| * OT_ERROR_PARSE as the reason. |
| */ |
| VerifyOrExit(aMessage->GetLength() - aMessage->GetHelpData().mHeaderOffset >= aMessage->GetOptionStart(), |
| err = OT_ERROR_PARSE); |
| |
| mMessage = aMessage; |
| GetFirstOption(); |
| |
| exit: |
| return err; |
| } |
| |
| const otCoapOption *OptionIterator::GetFirstOption(void) |
| { |
| const otCoapOption *option = NULL; |
| const Message & message = GetMessage(); |
| |
| ClearOption(); |
| |
| mNextOptionOffset = message.GetHelpData().mHeaderOffset + message.GetOptionStart(); |
| |
| if (mNextOptionOffset < message.GetLength()) |
| { |
| option = GetNextOption(); |
| } |
| |
| return option; |
| } |
| |
| const otCoapOption *OptionIterator::GetNextOption(void) |
| { |
| otError error = OT_ERROR_NONE; |
| uint16_t optionDelta; |
| uint16_t optionLength; |
| uint8_t buf[Message::kMaxOptionHeaderSize]; |
| uint8_t * cur = buf + 1; |
| otCoapOption * rval = NULL; |
| const Message &message = GetMessage(); |
| |
| VerifyOrExit(mNextOptionOffset < message.GetLength(), error = OT_ERROR_NOT_FOUND); |
| |
| message.Read(mNextOptionOffset, sizeof(buf), buf); |
| |
| optionDelta = buf[0] >> 4; |
| optionLength = buf[0] & 0xf; |
| mNextOptionOffset += sizeof(uint8_t); |
| |
| if (optionDelta < Message::kOption1ByteExtension) |
| { |
| // do nothing |
| } |
| else if (optionDelta == Message::kOption1ByteExtension) |
| { |
| optionDelta = Message::kOption1ByteExtensionOffset + cur[0]; |
| mNextOptionOffset += sizeof(uint8_t); |
| cur++; |
| } |
| else if (optionDelta == Message::kOption2ByteExtension) |
| { |
| optionDelta = Message::kOption2ByteExtensionOffset + static_cast<uint16_t>((cur[0] << 8) | cur[1]); |
| mNextOptionOffset += sizeof(uint16_t); |
| cur += 2; |
| } |
| else |
| { |
| // RFC7252 (Section 3): |
| // Reserved for payload marker. |
| VerifyOrExit(optionLength == 0xf, error = OT_ERROR_PARSE); |
| |
| // The presence of a marker followed by a zero-length payload MUST be processed |
| // as a message format error. |
| VerifyOrExit(mNextOptionOffset < message.GetLength(), error = OT_ERROR_PARSE); |
| |
| ExitNow(error = OT_ERROR_NOT_FOUND); |
| } |
| |
| if (optionLength < Message::kOption1ByteExtension) |
| { |
| // do nothing |
| } |
| else if (optionLength == Message::kOption1ByteExtension) |
| { |
| optionLength = Message::kOption1ByteExtensionOffset + cur[0]; |
| mNextOptionOffset += sizeof(uint8_t); |
| } |
| else if (optionLength == Message::kOption2ByteExtension) |
| { |
| optionLength = Message::kOption2ByteExtensionOffset + static_cast<uint16_t>((cur[0] << 8) | cur[1]); |
| mNextOptionOffset += sizeof(uint16_t); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_PARSE); |
| } |
| |
| VerifyOrExit(optionLength <= message.GetLength() - mNextOptionOffset, error = OT_ERROR_PARSE); |
| |
| rval = &mOption; |
| rval->mNumber += optionDelta; |
| rval->mLength = optionLength; |
| |
| mNextOptionOffset += optionLength; |
| |
| exit: |
| if (error == OT_ERROR_PARSE) |
| { |
| mNextOptionOffset = 0; |
| } |
| |
| return rval; |
| } |
| |
| otError OptionIterator::GetOptionValue(void *aValue) const |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(mNextOptionOffset > 0, error = OT_ERROR_NOT_FOUND); |
| |
| VerifyOrExit(GetMessage().Read(mNextOptionOffset - mOption.mLength, mOption.mLength, aValue) == mOption.mLength, |
| error = OT_ERROR_PARSE); |
| |
| exit: |
| return error; |
| } |
| |
| } // namespace Coap |
| } // namespace ot |