mirror of
https://github.com/espressif/openthread.git
synced 2026-09-05 00:30:07 +00:00
[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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user