Skip to content

Commit

Permalink
[dns] add Name::StripName() and support in-place ExtractLabels() (o…
Browse files Browse the repository at this point in the history
…penthread#10037)

- Updates `Dns::Name::ExtractLabels()` for in-place label extraction,
  optimizing string operations.
- Introduces `Name::StripName()` to efficiently remove a suffix name
  from a DNS name in place.
- Updates and simplifies unit tests `test_dns` to validate new
  functionality.
  • Loading branch information
abtink committed Apr 17, 2024
1 parent b6a1dc8 commit be7d36e
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 74 deletions.
6 changes: 5 additions & 1 deletion src/core/net/dns_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,11 @@ Error Name::ExtractLabels(const char *aName, const char *aSuffixName, char *aLab
nameLength -= (suffixLength + 1);
VerifyOrExit(nameLength < aLabelsSize, error = kErrorNoBufs);

memcpy(aLabels, aName, nameLength);
if (aLabels != aName)
{
memmove(aLabels, aName, nameLength);
}

aLabels[nameLength] = kNullChar;
error = kErrorNone;

Expand Down
28 changes: 28 additions & 0 deletions src/core/net/dns_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,9 @@ class Name : public Clearable<Name>
* Both @p aName and @p aSuffixName MUST follow the same style regarding inclusion of trailing dot ('.'). Otherwise
* `kErrorParse` is returned.
*
* The @p aLabels buffer may be the same as @p aName for in-place label extraction. In this case, the
* implementation avoids unnecessary character copies.
*
* @param[in] aName The name to extract labels from.
* @param[in] aSuffixName The suffix name (e.g., can be domain name).
* @param[out] aLabels Pointer to buffer to copy the extracted labels.
Expand All @@ -1047,6 +1050,9 @@ class Name : public Clearable<Name>
* Both @p aName and @p aSuffixName MUST follow the same style regarding inclusion of trailing dot ('.'). Otherwise
* `kErrorParse` is returned.
*
* The @p aLabels buffer may be the same as @p aName for in-place label extraction. In this case, the
* implementation avoids unnecessary character copies.
*
* @tparam kLabelsBufferSize Size of the buffer string.
*
* @param[in] aName The name to extract labels from.
Expand All @@ -1064,6 +1070,28 @@ class Name : public Clearable<Name>
return ExtractLabels(aName, aSuffixName, aLabels, kLabelsBufferSize);
}

/**
* Strips a given suffix name (e.g., a domain name) from a given DNS name string, updating it in place.
*
* First checks that @p Name ends with the given @p aSuffixName, otherwise `kErrorParse` is returned.
*
* Both @p aName and @p aSuffixName MUST follow the same style regarding inclusion of trailing dot ('.'). Otherwise
* `kErrorParse` is returned.
*
* @tparam kNameBufferSize The size of name buffer.
*
* @param[in] aName The name buffer to strip the @p aSuffixName from.
* @param[in] aSuffixName The suffix name (e.g., can be domain name).
*
* @retval kErrorNone Successfully stripped the suffix name from @p aName.
* @retval kErrorParse @p aName does not contain @p aSuffixName.
*
*/
template <uint16_t kNameBufferSize> static Error StripName(char (&aName)[kNameBufferSize], const char *aSuffixName)
{
return ExtractLabels(aName, aSuffixName, aName, kNameBufferSize);
}

/**
* Tests if a DNS name is a sub-domain of a given domain.
*
Expand Down
144 changes: 71 additions & 73 deletions tests/unit/test_dns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,79 +276,77 @@ void TestDnsName(void)
printf("----------------------------------------------------------------\n");
printf("Extracting label(s) and removing domains:\n");

fullName = "my-service._ipps._tcp.default.service.arpa.";
suffixName = "default.service.arpa.";
SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name));
VerifyOrQuit(strcmp(name, "my-service._ipps._tcp") == 0);

fullName = "my-service._ipps._tcp.default.service.arpa";
suffixName = "default.service.arpa";
SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name));
VerifyOrQuit(strcmp(name, "my-service._ipps._tcp") == 0);

fullName = "my-service._ipps._tcp.default.service.arpa";
suffixName = "default.service.arpa.";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);

fullName = "my-service._ipps._tcp.default.service.arpa.";
suffixName = "default.service.arpa";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);

fullName = "my.service._ipps._tcp.default.service.arpa.";
suffixName = "_ipps._tcp.default.service.arpa.";
SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name));
VerifyOrQuit(strcmp(name, "my.service") == 0);

fullName = "my-service._ipps._tcp.default.service.arpa.";
suffixName = "DeFault.SerVice.ARPA.";
SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name));
VerifyOrQuit(strcmp(name, "my-service._ipps._tcp") == 0);

fullName = "my-service._ipps._tcp.default.service.arpa";
suffixName = "DeFault.SerVice.ARPA";
SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name));
VerifyOrQuit(strcmp(name, "my-service._ipps._tcp") == 0);

fullName = "my-service._ipps._tcp.default.service.arpa.";
suffixName = "efault.service.arpa.";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);

fullName = "my-service._ipps._tcp.default.service.arpa";
suffixName = "efault.service.arpa";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);

fullName = "my-service._ipps._tcp.default.service.arpa.";
suffixName = "xdefault.service.arpa.";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);

fullName = "my-service._ipps._tcp.default.service.arpa.";
suffixName = ".default.service.arpa.";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);

fullName = "my-service._ipps._tcp.default.service.arpa.";
suffixName = "default.service.arp.";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);

fullName = "default.service.arpa.";
suffixName = "default.service.arpa.";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);

fullName = "default.service.arpa";
suffixName = "default.service.arpa";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);

fullName = "efault.service.arpa.";
suffixName = "default.service.arpa.";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);

fullName = "my-service._ipps._tcp.default.service.arpa.";
suffixName = "default.service.arpa.";
SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, 22));
VerifyOrQuit(strcmp(name, "my-service._ipps._tcp") == 0);

fullName = "my-service._ipps._tcp.default.service.arpa.";
suffixName = "default.service.arpa.";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, 21) == kErrorNoBufs);
{
struct TestCase
{
const char *mFullName;
const char *mSuffixName;
const char *mLabels;
};

static const TestCase kTestCases[] = {
{"my-service._ipps._tcp.default.service.arpa.", "default.service.arpa.", "my-service._ipps._tcp"},
{"my-service._ipps._tcp.default.service.arpa", "default.service.arpa", "my-service._ipps._tcp"},
{"my.service._ipps._tcp.default.service.arpa.", "_ipps._tcp.default.service.arpa.", "my.service"},
{"my-service._ipps._tcp.default.service.arpa.", "DeFault.SerVice.ARPA.", "my-service._ipps._tcp"},
{"my-service._ipps._tcp.default.service.arpa", "DeFault.SerVice.ARPA", "my-service._ipps._tcp"},

{"my-service._ipps._tcp.default.service.arpa", "default.service.arpa.", nullptr},
{"my-service._ipps._tcp.default.service.arpa.", "default.service.arpa", nullptr},
{"my-service._ipps._tcp.default.service.arpa.", "efault.service.arpa.", nullptr},
{"my-service._ipps._tcp.default.service.arpa", "efault.service.arpa", nullptr},
{"my-service._ipps._tcp.default.service.arpa.", "xdefault.service.arpa.", nullptr},
{"my-service._ipps._tcp.default.service.arpa.", ".default.service.arpa.", nullptr},
{"my-service._ipps._tcp.default.service.arpa.", "default.service.arp.", nullptr},
{"default.service.arpa.", "default.service.arpa.", nullptr},
{"default.service.arpa", "default.service.arpa", nullptr},
{"efault.service.arpa.", "default.service.arpa.", nullptr},
};

for (const TestCase &testCase : kTestCases)
{
Error error;

printf("\n");
printf(" FullName : %s\n", testCase.mFullName);
printf(" SuffixName : %s\n", testCase.mSuffixName);
printf(" Extracted labels: %s\n", (testCase.mLabels != nullptr) ? testCase.mLabels : "(parse)");

error = Dns::Name::ExtractLabels(testCase.mFullName, testCase.mSuffixName, name);

if (testCase.mLabels != nullptr)
{
SuccessOrQuit(error);
VerifyOrQuit(strcmp(name, testCase.mLabels) == 0);
}
else
{
VerifyOrQuit(error == kErrorParse);
}

strcpy(name, testCase.mFullName);
error = Dns::Name::StripName(name, testCase.mSuffixName);

if (testCase.mLabels != nullptr)
{
SuccessOrQuit(error);
VerifyOrQuit(strcmp(name, testCase.mLabels) == 0);
}
else
{
VerifyOrQuit(error == kErrorParse);
}
}

fullName = "my-service._ipps._tcp.default.service.arpa.";
suffixName = "default.service.arpa.";
SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, 22));
VerifyOrQuit(strcmp(name, "my-service._ipps._tcp") == 0);

fullName = "my-service._ipps._tcp.default.service.arpa.";
suffixName = "default.service.arpa.";
VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, 21) == kErrorNoBufs);
}

printf("----------------------------------------------------------------\n");
printf("Append names, check encoded bytes, parse name and read labels:\n");
Expand Down

0 comments on commit be7d36e

Please sign in to comment.