diff --git a/.travis/script.sh b/.travis/script.sh index 7547327a2..e9e3a55b5 100755 --- a/.travis/script.sh +++ b/.travis/script.sh @@ -37,7 +37,7 @@ set -x [ $BUILD_TARGET != pretty-check ] || { export PATH=/tmp/astyle/build/gcc/bin:$PATH || die ./bootstrap || die - ./configure --enable-ftd --enable-cli --enable-diag --enable-dhcp6-client --enable-dhcp6-server --enable-commissioner --enable-joiner --with-examples=posix || die + ./configure --enable-ftd --enable-cli --enable-diag --enable-dhcp6-client --enable-dhcp6-server --enable-dns-client --enable-commissioner --enable-joiner --with-examples=posix || die make pretty-check || die } @@ -53,7 +53,7 @@ set -x git checkout -- . || die git clean -xfd || die ./bootstrap || die - COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 make -f examples/Makefile-cc2538 || die + COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-cc2538 || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-ftd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-mtd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-ncp-ftd || die @@ -62,7 +62,7 @@ set -x git checkout -- . || die git clean -xfd || die ./bootstrap || die - COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 make -f examples/Makefile-da15000 || die + COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-da15000 || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-ftd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-mtd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-ncp-ftd || die @@ -71,7 +71,7 @@ set -x git checkout -- . || die git clean -xfd || die ./bootstrap || die - COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 make -f examples/Makefile-nrf52840 || die + COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-nrf52840 || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-ftd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-mtd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-ncp-ftd || die @@ -91,7 +91,7 @@ set -x git checkout -- . || die git clean -xfd || die ./bootstrap || die - COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 make -f examples/Makefile-cc2538 || die + COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-cc2538 || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-ftd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-mtd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-ncp-ftd || die @@ -100,7 +100,7 @@ set -x git checkout -- . || die git clean -xfd || die ./bootstrap || die - COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 make -f examples/Makefile-da15000 || die + COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-da15000 || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-ftd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-mtd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-ncp-ftd || die @@ -109,7 +109,7 @@ set -x git checkout -- . || die git clean -xfd || die ./bootstrap || die - COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 make -f examples/Makefile-nrf52840 || die + COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-nrf52840 || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-ftd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-cli-mtd || die arm-none-eabi-size output/bin/arm-none-eabi-ot-ncp-ftd || die diff --git a/Makefile.am b/Makefile.am index e15db519a..87dba9c8b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,6 +41,7 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \ --enable-joiner \ --enable-dhcp6-client \ --enable-dhcp6-server \ + --enable-dns-client \ $(NULL) SUBDIRS = \ diff --git a/configure.ac b/configure.ac index 2ba9f3484..f60cf5f66 100644 --- a/configure.ac +++ b/configure.ac @@ -758,6 +758,36 @@ AC_SUBST(OPENTHREAD_ENABLE_DHCP6_SERVER) AM_CONDITIONAL([OPENTHREAD_ENABLE_DHCP6_SERVER], [test "${enable_dhcp6_server}" = "yes"]) AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_DHCP6_SERVER],[${OPENTHREAD_ENABLE_DHCP6_SERVER}],[Define to 1 if you want to enable DHCPv6 Server]) +# +# DNS Client +# + +AC_ARG_ENABLE(dns_client, + [AS_HELP_STRING([--enable-dns-client],[Enable DNS client support @<:@default=no@:>@.])], + [ + case "${enableval}" in + + no|yes) + enable_dns_client=${enableval} + ;; + + *) + AC_MSG_ERROR([Invalid value ${enable_dns_client} for --enable-dns-client]) + ;; + esac + ], + [enable_dns_client=no]) + +if test "$enable_dns_client" = "yes"; then + OPENTHREAD_ENABLE_DNS_CLIENT=1 +else + OPENTHREAD_ENABLE_DNS_CLIENT=0 +fi + +AC_SUBST(OPENTHREAD_ENABLE_DNS_CLIENT) +AM_CONDITIONAL([OPENTHREAD_ENABLE_DNS_CLIENT], [test "${enable_dns_client}" = "yes"]) +AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_DNS_CLIENT],[${OPENTHREAD_ENABLE_DNS_CLIENT}],[Define to 1 if you want to enable DNS Client]) + # # Application CoAP # @@ -1109,6 +1139,7 @@ AC_MSG_NOTICE([ OpenThread Certification log support : ${enable_cert_log} OpenThread DHCPv6 Server support : ${enable_dhcp6_server} OpenThread DHCPv6 Client support : ${enable_dhcp6_client} + OpenThread DNS Client support : ${enable_dns_client} Openthread Application CoAP support : ${enable_application_coap} Openthread Raw Link-Layer support : ${enable_raw_link_api} OpenThread examples : ${OPENTHREAD_EXAMPLES} diff --git a/examples/Makefile-nrf52840 b/examples/Makefile-nrf52840 index 4dc53c82e..fc47629a1 100644 --- a/examples/Makefile-nrf52840 +++ b/examples/Makefile-nrf52840 @@ -91,8 +91,12 @@ ifeq ($(DHCP6_CLIENT),1) configure_OPTIONS += --enable-dhcp6-client endif +ifeq ($(DNS_CLIENT),1) +configure_OPTIONS += --enable-dns-client +endif + ifeq ($(COVERAGE),1) -configure_OPTIONS += --enable-coverage --enable-commissioner --enable-joiner --enable-dhcp6-client --enable-dhcp6-server +configure_OPTIONS += --enable-coverage --enable-commissioner --enable-joiner --enable-dhcp6-client --enable-dhcp6-server --enable-dns-client endif ifeq ($(DEBUG),1) diff --git a/examples/Makefile-posix b/examples/Makefile-posix index 0aaddda4c..851647d38 100644 --- a/examples/Makefile-posix +++ b/examples/Makefile-posix @@ -95,7 +95,7 @@ configure_OPTIONS = \ $(NULL) ifeq ($(COVERAGE),1) -configure_OPTIONS += --enable-coverage --enable-commissioner --enable-joiner --enable-dhcp6-client --enable-dhcp6-server +configure_OPTIONS += --enable-coverage --enable-commissioner --enable-joiner --enable-dhcp6-client --enable-dhcp6-server --enable-dns-client else configure_OPTIONS += endif @@ -295,4 +295,3 @@ help: $(ECHO) "" $(ECHO) " $(TargetTuple)" $(ECHO) "" - diff --git a/include/openthread/Makefile.am b/include/openthread/Makefile.am index 2bc0a06ba..f87d2e5cf 100644 --- a/include/openthread/Makefile.am +++ b/include/openthread/Makefile.am @@ -55,6 +55,7 @@ openthread_headers = \ diag.h \ dhcp6_client.h \ dhcp6_server.h \ + dns.h \ icmp6.h \ instance.h \ ip6.h \ diff --git a/include/openthread/dns.h b/include/openthread/dns.h new file mode 100644 index 000000000..341cb21c5 --- /dev/null +++ b/include/openthread/dns.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017, 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 top-level dns functions for the OpenThread library. + */ + +#ifndef OPENTHREAD_DNS_H_ +#define OPENTHREAD_DNS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup dns DNS + * + * @brief + * This module includes functions that control DNS communication. + * + * @{ + * + */ + +#define OT_DNS_MAX_HOSTNAME_LENGTH 62 ///< Maximum allowed hostname length (maximum label size - 1 for compression). + +#define OT_DNS_DEFAULT_DNS_SERVER_IP "2001:4860:4860::8888" ///< Defines default DNS Server address - Google DNS. +#define OT_DNS_DEFAULT_DNS_SERVER_PORT 53 ///< Defines default DNS Server port. + +/** + * This structure implements DNS Query parameters. + * + */ +typedef struct otDnsQuery +{ + const char *mHostname; ///< Identifies hostname to be found. It shall not change during resolving. + const otMessageInfo *mMessageInfo; ///< A reference to the message info related with DNS Server. + bool mNoRecursion; ///< If cleared, it directs name server to pursue the query recursively. +} otDnsQuery; + +/** + * This function pointer is called when a DNS response is received. + * + * @param[in] aContext A pointer to application-specific context. + * @param[in] aHostname Identifies hostname related with DNS response. + * @param[in] aAddress A pointer to the IPv6 address received in DNS response. May be null. + * @param[in] aTtl Specifies the maximum time in seconds that the resource record may be cached. + * @param[in] aResult A result of the DNS transaction. + * + * @retval kThreadError_None A response was received successfully and IPv6 address is provided + * in @p aAddress. + * @retval kThreadError_Abort A DNS transaction was aborted by stack. + * @retval kThreadError_ResponseTimeout No DNS response has been received within timeout. + * @retval kThreadError_NotFound A response was received but no IPv6 address has been found. + * @retval kThreadError_Failed A response was received but status code is different than success. + * + */ +typedef void (*otDnsResponseHandler)(void *aContext, const char *aHostname, otIp6Address *aAddress, + uint32_t aTtl, ThreadError aResult); + +#if OPENTHREAD_ENABLE_DNS_CLIENT +/** + * This function sends a DNS query for AAAA (IPv6) record. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aQuery A pointer to specify DNS query parameters. + * @param[in] aHandler A function pointer that shall be called on response reception or time-out. + * @param[in] aContext A pointer to arbitrary context information. + * + */ +ThreadError otDnsClientQuery(otInstance *aInstance, const otDnsQuery *aQuery, otDnsResponseHandler aHandler, + void *aContext); +#endif + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_DNS_H_ diff --git a/include/openthread/types.h b/include/openthread/types.h index 64e346fc9..c501c7f5c 100644 --- a/include/openthread/types.h +++ b/include/openthread/types.h @@ -185,7 +185,7 @@ typedef enum ThreadError kThreadError_NotCapable = 29, /** - * Coap response or acknowledgment not received. + * Coap response or acknowledgment or DNS response not received. */ kThreadError_ResponseTimeout = 30, diff --git a/src/cli/README.md b/src/cli/README.md index b04d074ed..c0be2d6ee 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -20,6 +20,7 @@ OpenThread test scripts use the CLI to execute test cases. * [dataset](#dataset-help) * [delaytimermin](#delaytimermin) * [discover](#discover-channel) +* [dns](#dns-resolve-hostname-dns-server-ip-dns-server-port) * [eidcache](#eidcache) * [eui64](#eui64) * [extaddr](#extaddr) @@ -463,7 +464,7 @@ pendingtimestamp pskc securitypolicy Done -> +> ``` ### dataset active @@ -686,6 +687,18 @@ Perform an MLE Discovery operation. Done ``` +### dns resolve \ \[DNS server IP\] \[DNS server port\] + +Send DNS Query to obtain IPv6 address for given hostname. +The latter two parameters have following default values: + * DNS server IP: 2001:4860:4860::8888 (Google DNS Server) + * DNS server port: 53 + +```bash +> dns resolve ipv6.google.com +> DNS response for ipv6.google.com - [2a00:1450:401b:801:0:0:0:200e] TTL: 300 +``` + ### eidcache Print the EID-to-RLOC cache entries. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index b0512d52f..f94573845 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -95,6 +95,9 @@ const struct Command Interpreter::sCommands[] = { "diag", &Interpreter::ProcessDiag }, #endif { "discover", &Interpreter::ProcessDiscover }, +#if OPENTHREAD_ENABLE_DNS_CLIENT + { "dns", &Interpreter::ProcessDns }, +#endif { "eidcache", &Interpreter::ProcessEidCache }, { "eui64", &Interpreter::ProcessEui64 }, #ifdef OPENTHREAD_EXAMPLES_POSIX @@ -193,6 +196,9 @@ Interpreter::Interpreter(otInstance *aInstance): sCount(1), sInterval(1000), sPingTimer(aInstance->mIp6.mTimerScheduler, &Interpreter::s_HandlePingTimer, this), +#if OPENTHREAD_ENABLE_DNS_CLIENT + mResolvingInProgress(0), +#endif #endif mInstance(aInstance) { @@ -211,6 +217,11 @@ Interpreter::Interpreter(otInstance *aInstance): #if OPENTHREAD_ENABLE_DHCP6_CLIENT memset(mDhcpAddresses, 0, sizeof(mDhcpAddresses)); #endif // OPENTHREAD_ENABLE_DHCP6_CLIENT + +#if OPENTHREAD_ENABLE_DNS_CLIENT + memset(mResolvingHostname, 0, sizeof(mResolvingHostname)); +#endif // OPENTHREAD_ENABLE_DNS_CLIENT + #endif } @@ -708,6 +719,96 @@ exit: AppendResult(error); } +#if OPENTHREAD_ENABLE_DNS_CLIENT +void Interpreter::ProcessDns(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + long port = OT_DNS_DEFAULT_DNS_SERVER_PORT; + Ip6::MessageInfo messageInfo; + otDnsQuery query; + + VerifyOrExit(argc > 0, error = kThreadError_InvalidArgs); + + if (strcmp(argv[0], "resolve") == 0) + { + VerifyOrExit(!mResolvingInProgress, error = kThreadError_Busy); + VerifyOrExit(argc > 1, error = kThreadError_InvalidArgs); + VerifyOrExit(strlen(argv[1]) < OT_DNS_MAX_HOSTNAME_LENGTH, error = kThreadError_InvalidArgs); + + strcpy(mResolvingHostname, argv[1]); + + memset(&messageInfo, 0, sizeof(messageInfo)); + messageInfo.mInterfaceId = OT_NETIF_INTERFACE_ID_THREAD; + + if (argc > 2) + { + SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(argv[2])); + } + else + { + // Use IPv6 address of default DNS server. + SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_DNS_DEFAULT_DNS_SERVER_IP)); + } + + if (argc > 3) + { + SuccessOrExit(error = ParseLong(argv[3], port)); + } + + messageInfo.SetPeerPort(static_cast(port)); + + query.mHostname = mResolvingHostname; + query.mMessageInfo = static_cast(&messageInfo); + query.mNoRecursion = false; + + SuccessOrExit(error = otDnsClientQuery(mInstance, &query, &Interpreter::s_HandleDnsResponse, this)); + + mResolvingInProgress = true; + return; + } + else + { + ExitNow(error = kThreadError_InvalidArgs); + } + +exit: + AppendResult(error); +} + +void Interpreter::s_HandleDnsResponse(void *aContext, const char *aHostname, otIp6Address *aAddress, + uint32_t aTtl, ThreadError aResult) +{ + static_cast(aContext)->HandleDnsResponse(aHostname, + *static_cast(aAddress), + aTtl, aResult); +} + +void Interpreter::HandleDnsResponse(const char *aHostname, Ip6::Address &aAddress, uint32_t aTtl, ThreadError aResult) +{ + sServer->OutputFormat("DNS response for %s - ", aHostname); + + if (aResult == kThreadError_None) + { + sServer->OutputFormat("[%x:%x:%x:%x:%x:%x:%x:%x] TTL: %d\r\n", + HostSwap16(aAddress.mFields.m16[0]), + HostSwap16(aAddress.mFields.m16[1]), + HostSwap16(aAddress.mFields.m16[2]), + HostSwap16(aAddress.mFields.m16[3]), + HostSwap16(aAddress.mFields.m16[4]), + HostSwap16(aAddress.mFields.m16[5]), + HostSwap16(aAddress.mFields.m16[6]), + HostSwap16(aAddress.mFields.m16[7]), + aTtl); + } + else + { + AppendResult(aResult); + } + + mResolvingInProgress = false; +} +#endif + void Interpreter::ProcessEidCache(int argc, char *argv[]) { otEidCacheEntry entry; diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 36cbafd53..9789af3ed 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -53,6 +53,7 @@ #include #include #include "openthread/dhcp6_client.h" +#include "openthread/dns.h" #endif #ifdef OTDLL @@ -172,6 +173,9 @@ private: void ProcessDiag(int argc, char *argv[]); #endif // OPENTHREAD_ENABLE_DIAG void ProcessDiscover(int argc, char *argv[]); +#if OPENTHREAD_ENABLE_DNS_CLIENT + void ProcessDns(int argc, char *argv[]); +#endif void ProcessEidCache(int argc, char *argv[]); void ProcessEui64(int argc, char *argv[]); #ifdef OPENTHREAD_EXAMPLES_POSIX @@ -257,6 +261,11 @@ private: #endif static void OTCALL s_HandleJoinerCallback(ThreadError aError, void *aContext); +#if OPENTHREAD_ENABLE_DNS_CLIENT + static void s_HandleDnsResponse(void *aContext, const char *aHostname, otIp6Address *aAddress, + uint32_t aTtl, ThreadError aResult); +#endif + #ifndef OTDLL void HandleIcmpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const Ip6::IcmpHeader &aIcmpHeader); @@ -278,6 +287,10 @@ private: #endif void HandleJoinerCallback(ThreadError aError); +#if OPENTHREAD_ENABLE_DNS_CLIENT + void HandleDnsResponse(const char *aHostname, Ip6::Address &aAddress, uint32_t aTtl, ThreadError aResult); +#endif + static const struct Command sCommands[]; Server *sServer; @@ -312,6 +325,11 @@ private: #endif // OPENTHREAD_ENABLE_DHCP6_CLIENT otIcmp6Handler mIcmpHandler; +#if OPENTHREAD_ENABLE_DNS_CLIENT + bool mResolvingInProgress; + char mResolvingHostname[OT_DNS_MAX_HOSTNAME_LENGTH]; +#endif + #endif otInstance *mInstance; diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 69c453c12..10e9cc4da 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -147,6 +147,13 @@ SOURCES_COMMON += \ $(NULL) endif # OPENTHREAD_ENABLE_DHCP6_SERVER +if OPENTHREAD_ENABLE_DNS_CLIENT +SOURCES_COMMON += \ + api/dns_api.cpp \ + net/dns_client.cpp \ + $(NULL) +endif # OPENTHREAD_ENABLE_DNS_CLIENT + if OPENTHREAD_ENABLE_JAM_DETECTION SOURCES_COMMON += \ api/jam_detection_api.cpp \ @@ -249,6 +256,11 @@ noinst_HEADERS = \ meshcop/panid_query_client.hpp \ meshcop/timestamp.hpp \ meshcop/tlvs.hpp \ + net/dhcp6.hpp \ + net/dhcp6_client.hpp \ + net/dhcp6_server.hpp \ + net/dns_client.hpp \ + net/dns_headers.hpp \ net/icmp6.hpp \ net/ip6.hpp \ net/ip6_address.hpp \ @@ -258,11 +270,8 @@ noinst_HEADERS = \ net/ip6_routes.hpp \ net/netif.hpp \ net/socket.hpp \ - net/udp6.hpp \ net/tcp.hpp \ - net/dhcp6.hpp \ - net/dhcp6_client.hpp \ - net/dhcp6_server.hpp \ + net/udp6.hpp \ thread/address_resolver.hpp \ thread/address_resolver_ftd.hpp \ thread/address_resolver_mtd.hpp \ diff --git a/src/core/api/dns_api.cpp b/src/core/api/dns_api.cpp new file mode 100644 index 000000000..e873d1935 --- /dev/null +++ b/src/core/api/dns_api.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017, 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 OpenThread UDP API. + */ + +#include "openthread/dns.h" + +#include "openthread-instance.h" + +using namespace Thread; + +#ifdef __cplusplus +extern "C" { +#endif + +#if OPENTHREAD_ENABLE_DNS_CLIENT +ThreadError otDnsClientQuery(otInstance *aInstance, const otDnsQuery *aQuery, otDnsResponseHandler aHandler, + void *aContext) +{ + return aInstance->mThreadNetif.GetDnsClient().Query(aQuery, aHandler, aContext); +} +#endif // OPENTHREAD_ENABLE_DNS_CLIENT + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/src/core/net/dns_client.cpp b/src/core/net/dns_client.cpp new file mode 100644 index 000000000..47e55f875 --- /dev/null +++ b/src/core/net/dns_client.cpp @@ -0,0 +1,517 @@ +/* + * Copyright (c) 2017, 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 + +#include +#include +#include +#include + +/** + * @file + * This file implements the DNS client. + */ + +using Thread::Encoding::BigEndian::HostSwap16; + +namespace Thread { +namespace Dns { + +ThreadError Client::Start(void) +{ + ThreadError error; + Ip6::SockAddr addr; + + SuccessOrExit(error = mSocket.Open(&Client::HandleUdpReceive, this)); + SuccessOrExit(error = mSocket.Bind(addr)); + +exit: + return error; +} + +ThreadError Client::Stop(void) +{ + Message *message = mPendingQueries.GetHead(); + Message *messageToRemove; + QueryMetadata queryMetadata; + + // Remove all pending queries. + while (message != NULL) + { + messageToRemove = message; + message = message->GetNext(); + + queryMetadata.ReadFrom(*messageToRemove); + FinalizeDnsTransaction(*message, queryMetadata, NULL, 0, kThreadError_Abort); + } + + return mSocket.Close(); +} + +ThreadError Client::Query(const otDnsQuery *aQuery, otDnsResponseHandler aHandler, void *aContext) +{ + ThreadError error; + QueryMetadata queryMetadata(aHandler, aContext); + Message *message = NULL; + Message *messageCopy = NULL; + Header header; + QuestionAaaa question; + const Ip6::MessageInfo *messageInfo; + + VerifyOrExit(aQuery->mHostname != NULL && aQuery->mMessageInfo != NULL, + error = kThreadError_InvalidArgs); + + header.SetMessageId(mMessageId++); + header.SetType(Header::kTypeQuery); + header.SetQueryType(Header::kQueryTypeStandard); + + if (!aQuery->mNoRecursion) + { + header.SetRecursionDesiredFlag(); + } + + header.SetQuestionCount(1); + + VerifyOrExit((message = NewMessage(header)) != NULL, error = kThreadError_NoBufs); + + SuccessOrExit(error = AppendCompressedHostname(*message, aQuery->mHostname)); + SuccessOrExit(error = question.AppendTo(*message)); + + messageInfo = static_cast(aQuery->mMessageInfo); + + queryMetadata.mHostname = aQuery->mHostname; + queryMetadata.mTransmissionTime = Timer::GetNow() + kResponseTimeout; + queryMetadata.mSourceAddress = messageInfo->GetSockAddr(); + queryMetadata.mDestinationPort = messageInfo->GetPeerPort(); + queryMetadata.mDestinationAddress = messageInfo->GetPeerAddr(); + queryMetadata.mRetransmissionCount = 0; + + VerifyOrExit((messageCopy = CopyAndEnqueueMessage(*message, queryMetadata)) != NULL, + error = kThreadError_NoBufs); + SuccessOrExit(error = SendMessage(*message, *messageInfo)); + +exit: + + if (error != kThreadError_None) + { + if (message) + { + message->Free(); + } + + if (messageCopy) + { + DequeueMessage(*messageCopy); + } + } + + return error; +} + +Message *Client::NewMessage(const Header &aHeader) +{ + Message *message = NULL; + + VerifyOrExit((message = mSocket.NewMessage(sizeof(aHeader))) != NULL, ;); + message->Prepend(&aHeader, sizeof(aHeader)); + message->SetOffset(0); + +exit: + return message; +} + +Message *Client::CopyAndEnqueueMessage(const Message &aMessage, const QueryMetadata &aQueryMetadata) +{ + ThreadError error = kThreadError_None; + uint32_t now = Timer::GetNow(); + Message *messageCopy = NULL; + uint32_t nextTransmissionTime; + + // Create a message copy for further retransmissions. + VerifyOrExit((messageCopy = aMessage.Clone()) != NULL, error = kThreadError_NoBufs); + + // Append the copy with retransmission data and add it to the queue. + SuccessOrExit(error = aQueryMetadata.AppendTo(*messageCopy)); + mPendingQueries.Enqueue(*messageCopy); + + // Setup the timer. + if (mRetransmissionTimer.IsRunning()) + { + // If timer is already running, check if it should be restarted with earlier fire time. + nextTransmissionTime = mRetransmissionTimer.Gett0() + mRetransmissionTimer.Getdt(); + + if (aQueryMetadata.IsEarlier(nextTransmissionTime)) + { + mRetransmissionTimer.Start(aQueryMetadata.mTransmissionTime - now); + } + } + else + { + mRetransmissionTimer.Start(aQueryMetadata.mTransmissionTime - now); + } + +exit: + + if (error != kThreadError_None && messageCopy != NULL) + { + messageCopy->Free(); + messageCopy = NULL; + } + + return messageCopy; +} + +void Client::DequeueMessage(Message &aMessage) +{ + mPendingQueries.Dequeue(aMessage); + + if (mRetransmissionTimer.IsRunning() && (mPendingQueries.GetHead() == NULL)) + { + // No more requests pending, stop the timer. + mRetransmissionTimer.Stop(); + } + + // Free the message memory. + aMessage.Free(); +} + +ThreadError Client::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + return mSocket.SendTo(aMessage, aMessageInfo); +} + +ThreadError Client::SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + ThreadError error; + Message *messageCopy = NULL; + + // Create a message copy for lower layers. + VerifyOrExit((messageCopy = aMessage.Clone(aMessage.GetLength() - sizeof(QueryMetadata))) != NULL, + error = kThreadError_NoBufs); + + // Send the copy. + SuccessOrExit(error = SendMessage(*messageCopy, aMessageInfo)); + +exit: + + if (error != kThreadError_None && messageCopy != NULL) + { + messageCopy->Free(); + } + + return error; +} + +ThreadError Client::AppendCompressedHostname(Message &aMessage, const char *aHostname) +{ + ThreadError error = kThreadError_None; + uint8_t index = 0; + uint8_t labelPosition = 0; + uint8_t labelSize = 0; + + while (true) + { + // Look for string separator. + if (aHostname[index] == kLabelSeparator || aHostname[index] == kLabelTerminator) + { + VerifyOrExit(labelSize > 0, error = kThreadError_InvalidArgs); + SuccessOrExit(error = aMessage.Append(&labelSize, 1)); + SuccessOrExit(error = aMessage.Append(&aHostname[labelPosition], labelSize)); + + labelPosition += labelSize + 1; + labelSize = 0; + + if (aHostname[index] == kLabelTerminator) + { + break; + } + } + else + { + labelSize++; + } + + index++; + } + + // Add termination character at the end. + labelSize = kLabelTerminator; + SuccessOrExit(error = aMessage.Append(&labelSize, 1)); + +exit: + return error; +} + +ThreadError Client::CompareQuestions(Message &aMessageResponse, Message &aMessageQuery, uint16_t &aOffset) +{ + ThreadError error = kThreadError_None; + uint8_t bufQuery[kBufSize]; + uint8_t bufResponse[kBufSize]; + uint16_t read = 0; + + // Compare question section of the query with the response. + uint16_t length = aMessageQuery.GetLength() - aMessageQuery.GetOffset() - + sizeof(Header) - sizeof(QueryMetadata); + uint16_t offset = aMessageQuery.GetOffset() + sizeof(Header); + + while (length > 0) + { + VerifyOrExit((read = aMessageQuery.Read(offset, + length < sizeof(bufQuery) ? length : sizeof(bufQuery), + bufQuery)) > 0, error = kThreadError_Parse); + VerifyOrExit(aMessageResponse.Read(aOffset, read, bufResponse) == read, + error = kThreadError_Parse); + + VerifyOrExit(memcmp(bufResponse, bufQuery, read) == 0, error = kThreadError_NotFound); + + aOffset += read; + offset += read; + length -= read; + } + +exit: + return error; +} + +ThreadError Client::SkipHostname(Message &aMessage, uint16_t &aOffset) +{ + ThreadError error = kThreadError_None; + uint8_t buf[kBufSize]; + uint16_t index; + uint16_t read = 0; + uint16_t offset = aOffset; + uint16_t length = aMessage.GetLength() - aOffset; + + while (length > 0) + { + VerifyOrExit((read = aMessage.Read(offset, sizeof(buf), buf)) > 0, + error = kThreadError_Parse); + + index = 0; + + while (index < read) + { + if (buf[index] == kLabelTerminator) + { + ExitNow(aOffset = offset + 1); + } + + if ((buf[index] & kCompressionOffsetMask) == kCompressionOffsetMask) + { + ExitNow(aOffset = offset + 2); + } + + index++; + offset++; + } + + length -= read; + } + + ExitNow(error = kThreadError_Parse); + +exit: + return error; +} + +Message *Client::FindRelatedQuery(const Header &aResponseHeader, QueryMetadata &aQueryMetadata) +{ + uint16_t messageId; + Message *message = mPendingQueries.GetHead(); + + while (message != NULL) + { + // Partially read DNS header to obtain message ID only. + assert(message->Read(message->GetOffset(), sizeof(messageId), &messageId) == sizeof(messageId)); + + if (HostSwap16(messageId) == aResponseHeader.GetMessageId()) + { + aQueryMetadata.ReadFrom(*message); + ExitNow(); + } + + message = message->GetNext(); + } + +exit: + return message; +} + +void Client::FinalizeDnsTransaction(Message &aQuery, const QueryMetadata &aQueryMetadata, + otIp6Address *aAddress, uint32_t aTtl, + ThreadError aResult) +{ + DequeueMessage(aQuery); + + if (aQueryMetadata.mResponseHandler != NULL) + { + aQueryMetadata.mResponseHandler(aQueryMetadata.mResponseContext, aQueryMetadata.mHostname, + aAddress, aTtl, aResult); + } +} + +void Client::HandleRetransmissionTimer(void *aContext) +{ + static_cast(aContext)->HandleRetransmissionTimer(); +} + +void Client::HandleRetransmissionTimer(void) +{ + uint32_t now = Timer::GetNow(); + uint32_t nextDelta = 0xffffffff; + QueryMetadata queryMetadata; + Message *message = mPendingQueries.GetHead(); + Message *nextMessage = NULL; + Ip6::MessageInfo messageInfo; + + while (message != NULL) + { + nextMessage = message->GetNext(); + queryMetadata.ReadFrom(*message); + + if (queryMetadata.IsLater(now)) + { + // Calculate the next delay and choose the lowest. + if (queryMetadata.mTransmissionTime - now < nextDelta) + { + nextDelta = queryMetadata.mTransmissionTime - now; + } + } + else if (queryMetadata.mRetransmissionCount < kMaxRetransmit) + { + // Increment retransmission counter and timer. + queryMetadata.mRetransmissionCount++; + queryMetadata.mTransmissionTime = now + kResponseTimeout; + queryMetadata.UpdateIn(*message); + + // Check if retransmission time is lower than current lowest. + if (queryMetadata.mTransmissionTime - now < nextDelta) + { + nextDelta = queryMetadata.mTransmissionTime - now; + } + + // Retransmit + messageInfo.SetPeerAddr(queryMetadata.mDestinationAddress); + messageInfo.SetPeerPort(queryMetadata.mDestinationPort); + messageInfo.SetSockAddr(queryMetadata.mSourceAddress); + + SendCopy(*message, messageInfo); + } + else + { + // No expected response. + FinalizeDnsTransaction(*message, queryMetadata, NULL, 0, kThreadError_ResponseTimeout); + } + + message = nextMessage; + } + + if (nextDelta != 0xffffffff) + { + mRetransmissionTimer.Start(nextDelta); + } +} + +void Client::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +{ + static_cast(aContext)->HandleUdpReceive(*static_cast(aMessage), + *static_cast(aMessageInfo)); +} + +void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + ThreadError error = kThreadError_None; + Header responseHeader; + QueryMetadata queryMetadata; + ResourceRecordAaaa record; + Message *message = NULL; + uint16_t offset; + + // RFC1035 7.3. Resolver cannot rely that a response will come from the same address + // which it sent the corresponding query to. + (void)aMessageInfo; + + VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(responseHeader), &responseHeader) == + sizeof(responseHeader), ;); + VerifyOrExit(responseHeader.GetType() == Header::kTypeResponse && + responseHeader.GetQuestionCount() == 1 && + responseHeader.IsTruncationFlagSet() == false, ;); + + aMessage.MoveOffset(sizeof(responseHeader)); + offset = aMessage.GetOffset(); + + VerifyOrExit((message = FindRelatedQuery(responseHeader, queryMetadata)) != NULL,); + + if (responseHeader.GetResponseCode() != Header::kResponseSuccess) + { + ExitNow(error = kThreadError_Failed); + } + + // Parse and check the question section. + SuccessOrExit(error = CompareQuestions(aMessage, *message, offset)); + + // Parse and check the answer section. + for (uint32_t index = 0; index < responseHeader.GetAnswerCount(); index++) + { + SuccessOrExit(error = SkipHostname(aMessage, offset)); + + if (offset + sizeof(ResourceRecord) > aMessage.GetLength()) + { + ExitNow(error = kThreadError_Parse); + } + + if (aMessage.Read(offset, sizeof(record), &record) != sizeof(record) || + record.GetType() != ResourceRecordAaaa::kType || + record.GetClass() != ResourceRecordAaaa::kClass) + { + offset += sizeof(ResourceRecord) + record.GetLength(); + + continue; + } + + // Return the first found IPv6 address. + FinalizeDnsTransaction(*message, queryMetadata, &record.GetAddress(), record.GetTtl(), kThreadError_None); + + ExitNow(); + } + + ExitNow(error = kThreadError_NotFound); + +exit: + + if (message != NULL && error != kThreadError_None) + { + FinalizeDnsTransaction(*message, queryMetadata, NULL, 0, error); + } + + return; +} + +} // namespace Coap +} // namespace Thread diff --git a/src/core/net/dns_client.hpp b/src/core/net/dns_client.hpp new file mode 100644 index 000000000..d289272e5 --- /dev/null +++ b/src/core/net/dns_client.hpp @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2017, 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 DNS_CLIENT_HPP_ +#define DNS_CLIENT_HPP_ + +#include +#include + +#include +#include +#include +#include +#include + +/** + * @file + * This file includes definitions for the DNS client. + */ + +namespace Thread { +namespace Dns { + + +/** + * This class implements metadata required for DNS retransmission. + * + */ +OT_TOOL_PACKED_BEGIN +class QueryMetadata +{ + friend class Client; + +public: + /** + * Default constructor for the object. + * + */ + QueryMetadata(void) { memset(this, 0, sizeof(*this)); }; + + /** + * This constructor initializes the object with specific values. + * + * @param[in] aHandler Pointer to a handler function for the response. + * @param[in] aContext Context for the handler function. + * + */ + QueryMetadata(otDnsResponseHandler aHandler, void *aContext) { + memset(this, 0, sizeof(*this)); + mResponseHandler = aHandler; + mResponseContext = aContext; + }; + + /** + * This method appends request data to the message. + * + * @param[in] aMessage A reference to the message. + * + * @retval kThreadError_None Successfully appended the bytes. + * @retval kThreadError_NoBufs Insufficient available buffers to grow the message. + * + */ + ThreadError AppendTo(Message &aMessage) const { + return aMessage.Append(this, sizeof(*this)); + }; + + /** + * This method reads request data from the message. + * + * @param[in] aMessage A reference to the message. + * + * @returns The number of bytes read. + * + */ + uint16_t ReadFrom(const Message &aMessage) { + return aMessage.Read(aMessage.GetLength() - sizeof(*this), sizeof(*this), this); + }; + + /** + * This method updates request data in the message. + * + * @param[in] aMessage A reference to the message. + * + * @returns The number of bytes updated. + * + */ + int UpdateIn(Message &aMessage) const { + return aMessage.Write(aMessage.GetLength() - sizeof(*this), sizeof(*this), this); + } + + /** + * This method checks if the message shall be sent before the given time. + * + * @param[in] aTime A time to compare. + * + * @retval TRUE If the message shall be sent before the given time. + * @retval FALSE Otherwise. + */ + bool IsEarlier(uint32_t aTime) const { return (static_cast(aTime - mTransmissionTime) > 0); }; + + /** + * This method checks if the message shall be sent after the given time. + * + * @param[in] aTime A time to compare. + * + * @retval TRUE If the message shall be sent after the given time. + * @retval FALSE Otherwise. + */ + bool IsLater(uint32_t aTime) const { return (static_cast(aTime - mTransmissionTime) < 0); }; + +private: + const char *mHostname; ///< A hostname to be find. + otDnsResponseHandler mResponseHandler; ///< A function pointer that is called on response reception. + void *mResponseContext; ///< A pointer to arbitrary context information. + uint32_t mTransmissionTime; ///< Time when the timer should shoot for this message. + Ip6::Address mSourceAddress; ///< IPv6 address of the message source. + Ip6::Address mDestinationAddress; ///< IPv6 address of the message destination. + uint16_t mDestinationPort; ///< UDP port of the message destination. + uint8_t mRetransmissionCount; ///< Number of retransmissions. +} OT_TOOL_PACKED_END; + +/** + * This class implements DNS client. + * + */ +class Client +{ +public: + /** + * This constructor initializes the object. + * + * @param[in] aNetif A reference to the network interface that DNS client should be assigned to. + * + */ + Client(Ip6::Netif &aNetif): + mSocket(aNetif.GetIp6().mUdp), + mMessageId(0), + mRetransmissionTimer(aNetif.GetIp6().mTimerScheduler, &Client::HandleRetransmissionTimer, this) { + }; + + /** + * This method starts the DNS client. + * + * @retval kThreadError_None Successfully started the DNS client. + * @retval kThreadError_Already The socket is already open. + */ + ThreadError Start(void); + + /** + * This method stops the DNS client. + * + * @retval kThreadError_None Successfully stopped the DNS client. + * + */ + ThreadError Stop(void); + + /** + * This method sends a DNS query. + * + * @param[in] aQuery A pointer to specify DNS query parameters. + * @param[in] aHandler A function pointer that shall be called on response reception or time-out. + * @param[in] aContext A pointer to arbitrary context information. + * + * @retval kThreadError_None Successfully sent DNS query. + * @retval kThreadError_NoBufs Failed to allocate retransmission data. + * @retval kThreadError_InvalidArgs Invalid arguments supplied. + * + */ + ThreadError Query(const otDnsQuery *aQuery, otDnsResponseHandler aHandler, void *aContext); + + /** + * This method returns a port number used by DNS client. + * + * @returns A port number. + * + */ + uint16_t GetPort(void) { return mSocket.GetSockName().mPort; }; + +private: + /** + * Retransmission parameters. + * + */ + enum + { + kResponseTimeout = OPENTHREAD_CONFIG_DNS_RESPONSE_TIMEOUT, + kMaxRetransmit = OPENTHREAD_CONFIG_DNS_MAX_RETRANSMIT, + }; + + /** + * Special DNS symbols. + */ + enum + { + kLabelTerminator = 0, + kLabelSeparator = '.', + kCompressionOffsetMask = 0xc0 + }; + + /** + * Operating on message buffers. + */ + enum + { + kBufSize = 16 + }; + + Message *NewMessage(const Header &aHeader); + Message *CopyAndEnqueueMessage(const Message &aMessage, const QueryMetadata &aQueryMetadata); + void DequeueMessage(Message &aMessage); + ThreadError SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + ThreadError SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + ThreadError AppendCompressedHostname(Message &aMessage, const char *aHostname); + ThreadError CompareQuestions(Message &aMessageResponse, Message &aMessageQuery, uint16_t &aOffset); + ThreadError SkipHostname(Message &aMessage, uint16_t &aOffset); + + Message *FindRelatedQuery(const Header &aResponseHeader, QueryMetadata &aQueryMetadata); + void FinalizeDnsTransaction(Message &aQuery, const QueryMetadata &aQueryMetadata, + otIp6Address *aAddress, uint32_t aTtl, + ThreadError aResult); + + static void HandleRetransmissionTimer(void *aContext); + void HandleRetransmissionTimer(void); + + static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); + void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + Ip6::UdpSocket mSocket; + + uint16_t mMessageId; + MessageQueue mPendingQueries; + Timer mRetransmissionTimer; +}; + +} // namespace Dns +} // namespace Thread + +#endif // DNS_CLIENT_HPP_ diff --git a/src/core/net/dns_headers.hpp b/src/core/net/dns_headers.hpp new file mode 100644 index 000000000..558236367 --- /dev/null +++ b/src/core/net/dns_headers.hpp @@ -0,0 +1,588 @@ +/* + * Copyright (c) 2017, 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 includes definitions for generating and processing DNS headers. + */ + +#ifndef DNS_HEADER_HPP_ +#define DNS_HEADER_HPP_ + +#include + +#include + +#include +#include + +using Thread::Encoding::BigEndian::HostSwap16; +using Thread::Encoding::BigEndian::HostSwap32; + +namespace Thread { + +/** + * @namespace Thread::Dns + * @brief + * This namespace includes definitions for DNS. + * + */ +namespace Dns { + +/** + * @addtogroup core-dns + * + * @brief + * This module includes definitions for DNS. + * + * @{ + * + */ + +/** + * This class implements DNS header generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class Header +{ +public: + + /** + * Default constructor for DNS Header. + * + */ + Header(void) { memset(this, 0, sizeof(*this)); } + + /** + * This method returns the Message ID. + * + * @returns The Message ID value. + * + */ + uint16_t GetMessageId(void) const { return HostSwap16(mMessageId); } + + /** + * This method sets the Message ID. + * + * @param[in] aMessageId The Message ID value. + * + */ + void SetMessageId(uint16_t aMessageId) { mMessageId = HostSwap16(aMessageId); } + + /** + * Defines types of DNS message. + * + */ + enum Type + { + kTypeQuery = 0, + kTypeResponse = 1, + }; + + /** + * This method returns the type of the message. + * + * @returns The type of the message. + */ + Type GetType(void) const { return static_cast((mFlags[0] & kQrFlagMask) >> kQrFlagOffset); } + + /** + * This method sets the type of the message. + * + * @param[in] aType The type of the message. + * + */ + void SetType(Type aType) { + mFlags[0] &= ~kQrFlagMask; + mFlags[0] |= static_cast(aType) << kQrFlagOffset; + } + + /** + * Defines types of query. + * + */ + enum QueryType + { + kQueryTypeStandard = 0, + kQueryTypeInverse = 1, + kQueryTypeStatus = 2, + kQueryTypeNotify = 4, + kQueryTypeUpdate = 5 + }; + + /** + * This method returns the type of the query. + * + * @returns The type of the query. + */ + QueryType GetQueryType(void) const { return static_cast((mFlags[0] & kOpCodeMask) >> kOpCodeOffset); } + + /** + * This method sets the type of the query. + * + * @param[in] aType The type of the query. + * + */ + void SetQueryType(QueryType aType) { + mFlags[0] &= ~kOpCodeMask; + mFlags[0] |= static_cast(aType) << kOpCodeOffset; + } + + /** + * This method specifies in response message if the responding name server is an + * authority for the domain name in question section. + * + * @returns True if Authoritative Answer flag (AA) is set in the header, false otherwise. + */ + bool IsAuthoritativeAnswerFlagSet(void) const { return (mFlags[0] & kAaFlagMask) == kAaFlagMask; } + + /** + * This method clears the Authoritative Answer flag (AA) in the header. + * + */ + void ClearAuthoritativeAnswerFlag(void) { mFlags[0] &= ~kAaFlagMask; } + + /** + * This method sets the Authoritative Answer flag (AA) in the header. + * + */ + void SetAuthoritativeAnswerFlag(void) { mFlags[0] |= kAaFlagMask; } + + /** + * This method specifies if message is truncated. + * + * @returns True if Truncation flag (TC) is set in the header, false otherwise. + */ + bool IsTruncationFlagSet(void) const { return (mFlags[0] & kTcFlagMask) == kTcFlagMask; } + + /** + * This method clears the Truncation flag (TC) in the header. + * + */ + void ClearTruncationFlag(void) { mFlags[0] &= ~kTcFlagMask; } + + /** + * This method sets the Truncation flag (TC) in the header. + * + */ + void SetTruncationFlag(void) { mFlags[0] |= kTcFlagMask; } + + /** + * This method specifies if resolver wants to direct the name server to pursue + * the query recursively. + * + * @returns True if Recursion Desired flag (RD) is set in the header, false otherwise. + */ + bool IsRecursionDesiredFlagSet(void) const { return (mFlags[0] & kRdFlagMask) == kRdFlagMask; } + + /** + * This method clears the Recursion Desired flag (RD) in the header. + * + */ + void ClearRecursionDesiredFlag(void) { mFlags[0] &= ~kRdFlagMask; } + + /** + * This method sets the Recursion Desired flag (RD) in the header. + * + */ + void SetRecursionDesiredFlag(void) { mFlags[0] |= kRdFlagMask; } + + /** + * This method denotes whether recursive query support is available in the name server. + * + * @returns True if Recursion Available flag (RA) is set in the header, false otherwise. + */ + bool IsRecursionAvailableFlagSet(void) const { return (mFlags[1] & kRaFlagMask) == kRaFlagMask; } + + /** + * This method clears the Recursion Available flag (RA) in the header. + * + */ + void ClearRecursionAvailableFlag(void) { mFlags[1] &= ~kRaFlagMask; } + + /** + * This method sets the Recursion Available flag (RA) in the header. + * + */ + void SetRecursionAvailableFlag(void) { mFlags[1] |= kRaFlagMask; } + + /** + * Defines response codes. + * + */ + enum Response + { + kResponseSuccess = 0, + kResponseFormatError = 1, + kResponseServerFailure = 2, + kResponseNameError = 3, + kResponseNotImplemented = 4, + kResponseRefused = 5, + kResponseNotAuth = 9, + kResponseNotZone = 10, + kResponseBadName = 20, + kResponseBadAlg = 21, + kResponseBadTruncation = 22, + }; + + /** + * This method returns the response code. + * + * @returns The response code from the header. + */ + Response GetResponseCode(void) const { return static_cast((mFlags[1] & kRCodeMask) >> kRCodeOffset); } + + /** + * This method sets the response code. + * + * @param[in] aResponse The type of the response. + * + */ + void SetResponseCode(Response aResponse) { + mFlags[1] &= ~kRCodeMask; + mFlags[1] |= static_cast(aResponse) << kRCodeOffset; + } + + /** + * This method returns the number of entries in question section. + * + * @returns The number of entries in question section. + * + */ + uint16_t GetQuestionCount(void) const { return HostSwap16(mQdCount); } + + /** + * This method sets the number of entries in question section. + * + * @param[in] aCount The number of entries in question section. + * + */ + void SetQuestionCount(uint16_t aCount) { mQdCount = HostSwap16(aCount); } + + /** + * This method returns the number of entries in answer section. + * + * @returns The number of entries in answer section. + * + */ + uint16_t GetAnswerCount(void) const { return HostSwap16(mAnCount); } + + /** + * This method sets the number of entries in answer section. + * + * @param[in] aCount The number of entries in answer section. + * + */ + void SetAnswerCount(uint16_t aCount) { mAnCount = HostSwap16(aCount); } + + /** + * This method returns the number of entries in authority records section. + * + * @returns The number of entries in authority records section. + * + */ + uint16_t GetAuthorityRecordsCount(void) const { return HostSwap16(mNsCount); } + + /** + * This method sets the number of entries in authority records section. + * + * @param[in] aCount The number of entries in authority records section. + * + */ + void SetAuthorityRecordsCount(uint16_t aCount) { mNsCount = HostSwap16(aCount); } + + /** + * This method returns the number of entries in additional records section. + * + * @returns The number of entries in additional records section. + * + */ + uint16_t GetAdditionalRecordsCount(void) const { return HostSwap16(mArCount); } + + /** + * This method sets the number of entries in additional records section. + * + * @param[in] aCount The number of entries in additional records section. + * + */ + void SetAdditionalRecordsCount(uint16_t aCount) { mArCount = HostSwap16(aCount); } + +private: + /** + * Protocol Constants (RFC 1035). + * + */ + enum + { + kQrFlagOffset = 7, ///< QR Flag offset. + kQrFlagMask = 0x01 << kQrFlagOffset, ///< QR Flag mask. + kOpCodeOffset = 3, ///< OpCode field offset. + kOpCodeMask = 0x0f << kOpCodeOffset, ///< OpCode field mask. + kAaFlagOffset = 2, ///< AA Flag offset. + kAaFlagMask = 0x01 << kAaFlagOffset, ///< AA Flag mask. + kTcFlagOffset = 1, ///< TC Flag offset. + kTcFlagMask = 0x01 << kTcFlagOffset, ///< TC Flag mask. + kRdFlagOffset = 0, ///< RD Flag offset. + kRdFlagMask = 0x01 << kRdFlagOffset, ///< RD Flag mask. + + kRaFlagOffset = 7, ///< RA Flag offset. + kRaFlagMask = 0x01 << kRaFlagOffset, ///< RA Flag mask. + kRCodeOffset = 0, ///< RCODE field offset. + kRCodeMask = 0x0f << kRCodeOffset, ///< RCODE field mask. + }; + + uint16_t mMessageId; ///< A message identifier that is used by the requester to match up replies to outstanding queries. + uint8_t mFlags[2]; ///< DNS header flags. + uint16_t mQdCount; ///< A number specifying the number of entries in the question section. + uint16_t mAnCount; ///< A number specifying the number of entries in the answer section. + uint16_t mNsCount; ///< A number specifying the number of entries in the authority records section. + uint16_t mArCount; ///< A number specifying the number of entries in the additional records section. + +} OT_TOOL_PACKED_END; + +/** + * This class implements Resource Record body format (RR). + * + */ +OT_TOOL_PACKED_BEGIN +class ResourceRecord +{ +public: + /** + * This method returns the type of the resource record. + * + * @returns The type of the resource record. + */ + uint16_t GetType(void) const { return HostSwap16(mType); } + + /** + * This method sets the type of the resource record. + * + * @param[in] aType The type of the resource record. + * + */ + void SetType(uint16_t aType) { mType = HostSwap16(aType); } + + /** + * This method returns the class of the resource record. + * + * @returns The class of the resource record. + */ + uint16_t GetClass(void) const { return HostSwap16(mClass); } + + /** + * This method sets the class of the resource record. + * + * @param[in] aClass The class of the resource record. + * + */ + void SetClass(uint16_t aClass) { mClass = HostSwap16(aClass); } + + /** + * This method returns the time to live field of the resource record. + * + * @returns The time to live field of the resource record. + */ + uint32_t GetTtl(void) const { return HostSwap32(mTtl); } + + /** + * This method sets the time to live field of the resource record. + * + * @param[in] aTtl The time to live field of the resource record. + * + */ + void SetTtl(uint32_t aTtl) { mTtl = HostSwap32(aTtl); } + + /** + * This method returns the length of the resource record. + * + * @returns The length of the resource record. + */ + uint16_t GetLength(void) const { return HostSwap16(mLength); } + + /** + * This method sets the length of the resource record. + * + * @param[in] aLength The length of the resource record. + * + */ + void SetLength(uint16_t aLength) { mLength = HostSwap16(aLength); } + +private: + uint16_t mType; ///< The type of the data in RDATA section. + uint16_t mClass; ///< The class of the data in RDATA section. + uint32_t mTtl; ///< Specifies the maximum time that the resource record may be cached. + uint16_t mLength; ///< The length of RDATA section in bytes. + +} OT_TOOL_PACKED_END; + +/** + * This class implements Resource Record body format of AAAA type. + * + */ +OT_TOOL_PACKED_BEGIN +class ResourceRecordAaaa: public ResourceRecord +{ +public: + enum + { + kType = 0x1C, ///< AAAA Resource Record type. + kClass = 0x01, ///< The value of the Internet class. + kLength = 16, ///< Size of the AAAA Resource Record type. + }; + + /** + * This method initializes the AAAA Resource Record. + * + */ + void Init(void) { + ResourceRecord::SetType(kType); + ResourceRecord::SetClass(kClass); + ResourceRecord::SetTtl(0); + ResourceRecord::SetLength(kLength); + memset(&mAddress, 0, sizeof(mAddress)); + } + + /** + * This method sets the IPv6 address of the resource record. + * + * @param[in] aAddress The IPv6 address of the resource record. + * + */ + void SetAddress(otIp6Address &aAddress) { mAddress = aAddress; } + + /** + * This method returns the reference to IPv6 address of the resource record. + * + * @returns The reference to IPv6 address of the resource record. + */ + otIp6Address &GetAddress(void) { return mAddress; } + +private: + otIp6Address mAddress; ///< Ipv6 Address of AAAA Resource Record. + +} OT_TOOL_PACKED_END; + +/** + * This class implements Question format. + * + */ +OT_TOOL_PACKED_BEGIN +class Question +{ +public: + /** + * Constructor for Question. + * + */ + Question(uint16_t aType, uint16_t aClass) { + SetType(aType); + SetClass(aClass); + }; + + /** + * This method returns the type of the question. + * + * @returns The type of the question. + */ + uint16_t GetType(void) const { return HostSwap16(mType); } + + /** + * This method sets the type of the question. + * + * @param[in] aType The type of the question. + * + */ + void SetType(uint16_t aType) { mType = HostSwap16(aType); } + + /** + * This method returns the class of the question. + * + * @returns The class of the question. + */ + uint16_t GetClass(void) const { return HostSwap16(mClass); } + + /** + * This method sets the class of the question. + * + * @param[in] aClass The class of the question. + * + */ + void SetClass(uint16_t aClass) { mClass = HostSwap16(aClass); } + +private: + uint16_t mType; ///< The type of the data in question section. + uint16_t mClass; ///< The class of the data in question section. + +} OT_TOOL_PACKED_END; + + +/** + * This class implements Question format of AAAA type. + * + */ +class QuestionAaaa: public Question +{ +public: + enum + { + kType = 0x1C, ///< AAAA Resource Record type. + kClass = 0x01, ///< The value of the Internet class. + }; + + /** + * Default constructor for AAAA Question. + * + */ + QuestionAaaa(void) : Question(kType, kClass) {}; + + /** + * This method appends request data to the message. + * + * @param[in] aMessage A reference to the message. + * + * @retval kThreadError_None Successfully appended the bytes. + * @retval kThreadError_NoBufs Insufficient available buffers to grow the message. + * + */ + ThreadError AppendTo(Message &aMessage) const { + return aMessage.Append(this, sizeof(*this)); + }; + +}; + +/** + * @} + * + */ + +} // namespace Dns +} // namespace Thread + +#endif // DNS_HEADER_HPP_ diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index e7563fff5..c29eb8ee2 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -321,6 +321,26 @@ #define OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT 4 #endif // OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT +/** + * @def OPENTHREAD_CONFIG_DNS_RESPONSE_TIMEOUT + * + * Maximum time that DNS Client waits for response in milliseconds. + * + */ +#ifndef OPENTHREAD_CONFIG_DNS_RESPONSE_TIMEOUT +#define OPENTHREAD_CONFIG_DNS_RESPONSE_TIMEOUT 3000 +#endif // OPENTHREAD_CONFIG_DNS_RESPONSE_TIMEOUT + +/** + * @def OPENTHREAD_CONFIG_DNS_MAX_RETRANSMIT + * + * Maximum number of retransmissions for DNS client. + * + */ +#ifndef OPENTHREAD_CONFIG_DNS_MAX_RETRANSMIT +#define OPENTHREAD_CONFIG_DNS_MAX_RETRANSMIT 2 +#endif // OPENTHREAD_CONFIG_DNS_MAX_RETRANSMIT + /** * @def OPENTHREAD_CONFIG_JOIN_BEACON_VERSION * diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index f0ef312fb..ef2e58bdd 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -64,6 +64,9 @@ ThreadNetif::ThreadNetif(Ip6::Ip6 &aIp6): #if OPENTHREAD_ENABLE_DHCP6_SERVER mDhcp6Server(*this), #endif // OPENTHREAD_ENABLE_DHCP6_SERVER +#if OPENTHREAD_ENABLE_DNS_CLIENT + mDnsClient(*this), +#endif // OPENTHREAD_ENABLE_DNS_CLIENT mActiveDataset(*this), mPendingDataset(*this), mKeyManager(*this), @@ -108,6 +111,9 @@ ThreadError ThreadNetif::Up(void) mCoapClient.Start(); #if OPENTHREAD_ENABLE_JOINER mSecureCoapClient.Start(); +#endif +#if OPENTHREAD_ENABLE_DNS_CLIENT + mDnsClient.Start(); #endif mMleRouter.Enable(); mIsUp = true; @@ -122,6 +128,9 @@ ThreadError ThreadNetif::Down(void) mCoapClient.Stop(); #if OPENTHREAD_ENABLE_JOINER mSecureCoapClient.Stop(); +#endif +#if OPENTHREAD_ENABLE_DNS_CLIENT + mDnsClient.Stop(); #endif mMleRouter.Disable(); mMeshForwarder.Stop(); diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index 710a434b2..29b3a92c9 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -157,15 +158,15 @@ public: ThreadError RouteLookup(const Ip6::Address &aSource, const Ip6::Address &aDestination, uint8_t *aPrefixMatch); /** - * This method returns a pointer to the address resolver object. + * This method returns a reference to the address resolver object. * - * @returns A pointer to the address resolver object. + * @returns A reference to the address resolver object. * */ AddressResolver &GetAddressResolver(void) { return mAddressResolver; } /** - * This method returns a pointer to the network diagnostic object. + * This method returns a reference to the network diagnostic object. * * @returns A reference to the address resolver object. * @@ -174,7 +175,7 @@ public: #if OPENTHREAD_ENABLE_DHCP6_CLIENT /** - * This method returns a pointer to the dhcp client object. + * This method returns a reference to the dhcp client object. * * @returns A reference to the dhcp client object. * @@ -184,7 +185,7 @@ public: #if OPENTHREAD_ENABLE_DHCP6_SERVER /** - * This method returns a pointer to the dhcp server object. + * This method returns a reference to the dhcp server object. * * @returns A reference to the the dhcp server object. * @@ -192,6 +193,16 @@ public: Dhcp6::Dhcp6Server &GetDhcp6Server(void) { return mDhcp6Server; } #endif // OPENTHREAD_ENABLE_DHCP6_SERVER +#if OPENTHREAD_ENABLE_DNS_CLIENT + /** + * This method returns a reference to the dns client object. + * + * @returns A reference to the dns client object. + * + */ + Dns::Client &GetDnsClient(void) { return mDnsClient; } +#endif // OPENTHREAD_ENABLE_DNS_CLIENT + /** * This method returns a reference to the CoAP server object. * @@ -217,57 +228,57 @@ public: Ip6::Filter &GetIp6Filter(void) { return mIp6Filter; } /** - * This method returns a pointer to the key manager object. + * This method returns a reference to the key manager object. * - * @returns A pointer to the key manager object. + * @returns A reference to the key manager object. * */ KeyManager &GetKeyManager(void) { return mKeyManager; } /** - * This method returns a pointer to the lowpan object. + * This method returns a reference to the lowpan object. * - * @returns A pointer to the lowpan object. + * @returns A reference to the lowpan object. * */ Lowpan::Lowpan &GetLowpan(void) { return mLowpan; } /** - * This method returns a pointer to the mac object. + * This method returns a reference to the mac object. * - * @returns A pointer to the mac object. + * @returns A reference to the mac object. * */ Mac::Mac &GetMac(void) { return mMac; } /** - * This method returns a pointer to the mle object. + * This method returns a reference to the mle object. * - * @returns A pointer to the mle object. + * @returns A reference to the mle object. * */ Mle::MleRouter &GetMle(void) { return mMleRouter; } /** - * This method returns a pointer to the mesh forwarder object. + * This method returns a reference to the mesh forwarder object. * - * @returns A pointer to the mesh forwarder object. + * @returns A reference to the mesh forwarder object. * */ MeshForwarder &GetMeshForwarder(void) { return mMeshForwarder; } /** - * This method returns a pointer to the network data local object. + * This method returns a reference to the network data local object. * - * @returns A pointer to the network data local object. + * @returns A reference to the network data local object. * */ NetworkData::Local &GetNetworkDataLocal(void) { return mNetworkDataLocal; } /** - * This method returns a pointer to the network data leader object. + * This method returns a reference to the network data leader object. * - * @returns A pointer to the network data leader object. + * @returns A reference to the network data leader object. * */ NetworkData::Leader &GetNetworkDataLeader(void) { return mNetworkDataLeader; } @@ -326,6 +337,9 @@ private: #if OPENTHREAD_ENABLE_DHCP6_SERVER Dhcp6::Dhcp6Server mDhcp6Server; #endif // OPENTHREAD_ENABLE_DHCP6_SERVER +#if OPENTHREAD_ENABLE_DNS_CLIENT + Dns::Client mDnsClient; +#endif // OPENTHREAD_ENABLE_DNS_CLIENT MeshCoP::ActiveDataset mActiveDataset; MeshCoP::PendingDataset mPendingDataset; Ip6::Filter mIp6Filter;