/* * Copyright (c) 2020, 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. */ #define OTBR_LOG_TAG "REST" #include "rest/resource.hpp" #define OT_PSKC_MAX_LENGTH 16 #define OT_EXTENDED_PANID_LENGTH 8 #define OT_REST_RESOURCE_PATH_DIAGNOSTICS "/diagnostics" #define OT_REST_RESOURCE_PATH_NODE "/node" #define OT_REST_RESOURCE_PATH_NODE_BAID "/node/ba-id" #define OT_REST_RESOURCE_PATH_NODE_RLOC "/node/rloc" #define OT_REST_RESOURCE_PATH_NODE_RLOC16 "/node/rloc16" #define OT_REST_RESOURCE_PATH_NODE_EXTADDRESS "/node/ext-address" #define OT_REST_RESOURCE_PATH_NODE_STATE "/node/state" #define OT_REST_RESOURCE_PATH_NODE_NETWORKNAME "/node/network-name" #define OT_REST_RESOURCE_PATH_NODE_LEADERDATA "/node/leader-data" #define OT_REST_RESOURCE_PATH_NODE_NUMOFROUTER "/node/num-of-router" #define OT_REST_RESOURCE_PATH_NODE_EXTPANID "/node/ext-panid" #define OT_REST_RESOURCE_PATH_NODE_DATASET_ACTIVE "/node/dataset/active" #define OT_REST_RESOURCE_PATH_NODE_DATASET_PENDING "/node/dataset/pending" #define OT_REST_RESOURCE_PATH_NETWORK "/networks" #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT "/networks/current" #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT_COMMISSION "/networks/commission" #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT_PREFIX "/networks/current/prefix" #define OT_REST_HTTP_STATUS_200 "200 OK" #define OT_REST_HTTP_STATUS_201 "201 Created" #define OT_REST_HTTP_STATUS_204 "204 No Content" #define OT_REST_HTTP_STATUS_400 "400 Bad Request" #define OT_REST_HTTP_STATUS_404 "404 Not Found" #define OT_REST_HTTP_STATUS_405 "405 Method Not Allowed" #define OT_REST_HTTP_STATUS_408 "408 Request Timeout" #define OT_REST_HTTP_STATUS_409 "409 Conflict" #define OT_REST_HTTP_STATUS_500 "500 Internal Server Error" using std::chrono::duration_cast; using std::chrono::microseconds; using std::chrono::steady_clock; using std::placeholders::_1; using std::placeholders::_2; namespace otbr { namespace rest { // MulticastAddr static const char *kMulticastAddrAllRouters = "ff03::2"; // Default TlvTypes for Diagnostic inforamtion static const uint8_t kAllTlvTypes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, 16, 17, 19}; // Timeout (in Microseconds) for deleting outdated diagnostics static const uint32_t kDiagResetTimeout = 3000000; // Timeout (in Microseconds) for collecting diagnostics static const uint32_t kDiagCollectTimeout = 2000000; static std::string GetHttpStatus(HttpStatusCode aErrorCode) { std::string httpStatus; switch (aErrorCode) { case HttpStatusCode::kStatusOk: httpStatus = OT_REST_HTTP_STATUS_200; break; case HttpStatusCode::kStatusCreated: httpStatus = OT_REST_HTTP_STATUS_201; break; case HttpStatusCode::kStatusNoContent: httpStatus = OT_REST_HTTP_STATUS_204; break; case HttpStatusCode::kStatusBadRequest: httpStatus = OT_REST_HTTP_STATUS_400; break; case HttpStatusCode::kStatusResourceNotFound: httpStatus = OT_REST_HTTP_STATUS_404; break; case HttpStatusCode::kStatusMethodNotAllowed: httpStatus = OT_REST_HTTP_STATUS_405; break; case HttpStatusCode::kStatusRequestTimeout: httpStatus = OT_REST_HTTP_STATUS_408; break; case HttpStatusCode::kStatusConflict: httpStatus = OT_REST_HTTP_STATUS_409; break; case HttpStatusCode::kStatusInternalServerError: httpStatus = OT_REST_HTTP_STATUS_500; break; } return httpStatus; } Resource::Resource(RcpHost *aHost) : mInstance(nullptr) , mHost(aHost) { // Resource Handler mResourceMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOSTICS, &Resource::Diagnostic); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE, &Resource::NodeInfo); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_BAID, &Resource::BaId); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_STATE, &Resource::State); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_EXTADDRESS, &Resource::ExtendedAddr); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_NETWORKNAME, &Resource::NetworkName); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_RLOC16, &Resource::Rloc16); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_LEADERDATA, &Resource::LeaderData); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_NUMOFROUTER, &Resource::NumOfRoute); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_EXTPANID, &Resource::ExtendedPanId); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_RLOC, &Resource::Rloc); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_DATASET_ACTIVE, &Resource::DatasetActive); mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_DATASET_PENDING, &Resource::DatasetPending); // Resource callback handler mResourceCallbackMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOSTICS, &Resource::HandleDiagnosticCallback); } void Resource::Init(void) { mInstance = mHost->GetThreadHelper()->GetInstance(); } void Resource::Handle(Request &aRequest, Response &aResponse) const { std::string url = aRequest.GetUrl(); auto it = mResourceMap.find(url); if (it != mResourceMap.end()) { ResourceHandler resourceHandler = it->second; (this->*resourceHandler)(aRequest, aResponse); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusResourceNotFound); } } void Resource::HandleCallback(Request &aRequest, Response &aResponse) { std::string url = aRequest.GetUrl(); auto it = mResourceCallbackMap.find(url); if (it != mResourceCallbackMap.end()) { ResourceCallbackHandler resourceHandler = it->second; (this->*resourceHandler)(aRequest, aResponse); } } void Resource::HandleDiagnosticCallback(const Request &aRequest, Response &aResponse) { OT_UNUSED_VARIABLE(aRequest); std::vector> diagContentSet; std::string body; std::string errorCode; auto duration = duration_cast(steady_clock::now() - aResponse.GetStartTime()).count(); if (duration >= kDiagCollectTimeout) { DeleteOutDatedDiagnostic(); for (auto it = mDiagSet.begin(); it != mDiagSet.end(); ++it) { diagContentSet.push_back(it->second.mDiagContent); } body = Json::Diag2JsonString(diagContentSet); errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); aResponse.SetBody(body); aResponse.SetComplete(); } } void Resource::ErrorHandler(Response &aResponse, HttpStatusCode aErrorCode) const { std::string errorMessage = GetHttpStatus(aErrorCode); std::string body = Json::Error2JsonString(aErrorCode, errorMessage); aResponse.SetResponsCode(errorMessage); aResponse.SetBody(body); aResponse.SetComplete(); } void Resource::GetNodeInfo(Response &aResponse) const { otbrError error = OTBR_ERROR_NONE; struct NodeInfo node = {}; otRouterInfo routerInfo; uint8_t maxRouterId; std::string body; std::string errorCode; VerifyOrExit(otBorderAgentGetId(mInstance, &node.mBaId) == OT_ERROR_NONE, error = OTBR_ERROR_REST); (void)otThreadGetLeaderData(mInstance, &node.mLeaderData); node.mNumOfRouter = 0; maxRouterId = otThreadGetMaxRouterId(mInstance); for (uint8_t i = 0; i <= maxRouterId; ++i) { if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE) { continue; } ++node.mNumOfRouter; } node.mRole = GetDeviceRoleName(otThreadGetDeviceRole(mInstance)); node.mExtAddress = reinterpret_cast(otLinkGetExtendedAddress(mInstance)); node.mNetworkName = otThreadGetNetworkName(mInstance); node.mRloc16 = otThreadGetRloc16(mInstance); node.mExtPanId = reinterpret_cast(otThreadGetExtendedPanId(mInstance)); node.mRlocAddress = *otThreadGetRloc(mInstance); body = Json::Node2JsonString(node); aResponse.SetBody(body); exit: if (error == OTBR_ERROR_NONE) { errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); } } void Resource::DeleteNodeInfo(Response &aResponse) const { otbrError error = OTBR_ERROR_NONE; std::string errorCode; VerifyOrExit(mHost->GetThreadHelper()->Detach() == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE); VerifyOrExit(otInstanceErasePersistentInfo(mInstance) == OT_ERROR_NONE, error = OTBR_ERROR_REST); mHost->Reset(); exit: if (error == OTBR_ERROR_NONE) { errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } else if (error == OTBR_ERROR_INVALID_STATE) { ErrorHandler(aResponse, HttpStatusCode::kStatusConflict); } else if (error != OTBR_ERROR_NONE) { ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); } } void Resource::NodeInfo(const Request &aRequest, Response &aResponse) const { std::string errorCode; switch (aRequest.GetMethod()) { case HttpMethod::kGet: GetNodeInfo(aResponse); break; case HttpMethod::kDelete: DeleteNodeInfo(aResponse); break; default: ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); break; } } void Resource::GetDataBaId(Response &aResponse) const { otbrError error = OTBR_ERROR_NONE; otBorderAgentId id; std::string body; std::string errorCode; VerifyOrExit(otBorderAgentGetId(mInstance, &id) == OT_ERROR_NONE, error = OTBR_ERROR_REST); body = Json::Bytes2HexJsonString(id.mId, sizeof(id)); aResponse.SetBody(body); exit: if (error == OTBR_ERROR_NONE) { errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); } } void Resource::BaId(const Request &aRequest, Response &aResponse) const { std::string errorCode; if (aRequest.GetMethod() == HttpMethod::kGet) { GetDataBaId(aResponse); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); } } void Resource::GetDataExtendedAddr(Response &aResponse) const { const uint8_t *extAddress = reinterpret_cast(otLinkGetExtendedAddress(mInstance)); std::string errorCode; std::string body = Json::Bytes2HexJsonString(extAddress, OT_EXT_ADDRESS_SIZE); aResponse.SetBody(body); errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } void Resource::ExtendedAddr(const Request &aRequest, Response &aResponse) const { std::string errorCode; if (aRequest.GetMethod() == HttpMethod::kGet) { GetDataExtendedAddr(aResponse); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); } } void Resource::GetDataState(Response &aResponse) const { std::string state; std::string errorCode; otDeviceRole role; role = otThreadGetDeviceRole(mInstance); state = Json::String2JsonString(GetDeviceRoleName(role)); aResponse.SetBody(state); errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } void Resource::SetDataState(const Request &aRequest, Response &aResponse) const { otbrError error = OTBR_ERROR_NONE; std::string errorCode; std::string body; VerifyOrExit(Json::JsonString2String(aRequest.GetBody(), body), error = OTBR_ERROR_INVALID_ARGS); if (body == "enable") { if (!otIp6IsEnabled(mInstance)) { VerifyOrExit(otIp6SetEnabled(mInstance, true) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE); } VerifyOrExit(otThreadSetEnabled(mInstance, true) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE); } else if (body == "disable") { VerifyOrExit(otThreadSetEnabled(mInstance, false) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE); VerifyOrExit(otIp6SetEnabled(mInstance, false) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE); } else { ExitNow(error = OTBR_ERROR_INVALID_ARGS); } errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); exit: if (error == OTBR_ERROR_INVALID_STATE) { ErrorHandler(aResponse, HttpStatusCode::kStatusConflict); } if (error == OTBR_ERROR_INVALID_ARGS) { ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest); } else if (error != OTBR_ERROR_NONE) { ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); } } void Resource::State(const Request &aRequest, Response &aResponse) const { std::string errorCode; switch (aRequest.GetMethod()) { case HttpMethod::kGet: GetDataState(aResponse); break; case HttpMethod::kPut: SetDataState(aRequest, aResponse); break; case HttpMethod::kOptions: errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); aResponse.SetComplete(); break; default: ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); break; } } void Resource::GetDataNetworkName(Response &aResponse) const { std::string networkName; std::string errorCode; networkName = otThreadGetNetworkName(mInstance); networkName = Json::String2JsonString(networkName); aResponse.SetBody(networkName); errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } void Resource::NetworkName(const Request &aRequest, Response &aResponse) const { std::string errorCode; if (aRequest.GetMethod() == HttpMethod::kGet) { GetDataNetworkName(aResponse); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); } } void Resource::GetDataLeaderData(Response &aResponse) const { otbrError error = OTBR_ERROR_NONE; otLeaderData leaderData; std::string body; std::string errorCode; VerifyOrExit(otThreadGetLeaderData(mInstance, &leaderData) == OT_ERROR_NONE, error = OTBR_ERROR_REST); body = Json::LeaderData2JsonString(leaderData); aResponse.SetBody(body); exit: if (error == OTBR_ERROR_NONE) { errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); } } void Resource::LeaderData(const Request &aRequest, Response &aResponse) const { std::string errorCode; if (aRequest.GetMethod() == HttpMethod::kGet) { GetDataLeaderData(aResponse); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); } } void Resource::GetDataNumOfRoute(Response &aResponse) const { uint8_t count = 0; uint8_t maxRouterId; otRouterInfo routerInfo; maxRouterId = otThreadGetMaxRouterId(mInstance); std::string body; std::string errorCode; for (uint8_t i = 0; i <= maxRouterId; ++i) { if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE) { continue; } ++count; } body = Json::Number2JsonString(count); aResponse.SetBody(body); errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } void Resource::NumOfRoute(const Request &aRequest, Response &aResponse) const { std::string errorCode; if (aRequest.GetMethod() == HttpMethod::kGet) { GetDataNumOfRoute(aResponse); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); } } void Resource::GetDataRloc16(Response &aResponse) const { uint16_t rloc16 = otThreadGetRloc16(mInstance); std::string body; std::string errorCode; body = Json::Number2JsonString(rloc16); aResponse.SetBody(body); errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } void Resource::Rloc16(const Request &aRequest, Response &aResponse) const { std::string errorCode; if (aRequest.GetMethod() == HttpMethod::kGet) { GetDataRloc16(aResponse); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); } } void Resource::GetDataExtendedPanId(Response &aResponse) const { const uint8_t *extPanId = reinterpret_cast(otThreadGetExtendedPanId(mInstance)); std::string body = Json::Bytes2HexJsonString(extPanId, OT_EXT_PAN_ID_SIZE); std::string errorCode; aResponse.SetBody(body); errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } void Resource::ExtendedPanId(const Request &aRequest, Response &aResponse) const { std::string errorCode; if (aRequest.GetMethod() == HttpMethod::kGet) { GetDataExtendedPanId(aResponse); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); } } void Resource::GetDataRloc(Response &aResponse) const { otIp6Address rlocAddress = *otThreadGetRloc(mInstance); std::string body; std::string errorCode; body = Json::IpAddr2JsonString(rlocAddress); aResponse.SetBody(body); errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } void Resource::Rloc(const Request &aRequest, Response &aResponse) const { std::string errorCode; if (aRequest.GetMethod() == HttpMethod::kGet) { GetDataRloc(aResponse); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); } } void Resource::GetDataset(DatasetType aDatasetType, const Request &aRequest, Response &aResponse) const { otbrError error = OTBR_ERROR_NONE; struct NodeInfo node; std::string body; std::string errorCode; otOperationalDataset dataset; otOperationalDatasetTlvs datasetTlvs; if (aRequest.GetHeaderValue(OT_REST_ACCEPT_HEADER) == OT_REST_CONTENT_TYPE_PLAIN) { if (aDatasetType == DatasetType::kActive) { VerifyOrExit(otDatasetGetActiveTlvs(mInstance, &datasetTlvs) == OT_ERROR_NONE, error = OTBR_ERROR_NOT_FOUND); } else if (aDatasetType == DatasetType::kPending) { VerifyOrExit(otDatasetGetPendingTlvs(mInstance, &datasetTlvs) == OT_ERROR_NONE, error = OTBR_ERROR_NOT_FOUND); } aResponse.SetContentType(OT_REST_CONTENT_TYPE_PLAIN); body = Utils::Bytes2Hex(datasetTlvs.mTlvs, datasetTlvs.mLength); } else { if (aDatasetType == DatasetType::kActive) { VerifyOrExit(otDatasetGetActive(mInstance, &dataset) == OT_ERROR_NONE, error = OTBR_ERROR_NOT_FOUND); body = Json::ActiveDataset2JsonString(dataset); } else if (aDatasetType == DatasetType::kPending) { VerifyOrExit(otDatasetGetPending(mInstance, &dataset) == OT_ERROR_NONE, error = OTBR_ERROR_NOT_FOUND); body = Json::PendingDataset2JsonString(dataset); } } aResponse.SetBody(body); exit: if (error == OTBR_ERROR_NONE) { errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); } else if (error == OTBR_ERROR_NOT_FOUND) { errorCode = GetHttpStatus(HttpStatusCode::kStatusNoContent); aResponse.SetResponsCode(errorCode); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); } } void Resource::SetDataset(DatasetType aDatasetType, const Request &aRequest, Response &aResponse) const { otError errorOt = OT_ERROR_NONE; otbrError error = OTBR_ERROR_NONE; struct NodeInfo node; std::string body; std::string errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); otOperationalDataset dataset = {}; otOperationalDatasetTlvs datasetTlvs; otOperationalDatasetTlvs datasetUpdateTlvs; int ret; bool isTlv; if (aDatasetType == DatasetType::kActive) { VerifyOrExit(otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_DISABLED, error = OTBR_ERROR_INVALID_STATE); errorOt = otDatasetGetActiveTlvs(mInstance, &datasetTlvs); } else if (aDatasetType == DatasetType::kPending) { errorOt = otDatasetGetPendingTlvs(mInstance, &datasetTlvs); } // Create a new operational dataset if it doesn't exist. if (errorOt == OT_ERROR_NOT_FOUND) { VerifyOrExit(otDatasetCreateNewNetwork(mInstance, &dataset) == OT_ERROR_NONE, error = OTBR_ERROR_REST); otDatasetConvertToTlvs(&dataset, &datasetTlvs); errorCode = GetHttpStatus(HttpStatusCode::kStatusCreated); } isTlv = aRequest.GetHeaderValue(OT_REST_CONTENT_TYPE_HEADER) == OT_REST_CONTENT_TYPE_PLAIN; if (isTlv) { ret = Json::Hex2BytesJsonString(aRequest.GetBody(), datasetUpdateTlvs.mTlvs, OT_OPERATIONAL_DATASET_MAX_LENGTH); VerifyOrExit(ret >= 0, error = OTBR_ERROR_INVALID_ARGS); datasetUpdateTlvs.mLength = ret; VerifyOrExit(otDatasetParseTlvs(&datasetUpdateTlvs, &dataset) == OT_ERROR_NONE, error = OTBR_ERROR_REST); VerifyOrExit(otDatasetUpdateTlvs(&dataset, &datasetTlvs) == OT_ERROR_NONE, error = OTBR_ERROR_REST); } else { if (aDatasetType == DatasetType::kActive) { VerifyOrExit(Json::JsonActiveDatasetString2Dataset(aRequest.GetBody(), dataset), error = OTBR_ERROR_INVALID_ARGS); } else if (aDatasetType == DatasetType::kPending) { VerifyOrExit(Json::JsonPendingDatasetString2Dataset(aRequest.GetBody(), dataset), error = OTBR_ERROR_INVALID_ARGS); VerifyOrExit(dataset.mComponents.mIsDelayPresent, error = OTBR_ERROR_INVALID_ARGS); } VerifyOrExit(otDatasetUpdateTlvs(&dataset, &datasetTlvs) == OT_ERROR_NONE, error = OTBR_ERROR_REST); } if (aDatasetType == DatasetType::kActive) { VerifyOrExit(otDatasetSetActiveTlvs(mInstance, &datasetTlvs) == OT_ERROR_NONE, error = OTBR_ERROR_REST); } else if (aDatasetType == DatasetType::kPending) { VerifyOrExit(otDatasetSetPendingTlvs(mInstance, &datasetTlvs) == OT_ERROR_NONE, error = OTBR_ERROR_REST); } aResponse.SetResponsCode(errorCode); exit: if (error == OTBR_ERROR_INVALID_ARGS) { ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest); } else if (error == OTBR_ERROR_INVALID_STATE) { ErrorHandler(aResponse, HttpStatusCode::kStatusConflict); } else if (error != OTBR_ERROR_NONE) { ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); } } void Resource::Dataset(DatasetType aDatasetType, const Request &aRequest, Response &aResponse) const { std::string errorCode; switch (aRequest.GetMethod()) { case HttpMethod::kGet: GetDataset(aDatasetType, aRequest, aResponse); break; case HttpMethod::kPut: SetDataset(aDatasetType, aRequest, aResponse); break; case HttpMethod::kOptions: errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); aResponse.SetResponsCode(errorCode); aResponse.SetComplete(); break; default: ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); break; } } void Resource::DatasetActive(const Request &aRequest, Response &aResponse) const { Dataset(DatasetType::kActive, aRequest, aResponse); } void Resource::DatasetPending(const Request &aRequest, Response &aResponse) const { Dataset(DatasetType::kPending, aRequest, aResponse); } void Resource::DeleteOutDatedDiagnostic(void) { auto eraseIt = mDiagSet.begin(); for (eraseIt = mDiagSet.begin(); eraseIt != mDiagSet.end();) { auto diagInfo = eraseIt->second; auto duration = duration_cast(steady_clock::now() - diagInfo.mStartTime).count(); if (duration >= kDiagResetTimeout) { eraseIt = mDiagSet.erase(eraseIt); } else { eraseIt++; } } } void Resource::UpdateDiag(std::string aKey, std::vector &aDiag) { DiagInfo value; value.mStartTime = steady_clock::now(); value.mDiagContent.assign(aDiag.begin(), aDiag.end()); mDiagSet[aKey] = value; } void Resource::Diagnostic(const Request &aRequest, Response &aResponse) const { otbrError error = OTBR_ERROR_NONE; OT_UNUSED_VARIABLE(aRequest); struct otIp6Address rloc16address = *otThreadGetRloc(mInstance); struct otIp6Address multicastAddress; VerifyOrExit(otThreadSendDiagnosticGet(mInstance, &rloc16address, kAllTlvTypes, sizeof(kAllTlvTypes), &Resource::DiagnosticResponseHandler, const_cast(this)) == OT_ERROR_NONE, error = OTBR_ERROR_REST); VerifyOrExit(otIp6AddressFromString(kMulticastAddrAllRouters, &multicastAddress) == OT_ERROR_NONE, error = OTBR_ERROR_REST); VerifyOrExit(otThreadSendDiagnosticGet(mInstance, &multicastAddress, kAllTlvTypes, sizeof(kAllTlvTypes), &Resource::DiagnosticResponseHandler, const_cast(this)) == OT_ERROR_NONE, error = OTBR_ERROR_REST); exit: if (error == OTBR_ERROR_NONE) { aResponse.SetStartTime(steady_clock::now()); aResponse.SetCallback(); } else { ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); } } void Resource::DiagnosticResponseHandler(otError aError, otMessage *aMessage, const otMessageInfo *aMessageInfo, void *aContext) { static_cast(aContext)->DiagnosticResponseHandler(aError, aMessage, aMessageInfo); } void Resource::DiagnosticResponseHandler(otError aError, const otMessage *aMessage, const otMessageInfo *aMessageInfo) { std::vector diagSet; otNetworkDiagTlv diagTlv; otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT; otError error; char rloc[7]; std::string keyRloc = "0xffee"; SuccessOrExit(aError); OTBR_UNUSED_VARIABLE(aMessageInfo); while ((error = otThreadGetNextDiagnosticTlv(aMessage, &iterator, &diagTlv)) == OT_ERROR_NONE) { if (diagTlv.mType == OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS) { snprintf(rloc, sizeof(rloc), "0x%04x", diagTlv.mData.mAddr16); keyRloc = Json::CString2JsonString(rloc); } diagSet.push_back(diagTlv); } UpdateDiag(keyRloc, diagSet); exit: if (aError != OT_ERROR_NONE) { otbrLogWarning("Failed to get diagnostic data: %s", otThreadErrorToString(aError)); } } } // namespace rest } // namespace otbr