Skip to content

Commit

Permalink
[bdx] Extract functions for building and parsing URI and add unit tes…
Browse files Browse the repository at this point in the history
…ts (#12784)

* [bdx] Add utility for parsing and making URI

* Restyled by whitespace

Co-authored-by: Restyled.io <commits@restyled.io>
  • Loading branch information
2 people authored and pull[bot] committed Jan 25, 2022
1 parent 787309a commit 981fe5d
Show file tree
Hide file tree
Showing 11 changed files with 267 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <crypto/RandUtils.h>
#include <lib/core/CHIPTLV.h>
#include <lib/support/CHIPMemString.h>
#include <protocols/bdx/BdxUri.h>

#include <string.h>

Expand Down Expand Up @@ -63,23 +64,6 @@ void GenerateUpdateToken(uint8_t * buf, size_t bufSize)
}
}

bool GenerateBdxUri(NodeId nodeId, CharSpan fileDesignator, MutableCharSpan outUri)
{
static constexpr char bdxPrefix[] = "bdx://";
size_t nodeIdHexStrLen = sizeof(nodeId) * 2;
size_t expectedLength = strlen(bdxPrefix) + nodeIdHexStrLen + 1 + fileDesignator.size();

if (expectedLength >= outUri.size())
{
return false;
}

size_t written = static_cast<size_t>(snprintf(outUri.data(), outUri.size(), "%s" ChipLogFormatX64 "/%s", bdxPrefix,
ChipLogValueX64(nodeId), fileDesignator.data()));

return expectedLength == written;
}

OTAProviderExample::OTAProviderExample()
{
memset(mOTAFilePath, 0, kFilepathBufLen);
Expand Down Expand Up @@ -128,7 +112,7 @@ EmberAfStatus OTAProviderExample::HandleQueryImage(chip::app::CommandHandler * c

// Only doing BDX transport for now
MutableCharSpan uri(uriBuf, kUriMaxLen);
GenerateBdxUri(nodeId, CharSpan(mOTAFilePath, strlen(mOTAFilePath)), uri);
chip::bdx::MakeURI(nodeId, CharSpan(mOTAFilePath, strlen(mOTAFilePath)), uri);
ChipLogDetail(SoftwareUpdate, "Generated URI: %.*s", static_cast<int>(uri.size()), uri.data());
}

Expand Down
59 changes: 0 additions & 59 deletions src/app/clusters/ota-requestor/BDXDownloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,63 +217,4 @@ CHIP_ERROR BDXDownloader::HandleBdxEvent(const chip::bdx::TransferSession::Outpu
return CHIP_NO_ERROR;
}

// TODO: Add unit tests for parsing BDX URI
CHIP_ERROR BDXDownloader::ParseBdxUri(CharSpan uri, NodeId & nodeId, MutableCharSpan fileDesignator)
{
// Check against minimum length of a valid BDX URI
if (uri.size() < kValidBdxUriMinLen)
{
return CHIP_ERROR_INVALID_STRING_LENGTH;
}

uint8_t readValue[kUriMaxLen];
Encoding::LittleEndian::Reader uriReader(reinterpret_cast<const uint8_t *>(uri.data()), uri.size());

// Check the scheme field matches the BDX prefix
memset(readValue, 0, sizeof(readValue));
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, sizeof(bdxPrefix)).StatusCode());
ByteSpan expectedScheme(bdxPrefix, sizeof(bdxPrefix));
ByteSpan actualScheme(readValue, sizeof(bdxPrefix));
if (!expectedScheme.data_equal(actualScheme))
{
return CHIP_ERROR_INVALID_SCHEME_PREFIX;
}

// Extract the node ID from the authority field
memset(readValue, 0, sizeof(readValue));
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, kNodeIdHexStringLen).StatusCode());
uint8_t buffer[kNodeIdHexStringLen];
if (Encoding::HexToBytes(reinterpret_cast<const char *>(readValue), kNodeIdHexStringLen, buffer, kNodeIdHexStringLen) == 0)
{
return CHIP_ERROR_INVALID_DESTINATION_NODE_ID;
}
nodeId = Encoding::BigEndian::Get64(buffer);
if (!IsOperationalNodeId(nodeId))
{
return CHIP_ERROR_INVALID_DESTINATION_NODE_ID;
}

// Verify the separator between authority and path fields
memset(readValue, 0, sizeof(readValue));
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, sizeof(bdxSeparator)).StatusCode());
ByteSpan expectedSeparator(bdxSeparator, sizeof(bdxSeparator));
ByteSpan actualSeparator(readValue, sizeof(bdxSeparator));
if (!expectedSeparator.data_equal(actualSeparator))
{
return CHIP_ERROR_MISSING_URI_SEPARATOR;
}

// Extract file designator from the path field
size_t fileDesignatorLength = uriReader.Remaining();
memset(readValue, 0, sizeof(readValue));
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, fileDesignatorLength).StatusCode());
size_t written = static_cast<size_t>(snprintf(fileDesignator.data(), fileDesignator.size(), "%s", readValue));
if (written != fileDesignatorLength)
{
return CHIP_ERROR_INTERNAL;
}

return CHIP_NO_ERROR;
}

} // namespace chip
12 changes: 0 additions & 12 deletions src/app/clusters/ota-requestor/BDXDownloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,6 @@

namespace chip {

// Constants for BDX URI parsing
constexpr uint8_t bdxPrefix[] = { 'b', 'd', 'x', ':', '/', '/' };
constexpr uint8_t bdxSeparator[] = { '/' };
constexpr uint8_t kValidBdxUriMinLen = 24;
constexpr uint8_t kNodeIdHexStringLen = 16;
constexpr size_t kUriMaxLen = 256;

class BDXDownloader : public chip::OTADownloader
{
public:
Expand Down Expand Up @@ -72,11 +65,6 @@ class BDXDownloader : public chip::OTADownloader
CHIP_ERROR FetchNextData() override;
// TODO: override SkipData

/**
* Validate the URI and parse the BDX URI for various fields
*/
CHIP_ERROR ParseBdxUri(CharSpan uri, NodeId & nodeId, MutableCharSpan fileDesignator);

private:
void PollTransferSession();
CHIP_ERROR HandleBdxEvent(const chip::bdx::TransferSession::OutputEvent & outEvent);
Expand Down
8 changes: 4 additions & 4 deletions src/app/clusters/ota-requestor/OTARequestor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <app-common/zap-generated/attributes/Accessors.h>
#include <lib/core/CHIPEncoding.h>
#include <platform/CHIPDeviceLayer.h>
#include <protocols/bdx/BdxUri.h>
#include <zap-generated/CHIPClusters.h>

#include "BDXDownloader.h"
Expand Down Expand Up @@ -114,10 +115,9 @@ void OTARequestor::OnQueryImageResponse(void * context, const QueryImageResponse
VerifyOrReturn(response.imageURI.HasValue(), ChipLogError(SoftwareUpdate, "Update is available but no image URI present"));

// Parse out the provider node ID and file designator from the image URI
NodeId nodeId = kUndefinedNodeId;
char fileDesignatorBuffer[kUriMaxLen] = { 0 };
MutableCharSpan fileDesignator(fileDesignatorBuffer, kUriMaxLen);
CHIP_ERROR err = requestorCore->mBdxDownloader->ParseBdxUri(response.imageURI.Value(), nodeId, fileDesignator);
NodeId nodeId = kUndefinedNodeId;
CharSpan fileDesignator;
CHIP_ERROR err = bdx::ParseURI(response.imageURI.Value(), nodeId, fileDesignator);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(SoftwareUpdate, "Parse BDX image URI (%.*s) returned err=%" CHIP_ERROR_FORMAT,
static_cast<int>(response.imageURI.Value().size()), response.imageURI.Value().data(),
Expand Down
6 changes: 6 additions & 0 deletions src/lib/support/Span.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ class Span
return Span(mDataBuf + offset, length);
}

Span SubSpan(size_t offset) const
{
VerifyOrDie(offset <= mDataLen);
return Span(mDataBuf + offset, mDataLen - offset);
}

// Allow reducing the size of a span.
void reduce_size(size_t new_size)
{
Expand Down
28 changes: 26 additions & 2 deletions src/lib/support/tests/TestSpan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,12 +247,36 @@ static void TestSpanOfPointers(nlTestSuite * inSuite, void * inContext)
NL_TEST_ASSERT(inSuite, s3.data_equal(s5));
}

static void TestSubSpan(nlTestSuite * inSuite, void * inContext)
{
uint8_t array[16];
ByteSpan span(array);

NL_TEST_ASSERT(inSuite, span.data() == &array[0]);
NL_TEST_ASSERT(inSuite, span.size() == 16);

ByteSpan subspan = span.SubSpan(1, 14);
NL_TEST_ASSERT(inSuite, subspan.data() == &array[1]);
NL_TEST_ASSERT(inSuite, subspan.size() == 14);

subspan = span.SubSpan(1, 0);
NL_TEST_ASSERT(inSuite, subspan.size() == 0);

subspan = span.SubSpan(10);
NL_TEST_ASSERT(inSuite, subspan.data() == &array[10]);
NL_TEST_ASSERT(inSuite, subspan.size() == 6);

subspan = span.SubSpan(16);
NL_TEST_ASSERT(inSuite, subspan.size() == 0);
}

#define NL_TEST_DEF_FN(fn) NL_TEST_DEF("Test " #fn, fn)
/**
* Test Suite. It lists all the test functions.
*/
static const nlTest sTests[] = { NL_TEST_DEF_FN(TestByteSpan), NL_TEST_DEF_FN(TestMutableByteSpan),
NL_TEST_DEF_FN(TestFixedByteSpan), NL_TEST_DEF_FN(TestSpanOfPointers), NL_TEST_SENTINEL() };
static const nlTest sTests[] = { NL_TEST_DEF_FN(TestByteSpan), NL_TEST_DEF_FN(TestMutableByteSpan),
NL_TEST_DEF_FN(TestFixedByteSpan), NL_TEST_DEF_FN(TestSpanOfPointers),
NL_TEST_DEF_FN(TestSubSpan), NL_TEST_SENTINEL() };

int TestSpan(void)
{
Expand Down
2 changes: 2 additions & 0 deletions src/protocols/bdx/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ static_library("bdx") {
"BdxMessages.h",
"BdxTransferSession.cpp",
"BdxTransferSession.h",
"BdxUri.cpp",
"BdxUri.h",
"TransferFacilitator.cpp",
"TransferFacilitator.h",
]
Expand Down
83 changes: 83 additions & 0 deletions src/protocols/bdx/BdxUri.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "BdxUri.h"

#include <lib/core/CHIPEncoding.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/support/BufferWriter.h>
#include <lib/support/BytesToHex.h>
#include <lib/support/CodeUtils.h>

#include <cstring>

namespace chip {
namespace bdx {

constexpr size_t kSchemeLen = sizeof(kScheme) - 1;
constexpr size_t kHostPos = kSchemeLen;
constexpr size_t kHostLen = sizeof(NodeId) * 2;
constexpr size_t kHostEnd = kHostPos + kHostLen;
constexpr size_t kSeparatorLen = 1;
constexpr size_t kMinUriLen = kHostEnd + kSeparatorLen + 1 /* file-designator */;

static_assert(sizeof(NodeId) == sizeof(uint64_t), "The code below assumes NodeId is uint64_t");

CHIP_ERROR ParseURI(CharSpan uri, NodeId & nodeId, CharSpan & fileDesignator)
{
VerifyOrReturnError(uri.size() >= kMinUriLen, CHIP_ERROR_INVALID_STRING_LENGTH);
VerifyOrReturnError(memcmp(uri.data(), kScheme, kSchemeLen) == 0, CHIP_ERROR_INVALID_SCHEME_PREFIX);

uint8_t nodeIdBytes[sizeof(NodeId)];
VerifyOrReturnError(Encoding::HexToBytes(uri.data() + kHostPos, kHostLen, nodeIdBytes, sizeof(nodeIdBytes)) ==
sizeof(nodeIdBytes),
CHIP_ERROR_INVALID_DESTINATION_NODE_ID);

nodeId = Encoding::BigEndian::Get64(nodeIdBytes);
VerifyOrReturnError(IsOperationalNodeId(nodeId), CHIP_ERROR_INVALID_DESTINATION_NODE_ID);
VerifyOrReturnError(uri.data()[kHostEnd] == '/', CHIP_ERROR_MISSING_URI_SEPARATOR);

fileDesignator = uri.SubSpan(kHostEnd + kSeparatorLen);

return CHIP_NO_ERROR;
}

CHIP_ERROR MakeURI(NodeId nodeId, CharSpan fileDesignator, MutableCharSpan & uri)
{
VerifyOrReturnError(fileDesignator.size() > 0, CHIP_ERROR_INVALID_STRING_LENGTH);

uint8_t nodeIdBytes[sizeof(NodeId)];
Encoding::BigEndian::Put64(nodeIdBytes, nodeId);

char nodeIdHex[sizeof(NodeId) * 2];
ReturnErrorOnFailure(Encoding::BytesToUppercaseHexBuffer(nodeIdBytes, sizeof(nodeIdBytes), nodeIdHex, sizeof(nodeIdHex)));

Encoding::BufferWriter writer(Uint8::from_char(uri.data()), uri.size());
writer.Put(kScheme, kSchemeLen);
writer.Put(nodeIdHex, sizeof(nodeIdHex));
writer.Put("/");
writer.Put(fileDesignator.data(), fileDesignator.size());

VerifyOrReturnError(writer.Fit(), CHIP_ERROR_BUFFER_TOO_SMALL);
uri.reduce_size(writer.WritePos());

return CHIP_NO_ERROR;
}

} // namespace bdx
} // namespace chip
31 changes: 31 additions & 0 deletions src/protocols/bdx/BdxUri.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <lib/core/NodeId.h>
#include <lib/support/Span.h>

namespace chip {
namespace bdx {

constexpr const char kScheme[] = "bdx://";

CHIP_ERROR ParseURI(CharSpan uri, NodeId & nodeId, CharSpan & fileDesignator);
CHIP_ERROR MakeURI(NodeId nodeId, CharSpan fileDesignator, MutableCharSpan & uri);

} // namespace bdx
} // namespace chip
1 change: 1 addition & 0 deletions src/protocols/bdx/tests/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ chip_test_suite("tests") {
test_sources = [
"TestBdxMessages.cpp",
"TestBdxTransferSession.cpp",
"TestBdxUri.cpp",
]

public_deps = [
Expand Down
Loading

0 comments on commit 981fe5d

Please sign in to comment.