[dns] implement DNS query over TCP (#8474)

This commit introduces DNS query over TCP protocol.
This commit is contained in:
Przemysław Bida
2023-03-17 09:44:26 -07:00
committed by GitHub
parent 30be45dc5c
commit 89d5799d76
13 changed files with 529 additions and 30 deletions
+1
View File
@@ -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
+1 -1
View File
@@ -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()
+19 -5
View File
@@ -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;
/**
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+17
View File
@@ -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;
}
+21
View File
@@ -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
View File
@@ -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
+80 -6
View File
@@ -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
+4 -1
View File
@@ -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"
+4 -1
View File
@@ -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
View File
@@ -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