mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
c923900de0
This commit provides the platform-level implementation for the instance-aware logging API `otPlatLogOutput()`. This API is used when `OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE` is enabled, allowing the platform to receive the `otInstance` pointer with each log line. The new API is implemented across: - The simulation platform logging. - The POSIX platform using `syslog()`. - The NCP base to route logs to the NCP host. - The CLI logging module. - Unit tests and mock platforms. The `OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE` configuration is also enabled for Toranj simulations to support multi-instance log testing.
2042 lines
78 KiB
C++
2042 lines
78 KiB
C++
/*
|
|
* 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/dataset_ftd.h>
|
|
#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/clearable.hpp"
|
|
#include "common/string.hpp"
|
|
#include "common/time.hpp"
|
|
#include "instance/instance.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" {
|
|
|
|
otRadioCaps otPlatRadioGetCaps(otInstance *) { return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF; }
|
|
|
|
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
|
|
#if OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE
|
|
void otPlatLogOutput(otInstance *, otLogLevel, const char *aLogLine) { printf(" %s\n", aLogLine); }
|
|
#else
|
|
void otPlatLog(otLogLevel, otLogRegion, const char *aFormat, ...)
|
|
{
|
|
va_list args;
|
|
|
|
printf(" ");
|
|
va_start(args, aFormat);
|
|
vprintf(aFormat, args);
|
|
va_end(args);
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
#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;
|
|
sAlarmOn = false;
|
|
sInstance = static_cast<Instance *>(testInitInstance());
|
|
|
|
memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame));
|
|
sRadioTxFrame.mPsdu = sRadioTxFramePsdu;
|
|
sRadioTxOngoing = false;
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Initialize Border Router and start Thread operation.
|
|
|
|
otOperationalDataset dataset;
|
|
otOperationalDatasetTlvs datasetTlvs;
|
|
|
|
SuccessOrQuit(otDatasetCreateNewNetwork(sInstance, &dataset));
|
|
otDatasetConvertToTlvs(&dataset, &datasetTlvs);
|
|
SuccessOrQuit(otDatasetSetActiveTlvs(sInstance, &datasetTlvs));
|
|
|
|
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)
|
|
{
|
|
AdvanceTime(30 * 1000);
|
|
|
|
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 kNonExistingName[] = "noname.nodomain.";
|
|
|
|
static const char kService1Name[] = "_srv._udp";
|
|
static const char kService1FullName[] = "_srv._udp.default.service.arpa.";
|
|
static const char kInstance1Label[] = "srv-instance";
|
|
static const char kInstance1FullName[] = "srv-instance._srv._udp.default.service.arpa.";
|
|
|
|
static const char kService2Name[] = "_game._udp";
|
|
static const char kService2FullName[] = "_game._udp.default.service.arpa.";
|
|
static const char kService2SubTypeFullName[] = "_best._sub._game._udp.default.service.arpa.";
|
|
static const char kInstance2Label[] = "last-ninja";
|
|
static const char kInstance2FullName[] = "last-ninja._game._udp.default.service.arpa.";
|
|
|
|
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[] = "_best";
|
|
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];
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
static constexpr uint16_t kMaxAddresses = 10;
|
|
|
|
struct AddressInfo
|
|
{
|
|
void Reset(void) { ClearAllBytes(*this); }
|
|
|
|
uint16_t mCallbackCount;
|
|
Error mError;
|
|
Dns::Name::Buffer mHostName;
|
|
Ip6::Address mHostAddresses[kMaxAddresses];
|
|
uint8_t mNumHostAddresses;
|
|
};
|
|
|
|
static AddressInfo sAddressInfo;
|
|
|
|
void AddressCallback(otError aError, const otDnsAddressResponse *aResponse, void *aContext)
|
|
{
|
|
const Dns::Client::AddressResponse &response = AsCoreType(aResponse);
|
|
|
|
Log("AddressCallback");
|
|
Log(" Error: %s", ErrorToString(aError));
|
|
|
|
VerifyOrQuit(aContext == sInstance);
|
|
|
|
sAddressInfo.mCallbackCount++;
|
|
sAddressInfo.mError = aError;
|
|
|
|
SuccessOrExit(aError);
|
|
|
|
SuccessOrQuit(response.GetHostName(sAddressInfo.mHostName, sizeof(sAddressInfo.mHostName)));
|
|
Log(" HostName: %s", sAddressInfo.mHostName);
|
|
|
|
for (uint16_t index = 0;; index++)
|
|
{
|
|
Error error;
|
|
uint32_t ttl;
|
|
|
|
VerifyOrQuit(index < kMaxAddresses);
|
|
|
|
error = response.GetAddress(index, sAddressInfo.mHostAddresses[index], ttl);
|
|
|
|
if (error == kErrorNotFound)
|
|
{
|
|
sAddressInfo.mNumHostAddresses = index;
|
|
break;
|
|
}
|
|
|
|
SuccessOrQuit(error);
|
|
|
|
Log(" %2u) %s ttl:%lu", index + 1, sAddressInfo.mHostAddresses[index].ToString().AsCString(), ToUlong(ttl));
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
struct BrowseInfo
|
|
{
|
|
void Reset(void) { mCallbackCount = 0; }
|
|
|
|
uint16_t mCallbackCount;
|
|
Error mError;
|
|
Dns::Name::Buffer mServiceName;
|
|
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++)
|
|
{
|
|
Dns::Name::LabelBuffer instLabel;
|
|
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;
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
static constexpr uint8_t kMaxHostAddresses = 10;
|
|
static constexpr uint16_t kMaxTxtBuffer = 256;
|
|
|
|
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;
|
|
Dns::Name::Buffer mNameBuffer;
|
|
uint8_t mTxtBuffer[kMaxTxtBuffer];
|
|
Ip6::Address mHostAddresses[kMaxHostAddresses];
|
|
uint8_t mNumHostAddresses;
|
|
};
|
|
|
|
static ResolveServiceInfo sResolveServiceInfo;
|
|
|
|
void ServiceCallback(otError aError, const otDnsServiceResponse *aResponse, void *aContext)
|
|
{
|
|
const Dns::Client::ServiceResponse &response = AsCoreType(aResponse);
|
|
Dns::Name::LabelBuffer instLabel;
|
|
Dns::Name::Buffer serviceName;
|
|
|
|
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));
|
|
|
|
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;
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
static constexpr uint16_t kMaxRecords = 16;
|
|
|
|
struct QueryRecordInfo
|
|
{
|
|
struct Record : public Dns::Client::RecordInfo
|
|
{
|
|
static constexpr uint16_t kMaxRecordDataSize = 500;
|
|
|
|
void Init(void)
|
|
{
|
|
ClearAllBytes(*this);
|
|
mNameBuffer = mName;
|
|
mNameBufferSize = sizeof(mName);
|
|
mDataBuffer = mData;
|
|
mDataBufferSize = sizeof(mData);
|
|
}
|
|
|
|
uint8_t mData[kMaxRecordDataSize];
|
|
char mName[Dns::Name::kMaxNameSize];
|
|
};
|
|
|
|
void Reset(void) { memset(this, 0, sizeof(*this)); };
|
|
|
|
uint16_t mCallbackCount;
|
|
Error mError;
|
|
char mQueryName[Dns::Name::kMaxNameSize];
|
|
Record mRecords[kMaxRecords];
|
|
uint16_t mNumRecords;
|
|
};
|
|
|
|
static QueryRecordInfo sQueryRecordInfo;
|
|
|
|
void RecordCallback(otError aError, const otDnsRecordResponse *aResponse, void *aContext)
|
|
{
|
|
static constexpr uint16_t kMaxStringSize = 400;
|
|
|
|
const Dns::Client::RecordResponse &response = AsCoreType(aResponse);
|
|
|
|
Log("RecordCallback");
|
|
Log(" Error: %s", ErrorToString(aError));
|
|
|
|
VerifyOrQuit(aContext == sInstance);
|
|
|
|
sQueryRecordInfo.mCallbackCount++;
|
|
sQueryRecordInfo.mError = aError;
|
|
sQueryRecordInfo.mNumRecords = 0;
|
|
|
|
SuccessOrExit(aError);
|
|
|
|
SuccessOrQuit(response.GetQueryName(sQueryRecordInfo.mQueryName, sizeof(sQueryRecordInfo.mQueryName)));
|
|
Log(" QueryName: %s", sQueryRecordInfo.mQueryName);
|
|
|
|
for (uint8_t index = 0; index < kMaxRecords; index++)
|
|
{
|
|
Error error;
|
|
uint32_t ttl;
|
|
|
|
sQueryRecordInfo.mRecords[index].Init();
|
|
|
|
error = response.GetRecordInfo(index, sQueryRecordInfo.mRecords[index]);
|
|
|
|
if (error == kErrorNotFound)
|
|
{
|
|
sQueryRecordInfo.mNumRecords = index;
|
|
break;
|
|
}
|
|
|
|
SuccessOrQuit(error);
|
|
}
|
|
|
|
Log(" NumRecords: %u", sQueryRecordInfo.mNumRecords);
|
|
|
|
for (uint16_t index = 0; index < sQueryRecordInfo.mNumRecords; index++)
|
|
{
|
|
const QueryRecordInfo::Record &record = sQueryRecordInfo.mRecords[index];
|
|
String<kMaxStringSize> string;
|
|
uint16_t rrType;
|
|
|
|
string.AppendHexBytes(record.mDataBuffer, record.mDataBufferSize);
|
|
rrType = record.mRecordType;
|
|
|
|
Log(" Record %u", index);
|
|
Log(" Name: %s", record.mNameBuffer);
|
|
Log(" Type: %u (%s)", rrType, Dns::ResourceRecord::TypeToString(rrType).AsCString());
|
|
Log(" Data: %s", string.AsCString());
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void ValidateSrvRecordData(const QueryRecordInfo::Record &aRecord, const char *aFullHostName)
|
|
{
|
|
// Validate that the read SRV record data contains
|
|
// the uncompressed host name.
|
|
|
|
Message *data = sInstance->Get<MessagePool>().Allocate(Message::kTypeOther);
|
|
uint16_t offset = sizeof(Dns::SrvRecord) - sizeof(Dns::ResourceRecord);
|
|
|
|
VerifyOrQuit(data != nullptr);
|
|
SuccessOrQuit(data->AppendBytes(aRecord.mDataBuffer, aRecord.mRecordLength));
|
|
|
|
SuccessOrQuit(Dns::Name::CompareName(*data, offset, aFullHostName));
|
|
VerifyOrQuit(offset == data->GetLength());
|
|
|
|
data->Free();
|
|
}
|
|
|
|
void ValidatePtrRecordData(const QueryRecordInfo::Record &aRecord, const char *aFullInstanceName)
|
|
{
|
|
// Validate that the read PTR record data contains
|
|
// the uncompressed service instance name.
|
|
|
|
Message *data = sInstance->Get<MessagePool>().Allocate(Message::kTypeOther);
|
|
uint16_t offset = 0;
|
|
|
|
VerifyOrQuit(data != nullptr);
|
|
SuccessOrQuit(data->AppendBytes(aRecord.mDataBuffer, aRecord.mRecordLength));
|
|
|
|
SuccessOrQuit(Dns::Name::CompareName(*data, offset, aFullInstanceName));
|
|
VerifyOrQuit(offset == data->GetLength());
|
|
|
|
data->Free();
|
|
}
|
|
|
|
void ValidateSoaRecordData(const QueryRecordInfo::Record &aRecord, const char *aServerName)
|
|
{
|
|
static constexpr uint32_t kSoaSerial = 0;
|
|
static constexpr uint32_t kSoaRefresh = 7200;
|
|
static constexpr uint32_t kSoaRetry = 3600;
|
|
static constexpr uint32_t kSoaExpire = 86400;
|
|
static constexpr uint32_t kSoaMinimum = 10;
|
|
|
|
uint16_t offset;
|
|
Message *message;
|
|
Dns::Name::Buffer name;
|
|
|
|
VerifyOrQuit(StringMatch(aRecord.mNameBuffer, "default.service.arpa."));
|
|
VerifyOrQuit(aRecord.mRecordType == Dns::ResourceRecord::kTypeSoa);
|
|
VerifyOrQuit(aRecord.mTtl == 7200);
|
|
|
|
message = sInstance->Get<MessagePool>().Allocate(Message::kTypeOther);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
VerifyOrQuit(aRecord.mRecordLength == aRecord.mDataBufferSize);
|
|
SuccessOrQuit(message->AppendBytes(aRecord.mDataBuffer, aRecord.mDataBufferSize));
|
|
|
|
// Validate the SOA record data.
|
|
|
|
offset = 0;
|
|
|
|
// MNAME field:
|
|
SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
|
|
VerifyOrQuit(StringMatch(name, aServerName, kStringCaseInsensitiveMatch));
|
|
|
|
// RNAME: must be "postmaster.default.service.arpa."
|
|
SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
|
|
SuccessOrQuit(Dns::Name::StripName(name, "default.service.arpa."));
|
|
VerifyOrQuit(StringMatch(name, "postmaster"));
|
|
|
|
// SERIAL
|
|
VerifyOrQuit(message->Compare<uint32_t>(offset, BigEndian::HostSwap32(kSoaSerial)));
|
|
offset += sizeof(uint32_t);
|
|
|
|
// REFRESH
|
|
VerifyOrQuit(message->Compare<uint32_t>(offset, BigEndian::HostSwap32(kSoaRefresh)));
|
|
offset += sizeof(uint32_t);
|
|
|
|
// RETRY
|
|
VerifyOrQuit(message->Compare<uint32_t>(offset, BigEndian::HostSwap32(kSoaRetry)));
|
|
offset += sizeof(uint32_t);
|
|
|
|
// EXPIRE
|
|
VerifyOrQuit(message->Compare<uint32_t>(offset, BigEndian::HostSwap32(kSoaExpire)));
|
|
offset += sizeof(uint32_t);
|
|
|
|
// MINIMUM
|
|
VerifyOrQuit(message->Compare<uint32_t>(offset, BigEndian::HostSwap32(kSoaMinimum)));
|
|
offset += sizeof(uint32_t);
|
|
|
|
VerifyOrQuit(offset == message->GetLength());
|
|
|
|
message->Free();
|
|
}
|
|
|
|
void ValidateNsRecordData(const QueryRecordInfo::Record &aRecord, const char *aServerName)
|
|
{
|
|
uint16_t offset;
|
|
Message *message;
|
|
Dns::Name::Buffer name;
|
|
|
|
VerifyOrQuit(StringMatch(aRecord.mNameBuffer, "default.service.arpa."));
|
|
VerifyOrQuit(aRecord.mRecordType == Dns::ResourceRecord::kTypeNs);
|
|
VerifyOrQuit(aRecord.mTtl == 7200);
|
|
|
|
message = sInstance->Get<MessagePool>().Allocate(Message::kTypeOther);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
VerifyOrQuit(aRecord.mRecordLength == aRecord.mDataBufferSize);
|
|
SuccessOrQuit(message->AppendBytes(aRecord.mDataBuffer, aRecord.mDataBufferSize));
|
|
|
|
// Validate the NS data
|
|
|
|
offset = 0;
|
|
|
|
SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
|
|
VerifyOrQuit(StringMatch(name, aServerName, kStringCaseInsensitiveMatch));
|
|
|
|
VerifyOrQuit(offset == message->GetLength());
|
|
|
|
message->Free();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
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,
|
|
Dns::Client::QueryConfig::kServiceModeSrvTxt,
|
|
Dns::Client::QueryConfig::kServiceModeSrvTxtSeparate,
|
|
Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize,
|
|
};
|
|
|
|
Array<Ip6::Address, kNumAddresses> addresses;
|
|
NetworkData::ExternalRouteConfig routeConfig;
|
|
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;
|
|
Dns::ServiceDiscovery::Server::Counters oldServerCounters;
|
|
Dns::ServiceDiscovery::Server::Counters newServerCounters;
|
|
uint16_t heapAllocations;
|
|
|
|
Log("--------------------------------------------------------------------------------------------");
|
|
Log("TestDnsClient");
|
|
|
|
InitTest();
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("Add a route prefix (with NAT64 flag) to network data");
|
|
|
|
routeConfig.Clear();
|
|
SuccessOrQuit(AsCoreType(&routeConfig.mPrefix.mPrefix).FromString("64:ff9b::"));
|
|
routeConfig.mPrefix.mLength = 96;
|
|
routeConfig.mPreference = NetworkData::kRoutePreferenceMedium;
|
|
routeConfig.mNat64 = true;
|
|
routeConfig.mStable = true;
|
|
|
|
SuccessOrQuit(otBorderRouterAddRoute(sInstance, &routeConfig));
|
|
SuccessOrQuit(otBorderRouterRegister(sInstance));
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("Add addresses on Thread netif");
|
|
|
|
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>();
|
|
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 `ResolveAddress()`
|
|
|
|
sAddressInfo.Reset();
|
|
Log("ResolveAddress(%s)", kHostFullName);
|
|
SuccessOrQuit(dnsClient->ResolveAddress(kHostFullName, AddressCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sAddressInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sAddressInfo.mError);
|
|
VerifyOrQuit(sAddressInfo.mNumHostAddresses == GetArrayLength(kAddresses));
|
|
|
|
for (uint8_t index = 0; index < sAddressInfo.mNumHostAddresses; index++)
|
|
{
|
|
VerifyOrQuit(addresses.Contains(sAddressInfo.mHostAddresses[index]));
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Validate DNS Client `ResolveAddress()` for an invalid (non-existing) name
|
|
|
|
sAddressInfo.Reset();
|
|
Log("ResolveAddress(%s)", kNonExistingName);
|
|
SuccessOrQuit(dnsClient->ResolveAddress(kNonExistingName, AddressCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sAddressInfo.mCallbackCount == 1);
|
|
VerifyOrQuit(sAddressInfo.mError == kErrorNotFound);
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Validate DNS Client `ResolveIp4Address()`
|
|
|
|
sAddressInfo.Reset();
|
|
Log("ResolveIp4Address(%s)", kHostFullName);
|
|
SuccessOrQuit(dnsClient->ResolveIp4Address(kHostFullName, AddressCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sAddressInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sAddressInfo.mError);
|
|
VerifyOrQuit(sAddressInfo.mNumHostAddresses == 0);
|
|
|
|
sAddressInfo.Reset();
|
|
Log("ResolveIp4Address(%s)", "badname");
|
|
SuccessOrQuit(dnsClient->ResolveIp4Address("badname", AddressCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sAddressInfo.mCallbackCount == 1);
|
|
VerifyOrQuit(sAddressInfo.mError != kErrorNone);
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Validate DNS Client `QueryRecord()` for host name and KEY record
|
|
|
|
sQueryRecordInfo.Reset();
|
|
Log("QueryRecord(%s) for KEY RR", kHostFullName);
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeKey, kHostName, "default.service.arpa.",
|
|
RecordCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
|
|
|
|
VerifyOrQuit(!strcmp(sQueryRecordInfo.mRecords[0].mNameBuffer, kHostFullName));
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordType == Dns::ResourceRecord::kTypeKey);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordLength == sizeof(Dns::Ecdsa256KeyRecord));
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mTtl > 0);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mDataBufferSize == sizeof(Dns::Ecdsa256KeyRecord));
|
|
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
|
|
|
|
sQueryRecordInfo.Reset();
|
|
Log("QueryRecord(%s) for misc RR", kHostFullName);
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeCname, kHostName, "default.service.arpa.",
|
|
RecordCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 0);
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Validate DNS Client `QueryRecord()` for host name and ANY record
|
|
|
|
sQueryRecordInfo.Reset();
|
|
Log("QueryRecord(%s) for ANY RR", kHostFullName);
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, kHostName, "default.service.arpa.",
|
|
RecordCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 3);
|
|
|
|
for (uint8_t index = 0; index < 3; index++)
|
|
{
|
|
const QueryRecordInfo::Record &record = sQueryRecordInfo.mRecords[index];
|
|
|
|
VerifyOrQuit(StringMatch(record.mNameBuffer, kHostFullName));
|
|
VerifyOrQuit(MapEnum(record.mSection) == Dns::Client::RecordInfo::kSectionAnswer);
|
|
VerifyOrQuit(record.mTtl > 0);
|
|
|
|
if (record.mRecordType == Dns::ResourceRecord::kTypeKey)
|
|
{
|
|
VerifyOrQuit(record.mRecordLength == sizeof(Dns::Ecdsa256KeyRecord));
|
|
VerifyOrQuit(record.mDataBufferSize == sizeof(Dns::Ecdsa256KeyRecord));
|
|
}
|
|
else if (record.mRecordType == Dns::ResourceRecord::kTypeAaaa)
|
|
{
|
|
VerifyOrQuit(record.mRecordLength == sizeof(Ip6::Address));
|
|
VerifyOrQuit(addresses.Contains(*reinterpret_cast<const Ip6::Address *>(record.mDataBuffer)));
|
|
}
|
|
else
|
|
{
|
|
// Unexpected record type.
|
|
VerifyOrQuit(false);
|
|
}
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Validate DNS Client `QueryRecord()` for service instance name and KEY record
|
|
|
|
sQueryRecordInfo.Reset();
|
|
Log("QueryRecord(%s) for KEY RR", kInstance1FullName);
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeKey, kInstance1Label, kService1FullName,
|
|
RecordCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
|
|
|
|
VerifyOrQuit(!strcmp(sQueryRecordInfo.mRecords[0].mNameBuffer, kInstance1FullName));
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordType == Dns::ResourceRecord::kTypeKey);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordLength == sizeof(Dns::Ecdsa256KeyRecord));
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mTtl > 0);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mDataBufferSize == sizeof(Dns::Ecdsa256KeyRecord));
|
|
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
|
|
|
|
sQueryRecordInfo.Reset();
|
|
Log("QueryRecord(%s) for misc RR", kInstance1FullName);
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeCname, kInstance1Label, kService1FullName,
|
|
RecordCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 0);
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Validate DNS Client `QueryRecord()` for service instance name and SRV record
|
|
|
|
sQueryRecordInfo.Reset();
|
|
Log("QueryRecord(%s) for SRV record", kInstance1FullName);
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeSrv, kInstance1Label, kService1FullName,
|
|
RecordCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 4);
|
|
|
|
VerifyOrQuit(!strcmp(sQueryRecordInfo.mRecords[0].mNameBuffer, kInstance1FullName));
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordType == Dns::ResourceRecord::kTypeSrv);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordLength > 0);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mTtl > 0);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mDataBufferSize == sQueryRecordInfo.mRecords[0].mRecordLength);
|
|
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
|
|
|
|
ValidateSrvRecordData(sQueryRecordInfo.mRecords[0], kHostFullName);
|
|
|
|
// Validate the records in additional data (TXT and two AAAA).
|
|
|
|
for (uint8_t index = 1; index < 4; index++)
|
|
{
|
|
const QueryRecordInfo::Record &record = sQueryRecordInfo.mRecords[index];
|
|
|
|
VerifyOrQuit(record.mRecordLength > 0);
|
|
VerifyOrQuit(record.mTtl > 0);
|
|
VerifyOrQuit(record.mDataBufferSize == record.mRecordLength);
|
|
VerifyOrQuit(MapEnum(record.mSection) == Dns::Client::RecordInfo::kSectionAdditional);
|
|
|
|
switch (record.mRecordType)
|
|
{
|
|
case Dns::ResourceRecord::kTypeTxt:
|
|
VerifyOrQuit(!strcmp(record.mNameBuffer, kInstance1FullName));
|
|
break;
|
|
case Dns::ResourceRecord::kTypeAaaa:
|
|
VerifyOrQuit(!strcmp(record.mNameBuffer, kHostFullName));
|
|
VerifyOrQuit(record.mRecordLength == sizeof(Ip6::Address));
|
|
break;
|
|
default:
|
|
VerifyOrQuit(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Validate DNS Client `QueryRecord()` for service instance name and ANY record
|
|
|
|
sQueryRecordInfo.Reset();
|
|
Log("QueryRecord(%s) for ANY record", kInstance1FullName);
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, kInstance1Label, kService1FullName,
|
|
RecordCallback, sInstance));
|
|
AdvanceTime(100);
|
|
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 3);
|
|
|
|
for (uint8_t index = 1; index < 3; index++)
|
|
{
|
|
const QueryRecordInfo::Record &record = sQueryRecordInfo.mRecords[index];
|
|
|
|
VerifyOrQuit(StringMatch(record.mNameBuffer, kInstance1FullName));
|
|
VerifyOrQuit(record.mRecordLength > 0);
|
|
VerifyOrQuit(record.mTtl > 0);
|
|
VerifyOrQuit(record.mDataBufferSize == record.mRecordLength);
|
|
VerifyOrQuit(MapEnum(record.mSection) == Dns::Client::RecordInfo::kSectionAnswer);
|
|
|
|
switch (record.mRecordType)
|
|
{
|
|
case Dns::ResourceRecord::kTypeKey:
|
|
case Dns::ResourceRecord::kTypeTxt:
|
|
case Dns::ResourceRecord::kTypeSrv:
|
|
break;
|
|
default:
|
|
VerifyOrQuit(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Validate DNS Client `QueryRecord()` for PTR record
|
|
|
|
sQueryRecordInfo.Reset();
|
|
Log("QueryRecord(%s) for PTR record", kService1FullName);
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypePtr, "_srv", "_udp.default.service.arpa.",
|
|
RecordCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 5);
|
|
|
|
VerifyOrQuit(!strcmp(sQueryRecordInfo.mRecords[0].mNameBuffer, kService1FullName));
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordType == Dns::ResourceRecord::kTypePtr);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordLength > 0);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mTtl > 0);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mDataBufferSize == sQueryRecordInfo.mRecords[0].mRecordLength);
|
|
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
|
|
|
|
ValidatePtrRecordData(sQueryRecordInfo.mRecords[0], kInstance1FullName);
|
|
|
|
// Validate the records in additional data (SRV, TXT and two AAAA).
|
|
|
|
for (uint8_t index = 1; index < 5; index++)
|
|
{
|
|
const QueryRecordInfo::Record &record = sQueryRecordInfo.mRecords[index];
|
|
|
|
VerifyOrQuit(record.mRecordLength > 0);
|
|
VerifyOrQuit(record.mTtl > 0);
|
|
VerifyOrQuit(record.mDataBufferSize == record.mRecordLength);
|
|
VerifyOrQuit(MapEnum(record.mSection) == Dns::Client::RecordInfo::kSectionAdditional);
|
|
|
|
switch (record.mRecordType)
|
|
{
|
|
case Dns::ResourceRecord::kTypeSrv:
|
|
VerifyOrQuit(!strcmp(record.mNameBuffer, kInstance1FullName));
|
|
ValidateSrvRecordData(record, kHostFullName);
|
|
break;
|
|
case Dns::ResourceRecord::kTypeTxt:
|
|
VerifyOrQuit(!strcmp(record.mNameBuffer, kInstance1FullName));
|
|
break;
|
|
case Dns::ResourceRecord::kTypeAaaa:
|
|
VerifyOrQuit(!strcmp(record.mNameBuffer, kHostFullName));
|
|
VerifyOrQuit(record.mRecordLength == sizeof(Ip6::Address));
|
|
break;
|
|
default:
|
|
VerifyOrQuit(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Validate DNS Client `QueryRecord()` for service name and ANY record
|
|
|
|
sQueryRecordInfo.Reset();
|
|
Log("QueryRecord(%s) for ANY record", kInstance1FullName);
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, "_srv", "_udp.default.service.arpa.",
|
|
RecordCallback, sInstance));
|
|
AdvanceTime(100);
|
|
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
|
|
|
|
VerifyOrQuit(StringMatch(sQueryRecordInfo.mRecords[0].mNameBuffer, kService1FullName));
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordType == Dns::ResourceRecord::kTypePtr);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordLength > 0);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mTtl > 0);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mDataBufferSize == sQueryRecordInfo.mRecords[0].mRecordLength);
|
|
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Validate DNS Client `QueryRecord()` for sub-type service name and ANY record
|
|
|
|
sQueryRecordInfo.Reset();
|
|
Log("QueryRecord(%s) for ANY record", kService2SubTypeFullName);
|
|
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, "_best",
|
|
"_sub._game._udp.default.service.arpa.", RecordCallback, sInstance));
|
|
AdvanceTime(100);
|
|
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
|
|
|
|
VerifyOrQuit(StringMatch(sQueryRecordInfo.mRecords[0].mNameBuffer, kService2SubTypeFullName));
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordType == Dns::ResourceRecord::kTypePtr);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mRecordLength > 0);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mTtl > 0);
|
|
VerifyOrQuit(sQueryRecordInfo.mRecords[0].mDataBufferSize == sQueryRecordInfo.mRecords[0].mRecordLength);
|
|
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// 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(%s)", kService2SubTypeFullName);
|
|
SuccessOrQuit(dnsClient->Browse(kService2SubTypeFullName, BrowseCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sBrowseInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sBrowseInfo.mError);
|
|
VerifyOrQuit(sBrowseInfo.mNumInstances == 1);
|
|
|
|
sBrowseInfo.Reset();
|
|
Log("Browse() for unknown 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);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
|
|
Log("Set TestMode on server to reject multi-question queries and send error");
|
|
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeRejectMultiQuestionQuery);
|
|
|
|
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);
|
|
|
|
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeDisabled);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
|
|
Log("Set TestMode on server to ignore multi-question queries (send no response)");
|
|
dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeIgnoreMultiQuestionQuery);
|
|
|
|
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(10 * 1000); // Wait longer than client response timeout.
|
|
|
|
VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sResolveServiceInfo.mError);
|
|
|
|
// Use `kServiceModeSrvTxt` and check that server does ignore 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));
|
|
|
|
// Wait for the client to time out after exhausting all retry attempts, and
|
|
// ensure that a `kErrorResponseTimeout` error is reported.
|
|
|
|
AdvanceTime(45 * 1000);
|
|
|
|
VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1);
|
|
VerifyOrQuit(sResolveServiceInfo.mError == kErrorResponseTimeout);
|
|
|
|
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::kTestModeRejectMultiQuestionQuery);
|
|
|
|
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);
|
|
|
|
oldServerCounters = dnsServer->GetCounters();
|
|
|
|
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]));
|
|
}
|
|
|
|
newServerCounters = dnsServer->GetCounters();
|
|
|
|
Log("Validate (using server counter) that client first tried to query SRV/TXT together and failed");
|
|
Log("and then send separate queries (for SRV, TXT and AAAA)");
|
|
Log(" Total : %2u -> %2u", oldServerCounters.GetTotalQueries(), newServerCounters.GetTotalQueries());
|
|
Log(" Failed: %2u -> %2u", oldServerCounters.GetTotalFailedQueries(), newServerCounters.GetTotalFailedQueries());
|
|
|
|
VerifyOrQuit(newServerCounters.GetTotalFailedQueries() == 1 + oldServerCounters.GetTotalFailedQueries());
|
|
VerifyOrQuit(newServerCounters.GetTotalQueries() == 4 + oldServerCounters.GetTotalQueries());
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("Resolve service again now using `kServiceModeSrvTxtOptimize` as default config");
|
|
Log("Client should already know that server is not capable of handling multi-question query");
|
|
|
|
queryConfig.Clear();
|
|
queryConfig.mServiceMode = static_cast<otDnsServiceMode>(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize);
|
|
|
|
dnsClient->SetDefaultConfig(queryConfig);
|
|
|
|
Log("ResolveService(%s,%s)", kInstance1Label, kService1FullName);
|
|
|
|
oldServerCounters = dnsServer->GetCounters();
|
|
|
|
sResolveServiceInfo.Reset();
|
|
SuccessOrQuit(dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, nullptr));
|
|
|
|
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);
|
|
|
|
newServerCounters = dnsServer->GetCounters();
|
|
|
|
Log("Client should already know that server is not capable of handling multi-question query");
|
|
Log("Check server counters to validate that client did send separate queries for TXT and SRV");
|
|
Log(" Total : %2u -> %2u", oldServerCounters.GetTotalQueries(), newServerCounters.GetTotalQueries());
|
|
Log(" Failed: %2u -> %2u", oldServerCounters.GetTotalFailedQueries(), newServerCounters.GetTotalFailedQueries());
|
|
|
|
VerifyOrQuit(newServerCounters.GetTotalFailedQueries() == oldServerCounters.GetTotalFailedQueries());
|
|
VerifyOrQuit(newServerCounters.GetTotalQueries() == 2 + oldServerCounters.GetTotalQueries());
|
|
|
|
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::kServiceModeSrvTxtSeparate));
|
|
|
|
queryConfig.Clear();
|
|
queryConfig.mServiceMode = static_cast<otDnsServiceMode>(Dns::Client::QueryConfig::kServiceModeSrvTxtSeparate);
|
|
|
|
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);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("Remove the route prefix (with NAT64 flag) from network data");
|
|
|
|
// This ensures the device is no longer tracked as a BR. This is
|
|
// required to release the associated heap allocation in
|
|
// `NetDataBrTracker`, which would otherwise cause the
|
|
// `heapAllocations` check to fail.
|
|
|
|
SuccessOrQuit(otBorderRouterRemoveRoute(sInstance, &routeConfig.mPrefix));
|
|
SuccessOrQuit(otBorderRouterRegister(sInstance));
|
|
AdvanceTime(1000);
|
|
|
|
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");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
Dns::Name::Buffer sLastSubscribeName;
|
|
Dns::Name::Buffer sLastUnsubscribeName;
|
|
|
|
void QuerySubscribe(void *aContext, const char *aFullName)
|
|
{
|
|
uint16_t length = StringLength(aFullName, Dns::Name::kMaxNameSize);
|
|
|
|
Log("QuerySubscribe(%s)", aFullName);
|
|
|
|
VerifyOrQuit(aContext == sInstance);
|
|
VerifyOrQuit(length < Dns::Name::kMaxNameSize);
|
|
strcpy(sLastSubscribeName, aFullName);
|
|
}
|
|
|
|
void QueryUnsubscribe(void *aContext, const char *aFullName)
|
|
{
|
|
uint16_t length = StringLength(aFullName, Dns::Name::kMaxNameSize);
|
|
|
|
Log("QueryUnsubscribe(%s)", aFullName);
|
|
|
|
VerifyOrQuit(aContext == sInstance);
|
|
VerifyOrQuit(length < Dns::Name::kMaxNameSize);
|
|
strcpy(sLastUnsubscribeName, aFullName);
|
|
}
|
|
|
|
void TestDnssdServerProxyCallback(void)
|
|
{
|
|
Srp::Server *srpServer;
|
|
Srp::Client *srpClient;
|
|
Dns::Client *dnsClient;
|
|
Dns::ServiceDiscovery::Server *dnsServer;
|
|
otDnssdServiceInstanceInfo instanceInfo;
|
|
|
|
Log("--------------------------------------------------------------------------------------------");
|
|
Log("TestDnssdServerProxyCallback");
|
|
|
|
InitTest();
|
|
|
|
srpServer = &sInstance->Get<Srp::Server>();
|
|
srpClient = &sInstance->Get<Srp::Client>();
|
|
dnsClient = &sInstance->Get<Dns::Client>();
|
|
dnsServer = &sInstance->Get<Dns::ServiceDiscovery::Server>();
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// 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());
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Set the query subscribe/unsubscribe callbacks on server
|
|
|
|
dnsServer->SetQueryCallbacks(QuerySubscribe, QueryUnsubscribe, sInstance);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
|
|
sLastSubscribeName[0] = '\0';
|
|
sLastUnsubscribeName[0] = '\0';
|
|
|
|
sBrowseInfo.Reset();
|
|
Log("Browse(%s)", kService1FullName);
|
|
SuccessOrQuit(dnsClient->Browse(kService1FullName, BrowseCallback, sInstance));
|
|
AdvanceTime(10);
|
|
|
|
VerifyOrQuit(strcmp(sLastSubscribeName, kService1FullName) == 0);
|
|
VerifyOrQuit(strcmp(sLastUnsubscribeName, "") == 0);
|
|
|
|
VerifyOrQuit(sBrowseInfo.mCallbackCount == 0);
|
|
|
|
Log("Invoke subscribe callback");
|
|
|
|
memset(&instanceInfo, 0, sizeof(instanceInfo));
|
|
instanceInfo.mFullName = kInstance1FullName;
|
|
instanceInfo.mHostName = kHostFullName;
|
|
instanceInfo.mPort = 200;
|
|
|
|
dnsServer->HandleDiscoveredServiceInstance(kService1FullName, instanceInfo);
|
|
|
|
AdvanceTime(10);
|
|
|
|
VerifyOrQuit(sBrowseInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sBrowseInfo.mError);
|
|
VerifyOrQuit(sBrowseInfo.mNumInstances == 1);
|
|
|
|
VerifyOrQuit(strcmp(sLastUnsubscribeName, kService1FullName) == 0);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
|
|
sLastSubscribeName[0] = '\0';
|
|
sLastUnsubscribeName[0] = '\0';
|
|
|
|
sBrowseInfo.Reset();
|
|
Log("Browse(%s)", kService2FullName);
|
|
SuccessOrQuit(dnsClient->Browse(kService2FullName, BrowseCallback, sInstance));
|
|
AdvanceTime(10);
|
|
|
|
VerifyOrQuit(strcmp(sLastSubscribeName, kService2FullName) == 0);
|
|
VerifyOrQuit(strcmp(sLastUnsubscribeName, "") == 0);
|
|
|
|
Log("Invoke subscribe callback for wrong name");
|
|
|
|
memset(&instanceInfo, 0, sizeof(instanceInfo));
|
|
instanceInfo.mFullName = kInstance1FullName;
|
|
instanceInfo.mHostName = kHostFullName;
|
|
instanceInfo.mPort = 200;
|
|
|
|
dnsServer->HandleDiscoveredServiceInstance(kService1FullName, instanceInfo);
|
|
|
|
AdvanceTime(10);
|
|
|
|
VerifyOrQuit(sBrowseInfo.mCallbackCount == 0);
|
|
|
|
Log("Invoke subscribe callback for correct name");
|
|
|
|
memset(&instanceInfo, 0, sizeof(instanceInfo));
|
|
instanceInfo.mFullName = kInstance2FullName;
|
|
instanceInfo.mHostName = kHostFullName;
|
|
instanceInfo.mPort = 200;
|
|
|
|
dnsServer->HandleDiscoveredServiceInstance(kService2FullName, instanceInfo);
|
|
|
|
AdvanceTime(10);
|
|
|
|
VerifyOrQuit(sBrowseInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sBrowseInfo.mError);
|
|
VerifyOrQuit(sBrowseInfo.mNumInstances == 1);
|
|
|
|
VerifyOrQuit(strcmp(sLastUnsubscribeName, kService2FullName) == 0);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
|
|
sLastSubscribeName[0] = '\0';
|
|
sLastUnsubscribeName[0] = '\0';
|
|
|
|
sBrowseInfo.Reset();
|
|
Log("Browse(%s)", kService2FullName);
|
|
SuccessOrQuit(dnsClient->Browse(kService2FullName, BrowseCallback, sInstance));
|
|
AdvanceTime(10);
|
|
|
|
VerifyOrQuit(strcmp(sLastSubscribeName, kService2FullName) == 0);
|
|
VerifyOrQuit(strcmp(sLastUnsubscribeName, "") == 0);
|
|
|
|
Log("Do not invoke subscribe callback and let query to timeout");
|
|
|
|
// Query timeout is set to 6 seconds
|
|
|
|
AdvanceTime(5000);
|
|
|
|
VerifyOrQuit(sBrowseInfo.mCallbackCount == 0);
|
|
|
|
AdvanceTime(2000);
|
|
|
|
VerifyOrQuit(sBrowseInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sBrowseInfo.mError);
|
|
VerifyOrQuit(sBrowseInfo.mNumInstances == 0);
|
|
|
|
VerifyOrQuit(strcmp(sLastUnsubscribeName, kService2FullName) == 0);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
|
|
sLastSubscribeName[0] = '\0';
|
|
sLastUnsubscribeName[0] = '\0';
|
|
|
|
sBrowseInfo.Reset();
|
|
Log("Browse(%s)", kService2FullName);
|
|
SuccessOrQuit(dnsClient->Browse(kService2FullName, BrowseCallback, sInstance));
|
|
AdvanceTime(10);
|
|
|
|
VerifyOrQuit(strcmp(sLastSubscribeName, kService2FullName) == 0);
|
|
VerifyOrQuit(strcmp(sLastUnsubscribeName, "") == 0);
|
|
|
|
VerifyOrQuit(sBrowseInfo.mCallbackCount == 0);
|
|
|
|
Log("Do not invoke subscribe callback and stop server");
|
|
|
|
dnsServer->Stop();
|
|
|
|
AdvanceTime(10);
|
|
|
|
VerifyOrQuit(sBrowseInfo.mCallbackCount == 1);
|
|
VerifyOrQuit(sBrowseInfo.mError != kErrorNone);
|
|
|
|
VerifyOrQuit(strcmp(sLastUnsubscribeName, kService2FullName) == 0);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Finalize OT instance and validate all heap allocations are freed.
|
|
|
|
Log("Finalizing OT instance");
|
|
FinalizeTest();
|
|
|
|
Log("End of TestDnssdServerProxyCallback");
|
|
}
|
|
|
|
void TestDnssdSoaNsResponse(void)
|
|
{
|
|
Srp::Server *srpServer;
|
|
Srp::Client *srpClient;
|
|
Dns::Client *dnsClient;
|
|
Dns::Name::Buffer serverName;
|
|
StringWriter writer(serverName, sizeof(serverName));
|
|
|
|
Log("--------------------------------------------------------------------------------------------");
|
|
Log("TestDnssdSoaNsResponse");
|
|
|
|
InitTest();
|
|
|
|
srpServer = &sInstance->Get<Srp::Server>();
|
|
srpClient = &sInstance->Get<Srp::Client>();
|
|
dnsClient = &sInstance->Get<Dns::Client>();
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// 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());
|
|
|
|
writer.Append("otDNS%s.default.service.arpa.", sInstance->Get<Mac::Mac>().GetExtAddress().ToString().AsCString());
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("SOA Query");
|
|
|
|
for (uint8_t iter = 0; iter < 2; iter++)
|
|
{
|
|
sQueryRecordInfo.Reset();
|
|
|
|
// First iteration: Query for `default.service.arpa.` directly, and
|
|
// validate that we see the SOA record in the Answer section.
|
|
// Second iteration: Query for `myhost.default.service.arpa.`, and
|
|
// validate that we see the SOA record in the Authority section.
|
|
|
|
if (iter == 0)
|
|
{
|
|
Log("QueryRecord(%s) for SOA RR", "default.service.arpa.");
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeSoa, "default", "service.arpa.",
|
|
RecordCallback, sInstance));
|
|
}
|
|
else
|
|
{
|
|
Log("QueryRecord(%s) for SOA RR", "myhost.default.service.arpa.");
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeSoa, "myhost", "default.service.arpa.",
|
|
RecordCallback, sInstance));
|
|
}
|
|
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
|
|
|
|
if (iter == 0)
|
|
{
|
|
VerifyOrQuit(StringMatch(sQueryRecordInfo.mQueryName, "default.service.arpa."));
|
|
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(StringMatch(sQueryRecordInfo.mQueryName, "myhost.default.service.arpa."));
|
|
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAuthority);
|
|
}
|
|
|
|
ValidateSoaRecordData(sQueryRecordInfo.mRecords[0], serverName);
|
|
|
|
AdvanceTime(1000);
|
|
}
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("NS Query");
|
|
|
|
for (uint8_t iter = 0; iter < 2; iter++)
|
|
{
|
|
sQueryRecordInfo.Reset();
|
|
|
|
// First iteration: Query for `default.service.arpa.` directly and
|
|
// validate that we see the NS response in the Answer section. Second
|
|
// iteration: Query for `myhost.default.service.arpa.` and validate
|
|
// that we see the SOA record instead in the Authority section in
|
|
// the response.
|
|
|
|
if (iter == 0)
|
|
{
|
|
Log("QueryRecord(%s) for NS RR", "default.service.arpa.");
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeNs, "default", "service.arpa.",
|
|
RecordCallback, sInstance));
|
|
}
|
|
else
|
|
{
|
|
Log("QueryRecord(%s) for NS RR", "myhost.default.service.arpa.");
|
|
SuccessOrQuit(dnsClient->QueryRecord(Dns::ResourceRecord::kTypeNs, "myhost", "default.service.arpa.",
|
|
RecordCallback, sInstance));
|
|
}
|
|
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 1);
|
|
|
|
if (iter == 0)
|
|
{
|
|
VerifyOrQuit(StringMatch(sQueryRecordInfo.mQueryName, "default.service.arpa."));
|
|
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAnswer);
|
|
ValidateNsRecordData(sQueryRecordInfo.mRecords[0], serverName);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(StringMatch(sQueryRecordInfo.mQueryName, "myhost.default.service.arpa."));
|
|
VerifyOrQuit(MapEnum(sQueryRecordInfo.mRecords[0].mSection) == Dns::Client::RecordInfo::kSectionAuthority);
|
|
ValidateSoaRecordData(sQueryRecordInfo.mRecords[0], serverName);
|
|
}
|
|
|
|
AdvanceTime(1000);
|
|
}
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
|
|
sQueryRecordInfo.Reset();
|
|
|
|
Log("QueryRecord(%s) for ANY RR", "default.service.arpa.");
|
|
SuccessOrQuit(
|
|
dnsClient->QueryRecord(Dns::ResourceRecord::kTypeAny, "default", "service.arpa.", RecordCallback, sInstance));
|
|
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sQueryRecordInfo.mCallbackCount == 1);
|
|
SuccessOrQuit(sQueryRecordInfo.mError);
|
|
VerifyOrQuit(sQueryRecordInfo.mNumRecords == 2);
|
|
|
|
VerifyOrQuit(StringMatch(sQueryRecordInfo.mQueryName, "default.service.arpa."));
|
|
|
|
for (uint8_t index = 0; index < 2; index++)
|
|
{
|
|
QueryRecordInfo::Record &record = sQueryRecordInfo.mRecords[index];
|
|
|
|
VerifyOrQuit(MapEnum(record.mSection) == Dns::Client::RecordInfo::kSectionAnswer);
|
|
|
|
switch (record.mRecordType)
|
|
{
|
|
case Dns::ResourceRecord::kTypeSoa:
|
|
ValidateSoaRecordData(record, serverName);
|
|
break;
|
|
case Dns::ResourceRecord::kTypeNs:
|
|
ValidateNsRecordData(record, serverName);
|
|
break;
|
|
default:
|
|
VerifyOrQuit(false);
|
|
break;
|
|
}
|
|
|
|
AdvanceTime(1000);
|
|
}
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
|
|
sAddressInfo.Reset();
|
|
Log("ResolveAddress(%s)", serverName);
|
|
SuccessOrQuit(dnsClient->ResolveAddress(serverName, AddressCallback, sInstance));
|
|
AdvanceTime(100);
|
|
VerifyOrQuit(sAddressInfo.mCallbackCount >= 1);
|
|
SuccessOrQuit(sAddressInfo.mError);
|
|
VerifyOrQuit(sAddressInfo.mHostAddresses[0] == sInstance->Get<Mle::Mle>().GetMeshLocalEid());
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Finalize OT instance and validate all heap allocations are freed.
|
|
|
|
Log("Finalizing OT instance");
|
|
FinalizeTest();
|
|
|
|
Log("End of TestDnssdSoaNsResponse");
|
|
}
|
|
|
|
#endif // ENABLE_DNS_TEST
|
|
|
|
int main(void)
|
|
{
|
|
#if ENABLE_DNS_TEST
|
|
TestDnsClient();
|
|
TestDnssdServerProxyCallback();
|
|
TestDnssdSoaNsResponse();
|
|
printf("All tests passed\n");
|
|
#else
|
|
printf("DNS_CLIENT or DSNSSD_SERVER feature is not enabled\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|