Add initial implementation of DNS Client (#1460)

* [DNS] Initial DNS Client implementation.
This commit is contained in:
Łukasz Duda
2017-03-17 14:43:08 -07:00
committed by Jonathan Hui
parent 0c95d6506e
commit 3316daa43c
19 changed files with 1792 additions and 35 deletions
+7 -7
View File
@@ -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
+1
View File
@@ -41,6 +41,7 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \
--enable-joiner \
--enable-dhcp6-client \
--enable-dhcp6-server \
--enable-dns-client \
$(NULL)
SUBDIRS = \
+31
View File
@@ -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}
+5 -1
View File
@@ -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)
+1 -2
View File
@@ -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) ""
+1
View File
@@ -55,6 +55,7 @@ openthread_headers = \
diag.h \
dhcp6_client.h \
dhcp6_server.h \
dns.h \
icmp6.h \
instance.h \
ip6.h \
+114
View File
@@ -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 <openthread/types.h>
#include <openthread/message.h>
#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_
+1 -1
View File
@@ -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,
+14 -1
View File
@@ -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 \<hostname\> \[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.
+101
View File
@@ -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<uint16_t>(port));
query.mHostname = mResolvingHostname;
query.mMessageInfo = static_cast<const otMessageInfo *>(&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<Interpreter *>(aContext)->HandleDnsResponse(aHostname,
*static_cast<Ip6::Address *>(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;
+18
View File
@@ -53,6 +53,7 @@
#include <net/icmp6.hpp>
#include <common/timer.hpp>
#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;
+13 -4
View File
@@ -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 \
+54
View File
@@ -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
+517
View File
@@ -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 <string.h>
#include <common/debug.hpp>
#include <common/code_utils.hpp>
#include <net/dns_client.hpp>
#include <net/udp6.hpp>
/**
* @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<const Ip6::MessageInfo *>(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<Client *>(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<Client *>(aContext)->HandleUdpReceive(*static_cast<Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(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
+264
View File
@@ -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 <openthread/dns.h>
#include <openthread/types.h>
#include <common/message.hpp>
#include <common/timer.hpp>
#include <net/dns_headers.hpp>
#include <net/ip6.hpp>
#include <net/netif.hpp>
/**
* @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<int32_t>(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<int32_t>(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_
+588
View File
@@ -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 <string.h>
#include <openthread/types.h>
#include <common/encoding.hpp>
#include <common/message.hpp>
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<Type>((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<uint8_t>(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<QueryType>((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<uint8_t>(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<Response>((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<uint8_t>(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_
+20
View File
@@ -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
*
+9
View File
@@ -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();
+33 -19
View File
@@ -53,6 +53,7 @@
#include <net/dhcp6.hpp>
#include <net/dhcp6_client.hpp>
#include <net/dhcp6_server.hpp>
#include <net/dns_client.hpp>
#include <net/ip6_filter.hpp>
#include <net/netif.hpp>
#include <thread/address_resolver.hpp>
@@ -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;