1259 lines
44 KiB
C++
1259 lines
44 KiB
C++
/*
|
|
* Copyright (c) 2021, 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.
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* This file implements the DNS-SD server.
|
|
*/
|
|
|
|
#include "dnssd_server.hpp"
|
|
|
|
#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
|
|
|
|
#include "common/array.hpp"
|
|
#include "common/as_core_type.hpp"
|
|
#include "common/code_utils.hpp"
|
|
#include "common/debug.hpp"
|
|
#include "common/instance.hpp"
|
|
#include "common/locator_getters.hpp"
|
|
#include "common/log.hpp"
|
|
#include "common/string.hpp"
|
|
#include "net/srp_server.hpp"
|
|
#include "net/udp6.hpp"
|
|
|
|
namespace ot {
|
|
namespace Dns {
|
|
namespace ServiceDiscovery {
|
|
|
|
RegisterLogModule("DnssdServer");
|
|
|
|
const char Server::kDnssdProtocolUdp[] = "_udp";
|
|
const char Server::kDnssdProtocolTcp[] = "_tcp";
|
|
const char Server::kDnssdSubTypeLabel[] = "._sub.";
|
|
const char Server::kDefaultDomainName[] = "default.service.arpa.";
|
|
|
|
Server::Server(Instance &aInstance)
|
|
: InstanceLocator(aInstance)
|
|
, mSocket(aInstance)
|
|
, mQueryCallbackContext(nullptr)
|
|
, mQuerySubscribe(nullptr)
|
|
, mQueryUnsubscribe(nullptr)
|
|
, mTimer(aInstance, Server::HandleTimer)
|
|
{
|
|
mCounters.Clear();
|
|
}
|
|
|
|
Error Server::Start(void)
|
|
{
|
|
Error error = kErrorNone;
|
|
|
|
VerifyOrExit(!IsRunning());
|
|
|
|
SuccessOrExit(error = mSocket.Open(&Server::HandleUdpReceive, this));
|
|
SuccessOrExit(error = mSocket.Bind(kPort, kBindUnspecifiedNetif ? OT_NETIF_UNSPECIFIED : OT_NETIF_THREAD));
|
|
|
|
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
Get<Srp::Server>().HandleDnssdServerStateChange();
|
|
#endif
|
|
|
|
exit:
|
|
LogInfo("started: %s", ErrorToString(error));
|
|
|
|
if (error != kErrorNone)
|
|
{
|
|
IgnoreError(mSocket.Close());
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void Server::Stop(void)
|
|
{
|
|
// Abort all query transactions
|
|
for (QueryTransaction &query : mQueryTransactions)
|
|
{
|
|
if (query.IsValid())
|
|
{
|
|
FinalizeQuery(query, Header::kResponseServerFailure);
|
|
}
|
|
}
|
|
|
|
mTimer.Stop();
|
|
|
|
IgnoreError(mSocket.Close());
|
|
LogInfo("stopped");
|
|
|
|
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
Get<Srp::Server>().HandleDnssdServerStateChange();
|
|
#endif
|
|
}
|
|
|
|
void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
|
|
{
|
|
static_cast<Server *>(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo));
|
|
}
|
|
|
|
void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
|
{
|
|
Header requestHeader;
|
|
|
|
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
// We first let the `Srp::Server` process the received message.
|
|
// It returns `kErrorNone` to indicate that it successfully
|
|
// processed the message.
|
|
|
|
VerifyOrExit(Get<Srp::Server>().HandleDnssdServerUdpReceive(aMessage, aMessageInfo) != kErrorNone);
|
|
#endif
|
|
|
|
SuccessOrExit(aMessage.Read(aMessage.GetOffset(), requestHeader));
|
|
VerifyOrExit(requestHeader.GetType() == Header::kTypeQuery);
|
|
|
|
ProcessQuery(requestHeader, aMessage, aMessageInfo);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage, const Ip6::MessageInfo &aMessageInfo)
|
|
{
|
|
Error error = kErrorNone;
|
|
Message * responseMessage = nullptr;
|
|
Header responseHeader;
|
|
NameCompressInfo compressInfo(kDefaultDomainName);
|
|
Header::Response response = Header::kResponseSuccess;
|
|
bool resolveByQueryCallbacks = false;
|
|
|
|
responseMessage = mSocket.NewMessage(0);
|
|
VerifyOrExit(responseMessage != nullptr, error = kErrorNoBufs);
|
|
|
|
// Allocate space for DNS header
|
|
SuccessOrExit(error = responseMessage->SetLength(sizeof(Header)));
|
|
|
|
// Setup initial DNS response header
|
|
responseHeader.Clear();
|
|
responseHeader.SetType(Header::kTypeResponse);
|
|
responseHeader.SetMessageId(aRequestHeader.GetMessageId());
|
|
|
|
// Validate the query
|
|
VerifyOrExit(aRequestHeader.GetQueryType() == Header::kQueryTypeStandard,
|
|
response = Header::kResponseNotImplemented);
|
|
VerifyOrExit(!aRequestHeader.IsTruncationFlagSet(), response = Header::kResponseFormatError);
|
|
VerifyOrExit(aRequestHeader.GetQuestionCount() > 0, response = Header::kResponseFormatError);
|
|
|
|
response = AddQuestions(aRequestHeader, aRequestMessage, responseHeader, *responseMessage, compressInfo);
|
|
VerifyOrExit(response == Header::kResponseSuccess);
|
|
|
|
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
// Answer the questions
|
|
response = ResolveBySrp(responseHeader, *responseMessage, compressInfo);
|
|
#endif
|
|
|
|
// Resolve the question using query callbacks if SRP server failed to resolve the questions.
|
|
if (responseHeader.GetAnswerCount() == 0)
|
|
{
|
|
if (kErrorNone == ResolveByQueryCallbacks(responseHeader, *responseMessage, compressInfo, aMessageInfo))
|
|
{
|
|
resolveByQueryCallbacks = true;
|
|
}
|
|
}
|
|
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
else
|
|
{
|
|
++mCounters.mResolvedBySrp;
|
|
}
|
|
#endif
|
|
|
|
exit:
|
|
if (error == kErrorNone && !resolveByQueryCallbacks)
|
|
{
|
|
SendResponse(responseHeader, response, *responseMessage, aMessageInfo, mSocket);
|
|
}
|
|
|
|
FreeMessageOnError(responseMessage, error);
|
|
}
|
|
|
|
void Server::SendResponse(Header aHeader,
|
|
Header::Response aResponseCode,
|
|
Message & aMessage,
|
|
const Ip6::MessageInfo &aMessageInfo,
|
|
Ip6::Udp::Socket & aSocket)
|
|
{
|
|
Error error;
|
|
|
|
if (aResponseCode == Header::kResponseServerFailure)
|
|
{
|
|
LogWarn("failed to handle DNS query due to server failure");
|
|
aHeader.SetQuestionCount(0);
|
|
aHeader.SetAnswerCount(0);
|
|
aHeader.SetAdditionalRecordCount(0);
|
|
IgnoreError(aMessage.SetLength(sizeof(Header)));
|
|
}
|
|
|
|
aHeader.SetResponseCode(aResponseCode);
|
|
aMessage.Write(0, aHeader);
|
|
|
|
error = aSocket.SendTo(aMessage, aMessageInfo);
|
|
|
|
FreeMessageOnError(&aMessage, error);
|
|
|
|
if (error != kErrorNone)
|
|
{
|
|
LogWarn("failed to send DNS-SD reply: %s", ErrorToString(error));
|
|
}
|
|
else
|
|
{
|
|
LogInfo("send DNS-SD reply: %s, RCODE=%d", ErrorToString(error), aResponseCode);
|
|
}
|
|
|
|
UpdateResponseCounters(aResponseCode);
|
|
}
|
|
|
|
Header::Response Server::AddQuestions(const Header & aRequestHeader,
|
|
const Message & aRequestMessage,
|
|
Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
NameCompressInfo &aCompressInfo)
|
|
{
|
|
Question question;
|
|
uint16_t readOffset;
|
|
Header::Response response = Header::kResponseSuccess;
|
|
char name[Name::kMaxNameSize];
|
|
|
|
readOffset = sizeof(Header);
|
|
|
|
// Check and append the questions
|
|
for (uint16_t i = 0; i < aRequestHeader.GetQuestionCount(); i++)
|
|
{
|
|
NameComponentsOffsetInfo nameComponentsOffsetInfo;
|
|
uint16_t qtype;
|
|
|
|
VerifyOrExit(kErrorNone == Name::ReadName(aRequestMessage, readOffset, name, sizeof(name)),
|
|
response = Header::kResponseFormatError);
|
|
VerifyOrExit(kErrorNone == aRequestMessage.Read(readOffset, question), response = Header::kResponseFormatError);
|
|
readOffset += sizeof(question);
|
|
|
|
qtype = question.GetType();
|
|
|
|
VerifyOrExit(qtype == ResourceRecord::kTypePtr || qtype == ResourceRecord::kTypeSrv ||
|
|
qtype == ResourceRecord::kTypeTxt || qtype == ResourceRecord::kTypeAaaa,
|
|
response = Header::kResponseNotImplemented);
|
|
|
|
VerifyOrExit(kErrorNone == FindNameComponents(name, aCompressInfo.GetDomainName(), nameComponentsOffsetInfo),
|
|
response = Header::kResponseNameError);
|
|
|
|
switch (question.GetType())
|
|
{
|
|
case ResourceRecord::kTypePtr:
|
|
VerifyOrExit(nameComponentsOffsetInfo.IsServiceName(), response = Header::kResponseNameError);
|
|
break;
|
|
case ResourceRecord::kTypeSrv:
|
|
VerifyOrExit(nameComponentsOffsetInfo.IsServiceInstanceName(), response = Header::kResponseNameError);
|
|
break;
|
|
case ResourceRecord::kTypeTxt:
|
|
VerifyOrExit(nameComponentsOffsetInfo.IsServiceInstanceName(), response = Header::kResponseNameError);
|
|
break;
|
|
case ResourceRecord::kTypeAaaa:
|
|
VerifyOrExit(nameComponentsOffsetInfo.IsHostName(), response = Header::kResponseNameError);
|
|
break;
|
|
default:
|
|
ExitNow(response = Header::kResponseNotImplemented);
|
|
}
|
|
|
|
VerifyOrExit(AppendQuestion(name, question, aResponseMessage, aCompressInfo) == kErrorNone,
|
|
response = Header::kResponseServerFailure);
|
|
}
|
|
|
|
aResponseHeader.SetQuestionCount(aRequestHeader.GetQuestionCount());
|
|
|
|
exit:
|
|
return response;
|
|
}
|
|
|
|
Error Server::AppendQuestion(const char * aName,
|
|
const Question & aQuestion,
|
|
Message & aMessage,
|
|
NameCompressInfo &aCompressInfo)
|
|
{
|
|
Error error = kErrorNone;
|
|
|
|
switch (aQuestion.GetType())
|
|
{
|
|
case ResourceRecord::kTypePtr:
|
|
SuccessOrExit(error = AppendServiceName(aMessage, aName, aCompressInfo));
|
|
break;
|
|
case ResourceRecord::kTypeSrv:
|
|
case ResourceRecord::kTypeTxt:
|
|
SuccessOrExit(error = AppendInstanceName(aMessage, aName, aCompressInfo));
|
|
break;
|
|
case ResourceRecord::kTypeAaaa:
|
|
SuccessOrExit(error = AppendHostName(aMessage, aName, aCompressInfo));
|
|
break;
|
|
default:
|
|
OT_ASSERT(false);
|
|
}
|
|
|
|
error = aMessage.Append(aQuestion);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error Server::AppendPtrRecord(Message & aMessage,
|
|
const char * aServiceName,
|
|
const char * aInstanceName,
|
|
uint32_t aTtl,
|
|
NameCompressInfo &aCompressInfo)
|
|
{
|
|
Error error;
|
|
PtrRecord ptrRecord;
|
|
uint16_t recordOffset;
|
|
|
|
ptrRecord.Init();
|
|
ptrRecord.SetTtl(aTtl);
|
|
|
|
SuccessOrExit(error = AppendServiceName(aMessage, aServiceName, aCompressInfo));
|
|
|
|
recordOffset = aMessage.GetLength();
|
|
SuccessOrExit(error = aMessage.SetLength(recordOffset + sizeof(ptrRecord)));
|
|
|
|
SuccessOrExit(error = AppendInstanceName(aMessage, aInstanceName, aCompressInfo));
|
|
|
|
ptrRecord.SetLength(aMessage.GetLength() - (recordOffset + sizeof(ResourceRecord)));
|
|
aMessage.Write(recordOffset, ptrRecord);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error Server::AppendSrvRecord(Message & aMessage,
|
|
const char * aInstanceName,
|
|
const char * aHostName,
|
|
uint32_t aTtl,
|
|
uint16_t aPriority,
|
|
uint16_t aWeight,
|
|
uint16_t aPort,
|
|
NameCompressInfo &aCompressInfo)
|
|
{
|
|
SrvRecord srvRecord;
|
|
Error error = kErrorNone;
|
|
uint16_t recordOffset;
|
|
|
|
srvRecord.Init();
|
|
srvRecord.SetTtl(aTtl);
|
|
srvRecord.SetPriority(aPriority);
|
|
srvRecord.SetWeight(aWeight);
|
|
srvRecord.SetPort(aPort);
|
|
|
|
SuccessOrExit(error = AppendInstanceName(aMessage, aInstanceName, aCompressInfo));
|
|
|
|
recordOffset = aMessage.GetLength();
|
|
SuccessOrExit(error = aMessage.SetLength(recordOffset + sizeof(srvRecord)));
|
|
|
|
SuccessOrExit(error = AppendHostName(aMessage, aHostName, aCompressInfo));
|
|
|
|
srvRecord.SetLength(aMessage.GetLength() - (recordOffset + sizeof(ResourceRecord)));
|
|
aMessage.Write(recordOffset, srvRecord);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error Server::AppendAaaaRecord(Message & aMessage,
|
|
const char * aHostName,
|
|
const Ip6::Address &aAddress,
|
|
uint32_t aTtl,
|
|
NameCompressInfo & aCompressInfo)
|
|
{
|
|
AaaaRecord aaaaRecord;
|
|
Error error;
|
|
|
|
aaaaRecord.Init();
|
|
aaaaRecord.SetTtl(aTtl);
|
|
aaaaRecord.SetAddress(aAddress);
|
|
|
|
SuccessOrExit(error = AppendHostName(aMessage, aHostName, aCompressInfo));
|
|
error = aMessage.Append(aaaaRecord);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error Server::AppendServiceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo)
|
|
{
|
|
Error error;
|
|
uint16_t serviceCompressOffset = aCompressInfo.GetServiceNameOffset(aMessage, aName);
|
|
const char *serviceName;
|
|
|
|
// Check whether `aName` is a sub-type service name.
|
|
serviceName = StringFind(aName, kDnssdSubTypeLabel, kStringCaseInsensitiveMatch);
|
|
|
|
if (serviceName != nullptr)
|
|
{
|
|
uint8_t subTypeLabelLength = static_cast<uint8_t>(serviceName - aName) + sizeof(kDnssdSubTypeLabel) - 1;
|
|
|
|
SuccessOrExit(error = Name::AppendMultipleLabels(aName, subTypeLabelLength, aMessage));
|
|
|
|
// Skip over the "._sub." label to get to the root service name.
|
|
serviceName += sizeof(kDnssdSubTypeLabel) - 1;
|
|
}
|
|
else
|
|
{
|
|
serviceName = aName;
|
|
}
|
|
|
|
if (serviceCompressOffset != NameCompressInfo::kUnknownOffset)
|
|
{
|
|
error = Name::AppendPointerLabel(serviceCompressOffset, aMessage);
|
|
}
|
|
else
|
|
{
|
|
uint8_t domainStart = static_cast<uint8_t>(StringLength(serviceName, Name::kMaxNameSize - 1) -
|
|
StringLength(aCompressInfo.GetDomainName(), Name::kMaxNameSize - 1));
|
|
uint16_t domainCompressOffset = aCompressInfo.GetDomainNameOffset();
|
|
|
|
serviceCompressOffset = aMessage.GetLength();
|
|
aCompressInfo.SetServiceNameOffset(serviceCompressOffset);
|
|
|
|
if (domainCompressOffset == NameCompressInfo::kUnknownOffset)
|
|
{
|
|
aCompressInfo.SetDomainNameOffset(serviceCompressOffset + domainStart);
|
|
error = Name::AppendName(serviceName, aMessage);
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = Name::AppendMultipleLabels(serviceName, domainStart, aMessage));
|
|
error = Name::AppendPointerLabel(domainCompressOffset, aMessage);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error Server::AppendInstanceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo)
|
|
{
|
|
Error error;
|
|
uint16_t instanceCompressOffset = aCompressInfo.GetInstanceNameOffset(aMessage, aName);
|
|
|
|
if (instanceCompressOffset != NameCompressInfo::kUnknownOffset)
|
|
{
|
|
error = Name::AppendPointerLabel(instanceCompressOffset, aMessage);
|
|
}
|
|
else
|
|
{
|
|
NameComponentsOffsetInfo nameComponentsInfo;
|
|
|
|
IgnoreError(FindNameComponents(aName, aCompressInfo.GetDomainName(), nameComponentsInfo));
|
|
OT_ASSERT(nameComponentsInfo.IsServiceInstanceName());
|
|
|
|
aCompressInfo.SetInstanceNameOffset(aMessage.GetLength());
|
|
|
|
// Append the instance name as one label
|
|
SuccessOrExit(error = Name::AppendLabel(aName, nameComponentsInfo.mServiceOffset - 1, aMessage));
|
|
|
|
{
|
|
const char *serviceName = aName + nameComponentsInfo.mServiceOffset;
|
|
uint16_t serviceCompressOffset = aCompressInfo.GetServiceNameOffset(aMessage, serviceName);
|
|
|
|
if (serviceCompressOffset != NameCompressInfo::kUnknownOffset)
|
|
{
|
|
error = Name::AppendPointerLabel(serviceCompressOffset, aMessage);
|
|
}
|
|
else
|
|
{
|
|
aCompressInfo.SetServiceNameOffset(aMessage.GetLength());
|
|
error = Name::AppendName(serviceName, aMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error Server::AppendTxtRecord(Message & aMessage,
|
|
const char * aInstanceName,
|
|
const void * aTxtData,
|
|
uint16_t aTxtLength,
|
|
uint32_t aTtl,
|
|
NameCompressInfo &aCompressInfo)
|
|
{
|
|
Error error = kErrorNone;
|
|
TxtRecord txtRecord;
|
|
const uint8_t kEmptyTxt = 0;
|
|
|
|
SuccessOrExit(error = AppendInstanceName(aMessage, aInstanceName, aCompressInfo));
|
|
|
|
txtRecord.Init();
|
|
txtRecord.SetTtl(aTtl);
|
|
txtRecord.SetLength(aTxtLength > 0 ? aTxtLength : sizeof(kEmptyTxt));
|
|
|
|
SuccessOrExit(error = aMessage.Append(txtRecord));
|
|
if (aTxtLength > 0)
|
|
{
|
|
error = aMessage.AppendBytes(aTxtData, aTxtLength);
|
|
}
|
|
else
|
|
{
|
|
error = aMessage.Append(kEmptyTxt);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error Server::AppendHostName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo)
|
|
{
|
|
Error error;
|
|
uint16_t hostCompressOffset = aCompressInfo.GetHostNameOffset(aMessage, aName);
|
|
|
|
if (hostCompressOffset != NameCompressInfo::kUnknownOffset)
|
|
{
|
|
error = Name::AppendPointerLabel(hostCompressOffset, aMessage);
|
|
}
|
|
else
|
|
{
|
|
uint8_t domainStart = static_cast<uint8_t>(StringLength(aName, Name::kMaxNameLength) -
|
|
StringLength(aCompressInfo.GetDomainName(), Name::kMaxNameSize - 1));
|
|
uint16_t domainCompressOffset = aCompressInfo.GetDomainNameOffset();
|
|
|
|
hostCompressOffset = aMessage.GetLength();
|
|
aCompressInfo.SetHostNameOffset(hostCompressOffset);
|
|
|
|
if (domainCompressOffset == NameCompressInfo::kUnknownOffset)
|
|
{
|
|
aCompressInfo.SetDomainNameOffset(hostCompressOffset + domainStart);
|
|
error = Name::AppendName(aName, aMessage);
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = Name::AppendMultipleLabels(aName, domainStart, aMessage));
|
|
error = Name::AppendPointerLabel(domainCompressOffset, aMessage);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Server::IncResourceRecordCount(Header &aHeader, bool aAdditional)
|
|
{
|
|
if (aAdditional)
|
|
{
|
|
aHeader.SetAdditionalRecordCount(aHeader.GetAdditionalRecordCount() + 1);
|
|
}
|
|
else
|
|
{
|
|
aHeader.SetAnswerCount(aHeader.GetAnswerCount() + 1);
|
|
}
|
|
}
|
|
|
|
Error Server::FindNameComponents(const char *aName, const char *aDomain, NameComponentsOffsetInfo &aInfo)
|
|
{
|
|
uint8_t nameLen = static_cast<uint8_t>(StringLength(aName, Name::kMaxNameLength));
|
|
uint8_t domainLen = static_cast<uint8_t>(StringLength(aDomain, Name::kMaxNameLength));
|
|
Error error = kErrorNone;
|
|
uint8_t labelBegin, labelEnd;
|
|
|
|
VerifyOrExit(Name::IsSubDomainOf(aName, aDomain), error = kErrorInvalidArgs);
|
|
|
|
labelBegin = nameLen - domainLen;
|
|
aInfo.mDomainOffset = labelBegin;
|
|
|
|
while (true)
|
|
{
|
|
error = FindPreviousLabel(aName, labelBegin, labelEnd);
|
|
|
|
VerifyOrExit(error == kErrorNone, error = (error == kErrorNotFound ? kErrorNone : error));
|
|
|
|
if (labelEnd == labelBegin + kProtocolLabelLength &&
|
|
(StringStartsWith(&aName[labelBegin], kDnssdProtocolUdp, kStringCaseInsensitiveMatch) ||
|
|
StringStartsWith(&aName[labelBegin], kDnssdProtocolTcp, kStringCaseInsensitiveMatch)))
|
|
{
|
|
// <Protocol> label found
|
|
aInfo.mProtocolOffset = labelBegin;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get service label <Service>
|
|
error = FindPreviousLabel(aName, labelBegin, labelEnd);
|
|
VerifyOrExit(error == kErrorNone, error = (error == kErrorNotFound ? kErrorNone : error));
|
|
|
|
aInfo.mServiceOffset = labelBegin;
|
|
|
|
// Check for service subtype
|
|
error = FindPreviousLabel(aName, labelBegin, labelEnd);
|
|
VerifyOrExit(error == kErrorNone, error = (error == kErrorNotFound ? kErrorNone : error));
|
|
|
|
// Note that `kDnssdSubTypeLabel` is "._sub.". Here we get the
|
|
// label only so we want to compare it with "_sub".
|
|
if ((labelEnd == labelBegin + kSubTypeLabelLength) &&
|
|
StringStartsWith(&aName[labelBegin], kDnssdSubTypeLabel + 1, kStringCaseInsensitiveMatch))
|
|
{
|
|
SuccessOrExit(error = FindPreviousLabel(aName, labelBegin, labelEnd));
|
|
VerifyOrExit(labelBegin == 0, error = kErrorInvalidArgs);
|
|
aInfo.mSubTypeOffset = labelBegin;
|
|
ExitNow();
|
|
}
|
|
|
|
// Treat everything before <Service> as <Instance> label
|
|
aInfo.mInstanceOffset = 0;
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error Server::FindPreviousLabel(const char *aName, uint8_t &aStart, uint8_t &aStop)
|
|
{
|
|
// This method finds the previous label before the current label (whose start index is @p aStart), and updates @p
|
|
// aStart to the start index of the label and @p aStop to the index of the dot just after the label.
|
|
// @note The input value of @p aStop does not matter because it is only used to output.
|
|
|
|
Error error = kErrorNone;
|
|
uint8_t start = aStart;
|
|
uint8_t end;
|
|
|
|
VerifyOrExit(start > 0, error = kErrorNotFound);
|
|
VerifyOrExit(aName[--start] == Name::kLabelSeperatorChar, error = kErrorInvalidArgs);
|
|
|
|
end = start;
|
|
while (start > 0 && aName[start - 1] != Name::kLabelSeperatorChar)
|
|
{
|
|
start--;
|
|
}
|
|
|
|
VerifyOrExit(start < end, error = kErrorInvalidArgs);
|
|
|
|
aStart = start;
|
|
aStop = end;
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
Header::Response Server::ResolveBySrp(Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
Server::NameCompressInfo &aCompressInfo)
|
|
{
|
|
Question question;
|
|
uint16_t readOffset = sizeof(Header);
|
|
Header::Response response = Header::kResponseSuccess;
|
|
char name[Name::kMaxNameSize];
|
|
|
|
for (uint16_t i = 0; i < aResponseHeader.GetQuestionCount(); i++)
|
|
{
|
|
IgnoreError(Name::ReadName(aResponseMessage, readOffset, name, sizeof(name)));
|
|
IgnoreError(aResponseMessage.Read(readOffset, question));
|
|
readOffset += sizeof(question);
|
|
|
|
response = ResolveQuestionBySrp(name, question, aResponseHeader, aResponseMessage, aCompressInfo,
|
|
/* aAdditional */ false);
|
|
|
|
LogInfo("ANSWER: TRANSACTION=0x%04x, QUESTION=[%s %d %d], RCODE=%d", aResponseHeader.GetMessageId(), name,
|
|
question.GetClass(), question.GetType(), response);
|
|
}
|
|
|
|
// Answer the questions with additional RRs if required
|
|
if (aResponseHeader.GetAnswerCount() > 0)
|
|
{
|
|
readOffset = sizeof(Header);
|
|
for (uint16_t i = 0; i < aResponseHeader.GetQuestionCount(); i++)
|
|
{
|
|
IgnoreError(Name::ReadName(aResponseMessage, readOffset, name, sizeof(name)));
|
|
IgnoreError(aResponseMessage.Read(readOffset, question));
|
|
readOffset += sizeof(question);
|
|
|
|
VerifyOrExit(Header::kResponseServerFailure != ResolveQuestionBySrp(name, question, aResponseHeader,
|
|
aResponseMessage, aCompressInfo,
|
|
/* aAdditional */ true),
|
|
response = Header::kResponseServerFailure);
|
|
|
|
LogInfo("ADDITIONAL: TRANSACTION=0x%04x, QUESTION=[%s %d %d], RCODE=%d", aResponseHeader.GetMessageId(),
|
|
name, question.GetClass(), question.GetType(), response);
|
|
}
|
|
}
|
|
exit:
|
|
return response;
|
|
}
|
|
|
|
Header::Response Server::ResolveQuestionBySrp(const char * aName,
|
|
const Question & aQuestion,
|
|
Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
NameCompressInfo &aCompressInfo,
|
|
bool aAdditional)
|
|
{
|
|
Error error = kErrorNone;
|
|
const Srp::Server::Host *host = nullptr;
|
|
TimeMilli now = TimerMilli::GetNow();
|
|
uint16_t qtype = aQuestion.GetType();
|
|
Header::Response response = Header::kResponseNameError;
|
|
|
|
while ((host = GetNextSrpHost(host)) != nullptr)
|
|
{
|
|
bool needAdditionalAaaaRecord = false;
|
|
const char *hostName = host->GetFullName();
|
|
|
|
// Handle PTR/SRV/TXT query
|
|
if (qtype == ResourceRecord::kTypePtr || qtype == ResourceRecord::kTypeSrv || qtype == ResourceRecord::kTypeTxt)
|
|
{
|
|
const Srp::Server::Service *service = nullptr;
|
|
|
|
while ((service = GetNextSrpService(*host, service)) != nullptr)
|
|
{
|
|
uint32_t instanceTtl = TimeMilli::MsecToSec(service->GetExpireTime() - TimerMilli::GetNow());
|
|
const char *instanceName = service->GetInstanceName();
|
|
bool serviceNameMatched = service->MatchesServiceName(aName);
|
|
bool instanceNameMatched = service->MatchesInstanceName(aName);
|
|
bool ptrQueryMatched = qtype == ResourceRecord::kTypePtr && serviceNameMatched;
|
|
bool srvQueryMatched = qtype == ResourceRecord::kTypeSrv && instanceNameMatched;
|
|
bool txtQueryMatched = qtype == ResourceRecord::kTypeTxt && instanceNameMatched;
|
|
|
|
if (ptrQueryMatched || srvQueryMatched)
|
|
{
|
|
needAdditionalAaaaRecord = true;
|
|
}
|
|
|
|
if (!aAdditional && ptrQueryMatched)
|
|
{
|
|
SuccessOrExit(
|
|
error = AppendPtrRecord(aResponseMessage, aName, instanceName, instanceTtl, aCompressInfo));
|
|
IncResourceRecordCount(aResponseHeader, aAdditional);
|
|
response = Header::kResponseSuccess;
|
|
}
|
|
|
|
if ((!aAdditional && srvQueryMatched) ||
|
|
(aAdditional && ptrQueryMatched &&
|
|
!HasQuestion(aResponseHeader, aResponseMessage, instanceName, ResourceRecord::kTypeSrv)))
|
|
{
|
|
SuccessOrExit(error = AppendSrvRecord(aResponseMessage, instanceName, hostName, instanceTtl,
|
|
service->GetPriority(), service->GetWeight(),
|
|
service->GetPort(), aCompressInfo));
|
|
IncResourceRecordCount(aResponseHeader, aAdditional);
|
|
response = Header::kResponseSuccess;
|
|
}
|
|
|
|
if ((!aAdditional && txtQueryMatched) ||
|
|
(aAdditional && ptrQueryMatched &&
|
|
!HasQuestion(aResponseHeader, aResponseMessage, instanceName, ResourceRecord::kTypeTxt)))
|
|
{
|
|
SuccessOrExit(error = AppendTxtRecord(aResponseMessage, instanceName, service->GetTxtData(),
|
|
service->GetTxtDataLength(), instanceTtl, aCompressInfo));
|
|
IncResourceRecordCount(aResponseHeader, aAdditional);
|
|
response = Header::kResponseSuccess;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle AAAA query
|
|
if ((!aAdditional && qtype == ResourceRecord::kTypeAaaa && host->Matches(aName)) ||
|
|
(aAdditional && needAdditionalAaaaRecord &&
|
|
!HasQuestion(aResponseHeader, aResponseMessage, hostName, ResourceRecord::kTypeAaaa)))
|
|
{
|
|
uint8_t addrNum;
|
|
const Ip6::Address *addrs = host->GetAddresses(addrNum);
|
|
uint32_t hostTtl = TimeMilli::MsecToSec(host->GetExpireTime() - now);
|
|
|
|
for (uint8_t i = 0; i < addrNum; i++)
|
|
{
|
|
SuccessOrExit(error = AppendAaaaRecord(aResponseMessage, hostName, addrs[i], hostTtl, aCompressInfo));
|
|
IncResourceRecordCount(aResponseHeader, aAdditional);
|
|
}
|
|
|
|
response = Header::kResponseSuccess;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error == kErrorNone ? response : Header::kResponseServerFailure;
|
|
}
|
|
|
|
const Srp::Server::Host *Server::GetNextSrpHost(const Srp::Server::Host *aHost)
|
|
{
|
|
const Srp::Server::Host *host = Get<Srp::Server>().GetNextHost(aHost);
|
|
|
|
while (host != nullptr && host->IsDeleted())
|
|
{
|
|
host = Get<Srp::Server>().GetNextHost(host);
|
|
}
|
|
|
|
return host;
|
|
}
|
|
|
|
const Srp::Server::Service *Server::GetNextSrpService(const Srp::Server::Host & aHost,
|
|
const Srp::Server::Service *aService)
|
|
{
|
|
return aHost.FindNextService(aService, Srp::Server::kFlagsAnyTypeActiveService);
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
|
|
Error Server::ResolveByQueryCallbacks(Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
NameCompressInfo & aCompressInfo,
|
|
const Ip6::MessageInfo &aMessageInfo)
|
|
{
|
|
QueryTransaction *query = nullptr;
|
|
DnsQueryType queryType;
|
|
char name[Name::kMaxNameSize];
|
|
|
|
Error error = kErrorNone;
|
|
|
|
VerifyOrExit(mQuerySubscribe != nullptr, error = kErrorFailed);
|
|
|
|
queryType = GetQueryTypeAndName(aResponseHeader, aResponseMessage, name);
|
|
VerifyOrExit(queryType != kDnsQueryNone, error = kErrorNotImplemented);
|
|
|
|
query = NewQuery(aResponseHeader, aResponseMessage, aCompressInfo, aMessageInfo);
|
|
VerifyOrExit(query != nullptr, error = kErrorNoBufs);
|
|
|
|
mQuerySubscribe(mQueryCallbackContext, name);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Server::QueryTransaction *Server::NewQuery(const Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
const NameCompressInfo &aCompressInfo,
|
|
const Ip6::MessageInfo &aMessageInfo)
|
|
{
|
|
QueryTransaction *newQuery = nullptr;
|
|
|
|
for (QueryTransaction &query : mQueryTransactions)
|
|
{
|
|
if (query.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
query.Init(aResponseHeader, aResponseMessage, aCompressInfo, aMessageInfo, GetInstance());
|
|
ExitNow(newQuery = &query);
|
|
}
|
|
|
|
exit:
|
|
if (newQuery != nullptr)
|
|
{
|
|
ResetTimer();
|
|
}
|
|
|
|
return newQuery;
|
|
}
|
|
|
|
bool Server::CanAnswerQuery(const QueryTransaction & aQuery,
|
|
const char * aServiceFullName,
|
|
const otDnssdServiceInstanceInfo &aInstanceInfo)
|
|
{
|
|
char name[Name::kMaxNameSize];
|
|
DnsQueryType sdType;
|
|
bool canAnswer = false;
|
|
|
|
sdType = GetQueryTypeAndName(aQuery.GetResponseHeader(), aQuery.GetResponseMessage(), name);
|
|
|
|
switch (sdType)
|
|
{
|
|
case kDnsQueryBrowse:
|
|
canAnswer = StringMatch(name, aServiceFullName, kStringCaseInsensitiveMatch);
|
|
break;
|
|
case kDnsQueryResolve:
|
|
canAnswer = StringMatch(name, aInstanceInfo.mFullName, kStringCaseInsensitiveMatch);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return canAnswer;
|
|
}
|
|
|
|
bool Server::CanAnswerQuery(const Server::QueryTransaction &aQuery, const char *aHostFullName)
|
|
{
|
|
char name[Name::kMaxNameSize];
|
|
DnsQueryType sdType;
|
|
|
|
sdType = GetQueryTypeAndName(aQuery.GetResponseHeader(), aQuery.GetResponseMessage(), name);
|
|
return (sdType == kDnsQueryResolveHost) && StringMatch(name, aHostFullName, kStringCaseInsensitiveMatch);
|
|
}
|
|
|
|
void Server::AnswerQuery(QueryTransaction & aQuery,
|
|
const char * aServiceFullName,
|
|
const otDnssdServiceInstanceInfo &aInstanceInfo)
|
|
{
|
|
Header & responseHeader = aQuery.GetResponseHeader();
|
|
Message & responseMessage = aQuery.GetResponseMessage();
|
|
Error error = kErrorNone;
|
|
NameCompressInfo &compressInfo = aQuery.GetNameCompressInfo();
|
|
|
|
if (HasQuestion(aQuery.GetResponseHeader(), aQuery.GetResponseMessage(), aServiceFullName,
|
|
ResourceRecord::kTypePtr))
|
|
{
|
|
SuccessOrExit(error = AppendPtrRecord(responseMessage, aServiceFullName, aInstanceInfo.mFullName,
|
|
aInstanceInfo.mTtl, compressInfo));
|
|
IncResourceRecordCount(responseHeader, false);
|
|
}
|
|
|
|
for (uint8_t additional = 0; additional <= 1; additional++)
|
|
{
|
|
if (HasQuestion(aQuery.GetResponseHeader(), aQuery.GetResponseMessage(), aInstanceInfo.mFullName,
|
|
ResourceRecord::kTypeSrv) == !additional)
|
|
{
|
|
SuccessOrExit(error = AppendSrvRecord(responseMessage, aInstanceInfo.mFullName, aInstanceInfo.mHostName,
|
|
aInstanceInfo.mTtl, aInstanceInfo.mPriority, aInstanceInfo.mWeight,
|
|
aInstanceInfo.mPort, compressInfo));
|
|
IncResourceRecordCount(responseHeader, additional);
|
|
}
|
|
|
|
if (HasQuestion(aQuery.GetResponseHeader(), aQuery.GetResponseMessage(), aInstanceInfo.mFullName,
|
|
ResourceRecord::kTypeTxt) == !additional)
|
|
{
|
|
SuccessOrExit(error = AppendTxtRecord(responseMessage, aInstanceInfo.mFullName, aInstanceInfo.mTxtData,
|
|
aInstanceInfo.mTxtLength, aInstanceInfo.mTtl, compressInfo));
|
|
IncResourceRecordCount(responseHeader, additional);
|
|
}
|
|
|
|
if (HasQuestion(aQuery.GetResponseHeader(), aQuery.GetResponseMessage(), aInstanceInfo.mHostName,
|
|
ResourceRecord::kTypeAaaa) == !additional)
|
|
{
|
|
for (uint8_t i = 0; i < aInstanceInfo.mAddressNum; i++)
|
|
{
|
|
const Ip6::Address &address = AsCoreType(&aInstanceInfo.mAddresses[i]);
|
|
|
|
OT_ASSERT(!address.IsUnspecified() && !address.IsLinkLocal() && !address.IsMulticast() &&
|
|
!address.IsLoopback());
|
|
|
|
SuccessOrExit(error = AppendAaaaRecord(responseMessage, aInstanceInfo.mHostName, address,
|
|
aInstanceInfo.mTtl, compressInfo));
|
|
IncResourceRecordCount(responseHeader, additional);
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
FinalizeQuery(aQuery, error == kErrorNone ? Header::kResponseSuccess : Header::kResponseServerFailure);
|
|
ResetTimer();
|
|
}
|
|
|
|
void Server::AnswerQuery(QueryTransaction &aQuery, const char *aHostFullName, const otDnssdHostInfo &aHostInfo)
|
|
{
|
|
Header & responseHeader = aQuery.GetResponseHeader();
|
|
Message & responseMessage = aQuery.GetResponseMessage();
|
|
Error error = kErrorNone;
|
|
NameCompressInfo &compressInfo = aQuery.GetNameCompressInfo();
|
|
|
|
if (HasQuestion(aQuery.GetResponseHeader(), aQuery.GetResponseMessage(), aHostFullName, ResourceRecord::kTypeAaaa))
|
|
{
|
|
for (uint8_t i = 0; i < aHostInfo.mAddressNum; i++)
|
|
{
|
|
const Ip6::Address &address = AsCoreType(&aHostInfo.mAddresses[i]);
|
|
|
|
OT_ASSERT(!address.IsUnspecified() && !address.IsMulticast() && !address.IsLinkLocal() &&
|
|
!address.IsLoopback());
|
|
|
|
SuccessOrExit(error =
|
|
AppendAaaaRecord(responseMessage, aHostFullName, address, aHostInfo.mTtl, compressInfo));
|
|
IncResourceRecordCount(responseHeader, /* aAdditional */ false);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
FinalizeQuery(aQuery, error == kErrorNone ? Header::kResponseSuccess : Header::kResponseServerFailure);
|
|
ResetTimer();
|
|
}
|
|
|
|
void Server::SetQueryCallbacks(otDnssdQuerySubscribeCallback aSubscribe,
|
|
otDnssdQueryUnsubscribeCallback aUnsubscribe,
|
|
void * aContext)
|
|
{
|
|
OT_ASSERT((aSubscribe == nullptr) == (aUnsubscribe == nullptr));
|
|
|
|
mQuerySubscribe = aSubscribe;
|
|
mQueryUnsubscribe = aUnsubscribe;
|
|
mQueryCallbackContext = aContext;
|
|
}
|
|
|
|
void Server::HandleDiscoveredServiceInstance(const char * aServiceFullName,
|
|
const otDnssdServiceInstanceInfo &aInstanceInfo)
|
|
{
|
|
OT_ASSERT(StringEndsWith(aServiceFullName, Name::kLabelSeperatorChar));
|
|
OT_ASSERT(StringEndsWith(aInstanceInfo.mFullName, Name::kLabelSeperatorChar));
|
|
OT_ASSERT(StringEndsWith(aInstanceInfo.mHostName, Name::kLabelSeperatorChar));
|
|
|
|
for (QueryTransaction &query : mQueryTransactions)
|
|
{
|
|
if (query.IsValid() && CanAnswerQuery(query, aServiceFullName, aInstanceInfo))
|
|
{
|
|
AnswerQuery(query, aServiceFullName, aInstanceInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Server::HandleDiscoveredHost(const char *aHostFullName, const otDnssdHostInfo &aHostInfo)
|
|
{
|
|
OT_ASSERT(StringEndsWith(aHostFullName, Name::kLabelSeperatorChar));
|
|
|
|
for (QueryTransaction &query : mQueryTransactions)
|
|
{
|
|
if (query.IsValid() && CanAnswerQuery(query, aHostFullName))
|
|
{
|
|
AnswerQuery(query, aHostFullName, aHostInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
const otDnssdQuery *Server::GetNextQuery(const otDnssdQuery *aQuery) const
|
|
{
|
|
const QueryTransaction *cur = &mQueryTransactions[0];
|
|
const QueryTransaction *found = nullptr;
|
|
const QueryTransaction *query = static_cast<const QueryTransaction *>(aQuery);
|
|
|
|
if (aQuery != nullptr)
|
|
{
|
|
cur = query + 1;
|
|
}
|
|
|
|
for (; cur < GetArrayEnd(mQueryTransactions); cur++)
|
|
{
|
|
if (cur->IsValid())
|
|
{
|
|
found = cur;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return static_cast<const otDnssdQuery *>(found);
|
|
}
|
|
|
|
Server::DnsQueryType Server::GetQueryTypeAndName(const otDnssdQuery *aQuery, char (&aName)[Name::kMaxNameSize])
|
|
{
|
|
const QueryTransaction *query = static_cast<const QueryTransaction *>(aQuery);
|
|
|
|
OT_ASSERT(query->IsValid());
|
|
return GetQueryTypeAndName(query->GetResponseHeader(), query->GetResponseMessage(), aName);
|
|
}
|
|
|
|
Server::DnsQueryType Server::GetQueryTypeAndName(const Header & aHeader,
|
|
const Message &aMessage,
|
|
char (&aName)[Name::kMaxNameSize])
|
|
{
|
|
DnsQueryType sdType = kDnsQueryNone;
|
|
|
|
for (uint16_t i = 0, readOffset = sizeof(Header); i < aHeader.GetQuestionCount(); i++)
|
|
{
|
|
Question question;
|
|
|
|
IgnoreError(Name::ReadName(aMessage, readOffset, aName, sizeof(aName)));
|
|
IgnoreError(aMessage.Read(readOffset, question));
|
|
readOffset += sizeof(question);
|
|
|
|
switch (question.GetType())
|
|
{
|
|
case ResourceRecord::kTypePtr:
|
|
ExitNow(sdType = kDnsQueryBrowse);
|
|
case ResourceRecord::kTypeSrv:
|
|
case ResourceRecord::kTypeTxt:
|
|
ExitNow(sdType = kDnsQueryResolve);
|
|
}
|
|
}
|
|
|
|
for (uint16_t i = 0, readOffset = sizeof(Header); i < aHeader.GetQuestionCount(); i++)
|
|
{
|
|
Question question;
|
|
|
|
IgnoreError(Name::ReadName(aMessage, readOffset, aName, sizeof(aName)));
|
|
IgnoreError(aMessage.Read(readOffset, question));
|
|
readOffset += sizeof(question);
|
|
|
|
switch (question.GetType())
|
|
{
|
|
case ResourceRecord::kTypeAaaa:
|
|
case ResourceRecord::kTypeA:
|
|
ExitNow(sdType = kDnsQueryResolveHost);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return sdType;
|
|
}
|
|
|
|
bool Server::HasQuestion(const Header &aHeader, const Message &aMessage, const char *aName, uint16_t aQuestionType)
|
|
{
|
|
bool found = false;
|
|
|
|
for (uint16_t i = 0, readOffset = sizeof(Header); i < aHeader.GetQuestionCount(); i++)
|
|
{
|
|
Question question;
|
|
Error error;
|
|
|
|
error = Name::CompareName(aMessage, readOffset, aName);
|
|
IgnoreError(aMessage.Read(readOffset, question));
|
|
readOffset += sizeof(question);
|
|
|
|
if (error == kErrorNone && aQuestionType == question.GetType())
|
|
{
|
|
ExitNow(found = true);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return found;
|
|
}
|
|
|
|
void Server::HandleTimer(Timer &aTimer)
|
|
{
|
|
aTimer.Get<Server>().HandleTimer();
|
|
}
|
|
|
|
void Server::HandleTimer(void)
|
|
{
|
|
TimeMilli now = TimerMilli::GetNow();
|
|
|
|
for (QueryTransaction &query : mQueryTransactions)
|
|
{
|
|
TimeMilli expire;
|
|
|
|
if (!query.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
expire = query.GetStartTime() + kQueryTimeout;
|
|
if (expire <= now)
|
|
{
|
|
FinalizeQuery(query, Header::kResponseSuccess);
|
|
}
|
|
}
|
|
|
|
ResetTimer();
|
|
}
|
|
|
|
void Server::ResetTimer(void)
|
|
{
|
|
TimeMilli now = TimerMilli::GetNow();
|
|
TimeMilli nextExpire = now.GetDistantFuture();
|
|
|
|
for (QueryTransaction &query : mQueryTransactions)
|
|
{
|
|
TimeMilli expire;
|
|
|
|
if (!query.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
expire = query.GetStartTime() + kQueryTimeout;
|
|
if (expire <= now)
|
|
{
|
|
nextExpire = now;
|
|
}
|
|
else if (expire < nextExpire)
|
|
{
|
|
nextExpire = expire;
|
|
}
|
|
}
|
|
|
|
if (nextExpire < now.GetDistantFuture())
|
|
{
|
|
mTimer.FireAt(nextExpire);
|
|
}
|
|
else
|
|
{
|
|
mTimer.Stop();
|
|
}
|
|
}
|
|
|
|
void Server::FinalizeQuery(QueryTransaction &aQuery, Header::Response aResponseCode)
|
|
{
|
|
char name[Name::kMaxNameSize];
|
|
DnsQueryType sdType;
|
|
|
|
OT_ASSERT(mQueryUnsubscribe != nullptr);
|
|
|
|
sdType = GetQueryTypeAndName(aQuery.GetResponseHeader(), aQuery.GetResponseMessage(), name);
|
|
|
|
OT_ASSERT(sdType != kDnsQueryNone);
|
|
OT_UNUSED_VARIABLE(sdType);
|
|
|
|
mQueryUnsubscribe(mQueryCallbackContext, name);
|
|
aQuery.Finalize(aResponseCode, mSocket);
|
|
}
|
|
|
|
void Server::QueryTransaction::Init(const Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
const NameCompressInfo &aCompressInfo,
|
|
const Ip6::MessageInfo &aMessageInfo,
|
|
Instance & aInstance)
|
|
{
|
|
OT_ASSERT(mResponseMessage == nullptr);
|
|
|
|
InstanceLocatorInit::Init(aInstance);
|
|
mResponseHeader = aResponseHeader;
|
|
mResponseMessage = &aResponseMessage;
|
|
mCompressInfo = aCompressInfo;
|
|
mMessageInfo = aMessageInfo;
|
|
mStartTime = TimerMilli::GetNow();
|
|
}
|
|
|
|
void Server::QueryTransaction::Finalize(Header::Response aResponseMessage, Ip6::Udp::Socket &aSocket)
|
|
{
|
|
OT_ASSERT(mResponseMessage != nullptr);
|
|
|
|
Get<Server>().SendResponse(mResponseHeader, aResponseMessage, *mResponseMessage, mMessageInfo, aSocket);
|
|
mResponseMessage = nullptr;
|
|
}
|
|
|
|
void Server::UpdateResponseCounters(Header::Response aResponseCode)
|
|
{
|
|
switch (aResponseCode)
|
|
{
|
|
case UpdateHeader::kResponseSuccess:
|
|
++mCounters.mSuccessResponse;
|
|
break;
|
|
case UpdateHeader::kResponseServerFailure:
|
|
++mCounters.mServerFailureResponse;
|
|
break;
|
|
case UpdateHeader::kResponseFormatError:
|
|
++mCounters.mFormatErrorResponse;
|
|
break;
|
|
case UpdateHeader::kResponseNameError:
|
|
++mCounters.mNameErrorResponse;
|
|
break;
|
|
case UpdateHeader::kResponseNotImplemented:
|
|
++mCounters.mNotImplementedResponse;
|
|
break;
|
|
default:
|
|
++mCounters.mOtherResponse;
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // namespace ServiceDiscovery
|
|
} // namespace Dns
|
|
} // namespace ot
|
|
|
|
#endif // OPENTHREAD_CONFIG_DNS_SERVER_ENABLE
|