[dns-client] add ServiceMode to control service resolution (#8772)

This commit updates DNS client to add `otDnsServiceMode` to the
`otDnsQueryConfig`. This new config property determines which records
to query and allow the API user to control the behavior during
service resolution: We can query for SRV record only or TXT record
only, or query for both SRV and TXT records in the same message, or
in parallel in different messages, or an "optimized" mode where
client will first try to query for both records together in the same
message but if server responds with an error, it then retries using
two parallel separate queries.

This gives flexibility and control to the API user. It also helps
address situations where the server (DNS resolver) may not accept
queries with more than one questions.

To support this new feature, this commit updates and enhances the
internal design of the `Dns::Client`. A new mechanism is added to
allow multiple `Query` instances to be associated with each other
under a main `Query` and responses for the related queries are saved
until all are received and validated before finalizing the main
`Query` and invoking the callback and allowing caller to retrieve the
info.

This commit also adds a detailed unit test `test_dns_client` which
covers DNS client browse and service resolution validating behavior
under all service modes. In order to test `Client` functionality,
this commit also adds a `TestMode` in `Dns::ServiceDiscovery::Server`
allowing us to change server behavior, e.g., reject messages with
more than one question in query.
This commit is contained in:
Abtin Keshavarzian
2023-04-05 15:49:08 -07:00
committed by GitHub
parent e587d43e4b
commit 3ffe8516f7
13 changed files with 1515 additions and 283 deletions
+26 -5
View File
@@ -80,6 +80,23 @@ typedef enum
OT_DNS_NAT64_DISALLOW = 2, ///< Do not allow NAT64 address translation during DNS client address resolution.
} otDnsNat64Mode;
/**
* This enumeration type represents the service resolution mode in an `otDnsQueryConfig`.
*
* This is only used during DNS client service resolution `otDnsClientResolveService()`. It determines which
* record types to query.
*
*/
typedef enum
{
OT_DNS_SERVICE_MODE_UNSPECIFIED = 0, ///< Mode is not specified. Use default service mode.
OT_DNS_SERVICE_MODE_SRV = 1, ///< Query for SRV record only.
OT_DNS_SERVICE_MODE_TXT = 2, ///< Query for TXT record only.
OT_DNS_SERVICE_MODE_SRV_TXT = 3, ///< Query for both SRV and TXT records in same message.
OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE = 4, ///< Query in parallel for SRV and TXT using separate messages.
OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE = 5, ///< Query for TXT/SRV together first, if fails then query separately.
} otDnsServiceMode;
/**
* This enumeration type represents the DNS transport protocol in an `otDnsQueryConfig`.
*
@@ -102,11 +119,12 @@ typedef enum
*/
typedef struct otDnsQueryConfig
{
otSockAddr mServerSockAddr; ///< Server address (IPv6 address/port). All zero or zero port for unspecified.
otSockAddr mServerSockAddr; ///< Server address (IPv6 addr/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.
otDnsServiceMode mServiceMode; ///< Determines which records to query during service resolution.
otDnsTransportProto mTransportProto; ///< Select default transport protocol.
} otDnsQueryConfig;
@@ -420,7 +438,8 @@ otError otDnsBrowseResponseGetServiceInstance(const otDnsBrowseResponse *aRespon
* (note that it is a SHOULD and not a MUST requirement). This function tries to retrieve this info for a given service
* instance when available.
*
* - If no matching SRV record is found in @p aResponse, `OT_ERROR_NOT_FOUND` is returned.
* - If no matching SRV record is found in @p aResponse, `OT_ERROR_NOT_FOUND` is returned. In this case, no additional
* records (no TXT and/or AAAA) are read.
* - If a matching SRV record is found in @p aResponse, @p aServiceInfo is updated and `OT_ERROR_NONE` is returned.
* - If no matching TXT record is found in @p aResponse, `mTxtDataSize` in @p aServiceInfo is set to zero.
* - If TXT data length is greater than `mTxtDataSize`, it is read partially and `mTxtDataTruncated` is set to true.
@@ -550,8 +569,10 @@ otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse
*
* This function MUST only be used from `otDnsServiceCallback`.
*
* - If no matching SRV record is found in @p aResponse, `OT_ERROR_NOT_FOUND` is returned.
* - If a matching SRV record is found in @p aResponse, @p aServiceInfo is updated and `OT_ERROR_NONE` is returned.
* - If a matching SRV record is found in @p aResponse, @p aServiceInfo is updated.
* - If no matching SRV record is found, `OT_ERROR_NOT_FOUND` is returned unless the query config for this query
* used `OT_DNS_SERVICE_MODE_TXT` for `mServiceMode` (meaning the request was only for TXT record). In this case, we
* still try to parse the SRV record from Additional Data Section of response (in case server provided the info).
* - If no matching TXT record is found in @p aResponse, `mTxtDataSize` in @p aServiceInfo is set to zero.
* - If TXT data length is greater than `mTxtDataSize`, it is read partially and `mTxtDataTruncated` is set to true.
* - If no matching AAAA record is found in @p aResponse, `mHostAddress is set to all zero or unspecified address.
@@ -562,7 +583,7 @@ otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse
* @param[out] aServiceInfo A `ServiceInfo` to output the service instance information (MUST NOT be NULL).
*
* @retval OT_ERROR_NONE The service instance info was read. @p aServiceInfo is updated.
* @retval OT_ERROR_NOT_FOUND Could not find a matching SRV record in @p aResponse.
* @retval OT_ERROR_NOT_FOUND Could not find a required record in @p aResponse.
* @retval OT_ERROR_NO_BUFS The host name and/or TXT data could not fit in the given buffers.
* @retval OT_ERROR_PARSE Could not parse the records in the @p aResponse.
*
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (306)
#define OPENTHREAD_API_VERSION (307)
/**
* @addtogroup api-instance
+29 -3
View File
@@ -1113,7 +1113,20 @@ Done
Get the default query config used by DNS client.
The config includes the server IPv6 address and port, response timeout in msec (wait time to rx response), maximum tx attempts before reporting failure, boolean flag to indicate whether the server can resolve the query recursively or not.
The config includes
- Server IPv6 address and port
- Response timeout in msec (wait time to rx response)
- Maximum tx attempts before reporting failure
- Boolean flag to indicate whether the server can resolve the query recursively or not.
- Service resolution mode which specifies which records to query. Possible options are:
- `srv` : Query for SRV record only.
- `txt` : Query for TXT record only.
- `srv_txt` : Query for both SRV and TXT records in the same message.
- `srv_txt_sep`: Query in parallel for SRV and TXT using separate messages.
- `srv_txt_opt`: Query for TXT/SRV together first, if it fails then query separately.
- Whether to allow/disallow NAT64 address translation during address resolution (requires `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE`)
- Transport protocol UDP or TCP (requires `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE`)
```bash
> dns config
@@ -1121,19 +1134,30 @@ Server: [fd00:0:0:0:0:0:0:1]:1234
ResponseTimeout: 5000 ms
MaxTxAttempts: 2
RecursionDesired: no
ServiceMode: srv_txt_opt
Nat64Mode: allow
TransportProtocol: udp
Done
>
```
### dns config \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] \[transport protocol\]
### dns config \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] \[service mode]
Set the default query config.
Service mode specifies which records to query. Possible options are:
- `def` : Use default option.
- `srv` : Query for SRV record only.
- `txt` : Query for TXT record only.
- `srv_txt` : Query for both SRV and TXT records in the same message.
- `srv_txt_sep`: Query in parallel for SRV and TXT using separate messages.
- `srv_txt_opt`: Query for TXT/SRV together first, if it fails then query separately.
To set protocol effectively to tcp `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE` is required.
```bash
> dns config fd00::1 1234 5000 2 0 tcp
> dns config fd00::1 1234 5000 2 0 srv_txt_sep tcp
Done
> dns config
@@ -1141,6 +1165,8 @@ Server: [fd00:0:0:0:0:0:0:1]:1234
ResponseTimeout: 5000 ms
MaxTxAttempts: 2
RecursionDesired: no
ServiceMode: srv_txt_sep
Nat64Mode: allow
TransportProtocol: tcp
Done
```
+73 -4
View File
@@ -2990,6 +2990,8 @@ template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
* ResponseTimeout: 5000 ms
* MaxTxAttempts: 2
* RecursionDesired: no
* ServiceMode: srv
* Nat64Mode: allow
* Done
* @endcode
* @par api_copy
@@ -3011,6 +3013,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");
OutputLine("ServiceMode: %s", DnsConfigServiceModeToString(defaultConfig->mServiceMode));
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
OutputLine("Nat64Mode: %s", (defaultConfig->mNat64Mode == OT_DNS_NAT64_ALLOW) ? "allow" : "disallow");
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
OutputLine("TransportProtocol: %s",
(defaultConfig->mTransportProto == OT_DNS_TRANSPORT_UDP) ? "udp" : "tcp");
@@ -3046,7 +3052,7 @@ template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
* #otDnsClientSetDefaultConfig
* @cparam dns config [@ca{dns-server-IP}] [@ca{dns-server-port}] <!--
* --> [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
* --> [@ca{recursion-desired-boolean}]
* --> [@ca{recursion-desired-boolean}] [@ca{service-mode}]
* @par
* We can leave some of the fields as unspecified (or use value zero). The
* unspecified fields are replaced by the corresponding OT config option
@@ -3256,11 +3262,69 @@ exit:
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
const char *Interpreter::DnsConfigServiceModeToString(otDnsServiceMode aMode) const
{
static const char *const kServiceModeStrings[] = {
"unspec", // OT_DNS_SERVICE_MODE_UNSPECIFIED (0)
"srv", // OT_DNS_SERVICE_MODE_SRV (1)
"txt", // OT_DNS_SERVICE_MODE_TXT (2)
"srv_txt", // OT_DNS_SERVICE_MODE_SRV_TXT (3)
"srv_txt_sep", // OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE (4)
"srv_txt_opt", // OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE (5)
};
static_assert(OT_DNS_SERVICE_MODE_UNSPECIFIED == 0, "OT_DNS_SERVICE_MODE_UNSPECIFIED value is incorrect");
static_assert(OT_DNS_SERVICE_MODE_SRV == 1, "OT_DNS_SERVICE_MODE_SRV value is incorrect");
static_assert(OT_DNS_SERVICE_MODE_TXT == 2, "OT_DNS_SERVICE_MODE_TXT value is incorrect");
static_assert(OT_DNS_SERVICE_MODE_SRV_TXT == 3, "OT_DNS_SERVICE_MODE_SRV_TXT value is incorrect");
static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE == 4, "OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE value is incorrect");
static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE == 5, "OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE value is incorrect");
return Stringify(aMode, kServiceModeStrings);
}
otError Interpreter::ParseDnsServiceMode(const Arg &aArg, otDnsServiceMode &aMode) const
{
otError error = OT_ERROR_NONE;
if (aArg == "def")
{
aMode = OT_DNS_SERVICE_MODE_UNSPECIFIED;
}
else if (aArg == "srv")
{
aMode = OT_DNS_SERVICE_MODE_SRV;
}
else if (aArg == "txt")
{
aMode = OT_DNS_SERVICE_MODE_TXT;
}
else if (aArg == "srv_txt")
{
aMode = OT_DNS_SERVICE_MODE_SRV_TXT;
}
else if (aArg == "srv_txt_sep")
{
aMode = OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE;
}
else if (aArg == "srv_txt_opt")
{
aMode = OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE;
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
return error;
}
otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig)
{
// This method gets the optional DNS config from `aArgs[]`.
// The format: `[server IP address] [server port] [timeout]
// [max tx attempt] [recursion desired]`.
// [max tx attempt] [recursion desired] [service mode]
// [transport]`
otError error = OT_ERROR_NONE;
bool recursionDesired;
@@ -3292,11 +3356,15 @@ otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig)
aConfig->mRecursionFlag = recursionDesired ? OT_DNS_FLAG_RECURSION_DESIRED : OT_DNS_FLAG_NO_RECURSION;
VerifyOrExit(!aArgs[5].IsEmpty());
if (aArgs[5] == "tcp")
SuccessOrExit(error = ParseDnsServiceMode(aArgs[5], aConfig->mServiceMode));
VerifyOrExit(!aArgs[6].IsEmpty());
if (aArgs[6] == "tcp")
{
aConfig->mTransportProto = OT_DNS_TRANSPORT_TCP;
}
else if (aArgs[5] == "udp")
else if (aArgs[6] == "udp")
{
aConfig->mTransportProto = OT_DNS_TRANSPORT_UDP;
}
@@ -3304,6 +3372,7 @@ otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig)
{
error = OT_ERROR_INVALID_ARGS;
}
exit:
return error;
}
+2
View File
@@ -459,6 +459,8 @@ private:
otError GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig);
static void HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext);
void HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse);
const char *DnsConfigServiceModeToString(otDnsServiceMode aMode) const;
otError ParseDnsServiceMode(const Arg &aArg, otDnsServiceMode &aMode) const;
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
void OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo);
static void HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext);
+10
View File
@@ -151,6 +151,16 @@
#define OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RECURSION_DESIRED_FLAG 1
#endif
/**
* @def OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE
*
* Specifies the default `otDnsServiceMode` to use. The value MUST be from `otDnsServiceMode` enumeration.
*
*/
#ifndef OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE
#define OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE
#endif
/**
* @def OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
*
File diff suppressed because it is too large Load Diff
+65 -23
View File
@@ -145,6 +145,20 @@ public:
};
#endif
/**
* This enumeration type represents the service resolution mode.
*
*/
enum ServiceMode : uint8_t
{
kServiceModeUnspecified = OT_DNS_SERVICE_MODE_UNSPECIFIED, ///< Unspecified. Use default.
kServiceModeSrv = OT_DNS_SERVICE_MODE_SRV, ///< SRV record only.
kServiceModeTxt = OT_DNS_SERVICE_MODE_TXT, ///< TXT record only.
kServiceModeSrvTxt = OT_DNS_SERVICE_MODE_SRV_TXT, ///< SRV and TXT same msg.
kServiceModeSrvTxtSeparate = OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE, ///< SRV and TXT separate msgs.
kServiceModeSrvTxtOptimize = OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE, ///< Same msg first, if fail separate.
};
/**
* This enumeration type represents the DNS transport protocol selection.
*
@@ -206,6 +220,13 @@ public:
*/
Nat64Mode GetNat64Mode(void) const { return static_cast<Nat64Mode>(mNat64Mode); }
#endif
/**
* This method gets the service resolution mode.
*
* @returns The service resolution mode.
*
*/
ServiceMode GetServiceMode(void) const { return static_cast<ServiceMode>(mServiceMode); }
/**
* This method gets the transport protocol.
@@ -220,6 +241,10 @@ public:
static constexpr uint16_t kDefaultServerPort = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_PORT;
static constexpr uint8_t kDefaultMaxTxAttempts = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_MAX_TX_ATTEMPTS;
static constexpr bool kDefaultRecursionDesired = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RECURSION_DESIRED_FLAG;
static constexpr ServiceMode kDefaultServiceMode =
static_cast<ServiceMode>(OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE);
static_assert(kDefaultServiceMode != kServiceModeUnspecified, "Invalid default service mode");
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
static constexpr bool kDefaultNat64Allowed = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_NAT64_ALLOWED;
@@ -239,6 +264,7 @@ public:
void SetResponseTimeout(uint32_t aResponseTimeout) { mResponseTimeout = aResponseTimeout; }
void SetMaxTxAttempts(uint8_t aMaxTxAttempts) { mMaxTxAttempts = aMaxTxAttempts; }
void SetRecursionFlag(RecursionFlag aFlag) { mRecursionFlag = static_cast<otDnsRecursionFlag>(aFlag); }
void SetServiceMode(ServiceMode aMode) { mServiceMode = static_cast<otDnsServiceMode>(aMode); }
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
void SetNat64Mode(Nat64Mode aMode) { mNat64Mode = static_cast<otDnsNat64Mode>(aMode); }
#endif
@@ -246,7 +272,8 @@ public:
{
mTransportProto = static_cast<otDnsTransportProto>(aTransportProto);
}
void SetFrom(const QueryConfig &aConfig, const QueryConfig &aDefaultConfig);
void SetFrom(const QueryConfig *aConfig, const QueryConfig &aDefaultConfig);
};
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
@@ -292,12 +319,16 @@ public:
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
Error FindServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const;
void InitServiceInfo(ServiceInfo &aServiceInfo) const;
Error ReadServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const;
Error ReadTxtRecord(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const;
#endif
void PopulateFrom(const Message &aMessage);
Instance *mInstance; // The OpenThread instance.
Query *mQuery; // The associated query.
const Message *mMessage; // The response message.
Response *mNext; // The next response when we have related queries.
uint16_t mAnswerOffset; // Answer section offset in `mMessage`.
uint16_t mAnswerRecordCount; // Number of records in answer section.
uint16_t mAdditionalOffset; // Additional data section offset in `mMessage`.
@@ -720,9 +751,12 @@ private:
kIp4AddressQuery, // IPv4 Address resolution
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
kBrowseQuery, // Browse (service instance enumeration).
kServiceQuery, // Service instance resolution.
kBrowseQuery, // Browse (service instance enumeration).
kServiceQuerySrvTxt, // Service instance resolution both SRV and TXT records.
kServiceQuerySrv, // Service instance resolution SRV record only.
kServiceQueryTxt, // Service instance resolution TXT record only.
#endif
kNoQuery,
};
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
@@ -757,30 +791,45 @@ private:
TimeMilli mRetransmissionTime;
QueryConfig mConfig;
uint8_t mTransmissionCount;
Query *mMainQuery;
Query *mNextQuery;
Message *mSavedResponse;
// Followed by the name (service, host, instance) encoded as a `Dns::Name`.
};
static constexpr uint16_t kNameOffsetInQuery = sizeof(QueryInfo);
Error StartQuery(QueryInfo &aInfo,
const QueryConfig *aConfig,
const char *aLabel,
const char *aName,
void *aContext);
Error StartQuery(QueryInfo &aInfo, const char *aLabel, const char *aName, QueryType aSecondType = kNoQuery);
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); }
Query &FindMainQuery(Query &aQuery);
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);
void FinalizeQuery(Response &Response, Error aError);
static void GetQueryTypeAndCallback(const Query &aQuery, QueryType &aType, Callback &aCallback, void *&aContext);
Error AppendNameFromQuery(const Query &aQuery, Message &aMessage);
Query *FindQueryById(uint16_t aMessageId);
static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMsgInfo);
void ProcessResponse(const Message &aMessage);
Error ParseResponse(Response &aResponse, QueryType &aType, Error &aResponseError);
void ProcessResponse(const Message &aResponseMessage);
Error ParseResponse(const Message &aResponseMessage, Query *&aQuery, Error &aResponseError);
bool CanFinalizeQuery(Query &aQuery);
void SaveQueryResponse(Query &aQuery, const Message &aResponseMessage);
Query *PopulateResponse(Response &aResponse, Query &aQuery, const Message &aResponseMessage);
void PrepareResponseAndFinalize(Query &aQuery, const Message &aResponseMessage, Response *aPrevResponse);
void HandleTimer(void);
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
Error ReplaceWithIp4Query(Query &aQuery);
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
Error ReplaceWithSeparateSrvTxtQueries(Query &aQuery);
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE
void UpdateDefaultConfigAddress(void);
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
static void HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint);
static void HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData);
@@ -805,15 +854,8 @@ private:
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
#if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE
void UpdateDefaultConfigAddress(void);
#endif
static const uint8_t kQuestionCount[];
static const uint16_t *kQuestionRecordTypes[];
static const uint8_t kQuestionCount[];
static const uint16_t *const kQuestionRecordTypes[];
static const uint16_t kIp6AddressQueryRecordTypes[];
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
@@ -840,7 +882,7 @@ private:
TcpState mTcpState;
#endif
QueryList mQueries;
QueryList mMainQueries;
RetryTimer mTimer;
QueryConfig mDefaultConfig;
#if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE
+10
View File
@@ -70,6 +70,7 @@ Server::Server(Instance &aInstance)
, mEnableUpstreamQuery(false)
#endif
, mTimer(aInstance)
, mTestMode(kTestModeDisabled)
{
mCounters.Clear();
}
@@ -203,6 +204,15 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage
VerifyOrExit(!aRequestHeader.IsTruncationFlagSet(), response = Header::kResponseFormatError);
VerifyOrExit(aRequestHeader.GetQuestionCount() > 0, response = Header::kResponseFormatError);
switch (mTestMode)
{
case kTestModeDisabled:
break;
case kTestModeSingleQuestionOnly:
VerifyOrExit(aRequestHeader.GetQuestionCount() == 1, response = Header::kResponseFormatError);
break;
}
response = AddQuestions(aRequestHeader, aRequestMessage, responseHeader, *responseMessage, compressInfo);
VerifyOrExit(response == Header::kResponseSuccess);
+23 -2
View File
@@ -262,6 +262,27 @@ public:
*/
const Counters &GetCounters(void) const { return mCounters; };
/**
* This enumeration represents different test modes.
*
* The test mode is intended for testing the client by having server behave in certain ways, e.g., reject messages
* with certain format (e.g., more than one question in query).
*
*/
enum TestMode : uint8_t
{
kTestModeDisabled, ///< Test mode is disabled.
kTestModeSingleQuestionOnly, ///< Allow single question in query message, send `FormatError` for two or more.
};
/**
* This method sets the test mode for `Server`.
*
* @param[in] aTestMode The new test mode.
*
*/
void SetTestMode(TestMode aTestMode) { mTestMode = aTestMode; }
private:
class NameCompressInfo : public Clearable<NameCompressInfo>
{
@@ -531,8 +552,8 @@ private:
#endif
ServerTimer mTimer;
Counters mCounters;
Counters mCounters;
TestMode mTestMode;
};
} // namespace ServiceDiscovery
@@ -39,7 +39,7 @@ 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"
send "dns resolve ipv6.google.com $addr_1 2000 6000 4 1 def tcp\n"
expect "DNS response for ipv6.google.com"
expect_line "Done"
+21
View File
@@ -258,6 +258,27 @@ target_link_libraries(ot-test-dns
add_test(NAME ot-test-dns COMMAND ot-test-dns)
add_executable(ot-test-dns-client
test_dns_client.cpp
)
target_include_directories(ot-test-dns-client
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(ot-test-dns-client
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(ot-test-dns-client
PRIVATE
${COMMON_LIBS}
)
add_test(NAME ot-test-dns-client COMMAND ot-test-dns-client)
add_executable(ot-test-dso
test_dso.cpp
)
+682
View File
@@ -0,0 +1,682 @@
/*
* Copyright (c) 2023, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <openthread/config.h>
#include "test_platform.h"
#include "test_util.hpp"
#include <openthread/dns_client.h>
#include <openthread/srp_client.h>
#include <openthread/srp_server.h>
#include <openthread/thread.h>
#include "common/arg_macros.hpp"
#include "common/array.hpp"
#include "common/instance.hpp"
#include "common/string.hpp"
#include "common/time.hpp"
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE && \
OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE && OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE && \
OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && \
!OPENTHREAD_CONFIG_TIME_SYNC_ENABLE && !OPENTHREAD_PLATFORM_POSIX
#define ENABLE_DNS_TEST 1
#else
#define ENABLE_DNS_TEST 0
#endif
#if ENABLE_DNS_TEST
using namespace ot;
// Logs a message and adds current time (sNow) as "<hours>:<min>:<secs>.<msec>"
#define Log(...) \
printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \
(sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__))
static constexpr uint16_t kMaxRaSize = 800;
static ot::Instance *sInstance;
static uint32_t sNow = 0;
static uint32_t sAlarmTime;
static bool sAlarmOn = false;
static otRadioFrame sRadioTxFrame;
static uint8_t sRadioTxFramePsdu[OT_RADIO_FRAME_MAX_SIZE];
static bool sRadioTxOngoing = false;
//----------------------------------------------------------------------------------------------------------------------
// Function prototypes
void ProcessRadioTxAndTasklets(void);
void AdvanceTime(uint32_t aDuration);
//----------------------------------------------------------------------------------------------------------------------
// `otPlatRadio`
extern "C" {
otError otPlatRadioTransmit(otInstance *, otRadioFrame *)
{
sRadioTxOngoing = true;
return OT_ERROR_NONE;
}
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) { return &sRadioTxFrame; }
//----------------------------------------------------------------------------------------------------------------------
// `otPlatAlaram`
void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; }
void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
{
sAlarmOn = true;
sAlarmTime = aT0 + aDt;
}
uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
//----------------------------------------------------------------------------------------------------------------------
Array<void *, 500> sHeapAllocatedPtrs;
#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
void *otPlatCAlloc(size_t aNum, size_t aSize)
{
void *ptr = calloc(aNum, aSize);
SuccessOrQuit(sHeapAllocatedPtrs.PushBack(ptr));
return ptr;
}
void otPlatFree(void *aPtr)
{
if (aPtr != nullptr)
{
void **entry = sHeapAllocatedPtrs.Find(aPtr);
VerifyOrQuit(entry != nullptr, "A heap allocated item is freed twice");
sHeapAllocatedPtrs.Remove(*entry);
}
free(aPtr);
}
#endif
#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED
void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
{
OT_UNUSED_VARIABLE(aLogLevel);
OT_UNUSED_VARIABLE(aLogRegion);
va_list args;
printf(" ");
va_start(args, aFormat);
vprintf(aFormat, args);
va_end(args);
printf("\n");
}
#endif
} // extern "C"
//---------------------------------------------------------------------------------------------------------------------
void ProcessRadioTxAndTasklets(void)
{
do
{
if (sRadioTxOngoing)
{
sRadioTxOngoing = false;
otPlatRadioTxStarted(sInstance, &sRadioTxFrame);
otPlatRadioTxDone(sInstance, &sRadioTxFrame, nullptr, OT_ERROR_NONE);
}
otTaskletsProcess(sInstance);
} while (otTaskletsArePending(sInstance));
}
void AdvanceTime(uint32_t aDuration)
{
uint32_t time = sNow + aDuration;
Log("AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000);
while (TimeMilli(sAlarmTime) <= TimeMilli(time))
{
ProcessRadioTxAndTasklets();
sNow = sAlarmTime;
otPlatAlarmMilliFired(sInstance);
}
ProcessRadioTxAndTasklets();
sNow = time;
}
void InitTest(void)
{
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Initialize OT instance.
sNow = 0;
sInstance = static_cast<Instance *>(testInitInstance());
memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame));
sRadioTxFrame.mPsdu = sRadioTxFramePsdu;
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Initialize Border Router and start Thread operation.
SuccessOrQuit(otLinkSetPanId(sInstance, 0x1234));
SuccessOrQuit(otIp6SetEnabled(sInstance, true));
SuccessOrQuit(otThreadSetEnabled(sInstance, true));
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ensure device starts as leader.
AdvanceTime(10000);
VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER);
}
void FinalizeTest(void)
{
SuccessOrQuit(otIp6SetEnabled(sInstance, false));
SuccessOrQuit(otThreadSetEnabled(sInstance, false));
// Make sure there is no message/buffer leak
VerifyOrQuit(sInstance->Get<MessagePool>().GetFreeBufferCount() ==
sInstance->Get<MessagePool>().GetTotalBufferCount());
SuccessOrQuit(otInstanceErasePersistentInfo(sInstance));
testFreeInstance(sInstance);
}
//---------------------------------------------------------------------------------------------------------------------
static const char kHostName[] = "elden";
static const char kHostFullName[] = "elden.default.service.arpa.";
static const char kService1Name[] = "_srv._udp";
static const char kService1FullName[] = "_srv._udp.default.service.arpa.";
static const char kInstance1Label[] = "srv-instance";
static const char kService2Name[] = "_game._udp";
static const char kService2FullName[] = "_game._udp.default.service.arpa.";
static const char kInstance2Label[] = "last-ninja";
void PrepareService1(Srp::Client::Service &aService)
{
static const char kSub1[] = "_sub1";
static const char kSub2[] = "_V1234567";
static const char kSub3[] = "_XYZWS";
static const char *kSubLabels[] = {kSub1, kSub2, kSub3, nullptr};
static const char kTxtKey1[] = "ABCD";
static const uint8_t kTxtValue1[] = {'a', '0'};
static const char kTxtKey2[] = "Z0";
static const uint8_t kTxtValue2[] = {'1', '2', '3'};
static const char kTxtKey3[] = "D";
static const uint8_t kTxtValue3[] = {0};
static const otDnsTxtEntry kTxtEntries[] = {
{kTxtKey1, kTxtValue1, sizeof(kTxtValue1)},
{kTxtKey2, kTxtValue2, sizeof(kTxtValue2)},
{kTxtKey3, kTxtValue3, sizeof(kTxtValue3)},
};
memset(&aService, 0, sizeof(aService));
aService.mName = kService1Name;
aService.mInstanceName = kInstance1Label;
aService.mSubTypeLabels = kSubLabels;
aService.mTxtEntries = kTxtEntries;
aService.mNumTxtEntries = 3;
aService.mPort = 777;
aService.mWeight = 1;
aService.mPriority = 2;
}
void PrepareService2(Srp::Client::Service &aService)
{
static const char kSub4[] = "_44444444";
static const char *kSubLabels2[] = {kSub4, nullptr};
memset(&aService, 0, sizeof(aService));
aService.mName = kService2Name;
aService.mInstanceName = kInstance2Label;
aService.mSubTypeLabels = kSubLabels2;
aService.mTxtEntries = nullptr;
aService.mNumTxtEntries = 0;
aService.mPort = 555;
aService.mWeight = 0;
aService.mPriority = 3;
}
void ValidateHost(Srp::Server &aServer, const char *aHostName)
{
// Validate that only a host with `aHostName` is
// registered on SRP server.
const Srp::Server::Host *host;
const char *name;
Log("ValidateHost()");
host = aServer.GetNextHost(nullptr);
VerifyOrQuit(host != nullptr);
name = host->GetFullName();
Log("Hostname: %s", name);
VerifyOrQuit(StringStartsWith(name, aHostName, kStringCaseInsensitiveMatch));
VerifyOrQuit(name[strlen(aHostName)] == '.');
// Only one host on server
VerifyOrQuit(aServer.GetNextHost(host) == nullptr);
}
//---------------------------------------------------------------------------------------------------------------------
void LogServiceInfo(const Dns::Client::ServiceInfo &aInfo)
{
Log(" TTL: %u", aInfo.mTtl);
Log(" Port: %u", aInfo.mPort);
Log(" Weight: %u", aInfo.mWeight);
Log(" HostName: %s", aInfo.mHostNameBuffer);
Log(" HostAddr: %s", AsCoreType(&aInfo.mHostAddress).ToString().AsCString());
Log(" TxtDataLength: %u", aInfo.mTxtDataSize);
Log(" TxtDataTTL: %u", aInfo.mTxtDataTtl);
}
const char *ServiceModeToString(Dns::Client::QueryConfig::ServiceMode aMode)
{
static const char *const kServiceModeStrings[] = {
"unspec", // kServiceModeUnspecified (0)
"srv", // kServiceModeSrv (1)
"txt", // kServiceModeTxt (2)
"srv_txt", // kServiceModeSrvTxt (3)
"srv_txt_sep", // kServiceModeSrvTxtSeparate (4)
"srv_txt_opt", // kServiceModeSrvTxtOptimize (5)
};
static_assert(Dns::Client::QueryConfig::kServiceModeUnspecified == 0, "Unspecified value is incorrect");
static_assert(Dns::Client::QueryConfig::kServiceModeSrv == 1, "Srv value is incorrect");
static_assert(Dns::Client::QueryConfig::kServiceModeTxt == 2, "Txt value is incorrect");
static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxt == 3, "SrvTxt value is incorrect");
static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxtSeparate == 4, "SrvTxtSeparate value is incorrect");
static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize == 5, "SrvTxtOptimize value is incorrect");
return kServiceModeStrings[aMode];
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct BrowseInfo
{
void Reset(void) { mCallbackCount = 0; }
uint16_t mCallbackCount;
Error mError;
char mServiceName[Dns::Name::kMaxNameSize];
uint16_t mNumInstances;
};
static BrowseInfo sBrowseInfo;
void BrowseCallback(otError aError, const otDnsBrowseResponse *aResponse, void *aContext)
{
const Dns::Client::BrowseResponse &response = AsCoreType(aResponse);
Log("BrowseCallback");
Log(" Error: %s", ErrorToString(aError));
VerifyOrQuit(aContext == sInstance);
sBrowseInfo.mCallbackCount++;
sBrowseInfo.mError = aError;
SuccessOrExit(aError);
SuccessOrQuit(response.GetServiceName(sBrowseInfo.mServiceName, sizeof(sBrowseInfo.mServiceName)));
Log(" ServiceName: %s", sBrowseInfo.mServiceName);
for (uint16_t index = 0;; index++)
{
char instLabel[Dns::Name::kMaxLabelSize];
Error error;
error = response.GetServiceInstance(index, instLabel, sizeof(instLabel));
if (error == kErrorNotFound)
{
sBrowseInfo.mNumInstances = index;
break;
}
SuccessOrQuit(error);
Log(" %2u) %s", index + 1, instLabel);
}
exit:
return;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct ResolveServiceInfo
{
void Reset(void)
{
memset(this, 0, sizeof(*this));
mInfo.mHostNameBuffer = mNameBuffer;
mInfo.mHostNameBufferSize = sizeof(mNameBuffer);
mInfo.mTxtData = mTxtBuffer;
mInfo.mTxtDataSize = sizeof(mTxtBuffer);
};
uint16_t mCallbackCount;
Error mError;
Dns::Client::ServiceInfo mInfo;
char mNameBuffer[Dns::Name::kMaxNameSize];
uint8_t mTxtBuffer[256];
};
static ResolveServiceInfo sResolveServiceInfo;
void ServiceCallback(otError aError, const otDnsServiceResponse *aResponse, void *aContext)
{
const Dns::Client::ServiceResponse &response = AsCoreType(aResponse);
char instLabel[Dns::Name::kMaxLabelSize];
char serviceName[Dns::Name::kMaxNameSize];
Log("ServiceCallback");
Log(" Error: %s", ErrorToString(aError));
VerifyOrQuit(aContext == sInstance);
SuccessOrQuit(response.GetServiceName(instLabel, sizeof(instLabel), serviceName, sizeof(serviceName)));
Log(" InstLabel: %s", instLabel);
Log(" ServiceName: %s", serviceName);
sResolveServiceInfo.mCallbackCount++;
sResolveServiceInfo.mError = aError;
SuccessOrExit(aError);
SuccessOrQuit(response.GetServiceInfo(sResolveServiceInfo.mInfo));
LogServiceInfo(sResolveServiceInfo.mInfo);
exit:
return;
}
//----------------------------------------------------------------------------------------------------------------------
void TestDnsClient(void)
{
const Dns::Client::QueryConfig::ServiceMode kServiceModes[] = {
Dns::Client::QueryConfig::kServiceModeSrv,
Dns::Client::QueryConfig::kServiceModeTxt,
Dns::Client::QueryConfig::kServiceModeSrvTxt,
Dns::Client::QueryConfig::kServiceModeSrvTxtSeparate,
Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize,
};
Srp::Server *srpServer;
Srp::Client *srpClient;
Srp::Client::Service service1;
Srp::Client::Service service2;
Dns::Client *dnsClient;
Dns::Client::QueryConfig queryConfig;
Dns::ServiceDiscovery::Server *dnsServer;
uint16_t heapAllocations;
Log("--------------------------------------------------------------------------------------------");
Log("TestDnsClient");
InitTest();
srpServer = &sInstance->Get<Srp::Server>();
srpClient = &sInstance->Get<Srp::Client>();
dnsClient = &sInstance->Get<Dns::Client>();
dnsServer = &sInstance->Get<Dns::ServiceDiscovery::Server>();
heapAllocations = sHeapAllocatedPtrs.GetLength();
PrepareService1(service1);
PrepareService2(service2);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Start SRP server.
SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
srpServer->SetEnabled(true);
VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
AdvanceTime(10000);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Start SRP client.
srpClient->EnableAutoStartMode(nullptr, nullptr);
VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
AdvanceTime(2000);
VerifyOrQuit(srpClient->IsRunning());
SuccessOrQuit(srpClient->SetHostName(kHostName));
SuccessOrQuit(srpClient->EnableAutoHostAddress());
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Register two services on SRP.
SuccessOrQuit(srpClient->AddService(service1));
SuccessOrQuit(srpClient->AddService(service2));
AdvanceTime(2 * 1000);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
ValidateHost(*srpServer, kHostName);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Check DNS Client's default config
VerifyOrQuit(dnsClient->GetDefaultConfig().GetServiceMode() ==
Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `Browse()`
sBrowseInfo.Reset();
Log("Browse(%s)", kService1FullName);
SuccessOrQuit(dnsClient->Browse(kService1FullName, BrowseCallback, sInstance));
AdvanceTime(100);
VerifyOrQuit(sBrowseInfo.mCallbackCount == 1);
SuccessOrQuit(sBrowseInfo.mError);
VerifyOrQuit(sBrowseInfo.mNumInstances == 1);
sBrowseInfo.Reset();
Log("Browse(%s)", kService2FullName);
SuccessOrQuit(dnsClient->Browse(kService2FullName, BrowseCallback, sInstance));
AdvanceTime(100);
VerifyOrQuit(sBrowseInfo.mCallbackCount == 1);
SuccessOrQuit(sBrowseInfo.mError);
VerifyOrQuit(sBrowseInfo.mNumInstances == 1);
sBrowseInfo.Reset();
Log("Browse() for unknwon service");
SuccessOrQuit(dnsClient->Browse("_unknown._udp.default.service.arpa.", BrowseCallback, sInstance));
AdvanceTime(100);
VerifyOrQuit(sBrowseInfo.mCallbackCount == 1);
VerifyOrQuit(sBrowseInfo.mError == kErrorNotFound);
Log("Issue four parallel `Browse()` at the same time");
sBrowseInfo.Reset();
SuccessOrQuit(dnsClient->Browse(kService1FullName, BrowseCallback, sInstance));
SuccessOrQuit(dnsClient->Browse(kService2FullName, BrowseCallback, sInstance));
SuccessOrQuit(dnsClient->Browse("_unknown._udp.default.service.arpa.", BrowseCallback, sInstance));
SuccessOrQuit(dnsClient->Browse("_unknown2._udp.default.service.arpa.", BrowseCallback, sInstance));
AdvanceTime(100);
VerifyOrQuit(sBrowseInfo.mCallbackCount == 4);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `ResolveService()` using all service modes
for (Dns::Client::QueryConfig::ServiceMode mode : kServiceModes)
{
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("ResolveService(%s,%s) with ServiceMode: %s", kInstance1Label, kService1FullName,
ServiceModeToString(mode));
queryConfig.Clear();
queryConfig.mServiceMode = static_cast<otDnsServiceMode>(mode);
sResolveServiceInfo.Reset();
SuccessOrQuit(
dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig));
AdvanceTime(100);
VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1);
SuccessOrQuit(sResolveServiceInfo.mError);
if (mode != Dns::Client::QueryConfig::kServiceModeTxt)
{
VerifyOrQuit(sResolveServiceInfo.mInfo.mTtl != 0);
VerifyOrQuit(sResolveServiceInfo.mInfo.mPort == service1.mPort);
VerifyOrQuit(sResolveServiceInfo.mInfo.mWeight == service1.mWeight);
VerifyOrQuit(strcmp(sResolveServiceInfo.mInfo.mHostNameBuffer, kHostFullName) == 0);
}
if (mode != Dns::Client::QueryConfig::kServiceModeSrv)
{
VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataTtl != 0);
VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataSize != 0);
}
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Set TestMode on server to only accept single question");
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeSingleQuestionOnly);
Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName,
ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize));
queryConfig.Clear();
queryConfig.mServiceMode = static_cast<otDnsServiceMode>(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize);
sResolveServiceInfo.Reset();
SuccessOrQuit(
dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig));
AdvanceTime(200);
VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1);
SuccessOrQuit(sResolveServiceInfo.mError);
// Use `kServiceModeSrvTxt` and check that server does reject two questions.
Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName,
ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxt));
queryConfig.Clear();
queryConfig.mServiceMode = static_cast<otDnsServiceMode>(Dns::Client::QueryConfig::kServiceModeSrvTxt);
sResolveServiceInfo.Reset();
SuccessOrQuit(
dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig));
AdvanceTime(200);
VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1);
VerifyOrQuit(sResolveServiceInfo.mError != kErrorNone);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop DNS-SD server");
dnsServer->Stop();
Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName,
ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrv));
queryConfig.Clear();
queryConfig.mServiceMode = static_cast<otDnsServiceMode>(Dns::Client::QueryConfig::kServiceModeSrv);
sResolveServiceInfo.Reset();
SuccessOrQuit(
dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig));
AdvanceTime(25 * 1000);
VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1);
VerifyOrQuit(sResolveServiceInfo.mError == kErrorResponseTimeout);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Disable SRP server, verify that all heap allocations by SRP server
// and/or by DNS Client are freed.
Log("Disabling SRP server");
srpServer->SetEnabled(false);
AdvanceTime(100);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Finalize OT instance and validate all heap allocations are freed.
Log("Finalizing OT instance");
FinalizeTest();
VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
Log("End of TestDnsClient");
}
#endif // ENABLE_DNS_TEST
int main(void)
{
#if ENABLE_DNS_TEST
TestDnsClient();
printf("All tests passed\n");
#else
printf("DNS_CLIENT or DSNSSD_SERVER feature is not enabled\n");
#endif
return 0;
}