[dns-client] new API resolve host and address (#9054)

This commit adds `otDnsClientResolveServiceAndHostAddress()` function
as a new DNS Client API. This function starts a DNS service instance
resolution for a given service instance, with a potential follow-up
address resolution for the host name discovered for the service
instance (when the server/resolver does not provide AAAA/A records
for the host name in the response to SRV query).

In order to test the behavior of the newly added function, `TestMode`
in `Dns::ServiceDiscovery::Server` is updated to add a new mode where
no RR is added in the Additional Data section of a DNS query response.

This commit adds a related CLI command for the new API and updates
`test_dns_client` to validate the behavior of new API using the new
`TestMode` on `Server`.
This commit is contained in:
Abtin Keshavarzian
2023-05-17 17:03:46 -07:00
committed by GitHub
parent f8f6cf95f6
commit d9abe3071c
10 changed files with 504 additions and 39 deletions
+47 -7
View File
@@ -515,7 +515,7 @@ typedef struct otDnsServiceResponse otDnsServiceResponse;
typedef void (*otDnsServiceCallback)(otError aError, const otDnsServiceResponse *aResponse, void *aContext);
/**
* This function sends a DNS service instance resolution query for a given service instance.
* This function starts a DNS service instance resolution for a given service instance.
*
* This function is available when `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is enabled.
*
@@ -554,6 +554,43 @@ otError otDnsClientResolveService(otInstance *aInstance,
void *aContext,
const otDnsQueryConfig *aConfig);
/**
* This function starts a DNS service instance resolution for a given service instance, with a potential follow-up
* address resolution for the host name discovered for the service instance.
*
* This function is available when `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is enabled.
*
* The @p aConfig can be NULL. In this case the default config (from `otDnsClientGetDefaultConfig()`) will be used as
* the config for this query. In a non-NULL @p aConfig, some of the fields can be left unspecified (value zero). The
* unspecified fields are then replaced by the values from the default config. This function cannot be used with
* `mServiceMode` in DNS config set to `OT_DNS_SERVICE_MODE_TXT` (i.e., querying for TXT record only) and will return
* `OT_ERROR_INVALID_ARGS`.
*
* This function behaves similarly to `otDnsClientResolveService()` sending queries for SRV and TXT records. However,
* if the server/resolver does not provide AAAA/A records for the host name in the response to SRV query (in the
* Additional Data section), it will perform host name resolution (sending an AAAA query) for the discovered host name
* from the SRV record. The callback @p aCallback is invoked when responses for all queries are received (i.e., both
* service and host address resolutions are finished).
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aInstanceLabel The service instance label.
* @param[in] aServiceName The service name (together with @p aInstanceLabel form full instance name).
* @param[in] aCallback A function pointer that shall be called on response reception or time-out.
* @param[in] aContext A pointer to arbitrary context information.
* @param[in] aConfig A pointer to the config to use for this query.
*
* @retval OT_ERROR_NONE Query sent successfully. @p aCallback will be invoked to report the status.
* @retval OT_ERROR_NO_BUFS Insufficient buffer to prepare and send query.
* @retval OT_ERROR_INVALID_ARGS @p aInstanceLabel is NULL, or @p aConfig is invalid.
*
*/
otError otDnsClientResolveServiceAndHostAddress(otInstance *aInstance,
const char *aInstanceLabel,
const char *aServiceName,
otDnsServiceCallback aCallback,
void *aContext,
const otDnsQueryConfig *aConfig);
/**
* This function gets the service instance name associated with a DNS service instance resolution response.
*
@@ -579,13 +616,16 @@ otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse
/**
* This function gets info for a service instance from a DNS service instance resolution response.
*
* This function MUST only be used from `otDnsServiceCallback`.
* This function MUST only be used from a `otDnsServiceCallback` triggered from `otDnsClientResolveService()` or
* `otDnsClientResolveServiceAndHostAddress()`.
*
* A service resolution DNS response may include AAAA records in its Additional Data section for host name associated
* with the service instance that is resolved. This is a SHOULD and not a MUST requirement so servers/resolvers are
* not required to provide this. This function attempts to retrieve AAAA record(s) if included in the response. If it
* is not included `mHostAddress` is set to all zero (unspecified address). If the caller wants to resolve the host
* address it can call `otDnsClientResolveAddress()` with the host name to start an address resolution query.
* When this is is used from a `otDnsClientResolveService()` callback, the DNS response from server/resolver may
* include AAAA records in its Additional Data section for the host name associated with the service instance that is
* resolved. This is a SHOULD and not a MUST requirement so servers/resolvers are not required to provide this. This
* function attempts to parse AAAA record(s) if included in the response. If it is not included `mHostAddress` is set
* to all zeros (unspecified address). To also resolve the host address, user can use the DNS client API function
* `otDnsClientResolveServiceAndHostAddress()` which will perform service resolution followed up by a host name
* address resolution query (when AAAA records are not provided by server/resolver in the SRV query response).
*
* - 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
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (322)
#define OPENTHREAD_API_VERSION (323)
/**
* @addtogroup api-instance
+8
View File
@@ -1269,6 +1269,14 @@ The parameters after `service-name` are optional. Any unspecified (or zero) valu
> Note: The DNS server IP can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. The command will return `InvalidState` when the DNS server IP is an IPv4 address but the preferred NAT64 prefix is unavailable.
### dns servicehost \<service-instance-label\> \<service-name\> \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\]
Send a service instance resolution DNS query for a given service instance with a potential follow-up address resolution for the host name discovered for the service instance (if the server/resolver does not provide AAAA/A records for the host name in the response to SRV query).
Service instance label is provided first, followed by the service name (note that service instance label can contain dot '.' character).
The parameters after `service-name` are optional. Any unspecified (or zero) value for these optional parameters is replaced by the value from the current default config (`dns config`).
### dns compression \[enable|disable\]
Enable/Disable the "DNS name compression" mode.
+36 -3
View File
@@ -3162,7 +3162,7 @@ template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
* `InvalidState` when the DNS server IP is an IPv4 address but the preferred NAT64 prefix
* is unavailable. When testing DNS-SD discovery proxy, the zone is not `local` and
* instead should be `default.service.arpa`.
* 'OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE' is required.
* `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
*/
else if (aArgs[0] == "browse")
{
@@ -3191,9 +3191,9 @@ template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
* @par
* Note: The DNS server IP can be an IPv4 address, which will be synthesized
* to an IPv6 address using the preferred NAT64 prefix from the network data.
* The command will return `InvalidState` when the DNS * server IP is an IPv4
* The command will return `InvalidState` when the DNS server IP is an IPv4
* address but the preferred NAT64 prefix is unavailable.
* 'OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE' is required.
* `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
*/
else if (aArgs[0] == "service")
{
@@ -3203,6 +3203,39 @@ template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
&Interpreter::HandleDnsServiceResponse, this, config));
error = OT_ERROR_PENDING;
}
/**
* @cli dns servicehost
* @cparam dns servicehost @ca{service-instance-label} @ca{service-name} <!--
* --> [@ca{DNS-server-IP}] [@ca{DNS-server-port}] <!--
* --> [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
* --> [@ca{recursion-desired-boolean}]
* @par api_copy
* #otDnsClientResolveServiceAndHostAddress
* @par
* Send a service instance resolution DNS query for a given service instance
* with potential follow-up host name resolution.
* Service instance label is provided first, followed by the service name
* (note that service instance label can contain dot '.' character).
* @par
* The parameters after `service-name` are optional. Any unspecified (or zero)
* value for these optional parameters is replaced by the value from the
* current default config (`dns config`).
* @par
* Note: The DNS server IP can be an IPv4 address, which will be synthesized
* to an IPv6 address using the preferred NAT64 prefix from the network data.
* The command will return `InvalidState` when the DNS server IP is an IPv4
* address but the preferred NAT64 prefix is unavailable.
* `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
*/
else if (aArgs[0] == "servicehost")
{
VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = GetDnsConfig(aArgs + 3, config));
SuccessOrExit(error = otDnsClientResolveServiceAndHostAddress(
GetInstancePtr(), aArgs[1].GetCString(), aArgs[2].GetCString(),
&Interpreter::HandleDnsServiceResponse, this, config));
error = OT_ERROR_PENDING;
}
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
+14
View File
@@ -188,6 +188,20 @@ otError otDnsClientResolveService(otInstance *aInstance,
AsCoreTypePtr(aConfig));
}
otError otDnsClientResolveServiceAndHostAddress(otInstance *aInstance,
const char *aInstanceLabel,
const char *aServiceName,
otDnsServiceCallback aCallback,
void *aContext,
const otDnsQueryConfig *aConfig)
{
AssertPointerIsNotNull(aInstanceLabel);
AssertPointerIsNotNull(aServiceName);
return AsCoreType(aInstance).Get<Dns::Client>().ResolveServiceAndHostAddress(
aInstanceLabel, aServiceName, aCallback, aContext, AsCoreTypePtr(aConfig));
}
otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse,
char *aLabelBuffer,
uint8_t aLabelBufferSize,
+112 -1
View File
@@ -309,6 +309,8 @@ Error Client::Response::ReadServiceInfo(Section aSection, const Name &aName, Ser
// Search in additional section for AAAA record for the host name.
VerifyOrExit(AsCoreType(&aServiceInfo.mHostAddress).IsUnspecified());
error = FindHostAddress(kAdditionalDataSection, hostName, /* aIndex */ 0, AsCoreType(&aServiceInfo.mHostAddress),
aServiceInfo.mHostAddressTtl);
@@ -598,6 +600,27 @@ Error Client::ServiceResponse::GetServiceInfo(ServiceInfo &aServiceInfo) const
info.ReadFrom(*response->mQuery);
switch (info.mQueryType)
{
case kIp6AddressQuery:
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
case kIp4AddressQuery:
#endif
IgnoreError(response->FindHostAddress(kAnswerSection, name, /* aIndex */ 0,
AsCoreType(&aServiceInfo.mHostAddress),
aServiceInfo.mHostAddressTtl));
continue; // to `for()` loop
case kServiceQuerySrvTxt:
case kServiceQuerySrv:
case kServiceQueryTxt:
break;
default:
continue;
}
// Determine from which section we should try to read the SRV and
// TXT records based on the query type.
//
@@ -639,7 +662,25 @@ Error Client::ServiceResponse::GetHostAddress(const char *aHostName,
for (const Response *response = this; response != nullptr; response = response->mNext)
{
error = response->FindHostAddress(kAdditionalDataSection, Name(aHostName), aIndex, aAddress, aTtl);
Section section = kAdditionalDataSection;
QueryInfo info;
info.ReadFrom(*response->mQuery);
switch (info.mQueryType)
{
case kIp6AddressQuery:
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
case kIp4AddressQuery:
#endif
section = kAnswerSection;
break;
default:
break;
}
error = response->FindHostAddress(section, Name(aHostName), aIndex, aAddress, aTtl);
if (error == kErrorNone)
{
@@ -863,6 +904,25 @@ Error Client::ResolveService(const char *aInstanceLabel,
ServiceCallback aCallback,
void *aContext,
const QueryConfig *aConfig)
{
return Resolve(aInstanceLabel, aServiceName, aCallback, aContext, aConfig, false);
}
Error Client::ResolveServiceAndHostAddress(const char *aInstanceLabel,
const char *aServiceName,
ServiceCallback aCallback,
void *aContext,
const QueryConfig *aConfig)
{
return Resolve(aInstanceLabel, aServiceName, aCallback, aContext, aConfig, true);
}
Error Client::Resolve(const char *aInstanceLabel,
const char *aServiceName,
ServiceCallback aCallback,
void *aContext,
const QueryConfig *aConfig,
bool aShouldResolveHostAddr)
{
QueryInfo info;
Error error;
@@ -873,6 +933,7 @@ Error Client::ResolveService(const char *aInstanceLabel,
info.Clear();
info.mConfig.SetFrom(aConfig, mDefaultConfig);
info.mShouldResolveHostAddr = aShouldResolveHostAddr;
switch (info.mConfig.GetServiceMode())
{
@@ -887,6 +948,7 @@ Error Client::ResolveService(const char *aInstanceLabel,
case QueryConfig::kServiceModeTxt:
info.mQueryType = kServiceQueryTxt;
VerifyOrExit(!info.mShouldResolveHostAddr, error = kErrorInvalidArgs);
break;
case QueryConfig::kServiceModeSrvTxt:
@@ -1274,6 +1336,10 @@ void Client::ProcessResponse(const Message &aResponseMessage)
// Received successful response from server.
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
ResolveHostAddressIfNeeded(*query, aResponseMessage);
#endif
if (!CanFinalizeQuery(*query))
{
SaveQueryResponse(*query, aResponseMessage);
@@ -1551,6 +1617,51 @@ exit:
return error;
}
void Client::ResolveHostAddressIfNeeded(Query &aQuery, const Message &aResponseMessage)
{
QueryInfo info;
Response response;
ServiceInfo serviceInfo;
char hostName[Name::kMaxNameSize];
info.ReadFrom(aQuery);
VerifyOrExit(info.mQueryType == kServiceQuerySrvTxt || info.mQueryType == kServiceQuerySrv);
VerifyOrExit(info.mShouldResolveHostAddr);
PopulateResponse(response, aQuery, aResponseMessage);
memset(&serviceInfo, 0, sizeof(serviceInfo));
serviceInfo.mHostNameBuffer = hostName;
serviceInfo.mHostNameBufferSize = sizeof(hostName);
SuccessOrExit(response.ReadServiceInfo(Response::kAnswerSection, Name(aQuery, kNameOffsetInQuery), serviceInfo));
// Check whether AAAA record for host address is provided in the SRV query response
if (AsCoreType(&serviceInfo.mHostAddress).IsUnspecified())
{
Query *newQuery;
info.mQueryType = kIp6AddressQuery;
info.mMessageId = 0;
info.mTransmissionCount = 0;
info.mMainQuery = &FindMainQuery(aQuery);
SuccessOrExit(AllocateQuery(info, nullptr, hostName, newQuery));
IgnoreError(SendQuery(*newQuery, info, /* aUpdateTimer */ true));
// Update `aQuery` to be linked with new query (inserting
// the `newQuery` into the linked-list after `aQuery`).
info.ReadFrom(aQuery);
info.mNextQuery = newQuery;
UpdateQuery(aQuery, info);
}
exit:
return;
}
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
+35 -1
View File
@@ -718,7 +718,7 @@ public:
const QueryConfig *aConfig = nullptr);
/**
* This method sends a DNS service instance resolution query for a given service instance.
* This method starts a DNS service instance resolution for a given service instance.
*
* The @p aConfig can be `nullptr`. In this case the default config (from `GetDefaultConfig()`) will be used as
* the config for this query. In a non-`nullptr` @p aConfig, some of the fields can be left unspecified (value
@@ -741,6 +741,32 @@ public:
void *aContext,
const QueryConfig *aConfig = nullptr);
/**
* This method starts a DNS service instance resolution for a given service instance, with a potential follow-up
* host name resolution (if the server/resolver does not provide AAAA/A records for the host name in the response
* to SRV query).
*
* The @p aConfig can be `nullptr`. In this case the default config (from `GetDefaultConfig()`) will be used as
* the config for this query. In a non-`nullptr` @p aConfig, some of the fields can be left unspecified (value
* zero). The unspecified fields are then replaced by the values from the default config.
*
* @param[in] aInstanceLabel The service instance label.
* @param[in] aServiceName The service name (together with @p aInstanceLabel form full instance name).
* @param[in] aCallback A function pointer that shall be called on response reception or time-out.
* @param[in] aContext A pointer to arbitrary context information.
* @param[in] aConfig The config to use for this query.
*
* @retval kErrorNone Query sent successfully. @p aCallback will be invoked to report the status.
* @retval kErrorNoBufs Insufficient buffer to prepare and send query.
* @retval kErrorInvalidArgs @p aInstanceLabel is `nullptr` or the @p aConfig is invalid.
*
*/
Error ResolveServiceAndHostAddress(const char *aInstanceLabel,
const char *aServiceName,
ServiceCallback aCallback,
void *aContext,
const QueryConfig *aConfig = nullptr);
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
private:
@@ -791,6 +817,7 @@ private:
TimeMilli mRetransmissionTime;
QueryConfig mConfig;
uint8_t mTransmissionCount;
bool mShouldResolveHostAddr;
Query *mMainQuery;
Query *mNextQuery;
Message *mSavedResponse;
@@ -823,7 +850,14 @@ private:
Error ReplaceWithIp4Query(Query &aQuery);
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
Error Resolve(const char *aInstanceLabel,
const char *aServiceName,
ServiceCallback aCallback,
void *aContext,
const QueryConfig *aConfig,
bool aShouldResolveHostAddr);
Error ReplaceWithSeparateSrvTxtQueries(Query &aQuery);
void ResolveHostAddressIfNeeded(Query &aQuery, const Message &aResponseMessage);
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE
+8 -5
View File
@@ -204,13 +204,9 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage
VerifyOrExit(!aRequestHeader.IsTruncationFlagSet(), response = Header::kResponseFormatError);
VerifyOrExit(aRequestHeader.GetQuestionCount() > 0, response = Header::kResponseFormatError);
switch (mTestMode)
if (mTestMode & kTestModeSingleQuestionOnly)
{
case kTestModeDisabled:
break;
case kTestModeSingleQuestionOnly:
VerifyOrExit(aRequestHeader.GetQuestionCount() == 1, response = Header::kResponseFormatError);
break;
}
response = AddQuestions(aRequestHeader, aRequestMessage, responseHeader, *responseMessage, compressInfo);
@@ -729,6 +725,8 @@ Header::Response Server::ResolveBySrp(Header &aResponseHeader,
// Answer the questions with additional RRs if required
if (aResponseHeader.GetAnswerCount() > 0)
{
VerifyOrExit(!(mTestMode & kTestModeEmptyAdditionalSection));
readOffset = sizeof(Header);
for (uint16_t i = 0; i < aResponseHeader.GetQuestionCount(); i++)
{
@@ -1046,6 +1044,11 @@ void Server::AnswerQuery(QueryTransaction &aQuery,
for (uint8_t additional = 0; additional <= 1; additional++)
{
if (additional == 1)
{
VerifyOrExit(!(mTestMode & kTestModeEmptyAdditionalSection));
}
if (HasQuestion(aQuery.GetResponseHeader(), aQuery.GetResponseMessage(), aInstanceInfo.mFullName,
ResourceRecord::kTypeSrv) == !additional)
{
+12 -10
View File
@@ -263,25 +263,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).
* This enumeration represents different test mode flags for use in `SetTestMode()`.
*
*/
enum TestMode : uint8_t
enum TestModeFlags : uint8_t
{
kTestModeDisabled, ///< Test mode is disabled.
kTestModeSingleQuestionOnly, ///< Allow single question in query message, send `FormatError` for two or more.
kTestModeSingleQuestionOnly = 1 << 0, ///< Allow single question in query, send `FormatError` otherwise.
kTestModeEmptyAdditionalSection = 1 << 1, ///< Do not include any RR in additional section.
};
static constexpr uint8_t kTestModeDisabled = 0; ///< Test mode is disabled (no flags).
/**
* This method sets the test mode for `Server`.
*
* @param[in] aTestMode The new test mode.
* The test mode flags are 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).
*
* @param[in] aTestMode The new test mode (combination of `TestModeFlags`).
*
*/
void SetTestMode(TestMode aTestMode) { mTestMode = aTestMode; }
void SetTestMode(uint8_t aTestMode) { mTestMode = aTestMode; }
private:
class NameCompressInfo : public Clearable<NameCompressInfo>
@@ -553,7 +555,7 @@ private:
ServerTimer mTimer;
Counters mCounters;
TestMode mTestMode;
uint8_t mTestMode;
};
} // namespace ServiceDiscovery
+231 -11
View File
@@ -392,6 +392,9 @@ exit:
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static constexpr uint8_t kMaxHostAddresses = 10;
static constexpr uint16_t kMaxTxtBuffer = 256;
struct ResolveServiceInfo
{
void Reset(void)
@@ -407,7 +410,9 @@ struct ResolveServiceInfo
Error mError;
Dns::Client::ServiceInfo mInfo;
char mNameBuffer[Dns::Name::kMaxNameSize];
uint8_t mTxtBuffer[256];
uint8_t mTxtBuffer[kMaxTxtBuffer];
Ip6::Address mHostAddresses[kMaxHostAddresses];
uint8_t mNumHostAddresses;
};
static ResolveServiceInfo sResolveServiceInfo;
@@ -432,7 +437,31 @@ void ServiceCallback(otError aError, const otDnsServiceResponse *aResponse, void
SuccessOrExit(aError);
SuccessOrQuit(response.GetServiceInfo(sResolveServiceInfo.mInfo));
for (uint8_t index = 0; index < kMaxHostAddresses; index++)
{
Error error;
uint32_t ttl;
error = response.GetHostAddress(sResolveServiceInfo.mInfo.mHostNameBuffer, index,
sResolveServiceInfo.mHostAddresses[index], ttl);
if (error == kErrorNotFound)
{
sResolveServiceInfo.mNumHostAddresses = index;
break;
}
SuccessOrQuit(error);
}
LogServiceInfo(sResolveServiceInfo.mInfo);
Log(" NumHostAddresses: %u", sResolveServiceInfo.mNumHostAddresses);
for (uint8_t index = 0; index < sResolveServiceInfo.mNumHostAddresses; index++)
{
Log(" %s", sResolveServiceInfo.mHostAddresses[index].ToString().AsCString());
}
exit:
return;
@@ -442,6 +471,10 @@ exit:
void TestDnsClient(void)
{
static constexpr uint8_t kNumAddresses = 2;
static const char *const kAddresses[kNumAddresses] = {"2001::beef:cafe", "fd00:1234:5678:9abc::1"};
const Dns::Client::QueryConfig::ServiceMode kServiceModes[] = {
Dns::Client::QueryConfig::kServiceModeSrv,
Dns::Client::QueryConfig::kServiceModeTxt,
@@ -450,20 +483,36 @@ void TestDnsClient(void)
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;
Array<Ip6::Address, kNumAddresses> addresses;
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();
for (const char *addrString : kAddresses)
{
otNetifAddress netifAddr;
memset(&netifAddr, 0, sizeof(netifAddr));
SuccessOrQuit(AsCoreType(&netifAddr.mAddress).FromString(addrString));
netifAddr.mPrefixLength = 64;
netifAddr.mAddressOrigin = OT_ADDRESS_ORIGIN_MANUAL;
netifAddr.mPreferred = true;
netifAddr.mValid = true;
SuccessOrQuit(otIp6AddUnicastAddress(sInstance, &netifAddr));
SuccessOrQuit(addresses.PushBack(AsCoreType(&netifAddr.mAddress)));
}
srpServer = &sInstance->Get<Srp::Server>();
srpClient = &sInstance->Get<Srp::Client>();
dnsClient = &sInstance->Get<Dns::Client>();
@@ -580,6 +629,14 @@ void TestDnsClient(void)
VerifyOrQuit(sResolveServiceInfo.mInfo.mPort == service1.mPort);
VerifyOrQuit(sResolveServiceInfo.mInfo.mWeight == service1.mWeight);
VerifyOrQuit(strcmp(sResolveServiceInfo.mInfo.mHostNameBuffer, kHostFullName) == 0);
VerifyOrQuit(sResolveServiceInfo.mNumHostAddresses == kNumAddresses);
VerifyOrQuit(AsCoreType(&sResolveServiceInfo.mInfo.mHostAddress) == sResolveServiceInfo.mHostAddresses[0]);
for (uint8_t index = 0; index < kNumAddresses; index++)
{
VerifyOrQuit(addresses.Contains(sResolveServiceInfo.mHostAddresses[index]));
}
}
if (mode != Dns::Client::QueryConfig::kServiceModeSrv)
@@ -624,16 +681,179 @@ void TestDnsClient(void)
VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1);
VerifyOrQuit(sResolveServiceInfo.mError != kErrorNone);
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeDisabled);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `ResolveService()` using all service modes
// when sever does not provide any RR in the addition data section.
for (Dns::Client::QueryConfig::ServiceMode mode : kServiceModes)
{
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Set TestMode on server to not include any RR in additional section");
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeEmptyAdditionalSection);
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);
}
// Since server is using `kTestModeEmptyAdditionalSection`, there
// should be no AAAA records for host address.
VerifyOrQuit(AsCoreType(&sResolveServiceInfo.mInfo.mHostAddress).IsUnspecified());
VerifyOrQuit(sResolveServiceInfo.mNumHostAddresses == 0);
}
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeDisabled);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate DNS Client `ResolveServiceAndHostAddress()` using all service modes
// with different TestMode configs on server:
// - Normal behavior when server provides AAAA records for host in
// additional section.
// - Server provides no records in additional section. We validate that
// client will send separate query to resolve host address.
for (Dns::Client::QueryConfig::ServiceMode mode : kServiceModes)
{
for (uint8_t testIter = 0; testIter <= 1; testIter++)
{
Error error;
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
if (testIter == 1)
{
Log("Set TestMode on server to not include any RR in additional section");
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeEmptyAdditionalSection);
}
else
{
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeDisabled);
}
Log("ResolveServiceAndHostAddress(%s,%s) with ServiceMode: %s", kInstance1Label, kService1FullName,
ServiceModeToString(mode));
queryConfig.Clear();
queryConfig.mServiceMode = static_cast<otDnsServiceMode>(mode);
sResolveServiceInfo.Reset();
error = dnsClient->ResolveServiceAndHostAddress(kInstance1Label, kService1FullName, ServiceCallback,
sInstance, &queryConfig);
if (mode == Dns::Client::QueryConfig::kServiceModeTxt)
{
Log("ResolveServiceAndHostAddress() with ServiceMode: %s failed correctly", ServiceModeToString(mode));
VerifyOrQuit(error == kErrorInvalidArgs);
continue;
}
SuccessOrQuit(error);
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);
VerifyOrQuit(sResolveServiceInfo.mNumHostAddresses == kNumAddresses);
VerifyOrQuit(AsCoreType(&sResolveServiceInfo.mInfo.mHostAddress) ==
sResolveServiceInfo.mHostAddresses[0]);
for (uint8_t index = 0; index < kNumAddresses; index++)
{
VerifyOrQuit(addresses.Contains(sResolveServiceInfo.mHostAddresses[index]));
}
}
if (mode != Dns::Client::QueryConfig::kServiceModeSrv)
{
VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataTtl != 0);
VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataSize != 0);
}
}
}
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeDisabled);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Set TestMode on server to not include any RR in additional section AND to only accept single question");
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeEmptyAdditionalSection +
Dns::ServiceDiscovery::Server::kTestModeSingleQuestionOnly);
Log("ResolveServiceAndHostAddress(%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->ResolveServiceAndHostAddress(kInstance1Label, kService1FullName, ServiceCallback,
sInstance, &queryConfig));
AdvanceTime(100);
VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1);
SuccessOrQuit(sResolveServiceInfo.mError);
VerifyOrQuit(sResolveServiceInfo.mInfo.mTtl != 0);
VerifyOrQuit(sResolveServiceInfo.mInfo.mPort == service1.mPort);
VerifyOrQuit(sResolveServiceInfo.mInfo.mWeight == service1.mWeight);
VerifyOrQuit(strcmp(sResolveServiceInfo.mInfo.mHostNameBuffer, kHostFullName) == 0);
VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataTtl != 0);
VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataSize != 0);
VerifyOrQuit(sResolveServiceInfo.mNumHostAddresses == kNumAddresses);
VerifyOrQuit(AsCoreType(&sResolveServiceInfo.mInfo.mHostAddress) == sResolveServiceInfo.mHostAddresses[0]);
for (uint8_t index = 0; index < kNumAddresses; index++)
{
VerifyOrQuit(addresses.Contains(sResolveServiceInfo.mHostAddresses[index]));
}
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeDisabled);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop DNS-SD server");
dnsServer->Stop();
Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName,
ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrv));
ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxtSeparate));
queryConfig.Clear();
queryConfig.mServiceMode = static_cast<otDnsServiceMode>(Dns::Client::QueryConfig::kServiceModeSrv);
queryConfig.mServiceMode = static_cast<otDnsServiceMode>(Dns::Client::QueryConfig::kServiceModeSrvTxtSeparate);
sResolveServiceInfo.Reset();
SuccessOrQuit(