diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index d0ac55a7a..1f4e34bb1 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -103,6 +103,7 @@ ot_option(OT_DHCP6_SERVER OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE "DHCP6 server") ot_option(OT_DIAGNOSTIC OPENTHREAD_CONFIG_DIAG_ENABLE "diagnostic") ot_option(OT_DNS_CLIENT OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE "DNS client") ot_option(OT_DNS_DSO OPENTHREAD_CONFIG_DNS_DSO_ENABLE "DNS Stateful Operations (DSO)") +ot_option(OT_DNS_QUERY_UPSTREAM OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE "Allow sending DNS queries to upstream") ot_option(OT_DNSSD_SERVER OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE "DNS-SD server") ot_option(OT_DUA OPENTHREAD_CONFIG_DUA_ENABLE "Domain Unicast Address (DUA)") ot_option(OT_ECDSA OPENTHREAD_CONFIG_ECDSA_ENABLE "ECDSA") diff --git a/examples/README.md b/examples/README.md index 8d8d1eca7..9c05718ef 100644 --- a/examples/README.md +++ b/examples/README.md @@ -35,6 +35,7 @@ This page lists the available common switches with description. Unless stated ot | DEBUG_UART_LOG | not implemented | Enables the log output for the debug UART. Requires OPENTHREAD_CONFIG_ENABLE_DEBUG_UART to be enabled. | | DNS_CLIENT | OT_DNS_CLIENT | Enables support for DNS client. Enable this switch on a device that sends a DNS query for AAAA (IPv6) record. | | DNS_DSO | OT_DNS_DSO | Enables support for DNS Stateful Operations (DSO). | +| DNS_QUERY_UPSTREAM | OT_DNS_QUERY_UPSTREAM | Enables forwarding DNS queries to upstream DNS server. | | DNSSD_SERVER | OT_DNSSD_SERVER | Enables support for DNS-SD server. DNS-SD server use service information from local SRP server to resolve DNS-SD query questions. | | DUA | OT_DUA | Enables the Domain Unicast Address feature for Thread 1.2. | | DYNAMIC_LOG_LEVEL | not implemented | Enables the dynamic log level feature. Enable this switch if OpenThread log level is required to be set at runtime. See [Logging guide](https://openthread.io/guides/build/logs) to learn more. | diff --git a/examples/common-switches.mk b/examples/common-switches.mk index 83e6620d9..0bf04fd8b 100644 --- a/examples/common-switches.mk +++ b/examples/common-switches.mk @@ -52,6 +52,7 @@ DISABLE_DOC ?= 0 DISABLE_TOOLS ?= 0 DNS_CLIENT ?= 0 DNS_DSO ?= 0 +DNS_QUERY_UPSTREAM ?= 0 DNSSD_SERVER ?= 0 DUA ?= 0 DYNAMIC_LOG_LEVEL ?= 0 @@ -212,6 +213,10 @@ ifeq ($(DNS_DSO),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_DNS_DSO_ENABLE=1 endif +ifeq ($(DNS_QUERY_UPSTREAM), 1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE=1 +endif + ifeq ($(DNSSD_SERVER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE=1 endif diff --git a/include/Makefile.am b/include/Makefile.am index 722775d8e..edc0b607c 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -102,6 +102,7 @@ ot_platform_headers = \ openthread/platform/crypto.h \ openthread/platform/debug_uart.h \ openthread/platform/diag.h \ + openthread/platform/dns.h \ openthread/platform/dso_transport.h \ openthread/platform/entropy.h \ openthread/platform/flash.h \ diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn index 931c7ac12..e0088a468 100644 --- a/include/openthread/BUILD.gn +++ b/include/openthread/BUILD.gn @@ -86,6 +86,7 @@ source_set("openthread") { "platform/crypto.h", "platform/debug_uart.h", "platform/diag.h", + "platform/dns.h", "platform/dso_transport.h", "platform/entropy.h", "platform/flash.h", diff --git a/include/openthread/dnssd_server.h b/include/openthread/dnssd_server.h index 2feb95e16..8ac14c417 100644 --- a/include/openthread/dnssd_server.h +++ b/include/openthread/dnssd_server.h @@ -245,6 +245,21 @@ otDnssdQueryType otDnssdGetQueryTypeAndName(const otDnssdQuery *aQuery, char (*a */ const otDnssdCounters *otDnssdGetCounters(otInstance *aInstance); +/** + * Enable or disable forwarding DNS queries to platform DNS upstream API. + * + * Available when `OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aEnabled A boolean to enable/disable forwarding DNS queries to upstream. + * + * @sa otPlatDnsStartUpstreamQuery + * @sa otPlatDnsCancelUpstreamQuery + * @sa otPlatDnsUpstreamQueryDone + * + */ +void otDnssdUpstreamQuerySetEnabled(otInstance *aInstance, bool aEnabled); + /** * @} * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 60d5159aa..2ab91ec4e 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (288) +#define OPENTHREAD_API_VERSION (289) /** * @addtogroup api-instance diff --git a/include/openthread/platform/dns.h b/include/openthread/platform/dns.h new file mode 100644 index 000000000..9e84fbe43 --- /dev/null +++ b/include/openthread/platform/dns.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023, 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 platform DNS interface. + * + */ + +#ifndef OPENTHREAD_PLATFORM_DNS_H_ +#define OPENTHREAD_PLATFORM_DNS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup plat-dns + * + * @brief + * This module includes the platform abstraction for sending recursive DNS query to upstream DNS servers. + * + * @{ + * + */ + +/** + * This opaque type represents an upstream DNS query transaction. + * + */ +typedef struct otPlatDnsUpstreamQuery otPlatDnsUpstreamQuery; + +/** + * Starts an upstream query transaction. + * + * - In success case (and errors represented by DNS protocol messages), the platform is expected to call + * `otPlatDnsUpstreamQueryDone`. + * - The OpenThread core may cancel a (possibly timeout) query transaction by calling + * `otPlatDnsCancelUpstreamQuery`, the platform must not call `otPlatDnsUpstreamQueryDone` on a + * cancelled transaction. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aTxn A pointer to the opaque DNS query transaction object. + * @param[in] aQuery A message buffer of the DNS payload that should be sent to upstream DNS server. + * + */ +void otPlatDnsStartUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery); + +/** + * Cancels a transaction of upstream query. + * + * The platform must call `otPlatDnsUpstreamQueryDone` to release the resources. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aTxn A pointer to the opaque DNS query transaction object. + * + */ +void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn); + +/** + * The platform calls this function to finish DNS query. + * + * The transaction will be released, so the platform must not call on the same transaction twice. This function passes + * the ownership of `aResponse` to OpenThread stack. + * + * Platform can pass a nullptr to close a transaction without a response. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aTxn A pointer to the opaque DNS query transaction object. + * @param[in] aResponse A message buffer of the DNS response payload or `nullptr` to close a transaction without a + * response. + * + */ +extern void otPlatDnsUpstreamQueryDone(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, otMessage *aResponse); + +/** + * @} + * + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/script/make-pretty b/script/make-pretty index 030f1f7fb..ecfe33d31 100755 --- a/script/make-pretty +++ b/script/make-pretty @@ -108,6 +108,7 @@ OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_DIAGNOSTIC=ON' '-DOT_DNS_CLIENT=ON' '-DOT_DNS_DSO=ON' + '-DOT_DNS_QUERY_UPSTREAM=ON' '-DOT_DNSSD_SERVER=ON' '-DOT_DUA=ON' '-DOT_MLR=ON' diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 1fe4a8dcf..14a3e8c37 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -3334,6 +3334,37 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE +#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE + else if (aArgs[0] == "server") + { + if (aArgs[1].IsEmpty()) + { + error = OT_ERROR_INVALID_ARGS; + } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + /** + * @cli dns server upstream {enable|disable} + * @code + * Done + * @endcode + * @cparam dns server upstream @ca{enable|disable} + * @par api_copy + * #otDnssdUpstreamQuerySetEnabled + */ + else if (aArgs[1] == "upstream") + { + bool enable; + + SuccessOrExit(error = ParseEnableOrDisable(aArgs[2], enable)); + otDnssdUpstreamQuerySetEnabled(GetInstancePtr(), enable); + } +#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + else + { + ExitNow(error = OT_ERROR_INVALID_COMMAND); + } + } +#endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE else { ExitNow(error = OT_ERROR_INVALID_COMMAND); diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 47f3c2241..d6abf3b04 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -540,6 +540,7 @@ openthread_core_files = [ "net/dns_client.hpp", "net/dns_dso.cpp", "net/dns_dso.hpp", + "net/dns_platform.cpp", "net/dns_types.cpp", "net/dns_types.hpp", "net/dnssd_server.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7b4356f37..0f5521b42 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -161,6 +161,7 @@ set(COMMON_SOURCES net/dhcp6_server.cpp net/dns_client.cpp net/dns_dso.cpp + net/dns_platform.cpp net/dns_types.cpp net/dnssd_server.cpp net/icmp6.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 282c89018..c0cdb6763 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -251,6 +251,7 @@ SOURCES_COMMON = \ net/dhcp6_server.cpp \ net/dns_client.cpp \ net/dns_dso.cpp \ + net/dns_platform.cpp \ net/dns_types.cpp \ net/dnssd_server.cpp \ net/icmp6.cpp \ diff --git a/src/core/api/dns_server_api.cpp b/src/core/api/dns_server_api.cpp index 7793e9187..1bdf4fca2 100644 --- a/src/core/api/dns_server_api.cpp +++ b/src/core/api/dns_server_api.cpp @@ -86,4 +86,11 @@ const otDnssdCounters *otDnssdGetCounters(otInstance *aInstance) return &AsCoreType(aInstance).Get().GetCounters(); } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +void otDnssdUpstreamQuerySetEnabled(otInstance *aInstance, bool aEnabled) +{ + return AsCoreType(aInstance).Get().SetUpstreamQueryEnabled(aEnabled); +} +#endif + #endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE diff --git a/src/core/config/dnssd_server.h b/src/core/config/dnssd_server.h index 70834937c..3edc4097f 100644 --- a/src/core/config/dnssd_server.h +++ b/src/core/config/dnssd_server.h @@ -75,4 +75,15 @@ #define OPENTHREAD_CONFIG_DNSSD_QUERY_TIMEOUT 6000 #endif +/** + * @def OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + * + * Define to 1 to enable upstream forwarding support. The platform MUST implement `otPlatDnsStartUpstreamQuery` and + * `otPlatDnsCancelUpstreamQuery`. + * + */ +#ifndef OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +#define OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE 0 +#endif + #endif // CONFIG_DNSSD_SERVER_H_ diff --git a/src/core/net/dns_platform.cpp b/src/core/net/dns_platform.cpp new file mode 100644 index 000000000..ef48077b6 --- /dev/null +++ b/src/core/net/dns_platform.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, 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 platform callbacks into OpenThread. + */ + +#include "openthread-core-config.h" + +#include +#include + +#include "common/code_utils.hpp" +#include "common/instance.hpp" +#include "common/message.hpp" + +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + +using namespace ot; + +void otPlatDnsUpstreamQueryDone(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, otMessage *aResponse) +{ + return AsCoreType(aInstance).Get().OnUpstreamQueryDone(AsCoreType(aTxn), + AsCoreTypePtr(aResponse)); +} +#endif diff --git a/src/core/net/dns_types.cpp b/src/core/net/dns_types.cpp index e7717780d..af3bde0e5 100644 --- a/src/core/net/dns_types.cpp +++ b/src/core/net/dns_types.cpp @@ -687,6 +687,11 @@ exit: return match; } +bool Name::IsSameDomain(const char *aDomain1, const char *aDomain2) +{ + return IsSubDomainOf(aDomain1, aDomain2) && IsSubDomainOf(aDomain2, aDomain1); +} + Error ResourceRecord::ParseRecords(const Message &aMessage, uint16_t &aOffset, uint16_t aNumRecords) { Error error = kErrorNone; diff --git a/src/core/net/dns_types.hpp b/src/core/net/dns_types.hpp index 075d625d7..a6317e2e5 100644 --- a/src/core/net/dns_types.hpp +++ b/src/core/net/dns_types.hpp @@ -990,6 +990,20 @@ public: */ static bool IsSubDomainOf(const char *aName, const char *aDomain); + /** + * This static method tests if the two DNS name are the same domain. + * + * Both @p aDomain1 and @p aDomain2 can end without dot ('.'). + * + * @param[in] aDomain1 The dot-separated name. + * @param[in] aDomain2 The dot-separated domain. + * + * @retval TRUE If the two DNS names are the same domain. + * @retval FALSE If the two DNS names are not the same domain. + * + */ + static bool IsSameDomain(const char *aDomain1, const char *aDomain2); + private: // The first 2 bits of the encoded label specifies label type. // diff --git a/src/core/net/dnssd_server.cpp b/src/core/net/dnssd_server.cpp index 3b4d69577..453eeb68e 100644 --- a/src/core/net/dnssd_server.cpp +++ b/src/core/net/dnssd_server.cpp @@ -35,6 +35,8 @@ #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE +#include + #include "common/array.hpp" #include "common/as_core_type.hpp" #include "common/code_utils.hpp" @@ -52,10 +54,11 @@ namespace ServiceDiscovery { RegisterLogModule("DnssdServer"); -const char Server::kDnssdProtocolUdp[] = "_udp"; -const char Server::kDnssdProtocolTcp[] = "_tcp"; -const char Server::kDnssdSubTypeLabel[] = "._sub."; -const char Server::kDefaultDomainName[] = "default.service.arpa."; +const char Server::kDnssdProtocolUdp[] = "_udp"; +const char Server::kDnssdProtocolTcp[] = "_tcp"; +const char Server::kDnssdSubTypeLabel[] = "._sub."; +const char Server::kDefaultDomainName[] = "default.service.arpa."; +const char *Server::kBlockedDomains[] = {"ipv4only.arpa."}; Server::Server(Instance &aInstance) : InstanceLocator(aInstance) @@ -63,6 +66,9 @@ Server::Server(Instance &aInstance) , mQueryCallbackContext(nullptr) , mQuerySubscribe(nullptr) , mQueryUnsubscribe(nullptr) +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + , mEnableUpstreamQuery(false) +#endif , mTimer(aInstance) { mCounters.Clear(); @@ -103,6 +109,16 @@ void Server::Stop(void) } } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + for (UpstreamQueryTransaction &txn : mUpstreamQueryTransactions) + { + if (txn.IsValid()) + { + ResetUpstreamQueryTransaction(txn, kErrorFailed); + } + } +#endif + mTimer.Stop(); IgnoreError(mSocket.Close()); @@ -145,8 +161,22 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage Message *responseMessage = nullptr; Header responseHeader; NameCompressInfo compressInfo(kDefaultDomainName); - Header::Response response = Header::kResponseSuccess; - bool resolveByQueryCallbacks = false; + Header::Response response = Header::kResponseSuccess; + bool shouldSendResponse = true; + +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + if (mEnableUpstreamQuery && ShouldForwardToUpstream(aRequestHeader, aRequestMessage)) + { + error = ResolveByUpstream(aRequestMessage, aMessageInfo); + if (error == kErrorNone) + { + shouldSendResponse = false; + ExitNow(); + } + response = Header::kResponseServerFailure; + LogWarn("Failed to forward DNS query to upstream: %s", ErrorToString(error)); + } +#endif responseMessage = mSocket.NewMessage(0); VerifyOrExit(responseMessage != nullptr, error = kErrorNoBufs); @@ -158,6 +188,14 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage responseHeader.Clear(); responseHeader.SetType(Header::kTypeResponse); responseHeader.SetMessageId(aRequestHeader.GetMessageId()); + responseHeader.SetQueryType(aRequestHeader.GetQueryType()); + if (aRequestHeader.IsRecursionDesiredFlagSet()) + { + responseHeader.SetRecursionDesiredFlag(); + } + + // We may met errors when forwarding the query to the upstream + VerifyOrExit(response == Header::kResponseSuccess); // Validate the query VerifyOrExit(aRequestHeader.GetQueryType() == Header::kQueryTypeStandard, @@ -172,13 +210,11 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage // Answer the questions response = ResolveBySrp(responseHeader, *responseMessage, compressInfo); #endif - - // Resolve the question using query callbacks if SRP server failed to resolve the questions. if (responseHeader.GetAnswerCount() == 0) { if (kErrorNone == ResolveByQueryCallbacks(responseHeader, *responseMessage, compressInfo, aMessageInfo)) { - resolveByQueryCallbacks = true; + shouldSendResponse = false; } } #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE @@ -189,7 +225,7 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage #endif exit: - if (error == kErrorNone && !resolveByQueryCallbacks) + if (error == kErrorNone && shouldSendResponse) { SendResponse(responseHeader, response, *responseMessage, aMessageInfo, mSocket); } @@ -839,6 +875,87 @@ exit: return error; } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +bool Server::ShouldForwardToUpstream(const Header &aRequestHeader, const Message &aRequestMessage) +{ + bool ret = true; + uint16_t readOffset; + char name[Name::kMaxNameSize]; + + VerifyOrExit(aRequestHeader.IsRecursionDesiredFlagSet(), ret = false); + readOffset = sizeof(Header); + + for (uint16_t i = 0; i < aRequestHeader.GetQuestionCount(); i++) + { + VerifyOrExit(kErrorNone == Name::ReadName(aRequestMessage, readOffset, name, sizeof(name)), ret = false); + readOffset += sizeof(Question); + + VerifyOrExit(!Name::IsSubDomainOf(name, kDefaultDomainName), ret = false); + for (const char *blockedDomain : kBlockedDomains) + { + VerifyOrExit(!Name::IsSameDomain(name, blockedDomain), ret = false); + } + } + +exit: + return ret; +} + +void Server::OnUpstreamQueryDone(UpstreamQueryTransaction &aQueryTransaction, Message *aResponseMessage) +{ + Error error = kErrorNone; + + VerifyOrExit(aQueryTransaction.IsValid(), error = kErrorInvalidArgs); + + if (aResponseMessage != nullptr) + { + error = mSocket.SendTo(*aResponseMessage, aQueryTransaction.GetMessageInfo()); + } + ResetUpstreamQueryTransaction(aQueryTransaction, error); + ResetTimer(); + +exit: + FreeMessageOnError(aResponseMessage, error); +} + +Server::UpstreamQueryTransaction *Server::AllocateUpstreamQueryTransaction(const Ip6::MessageInfo &aMessageInfo) +{ + UpstreamQueryTransaction *ret = nullptr; + + for (UpstreamQueryTransaction &txn : mUpstreamQueryTransactions) + { + if (!txn.IsValid()) + { + ret = &txn; + txn.Init(aMessageInfo); + break; + } + } + + if (ret != nullptr) + { + LogInfo("Upstream query transaction %d initialized.", static_cast(ret - mUpstreamQueryTransactions)); + mTimer.FireAtIfEarlier(ret->GetExpireTime()); + } + + return ret; +} + +Error Server::ResolveByUpstream(const Message &aRequestMessage, const Ip6::MessageInfo &aMessageInfo) +{ + Error error = kErrorNone; + UpstreamQueryTransaction *txn = nullptr; + + txn = AllocateUpstreamQueryTransaction(aMessageInfo); + VerifyOrExit(txn != nullptr, error = kErrorNoBufs); + + otPlatDnsStartUpstreamQuery(&GetInstance(), txn, &aRequestMessage); + +exit: + return error; +} +#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + Server::QueryTransaction *Server::NewQuery(const Header &aResponseHeader, Message &aResponseMessage, const NameCompressInfo &aCompressInfo, @@ -1143,6 +1260,21 @@ void Server::HandleTimer(void) } } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + for (UpstreamQueryTransaction &query : mUpstreamQueryTransactions) + { + if (!query.IsValid()) + { + continue; + } + + if (query.GetExpireTime() <= now) + { + otPlatDnsCancelUpstreamQuery(&GetInstance(), &query); + } + } +#endif + ResetTimer(); } @@ -1171,6 +1303,28 @@ void Server::ResetTimer(void) } } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + for (UpstreamQueryTransaction &query : mUpstreamQueryTransactions) + { + TimeMilli expire; + + if (!query.IsValid()) + { + continue; + } + + expire = query.GetExpireTime(); + if (expire <= now) + { + nextExpire = now; + } + else if (expire < nextExpire) + { + nextExpire = expire; + } + } +#endif + if (nextExpire < now.GetDistantFuture()) { mTimer.FireAt(nextExpire); @@ -1246,6 +1400,32 @@ void Server::UpdateResponseCounters(Header::Response aResponseCode) } } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +void Server::UpstreamQueryTransaction::Init(const Ip6::MessageInfo &aMessageInfo) +{ + mMessageInfo = aMessageInfo; + mValid = true; + mExpireTime = TimerMilli::GetNow() + kQueryTimeout; +} + +void Server::ResetUpstreamQueryTransaction(UpstreamQueryTransaction &aTxn, Error aError) +{ + int index = static_cast(&aTxn - mUpstreamQueryTransactions); + + // Avoid the warnings when info / warn logging is disabled. + OT_UNUSED_VARIABLE(index); + if (aError == kErrorNone) + { + LogInfo("Upstream query transaction %d completed.", index); + } + else + { + LogWarn("Upstream query transaction %d closed: %s.", index, ErrorToString(aError)); + } + aTxn.Reset(); +} +#endif + } // namespace ServiceDiscovery } // namespace Dns } // namespace ot diff --git a/src/core/net/dnssd_server.hpp b/src/core/net/dnssd_server.hpp index 92dfd8d46..a71bf8708 100644 --- a/src/core/net/dnssd_server.hpp +++ b/src/core/net/dnssd_server.hpp @@ -49,6 +49,10 @@ * This file includes definitions for the DNS-SD server. */ +struct otPlatDnsUpstreamQuery +{ +}; + namespace ot { namespace Srp { @@ -75,6 +79,62 @@ public: { }; +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + /** + * This class represents an upstream query transaction. The methods should only be used by + * `Dns::ServiceDiscovery::Server`. + * + */ + class UpstreamQueryTransaction : public otPlatDnsUpstreamQuery + { + public: + /** + * This method returns whether the transaction is valid. + * + * @retval TRUE The transaction is valid. + * @retval FALSE The transaction is not valid. + * + */ + bool IsValid(void) const { return mValid; } + + /** + * This method returns the time when the transaction expires. + * + * @returns The expire time of the transaction. + * + */ + TimeMilli GetExpireTime(void) const { return mExpireTime; } + + /** + * This method resets the transaction with a reason. The transaction will be invalid and can be reused for + * another upstream query after this call. + * + */ + void Reset(void) { mValid = false; } + + /** + * This method initializes the transaction. + * + * @param[in] aMessageInfo The IP message info of the query. + * + */ + void Init(const Ip6::MessageInfo &aMessageInfo); + + /** + * This method returns the message info of the query. + * + * @returns The message info of the query. + * + */ + const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; } + + private: + Ip6::MessageInfo mMessageInfo; + TimeMilli mExpireTime; + bool mValid; + }; +#endif + /** * This enumeration specifies a DNS-SD query type. * @@ -133,6 +193,28 @@ public: */ void HandleDiscoveredServiceInstance(const char *aServiceFullName, const otDnssdServiceInstanceInfo &aInstanceInfo); +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + /** + * This method notifies an answer of an upstream DNS query. + * + * The Transaction will be released. + * + * @param[in] aQueryTransaction A reference to upstream DNS query transaction. + * @param[in] aResponseMessage A pointer to response UDP message, should be allocated from Udp::NewMessage. + * Passing a nullptr means close the transaction without a response. + * + */ + void OnUpstreamQueryDone(UpstreamQueryTransaction &aQueryTransaction, Message *aResponseMessage); + + /** + * This method enables or disables forwarding DNS queries to platform DNS upstream API. + * + * @param[in] aEnabled A boolean to enable/disable forwarding DNS queries to upstream. + * + */ + void SetUpstreamQueryEnabled(bool aEnabled) { mEnableUpstreamQuery = aEnabled; } +#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + /** * This method notifies a discovered host. * @@ -250,10 +332,11 @@ private: uint16_t mHostNameOffset; // Offset of host name serialization into the response message. }; - static constexpr bool kBindUnspecifiedNetif = OPENTHREAD_CONFIG_DNSSD_SERVER_BIND_UNSPECIFIED_NETIF; - static constexpr uint8_t kProtocolLabelLength = 4; - static constexpr uint8_t kSubTypeLabelLength = 4; - static constexpr uint16_t kMaxConcurrentQueries = 32; + static constexpr bool kBindUnspecifiedNetif = OPENTHREAD_CONFIG_DNSSD_SERVER_BIND_UNSPECIFIED_NETIF; + static constexpr uint8_t kProtocolLabelLength = 4; + static constexpr uint8_t kSubTypeLabelLength = 4; + static constexpr uint16_t kMaxConcurrentQueries = 32; + static constexpr uint16_t kMaxConcurrentUpstreamQueries = 32; // This structure represents the splitting information of a full name. struct NameComponentsOffsetInfo @@ -384,6 +467,13 @@ private: const Srp::Server::Service *aService); #endif +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + static bool ShouldForwardToUpstream(const Header &aRequestHeader, const Message &aRequestMessage); + UpstreamQueryTransaction *AllocateUpstreamQueryTransaction(const Ip6::MessageInfo &aMessageInfo); + void ResetUpstreamQueryTransaction(UpstreamQueryTransaction &aTxn, Error aError); + Error ResolveByUpstream(const Message &aRequestMessage, const Ip6::MessageInfo &aMessageInfo); +#endif + Error ResolveByQueryCallbacks(Header &aResponseHeader, Message &aResponseMessage, NameCompressInfo &aCompressInfo, @@ -424,7 +514,14 @@ private: void *mQueryCallbackContext; otDnssdQuerySubscribeCallback mQuerySubscribe; otDnssdQueryUnsubscribeCallback mQueryUnsubscribe; - ServerTimer mTimer; + + static const char *kBlockedDomains[]; +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + bool mEnableUpstreamQuery; + UpstreamQueryTransaction mUpstreamQueryTransactions[kMaxConcurrentUpstreamQueries]; +#endif + + ServerTimer mTimer; Counters mCounters; }; @@ -433,6 +530,9 @@ private: } // namespace Dns DefineMapEnum(otDnssdQueryType, Dns::ServiceDiscovery::Server::DnsQueryType); +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +DefineCoreType(otPlatDnsUpstreamQuery, Dns::ServiceDiscovery::Server::UpstreamQueryTransaction); +#endif } // namespace ot diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt index 668f6c605..28a5527a8 100644 --- a/src/posix/platform/CMakeLists.txt +++ b/src/posix/platform/CMakeLists.txt @@ -120,6 +120,7 @@ add_library(openthread-posix power_updater.cpp radio.cpp radio_url.cpp + resolver.cpp settings.cpp spi_interface.cpp system.cpp diff --git a/src/posix/platform/Makefile.am b/src/posix/platform/Makefile.am index c23a9286b..ae5102306 100644 --- a/src/posix/platform/Makefile.am +++ b/src/posix/platform/Makefile.am @@ -62,6 +62,7 @@ libopenthread_posix_a_SOURCES = \ power.cpp \ power_updater.cpp \ radio.cpp \ + resolver.cpp \ radio_url.cpp \ settings.cpp \ spi_interface.cpp \ diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp index d18c73aa5..3b2e90830 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -152,6 +152,8 @@ extern int #include "common/debug.hpp" #include "net/ip6_address.hpp" +#include "resolver.hpp" + unsigned int gNetifIndex = 0; char gNetifName[IFNAMSIZ]; otIp4Cidr gNat64Cidr; @@ -209,6 +211,10 @@ static otIp6Prefix sAddedExternalRoutes[kMaxExternalRoutesNum]; static constexpr uint32_t kNat64RoutePriority = 100; ///< Priority for route to NAT64 CIDR, 100 means a high priority. #endif +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +ot::Posix::Resolver gResolver; +#endif + #if defined(RTM_NEWMADDR) || defined(__NetBSD__) // on some BSDs (mac OS, FreeBSD), we get RTM_NEWMADDR/RTM_DELMADDR messages, so we don't need to monitor using MLD // on NetBSD, MLD monitoring simply doesn't work @@ -1899,6 +1905,9 @@ void platformNetifSetUp(void) #if OPENTHREAD_POSIX_MULTICAST_PROMISCUOUS_REQUIRED otIp6SetMulticastPromiscuousEnabled(aInstance, true); #endif +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + gResolver.Init(); +#endif } void platformNetifTearDown(void) {} @@ -1957,6 +1966,10 @@ void platformNetifUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *a FD_SET(sMLDMonitorFd, aErrorFdSet); #endif +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + gResolver.UpdateFdSet(aReadFdSet, aErrorFdSet, aMaxFd); +#endif + if (sTunFd > *aMaxFd) { *aMaxFd = sTunFd; @@ -2019,6 +2032,10 @@ void platformNetifProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, c } #endif +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + gResolver.Process(aReadFdSet, aErrorFdSet); +#endif + exit: return; } diff --git a/src/posix/platform/resolver.cpp b/src/posix/platform/resolver.cpp new file mode 100644 index 000000000..6528ea450 --- /dev/null +++ b/src/posix/platform/resolver.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2023, 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. + */ + +#include "resolver.hpp" + +#include "platform-posix.h" + +#include +#include +#include +#include + +#include "common/code_utils.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + +namespace { +constexpr char kResolveConfLocation[] = "/etc/resolv.conf"; +constexpr char kNameserverItem[] = "nameserver"; +} // namespace + +extern ot::Posix::Resolver gResolver; + +namespace ot { +namespace Posix { + +void Resolver::Init(void) +{ + memset(mUpstreamTransaction, 0, sizeof(mUpstreamTransaction)); + + LoadDnsServerListFromConf(); +} + +void Resolver::LoadDnsServerListFromConf(void) +{ + std::string line; + std::ifstream fp; + + fp.open(kResolveConfLocation); + if (fp.bad()) + { + otLogCritPlat("Cannot read %s for domain name servers, default to 127.0.0.1", kResolveConfLocation); + mUpstreamDnsServerCount = 1; + assert(inet_pton(AF_INET, "127.0.0.1", &mUpstreamDnsServerList[0]) == 1); + ExitNow(); + } + + while (std::getline(fp, line) && mUpstreamDnsServerCount < kMaxUpstreamServerCount) + { + if (line.find(kNameserverItem, 0) == 0) + { + in_addr_t addr; + + if (inet_pton(AF_INET, &line.c_str()[sizeof(kNameserverItem)], &addr) == 1) + { + otLogCritPlat("Got nameserver #%d: %s", mUpstreamDnsServerCount, + &line.c_str()[sizeof(kNameserverItem)]); + mUpstreamDnsServerList[mUpstreamDnsServerCount] = addr; + mUpstreamDnsServerCount++; + } + } + } + +exit: + return; +} + +void Resolver::Query(otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery) +{ + char packet[kMaxDnsMessageSize]; + otError error = OT_ERROR_NONE; + uint16_t length = otMessageGetLength(aQuery); + sockaddr_in serverAddr; + + Transaction *txn; + + VerifyOrExit(length <= kMaxDnsMessageSize, error = OT_ERROR_NO_BUFS); + VerifyOrExit(otMessageRead(aQuery, 0, &packet, sizeof(packet)) == length, error = OT_ERROR_NO_BUFS); + + txn = AllocateTransaction(aTxn); + VerifyOrExit(txn != nullptr, error = OT_ERROR_NO_BUFS); + + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(53); + for (int i = 0; i < mUpstreamDnsServerCount; i++) + { + serverAddr.sin_addr.s_addr = mUpstreamDnsServerList[i]; + VerifyOrExit( + sendto(txn->mUdpFd, packet, length, MSG_DONTWAIT, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) > 0, + error = OT_ERROR_NO_ROUTE); + } + otLogInfoPlat("Forwarded DNS query %p to %d server(s).", static_cast(aTxn), mUpstreamDnsServerCount); + +exit: + if (error != OT_ERROR_NONE) + { + otLogCritPlat("Failed to forward DNS query %p to server: %d", static_cast(aTxn), error); + } + return; +} + +void Resolver::Cancel(otPlatDnsUpstreamQuery *aTxn) +{ + Transaction *txn = GetTransaction(aTxn); + + if (txn != nullptr) + { + CloseTransaction(txn); + } + + otPlatDnsUpstreamQueryDone(gInstance, aTxn, nullptr); +} + +Resolver::Transaction *Resolver::AllocateTransaction(otPlatDnsUpstreamQuery *aThreadTxn) +{ + int fdOrError = 0; + Transaction *ret = nullptr; + + for (Transaction &txn : mUpstreamTransaction) + { + if (txn.mThreadTxn == nullptr) + { + fdOrError = socket(AF_INET, SOCK_DGRAM, 0); + if (fdOrError < 0) + { + otLogInfoPlat("Failed to create socket for upstream resolver: %d", fdOrError); + break; + } + ret = &txn; + ret->mUdpFd = fdOrError; + ret->mThreadTxn = aThreadTxn; + break; + } + } + + return ret; +} + +void Resolver::ForwardResponse(Transaction *aTxn) +{ + char response[kMaxDnsMessageSize]; + ssize_t readSize; + otMessage *message; + + VerifyOrExit((readSize = read(aTxn->mUdpFd, response, sizeof(response))) > 0); + + message = otUdpNewMessage(gInstance, nullptr); + SuccessOrExit(otMessageAppend(message, response, readSize)); + + otPlatDnsUpstreamQueryDone(gInstance, aTxn->mThreadTxn, message); + message = nullptr; + +exit: + if (readSize < 0) + { + otLogInfoPlat("Failed to read response from upstream resolver socket: %d", errno); + } + if (message != nullptr) + { + otMessageFree(message); + } +} + +Resolver::Transaction *Resolver::GetTransaction(int aFd) +{ + Transaction *ret = nullptr; + + for (Transaction &txn : mUpstreamTransaction) + { + if (txn.mThreadTxn != nullptr && txn.mUdpFd == aFd) + { + ret = &txn; + break; + } + } + + return ret; +} + +Resolver::Transaction *Resolver::GetTransaction(otPlatDnsUpstreamQuery *aThreadTxn) +{ + Transaction *ret = nullptr; + + for (Transaction &txn : mUpstreamTransaction) + { + if (txn.mThreadTxn == aThreadTxn) + { + ret = &txn; + break; + } + } + + return ret; +} + +void Resolver::CloseTransaction(Transaction *aTxn) +{ + if (aTxn->mUdpFd >= 0) + { + close(aTxn->mUdpFd); + aTxn->mUdpFd = -1; + } + aTxn->mThreadTxn = nullptr; +} + +void Resolver::UpdateFdSet(fd_set *aReadFdSet, fd_set *aErrorFdSet, int *aMaxFd) +{ + for (Transaction &txn : mUpstreamTransaction) + { + if (txn.mThreadTxn != nullptr) + { + FD_SET(txn.mUdpFd, aReadFdSet); + FD_SET(txn.mUdpFd, aErrorFdSet); + if (txn.mUdpFd > *aMaxFd) + { + *aMaxFd = txn.mUdpFd; + } + } + } +} + +void Resolver::Process(const fd_set *aReadFdSet, const fd_set *aErrorFdSet) +{ + for (Transaction &txn : mUpstreamTransaction) + { + if (txn.mThreadTxn != nullptr) + { + // Note: On Linux, we can only get the error via read, so they should share the same logic. + if (FD_ISSET(txn.mUdpFd, aErrorFdSet) || FD_ISSET(txn.mUdpFd, aReadFdSet)) + { + ForwardResponse(&txn); + CloseTransaction(&txn); + } + } + } +} + +} // namespace Posix +} // namespace ot + +void otPlatDnsStartUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery) +{ + OT_UNUSED_VARIABLE(aInstance); + + gResolver.Query(aTxn, aQuery); +} + +void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn) +{ + OT_UNUSED_VARIABLE(aInstance); + + gResolver.Cancel(aTxn); +} + +#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE diff --git a/src/posix/platform/resolver.hpp b/src/posix/platform/resolver.hpp new file mode 100644 index 000000000..fe967d772 --- /dev/null +++ b/src/posix/platform/resolver.hpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2023, 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. + */ + +#ifndef POSIX_PLATFORM_RESOLVER_HPP_ +#define POSIX_PLATFORM_RESOLVER_HPP_ + +#include + +#include +#include + +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + +namespace ot { +namespace Posix { + +class Resolver +{ +public: + constexpr static ssize_t kMaxDnsMessageSize = 512; + constexpr static ssize_t kMaxUpstreamTransactionCount = 16; + constexpr static ssize_t kMaxUpstreamServerCount = 3; + + /** + * This method initialize the upstream DNS resolver. + * + */ + void Init(void); + + /** + * Sends the query to the upstream. + * + * @param[in] aTxn A pointer to the OpenThread upstream DNS query transaction. + * @param[in] aQuery A pointer to a message for the payload of the DNS query. + * + */ + void Query(otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery); + + /** + * Cancels a upstream DNS query transaction. + * + * @param[in] aTxn A pointer to the OpenThread upstream DNS query transaction. + * + */ + void Cancel(otPlatDnsUpstreamQuery *aTxn); + + /** + * Updates the file descriptor sets with file descriptors used by the radio driver. + * + * @param[in,out] aReadFdSet A reference to the read file descriptors. + * @param[in,out] aErrorFdSet A reference to the error file descriptors. + * @param[in,out] aMaxFd A reference to the max file descriptor. + * @param[in,out] aTimeout A reference to the timeout. + * + */ + void UpdateFdSet(fd_set *aReadFdSet, fd_set *aErrorFdSet, int *aMaxFd); + + /** + * Handles the result of select. + * + * @param[in] aReadFdSet A reference to the read file descriptors. + * @param[in] aErrorFdSet A reference to the error file descriptors. + * + */ + void Process(const fd_set *aReadFdSet, const fd_set *aErrorFdSet); + +private: + struct Transaction + { + otPlatDnsUpstreamQuery *mThreadTxn; + int mUdpFd; + }; + + Transaction *GetTransaction(int aFd); + Transaction *GetTransaction(otPlatDnsUpstreamQuery *aThreadTxn); + Transaction *AllocateTransaction(otPlatDnsUpstreamQuery *aThreadTxn); + + void ForwardResponse(Transaction *aTxn); + void CloseTransaction(Transaction *aTxn); + void FinishTransaction(int aFd); + void LoadDnsServerListFromConf(void); + + int mUpstreamDnsServerCount = 0; + in_addr_t mUpstreamDnsServerList[kMaxUpstreamServerCount]; + + Transaction mUpstreamTransaction[kMaxUpstreamTransactionCount]; +}; + +} // namespace Posix +} // namespace ot + +#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + +#endif // POSIX_PLATFORM_RESOLVER_HPP_ diff --git a/tests/unit/test_dns.cpp b/tests/unit/test_dns.cpp index 7214f2ce4..6df6b3bd1 100644 --- a/tests/unit/test_dns.cpp +++ b/tests/unit/test_dns.cpp @@ -67,6 +67,7 @@ void TestDnsName(void) char name[Dns::Name::kMaxNameSize]; const char *subDomain; const char *domain; + const char *domain2; static const uint8_t kEncodedName1[] = {7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0}; static const uint8_t kEncodedName2[] = {3, 'f', 'o', 'o', 1, 'a', 2, 'b', 'b', 3, 'e', 'd', 'u', 0}; @@ -210,6 +211,42 @@ void TestDnsName(void) domain = "Vice.arpa."; VerifyOrQuit(!Dns::Name::IsSubDomainOf(subDomain, domain)); + domain = "example.com."; + domain2 = "example.com."; + VerifyOrQuit(Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = "example.com"; + VerifyOrQuit(Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = "ExAmPlE.cOm"; + VerifyOrQuit(Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com"; + domain2 = "ExAmPlE.cOm"; + VerifyOrQuit(Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = "ExAmPlE.cOm."; + VerifyOrQuit(Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = "aExAmPlE.cOm."; + VerifyOrQuit(!Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = "cOm."; + VerifyOrQuit(!Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example."; + domain2 = "example.com."; + VerifyOrQuit(!Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = ".example.com."; + VerifyOrQuit(!Dns::Name::IsSameDomain(domain, domain2)); + printf("----------------------------------------------------------------\n"); printf("Append names, check encoded bytes, parse name and read labels:\n"); diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 81e73da2f..e2ef71986 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -505,4 +505,18 @@ otError otPlatUdpLeaveMulticastGroup(otUdpSocket *aUdpSocket, } #endif // OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +void otPlatDnsStartUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aTxn); + OT_UNUSED_VARIABLE(aQuery); +} + +void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn) +{ + otPlatDnsUpstreamQueryDone(aInstance, aTxn, nullptr); +} +#endif + } // extern "C" diff --git a/tests/unit/test_platform.h b/tests/unit/test_platform.h index 2944e5225..ec5f88709 100644 --- a/tests/unit/test_platform.h +++ b/tests/unit/test_platform.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include