mirror of
https://github.com/espressif/openthread.git
synced 2026-09-18 15:10:12 +00:00
[dns] match DNS name with specific domain (#6114)
This commit adds: - a new method Dns::Name::IsSubDomainOf that matches a DNS name with given domain. This is useful for SRP and DNS-SD server to verify if a RR is in our authorized domain. - domain checks for names in a SRP update to the SRP server.
This commit is contained in:
@@ -539,6 +539,37 @@ bool Name::LabelIterator::CompareLabel(const LabelIterator &aOtherIterator) cons
|
||||
mLabelLength);
|
||||
}
|
||||
|
||||
bool Name::IsSubDomainOf(const char *aName, const char *aDomain)
|
||||
{
|
||||
bool match = false;
|
||||
uint16_t nameLength = StringLength(aName, kMaxLength);
|
||||
uint16_t domainLength = StringLength(aDomain, kMaxLength);
|
||||
|
||||
if (nameLength > 0 && aName[nameLength - 1] == kLabelSeperatorChar)
|
||||
{
|
||||
--nameLength;
|
||||
}
|
||||
|
||||
if (domainLength > 0 && aDomain[domainLength - 1] == kLabelSeperatorChar)
|
||||
{
|
||||
--domainLength;
|
||||
}
|
||||
|
||||
VerifyOrExit(nameLength >= domainLength);
|
||||
aName += nameLength - domainLength;
|
||||
|
||||
if (nameLength > domainLength)
|
||||
{
|
||||
VerifyOrExit(aName[-1] == kLabelSeperatorChar);
|
||||
}
|
||||
VerifyOrExit(memcmp(aName, aDomain, domainLength) == 0);
|
||||
|
||||
match = true;
|
||||
|
||||
exit:
|
||||
return match;
|
||||
}
|
||||
|
||||
otError ResourceRecord::ParseRecords(const Message &aMessage, uint16_t &aOffset, uint16_t aNumRecords)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
@@ -890,6 +890,19 @@ public:
|
||||
*/
|
||||
static otError CompareName(const Message &aMessage, uint16_t &aOffset, const Name &aName);
|
||||
|
||||
/**
|
||||
* This static method tests if a DNS name is a sub-domain of a given domain.
|
||||
*
|
||||
* Both @p aName and @p aDomain can end without dot ('.').
|
||||
*
|
||||
* @param[in] aName The dot-separated name.
|
||||
* @param[in] aDomain The dot-separated domain.
|
||||
*
|
||||
* @returns TRUE if the name is a sub-domain of @p aDomain, FALSE if is not.
|
||||
*
|
||||
*/
|
||||
static bool IsSubDomainOf(const char *aName, const char *aDomain);
|
||||
|
||||
private:
|
||||
enum : char
|
||||
{
|
||||
|
||||
+28
-14
@@ -223,11 +223,11 @@ void Server::RemoveAndFreeHost(Host *aHost)
|
||||
aHost->Free();
|
||||
}
|
||||
|
||||
Server::Service *Server::FindService(const char *aFullName)
|
||||
const Server::Service *Server::FindService(const char *aFullName) const
|
||||
{
|
||||
Service *service = nullptr;
|
||||
const Service *service = nullptr;
|
||||
|
||||
for (Host *host = mHosts.GetHead(); host != nullptr; host = host->GetNext())
|
||||
for (const Host *host = mHosts.GetHead(); host != nullptr; host = host->GetNext())
|
||||
{
|
||||
service = host->FindService(aFullName);
|
||||
if (service != nullptr)
|
||||
@@ -239,11 +239,11 @@ Server::Service *Server::FindService(const char *aFullName)
|
||||
return service;
|
||||
}
|
||||
|
||||
bool Server::HasNameConflictsWith(Host &aHost)
|
||||
bool Server::HasNameConflictsWith(Host &aHost) const
|
||||
{
|
||||
bool hasConflicts = false;
|
||||
const Service *service = nullptr;
|
||||
Host * existingHost = mHosts.FindMatching(aHost.GetFullName());
|
||||
const Host * existingHost = mHosts.FindMatching(aHost.GetFullName());
|
||||
|
||||
if (existingHost != nullptr && *aHost.GetKey() != *existingHost->GetKey())
|
||||
{
|
||||
@@ -253,7 +253,7 @@ bool Server::HasNameConflictsWith(Host &aHost)
|
||||
// Check not only services of this host but all hosts.
|
||||
while ((service = aHost.GetNextService(service)) != nullptr)
|
||||
{
|
||||
Service *existingService = FindService(service->mFullName);
|
||||
const Service *existingService = FindService(service->mFullName);
|
||||
if (existingService != nullptr && *service->GetHost().GetKey() != *existingService->GetHost().GetKey())
|
||||
{
|
||||
ExitNow(hasConflicts = true);
|
||||
@@ -569,14 +569,17 @@ exit:
|
||||
otError Server::ProcessZoneSection(const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
uint16_t & aOffset,
|
||||
Dns::Zone & aZone)
|
||||
Dns::Zone & aZone) const
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
char name[Dns::Name::kMaxLength + 1];
|
||||
Dns::Zone zone;
|
||||
|
||||
VerifyOrExit(aDnsHeader.GetZoneRecordCount() == 1, error = OT_ERROR_PARSE);
|
||||
|
||||
SuccessOrExit(error = Dns::Name::ParseName(aMessage, aOffset));
|
||||
SuccessOrExit(error = Dns::Name::ReadName(aMessage, aOffset, name, sizeof(name)));
|
||||
// TODO: return `Dns::kResponseNotAuth` for not authorized zone names.
|
||||
VerifyOrExit(strcmp(name, GetDomain()) == 0, error = OT_ERROR_SECURITY);
|
||||
SuccessOrExit(error = aMessage.Read(aOffset, zone));
|
||||
aOffset += sizeof(zone);
|
||||
|
||||
@@ -591,7 +594,7 @@ otError Server::ProcessUpdateSection(Host & aHost,
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t & aOffset)
|
||||
uint16_t & aOffset) const
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
@@ -623,7 +626,7 @@ otError Server::ProcessHostDescriptionInstruction(Host & aHost
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t aOffset)
|
||||
uint16_t aOffset) const
|
||||
{
|
||||
otError error;
|
||||
|
||||
@@ -635,6 +638,8 @@ otError Server::ProcessHostDescriptionInstruction(Host & aHost
|
||||
Dns::ResourceRecord record;
|
||||
|
||||
SuccessOrExit(error = Dns::Name::ReadName(aMessage, aOffset, name, sizeof(name)));
|
||||
// TODO: return `Dns::kResponseNotZone` for names not in the zone.
|
||||
VerifyOrExit(Dns::Name::IsSubDomainOf(name, GetDomain()), error = OT_ERROR_SECURITY);
|
||||
SuccessOrExit(error = aMessage.Read(aOffset, record));
|
||||
|
||||
if (record.GetClass() == Dns::ResourceRecord::kClassAny)
|
||||
@@ -728,7 +733,7 @@ otError Server::ProcessServiceDiscoveryInstructions(Host & aHo
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t aOffset)
|
||||
uint16_t aOffset) const
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
@@ -740,6 +745,7 @@ otError Server::ProcessServiceDiscoveryInstructions(Host & aHo
|
||||
Service * service;
|
||||
|
||||
SuccessOrExit(error = Dns::Name::ReadName(aMessage, aOffset, name, sizeof(name)));
|
||||
VerifyOrExit(Dns::Name::IsSubDomainOf(name, GetDomain()), error = OT_ERROR_SECURITY);
|
||||
SuccessOrExit(error = aMessage.Read(aOffset, record));
|
||||
|
||||
aOffset += sizeof(record);
|
||||
@@ -747,6 +753,7 @@ otError Server::ProcessServiceDiscoveryInstructions(Host & aHo
|
||||
if (record.GetType() == Dns::ResourceRecord::kTypePtr)
|
||||
{
|
||||
SuccessOrExit(error = Dns::Name::ReadName(aMessage, aOffset, serviceName, sizeof(serviceName)));
|
||||
VerifyOrExit(Dns::Name::IsSubDomainOf(name, GetDomain()), error = OT_ERROR_SECURITY);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -776,7 +783,7 @@ otError Server::ProcessServiceDescriptionInstructions(Host & a
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t & aOffset)
|
||||
uint16_t & aOffset) const
|
||||
{
|
||||
Service *service;
|
||||
otError error = OT_ERROR_NONE;
|
||||
@@ -787,6 +794,7 @@ otError Server::ProcessServiceDescriptionInstructions(Host & a
|
||||
Dns::ResourceRecord record;
|
||||
|
||||
SuccessOrExit(error = Dns::Name::ReadName(aMessage, aOffset, name, sizeof(name)));
|
||||
VerifyOrExit(Dns::Name::IsSubDomainOf(name, GetDomain()), error = OT_ERROR_SECURITY);
|
||||
SuccessOrExit(error = aMessage.Read(aOffset, record));
|
||||
|
||||
if (record.GetClass() == Dns::ResourceRecord::kClassAny)
|
||||
@@ -814,6 +822,7 @@ otError Server::ProcessServiceDescriptionInstructions(Host & a
|
||||
aOffset += sizeof(srvRecord);
|
||||
|
||||
SuccessOrExit(error = Dns::Name::ReadName(aMessage, aOffset, hostName, hostNameLength));
|
||||
VerifyOrExit(Dns::Name::IsSubDomainOf(name, GetDomain()), error = OT_ERROR_SECURITY);
|
||||
VerifyOrExit(aHost.Matches(hostName), error = OT_ERROR_FAILED);
|
||||
|
||||
service = aHost.FindService(name);
|
||||
@@ -862,7 +871,7 @@ bool Server::IsValidDeleteAllRecord(const Dns::ResourceRecord &aRecord)
|
||||
otError Server::ProcessAdditionalSection(Host * aHost,
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
uint16_t & aOffset)
|
||||
uint16_t & aOffset) const
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
Dns::OptRecord optRecord;
|
||||
@@ -927,7 +936,7 @@ otError Server::VerifySignature(const Dns::Ecdsa256KeyRecord &aKey,
|
||||
uint16_t aSigOffset,
|
||||
uint16_t aSigRdataOffset,
|
||||
uint16_t aSigRdataLength,
|
||||
const char * aSignerName)
|
||||
const char * aSignerName) const
|
||||
{
|
||||
otError error;
|
||||
uint16_t offset = aMessage.GetOffset();
|
||||
@@ -1571,6 +1580,11 @@ Server::Service *Server::Host::FindService(const char *aFullName)
|
||||
return mServices.FindMatching(aFullName);
|
||||
}
|
||||
|
||||
const Server::Service *Server::Host::FindService(const char *aFullName) const
|
||||
{
|
||||
return const_cast<Host *>(this)->FindService(aFullName);
|
||||
}
|
||||
|
||||
otError Server::Host::AddIp6Address(const Ip6::Address &aIp6Address)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
+26
-25
@@ -361,7 +361,8 @@ public:
|
||||
void DeleteResourcesButRetainName(void);
|
||||
void CopyResourcesFrom(const Host &aHost);
|
||||
Service *FindService(const char *aFullName);
|
||||
otError AddIp6Address(const Ip6::Address &aIp6Address);
|
||||
const Service *FindService(const char *aFullName) const;
|
||||
otError AddIp6Address(const Ip6::Address &aIp6Address);
|
||||
|
||||
char * mFullName;
|
||||
Ip6::Address mAddresses[kMaxAddressesNum];
|
||||
@@ -552,45 +553,45 @@ private:
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t & aOffset);
|
||||
uint16_t & aOffset) const;
|
||||
otError ProcessAdditionalSection(Host * aHost,
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
uint16_t & aOffset);
|
||||
uint16_t & aOffset) const;
|
||||
otError VerifySignature(const Dns::Ecdsa256KeyRecord &aKey,
|
||||
const Message & aMessage,
|
||||
Dns::UpdateHeader aDnsHeader,
|
||||
uint16_t aSigOffset,
|
||||
uint16_t aSigRdataOffset,
|
||||
uint16_t aSigRdataLength,
|
||||
const char * aSignerName);
|
||||
const char * aSignerName) const;
|
||||
otError ProcessZoneSection(const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
uint16_t & aOffset,
|
||||
Dns::Zone & aZone) const;
|
||||
otError ProcessHostDescriptionInstruction(Host & aHost,
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t aOffset) const;
|
||||
otError ProcessServiceDiscoveryInstructions(Host & aHost,
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t aOffset) const;
|
||||
otError ProcessServiceDescriptionInstructions(Host & aHost,
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t & aOffset) const;
|
||||
|
||||
static otError ProcessZoneSection(const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
uint16_t & aOffset,
|
||||
Dns::Zone & aZone);
|
||||
static otError ProcessHostDescriptionInstruction(Host & aHost,
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t aOffset);
|
||||
static otError ProcessServiceDiscoveryInstructions(Host & aHost,
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t aOffset);
|
||||
static otError ProcessServiceDescriptionInstructions(Host & aHost,
|
||||
const Message & aMessage,
|
||||
const Dns::UpdateHeader &aDnsHeader,
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t & aOffset);
|
||||
static bool IsValidDeleteAllRecord(const Dns::ResourceRecord &aRecord);
|
||||
const Service *FindService(const char *aFullName) const;
|
||||
|
||||
void HandleUpdate(const Dns::UpdateHeader &aDnsHeader, Host *aHost, const Ip6::MessageInfo &aMessageInfo);
|
||||
void AddHost(Host *aHost);
|
||||
void RemoveAndFreeHost(Host *aHost);
|
||||
Service * FindService(const char *aFullName);
|
||||
bool HasNameConflictsWith(Host &aHost);
|
||||
bool HasNameConflictsWith(Host &aHost) const;
|
||||
void SendResponse(const Dns::UpdateHeader & aHeader,
|
||||
Dns::UpdateHeader::Response aResponseCode,
|
||||
const Ip6::MessageInfo & aMessageInfo);
|
||||
|
||||
@@ -145,6 +145,43 @@ void TestDnsName(void)
|
||||
|
||||
message->SetOffset(0);
|
||||
|
||||
printf("----------------------------------------------------------------\n");
|
||||
printf("Verify domain name match:\n");
|
||||
|
||||
{
|
||||
const char *subDomain;
|
||||
const char *domain;
|
||||
|
||||
subDomain = "my-service._ipps._tcp.local.";
|
||||
domain = "local.";
|
||||
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain), "Name::IsSubDomainOf() failed");
|
||||
|
||||
subDomain = "my-service._ipps._tcp.local";
|
||||
domain = "local.";
|
||||
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain), "Name::IsSubDomainOf() failed");
|
||||
|
||||
subDomain = "my-service._ipps._tcp.local.";
|
||||
domain = "local";
|
||||
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain), "Name::IsSubDomainOf() failed");
|
||||
|
||||
subDomain = "my-service._ipps._tcp.local";
|
||||
domain = "local";
|
||||
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain), "Name::IsSubDomainOf() failed");
|
||||
|
||||
subDomain = "my-service._ipps._tcp.default.service.arpa.";
|
||||
domain = "default.service.arpa.";
|
||||
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain), "Name::IsSubDomainOf() failed");
|
||||
|
||||
subDomain = "my-service._ipps._tcp.default.service.arpa.";
|
||||
domain = "service.arpa.";
|
||||
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain), "Name::IsSubDomainOf() failed");
|
||||
|
||||
// Verify it doesn't match a portion of a label.
|
||||
subDomain = "my-service._ipps._tcp.default.service.arpa.";
|
||||
domain = "vice.arpa.";
|
||||
VerifyOrQuit(!Dns::Name::IsSubDomainOf(subDomain, domain), "Name::IsSubDomainOf() succeed");
|
||||
}
|
||||
|
||||
printf("----------------------------------------------------------------\n");
|
||||
printf("Append names, check encoded bytes, parse name and read labels:\n");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user