From bbc73280ba6378e79e14554c316a37dbbb457219 Mon Sep 17 00:00:00 2001 From: Li Cao Date: Fri, 31 Oct 2025 22:37:18 +0800 Subject: [PATCH] [ncp] implement ncp platform dnssd discovery methods (#12068) This commit implements `otPlatDnssdStartBrowser` and `otPlatDnssdStopBrowser` to support service discovery on NCP. The platform APIs are intended to be used by `ServiceDiscovery::Server::DiscoveryProxy` for discovery functionality. This commit only supports browser for now and will suppport Srv/Txt/Address resolver in other commits to avoid this commit from being too large. This commit contains these changes: * Add new spinel property for starting / stopping service browser * Implement encoding/decoding of the new property * Add unit test for encoding/decoding * Implement ncp version of dnssd platform API `otPlatDnssdStartBrowser` and `otPlatDnssdStopBrowser` * Add property handler to get browser result on NCP side * Add unit test to verify that the browser callback is correctly invoked after getting browser result. --- src/lib/spinel/spinel.c | 2 + src/lib/spinel/spinel.h | 28 ++++++++ src/lib/spinel/spinel_prop_codec.cpp | 94 +++++++++++++++++++++++++++ src/lib/spinel/spinel_prop_codec.hpp | 59 +++++++++++++++++ src/ncp/ncp_base.hpp | 47 +++++++++++++- src/ncp/ncp_base_dispatcher.cpp | 1 + src/ncp/ncp_base_ftd.cpp | 27 ++++++++ src/ncp/platform/dnssd.cpp | 8 ++- tests/unit/test_ncp_dnssd.cpp | 67 +++++++++++++++++++ tests/unit/test_spinel_prop_codec.cpp | 75 +++++++++++++++++++++ 10 files changed, 405 insertions(+), 3 deletions(-) diff --git a/src/lib/spinel/spinel.c b/src/lib/spinel/spinel.c index 0b1af9cc7..3b9632ef9 100644 --- a/src/lib/spinel/spinel.c +++ b/src/lib/spinel/spinel.c @@ -1419,6 +1419,8 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) {SPINEL_PROP_DNSSD_HOST, "DNSSD_HOST"}, {SPINEL_PROP_DNSSD_SERVICE, "DNSSD_SERVICE"}, {SPINEL_PROP_DNSSD_KEY_RECORD, "DNSSD_KEY_RECORD"}, + {SPINEL_PROP_DNSSD_BROWSER, "DNSSD_BROWSER"}, + {SPINEL_PROP_DNSSD_BROWSE_RESULT, "DNSSD_BROWSE_RESULT"}, {SPINEL_PROP_BORDER_AGENT_MESHCOP_SERVICE_STATE, "BORDER_AGENT_MESHCOP_SERVICE_STATE"}, {SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE, "SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE"}, {SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE, "SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE"}, diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index 821244d15..b436f582b 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -4897,6 +4897,34 @@ enum */ SPINEL_PROP_DNSSD_KEY_RECORD = SPINEL_PROP_DNSSD__BEGIN + 5, + /// DNS-SD Service Browser + /** + * Format : Inserted/Removed + * + * `U` : The service type (e.g., "_mt._udp"). MUST NOT include domain name. + * `t(U)` : The sub-type label if browsing for sub-type, empty otherwise. + * `L` : The infrastructure network interface index to browse the service. + * `D` : The context of the request. A pointer to the browse callback to receive the result. + * + * NCP uses this property to browse services. + */ + SPINEL_PROP_DNSSD_BROWSER = SPINEL_PROP_DNSSD__BEGIN + 6, + + /// DNS-SD Service Browser Result + /** + * Format : Set + * + * `U` : The service type (e.g., "_mt._udp"). MUST NOT include domain name. + * `t(U)` : The sub-type label if browsing for sub-type, empty otherwise. + * `U` : Service instance label. + * `L` : TTL in seconds. Zero TTL indicates that service is removed. + * `L` : The infrastructure network interface index. + * `D` : The context of the request. A pointer to the browse callback to receive the result. + * + * Host uses this property to send the result of the browser to NCP. + */ + SPINEL_PROP_DNSSD_BROWSE_RESULT = SPINEL_PROP_DNSSD__BEGIN + 7, + SPINEL_PROP_DNSSD__END = 0x950, SPINEL_PROP_BORDER_AGENT__BEGIN = 0x950, diff --git a/src/lib/spinel/spinel_prop_codec.cpp b/src/lib/spinel/spinel_prop_codec.cpp index 0e4cd488a..bc50fb915 100644 --- a/src/lib/spinel/spinel_prop_codec.cpp +++ b/src/lib/spinel/spinel_prop_codec.cpp @@ -196,5 +196,99 @@ exit: return error; } +template <> otError EncodeDnssdDiscovery(Encoder &aEncoder, const otPlatDnssdBrowser &aDiscovery) +{ + otError error = OT_ERROR_NONE; + + SuccessOrExit(error = aEncoder.WriteUtf8(aDiscovery.mServiceType)); + SuccessOrExit(error = aEncoder.OpenStruct()); + if (aDiscovery.mSubTypeLabel != nullptr) + { + SuccessOrExit(error = aEncoder.WriteUtf8(aDiscovery.mSubTypeLabel)); + } + SuccessOrExit(error = aEncoder.CloseStruct()); + SuccessOrExit(error = aEncoder.WriteUint32(aDiscovery.mInfraIfIndex)); + SuccessOrExit(error = aEncoder.WriteData(reinterpret_cast(&aDiscovery.mCallback), + sizeof(aDiscovery.mCallback))); + +exit: + return error; +} + +otError EncodeDnssdBrowseResult(Encoder &aEncoder, + const otPlatDnssdBrowseResult &aBrowseResult, + const uint8_t *aCallbackData, + uint16_t aCallbackDataLen) +{ + otError error = OT_ERROR_NONE; + + SuccessOrExit(error = aEncoder.WriteUtf8(aBrowseResult.mServiceType)); + SuccessOrExit(error = aEncoder.OpenStruct()); + if (aBrowseResult.mSubTypeLabel != nullptr) + { + SuccessOrExit(error = aEncoder.WriteUtf8(aBrowseResult.mSubTypeLabel)); + } + SuccessOrExit(error = aEncoder.CloseStruct()); + SuccessOrExit(error = aEncoder.WriteUtf8(aBrowseResult.mServiceInstance)); + SuccessOrExit(error = aEncoder.WriteUint32(aBrowseResult.mTtl)); + SuccessOrExit(error = aEncoder.WriteUint32(aBrowseResult.mInfraIfIndex)); + SuccessOrExit(error = aEncoder.WriteData(aCallbackData, aCallbackDataLen)); + +exit: + return error; +} + +otError DecodeDnssdBrowser(Decoder &aDecoder, + otPlatDnssdBrowser &aBrowser, + const uint8_t *&aCallbackData, + uint16_t &aCallbackDataLen) +{ + otError error = OT_ERROR_NONE; + + SuccessOrExit(error = aDecoder.ReadUtf8(aBrowser.mServiceType)); + SuccessOrExit(error = aDecoder.OpenStruct()); + if (!aDecoder.IsAllReadInStruct()) + { + SuccessOrExit(error = aDecoder.ReadUtf8(aBrowser.mSubTypeLabel)); + } + else + { + aBrowser.mSubTypeLabel = nullptr; + } + SuccessOrExit(error = aDecoder.CloseStruct()); + SuccessOrExit(error = aDecoder.ReadUint32(aBrowser.mInfraIfIndex)); + SuccessOrExit(error = aDecoder.ReadData(aCallbackData, aCallbackDataLen)); + +exit: + return error; +} + +otError DecodeDnssdBrowseResult(Decoder &aDecoder, + otPlatDnssdBrowseResult &aBrowseResult, + const uint8_t *&aCallbackData, + uint16_t &aCallbackDataLen) +{ + otError error = OT_ERROR_NONE; + + SuccessOrExit(error = aDecoder.ReadUtf8(aBrowseResult.mServiceType)); + SuccessOrExit(error = aDecoder.OpenStruct()); + if (!aDecoder.IsAllReadInStruct()) + { + SuccessOrExit(error = aDecoder.ReadUtf8(aBrowseResult.mSubTypeLabel)); + } + else + { + aBrowseResult.mSubTypeLabel = nullptr; + } + SuccessOrExit(error = aDecoder.CloseStruct()); + SuccessOrExit(error = aDecoder.ReadUtf8(aBrowseResult.mServiceInstance)); + SuccessOrExit(error = aDecoder.ReadUint32(aBrowseResult.mTtl)); + SuccessOrExit(error = aDecoder.ReadUint32(aBrowseResult.mInfraIfIndex)); + SuccessOrExit(error = aDecoder.ReadData(aCallbackData, aCallbackDataLen)); + +exit: + return error; +} + } // namespace Spinel } // namespace ot diff --git a/src/lib/spinel/spinel_prop_codec.hpp b/src/lib/spinel/spinel_prop_codec.hpp index ec9d3b00f..36a95bde7 100644 --- a/src/lib/spinel/spinel_prop_codec.hpp +++ b/src/lib/spinel/spinel_prop_codec.hpp @@ -58,6 +58,35 @@ otError EncodeDnssd(Encoder &aEncoder, otPlatDnssdRequestId aRequestId, otPlatDnssdRegisterCallback aCallback); +/** + * Use Spinel::Encode to encode a Dnssd Discovery. + * + * DnssdDiscoveryType can be: otPlatDnssdBrowser, otPlatDnssdSrvResolver, otPlatDnssdTxtResolver, + * otPlatDnssdAddressResolver and otPlatDnssdRecordQuerier. + * + * A Spinel header and command MUST have been encoded by the encoder. + * + * @param[in] aEncoder A reference to the encoder object. + * @param[in] aDiscovery A reference to the dnssd discovery object. + */ +template +otError EncodeDnssdDiscovery(Encoder &aEncoder, const DnssdDiscoveryType &aDiscovery); + +/** + * Use Spinel::Encode to encode a Dnssd Browser result. + * + * A Spinel header and command MUST have been encoded by the encoder. + * + * @param[in] aEncoder A reference to the encoder object. + * @param[in] aBrowseResult A reference to the browse result object. + * @param[in] aCallbackData A pointer to the callback data. + * @param[in] aCallbackDataLen The data length. + */ +otError EncodeDnssdBrowseResult(Encoder &aEncoder, + const otPlatDnssdBrowseResult &aBrowseResult, + const uint8_t *aCallbackData, + uint16_t aCallbackDataLen); + /** * Use Spinel::Decoder to decode a SPINEL_PROP_DNSSD_HOST message to a otPlatDnssdHost. * @@ -113,6 +142,36 @@ otError DecodeDnssdKey(Decoder &aDecoder, const uint8_t *&aCallbackData, uint16_t &aCallbackDataLen); +/** + * Use Spinel::Decoder to decode a SPINEL_PROP_DNSSD_BROWSER message to a otPlatDnssdBrowser. + * + * The decoder MUST have read the header, command and property key of the frame. + * + * @param[in] aDecoder A reference to the decoder object. + * @param[out] aBrowser A reference to the dnssd browser. + * @param[out] aCallbackData A reference to the pointer to the callback data. + * @param[out] aCallbackDataLen A reference to the callback data length. + */ +otError DecodeDnssdBrowser(Decoder &aDecoder, + otPlatDnssdBrowser &aBrowser, + const uint8_t *&aCallbackData, + uint16_t &aCallbackDataLen); + +/** + * Use Spinel::Decoder to decode a Dnssd Browser result. + * + * The decoder MUST have read the header, command and property key of the frame. + * + * @param[in] aDecoder A reference to the decoder object. + * @param[out] aBrowseResult A reference to the browse result. + * @param[out] aCallbackData A reference to the pointer to the callback data. + * @param[out] aCallbackDataLen A reference to the callback data length. + */ +otError DecodeDnssdBrowseResult(Decoder &aDecoder, + otPlatDnssdBrowseResult &aBrowseResult, + const uint8_t *&aCallbackData, + uint16_t &aCallbackDataLen); + } // namespace Spinel } // namespace ot diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index f6eae6387..21470cc5b 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -310,13 +310,27 @@ public: otPlatDnssdRequestId aRequestId, otPlatDnssdRegisterCallback aCallback); + /** + * Starts a service browser. + * + * @param[in] aBrowser The browser to be started. + */ + void DnssdStartBrowser(const otPlatDnssdBrowser *aBrowser); + + /** + * Stops a service browser. + * + * @param[in] aBrowser The browser to be stopped. + */ + void DnssdStopBrowser(const otPlatDnssdBrowser *aBrowser); + /** * Gets the Dnssd state. * * Returns the platform dnssd state. */ otPlatDnssdState DnssdGetState(void); -#endif +#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE protected: static constexpr uint8_t kBitsPerByte = 8; ///< Number of bits in a byte. @@ -851,6 +865,8 @@ protected: #if OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE + template struct DnssdDiscoveryPropKeyFor; + template void DnssdUpdate(const DnssdObjType *aObj, otPlatDnssdRequestId aRequestId, @@ -875,6 +891,28 @@ protected: } } + /** + * Template for making service discovery. + * + * DnssdDiscoveryType can be: otPlatDnssdBrowser, otPlatDnssdSrvResolver, otPlatDnssdTxtResolver, + * otPlatDnssdAddressResolver and otPlatDnssdRecordQuerier. + */ + template void DnssdUpdateDiscovery(const DnssdDiscoveryType *aDiscovery, bool aStart) + { + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID; + spinel_command_t cmd = aStart ? SPINEL_CMD_PROP_VALUE_INSERTED : SPINEL_CMD_PROP_VALUE_REMOVED; + + VerifyOrExit(aDiscovery != nullptr); + VerifyOrExit(mDnssdState == OT_PLAT_DNSSD_READY); + + SuccessOrExit(mEncoder.BeginFrame(header, cmd, DnssdDiscoveryPropKeyFor::Key)); + SuccessOrExit(Spinel::EncodeDnssdDiscovery(mEncoder, *aDiscovery)); + SuccessOrExit(mEncoder.EndFrame()); + + exit: + return; + } + otPlatDnssdState mDnssdState; #endif // OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE @@ -899,6 +937,13 @@ protected: #endif }; +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE +template <> struct NcpBase::DnssdDiscoveryPropKeyFor +{ + static constexpr spinel_prop_key_t Key = SPINEL_PROP_DNSSD_BROWSER; +}; +#endif + } // namespace Ncp } // namespace ot diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index d653a15eb..6646461b7 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -545,6 +545,7 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) #if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE && OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_DNSSD_STATE), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_DNSSD_REQUEST_RESULT), + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_DNSSD_BROWSE_RESULT), #endif #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE), diff --git a/src/ncp/ncp_base_ftd.cpp b/src/ncp/ncp_base_ftd.cpp index 0b3ab9f9b..ed20d6a64 100644 --- a/src/ncp/ncp_base_ftd.cpp +++ b/src/ncp/ncp_base_ftd.cpp @@ -1692,6 +1692,16 @@ void NcpBase::DnssdUnregisterKey(const otPlatDnssdKey *aKey, DnssdUpdate(aKey, aRequestId, aCallback, /* aRegister */ false); } +void NcpBase::DnssdStartBrowser(const otPlatDnssdBrowser *aBrowser) +{ + DnssdUpdateDiscovery(aBrowser, /* aStart */ true); +} + +void NcpBase::DnssdStopBrowser(const otPlatDnssdBrowser *aBrowser) +{ + DnssdUpdateDiscovery(aBrowser, /* aStart */ false); +} + otPlatDnssdState NcpBase::DnssdGetState(void) { return mDnssdState; } template <> otError NcpBase::HandlePropertySet(void) @@ -1731,6 +1741,23 @@ exit: return error; } +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error = OT_ERROR_NONE; + otPlatDnssdBrowseResult browseResult; + otPlatDnssdBrowseCallback callback = nullptr; + const uint8_t *context; + uint16_t contextLen; + + SuccessOrExit(error = DecodeDnssdBrowseResult(mDecoder, browseResult, context, contextLen)); + VerifyOrExit(contextLen == sizeof(otPlatDnssdBrowseCallback), error = OT_ERROR_PARSE); + callback = *reinterpret_cast(context); + callback(mInstance, &browseResult); + +exit: + return error; +} + #endif // OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE diff --git a/src/ncp/platform/dnssd.cpp b/src/ncp/platform/dnssd.cpp index af97c3e81..8723d9406 100644 --- a/src/ncp/platform/dnssd.cpp +++ b/src/ncp/platform/dnssd.cpp @@ -108,13 +108,17 @@ void otPlatDnssdUnregisterKey(otInstance *aInstance, void otPlatDnssdStartBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser) { OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aBrowser); + ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance(); + + ncp->DnssdStartBrowser(aBrowser); } void otPlatDnssdStopBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser) { OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aBrowser); + ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance(); + + ncp->DnssdStopBrowser(aBrowser); } void otPlatDnssdStartSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver) diff --git a/tests/unit/test_ncp_dnssd.cpp b/tests/unit/test_ncp_dnssd.cpp index 280e1a6b9..05b892bb0 100644 --- a/tests/unit/test_ncp_dnssd.cpp +++ b/tests/unit/test_ncp_dnssd.cpp @@ -178,6 +178,72 @@ void TestNcpDnssdRegistrations(void) VerifyOrQuit(sError == OT_ERROR_NONE); } +static bool sDnssdBrowseCallbackInvoked = false; + +static void TestDnssdBrowseCallback(otInstance *aInstance, const otPlatDnssdBrowseResult *aResult) +{ + VerifyOrQuit(strcmp(aResult->mServiceType, "_ms._tcp") == 0); + VerifyOrQuit(strcmp(aResult->mSubTypeLabel, "_battery") == 0); + VerifyOrQuit(strcmp(aResult->mServiceInstance, "GAT-X105 #1") == 0); + VerifyOrQuit(aResult->mTtl == 12345); + VerifyOrQuit(aResult->mInfraIfIndex == 2); + + sDnssdBrowseCallbackInvoked = true; +} + +static otError GenerateSpinelDnssdBrowseResultFrame(const otPlatDnssdBrowseResult &aBrowseResult, + uint8_t *aBuf, + uint16_t &aLen) +{ + otError error = OT_ERROR_NONE; + uint8_t buf[kMaxSpinelBufferSize]; + Spinel::Buffer ncpBuffer(buf, kMaxSpinelBufferSize); + Spinel::Encoder encoder(ncpBuffer); + otPlatDnssdBrowseCallback callback = &TestDnssdBrowseCallback; + + uint8_t header = SPINEL_HEADER_FLAG | 0 /* Iid */ | 1 /* Tid */; + SuccessOrExit(error = encoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_DNSSD_BROWSE_RESULT)); + SuccessOrExit(error = EncodeDnssdBrowseResult(encoder, aBrowseResult, reinterpret_cast(&callback), + sizeof(callback))); + SuccessOrExit(error = encoder.EndFrame()); + + SuccessOrExit(ncpBuffer.OutFrameBegin()); + aLen = ncpBuffer.OutFrameGetLength(); + VerifyOrExit(ncpBuffer.OutFrameRead(aLen, aBuf) == aLen, error = OT_ERROR_FAILED); + +exit: + return error; +} + +void TestNcpDnssdBrowse(void) +{ + Instance *instance = static_cast(testInitInstance()); + Ncp::NcpBase ncpBase(instance); + uint8_t recvBuf[kMaxSpinelBufferSize]; + uint16_t recvLen; + otPlatDnssdBrowser browser; + otPlatDnssdBrowseResult browseResult; + + browser.mServiceType = "_ms._tcp"; + browser.mSubTypeLabel = "_battery"; + browser.mInfraIfIndex = 2; + browser.mCallback = TestDnssdBrowseCallback; + + otPlatDnssdStartBrowser(instance, &browser); + + browseResult.mServiceType = "_ms._tcp"; + browseResult.mSubTypeLabel = "_battery"; + browseResult.mServiceInstance = "GAT-X105 #1"; + browseResult.mTtl = 12345; + browseResult.mInfraIfIndex = 2; + + SuccessOrQuit(GenerateSpinelDnssdBrowseResultFrame(browseResult, recvBuf, recvLen)); + + ncpBase.HandleReceive(recvBuf, recvLen); + + VerifyOrQuit(sDnssdBrowseCallbackInvoked); +} + } // namespace ot #endif // OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE @@ -187,6 +253,7 @@ int main(void) #if OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE ot::TestNcpDnssdGetState(); ot::TestNcpDnssdRegistrations(); + ot::TestNcpDnssdBrowse(); #endif printf("All tests passed\n"); return 0; diff --git a/tests/unit/test_spinel_prop_codec.cpp b/tests/unit/test_spinel_prop_codec.cpp index 68a17f1a6..c4c64aa25 100644 --- a/tests/unit/test_spinel_prop_codec.cpp +++ b/tests/unit/test_spinel_prop_codec.cpp @@ -41,6 +41,12 @@ static void DnssdFakeCallback(otInstance *aInstance, otPlatDnssdRequestId aReque OT_UNUSED_VARIABLE(aError); } +static void FakeDnssdBrowseCallback(otInstance *aInstance, const otPlatDnssdBrowseResult *aResult) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResult); +} + void TestDnssd(void) { constexpr uint16_t kMaxSpinelBufferSize = 2048; @@ -178,6 +184,75 @@ void TestDnssd(void) VerifyOrQuit(requestId == 3); VerifyOrQuit(callbackDataLen == sizeof(otPlatDnssdRegisterCallback)); VerifyOrQuit(*reinterpret_cast(callbackData) == DnssdFakeCallback); + + // Test Dnssd Browser encoding and decoding + otPlatDnssdBrowser dnssdBrowserEncode; + otPlatDnssdBrowser dnssdBrowserDecode; + + dnssdBrowserEncode.mServiceType = "_meshcop._udp"; + dnssdBrowserEncode.mSubTypeLabel = nullptr; + dnssdBrowserEncode.mInfraIfIndex = 1; + dnssdBrowserEncode.mCallback = FakeDnssdBrowseCallback; + + ncpBuffer.Clear(); + SuccessOrQuit( + error = encoder.BeginFrame(SPINEL_HEADER_FLAG, SPINEL_CMD_PROP_VALUE_INSERTED, SPINEL_PROP_DNSSD_BROWSER)); + SuccessOrQuit(error = EncodeDnssdDiscovery(encoder, dnssdBrowserEncode)); + SuccessOrQuit(error = encoder.EndFrame()); + SuccessOrQuit(ncpBuffer.OutFrameBegin()); + len = ncpBuffer.OutFrameGetLength(); + VerifyOrQuit(ncpBuffer.OutFrameRead(len, buf) == len); + + decoder.Init(buf, len); + SuccessOrQuit(error = decoder.ReadUint8(header)); + VerifyOrQuit(header == SPINEL_HEADER_FLAG); + SuccessOrQuit(error = decoder.ReadUintPacked(command)); + VerifyOrQuit(command == SPINEL_CMD_PROP_VALUE_INSERTED); + SuccessOrQuit(error = decoder.ReadUintPacked(propKey)); + VerifyOrQuit(static_cast(propKey) == SPINEL_PROP_DNSSD_BROWSER); + SuccessOrQuit(error = DecodeDnssdBrowser(decoder, dnssdBrowserDecode, callbackData, callbackDataLen)); + VerifyOrQuit(strcmp(dnssdBrowserDecode.mServiceType, dnssdBrowserEncode.mServiceType) == 0); + VerifyOrQuit(dnssdBrowserDecode.mSubTypeLabel == dnssdBrowserEncode.mSubTypeLabel); + VerifyOrQuit(dnssdBrowserDecode.mInfraIfIndex == dnssdBrowserEncode.mInfraIfIndex); + VerifyOrQuit(callbackDataLen == sizeof(otPlatDnssdBrowseCallback)); + VerifyOrQuit(*reinterpret_cast(callbackData) == FakeDnssdBrowseCallback); + + // Test Dnssd Browser Result encoding and decoding + otPlatDnssdBrowseResult dnssdBrowseResultEncode; + otPlatDnssdBrowseResult dnssdBrowseResultDecode; + + dnssdBrowseResultEncode.mServiceType = "_ms._tcp"; + dnssdBrowseResultEncode.mSubTypeLabel = "_nuclear"; + dnssdBrowseResultEncode.mServiceInstance = "ZGMF-X09A #1"; + dnssdBrowseResultEncode.mTtl = 1999; + dnssdBrowseResultEncode.mInfraIfIndex = 1; + + otPlatDnssdBrowseCallback callback = &FakeDnssdBrowseCallback; + + ncpBuffer.Clear(); + SuccessOrQuit( + error = encoder.BeginFrame(SPINEL_HEADER_FLAG, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_DNSSD_BROWSE_RESULT)); + SuccessOrQuit(error = EncodeDnssdBrowseResult(encoder, dnssdBrowseResultEncode, + reinterpret_cast(&callback), sizeof(callback))); + SuccessOrQuit(error = encoder.EndFrame()); + SuccessOrQuit(ncpBuffer.OutFrameBegin()); + len = ncpBuffer.OutFrameGetLength(); + VerifyOrQuit(ncpBuffer.OutFrameRead(len, buf) == len); + + decoder.Init(buf, len); + SuccessOrQuit(error = decoder.ReadUint8(header)); + VerifyOrQuit(header == SPINEL_HEADER_FLAG); + SuccessOrQuit(error = decoder.ReadUintPacked(command)); + VerifyOrQuit(command == SPINEL_CMD_PROP_VALUE_SET); + SuccessOrQuit(error = decoder.ReadUintPacked(propKey)); + VerifyOrQuit(static_cast(propKey) == SPINEL_PROP_DNSSD_BROWSE_RESULT); + SuccessOrQuit(error = DecodeDnssdBrowseResult(decoder, dnssdBrowseResultDecode, callbackData, callbackDataLen)); + VerifyOrQuit(strcmp(dnssdBrowseResultDecode.mServiceType, dnssdBrowseResultEncode.mServiceType) == 0); + VerifyOrQuit(strcmp(dnssdBrowseResultDecode.mSubTypeLabel, dnssdBrowseResultEncode.mSubTypeLabel) == 0); + VerifyOrQuit(strcmp(dnssdBrowseResultDecode.mServiceInstance, dnssdBrowseResultEncode.mServiceInstance) == 0); + VerifyOrQuit(dnssdBrowseResultDecode.mInfraIfIndex == dnssdBrowseResultEncode.mInfraIfIndex); + VerifyOrQuit(callbackDataLen == sizeof(otPlatDnssdBrowseCallback)); + VerifyOrQuit(*reinterpret_cast(callbackData) == FakeDnssdBrowseCallback); } } // namespace Spinel