mirror of
https://github.com/espressif/openthread.git
synced 2026-07-31 16:17:47 +00:00
[dns] implement DNS query over TCP (#8474)
This commit introduces DNS query over TCP protocol.
This commit is contained in:
@@ -95,6 +95,7 @@ jobs:
|
||||
sudo apt-get install --no-install-recommends -y bind9-host ntp socat
|
||||
sudo systemctl restart ntp
|
||||
sudo socat 'UDP6-LISTEN:53,fork,reuseaddr,bind=[::1]' UDP:127.0.0.53:53 &
|
||||
socat 'TCP6-LISTEN:2000,fork,reuseaddr' TCP:127.0.0.53:53 &
|
||||
host ipv6.google.com 127.0.0.53
|
||||
host ipv6.google.com ::1
|
||||
ulimit -c unlimited
|
||||
|
||||
@@ -101,6 +101,7 @@ ot_option(OT_DHCP6_CLIENT OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE "DHCP6 client")
|
||||
ot_option(OT_DHCP6_SERVER OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE "DHCP6 server")
|
||||
ot_option(OT_DIAGNOSTIC OPENTHREAD_CONFIG_DIAG_ENABLE "diagnostic")
|
||||
ot_option(OT_DNS_CLIENT OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE "DNS client")
|
||||
ot_option(OT_DNS_CLIENT_OVER_TCP OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE "Enable dns query over tcp")
|
||||
ot_option(OT_DNS_DSO OPENTHREAD_CONFIG_DNS_DSO_ENABLE "DNS Stateful Operations (DSO)")
|
||||
ot_option(OT_DNS_UPSTREAM_QUERY OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE "Allow sending DNS queries to upstream")
|
||||
ot_option(OT_DNSSD_SERVER OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE "DNS-SD server")
|
||||
@@ -192,4 +193,3 @@ endif()
|
||||
if(OT_POSIX_SETTINGS_PATH)
|
||||
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH=${OT_POSIX_SETTINGS_PATH}")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -80,6 +80,19 @@ typedef enum
|
||||
OT_DNS_NAT64_DISALLOW = 2, ///< Do not allow NAT64 address translation during DNS client address resolution.
|
||||
} otDnsNat64Mode;
|
||||
|
||||
/**
|
||||
* This enumeration type represents the DNS transport protocol in an `otDnsQueryConfig`.
|
||||
*
|
||||
* This `OT_DNS_TRANSPORT_TCP` is only supported when `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE` is enabled.
|
||||
*
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
OT_DNS_TRANSPORT_UNSPECIFIED = 0, /// DNS transport is unspecified.
|
||||
OT_DNS_TRANSPORT_UDP = 1, /// DNS query should be sent via UDP.
|
||||
OT_DNS_TRANSPORT_TCP = 2, /// DNS query should be sent via TCP.
|
||||
} otDnsTransportProto;
|
||||
|
||||
/**
|
||||
* This structure represents a DNS query configuration.
|
||||
*
|
||||
@@ -89,11 +102,12 @@ typedef enum
|
||||
*/
|
||||
typedef struct otDnsQueryConfig
|
||||
{
|
||||
otSockAddr mServerSockAddr; ///< Server address (IPv6 address/port). All zero or zero port for unspecified.
|
||||
uint32_t mResponseTimeout; ///< Wait time (in msec) to rx response. Zero indicates unspecified value.
|
||||
uint8_t mMaxTxAttempts; ///< Maximum tx attempts before reporting failure. Zero for unspecified value.
|
||||
otDnsRecursionFlag mRecursionFlag; ///< Indicates whether the server can resolve the query recursively or not.
|
||||
otDnsNat64Mode mNat64Mode; ///< Allow/Disallow NAT64 address translation during address resolution.
|
||||
otSockAddr mServerSockAddr; ///< Server address (IPv6 address/port). All zero or zero port for unspecified.
|
||||
uint32_t mResponseTimeout; ///< Wait time (in msec) to rx response. Zero indicates unspecified value.
|
||||
uint8_t mMaxTxAttempts; ///< Maximum tx attempts before reporting failure. Zero for unspecified value.
|
||||
otDnsRecursionFlag mRecursionFlag; ///< Indicates whether the server can resolve the query recursively or not.
|
||||
otDnsNat64Mode mNat64Mode; ///< Allow/Disallow NAT64 address translation during address resolution.
|
||||
otDnsTransportProto mTransportProto; ///< Select default transport protocol.
|
||||
} otDnsQueryConfig;
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (300)
|
||||
#define OPENTHREAD_API_VERSION (301)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
+4
-1
@@ -155,13 +155,16 @@ main()
|
||||
case "${platform}" in
|
||||
posix)
|
||||
local_options+=(
|
||||
"-DOT_TCP=OFF"
|
||||
"-DOT_LOG_OUTPUT=PLATFORM_DEFINED"
|
||||
"-DOT_POSIX_MAX_POWER_TABLE=ON"
|
||||
)
|
||||
options+=("${OT_POSIX_SIM_COMMON_OPTIONS[@]}" "${local_options[@]}")
|
||||
;;
|
||||
simulation)
|
||||
local_options=("-DOT_LINK_RAW=ON")
|
||||
local_options=("-DOT_LINK_RAW=ON"
|
||||
"-DOT_DNS_CLIENT_OVER_TCP=ON"
|
||||
)
|
||||
options+=("${OT_POSIX_SIM_COMMON_OPTIONS[@]}" "${local_options[@]}")
|
||||
;;
|
||||
*)
|
||||
|
||||
+9
-3
@@ -1082,16 +1082,19 @@ Server: [fd00:0:0:0:0:0:0:1]:1234
|
||||
ResponseTimeout: 5000 ms
|
||||
MaxTxAttempts: 2
|
||||
RecursionDesired: no
|
||||
TransportProtocol: udp
|
||||
Done
|
||||
>
|
||||
```
|
||||
|
||||
### dns config \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\]
|
||||
### dns config \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] \[transport protocol\]
|
||||
|
||||
Set the default query config.
|
||||
|
||||
To set protocol effectively to tcp `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE` is required.
|
||||
|
||||
```bash
|
||||
> dns config fd00::1 1234 5000 2 0
|
||||
> dns config fd00::1 1234 5000 2 0 tcp
|
||||
Done
|
||||
|
||||
> dns config
|
||||
@@ -1099,6 +1102,7 @@ Server: [fd00:0:0:0:0:0:0:1]:1234
|
||||
ResponseTimeout: 5000 ms
|
||||
MaxTxAttempts: 2
|
||||
RecursionDesired: no
|
||||
TransportProtocol: tcp
|
||||
Done
|
||||
```
|
||||
|
||||
@@ -1116,12 +1120,14 @@ RecursionDesired: yes
|
||||
Done
|
||||
```
|
||||
|
||||
### dns resolve \<hostname\> \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\]
|
||||
### dns resolve \<hostname\> \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] \[transport protocol\]
|
||||
|
||||
Send DNS Query to obtain IPv6 address for given hostname.
|
||||
|
||||
The parameters after `hostname` are optional. Any unspecified (or zero) value for these optional parameters is replaced by the value from the current default config (`dns config`).
|
||||
|
||||
To use tcp, `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE` is required.
|
||||
|
||||
```bash
|
||||
> dns resolve ipv6.google.com
|
||||
> DNS response for ipv6.google.com - 2a00:1450:401b:801:0:0:0:200e TTL: 300
|
||||
|
||||
@@ -3011,6 +3011,10 @@ template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
|
||||
OutputLine("MaxTxAttempts: %u", defaultConfig->mMaxTxAttempts);
|
||||
OutputLine("RecursionDesired: %s",
|
||||
(defaultConfig->mRecursionFlag == OT_DNS_FLAG_RECURSION_DESIRED) ? "yes" : "no");
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
OutputLine("TransportProtocol: %s",
|
||||
(defaultConfig->mTransportProto == OT_DNS_TRANSPORT_UDP) ? "udp" : "tcp");
|
||||
#endif
|
||||
}
|
||||
/* clang-format off */
|
||||
/**
|
||||
@@ -3286,6 +3290,19 @@ otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig)
|
||||
SuccessOrExit(error = aArgs[4].ParseAsBool(recursionDesired));
|
||||
aConfig->mRecursionFlag = recursionDesired ? OT_DNS_FLAG_RECURSION_DESIRED : OT_DNS_FLAG_NO_RECURSION;
|
||||
|
||||
VerifyOrExit(!aArgs[5].IsEmpty());
|
||||
if (aArgs[5] == "tcp")
|
||||
{
|
||||
aConfig->mTransportProto = OT_DNS_TRANSPORT_TCP;
|
||||
}
|
||||
else if (aArgs[5] == "udp")
|
||||
{
|
||||
aConfig->mTransportProto = OT_DNS_TRANSPORT_UDP;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = OT_ERROR_INVALID_ARGS;
|
||||
}
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#ifndef CONFIG_DNS_CLIENT_H_
|
||||
#define CONFIG_DNS_CLIENT_H_
|
||||
|
||||
#include "config/ip6.h"
|
||||
#include "config/srp_client.h"
|
||||
|
||||
/**
|
||||
@@ -150,4 +151,24 @@
|
||||
#define OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RECURSION_DESIRED_FLAG 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
*
|
||||
* Enables support for sending DNS Queries over TCP.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
#define OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE
|
||||
*
|
||||
* Specifies size of receive and transmit buffers of TCP sockets for DNS query over TCP.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE
|
||||
#define OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE 1024
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_DNS_CLIENT_H_
|
||||
|
||||
+322
-11
@@ -49,6 +49,11 @@
|
||||
namespace ot {
|
||||
namespace Dns {
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
using ot::Encoding::BigEndian::ReadUint16;
|
||||
using ot::Encoding::BigEndian::WriteUint16;
|
||||
#endif
|
||||
|
||||
RegisterLogModule("DnsClient");
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -68,6 +73,7 @@ Client::QueryConfig::QueryConfig(InitMode aMode)
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
|
||||
SetNat64Mode(kDefaultNat64Allowed ? kNat64Allow : kNat64Disallow);
|
||||
#endif
|
||||
SetTransportProto(kDnsTransportUdp);
|
||||
}
|
||||
|
||||
void Client::QueryConfig::SetFrom(const QueryConfig &aConfig, const QueryConfig &aDefaultConfig)
|
||||
@@ -109,6 +115,10 @@ void Client::QueryConfig::SetFrom(const QueryConfig &aConfig, const QueryConfig
|
||||
SetNat64Mode(aDefaultConfig.GetNat64Mode());
|
||||
}
|
||||
#endif
|
||||
if (GetTransportProto() == kDnsTransportUnspecified)
|
||||
{
|
||||
SetTransportProto(aDefaultConfig.GetTransportProto());
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -540,6 +550,9 @@ const uint16_t *Client::kQuestionRecordTypes[] = {
|
||||
Client::Client(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mSocket(aInstance)
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
, mTcpState(kTcpUninitialized)
|
||||
#endif
|
||||
, mTimer(aInstance)
|
||||
, mDefaultConfig(QueryConfig::kInitFromDefaults)
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE
|
||||
@@ -580,8 +593,39 @@ void Client::Stop(void)
|
||||
}
|
||||
|
||||
IgnoreError(mSocket.Close());
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
if (mTcpState != kTcpUninitialized)
|
||||
{
|
||||
IgnoreError(mEndpoint.Deinitialize());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
Error Client::InitTcpSocket(void)
|
||||
{
|
||||
Error error;
|
||||
otTcpEndpointInitializeArgs endpointArgs;
|
||||
|
||||
memset(&endpointArgs, 0x00, sizeof(endpointArgs));
|
||||
endpointArgs.mSendDoneCallback = HandleTcpSendDoneCallback;
|
||||
endpointArgs.mEstablishedCallback = HandleTcpEstablishedCallback;
|
||||
endpointArgs.mReceiveAvailableCallback = HandleTcpReceiveAvailableCallback;
|
||||
endpointArgs.mDisconnectedCallback = HandleTcpDisconnectedCallback;
|
||||
endpointArgs.mContext = this;
|
||||
endpointArgs.mReceiveBuffer = mReceiveBufferBytes;
|
||||
endpointArgs.mReceiveBufferSize = sizeof(mReceiveBufferBytes);
|
||||
|
||||
mSendLink.mNext = nullptr;
|
||||
mSendLink.mData = mSendBufferBytes;
|
||||
mSendLink.mLength = 0;
|
||||
|
||||
SuccessOrExit(error = mEndpoint.Initialize(Get<Instance>(), endpointArgs));
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Client::SetDefaultConfig(const QueryConfig &aQueryConfig)
|
||||
{
|
||||
QueryConfig startingDefault(QueryConfig::kInitFromDefaults);
|
||||
@@ -727,8 +771,10 @@ Error Client::StartQuery(QueryInfo &aInfo,
|
||||
|
||||
SuccessOrExit(error = AllocateQuery(aInfo, aLabel, aName, query));
|
||||
mQueries.Enqueue(*query);
|
||||
|
||||
SendQuery(*query, aInfo, /* aUpdateTimer */ true);
|
||||
if ((error = SendQuery(*query, aInfo, /* aUpdateTimer */ true)) != kErrorNone)
|
||||
{
|
||||
FreeQuery(*query);
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
@@ -761,7 +807,7 @@ exit:
|
||||
|
||||
void Client::FreeQuery(Query &aQuery) { mQueries.DequeueAndFree(aQuery); }
|
||||
|
||||
void Client::SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer)
|
||||
Error Client::SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer)
|
||||
{
|
||||
// This method prepares and sends a query message represented by
|
||||
// `aQuery` and `aInfo`. This method updates `aInfo` (e.g., sets
|
||||
@@ -774,6 +820,7 @@ void Client::SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer)
|
||||
Message *message = nullptr;
|
||||
Header header;
|
||||
Ip6::MessageInfo messageInfo;
|
||||
uint16_t length = 0;
|
||||
|
||||
aInfo.mTransmissionCount++;
|
||||
aInfo.mRetransmissionTime = TimerMilli::GetNow() + aInfo.mConfig.GetResponseTimeout();
|
||||
@@ -815,20 +862,76 @@ void Client::SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer)
|
||||
SuccessOrExit(error = message->Append(Question(kQuestionRecordTypes[aInfo.mQueryType][num])));
|
||||
}
|
||||
|
||||
messageInfo.SetPeerAddr(aInfo.mConfig.GetServerSockAddr().GetAddress());
|
||||
messageInfo.SetPeerPort(aInfo.mConfig.GetServerSockAddr().GetPort());
|
||||
length = message->GetLength() - message->GetOffset();
|
||||
|
||||
SuccessOrExit(error = mSocket.SendTo(*message, messageInfo));
|
||||
if (aInfo.mConfig.GetTransportProto() == QueryConfig::kDnsTransportTcp)
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
{
|
||||
// Check if query will fit into tcp buffer if not return error.
|
||||
VerifyOrExit(length + sizeof(uint16_t) + mSendLink.mLength <=
|
||||
OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE,
|
||||
error = kErrorNoBufs);
|
||||
|
||||
// In case of initialized connection check if connected peer and new query have the same address.
|
||||
if (mTcpState != kTcpUninitialized)
|
||||
{
|
||||
VerifyOrExit(mEndpoint.GetPeerAddress() == AsCoreType(&aInfo.mConfig.mServerSockAddr),
|
||||
error = kErrorFailed);
|
||||
}
|
||||
|
||||
switch (mTcpState)
|
||||
{
|
||||
case kTcpUninitialized:
|
||||
SuccessOrExit(error = InitTcpSocket());
|
||||
SuccessOrExit(
|
||||
error = mEndpoint.Connect(AsCoreType(&aInfo.mConfig.mServerSockAddr), OT_TCP_CONNECT_NO_FAST_OPEN));
|
||||
mTcpState = kTcpConecting;
|
||||
PrepareTcpMessage(*message);
|
||||
break;
|
||||
case kTcpConnectedIdle:
|
||||
PrepareTcpMessage(*message);
|
||||
SuccessOrExit(error = mEndpoint.SendByReference(mSendLink, /* aFlags */ 0));
|
||||
mTcpState = kTcpConnectedSending;
|
||||
break;
|
||||
case kTcpConecting:
|
||||
PrepareTcpMessage(*message);
|
||||
break;
|
||||
case kTcpConnectedSending:
|
||||
WriteUint16(length, mSendBufferBytes + mSendLink.mLength);
|
||||
SuccessOrAssert(error = message->Read(message->GetOffset(),
|
||||
(mSendBufferBytes + sizeof(uint16_t) + mSendLink.mLength), length));
|
||||
IgnoreError(mEndpoint.SendByExtension(length + sizeof(uint16_t), /* aFlags */ 0));
|
||||
break;
|
||||
}
|
||||
message->Free();
|
||||
message = nullptr;
|
||||
}
|
||||
#else
|
||||
{
|
||||
error = kErrorInvalidArgs;
|
||||
LogWarn("DNS query over TCP not supported.");
|
||||
ExitNow();
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
VerifyOrExit(length <= kUdpQueryMaxSize, error = kErrorInvalidArgs);
|
||||
messageInfo.SetPeerAddr(aInfo.mConfig.GetServerSockAddr().GetAddress());
|
||||
messageInfo.SetPeerPort(aInfo.mConfig.GetServerSockAddr().GetPort());
|
||||
SuccessOrExit(error = mSocket.SendTo(*message, messageInfo));
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
FreeMessageOnError(message, error);
|
||||
|
||||
UpdateQuery(aQuery, aInfo);
|
||||
|
||||
if (aUpdateTimer)
|
||||
{
|
||||
mTimer.FireAtIfEarlier(aInfo.mRetransmissionTime);
|
||||
}
|
||||
|
||||
UpdateQuery(aQuery, aInfo);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Error Client::AppendNameFromQuery(const Query &aQuery, Message &aMessage)
|
||||
@@ -1043,7 +1146,7 @@ Error Client::ParseResponse(Response &aResponse, QueryType &aType, Error &aRespo
|
||||
info.mMessageId = 0;
|
||||
info.mTransmissionCount = 0;
|
||||
|
||||
SendQuery(*aResponse.mQuery, info, /* aUpdateTimer */ true);
|
||||
IgnoreReturnValue(SendQuery(*aResponse.mQuery, info, /* aUpdateTimer */ true));
|
||||
|
||||
error = kErrorPending;
|
||||
}
|
||||
@@ -1064,6 +1167,9 @@ void Client::HandleTimer(void)
|
||||
TimeMilli now = TimerMilli::GetNow();
|
||||
TimeMilli nextTime = now.GetDistantFuture();
|
||||
QueryInfo info;
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
bool hasTcpQuery = false;
|
||||
#endif
|
||||
|
||||
for (Query &query : mQueries)
|
||||
{
|
||||
@@ -1077,18 +1183,223 @@ void Client::HandleTimer(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
SendQuery(query, info, /* aUpdateTimer */ false);
|
||||
IgnoreReturnValue(SendQuery(query, info, /* aUpdateTimer */ false));
|
||||
}
|
||||
|
||||
nextTime = Min(nextTime, info.mRetransmissionTime);
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
if (info.mConfig.GetTransportProto() == QueryConfig::kDnsTransportTcp)
|
||||
{
|
||||
hasTcpQuery = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (nextTime < now.GetDistantFuture())
|
||||
{
|
||||
mTimer.FireAt(nextTime);
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
if (!hasTcpQuery && mTcpState != kTcpUninitialized)
|
||||
{
|
||||
IgnoreError(mEndpoint.SendEndOfStream());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
void Client::PrepareTcpMessage(Message &aMessage)
|
||||
{
|
||||
uint16_t length = aMessage.GetLength() - aMessage.GetOffset();
|
||||
|
||||
// Prepending the DNS query with length of the packet according to RFC1035.
|
||||
WriteUint16(length, mSendBufferBytes + mSendLink.mLength);
|
||||
SuccessOrAssert(
|
||||
aMessage.Read(aMessage.GetOffset(), (mSendBufferBytes + sizeof(uint16_t) + mSendLink.mLength), length));
|
||||
mSendLink.mLength += length + sizeof(uint16_t);
|
||||
}
|
||||
|
||||
void Client::HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aEndpoint);
|
||||
OT_UNUSED_VARIABLE(aData);
|
||||
OT_ASSERT(mTcpState == kTcpConnectedSending);
|
||||
|
||||
mSendLink.mLength = 0;
|
||||
mTcpState = kTcpConnectedIdle;
|
||||
}
|
||||
|
||||
void Client::HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData)
|
||||
{
|
||||
static_cast<Client *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpSendDone(aEndpoint, aData);
|
||||
}
|
||||
|
||||
void Client::HandleTcpEstablished(otTcpEndpoint *aEndpoint)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aEndpoint);
|
||||
IgnoreError(mEndpoint.SendByReference(mSendLink, /* aFlags */ 0));
|
||||
mTcpState = kTcpConnectedSending;
|
||||
}
|
||||
|
||||
void Client::HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint)
|
||||
{
|
||||
static_cast<Client *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpEstablished(aEndpoint);
|
||||
}
|
||||
|
||||
Error Client::ReadFromLinkBuffer(const otLinkedBuffer *&aLinkedBuffer,
|
||||
size_t &aOffset,
|
||||
Message &aMessage,
|
||||
uint16_t aLength)
|
||||
{
|
||||
// Read `aLength` bytes from `aLinkedBuffer` starting at `aOffset`
|
||||
// and copy the content into `aMessage`. As we read we can move
|
||||
// to the next `aLinkedBuffer` and update `aOffset`.
|
||||
// Returns:
|
||||
// - `kErrorNone` if `aLengh` bytes are successfully read and
|
||||
// `aOffset` and `aLinkedBuffer` are updated.
|
||||
// - `kErrorNotFound` is not enough bytes available to read
|
||||
// from `aLinkedBuffer`.
|
||||
// - `kErrorNotBufs` if cannot grow `aMessage` to append bytes.
|
||||
|
||||
Error error = kErrorNone;
|
||||
|
||||
while (aLength > 0)
|
||||
{
|
||||
uint16_t bytesToRead = aLength;
|
||||
|
||||
VerifyOrExit(aLinkedBuffer != nullptr, error = kErrorNotFound);
|
||||
|
||||
if (bytesToRead > aLinkedBuffer->mLength - aOffset)
|
||||
{
|
||||
bytesToRead = static_cast<uint16_t>(aLinkedBuffer->mLength - aOffset);
|
||||
}
|
||||
|
||||
SuccessOrExit(error = aMessage.AppendBytes(&aLinkedBuffer->mData[aOffset], bytesToRead));
|
||||
|
||||
aLength -= bytesToRead;
|
||||
aOffset += bytesToRead;
|
||||
|
||||
if (aOffset == aLinkedBuffer->mLength)
|
||||
{
|
||||
aLinkedBuffer = aLinkedBuffer->mNext;
|
||||
aOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void Client::HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint,
|
||||
size_t aBytesAvailable,
|
||||
bool aEndOfStream,
|
||||
size_t aBytesRemaining)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aEndpoint);
|
||||
OT_UNUSED_VARIABLE(aBytesRemaining);
|
||||
|
||||
Message *message = nullptr;
|
||||
size_t totalRead = 0;
|
||||
size_t offset = 0;
|
||||
const otLinkedBuffer *data;
|
||||
|
||||
if (aEndOfStream)
|
||||
{
|
||||
// Cleanup is done in disconnected callback.
|
||||
IgnoreError(mEndpoint.SendEndOfStream());
|
||||
}
|
||||
|
||||
SuccessOrExit(mEndpoint.ReceiveByReference(data));
|
||||
VerifyOrExit(data != nullptr);
|
||||
|
||||
message = mSocket.NewMessage(0);
|
||||
VerifyOrExit(message != nullptr);
|
||||
|
||||
while (aBytesAvailable > totalRead)
|
||||
{
|
||||
uint16_t length;
|
||||
|
||||
// Read the `length` field.
|
||||
SuccessOrExit(ReadFromLinkBuffer(data, offset, *message, sizeof(uint16_t)));
|
||||
|
||||
IgnoreError(message->Read(/* aOffset */ 0, length));
|
||||
length = HostSwap16(length);
|
||||
|
||||
// Try to read `length` bytes.
|
||||
IgnoreError(message->SetLength(0));
|
||||
SuccessOrExit(ReadFromLinkBuffer(data, offset, *message, length));
|
||||
|
||||
totalRead += length + sizeof(uint16_t);
|
||||
|
||||
// Now process the read message as query response.
|
||||
{
|
||||
Response response;
|
||||
QueryType type;
|
||||
Error responseError;
|
||||
|
||||
response.mInstance = &Get<Instance>();
|
||||
response.mMessage = message;
|
||||
|
||||
if (ParseResponse(response, type, responseError) == kErrorNone)
|
||||
{
|
||||
if (responseError == kErrorNone && length > OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE)
|
||||
{
|
||||
LogWarn("Dns query over TCP wasn't received - message is too big.");
|
||||
responseError = kErrorNoBufs;
|
||||
}
|
||||
FinalizeQuery(response, type, responseError);
|
||||
}
|
||||
}
|
||||
|
||||
IgnoreError(message->SetLength(0));
|
||||
|
||||
// Loop again to see if we can read another response.
|
||||
}
|
||||
|
||||
exit:
|
||||
// Inform `mEndPoint` about the total read and processed bytes
|
||||
IgnoreError(mEndpoint.CommitReceive(totalRead, /* aFlags */ 0));
|
||||
FreeMessage(message);
|
||||
}
|
||||
|
||||
void Client::HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint,
|
||||
size_t aBytesAvailable,
|
||||
bool aEndOfStream,
|
||||
size_t aBytesRemaining)
|
||||
{
|
||||
static_cast<Client *>(otTcpEndpointGetContext(aEndpoint))
|
||||
->HandleTcpReceiveAvailable(aEndpoint, aBytesAvailable, aEndOfStream, aBytesRemaining);
|
||||
}
|
||||
|
||||
void Client::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aEndpoint);
|
||||
OT_UNUSED_VARIABLE(aReason);
|
||||
QueryInfo info;
|
||||
|
||||
IgnoreError(mEndpoint.Deinitialize());
|
||||
mTcpState = kTcpUninitialized;
|
||||
|
||||
// Abort queries in case of connection failures
|
||||
for (Query &query : mQueries)
|
||||
{
|
||||
info.ReadFrom(query);
|
||||
if (info.mConfig.GetTransportProto() == QueryConfig::kDnsTransportTcp)
|
||||
{
|
||||
FinalizeQuery(query, kErrorAbort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason)
|
||||
{
|
||||
static_cast<Client *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpDisconnected(aEndpoint, aReason);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
|
||||
} // namespace Dns
|
||||
} // namespace ot
|
||||
|
||||
|
||||
@@ -61,6 +61,10 @@
|
||||
|
||||
#endif
|
||||
|
||||
#if !OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
#error "OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE requires OPENTHREAD_CONFIG_TCP_ENABLE"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This struct represents an opaque (and empty) type for a response to an address resolution DNS query.
|
||||
*
|
||||
@@ -136,11 +140,22 @@ public:
|
||||
enum Nat64Mode : uint8_t
|
||||
{
|
||||
kNat64Unspecified = OT_DNS_NAT64_UNSPECIFIED, ///< NAT64 mode is not specified. Use default NAT64 mode.
|
||||
kNat64Allow = OT_DNS_NAT64_ALLOW, ///< Allow NAT64 address translation
|
||||
kNat64Allow = OT_DNS_NAT64_ALLOW, ///< Allow NAT64 address translation.
|
||||
kNat64Disallow = OT_DNS_NAT64_DISALLOW, ///< Disallow NAT64 address translation.
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This enumeration type represents the DNS transport protocol selection.
|
||||
*
|
||||
*/
|
||||
enum TransportProto : uint8_t
|
||||
{
|
||||
kDnsTransportUnspecified = OT_DNS_TRANSPORT_UNSPECIFIED, /// Dns transport is unspecified.
|
||||
kDnsTransportUdp = OT_DNS_TRANSPORT_UDP, /// Dns query should be sent via UDP.
|
||||
kDnsTransportTcp = OT_DNS_TRANSPORT_TCP, /// Dns query should be sent via TCP.
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the default constructor for `QueryConfig` object.
|
||||
*
|
||||
@@ -192,6 +207,14 @@ public:
|
||||
Nat64Mode GetNat64Mode(void) const { return static_cast<Nat64Mode>(mNat64Mode); }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This method gets the transport protocol.
|
||||
*
|
||||
* @returns The transport protocol.
|
||||
*
|
||||
*/
|
||||
TransportProto GetTransportProto(void) const { return static_cast<TransportProto>(mTransportProto); };
|
||||
|
||||
private:
|
||||
static constexpr uint32_t kDefaultResponseTimeout = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RESPONSE_TIMEOUT;
|
||||
static constexpr uint16_t kDefaultServerPort = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_PORT;
|
||||
@@ -219,7 +242,10 @@ public:
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
|
||||
void SetNat64Mode(Nat64Mode aMode) { mNat64Mode = static_cast<otDnsNat64Mode>(aMode); }
|
||||
#endif
|
||||
|
||||
void SetTransportProto(TransportProto aTransportProto)
|
||||
{
|
||||
mTransportProto = static_cast<otDnsTransportProto>(aTransportProto);
|
||||
}
|
||||
void SetFrom(const QueryConfig &aConfig, const QueryConfig &aDefaultConfig);
|
||||
};
|
||||
|
||||
@@ -699,6 +725,16 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
enum TcpState : uint8_t
|
||||
{
|
||||
kTcpUninitialized = 0,
|
||||
kTcpConecting,
|
||||
kTcpConnectedIdle,
|
||||
kTcpConnectedSending,
|
||||
};
|
||||
#endif
|
||||
|
||||
union Callback
|
||||
{
|
||||
AddressCallback mAddressCallback;
|
||||
@@ -734,7 +770,7 @@ private:
|
||||
Error AllocateQuery(const QueryInfo &aInfo, const char *aLabel, const char *aName, Query *&aQuery);
|
||||
void FreeQuery(Query &aQuery);
|
||||
void UpdateQuery(Query &aQuery, const QueryInfo &aInfo) { aQuery.Write(0, aInfo); }
|
||||
void SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer);
|
||||
Error SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer);
|
||||
void FinalizeQuery(Query &aQuery, Error aError);
|
||||
void FinalizeQuery(Response &Response, QueryType aType, Error aError);
|
||||
static void GetCallback(const Query &aQuery, Callback &aCallback, void *&aContext);
|
||||
@@ -744,6 +780,31 @@ private:
|
||||
void ProcessResponse(const Message &aMessage);
|
||||
Error ParseResponse(Response &aResponse, QueryType &aType, Error &aResponseError);
|
||||
void HandleTimer(void);
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
static void HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint);
|
||||
static void HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData);
|
||||
static void HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason);
|
||||
static void HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint,
|
||||
size_t aBytesAvailable,
|
||||
bool aEndOfStream,
|
||||
size_t aBytesRemaining);
|
||||
|
||||
void HandleTcpEstablished(otTcpEndpoint *aEndpoint);
|
||||
void HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData);
|
||||
void HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason);
|
||||
void HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint,
|
||||
size_t aBytesAvailable,
|
||||
bool aEndOfStream,
|
||||
size_t aBytesRemaining);
|
||||
Error InitTcpSocket(void);
|
||||
Error ReadFromLinkBuffer(const otLinkedBuffer *&aLinkedBuffer,
|
||||
size_t &aOffset,
|
||||
Message &aMessage,
|
||||
uint16_t aLength);
|
||||
void PrepareTcpMessage(Message &aMessage);
|
||||
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
|
||||
Error CheckAddressResponse(Response &aResponse, Error aResponseError) const;
|
||||
#endif
|
||||
@@ -763,12 +824,25 @@ private:
|
||||
static const uint16_t kServiceQueryRecordTypes[];
|
||||
#endif
|
||||
|
||||
static constexpr uint16_t kUdpQueryMaxSize = 512;
|
||||
|
||||
using RetryTimer = TimerMilliIn<Client, &Client::HandleTimer>;
|
||||
|
||||
Ip6::Udp::Socket mSocket;
|
||||
QueryList mQueries;
|
||||
RetryTimer mTimer;
|
||||
QueryConfig mDefaultConfig;
|
||||
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
|
||||
Ip6::Tcp::Endpoint mEndpoint;
|
||||
|
||||
otLinkedBuffer mSendLink;
|
||||
uint8_t mSendBufferBytes[OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE];
|
||||
uint8_t mReceiveBufferBytes[OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE];
|
||||
|
||||
TcpState mTcpState;
|
||||
#endif
|
||||
|
||||
QueryList mQueries;
|
||||
RetryTimer mTimer;
|
||||
QueryConfig mDefaultConfig;
|
||||
#if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE
|
||||
bool mUserDidSetDefaultAddress;
|
||||
#endif
|
||||
|
||||
@@ -30,7 +30,10 @@
|
||||
source "tests/scripts/expect/_common.exp"
|
||||
source "tests/scripts/expect/_multinode.exp"
|
||||
|
||||
setup_two_nodes
|
||||
spawn_node 2 "cli"
|
||||
spawn_node 1 "cli"
|
||||
setup_leader
|
||||
setup_node 2 "rnd" "router"
|
||||
|
||||
switch_node 1
|
||||
send "tcp init tls\n"
|
||||
|
||||
@@ -30,7 +30,10 @@
|
||||
source "tests/scripts/expect/_common.exp"
|
||||
source "tests/scripts/expect/_multinode.exp"
|
||||
|
||||
setup_two_nodes
|
||||
spawn_node 2 "cli"
|
||||
spawn_node 1 "cli"
|
||||
setup_leader
|
||||
setup_node 2 "rnd" "router"
|
||||
|
||||
switch_node 1
|
||||
send "tcp init circular\n"
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/expect -f
|
||||
#
|
||||
# Copyright (c) 2020, 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.
|
||||
#
|
||||
|
||||
source "tests/scripts/expect/_common.exp"
|
||||
source "tests/scripts/expect/_multinode.exp"
|
||||
|
||||
spawn_node 2 "cli"
|
||||
spawn_node 1
|
||||
setup_leader
|
||||
setup_node 2 "rnd" "router"
|
||||
|
||||
switch_node 1
|
||||
set addr_1 [get_ipaddr mleid]
|
||||
|
||||
switch_node 2
|
||||
send "dns resolve ipv6.google.com $addr_1 2000 6000 4 1 tcp\n"
|
||||
expect "DNS response for ipv6.google.com"
|
||||
expect_line "Done"
|
||||
|
||||
dispose_all
|
||||
Reference in New Issue
Block a user