[mac] new public API to validate and set otNetworkName (#6703)

This commit adds a new public API `otNetworkNameFromString()` to set
an `otNetworkName` instance from a given null terminated C string. The
new API will also validate that the given name string follows UTF-8
encoding and its length is not longer than max network name size.

This commit also simplifies the UTF-8 validation of `NetworkName` by
moving the check from `Mac::SetNetworkName()` to a newly added method
in `Mac::NetworkName` to set it from a C string.

It also defines `DomainName` as a `typedef` of `NetworkName` which
helps simplify its use and remove repeated code.

Finally, it updates the `Cli::Dataset` to use the new public API for
validating `otNetworkName`.
This commit is contained in:
Abtin Keshavarzian
2021-06-03 22:13:51 -07:00
committed by GitHub
parent d6ff3e5cc0
commit f3671d5a75
9 changed files with 96 additions and 145 deletions
+18 -1
View File
@@ -75,10 +75,12 @@ typedef struct otMasterKey otMasterKey;
/**
* This structure represents a Network Name.
*
* The `otNetworkName` is a null terminated C string (i.e., `m8` char array MUST end with null char `\0`).
*
*/
typedef struct otNetworkName
{
char m8[OT_NETWORK_NAME_MAX_SIZE + 1]; ///< Byte values
char m8[OT_NETWORK_NAME_MAX_SIZE + 1]; ///< Byte values. The `+ 1` is for null char.
} otNetworkName;
#define OT_EXT_PAN_ID_SIZE 8 ///< Size of a Thread PAN ID (bytes)
@@ -515,6 +517,21 @@ otError otDatasetGeneratePskc(const char * aPassPhrase,
const otExtendedPanId *aExtPanId,
otPskc * aPskc);
/**
* This function sets an `otNetworkName` instance from a given null terminated C string.
*
* This function also validates that the given @p aNameString follows UTF-8 encoding and its length is not longer than
* `OT_NETWORK_NAME_MAX_SIZE`.
*
* @param[out] aNetworkName A pointer to the `otNetworkName` to set.
* @param[in] aNameString A name C string.
*
* @retval OT_ERROR_NONE Successfully set @p aNetworkName from @p aNameString.
* @retval OT_ERROR_INVALID_ARGS @p aNameStrng is invalid (too long or does not follow UTF-8 encoding).
*
*/
otError otNetworkNameFromString(otNetworkName *aNetworkName, const char *aNameString);
/**
* @}
*
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (121)
#define OPENTHREAD_API_VERSION (122)
/**
* @addtogroup api-instance
+8 -20
View File
@@ -41,7 +41,6 @@
#include <openthread/dataset_updater.h>
#include "cli/cli.hpp"
#include "common/string.hpp"
namespace ot {
namespace Cli {
@@ -100,7 +99,7 @@ otError Dataset::Print(otOperationalDataset &aDataset)
if (aDataset.mComponents.mIsNetworkNamePresent)
{
mInterpreter.OutputFormat("Network Name: ");
mInterpreter.OutputLine("%.*s", static_cast<uint16_t>(sizeof(aDataset.mNetworkName)), aDataset.mNetworkName.m8);
mInterpreter.OutputLine("%s", aDataset.mNetworkName.m8);
}
if (aDataset.mComponents.mIsPanIdPresent)
@@ -441,19 +440,12 @@ otError Dataset::ProcessNetworkName(uint8_t aArgsLength, Arg aArgs[])
{
if (sDataset.mComponents.mIsNetworkNamePresent)
{
mInterpreter.OutputLine("%.*s", static_cast<uint16_t>(sizeof(sDataset.mNetworkName)),
sDataset.mNetworkName.m8);
mInterpreter.OutputLine("%s", sDataset.mNetworkName.m8);
}
}
else
{
uint16_t length;
VerifyOrExit((length = aArgs[0].GetLength()) <= OT_NETWORK_NAME_MAX_SIZE, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(IsValidUtf8String(aArgs[0].GetCString()), error = OT_ERROR_INVALID_ARGS);
memset(&sDataset.mNetworkName, 0, sizeof(sDataset.mNetworkName));
memcpy(sDataset.mNetworkName.m8, aArgs[0].GetCString(), length);
SuccessOrExit(error = otNetworkNameFromString(&sDataset.mNetworkName, aArgs[0].GetCString()));
sDataset.mComponents.mIsNetworkNamePresent = true;
}
@@ -536,14 +528,9 @@ otError Dataset::ProcessMgmtSetCommand(uint8_t aArgsLength, Arg aArgs[])
}
else if (aArgs[index] == "networkname")
{
uint16_t length;
VerifyOrExit(++index < aArgsLength, error = OT_ERROR_INVALID_ARGS);
dataset.mComponents.mIsNetworkNamePresent = true;
VerifyOrExit((length = aArgs[index].GetLength()) <= OT_NETWORK_NAME_MAX_SIZE,
error = OT_ERROR_INVALID_ARGS);
memset(&dataset.mNetworkName, 0, sizeof(sDataset.mNetworkName));
memcpy(dataset.mNetworkName.m8, aArgs[index].GetCString(), length);
SuccessOrExit(error = otNetworkNameFromString(&dataset.mNetworkName, aArgs[index].GetCString()));
}
else if (aArgs[index] == "extpanid")
{
@@ -809,9 +796,9 @@ void Dataset::OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy)
}
}
Error Dataset::ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, uint8_t aArgsLength, Arg aArgs[])
otError Dataset::ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, uint8_t aArgsLength, Arg aArgs[])
{
Error error;
otError error;
otSecurityPolicy policy;
memset(&policy, 0, sizeof(policy));
@@ -865,10 +852,11 @@ Error Dataset::ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, uint8_t aA
}
exit:
if (error == kErrorNone)
if (error == OT_ERROR_NONE)
{
aSecurityPolicy = policy;
}
return error;
}
+2 -2
View File
@@ -108,8 +108,8 @@ private:
void HandleDatasetUpdater(otError aError);
#endif
void OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy);
Error ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, uint8_t aArgsLength, Arg aArgs[]);
void OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy);
otError ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, uint8_t aArgsLength, Arg aArgs[]);
static constexpr Command sCommands[] = {
{"active", &Dataset::ProcessActive},
+7
View File
@@ -177,3 +177,10 @@ otError otDatasetGeneratePskc(const char * aPassPhrase,
*static_cast<const Mac::ExtendedPanId *>(aExtPanId), *static_cast<Pskc *>(aPskc));
}
#endif // OPENTHREAD_FTD
otError otNetworkNameFromString(otNetworkName *aNetworkName, const char *aNameString)
{
otError error = static_cast<Mac::NetworkName *>(aNetworkName)->Set(aNameString);
return (error == OT_ERROR_ALREADY) ? OT_ERROR_NONE : error;
}
+20 -48
View File
@@ -475,75 +475,47 @@ void Mac::SetSupportedChannelMask(const ChannelMask &aMask)
Error Mac::SetNetworkName(const char *aNameString)
{
// When setting Network Name from a string, we treat it as `NameData`
// with `kMaxSize + 1` chars. `NetworkName::Set(data)` will look
// for null char in the data (within its given size) to calculate
// the name's length and ensure that the name fits in `kMaxSize`
// chars. The `+ 1` ensures that a `aNameString` with length
// longer than `kMaxSize` is correctly rejected (returning error
// `kErrorInvalidArgs`).
Error error;
NameData data(aNameString, NetworkName::kMaxSize + 1);
VerifyOrExit(IsValidUtf8String(aNameString), error = kErrorInvalidArgs);
error = SetNetworkName(data);
exit:
return error;
return SignalNetworkNameChange(mNetworkName.Set(aNameString));
}
Error Mac::SetNetworkName(const NameData &aNameData)
{
Error error = mNetworkName.Set(aNameData);
return SignalNetworkNameChange(mNetworkName.Set(aNameData));
}
if (error == kErrorAlready)
Error Mac::SignalNetworkNameChange(Error aError)
{
switch (aError)
{
case kErrorNone:
Get<Notifier>().Signal(kEventThreadNetworkNameChanged);
break;
case kErrorAlready:
Get<Notifier>().SignalIfFirst(kEventThreadNetworkNameChanged);
error = kErrorNone;
ExitNow();
aError = kErrorNone;
break;
default:
break;
}
SuccessOrExit(error);
Get<Notifier>().Signal(kEventThreadNetworkNameChanged);
exit:
return error;
return aError;
}
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
Error Mac::SetDomainName(const char *aNameString)
{
// When setting Domain Name from a string, we treat it as `NameData`
// with `kMaxSize + 1` chars. `DomainName::Set(data)` will look
// for null char in the data (within its given size) to calculate
// the name's length and ensure that the name fits in `kMaxSize`
// chars. The `+ 1` ensures that a `aNameString` with length
// longer than `kMaxSize` is correctly rejected (returning error
// `kErrorInvalidArgs`).
Error error = mDomainName.Set(aNameString);
Error error;
NameData data(aNameString, DomainName::kMaxSize + 1);
VerifyOrExit(IsValidUtf8String(aNameString), error = kErrorInvalidArgs);
error = SetDomainName(data);
exit:
return error;
return (error == kErrorAlready) ? kErrorNone : error;
}
Error Mac::SetDomainName(const NameData &aNameData)
{
Error error = mDomainName.Set(aNameData);
if (error == kErrorAlready)
{
error = kErrorNone;
}
return error;
return (error == kErrorAlready) ? kErrorNone : error;
}
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+1
View File
@@ -807,6 +807,7 @@ private:
Error ConvertBeaconToActiveScanResult(const RxFrame *aBeaconFrame, ActiveScanResult &aResult);
void PerformEnergyScan(void);
void ReportEnergyScanResult(int8_t aRssi);
Error SignalNetworkNameChange(Error aError);
void LogFrameRxFailure(const RxFrame *aFrame, Error aError) const;
void LogFrameTxFailure(const TxFrame &aFrame, Error aError, uint8_t aRetryCount, bool aWillRetx) const;
+21 -26
View File
@@ -142,6 +142,27 @@ NameData NetworkName::GetAsData(void) const
return NameData(m8, len);
}
Error NetworkName::Set(const char *aNameString)
{
// When setting `NetworkName` from a string, we treat it as `NameData`
// with `kMaxSize + 1` chars. `NetworkName::Set(data)` will look
// for null char in the data (within its given size) to calculate
// the name's length and ensure that the name fits in `kMaxSize`
// chars. The `+ 1` ensures that a `aNameString` with length
// longer than `kMaxSize` is correctly rejected (returning error
// `kErrorInvalidArgs`).
Error error;
NameData data(aNameString, kMaxSize + 1);
VerifyOrExit(IsValidUtf8String(aNameString), error = kErrorInvalidArgs);
error = Set(data);
exit:
return error;
}
Error NetworkName::Set(const NameData &aNameData)
{
Error error = kErrorNone;
@@ -168,32 +189,6 @@ bool NetworkName::operator==(const NetworkName &aOther) const
(memcmp(data.GetBuffer(), otherData.GetBuffer(), data.GetLength()) == 0);
}
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
NameData DomainName::GetAsData(void) const
{
uint8_t len = static_cast<uint8_t>(StringLength(m8, kMaxSize + 1));
return NameData(m8, len);
}
Error DomainName::Set(const NameData &aNameData)
{
Error error = kErrorNone;
uint8_t newLen = static_cast<uint8_t>(StringLength(aNameData.GetBuffer(), aNameData.GetLength()));
VerifyOrExit(newLen <= kMaxSize, error = kErrorInvalidArgs);
// Ensure the new name does not match the current one.
VerifyOrExit(memcmp(m8, aNameData.GetBuffer(), newLen) || (m8[newLen] != '\0'), error = kErrorAlready);
memcpy(m8, aNameData.GetBuffer(), newLen);
m8[newLen] = '\0';
exit:
return error;
}
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#if OPENTHREAD_CONFIG_MULTI_RADIO
const RadioType RadioTypes::kAllRadioTypes[kNumRadioTypes] = {
+18 -47
View File
@@ -559,6 +559,21 @@ public:
*/
NameData GetAsData(void) const;
/**
* This method sets the IEEE 802.15.4 Network Name from a given null terminated C string.
*
* This method also validates that the given @p aNameString follows UTF-8 encoding and can fit in `kMaxSize`
* chars.
*
* @param[in] aNameString A name C string.
*
* @retval kErrorNone Successfully set the IEEE 802.15.4 Network Name.
* @retval kErrorAlready The name is already set to the same string.
* @retval kErrorInvalidArgs Given name is invalid (too long or does not follow UTF-8 encoding).
*
*/
Error Set(const char *aNameString);
/**
* This method sets the IEEE 802.15.4 Network Name.
*
@@ -585,55 +600,11 @@ public:
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
/**
* This structure represents a Thread Domain Name.
* This type represents a Thread Domain Name.
*
*/
class DomainName
{
public:
enum
{
kMaxSize = 16, // Maximum number of chars in Domain Name (excludes null char).
};
/**
* This constructor initializes the Thread Domain Name as an empty string.
*
*/
DomainName(void) { m8[0] = '\0'; }
/**
* This method gets the Thread Domain Name as a null terminated C string.
*
* @returns The Domain Name as a null terminated C string array.
*
*/
const char *GetAsCString(void) const { return m8; }
/**
* This method gets the Thread Domain Name as NameData.
*
* @returns The Domain Name as NameData.
*
*/
NameData GetAsData(void) const;
/**
* This method sets the Thread Domain Name.
*
* @param[in] aNameData A reference to name data.
*
* @retval kErrorNone Successfully set the Thread Domain Name.
* @retval kErrorAlready The name is already set to the same string.
* @retval kErrorInvalidArgs Given name is too long.
*
*/
Error Set(const NameData &aNameData);
private:
char m8[kMaxSize + 1]; ///< Byte values.
};
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
typedef NetworkName DomainName;
#endif
#if OPENTHREAD_CONFIG_MULTI_RADIO