mirror of
https://github.com/espressif/openthread.git
synced 2026-07-30 07:37:46 +00:00
[dnssd] support forwarding DNS queries to platform upstream (#8668)
This commit adds a set of APsI for forwarding a DNS query to the
upstream DNS server via platform API and a posix platform
implementation.
This feature is disabled by default, and this PR adds a `dns server
upstream {enable|disable}` command CLI for enabling and disabling this
feature.
Tests will be added after ot-br-posix adds related support.
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <openthread/instance.h>
|
||||
#include <openthread/message.h>
|
||||
|
||||
#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
|
||||
@@ -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'
|
||||
|
||||
@@ -3334,6 +3334,37 @@ template <> otError Interpreter::Process<Cmd("dns")>(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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -86,4 +86,11 @@ const otDnssdCounters *otDnssdGetCounters(otInstance *aInstance)
|
||||
return &AsCoreType(aInstance).Get<Dns::ServiceDiscovery::Server>().GetCounters();
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
|
||||
void otDnssdUpstreamQuerySetEnabled(otInstance *aInstance, bool aEnabled)
|
||||
{
|
||||
return AsCoreType(aInstance).Get<Dns::ServiceDiscovery::Server>().SetUpstreamQueryEnabled(aEnabled);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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 <openthread/instance.h>
|
||||
#include <openthread/platform/dns.h>
|
||||
|
||||
#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<Dns::ServiceDiscovery::Server>().OnUpstreamQueryDone(AsCoreType(aTxn),
|
||||
AsCoreTypePtr(aResponse));
|
||||
}
|
||||
#endif
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
+190
-10
@@ -35,6 +35,8 @@
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
|
||||
|
||||
#include <openthread/platform/dns.h>
|
||||
|
||||
#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<int>(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<int>(&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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 <openthread/logging.h>
|
||||
#include <openthread/message.h>
|
||||
#include <openthread/udp.h>
|
||||
#include <openthread/platform/dns.h>
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/nameser.h>
|
||||
#include <cassert>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#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<void *>(aTxn), mUpstreamDnsServerCount);
|
||||
|
||||
exit:
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
otLogCritPlat("Failed to forward DNS query %p to server: %d", static_cast<void *>(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
|
||||
@@ -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 <openthread/platform/dns.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#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_
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <openthread/config.h>
|
||||
#include <openthread/platform/alarm-milli.h>
|
||||
#include <openthread/platform/dns.h>
|
||||
#include <openthread/platform/dso_transport.h>
|
||||
#include <openthread/platform/entropy.h>
|
||||
#include <openthread/platform/logging.h>
|
||||
|
||||
Reference in New Issue
Block a user