[dnssd] implement DNS-SD Discovery Proxy (#6191)

This commit implements DNS-SD Discovery Proxy.
- Implemented DNS-SD Discovery Proxy functionalities - OT part
- Fixed an memory issue of NameCompressInfo (introduced in #6155 , but
  somehow revealed by this commit)

Some implementation details:
- Discovery Proxy subscribes to services/service instances/hosts
  through callbacks set by a public OT API. It is up to the platform
  mDNS implementation to collect service instance/host information and
  notify OT via a public OT API.
- Discovery Proxy can handle DNS browsing of one service or DNS
  resolving of one service instance/host. We leave browsing multiple
  services or resolving multiple service instances/hosts for future
  enhancements if necessary.
This commit is contained in:
Simon Lin
2021-04-09 09:21:23 +08:00
committed by GitHub
parent e43cd4bb07
commit 5a39566f81
21 changed files with 1184 additions and 279 deletions
+1 -1
View File
@@ -63,7 +63,7 @@ jobs:
export ASAN_OPTIONS=symbolize=1
export DISTCHECK_CONFIGURE_FLAGS= CPPFLAGS=-DOPENTHREAD_SIMULATION_VIRTUAL_TIME=1
./bootstrap
make -f examples/Makefile-simulation distcheck
VERBOSE=1 make -f examples/Makefile-simulation distcheck
cli-ftd-otns:
runs-on: ubuntu-20.04
+1
View File
@@ -172,6 +172,7 @@ LOCAL_SRC_FILES := \
src/core/api/dataset_updater_api.cpp \
src/core/api/diags_api.cpp \
src/core/api/dns_api.cpp \
src/core/api/dns_server_api.cpp \
src/core/api/entropy_api.cpp \
src/core/api/error_api.cpp \
src/core/api/heap_api.cpp \
+1
View File
@@ -54,6 +54,7 @@
* @{
*
* @defgroup api-dns DNSv6
* @defgroup api-dnssd-server DNS-SD Server
* @defgroup api-icmp6 ICMPv6
* @defgroup api-ip6 IPv6
* @defgroup api-srp SRP
+1
View File
@@ -54,6 +54,7 @@ openthread_headers = \
openthread/diag.h \
openthread/dns.h \
openthread/dns_client.h \
openthread/dnssd_server.h \
openthread/entropy.h \
openthread/error.h \
openthread/heap.h \
+1
View File
@@ -75,6 +75,7 @@ source_set("openthread") {
"diag.h",
"dns.h",
"dns_client.h",
"dnssd_server.h",
"entropy.h",
"error.h",
"heap.h",
+190
View File
@@ -0,0 +1,190 @@
/*
* 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
* @brief
* This file defines the DNS-SD server APIs.
*/
#ifndef OPENTHREAD_DNSSD_SERVER_H_
#define OPENTHREAD_DNSSD_SERVER_H_
#include <stdint.h>
#include <openthread/error.h>
#include <openthread/ip6.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup api-dnssd-server
*
* @brief
* This module includes APIs for DNS-SD server.
*
* @{
*
*/
/**
* This function is called when a DNS-SD query subscribes one of:
* 1. a service name.
* 2. a service instance name.
* 3. a host name.
*
* The DNS-SD query implementation is responsible for identifying what @p aFullName is.
* If @p aFullName is a service name or service instance name, the DNS-SD query implementation should discover
* corresponding service instance information and notify the DNS-SD server using
* `otDnssdQueryHandleDiscoveredServiceInstance`.
* If @p aFullName is a host name, the DNS-SD query implementation should
* discover the host information and notify the DNS-SD server using `otDnssdQueryHandleDiscoveredHost`.
*
* @note There can be multiple subscription to the same name. DNS-SD query implementation should record the number of
* active subscriptions and stop notifying when there is no active subscription for @p aFullName.
*
* @param[in] aContext A pointer to the application-specific context.
* @param[in] aFullName The null-terminated full service name (e.g. "_ipps._tcp.default.service.arpa."),
* or full service instance name (e.g. "OpenThread._ipps._tcp.default.service.arpa."),
* or full host name (e.g. "ot-host.default.service.arpa.").
*
* @sa otDnssdQueryHandleDiscoveredServiceInstance
* @sa otDnssdQueryHandleDiscoveredHost
*
*/
typedef void (*otDnssdQuerySubscribeCallback)(void *aContext, const char *aFullName);
/**
* This function is called when a DNS-SD query unsubscribes one of:
* 1. a service name.
* 2. a service instance name.
* 3. a host name.
*
* The DNS-SD query implementation is responsible for identifying what @p aFullName is.
*
* @note There can be multiple subscription to the same name. DNS-SD query implementation should record the number of
* active subscriptions and stop notifying when there is no active subscription for @p aFullName.
*
* @param[in] aContext A pointer to the application-specific context.
* @param[in] aFullName The null-terminated full service name (e.g. "_ipps._tcp.default.service.arpa."), or
* full service instance name (e.g. "OpenThread._ipps._tcp.default.service.arpa.").
*
*/
typedef void (*otDnssdQueryUnsubscribeCallback)(void *aContext, const char *aFullName);
/**
* This structure represents information of a discovered service instance for a DNS-SD query.
*
*/
typedef struct otDnssdServiceInstanceInfo
{
const char * mFullName; ///< Full instance name (e.g. "OpenThread._ipps._tcp.default.service.arpa.").
const char * mHostName; ///< Host name (e.g. "ot-host.default.service.arpa.").
uint8_t mAddressNum; ///< Number of host IPv6 addresses.
const otIp6Address *mAddresses; ///< Host IPv6 addresses.
uint16_t mPort; ///< Service port.
uint16_t mPriority; ///< Service priority.
uint16_t mWeight; ///< Service weight.
uint16_t mTxtLength; ///< Service TXT RDATA length.
const uint8_t * mTxtData; ///< Service TXT RDATA.
uint32_t mTtl; ///< Service TTL (in seconds).
} otDnssdServiceInstanceInfo;
/**
* This structure represents information of a discovered host for a DNS-SD query.
*
*/
typedef struct otDnssdHostInfo
{
uint8_t mAddressNum; ///< Number of host IPv6 addresses.
const otIp6Address *mAddresses; ///< Host IPv6 addresses.
uint32_t mTtl; ///< Service TTL (in seconds).
} otDnssdHostInfo;
/**
* This function sets DNS-SD server query callbacks.
*
* The DNS-SD server calls @p aSubscribe to subscribe to a service or service instance to resolve a DNS-SD query and @p
* aUnsubscribe to unsubscribe when the query is resolved or timeout.
*
* @note @p aSubscribe and @p aUnsubscribe must be both set or unset.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aSubscribe A pointer to the callback function to subscribe a service or service instance.
* @param[in] aUnsubscribe A pointer to the callback function to unsubscribe a service or service instance.
* @param[in] aContext A pointer to the application-specific context.
*
*/
void otDnssdQuerySetCallbacks(otInstance * aInstance,
otDnssdQuerySubscribeCallback aSubscribe,
otDnssdQueryUnsubscribeCallback aUnsubscribe,
void * aContext);
/**
* This function notifies a discovered service instance.
*
* The external query resolver (e.g. Discovery Proxy) should call this function to notify OpenThread core of the
* subscribed services or service instances.
*
* @note @p aInstanceInfo must not contain unspecified or link-local or loop-back or multicast IP addresses.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aServiceFullName The null-terminated full service name.
* @param[in] aInstanceInfo A pointer to the discovered service instance information.
*
*/
void otDnssdQueryHandleDiscoveredServiceInstance(otInstance * aInstance,
const char * aServiceFullName,
otDnssdServiceInstanceInfo *aInstanceInfo);
/**
* This function notifies a discovered host.
*
* The external query resolver (e.g. Discovery Proxy) should call this function to notify OpenThread core of the
* subscribed hosts.
*
* @note @p aHostInfo must not contain unspecified or link-local or loop-back or multicast IP addresses.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aHostFullName The null-terminated full host name.
* @param[in] aHostInfo A pointer to the discovered service instance information.
*
*/
void otDnssdQueryHandleDiscoveredHost(otInstance *aInstance, const char *aHostFullName, otDnssdHostInfo *aHostInfo);
/**
* @}
*
*/
#ifdef __cplusplus
} // extern "C"
#endif
#endif // OPENTHREAD_DNSSD_SERVER_H_
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (96)
#define OPENTHREAD_API_VERSION (97)
/**
* @addtogroup api-instance
+1 -1
View File
@@ -263,7 +263,7 @@ do_build_otbr_docker()
echo "Building OTBR Docker ..."
local otdir
local otbrdir
local otbr_options="-DOT_DNSSD_SERVER=ON -DOT_DNS_CLIENT=ON -DOT_SRP_CLIENT=ON -DOT_SLAAC=ON -DOT_DUA=ON -DOT_MLR=ON -DOT_COVERAGE=ON -DOTBR_REST=OFF -DOTBR_WEB=OFF -DOTBR_DUA_ROUTING=ON"
local otbr_options="-DOTBR_DNSSD_DISCOVERY_PROXY=ON -DOT_DNS_CLIENT=ON -DOT_SRP_CLIENT=ON -DOT_SLAAC=ON -DOT_DUA=ON -DOT_MLR=ON -DOT_COVERAGE=ON -DOTBR_REST=OFF -DOTBR_WEB=OFF -DOTBR_DUA_ROUTING=ON"
local otbr_docker_image=${OTBR_DOCKER_IMAGE:-otbr-ot12-backbone-ci}
otbrdir=$(mktemp -d -t otbr_XXXXXX)
+1 -2
View File
@@ -1538,10 +1538,9 @@ void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressRes
OutputFormat(" TTL:%u ", ttl);
index++;
}
OutputLine("");
}
OutputLine("");
OutputResult(aError);
}
+1
View File
@@ -307,6 +307,7 @@ openthread_core_files = [
"api/dataset_updater_api.cpp",
"api/diags_api.cpp",
"api/dns_api.cpp",
"api/dns_server_api.cpp",
"api/entropy_api.cpp",
"api/error_api.cpp",
"api/heap_api.cpp",
+1
View File
@@ -47,6 +47,7 @@ set(COMMON_SOURCES
api/dataset_updater_api.cpp
api/diags_api.cpp
api/dns_api.cpp
api/dns_server_api.cpp
api/entropy_api.cpp
api/error_api.cpp
api/heap_api.cpp
+1
View File
@@ -124,6 +124,7 @@ SOURCES_COMMON = \
api/dataset_updater_api.cpp \
api/diags_api.cpp \
api/dns_api.cpp \
api/dns_server_api.cpp \
api/entropy_api.cpp \
api/error_api.cpp \
api/heap_api.cpp \
+76
View File
@@ -0,0 +1,76 @@
/*
* 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 APIs.
*/
#include "openthread-core-config.h"
#include "common/instance.hpp"
#include "net/dns_types.hpp"
#include "net/dnssd_server.hpp"
using namespace ot;
#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
void otDnssdQuerySetCallbacks(otInstance * aInstance,
otDnssdQuerySubscribeCallback aSubscribe,
otDnssdQueryUnsubscribeCallback aUnsubscribe,
void * aContext)
{
Instance &instance = *static_cast<Instance *>(aInstance);
instance.Get<Dns::ServiceDiscovery::Server>().SetQueryCallbacks(aSubscribe, aUnsubscribe, aContext);
}
void otDnssdQueryHandleDiscoveredServiceInstance(otInstance * aInstance,
const char * aServiceFullName,
otDnssdServiceInstanceInfo *aInstanceInfo)
{
Instance &instance = *static_cast<Instance *>(aInstance);
OT_ASSERT(aServiceFullName != nullptr);
OT_ASSERT(aInstanceInfo != nullptr);
instance.Get<Dns::ServiceDiscovery::Server>().HandleDiscoveredServiceInstance(aServiceFullName, *aInstanceInfo);
}
void otDnssdQueryHandleDiscoveredHost(otInstance *aInstance, const char *aHostFullName, otDnssdHostInfo *aHostInfo)
{
Instance &instance = *static_cast<Instance *>(aInstance);
OT_ASSERT(aHostFullName != nullptr);
OT_ASSERT(aHostInfo != nullptr);
instance.Get<Dns::ServiceDiscovery::Server>().HandleDiscoveredHost(aHostFullName, *aHostInfo);
}
#endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
+7
View File
@@ -65,6 +65,13 @@ const char *StringFind(const char *aString, char aChar)
return ret;
}
bool StringEndsWith(const char *aString, char aChar)
{
size_t len = strlen(aString);
return len > 0 && aString[len - 1] == aChar;
}
Error StringBase::Write(char *aBuffer, uint16_t aSize, uint16_t &aLength, const char *aFormat, va_list aArgs)
{
Error error = kErrorNone;
+12
View File
@@ -78,6 +78,18 @@ uint16_t StringLength(const char *aString, uint16_t aMaxLength);
*/
const char *StringFind(const char *aString, char aChar);
/**
* This function checks whether a null-terminated string ends with a given character.
*
* @param[in] aString A pointer to the string.
* @param[in] aChar A char to check.
*
* @retval TRUE If @p aString ends with character @p aChar.
* @retval FALSE If @p aString does not end with character @p aChar.
*
*/
bool StringEndsWith(const char *aString, char aChar);
/**
* This class defines the base class for `String`.
*
+10
View File
@@ -55,4 +55,14 @@
#define OPENTHREAD_CONFIG_DNSSD_SERVER_PORT 53
#endif
/**
* @def OPENTHREAD_CONFIG_DNSSD_QUERY_TIMEOUT
*
* Specifies the default wait time that DNS-SD Server waits for a query response (e.g. from Discovery Proxy).
*
*/
#ifndef OPENTHREAD_CONFIG_DNSSD_QUERY_TIMEOUT
#define OPENTHREAD_CONFIG_DNSSD_QUERY_TIMEOUT 6000
#endif
#endif // CONFIG_DNSSD_SERVER_H_
+590 -158
View File
@@ -56,6 +56,10 @@ 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)
{
}
@@ -70,11 +74,26 @@ Error Server::Start(void)
exit:
otLogInfoDns("[server] 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());
otLogInfoDns("[server] stopped");
}
@@ -87,40 +106,32 @@ void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessa
void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
Error error = kErrorNone;
Header requestHeader;
Message *responseMessage = nullptr;
Error error = kErrorNone;
Header requestHeader;
SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), requestHeader));
VerifyOrExit(requestHeader.GetType() == Header::kTypeQuery, error = kErrorDrop);
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)));
// ProcessQuery is assumed to always prepare the response DNS message and header properly even in the case of system
// failures (e.g. no more buffers).
ProcessQuery(aMessage, *responseMessage, requestHeader);
error = mSocket.SendTo(*responseMessage, aMessageInfo);
exit:
FreeMessageOnError(responseMessage, error);
}
void Server::ProcessQuery(Message &aMessage, Message &aResponse, const Header &aRequestHeader)
{
Header responseHeader;
uint16_t readOffset;
Question question;
char name[Dns::Name::kMaxNameSize];
NameCompressInfo compressInfo(kDefaultDomainName);
Header::Response response = Header::Response::kResponseSuccess;
Error error = kErrorNone;
uint8_t resolveAdditional = kResolveAdditionalAll;
// Setup initial DNS response header
responseHeader.Clear();
responseHeader.SetType(Header::kTypeResponse);
@@ -132,6 +143,75 @@ void Server::ProcessQuery(Message &aMessage, Message &aResponse, const Header &a
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 &&
kErrorNone == ResolveByQueryCallbacks(responseHeader, *responseMessage, compressInfo, aMessageInfo))
{
resolveByQueryCallbacks = true;
}
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)
{
otLogWarnDns("[server] 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)
{
otLogWarnDns("[server] failed to send DNS-SD reply: %s", otThreadErrorToString(error));
}
else
{
otLogInfoDns("[server] send DNS-SD reply: %s, RCODE=%d", otThreadErrorToString(error), 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
@@ -139,9 +219,9 @@ void Server::ProcessQuery(Message &aMessage, Message &aResponse, const Header &a
{
NameComponentsOffsetInfo nameComponentsOffsetInfo;
VerifyOrExit(kErrorNone == Dns::Name::ReadName(aMessage, readOffset, name, sizeof(name)),
VerifyOrExit(kErrorNone == Name::ReadName(aRequestMessage, readOffset, name, sizeof(name)),
response = Header::kResponseFormatError);
VerifyOrExit(kErrorNone == aMessage.Read(readOffset, question), response = Header::kResponseFormatError);
VerifyOrExit(kErrorNone == aRequestMessage.Read(readOffset, question), response = Header::kResponseFormatError);
readOffset += sizeof(question);
uint16_t qtype = question.GetType();
@@ -150,7 +230,7 @@ void Server::ProcessQuery(Message &aMessage, Message &aResponse, const Header &a
qtype == ResourceRecord::kTypeTxt || qtype == ResourceRecord::kTypeAaaa,
response = Header::kResponseNotImplemented);
VerifyOrExit(kErrorNone == FindNameComponents(name, compressInfo.GetDomainName(), nameComponentsOffsetInfo),
VerifyOrExit(kErrorNone == FindNameComponents(name, aCompressInfo.GetDomainName(), nameComponentsOffsetInfo),
response = Header::kResponseNameError);
switch (question.GetType())
@@ -160,94 +240,24 @@ void Server::ProcessQuery(Message &aMessage, Message &aResponse, const Header &a
break;
case ResourceRecord::kTypeSrv:
VerifyOrExit(nameComponentsOffsetInfo.IsServiceInstanceName(), response = Header::kResponseNameError);
resolveAdditional &= ~kResolveAdditionalSrv;
break;
case ResourceRecord::kTypeTxt:
VerifyOrExit(nameComponentsOffsetInfo.IsServiceInstanceName(), response = Header::kResponseNameError);
resolveAdditional &= ~kResolveAdditionalTxt;
break;
case ResourceRecord::kTypeAaaa:
VerifyOrExit(nameComponentsOffsetInfo.IsHostName(), response = Header::kResponseNameError);
resolveAdditional &= ~kResolveAdditionalAaaa;
break;
default:
ExitNow(response = Header::kResponseNotImplemented);
}
SuccessOrExit(error = AppendQuestion(name, question, aResponse, compressInfo));
}
responseHeader.SetQuestionCount(aRequestHeader.GetQuestionCount());
// Answer the questions
readOffset = sizeof(Header);
for (uint16_t i = 0; i < aRequestHeader.GetQuestionCount(); i++)
{
uint8_t resolveKind = kResolveAnswer;
IgnoreError(Dns::Name::ReadName(aMessage, readOffset, name, sizeof(name)));
IgnoreError(aMessage.Read(readOffset, question));
readOffset += sizeof(question);
response = ResolveQuestion(name, question, responseHeader, aResponse, resolveKind, compressInfo);
otLogInfoDns("[server] ANSWER: TRANSACTION=0x%04x, QUESTION=[%s %d %d], RCODE=%d",
aRequestHeader.GetMessageId(), name, question.GetClass(), question.GetType(), response);
}
// Answer the questions with additional RRs if required
VerifyOrExit(resolveAdditional != kResolveNone);
readOffset = sizeof(Header);
for (uint16_t i = 0; i < aRequestHeader.GetQuestionCount(); i++)
{
IgnoreError(Dns::Name::ReadName(aMessage, readOffset, name, sizeof(name)));
IgnoreError(aMessage.Read(readOffset, question));
readOffset += sizeof(question);
VerifyOrExit(Header::kResponseServerFailure !=
ResolveQuestion(name, question, responseHeader, aResponse, resolveAdditional, compressInfo),
VerifyOrExit(AppendQuestion(name, question, aResponseMessage, aCompressInfo) == kErrorNone,
response = Header::kResponseServerFailure);
otLogInfoDns("[server] ADDITIONAL: TRANSACTION=0x%04x, QUESTION=[%s %d %d], RCODE=%d",
aRequestHeader.GetMessageId(), name, question.GetClass(), question.GetType(), response);
}
aResponseHeader.SetQuestionCount(aRequestHeader.GetQuestionCount());
exit:
response = (error == kErrorNone) ? response : Header::Response::kResponseServerFailure;
if (response == Header::Response::kResponseServerFailure)
{
otLogWarnDns("[server] failed to handle DNS query due to server failure");
responseHeader.SetQuestionCount(0);
responseHeader.SetAnswerCount(0);
responseHeader.SetAdditionalRecordCount(0);
IgnoreError(aResponse.SetLength(sizeof(Header)));
}
responseHeader.SetResponseCode(response);
aResponse.Write(0, responseHeader);
}
Header::Response Server::ResolveQuestion(const char * aName,
const Question & aQuestion,
Header & aResponseHeader,
Message & aResponseMessage,
uint8_t aResolveKind,
NameCompressInfo &aCompressInfo)
{
OT_UNUSED_VARIABLE(aName);
OT_UNUSED_VARIABLE(aQuestion);
OT_UNUSED_VARIABLE(aResponseHeader);
OT_UNUSED_VARIABLE(aResponseMessage);
OT_UNUSED_VARIABLE(aCompressInfo);
Header::Response response = Header::kResponseNameError;
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
response = ResolveQuestionBySrp(aName, aQuestion, aResponseHeader, aResponseMessage, aResolveKind, aCompressInfo);
#endif
return response;
}
@@ -363,11 +373,11 @@ exit:
Error Server::AppendServiceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo)
{
Error error;
uint16_t serviceCompressOffset = aCompressInfo.GetServiceNameOffset(aName);
uint16_t serviceCompressOffset = aCompressInfo.GetServiceNameOffset(aMessage, aName);
if (serviceCompressOffset != NameCompressInfo::kUnknownOffset)
{
error = Dns::Name::AppendPointerLabel(serviceCompressOffset, aMessage);
error = Name::AppendPointerLabel(serviceCompressOffset, aMessage);
}
else
{
@@ -376,17 +386,17 @@ Error Server::AppendServiceName(Message &aMessage, const char *aName, NameCompre
uint16_t domainCompressOffset = aCompressInfo.GetDomainNameOffset();
serviceCompressOffset = aMessage.GetLength();
aCompressInfo.SetServiceNameOffset(serviceCompressOffset, aName);
aCompressInfo.SetServiceNameOffset(serviceCompressOffset);
if (domainCompressOffset == NameCompressInfo::kUnknownOffset)
{
aCompressInfo.SetDomainNameOffset(serviceCompressOffset + domainStart);
error = Dns::Name::AppendName(aName, aMessage);
error = Name::AppendName(aName, aMessage);
}
else
{
SuccessOrExit(error = Dns::Name::AppendMultipleLabels(aName, domainStart, aMessage));
error = Dns::Name::AppendPointerLabel(domainCompressOffset, aMessage);
SuccessOrExit(error = Name::AppendMultipleLabels(aName, domainStart, aMessage));
error = Name::AppendPointerLabel(domainCompressOffset, aMessage);
}
}
@@ -398,11 +408,11 @@ Error Server::AppendInstanceName(Message &aMessage, const char *aName, NameCompr
{
Error error;
uint16_t instanceCompressOffset = aCompressInfo.GetInstanceNameOffset(aName);
uint16_t instanceCompressOffset = aCompressInfo.GetInstanceNameOffset(aMessage, aName);
if (instanceCompressOffset != NameCompressInfo::kUnknownOffset)
{
error = Dns::Name::AppendPointerLabel(instanceCompressOffset, aMessage);
error = Name::AppendPointerLabel(instanceCompressOffset, aMessage);
}
else
{
@@ -411,27 +421,49 @@ Error Server::AppendInstanceName(Message &aMessage, const char *aName, NameCompr
IgnoreError(FindNameComponents(aName, aCompressInfo.GetDomainName(), nameComponentsInfo));
OT_ASSERT(nameComponentsInfo.IsServiceInstanceName());
aCompressInfo.SetInstanceNameOffset(aMessage.GetLength(), aName);
aCompressInfo.SetInstanceNameOffset(aMessage.GetLength());
// Append the instance name as one label
SuccessOrExit(error = Dns::Name::AppendLabel(aName, nameComponentsInfo.mServiceOffset - 1, aMessage));
SuccessOrExit(error = Name::AppendLabel(aName, nameComponentsInfo.mServiceOffset - 1, aMessage));
{
const char *serviceName = aName + nameComponentsInfo.mServiceOffset;
uint16_t serviceCompressOffset = aCompressInfo.GetServiceNameOffset(serviceName);
uint16_t serviceCompressOffset = aCompressInfo.GetServiceNameOffset(aMessage, serviceName);
if (serviceCompressOffset != NameCompressInfo::kUnknownOffset)
{
error = Dns::Name::AppendPointerLabel(serviceCompressOffset, aMessage);
error = Name::AppendPointerLabel(serviceCompressOffset, aMessage);
}
else
{
aCompressInfo.SetServiceNameOffset(aMessage.GetLength(), serviceName);
error = Dns::Name::AppendName(serviceName, aMessage);
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;
SuccessOrExit(error = AppendInstanceName(aMessage, aInstanceName, aCompressInfo));
txtRecord.Init();
txtRecord.SetTtl(aTtl);
txtRecord.SetLength(aTxtLength);
SuccessOrExit(error = aMessage.Append(txtRecord));
error = aMessage.AppendBytes(aTxtData, aTxtLength);
exit:
return error;
}
@@ -439,11 +471,11 @@ exit:
Error Server::AppendHostName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo)
{
Error error;
uint16_t hostCompressOffset = aCompressInfo.GetHostNameOffset(aName);
uint16_t hostCompressOffset = aCompressInfo.GetHostNameOffset(aMessage, aName);
if (hostCompressOffset != NameCompressInfo::kUnknownOffset)
{
error = Dns::Name::AppendPointerLabel(hostCompressOffset, aMessage);
error = Name::AppendPointerLabel(hostCompressOffset, aMessage);
}
else
{
@@ -452,17 +484,17 @@ Error Server::AppendHostName(Message &aMessage, const char *aName, NameCompressI
uint16_t domainCompressOffset = aCompressInfo.GetDomainNameOffset();
hostCompressOffset = aMessage.GetLength();
aCompressInfo.SetHostNameOffset(hostCompressOffset, aName);
aCompressInfo.SetHostNameOffset(hostCompressOffset);
if (domainCompressOffset == NameCompressInfo::kUnknownOffset)
{
aCompressInfo.SetDomainNameOffset(hostCompressOffset + domainStart);
error = Dns::Name::AppendName(aName, aMessage);
error = Name::AppendName(aName, aMessage);
}
else
{
SuccessOrExit(error = Dns::Name::AppendMultipleLabels(aName, domainStart, aMessage));
error = Dns::Name::AppendPointerLabel(domainCompressOffset, aMessage);
SuccessOrExit(error = Name::AppendMultipleLabels(aName, domainStart, aMessage));
error = Name::AppendPointerLabel(domainCompressOffset, aMessage);
}
}
@@ -489,7 +521,7 @@ Error Server::FindNameComponents(const char *aName, const char *aDomain, NameCom
Error error = kErrorNone;
uint8_t labelBegin, labelEnd;
VerifyOrExit(Dns::Name::IsSubDomainOf(aName, aDomain), error = kErrorInvalidArgs);
VerifyOrExit(Name::IsSubDomainOf(aName, aDomain), error = kErrorInvalidArgs);
labelBegin = nameLen - domainLen;
aInfo.mDomainOffset = labelBegin;
@@ -555,12 +587,57 @@ exit:
}
#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);
otLogInfoDns("[server] 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);
otLogInfoDns("[server] 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,
uint8_t aResolveKind,
NameCompressInfo &aCompressInfo)
NameCompressInfo &aCompressInfo,
bool aAdditional)
{
Error error = kErrorNone;
const Srp::Server::Host *host = nullptr;
@@ -593,38 +670,41 @@ Header::Response Server::ResolveQuestionBySrp(const char * aName,
needAdditionalAaaaRecord = true;
}
if (aResolveKind == kResolveAnswer && ptrQueryMatched)
if (!aAdditional && ptrQueryMatched)
{
SuccessOrExit(
error = AppendPtrRecord(aResponseMessage, aName, instanceName, instanceTtl, aCompressInfo));
IncResourceRecordCount(aResponseHeader, aResolveKind != kResolveAnswer);
response = Header::Response::kResponseSuccess;
IncResourceRecordCount(aResponseHeader, aAdditional);
response = Header::kResponseSuccess;
}
if ((aResolveKind == kResolveAnswer && srvQueryMatched) ||
((aResolveKind & kResolveAdditionalSrv) && ptrQueryMatched))
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, aResolveKind != kResolveAnswer);
response = Header::Response::kResponseSuccess;
IncResourceRecordCount(aResponseHeader, aAdditional);
response = Header::kResponseSuccess;
}
if ((aResolveKind == kResolveAnswer && txtQueryMatched) ||
((aResolveKind & kResolveAdditionalTxt) && ptrQueryMatched))
if ((!aAdditional && txtQueryMatched) ||
(aAdditional && ptrQueryMatched &&
!HasQuestion(aResponseHeader, aResponseMessage, instanceName, ResourceRecord::kTypeTxt)))
{
SuccessOrExit(
error = AppendTxtRecord(aResponseMessage, instanceName, *service, instanceTtl, aCompressInfo));
IncResourceRecordCount(aResponseHeader, aResolveKind != kResolveAnswer);
response = Header::Response::kResponseSuccess;
SuccessOrExit(error = AppendTxtRecord(aResponseMessage, instanceName, service->GetTxtData(),
service->GetTxtDataLength(), instanceTtl, aCompressInfo));
IncResourceRecordCount(aResponseHeader, aAdditional);
response = Header::kResponseSuccess;
}
}
}
// Handle AAAA query
if ((aResolveKind == kResolveAnswer && qtype == ResourceRecord::kTypeAaaa && host->Matches(aName)) ||
((aResolveKind & kResolveAdditionalAaaa) && needAdditionalAaaaRecord))
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);
@@ -633,15 +713,15 @@ Header::Response Server::ResolveQuestionBySrp(const char * aName,
for (uint8_t i = 0; i < addrNum; i++)
{
SuccessOrExit(error = AppendAaaaRecord(aResponseMessage, hostName, addrs[i], hostTtl, aCompressInfo));
IncResourceRecordCount(aResponseHeader, aResolveKind != kResolveAnswer);
IncResourceRecordCount(aResponseHeader, aAdditional);
}
response = Header::Response::kResponseSuccess;
response = Header::kResponseSuccess;
}
}
exit:
return error == kErrorNone ? response : Header::Response::kResponseServerFailure;
return error == kErrorNone ? response : Header::kResponseServerFailure;
}
const Srp::Server::Host *Server::GetNextSrpHost(const Srp::Server::Host *aHost)
@@ -668,34 +748,386 @@ const Srp::Server::Service *Server::GetNextSrpService(const Srp::Server::Host &
return service;
}
#endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
Error Server::AppendTxtRecord(Message & aMessage,
const char * aInstanceName,
const Srp::Server::Service &aService,
uint32_t aTtl,
NameCompressInfo & aCompressInfo)
Error Server::ResolveByQueryCallbacks(Header & aResponseHeader,
Message & aResponseMessage,
NameCompressInfo & aCompressInfo,
const Ip6::MessageInfo &aMessageInfo)
{
Error error;
uint16_t recordOffset;
TxtRecord txtRecord;
QueryTransaction *query = nullptr;
DnsQueryType queryType;
char name[Name::kMaxNameSize];
SuccessOrExit(error = AppendInstanceName(aMessage, aInstanceName, aCompressInfo));
Error error = kErrorNone;
recordOffset = aMessage.GetLength();
SuccessOrExit(error = aMessage.SetLength(recordOffset + sizeof(txtRecord)));
VerifyOrExit(mQuerySubscribe != nullptr, error = kErrorFailed);
SuccessOrExit(error = aMessage.AppendBytes(aService.GetTxtData(), aService.GetTxtDataLength()));
queryType = GetQueryType(aResponseHeader, aResponseMessage, name);
VerifyOrExit(queryType != kDnsQueryNone, error = kErrorNotImplemented);
txtRecord.Init();
txtRecord.SetTtl(aTtl);
txtRecord.SetLength(aMessage.GetLength() - (recordOffset + sizeof(ResourceRecord)));
query = NewQuery(aResponseHeader, aResponseMessage, aCompressInfo, aMessageInfo);
VerifyOrExit(query != nullptr, error = kErrorNoBufs);
aMessage.Write(recordOffset, txtRecord);
mQuerySubscribe(mQueryCallbackContext, name);
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
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);
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 = GetQueryType(aQuery.GetResponseHeader(), aQuery.GetResponseMessage(), name);
switch (sdType)
{
case kDnsQueryBrowse:
canAnswer = (strcmp(name, aServiceFullName) == 0);
break;
case kDnsQueryResolve:
canAnswer = (strcmp(name, aInstanceInfo.mFullName) == 0);
break;
default:
break;
}
return canAnswer;
}
bool Server::CanAnswerQuery(const Server::QueryTransaction &aQuery, const char *aHostFullName)
{
char name[Name::kMaxNameSize];
DnsQueryType sdType;
sdType = GetQueryType(aQuery.GetResponseHeader(), aQuery.GetResponseMessage(), name);
return (sdType == kDnsQueryResolveHost) && (strcmp(name, aHostFullName) == 0);
}
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 = static_cast<const Ip6::Address &>(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 = static_cast<const Ip6::Address &>(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);
}
}
}
Server::DnsQueryType Server::GetQueryType(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 = GetQueryType(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)
{
OT_ASSERT(mResponseMessage == nullptr);
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);
SendResponse(mResponseHeader, aResponseMessage, *mResponseMessage, mMessageInfo, aSocket);
mResponseMessage = nullptr;
}
} // namespace ServiceDiscovery
} // namespace Dns
+187 -108
View File
@@ -33,7 +33,7 @@
#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
#include <openthread/dns.h>
#include <openthread/dnssd_server.h>
#include "common/message.hpp"
#include "common/non_copyable.hpp"
@@ -82,21 +82,42 @@ public:
*/
void Stop(void);
/**
* This method sets DNS-SD query callbacks.
*
* @param[in] aSubscribe A pointer to the callback function to subscribe a service or service instance.
* @param[in] aUnsubscribe A pointer to the callback function to unsubscribe a service or service instance.
* @param[in] aContext A pointer to the application-specific context.
*
*/
void SetQueryCallbacks(otDnssdQuerySubscribeCallback aSubscribe,
otDnssdQueryUnsubscribeCallback aUnsubscribe,
void * aContext);
/**
* This method notifies a discovered service instance.
*
* @param[in] aServiceFullName The null-terminated full service name.
* @param[in] aInstanceInfo A pointer to the discovered service instance information.
*
*/
void HandleDiscoveredServiceInstance(const char *aServiceFullName, const otDnssdServiceInstanceInfo &aInstanceInfo);
/**
* This method notifies a discovered host.
*
* @param[in] aHostFullName The null-terminated full host name.
* @param[in] aHostInfo A pointer to the discovered host information.
*
*/
void HandleDiscoveredHost(const char *aHostFullName, const otDnssdHostInfo &aHostInfo);
private:
enum
{
kPort = OPENTHREAD_CONFIG_DNSSD_SERVER_PORT,
kProtocolLabelLength = 4,
};
enum : uint8_t
{
kResolveNone = 0,
kResolveAnswer = 1u << 0,
kResolveAdditionalSrv = 1u << 1,
kResolveAdditionalTxt = 1u << 2,
kResolveAdditionalAaaa = 1u << 3,
kResolveAdditionalAll = kResolveAdditionalSrv | kResolveAdditionalTxt | kResolveAdditionalAaaa,
kPort = OPENTHREAD_CONFIG_DNSSD_SERVER_PORT,
kProtocolLabelLength = 4,
kMaxConcurrentQueries = 32,
};
class NameCompressInfo : public Clearable<NameCompressInfo>
@@ -107,11 +128,10 @@ private:
kUnknownOffset = 0, // Unknown offset value (used when offset is not yet set).
};
explicit NameCompressInfo(void) = default;
explicit NameCompressInfo(const char *aDomainName)
: mDomainName(aDomainName)
, mServiceName(nullptr)
, mInstanceName(nullptr)
, mHostName(nullptr)
, mDomainNameOffset(kUnknownOffset)
, mServiceNameOffset(kUnknownOffset)
, mInstanceNameOffset(kUnknownOffset)
@@ -125,80 +145,60 @@ private:
const char *GetDomainName(void) const { return mDomainName; }
uint16_t GetServiceNameOffset(const char *aServiceName) const
uint16_t GetServiceNameOffset(const Message &aMessage, const char *aServiceName) const
{
uint16_t offset = mServiceNameOffset;
if (offset != kUnknownOffset && strcmp(aServiceName, mServiceName) != 0)
{
offset = kUnknownOffset;
}
return offset;
return MatchCompressedName(aMessage, mServiceNameOffset, aServiceName)
? mServiceNameOffset
: static_cast<uint16_t>(kUnknownOffset);
};
void SetServiceNameOffset(uint16_t aOffset, const char *aName)
void SetServiceNameOffset(uint16_t aOffset)
{
if (mServiceName == nullptr)
if (mServiceNameOffset == kUnknownOffset)
{
mServiceName = aName;
mServiceNameOffset = aOffset;
}
}
const char *GetServiceName() const { return mServiceName; }
uint16_t GetInstanceNameOffset(const char *aName) const
uint16_t GetInstanceNameOffset(const Message &aMessage, const char *aName) const
{
uint16_t offset = mInstanceNameOffset;
if (offset != kUnknownOffset && strcmp(aName, mInstanceName) != 0)
{
offset = kUnknownOffset;
}
return offset;
return MatchCompressedName(aMessage, mInstanceNameOffset, aName) ? mInstanceNameOffset
: static_cast<uint16_t>(kUnknownOffset);
}
void SetInstanceNameOffset(uint16_t aOffset, const char *aName)
void SetInstanceNameOffset(uint16_t aOffset)
{
if (mInstanceName == nullptr)
if (mInstanceNameOffset == kUnknownOffset)
{
mInstanceName = aName;
mInstanceNameOffset = aOffset;
}
}
uint16_t GetHostNameOffset(const char *aName) const
uint16_t GetHostNameOffset(const Message &aMessage, const char *aName) const
{
uint16_t offset = mHostNameOffset;
if (offset != kUnknownOffset && strcmp(aName, mHostName) != 0)
{
offset = kUnknownOffset;
}
return offset;
return MatchCompressedName(aMessage, mHostNameOffset, aName) ? mHostNameOffset
: static_cast<uint16_t>(kUnknownOffset);
}
void SetHostNameOffset(uint16_t aOffset, const char *aName)
void SetHostNameOffset(uint16_t aOffset)
{
if (mHostName == nullptr)
if (mHostNameOffset == kUnknownOffset)
{
mHostName = aName;
mHostNameOffset = aOffset;
}
}
private:
const char *const mDomainName; // The serialized domain name.
const char * mServiceName; // The serialized service name (only support one service name).
const char * mInstanceName; // The serialized instance name or nullptr (only support one instance name).
const char * mHostName; // The serialized host name or nullptr (only support one host name).
uint16_t mDomainNameOffset; // Offset of domain name serialization into the response message.
uint16_t mServiceNameOffset; // Offset of service name serialization into the response message.
uint16_t mInstanceNameOffset; // Offset of instance name serialization into the response message.
uint16_t mHostNameOffset; // Offset of host name serialization into the response message.
static bool MatchCompressedName(const Message &aMessage, uint16_t aOffset, const char *aName)
{
return aOffset != kUnknownOffset && Name::CompareName(aMessage, aOffset, aName) == kErrorNone;
}
const char *mDomainName; // The serialized domain name.
uint16_t mDomainNameOffset; // Offset of domain name serialization into the response message.
uint16_t mServiceNameOffset; // Offset of service name serialization into the response message.
uint16_t mInstanceNameOffset; // Offset of instance name serialization into the response message.
uint16_t mHostNameOffset; // Offset of host name serialization into the response message.
};
// This structure represents the splitting information of a full name.
@@ -232,67 +232,146 @@ private:
// instance.
};
bool IsRunning(void) const { return mSocket.IsBound(); }
static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
void ProcessQuery(Message &aMessage, Message &aResponse, const Header &aRequestHeader);
Header::Response ResolveQuestion(const char * aName,
const Question & aQuestion,
Header & aResponseHeader,
Message & aResponseMessage,
uint8_t aResolveKind,
NameCompressInfo &aCompressInfo);
static Error AppendQuestion(const char * aName,
const Question & aQuestion,
Message & aMessage,
NameCompressInfo &aCompressInfo);
static Error AppendPtrRecord(Message & aMessage,
const char * aServiceName,
const char * aInstanceName,
uint32_t aTtl,
NameCompressInfo &aCompressInfo);
static Error AppendSrvRecord(Message & aMessage,
const char * aInstanceName,
const char * aHostName,
uint32_t aTtl,
uint16_t aPriority,
uint16_t aWeight,
uint16_t aPort,
NameCompressInfo &aCompressInfo);
static Error AppendAaaaRecord(Message & aMessage,
const char * aHostName,
const Ip6::Address &aAddress,
uint32_t aTtl,
NameCompressInfo & aCompressInfo);
static Error AppendServiceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo);
static Error AppendInstanceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo);
static Error AppendHostName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo);
static void IncResourceRecordCount(Header &aHeader, bool aAdditional);
static Error FindNameComponents(const char *aName, const char *aDomain, NameComponentsOffsetInfo &aInfo);
static Error FindPreviousLabel(const char *aName, uint8_t &aStart, uint8_t &aStop);
enum : uint32_t
{
kQueryTimeout = OPENTHREAD_CONFIG_DNSSD_QUERY_TIMEOUT,
};
class QueryTransaction
{
public:
explicit QueryTransaction(void)
: mResponseMessage(nullptr)
{
}
void Init(const Header & aResponseHeader,
Message & aResponseMessage,
const NameCompressInfo &aCompressInfo,
const Ip6::MessageInfo &aMessageInfo);
bool IsValid(void) const { return mResponseMessage != nullptr; }
const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; }
Header & GetResponseHeader(void) { return mResponseHeader; }
const Header & GetResponseHeader(void) const { return mResponseHeader; }
Message & GetResponseMessage(void) { return *mResponseMessage; }
const Message & GetResponseMessage(void) const { return *mResponseMessage; }
TimeMilli GetStartTime(void) const { return mStartTime; }
NameCompressInfo & GetNameCompressInfo(void) { return mCompressInfo; };
void Finalize(Header::Response aResponseMessage, Ip6::Udp::Socket &aSocket);
private:
Header mResponseHeader;
Message * mResponseMessage;
NameCompressInfo mCompressInfo;
Ip6::MessageInfo mMessageInfo;
TimeMilli mStartTime;
};
enum DnsQueryType
{
kDnsQueryNone,
kDnsQueryBrowse,
kDnsQueryResolve,
kDnsQueryResolveHost,
};
bool IsRunning(void) const { return mSocket.IsBound(); }
static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
void ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage, const Ip6::MessageInfo &aMessageInfo);
static Header::Response AddQuestions(const Header & aRequestHeader,
const Message & aRequestMessage,
Header & aResponseHeader,
Message & aResponseMessage,
NameCompressInfo &aCompressInfo);
static Error AppendQuestion(const char * aName,
const Question & aQuestion,
Message & aMessage,
NameCompressInfo &aCompressInfo);
static Error AppendPtrRecord(Message & aMessage,
const char * aServiceName,
const char * aInstanceName,
uint32_t aTtl,
NameCompressInfo &aCompressInfo);
static Error AppendSrvRecord(Message & aMessage,
const char * aInstanceName,
const char * aHostName,
uint32_t aTtl,
uint16_t aPriority,
uint16_t aWeight,
uint16_t aPort,
NameCompressInfo &aCompressInfo);
static Error AppendTxtRecord(Message & aMessage,
const char * aInstanceName,
const void * aTxtData,
uint16_t aTxtLength,
uint32_t aTtl,
NameCompressInfo &aCompressInfo);
static Error AppendAaaaRecord(Message & aMessage,
const char * aHostName,
const Ip6::Address &aAddress,
uint32_t aTtl,
NameCompressInfo & aCompressInfo);
static Error AppendServiceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo);
static Error AppendInstanceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo);
static Error AppendHostName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo);
static void IncResourceRecordCount(Header &aHeader, bool aAdditional);
static Error FindNameComponents(const char *aName, const char *aDomain, NameComponentsOffsetInfo &aInfo);
static Error FindPreviousLabel(const char *aName, uint8_t &aStart, uint8_t &aStop);
static void SendResponse(Header aHeader,
Header::Response aResponseCode,
Message & aMessage,
const Ip6::MessageInfo &aMessageInfo,
Ip6::Udp::Socket & aSocket);
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
Header::Response ResolveBySrp(Header & aResponseHeader,
Message & aResponseMessage,
Server::NameCompressInfo &aCompressInfo);
Header::Response ResolveQuestionBySrp(const char * aName,
const Question & aQuestion,
Header & aResponseHeader,
Message & aResponseMessage,
uint8_t aResolveKind,
NameCompressInfo &aCompressInfo);
NameCompressInfo &aCompressInfo,
bool aAdditional);
const Srp::Server::Host * GetNextSrpHost(const Srp::Server::Host *aHost);
static const Srp::Server::Service *GetNextSrpService(const Srp::Server::Host & aHost,
const Srp::Server::Service *aService);
static Error AppendTxtRecord(Message & aMessage,
const char * aInstanceName,
const Srp::Server::Service &aService,
uint32_t aTtl,
NameCompressInfo & aCompressInfo);
#endif
Error ResolveByQueryCallbacks(Header & aResponseHeader,
Message & aResponseMessage,
NameCompressInfo & aCompressInfo,
const Ip6::MessageInfo &aMessageInfo);
QueryTransaction *NewQuery(const Header & aResponseHeader,
Message & aResponseMessage,
const NameCompressInfo &aCompressInfo,
const Ip6::MessageInfo &aMessageInfo);
static bool CanAnswerQuery(const QueryTransaction & aQuery,
const char * aServiceFullName,
const otDnssdServiceInstanceInfo &aInstanceInfo);
void AnswerQuery(QueryTransaction & aQuery,
const char * aServiceFullName,
const otDnssdServiceInstanceInfo &aInstanceInfo);
static bool CanAnswerQuery(const Server::QueryTransaction &aQuery, const char *aHostFullName);
void AnswerQuery(QueryTransaction &aQuery, const char *aHostFullName, const otDnssdHostInfo &aHostInfo);
void FinalizeQuery(QueryTransaction &aQuery, Header::Response aResponseCode);
static DnsQueryType GetQueryType(const Header &aHeader, const Message &aMessage, char (&aName)[Name::kMaxNameSize]);
static bool HasQuestion(const Header &aHeader, const Message &aMessage, const char *aName, uint16_t aQuestionType);
static void HandleTimer(Timer &aTimer);
void HandleTimer(void);
void ResetTimer(void);
static const char kDnssdProtocolUdp[4];
static const char kDnssdProtocolTcp[4];
static const char kDefaultDomainName[];
Ip6::Udp::Socket mSocket;
Ip6::Udp::Socket mSocket;
QueryTransaction mQueryTransactions[kMaxConcurrentQueries];
void * mQueryCallbackContext;
otDnssdQuerySubscribeCallback mQuerySubscribe;
otDnssdQueryUnsubscribeCallback mQueryUnsubscribe;
TimerMilli mTimer;
};
} // namespace ServiceDiscovery
@@ -109,6 +109,8 @@ class TestDnssdServerOnBr(thread_cert.TestCase):
self.simulator.go(10)
server_addr = self.nodes[SERVER].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]
# Router1 can ping to/from the Host on infra link.
self.assertTrue(self.nodes[BR1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0],
backbone=True))
@@ -129,7 +131,7 @@ class TestDnssdServerOnBr(thread_cert.TestCase):
ins2_full_name = f'ins2.{SERVICE_FULL_NAME}'
host1_full_name = f'host1.{DOMAIN}'
host2_full_name = f'host2.{DOMAIN}'
server_addr = self.nodes[SERVER].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]
EMPTY_TXT = {}
# check if PTR query works
dig_result = self.nodes[DIGGER].dns_dig(server_addr, SERVICE_FULL_NAME, 'PTR')
@@ -141,11 +143,11 @@ class TestDnssdServerOnBr(thread_cert.TestCase):
(SERVICE_FULL_NAME, 'IN', 'PTR', f'ins2.{SERVICE_FULL_NAME}')],
'ADDITIONAL': [
(ins1_full_name, 'IN', 'SRV', 1, 1, 11111, host1_full_name),
(ins1_full_name, 'IN', 'TXT', '""'),
(ins1_full_name, 'IN', 'TXT', EMPTY_TXT),
(host1_full_name, 'IN', 'AAAA', client1_addrs[0]),
(host1_full_name, 'IN', 'AAAA', client1_addrs[1]),
(ins2_full_name, 'IN', 'SRV', 2, 2, 22222, host2_full_name),
(ins2_full_name, 'IN', 'TXT', '""'),
(ins2_full_name, 'IN', 'TXT', EMPTY_TXT),
(host2_full_name, 'IN', 'AAAA', client2_addrs[0]),
(host2_full_name, 'IN', 'AAAA', client2_addrs[1]),
],
@@ -178,13 +180,13 @@ class TestDnssdServerOnBr(thread_cert.TestCase):
dig_result = self.nodes[DIGGER].dns_dig(server_addr, ins1_full_name, 'TXT')
self._assert_dig_result_matches(dig_result, {
'QUESTION': [(ins1_full_name, 'IN', 'TXT')],
'ANSWER': [(ins1_full_name, 'IN', 'TXT', '""'),],
'ANSWER': [(ins1_full_name, 'IN', 'TXT', EMPTY_TXT),],
})
dig_result = self.nodes[DIGGER].dns_dig(server_addr, ins2_full_name, 'TXT')
self._assert_dig_result_matches(dig_result, {
'QUESTION': [(ins2_full_name, 'IN', 'TXT')],
'ANSWER': [(ins2_full_name, 'IN', 'TXT', '""'),],
'ANSWER': [(ins2_full_name, 'IN', 'TXT', EMPTY_TXT),],
})
# check if AAAA query works
@@ -221,6 +223,63 @@ class TestDnssdServerOnBr(thread_cert.TestCase):
'status': 'NXDOMAIN',
})
# verify Discovery Proxy works for _meshcop._udp
self._verify_discovery_proxy_meshcop(server_addr)
def _verify_discovery_proxy_meshcop(self, server_addr):
dp_service_name = '_meshcop._udp.default.service.arpa.'
network_name = self.nodes[SERVER].get_network_name()
dp_instance_name = f'{network_name}._meshcop._udp.default.service.arpa.'
dp_hostname = lambda x: x.endswith('.default.service.arpa.')
dig_result = self.nodes[DIGGER].dns_dig(server_addr, dp_service_name, 'PTR')
self._assert_dig_result_matches(
dig_result, {
'QUESTION': [(dp_service_name, 'IN', 'PTR'),],
'ANSWER': [(dp_service_name, 'IN', 'PTR', dp_instance_name),],
'ADDITIONAL': [
(dp_instance_name, 'IN', 'SRV', 0, 0, config.BORDER_AGENT_UDP_PORT, dp_hostname),
(dp_instance_name, 'IN', 'TXT', lambda txt: (isinstance(txt, dict) and txt.get(
'nn') == network_name and 'xp' in txt and 'tv' in txt and 'dd' in txt)),
],
})
# Find the actual host name and IPv6 address
dp_ip6_address = None
for rr in dig_result['ADDITIONAL']:
if rr[3] == 'SRV':
dp_hostname = rr[7]
elif rr[3] == 'AAAA':
dp_ip6_address = rr[4]
assert isinstance(dp_hostname, str), dig_result
dig_result = self.nodes[DIGGER].dns_dig(server_addr, dp_instance_name, 'SRV')
self._assert_dig_result_matches(
dig_result, {
'QUESTION': [(dp_instance_name, 'IN', 'SRV'),],
'ANSWER': [(dp_instance_name, 'IN', 'SRV', 0, 0, config.BORDER_AGENT_UDP_PORT, dp_hostname),],
'ADDITIONAL': [(dp_instance_name, 'IN', 'TXT', lambda txt: (isinstance(txt, dict) and txt.get(
'nn') == network_name and 'xp' in txt and 'tv' in txt and 'dd' in txt)),],
})
dig_result = self.nodes[DIGGER].dns_dig(server_addr, dp_instance_name, 'TXT')
self._assert_dig_result_matches(
dig_result, {
'QUESTION': [(dp_instance_name, 'IN', 'TXT'),],
'ANSWER': [(dp_instance_name, 'IN', 'TXT', lambda txt: (isinstance(txt, dict) and txt.get(
'nn') == network_name and 'xp' in txt and 'tv' in txt and 'dd' in txt)),],
'ADDITIONAL': [(dp_instance_name, 'IN', 'SRV', 0, 0, config.BORDER_AGENT_UDP_PORT, dp_hostname),],
})
if dp_ip6_address is not None:
dig_result = self.nodes[DIGGER].dns_dig(server_addr, dp_hostname, 'AAAA')
self._assert_dig_result_matches(dig_result, {
'QUESTION': [(dp_hostname, 'IN', 'AAAA'),],
'ANSWER': [(dp_hostname, 'IN', 'AAAA', dp_ip6_address),],
})
def _config_srp_client_services(self, client, instancename, hostname, port, priority, weight, addrs):
self.nodes[client].netdata_show()
srp_server_port = self.nodes[client].get_srp_server_port()
@@ -234,7 +293,11 @@ class TestDnssdServerOnBr(thread_cert.TestCase):
self.assertEqual(self.nodes[client].srp_client_get_host_state(), 'Registered')
def _assert_have_question(self, dig_result, question):
self.assertIn(question, dig_result['QUESTION'], (question, dig_result))
for dig_question in dig_result['QUESTION']:
if self._match_record(dig_question, question):
return
self.fail((dig_result, question))
def _assert_have_answer(self, dig_result, record, additional=False):
for dig_answer in dig_result['ANSWER' if not additional else 'ADDITIONAL']:
@@ -250,11 +313,22 @@ class TestDnssdServerOnBr(thread_cert.TestCase):
if record[2] == 'AAAA':
record[3] = ipaddress.IPv6Address(record[3])
if dig_answer == record:
if self._match_record(dig_answer, record):
return
print('not match: ', dig_answer, record,
list(a == b or (callable(b) and b(a)) for a, b in zip(dig_answer, record)))
self.fail((record, dig_result))
def _match_record(self, record, match):
assert not any(callable(elem) for elem in record), record
if record == match:
return True
return all(a == b or (callable(b) and b(a)) for a, b in zip(record, match))
def _assert_dig_result_matches(self, dig_result, expected_result):
self.assertEqual(dig_result['opcode'], expected_result.get('opcode', 'QUERY'), dig_result)
self.assertEqual(dig_result['status'], expected_result.get('status', 'NOERROR'), dig_result)
@@ -272,7 +346,7 @@ class TestDnssdServerOnBr(thread_cert.TestCase):
self._assert_have_answer(dig_result, record, additional=False)
if 'ADDITIONAL' in expected_result:
self.assertEqual(len(dig_result['ADDITIONAL']), len(expected_result['ADDITIONAL']), dig_result)
self.assertGreaterEqual(len(dig_result['ADDITIONAL']), len(expected_result['ADDITIONAL']), dig_result)
for record in expected_result['ADDITIONAL']:
self._assert_have_answer(dig_result, record, additional=True)
+2
View File
@@ -144,6 +144,8 @@ LEADER_NOTIFY_SED_BY_CHILD_UPDATE_REQUEST = True
THREAD_VERSION_1_1 = 2
THREAD_VERSION_1_2 = 3
BORDER_AGENT_UDP_PORT = 49191
def create_default_network_data_prefix_sub_tlvs_factories():
return {
+17
View File
@@ -293,11 +293,28 @@ class OtbrDocker:
record[1] = int(record[1])
if record[3] == 'SRV':
record[4], record[5], record[6] = map(int, [record[4], record[5], record[6]])
elif record[3] == 'TXT':
record[4:] = [self.__parse_dns_dig_txt(record[4:])]
dig_result[section].append(tuple(record))
return dig_result
def __parse_dns_dig_txt(self, txt_entries):
# Example txt_entries:
# ['"nn=OpenThread"', '"xp=\\000\\013\\184\\000\\000\\000\\000\\000"', '"tv=1.2.0"', '"dd=\\022n\\010\\000\\000\\000\\000\\001"']
txt = {}
for entry in txt_entries:
assert entry.startswith('"') and entry.endswith('"')
entry = entry[1:-1]
if entry == "":
continue
k, v = entry.split('=')
txt[k] = v
return txt
def _setup_sysctl(self):
self.bash(f'sysctl net.ipv6.conf.{self.ETH_DEV}.accept_ra=2')
self.bash(f'sysctl net.ipv6.conf.{self.ETH_DEV}.accept_ra_rt_info_max_plen=64')