mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
[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:
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -75,6 +75,7 @@ source_set("openthread") {
|
||||
"diag.h",
|
||||
"dns.h",
|
||||
"dns_client.h",
|
||||
"dnssd_server.h",
|
||||
"entropy.h",
|
||||
"error.h",
|
||||
"heap.h",
|
||||
|
||||
@@ -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_
|
||||
@@ -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
@@ -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
@@ -1538,10 +1538,9 @@ void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressRes
|
||||
OutputFormat(" TTL:%u ", ttl);
|
||||
index++;
|
||||
}
|
||||
|
||||
OutputLine("");
|
||||
}
|
||||
|
||||
OutputLine("");
|
||||
OutputResult(aError);
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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`.
|
||||
*
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user