From 0f2d158437b49e8fbe42c837d8b68af1a4bebe8d Mon Sep 17 00:00:00 2001 From: Handa Wang Date: Tue, 7 Feb 2023 00:10:30 +0800 Subject: [PATCH] save --- .github/workflows/macOS.yml | 2 +- etc/cmake/options.cmake | 10 + script/test | 3 +- src/CMakeLists.txt | 2 + src/agent/CMakeLists.txt | 1 + src/agent/application.cpp | 6 + src/agent/application.hpp | 4 + src/border_agent/CMakeLists.txt | 1 + src/border_agent/border_agent.cpp | 3 + src/border_agent/border_agent.hpp | 4 + src/dso/CMakeLists.txt | 38 ++ src/dso/dso_transport.cpp | 692 ++++++++++++++++++++++++++ src/dso/dso_transport.hpp | 338 +++++++++++++ src/mdns/mdns.cpp | 12 + src/mdns/mdns.hpp | 11 + src/srpl_dnssd/CMakeLists.txt | 37 ++ src/srpl_dnssd/srpl_dnssd.cpp | 190 +++++++ src/srpl_dnssd/srpl_dnssd.hpp | 122 +++++ tests/unit/CMakeLists.txt | 3 + tests/unit/test_dso_transport.cpp | 364 ++++++++++++++ third_party/openthread/CMakeLists.txt | 6 + 21 files changed, 1847 insertions(+), 2 deletions(-) create mode 100644 src/dso/CMakeLists.txt create mode 100644 src/dso/dso_transport.cpp create mode 100644 src/dso/dso_transport.hpp create mode 100644 src/srpl_dnssd/CMakeLists.txt create mode 100644 src/srpl_dnssd/srpl_dnssd.cpp create mode 100644 src/srpl_dnssd/srpl_dnssd.hpp create mode 100644 tests/unit/test_dso_transport.cpp diff --git a/.github/workflows/macOS.yml b/.github/workflows/macOS.yml index 7e473ac6bfd..57855455681 100644 --- a/.github/workflows/macOS.yml +++ b/.github/workflows/macOS.yml @@ -63,4 +63,4 @@ jobs: brew reinstall boost cmake cpputest dbus jsoncpp ninja protobuf pkg-config - name: Build run: | - OTBR_OPTIONS='-DOTBR_BORDER_AGENT=OFF -DOTBR_MDNS=OFF -DOT_FIREWALL=OFF -DOTBR_DBUS=OFF' ./script/test build + OTBR_OPTIONS='-DOTBR_BORDER_AGENT=OFF -DOTBR_MDNS=OFF -DOT_FIREWALL=OFF -DOTBR_DBUS=OFF -DOTBR_DNS_DSO=OFF' ./script/test build diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 908bf0adc0f..ff36e7d110d 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -116,6 +116,16 @@ if(OTBR_NAT64) target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_NAT64=1) endif() +option(OTBR_DNS_DSO "Enable DSO" OFF) +if(OTBR_DNS_DSO) + target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_DNS_DSO=1) +endif() + +option(OTBR_SRP_REPLICATION "Enable SRP Replication" OFF) +if(OTBR_SRP_REPLICATION) + target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_SRP_REPLICATION=1) +endif() + option(OTBR_VENDOR_INFRA_LINK_SELECT "Enable Vendor-specific infrastructure link selection rules" OFF) if(OTBR_VENDOR_INFRA_LINK_SELECT) target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_VENDOR_INFRA_LINK_SELECT=1) diff --git a/script/test b/script/test index a3f69489a54..9f401ec6f27 100755 --- a/script/test +++ b/script/test @@ -132,6 +132,7 @@ do_build() "-DOTBR_WEB=ON" "-DOTBR_UNSECURE_JOIN=ON" "-DOTBR_TREL=ON" + "-DOTBR_DNS_DSO=ON" ${otbr_options[@]+"${otbr_options[@]}"} ) @@ -148,7 +149,7 @@ do_check() { (cd "${OTBR_TOP_BUILDDIR}" \ && ninja && sudo ninja install \ - && CTEST_OUTPUT_ON_FAILURE=1 ninja test) + && CTEST_OUTPUT_ON_FAILURE=1 sudo ninja test) } do_doxygen() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ba8f05d9363..219d87b6071 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,8 +34,10 @@ add_subdirectory(common) if(OTBR_FEATURE_FLAGS) add_subdirectory(proto) endif() +add_subdirectory(dso) add_subdirectory(ncp) add_subdirectory(sdp_proxy) +add_subdirectory(srpl_dnssd) add_subdirectory(trel_dnssd) if(OTBR_DBUS) diff --git a/src/agent/CMakeLists.txt b/src/agent/CMakeLists.txt index c16a5e5e820..a323ffd84c7 100644 --- a/src/agent/CMakeLists.txt +++ b/src/agent/CMakeLists.txt @@ -45,6 +45,7 @@ target_link_libraries(otbr-agent PRIVATE openthread-ftd openthread-spinel-rcp openthread-hdlc + otbr-dso otbr-sdp-proxy otbr-ncp otbr-common diff --git a/src/agent/application.cpp b/src/agent/application.cpp index ad834303d0b..88fef51c559 100644 --- a/src/agent/application.cpp +++ b/src/agent/application.cpp @@ -78,6 +78,9 @@ Application::Application(const std::string &aInterfaceName, #if OTBR_ENABLE_VENDOR_SERVER , mVendorServer(mNcp) #endif +#if OTBR_ENABLE_DNS_DSO + , mDsoAgent() +#endif { OTBR_UNUSED_VARIABLE(aRestListenAddress); } @@ -104,6 +107,9 @@ void Application::Init(void) #if OTBR_ENABLE_VENDOR_SERVER mVendorServer.Init(); #endif +#if OTBR_ENABLE_DNS_DSO + mDsoAgent.Init(mNcp.GetInstance(), mBackboneInterfaceName); +#endif } void Application::Deinit(void) diff --git a/src/agent/application.hpp b/src/agent/application.hpp index 5c9e2163557..6bd75e244f0 100644 --- a/src/agent/application.hpp +++ b/src/agent/application.hpp @@ -58,6 +58,7 @@ #if OTBR_ENABLE_VENDOR_SERVER #include "agent/vendor.hpp" #endif +#include "dso/dso_transport.hpp" #include "utils/infra_link_selector.hpp" namespace otbr { @@ -144,6 +145,9 @@ class Application : private NonCopyable #if OTBR_ENABLE_VENDOR_SERVER vendor::VendorServer mVendorServer; #endif +#if OTBR_ENABLE_DNS_DSO + Dso::DsoAgent mDsoAgent; +#endif static std::atomic_bool sShouldTerminate; }; diff --git a/src/border_agent/CMakeLists.txt b/src/border_agent/CMakeLists.txt index c58acdbc46f..cb2f38e9fe5 100644 --- a/src/border_agent/CMakeLists.txt +++ b/src/border_agent/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(otbr-border-agent target_link_libraries(otbr-border-agent PRIVATE $<$:otbr-mdns> $<$:otbr-backbone-router> + otbr-srpl-dnssd otbr-trel-dnssd otbr-common ) diff --git a/src/border_agent/border_agent.cpp b/src/border_agent/border_agent.cpp index 382b5597cb5..5c402f8ce37 100644 --- a/src/border_agent/border_agent.cpp +++ b/src/border_agent/border_agent.cpp @@ -149,6 +149,9 @@ BorderAgent::BorderAgent(otbr::Ncp::ControllerOpenThread &aNcp) #if OTBR_ENABLE_TREL , mTrelDnssd(aNcp, *mPublisher) #endif +#if OTBR_ENABLE_SRP_REPLICATION + , mSrplDnssd(aNcp, *mPublisher) +#endif { } diff --git a/src/border_agent/border_agent.hpp b/src/border_agent/border_agent.hpp index 896281e6db5..c76f3ad6c4a 100644 --- a/src/border_agent/border_agent.hpp +++ b/src/border_agent/border_agent.hpp @@ -49,6 +49,7 @@ #include "ncp/ncp_openthread.hpp" #include "sdp_proxy/advertising_proxy.hpp" #include "sdp_proxy/discovery_proxy.hpp" +#include "srpl_dnssd/srpl_dnssd.hpp" #include "trel_dnssd/trel_dnssd.hpp" #ifndef OTBR_VENDOR_NAME @@ -144,6 +145,9 @@ class BorderAgent : private NonCopyable #if OTBR_ENABLE_TREL TrelDnssd::TrelDnssd mTrelDnssd; #endif +#if OTBR_ENABLE_SRP_REPLICATION + SrplDnssd::SrplDnssd mSrplDnssd; +#endif std::string mServiceInstanceName; }; diff --git a/src/dso/CMakeLists.txt b/src/dso/CMakeLists.txt new file mode 100644 index 00000000000..843034eeec8 --- /dev/null +++ b/src/dso/CMakeLists.txt @@ -0,0 +1,38 @@ +# +# 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. +# + +add_library(otbr-dso + dso_transport.cpp + dso_transport.hpp + ) +target_link_libraries(otbr-dso + PUBLIC + mbedtls + otbr-config + otbr-common +) diff --git a/src/dso/dso_transport.cpp b/src/dso/dso_transport.cpp new file mode 100644 index 00000000000..4d389e72b0f --- /dev/null +++ b/src/dso/dso_transport.cpp @@ -0,0 +1,692 @@ +/* + * 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. + */ + +#if OTBR_ENABLE_DNS_DSO + +#define OTBR_LOG_TAG "DSO" + +#include "dso_transport.hpp" + +#include +#include +#include +#include +#include + +#include "mbedtls/net_sockets.h" +#include "openthread/openthread-system.h" +#include "openthread/platform/dso_transport.h" + +static otbr::Dso::DsoAgent *sDsoAgent = nullptr; + +extern "C" void otPlatDsoEnableListening(otInstance *aInstance, bool aEnabled) +{ + sDsoAgent->SetEnabled(aInstance, aEnabled); +} + +extern "C" void otPlatDsoConnect(otPlatDsoConnection *aConnection, const otSockAddr *aPeerSockAddr) +{ + sDsoAgent->FindOrCreateConnection(aConnection)->Connect(aPeerSockAddr); +} + +extern "C" void otPlatDsoSend(otPlatDsoConnection *aConnection, otMessage *aMessage) +{ + auto conn = sDsoAgent->FindConnection(aConnection); + + VerifyOrExit(conn != nullptr); + conn->Send(aMessage); + +exit: + otMessageFree(aMessage); +} + +extern "C" void otPlatDsoDisconnect(otPlatDsoConnection *aConnection, otPlatDsoDisconnectMode aMode) +{ + auto conn = sDsoAgent->FindConnection(aConnection); + + VerifyOrExit(conn != nullptr); + conn->Disconnect(aMode); + + sDsoAgent->RemoveConnection(aConnection); + +exit: + return; +} + +namespace otbr { +namespace Dso { + +DsoAgent::DsoAgent(void) +{ + signal(SIGPIPE, SIG_IGN); + mbedtls_net_init(&mListeningCtx); + sDsoAgent = this; +} + +void DsoAgent::Init(otInstance *aInstance, const std::string &aInfraNetIfName) +{ + assert(mInstance == nullptr); + + mInstance = aInstance; + mInfraNetIfName = aInfraNetIfName; +} + +DsoAgent::DsoConnection *DsoAgent::FindConnection(otPlatDsoConnection *aConnection) +{ + DsoConnection *ret = nullptr; + auto it = mMap.find(aConnection); + + if (it != mMap.end()) + { + ret = it->second.get(); + } + + return ret; +} + +DsoAgent::DsoConnection *DsoAgent::FindOrCreateConnection(otPlatDsoConnection *aConnection) +{ + auto &ret = mMap[aConnection]; + + if (!ret) + { + ret = MakeUnique(this, aConnection); + } + + return ret.get(); +} + +DsoAgent::DsoConnection *DsoAgent::FindOrCreateConnection(otPlatDsoConnection *aConnection, mbedtls_net_context aCtx) +{ + auto &ret = mMap[aConnection]; + + if (!ret) + { + ret = MakeUnique(this, aConnection, aCtx); + } + + return ret.get(); +} + +void DsoAgent::Enable(otInstance *aInstance) +{ + OTBR_UNUSED_VARIABLE(aInstance); + + assert(aInstance == mInstance); + + constexpr int kOne = 1; + sockaddr_in6 sockAddr; + + VerifyOrExit(!mListeningEnabled); + + mListeningCtx.fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + + VerifyOrExit( + !setsockopt(mListeningCtx.fd, SOL_SOCKET, SO_BINDTODEVICE, mInfraNetIfName.c_str(), mInfraNetIfName.size())); + VerifyOrExit(!setsockopt(mListeningCtx.fd, SOL_SOCKET, SO_REUSEADDR, (const uint8_t *)&kOne, sizeof(kOne))); + VerifyOrExit(!setsockopt(mListeningCtx.fd, SOL_SOCKET, SO_REUSEPORT, (const uint8_t *)&kOne, sizeof(kOne))); + + sockAddr.sin6_family = AF_INET6; + sockAddr.sin6_addr = in6addr_any; + sockAddr.sin6_port = htons(kListeningPort); + VerifyOrExit(!bind(mListeningCtx.fd, reinterpret_cast(&sockAddr), sizeof(sockAddr))); + VerifyOrExit(!mbedtls_net_set_nonblock(&mListeningCtx), otbrLogWarning("Failed to set non-blocking: %d", errno)); + VerifyOrExit(!listen(mListeningCtx.fd, kMaxQueuedConnections)); + + mListeningEnabled = true; + + otbrLogInfo("DSO socket starts listening"); + +exit: + return; +} + +void DsoAgent::Disable(otInstance *aInstance) +{ + OTBR_UNUSED_VARIABLE(aInstance); + + assert(aInstance == mInstance); + + VerifyOrExit(mListeningEnabled); + + mbedtls_net_close(&mListeningCtx); + mbedtls_net_free(&mListeningCtx); + mMap.clear(); + mListeningEnabled = false; + +exit: + return; +} + +void DsoAgent::SetEnabled(otInstance *aInstance, bool aEnabled) +{ + if (aEnabled) + { + Enable(aInstance); + } + else + { + Disable(aInstance); + } +} + +void DsoAgent::RemoveConnection(otPlatDsoConnection *aConnection) +{ + mMap.erase(aConnection); +} + +void DsoAgent::SetHandlers(AcceptHandler aAcceptHandler, + ConnectedHandler aConnectedHandler, + DisconnectedHandler aDisconnectedHandler, + ReceiveHandler aReceiveHandler) +{ + mAcceptHandler = std::move(aAcceptHandler); + mConnectedHandler = std::move(aConnectedHandler); + mDisconnectedHandler = std::move(aDisconnectedHandler); + mReceiveHandler = std::move(aReceiveHandler); +} + +const char *DsoAgent::DsoConnection::StateToString(State aState) +{ + const char *ret = ""; + + switch (aState) + { + case State::kDisabled: + ret = "Disabled"; + break; + case State::kConnecting: + ret = "Connecting"; + break; + case State::kConnected: + ret = "Connected"; + break; + } + + return ret; +} + +void DsoAgent::DsoConnection::Connect(const otSockAddr *aPeerSockAddr) +{ + int ret; + char addrBuf[OT_IP6_ADDRESS_STRING_SIZE]; + std::string portString; + struct sockaddr_in6 sockAddrIn6; + + VerifyOrExit(mState == State::kDisabled); + + mbedtls_net_init(&mCtx); + mPeerSockAddr = *aPeerSockAddr; + portString = std::to_string(aPeerSockAddr->mPort); + otIp6AddressToString(&aPeerSockAddr->mAddress, addrBuf, sizeof(addrBuf)); + + otbrLogInfo("Connecting to [%s]:%s", addrBuf, portString.c_str()); + + mCtx.fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + VerifyOrExit(mCtx.fd > 0, otbrLogWarning("Failed to create a socket: %d", errno)); + VerifyOrExit(!mbedtls_net_set_nonblock(&mCtx), otbrLogWarning("Failed to set non-blocking: %d", errno)); + + memset(&sockAddrIn6, 0, sizeof(sockAddrIn6)); + memcpy(&sockAddrIn6.sin6_addr, aPeerSockAddr->mAddress.mFields.m8, sizeof(sockAddrIn6.sin6_addr)); + sockAddrIn6.sin6_family = AF_INET6; + sockAddrIn6.sin6_port = htons(aPeerSockAddr->mPort); + ret = connect(mCtx.fd, reinterpret_cast(&sockAddrIn6), sizeof(sockAddrIn6)); + + otbrLogInfo("Connecting to [%s]:%s fd=%d", addrBuf, portString.c_str(), mCtx.fd); + + if (!ret) + { + otbrLogInfo("Connected [%s]:%s", addrBuf, portString.c_str()); + MarkStateAs(State::kConnected); + mAgent->HandleConnected(this); + } + else if (errno == EAGAIN || errno == EINPROGRESS) + { + MarkStateAs(State::kConnecting); + } + +exit: + if (mState == State::kDisabled) + { + mAgent->HandleDisconnected(this, OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT); + } +} + +void DsoAgent::DsoConnection::Disconnect(otPlatDsoDisconnectMode aMode) +{ + struct linger l; + + VerifyOrExit(mState != State::kDisabled); + + switch (aMode) + { + case OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT: + l.l_onoff = 1; + l.l_linger = 0; + setsockopt(mCtx.fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); + break; + case OT_PLAT_DSO_DISCONNECT_MODE_GRACEFULLY_CLOSE: + break; + default: + otbrLogWarning("Unknown disconnection mode: %d", aMode); + break; + } + + mbedtls_net_close(&mCtx); + mbedtls_net_free(&mCtx); + MarkStateAs(State::kDisabled); + +exit: + return; +} + +void DsoAgent::DsoConnection::Send(otMessage *aMessage) +{ + uint16_t length = otMessageGetLength(aMessage); + std::vector buf(length); + + VerifyOrExit(length == otMessageRead(aMessage, 0, buf.data(), length), + otbrLogWarning("Failed to read message data")); + Send(buf); + +exit: + return; +} + +void DsoAgent::DsoConnection::Send(const std::vector &aData) +{ + uint16_t lengthInBigEndian = htons(aData.size()); + + VerifyOrExit(mState == State::kConnected); + + otbrLogDebug("Sending a message with length %zu", aData.size()); + + mSendMessageBuffer.resize(mSendMessageBuffer.size() + sizeof(lengthInBigEndian)); + *reinterpret_cast(&mSendMessageBuffer[mSendMessageBuffer.size() - sizeof(lengthInBigEndian)]) = + lengthInBigEndian; + std::copy(aData.begin(), aData.end(), std::back_inserter(mSendMessageBuffer)); + +exit: + return; +} + +void DsoAgent::DsoConnection::HandleReceive(void) +{ + int readLen, totalReadLen = 0; + uint8_t buf[kRxBufferSize]; + + VerifyOrExit(mState == State::kConnected); + + while (true) + { + size_t wantReadLen = mNeedBytes ? mNeedBytes : (sizeof(uint16_t) - mReceiveLengthBuffer.size()); + + readLen = mbedtls_net_recv(&mCtx, buf, std::min(sizeof(buf), wantReadLen)); + + assert(readLen <= static_cast(std::min(sizeof(buf), wantReadLen))); + + if (readLen <= 0) + { + HandleMbedTlsError(readLen); + VerifyOrExit(readLen != 0); + VerifyOrExit(readLen != MBEDTLS_ERR_SSL_WANT_READ && readLen != MBEDTLS_ERR_SSL_WANT_WRITE); + otbrLogWarning("Failed to receive message: %d", readLen); + ExitNow(); + } + + totalReadLen += readLen; + + if (mNeedBytes) + { + std::copy(buf, buf + readLen, std::back_inserter(mReceiveMessageBuffer)); + mNeedBytes -= readLen; + if (!mNeedBytes) + { + mAgent->HandleReceive(this, mReceiveMessageBuffer); + mReceiveMessageBuffer.clear(); + } + } + else + { + assert(mReceiveLengthBuffer.size() < sizeof(uint16_t)); + assert(mReceiveMessageBuffer.empty()); + + std::copy(buf, buf + readLen, std::back_inserter(mReceiveLengthBuffer)); + if (mReceiveLengthBuffer.size() == sizeof(uint16_t)) + { + mNeedBytes = mReceiveLengthBuffer[0] << 8 | mReceiveLengthBuffer[1]; + mReceiveLengthBuffer.clear(); + } + } + } + +exit: + if (!totalReadLen && mState != State::kDisabled) + { + MarkStateAs(State::kDisabled); + mAgent->HandleDisconnected(this, OT_PLAT_DSO_DISCONNECT_MODE_GRACEFULLY_CLOSE); + } + + return; +} + +void DsoAgent::DsoConnection::HandleMbedTlsError(int aError) +{ + VerifyOrExit(aError < 0); + + switch (aError) + { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + MarkStateAs(State::kDisabled); + mAgent->HandleDisconnected(this, OT_PLAT_DSO_DISCONNECT_MODE_GRACEFULLY_CLOSE); + break; + case MBEDTLS_ERR_NET_CONN_RESET: + MarkStateAs(State::kDisabled); + mAgent->HandleDisconnected(this, OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT); + break; + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + break; + default: + MarkStateAs(State::kDisabled); + mAgent->HandleDisconnected(this, OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT); + break; + } + +exit: + return; +} + +void DsoAgent::DsoConnection::UpdateStateBySocketState(void) +{ + int optVal = 0; + socklen_t optLen = sizeof(optVal); + + if (!getsockopt(GetFd(), SOL_SOCKET, SO_ERROR, &optVal, &optLen)) + { + if (!optVal) + { + MarkStateAs(State::kConnected); + mAgent->HandleConnected(this); + } + else + { + MarkStateAs(State::kDisabled); + mAgent->HandleDisconnected(this, OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT); + } + } + else + { + otbrLogWarning("Failed to query socket status: %d Fd = %d", errno, GetFd()); + MarkStateAs(State::kDisabled); + mAgent->HandleDisconnected(this, OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT); + } +} + +void DsoAgent::DsoConnection::MarkStateAs(State aState) +{ + VerifyOrExit(mState != aState); + + otbrLogInfo("Connection state changed: %s -> %s", StateToString(mState), StateToString(aState)); + mState = aState; + +exit: + return; +} + +void DsoAgent::DsoConnection::FlushSendBuffer(void) +{ + int writeLen = mbedtls_net_send(&mCtx, mSendMessageBuffer.data(), mSendMessageBuffer.size()); + + VerifyOrExit(mState == State::kConnected); + + if (writeLen < 0) + { + otbrLogWarning("Failed to send DSO message: %d", writeLen); + HandleMbedTlsError(writeLen); + } + else + { + mSendMessageBuffer.erase(mSendMessageBuffer.begin(), mSendMessageBuffer.begin() + writeLen); + } + +exit: + return; +} + +void DsoAgent::Update(MainloopContext &aMainloop) +{ + if (mListeningEnabled) + { + FD_SET(mListeningCtx.fd, &aMainloop.mReadFdSet); + aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, mListeningCtx.fd); + } + + for (const auto &pair : mMap) + { + switch (pair.second->GetState()) + { + case DsoConnection::State::kDisabled: + break; + case DsoConnection::State::kConnecting: + FD_SET(pair.second->GetFd(), &aMainloop.mWriteFdSet); + aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, pair.second->GetFd()); + break; + case DsoConnection::State::kConnected: + FD_SET(pair.second->GetFd(), &aMainloop.mReadFdSet); + if (!pair.second->mSendMessageBuffer.empty()) + { + FD_SET(pair.second->GetFd(), &aMainloop.mWriteFdSet); + } + aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, pair.second->GetFd()); + break; + default: + break; + } + } +} + +void DsoAgent::Process(const MainloopContext &aMainloop) +{ + if (FD_ISSET(mListeningCtx.fd, &aMainloop.mReadFdSet)) + { + ProcessIncomingConnections(); + } + ProcessConnections(aMainloop); +} + +void DsoAgent::ProcessIncomingConnections() +{ + mbedtls_net_context incomingCtx; + uint8_t address[sizeof(sockaddr_in6)]; + size_t len; + int ret = 0; + + VerifyOrExit(mListeningEnabled); + + while (!(ret = mbedtls_net_accept(&mListeningCtx, &incomingCtx, &address, sizeof(address), &len))) + { + ProcessIncomingConnection(incomingCtx, address, len); + } + + if (ret != MBEDTLS_ERR_SSL_WANT_READ) + { + otbrLogWarning("Failed to accept incoming connection: %d", ret); + } + +exit: + + return; +} + +void DsoAgent::ProcessIncomingConnection(mbedtls_net_context aCtx, uint8_t *aAddress, size_t aAddressLength) +{ + otSockAddr sockAddr; + otPlatDsoConnection *conn; + in6_addr *addrIn6 = nullptr; + bool successful = false; + + VerifyOrExit(!mbedtls_net_set_nonblock(&aCtx), + otbrLogWarning("Failed to set the socket as non-blocking: %d", errno)); + + // TODO: support IPv4 + if (aAddressLength == OT_IP6_ADDRESS_SIZE) + { + Ip6Address address; + + addrIn6 = reinterpret_cast(aAddress); + memcpy(&sockAddr.mAddress.mFields.m8, addrIn6, aAddressLength); + sockAddr.mPort = 0; // Mbed TLS doesn't provide the client's port number. + + address.CopyFrom(*addrIn6); + otbrLogInfo("Receiving connection from %s", address.ToString().c_str()); + } + else + { + otbrLogInfo("Unsupported address length: %zu", aAddressLength); + ExitNow(); + } + + conn = HandleAccept(mInstance, &sockAddr); + + VerifyOrExit(conn != nullptr, otbrLogInfo("Failed to accept connection")); + + HandleConnected(FindOrCreateConnection(conn, aCtx)); + successful = true; + +exit: + if (!successful) + { + mbedtls_net_close(&aCtx); + } +} + +void DsoAgent::ProcessConnections(const MainloopContext &aMainloop) +{ + std::vector connections; + + connections.reserve(mMap.size()); + for (auto &conn : mMap) + { + connections.push_back(conn.second.get()); + } + for (const auto &conn : connections) + { + switch (conn->GetState()) + { + case DsoConnection::State::kDisabled: + RemoveConnection(conn->GetOtPlatDsoConnection()); + break; + case DsoConnection::State::kConnecting: + if (FD_ISSET(conn->GetFd(), &aMainloop.mWriteFdSet)) + { + conn->UpdateStateBySocketState(); + } + break; + case DsoConnection::State::kConnected: + if (FD_ISSET(conn->GetFd(), &aMainloop.mReadFdSet)) + { + conn->HandleReceive(); + } + if (FD_ISSET(conn->GetFd(), &aMainloop.mWriteFdSet)) + { + conn->FlushSendBuffer(); + } + break; + default: + break; + } + } +} + +otPlatDsoConnection *DsoAgent::HandleAccept(otInstance *aInstance, otSockAddr *aSockAddr) +{ + assert(mAcceptHandler != nullptr); + + return mAcceptHandler(aInstance, aSockAddr); +} + +void DsoAgent::HandleConnected(DsoConnection *aConnection) +{ + if (mConnectedHandler) + { + mConnectedHandler(aConnection); + } +} + +void DsoAgent::HandleDisconnected(DsoConnection *aConnection, otPlatDsoDisconnectMode aMode) +{ + if (mDisconnectedHandler) + { + mDisconnectedHandler(aConnection, aMode); + } +} + +void DsoAgent::HandleReceive(DsoConnection *aConnection, const std::vector &aData) +{ + if (mReceiveHandler) + { + mReceiveHandler(aConnection, aData); + } +} + +otPlatDsoConnection *DsoAgent::DefaultAcceptHandler(otInstance *aInstance, otSockAddr *aSockAddr) +{ + return otPlatDsoAccept(aInstance, aSockAddr); +} + +void DsoAgent::DefaultConnectedHandler(DsoConnection *aConnection) +{ + otPlatDsoHandleConnected(aConnection->GetOtPlatDsoConnection()); +} + +void DsoAgent::DefaultDisconnectedHandler(DsoConnection *aConnection, otPlatDsoDisconnectMode aMode) +{ + otPlatDsoHandleDisconnected(aConnection->GetOtPlatDsoConnection(), aMode); +} + +void DsoAgent::DefaultReceiveHandler(DsoConnection *aConnection, const std::vector &aData) +{ + otError error; + otMessage *message = otIp6NewMessage(aConnection->mAgent->mInstance, nullptr); + + VerifyOrExit(message != nullptr, otbrLogWarning("Failed to allocate message buffer")); + error = otMessageAppend(message, aData.data(), aData.size()); + VerifyOrExit(error == OT_ERROR_NONE, + otbrLogWarning("Failed to construct message: %s", otThreadErrorToString(error))); + + otPlatDsoHandleReceive(aConnection->GetOtPlatDsoConnection(), message); + +exit: + return; +} + +} // namespace Dso +} // namespace otbr + +#endif diff --git a/src/dso/dso_transport.hpp b/src/dso/dso_transport.hpp new file mode 100644 index 00000000000..065436d2b8b --- /dev/null +++ b/src/dso/dso_transport.hpp @@ -0,0 +1,338 @@ +/* + * 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. + */ + +/** + * @file + * This file includes definitions for DSO agent. + */ + +#ifndef OTBR_AGENT_DSO_TRANSPORT_HPP_ +#define OTBR_AGENT_DSO_TRANSPORT_HPP_ + +#if OTBR_ENABLE_DNS_DSO + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common/code_utils.hpp" +#include "common/mainloop.hpp" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" +#include "mbedtls/entropy.h" +#include "mbedtls/error.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/ssl.h" +#include "ncp/ncp_openthread.hpp" +#include "openthread/logging.h" +#include "openthread/message.h" +#include "openthread/platform/dso_transport.h" + +class DsoTestFixture; + +namespace otbr { +namespace Dso { + +// TODO: Support DNS-over-TLS +/** + * @addtogroup border-router-dso + * + * @brief + * This module includes definitions for DSO agent. + * + * @{ + */ + +class DsoAgent : public MainloopProcessor +{ +private: + class DsoConnection; + + friend class ::DsoTestFixture; + +public: + using AcceptHandler = std::function; + using ConnectedHandler = std::function; + using DisconnectedHandler = std::function; + using ReceiveHandler = std::function &)>; + + /** + * This constructor initializes the DsoAgent instance. + * + */ + explicit DsoAgent(void); + + /** + * This method initializes the DsoAgent instance. + * + * @param[in] aInstance The OT instance. + * + */ + void Init(otInstance *aInstance, const std::string &aInfraNetIfName); + + /** + * This method enables listening for DsoAgent. + * + * @param[in] aInstance The OT instance. + * + */ + void Enable(otInstance *aInstance); + + /** + * This method disables listening for DsoAgent. + * + * @param[in] aInstance The OT instance. + * + */ + void Disable(otInstance *aInstance); + + /** + * This method enables/disables listening for DsoAgent. + * + * @param[in] aInstance The OT instance. + * @param[in] aEnabled A boolean indicating whether to enable listening. + * + */ + void SetEnabled(otInstance *aInstance, bool aEnabled); + + /** + * This method finds the DsoConnection corresponding to the given otPlatDsoConnection. + * + * @param[in] aConnection A pointer to the otPlatDsoConnection. + * + * @returns A pointer to the matching DsoConnection object. + * + */ + DsoConnection *FindConnection(otPlatDsoConnection *aConnection); + + /** + * This method finds the DsoConnection corresponding to the given otPlatDsoConnection. If the DsoConnection doesn't + * exist, it creates a new one. + * + * @param[in] aConnection A pointer to the otPlatDsoConnection. + * + * @returns A pointer to the matching DsoConnection object. + * + */ + DsoConnection *FindOrCreateConnection(otPlatDsoConnection *aConnection); + + /** + * This method finds the DsoConnection corresponding to the given otPlatDsoConnection. If the DsoConnection doesn't + * exist, it creates a new one. + * + * @param[in] aConnection A pointer to the otPlatDsoConnection. + * @param[in] aCtx A mbedtls_net_context object representing the platform connection. This is used for + * creating the DsoConnection. + * + * @returns A pointer to the matching DsoConnection object. + * + */ + DsoConnection *FindOrCreateConnection(otPlatDsoConnection *aConnection, mbedtls_net_context aCtx); + + /** + * This method removes and destroys the DsoConnection corresponding to the given otPlatDsoConnection. + * + * @param aConnection A pointer to the otPlatDsoConnection. + * + */ + void RemoveConnection(otPlatDsoConnection *aConnection); + + /** + * This method sets the handlers for each event. This method can decouple DsoAgent from OT API so that we can + * implement unit tests for DsoAgent. + * + * @param aAcceptHandler The handler is called when a DsoConnection gets accepted. + * @param aConnectedHandler The handler is called when a DsoConnection gets connected. + * @param aDisconnectedHandler The handler is called when a DsoConnection is disconnected. + * @param aReceiveHandler The handler is called when a DsoConnection receives a DSO message. + * + */ + void SetHandlers(AcceptHandler aAcceptHandler, + ConnectedHandler aConnectedHandler, + DisconnectedHandler aDisconnectedHandler, + ReceiveHandler aReceiveHandler); + + /** + * This function is the default AcceptHandler for DsoAgent. It basically calls otPlatDsoAccept(). + * + * @param aInstance A pointer to the OT instance. + * @param aSockAddr A pointer to the peer socket address. + * + * @returns otPlatDsoConnection* A pointer to the otPlatDsoConnection. + * + */ + static otPlatDsoConnection *DefaultAcceptHandler(otInstance *aInstance, otSockAddr *aSockAddr); + + /** + * This function is the default ConnectedHandler for DsoAgent. It basically calls otPlatDsoHandleConnected(). + * + * @param aConnection A pointer to the DsoConnection. + * + */ + static void DefaultConnectedHandler(DsoConnection *aConnection); + + /** + * This function is the default DisconnectedHandler for DsoAgent. It basically calls otPlatDsoHandleDisconnected(). + * + * @param aConnection A pointer to the DsoConnection. + * @param aMode The disconnection mode. + * + */ + static void DefaultDisconnectedHandler(DsoConnection *aConnection, otPlatDsoDisconnectMode aMode); + + /** + * This function is the default ReceiveHandler for DsoAgent. It basically calls otPlatHandleReceive(). + * + * @param aConnection A pointer to the DsoConnection. + * @param aData A vector of bytes representing the received data. + * + */ + static void DefaultReceiveHandler(DsoConnection *aConnection, const std::vector &aData); + +private: + class DsoConnection : NonCopyable + { + public: + enum class State + { + kDisabled, + kConnecting, + kConnected + }; + + explicit DsoConnection(DsoAgent *aAgent, otPlatDsoConnection *aConnection) + : mAgent(aAgent) + , mConnection(aConnection) + , mCtx() + , mState(State::kDisabled) + { + } + + DsoConnection(DsoAgent *aAgent, otPlatDsoConnection *aConnection, mbedtls_net_context aCtx) + : mAgent(aAgent) + , mConnection(aConnection) + , mCtx(aCtx) + , mState(State::kConnected) + { + } + + ~DsoConnection(void) + { + Disconnect(OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT); + mbedtls_net_free(&mCtx); + } + + static const char *StateToString(State aState); + + State GetState(void) const { return mState; } + + int GetFd(void) const { return mCtx.fd; } + + otPlatDsoConnection *GetOtPlatDsoConnection(void) const { return mConnection; } + + void Connect(const otSockAddr *aPeerSockAddr); + + void Disconnect(otPlatDsoDisconnectMode aMode); + + void Send(const std::vector &aData); + + void Send(otMessage *aMessage); + + void HandleReceive(void); + + void HandleMbedTlsError(int aError); // Returns true if the connection is still alive, false otherwise. + + void UpdateStateBySocketState(void); // Update the state according to socket's state. + + void MarkStateAs(State aState); + + private: + static constexpr size_t kRxBufferSize = 512; + + friend class ::DsoTestFixture; + friend class DsoAgent; + + void FlushSendBuffer(void); + + DsoAgent *mAgent; + otPlatDsoConnection *mConnection; + otSockAddr mPeerSockAddr{}; + size_t mNeedBytes = 0; + std::vector mReceiveLengthBuffer; + std::vector mReceiveMessageBuffer; + std::vector mSendMessageBuffer; + mbedtls_net_context mCtx; + State mState; + }; + + static constexpr uint16_t kListeningPort = 853; + static constexpr int kMaxQueuedConnections = 10; + + friend class DsoConnection; + + void Update(MainloopContext &aMainloop) override; + void Process(const MainloopContext &aMainloop) override; + + void ProcessIncomingConnections(void); + void ProcessIncomingConnection(mbedtls_net_context aCtx, uint8_t *aAddress, size_t aAddressLength); + void ProcessConnections(const MainloopContext &aMainloop); + + otPlatDsoConnection *HandleAccept(otInstance *aInstance, otSockAddr *aSockAddr); + void HandleConnected(DsoConnection *aConnection); + void HandleDisconnected(DsoConnection *aConnection, otPlatDsoDisconnectMode aMode); + void HandleReceive(DsoConnection *aConnection, const std::vector &aData); + + otInstance *mInstance = nullptr; + std::string mInfraNetIfName; + bool mListeningEnabled = false; + mbedtls_net_context mListeningCtx; + + AcceptHandler mAcceptHandler{&DsoAgent::DefaultAcceptHandler}; + ConnectedHandler mConnectedHandler{&DsoAgent::DefaultConnectedHandler}; + DisconnectedHandler mDisconnectedHandler{&DsoAgent::DefaultDisconnectedHandler}; + ReceiveHandler mReceiveHandler{&DsoAgent::DefaultReceiveHandler}; + + std::map> mMap; +}; + +/** + * @} + */ + +} // namespace Dso +} // namespace otbr + +#endif // OTBR_ENABLE_DNS_DSO + +#endif // OTBR_AGENT_DSO_TRANSPORT_HPP_ diff --git a/src/mdns/mdns.cpp b/src/mdns/mdns.cpp index 9646d5513c8..bd18073fab8 100644 --- a/src/mdns/mdns.cpp +++ b/src/mdns/mdns.cpp @@ -576,6 +576,18 @@ void Publisher::UpdateHostResolutionEmaLatency(const std::string &aHostName, otb } } +const Publisher::ServiceRegistration *Publisher::FindServiceRegistrationByType(const std::string &aType) const +{ + for (const auto &serviceReg : mServiceRegistrations) + { + if (serviceReg.second->mType == aType) + { + return serviceReg.second.get(); + } + } + return nullptr; +} + } // namespace Mdns } // namespace otbr diff --git a/src/mdns/mdns.hpp b/src/mdns/mdns.hpp index a06578a3870..b6134d007e4 100644 --- a/src/mdns/mdns.hpp +++ b/src/mdns/mdns.hpp @@ -563,6 +563,17 @@ class Publisher : private NonCopyable std::map mHostResolutionBeginTime; otbr::MdnsTelemetryInfo mTelemetryInfo{}; + +public: + /** + * Find the service registration of the given type. + * + * @param aType The service type. + * @returns A pointer to the service registration of the given type. If no service registration is found, + * returns a nullptr. + * + */ + const ServiceRegistration *FindServiceRegistrationByType(const std::string &aType) const; }; /** diff --git a/src/srpl_dnssd/CMakeLists.txt b/src/srpl_dnssd/CMakeLists.txt new file mode 100644 index 00000000000..cc8b0a4a98d --- /dev/null +++ b/src/srpl_dnssd/CMakeLists.txt @@ -0,0 +1,37 @@ +# +# 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. +# + +add_library(otbr-srpl-dnssd + srpl_dnssd.cpp + srpl_dnssd.hpp + ) + +target_link_libraries(otbr-srpl-dnssd PRIVATE + $<$:otbr-mdns> + otbr-common + ) diff --git a/src/srpl_dnssd/srpl_dnssd.cpp b/src/srpl_dnssd/srpl_dnssd.cpp new file mode 100644 index 00000000000..ef143ded02f --- /dev/null +++ b/src/srpl_dnssd/srpl_dnssd.cpp @@ -0,0 +1,190 @@ +/* + * 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. + */ + +/** + * @file + * This file includes implementation of SRPL DNS-SD over mDNS. + */ + +#if OTBR_ENABLE_SRP_REPLICATION + +#define OTBR_LOG_TAG "SrplDns" + +#include "srpl_dnssd/srpl_dnssd.hpp" + +#include "openthread/platform/srp_replication.h" + +#include "mdns/mdns.hpp" +#include "openthread/ip6.h" +#include "utils/string_utils.hpp" + +static otbr::SrplDnssd::SrplDnssd *sSrplDnssd = nullptr; + +extern "C" void otPlatSrplDnssdBrowse(otInstance *aInstance, bool aEnable) +{ + OTBR_UNUSED_VARIABLE(aInstance); + + if (aEnable) + { + sSrplDnssd->StartBrowse(); + } + else + { + sSrplDnssd->StopBrowse(); + } +} + +extern "C" void otPlatSrplRegisterDnssdService(otInstance *aInstance, const uint8_t *aTxtData, uint16_t aTxtLength) +{ + OTBR_UNUSED_VARIABLE(aInstance); + + sSrplDnssd->RegisterService(aTxtData, aTxtLength); +} + +extern "C" void otPlatSrplUnregisterDnssdService(otInstance *aInstance) +{ + OTBR_UNUSED_VARIABLE(aInstance); + + sSrplDnssd->UnregisterService(); +} + +namespace otbr { + +namespace SrplDnssd { + +SrplDnssd::SrplDnssd(Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher) + : mNcp(aNcp) + , mPublisher(aPublisher) +{ + sSrplDnssd = this; +} + +void SrplDnssd::StartBrowse(void) +{ + VerifyOrExit(!IsBrowsing()); + + otbrLogDebug("Start browsing SRPL services ..."); + + mSubscriberId = mPublisher.AddSubscriptionCallbacks( + [this](const std::string &aType, const DiscoveredInstanceInfo &aInstanceInfo) { + OnServiceInstanceResolved(aType, aInstanceInfo); + }, + nullptr); + mPublisher.SubscribeService(kServiceType, /* aInstanceName */ ""); + +exit: + return; +} + +void SrplDnssd::StopBrowse(void) +{ + VerifyOrExit(IsBrowsing()); + + otbrLogDebug("Stop browsing SRPL services."); + + mPublisher.UnsubscribeService(kServiceType, /* aInstanceName */ ""); + mPublisher.RemoveSubscriptionCallbacks(mSubscriberId); + mSubscriberId = 0; + +exit: + return; +} + +// TODO: handle the case when mDNS publisher is not ready +void SrplDnssd::RegisterService(const uint8_t *aTxtData, uint8_t aTxtLength) +{ + otbr::Mdns::Publisher::TxtList txtList; + + SuccessOrExit(otbr::Mdns::Publisher::DecodeTxtData(txtList, aTxtData, aTxtLength)); + + otbrLogInfo("Publishing SRPL service"); + mPublisher.PublishService(/* aHostName */ "", /* aName */ "", kServiceType, {}, kPort, txtList, + [this](otbrError aError) { + otbrLogResult(aError, "Result of publishing SRPL service"); + if (aError == OTBR_ERROR_NONE) + { + auto serviceRegistration = mPublisher.FindServiceRegistrationByType(kServiceType); + if (serviceRegistration) + { + mServiceInstanceName = serviceRegistration->mName; + otbrLogInfo("SRPL service instance name is %s", mServiceInstanceName.c_str()); + } + } + }); + +exit: + return; +} + +void SrplDnssd::UnregisterService() +{ + otbrLogInfo("Unpublishing SRPL service: %s", mServiceInstanceName.c_str()); + mPublisher.UnpublishService(mServiceInstanceName, kServiceType, [this](otbrError aError) { + if (aError == OTBR_ERROR_NONE) + { + mServiceInstanceName.clear(); + } + }); +} + +void SrplDnssd::OnServiceInstanceResolved(const std::string &aType, const DiscoveredInstanceInfo &aInstanceInfo) +{ + otPlatSrplPartnerInfo partnerInfo; + + VerifyOrExit(IsBrowsing()); + VerifyOrExit(StringUtils::EqualCaseInsensitive(aType, kServiceType)); + VerifyOrExit(!StringUtils::EqualCaseInsensitive(aInstanceInfo.mName, mServiceInstanceName)); + + // TODO: Also need to check by addresses to mark as 'me'. + + partnerInfo.mRemoved = aInstanceInfo.mRemoved; + otbrLogInfo("Discovered SRPL peer: %s", aInstanceInfo.mName.c_str()); + + if (!partnerInfo.mRemoved) + { + VerifyOrExit(!aInstanceInfo.mAddresses.empty()); + // TODO: choose the address with the largest scope + // Currently the mDNS publisher only returns 1 address in every callback, probably we should let BR wait for + // some time to collect all discovered addresses and decide which address to use. + SuccessOrExit(otIp6AddressFromString(aInstanceInfo.mAddresses.front().ToString().c_str(), + &partnerInfo.mSockAddr.mAddress)); + + partnerInfo.mTxtData = aInstanceInfo.mTxtData.data(); + partnerInfo.mTxtLength = aInstanceInfo.mTxtData.size(); + partnerInfo.mSockAddr.mPort = aInstanceInfo.mPort; + } + otPlatSrplHandleDnssdBrowseResult(mNcp.GetInstance(), &partnerInfo); + +exit: + return; +} + +} // namespace SrplDnssd +} // namespace otbr + +#endif diff --git a/src/srpl_dnssd/srpl_dnssd.hpp b/src/srpl_dnssd/srpl_dnssd.hpp new file mode 100644 index 00000000000..c810397b1ca --- /dev/null +++ b/src/srpl_dnssd/srpl_dnssd.hpp @@ -0,0 +1,122 @@ +/* + * 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. + */ + +/** + * @file + * This file includes definitions for SRPL DNS-SD over mDNS. + */ + +#ifndef OTBR_AGENT_SRPL_DNSSD_HPP_ +#define OTBR_AGENT_SRPL_DNSSD_HPP_ + +#if OTBR_ENABLE_SRP_REPLICATION + +#include + +#include "common/types.hpp" +#include "mdns/mdns.hpp" +#include "ncp/ncp_openthread.hpp" + +namespace otbr { + +namespace SrplDnssd { + +/** + * + * @addtogroup border-router-srpl-dnssd + * + * @brief + * This module includes definition for SRPL DNS-SD over mDNS. + * + * @{ + */ + +class SrplDnssd +{ +public: + /** + * This constructor initializes the SrplDnssd instance. + * + * @param aNcp + * @param aPublisher + */ + explicit SrplDnssd(Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher); + + /** + * This method starts browsing for SRPL peers. + * + */ + void StartBrowse(void); + + /** + * This method stops browsing for SRPL peers. + * + */ + void StopBrowse(void); + + /** + * This method registers the SRPL service to DNS-SD. + * + * @param[in] aTxtData The TXT data of SRPL service. + * @param[in] aTxtLength The TXT length of SRPL service. + */ + void RegisterService(const uint8_t *aTxtData, uint8_t aTxtLength); + + /** + * This method removes the SRPL service from DNS-SD. + * + */ + void UnregisterService(void); + +private: + static constexpr const char *kServiceType = "_srpl-tls._tcp"; + static constexpr uint16_t kPort = 853; + + using DiscoveredInstanceInfo = otbr::Mdns::Publisher::DiscoveredInstanceInfo; + + bool IsBrowsing(void) const { return mSubscriberId != 0; } + + void OnServiceInstanceResolved(const std::string &aType, const DiscoveredInstanceInfo &aInstanceInfo); + + Ncp::ControllerOpenThread &mNcp; + Mdns::Publisher &mPublisher; + std::string mServiceInstanceName; + uint64_t mSubscriberId = 0; +}; + +/** + * @} + */ + +} // namespace SrplDnssd + +} // namespace otbr + +#endif // OTBR_ENABLE_SRP_REPLICATION + +#endif // OTBR_AGENT_SRPL_DNSSD_HPP_ diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 784f859e8a6..f9975864f41 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable(otbr-test-unit $<$:test_mdns_mdnssd.cpp> main.cpp test_dns_utils.cpp + test_dso_transport.cpp test_logging.cpp test_once_callback.cpp test_pskc.cpp @@ -45,6 +46,8 @@ target_link_libraries(otbr-test-unit $<$:-L$> ${CPPUTEST_LIBRARIES} mbedtls + openthread-ftd + otbr-dso otbr-common otbr-utils pthread diff --git a/tests/unit/test_dso_transport.cpp b/tests/unit/test_dso_transport.cpp new file mode 100644 index 00000000000..4cc4f8529eb --- /dev/null +++ b/tests/unit/test_dso_transport.cpp @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2018, 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. + */ + +#if OTBR_ENABLE_DNS_DSO + +#include +#include +#include +#include +#include +#include +#include "common/mainloop_manager.hpp" +#include "dso/dso_transport.hpp" +#include "openthread/platform/dso_transport.h" + +#include + +void otPlatReset(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); +} + +class DsoTestFixture +{ +public: + enum EventType + { + kConnected, + kDisconnected, + kReceive, + }; + + struct Event + { + otPlatDsoConnection *mPlatDsoConnection; + EventType mType; + std::vector mData; + + void AssertConnected(otPlatDsoConnection *aConnection) const + { + CHECK(mPlatDsoConnection == aConnection); + CHECK(mType == EventType::kConnected); + } + + void AssertDisconnected(otPlatDsoConnection *aConnection, otPlatDsoDisconnectMode aMode) const + { + CHECK(mPlatDsoConnection == aConnection); + CHECK(mType == EventType::kDisconnected); + CHECK(mData.size() == 1); + CHECK(mData[0] == static_cast(aMode)); + } + + void AssertReceive(otPlatDsoConnection *aConnection, const std::vector &aData) const + { + CHECK(mPlatDsoConnection == aConnection); + CHECK(mType == EventType::kReceive); + CHECK(mData == aData); + } + }; + + otPlatDsoConnection *AcceptConnection(otInstance *aInstance, otSockAddr *aSockAddr) + { + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aSockAddr); + + otPlatDsoConnection *newConnection = reinterpret_cast(++mCurrentPlatDsoConnectionId); + + mPlatDsoConnections.push_back(newConnection); + + return newConnection; + } + + void HandleConnected(otbr::Dso::DsoAgent::DsoConnection *aConnection) + { + mEvents.push_back(Event{aConnection->mConnection, EventType::kConnected, {}}); + } + + void HandleDisconnected(otbr::Dso::DsoAgent::DsoConnection *aConnection, otPlatDsoDisconnectMode aMode) + { + mEvents.push_back({Event{aConnection->mConnection, EventType::kDisconnected, {static_cast(aMode)}}}); + } + + void HandleReceive(otbr::Dso::DsoAgent::DsoConnection *aConnection, const std::vector &aData) + { + mEvents.push_back({Event{aConnection->mConnection, EventType::kReceive, std::move(aData)}}); + } + + static std::vector MakeDataForSending(std::vector aData) + { + int length = aData.size(); + + aData.insert(aData.begin(), length >> 8); + aData.insert(aData.begin() + 1, length & ((1 << 8) - 1)); + + return aData; + } + + static void Send(mbedtls_net_context &aCtx, const std::vector &aData) + { + auto data = MakeDataForSending(aData); + + mbedtls_net_send(&aCtx, data.data(), data.size()); + } + + static std::vector Receive(mbedtls_net_context &aCtx) + { + uint16_t length; + std::vector data; + + CHECK(sizeof(length) == mbedtls_net_recv(&aCtx, reinterpret_cast(&length), sizeof(length))); + length = ntohs(length); + data.resize(length); + CHECK(length == mbedtls_net_recv(&aCtx, data.data(), length)); + + return data; + } + + void SetUp() + { + mAgent.Init(mInstance, "lo"); + mAgent.SetHandlers( + [this](otInstance *aInstance, otSockAddr *aSockAddr) { return AcceptConnection(aInstance, aSockAddr); }, + [this](otbr::Dso::DsoAgent::DsoConnection *aConnection) { HandleConnected(aConnection); }, + [this](otbr::Dso::DsoAgent::DsoConnection *aConnection, otPlatDsoDisconnectMode aMode) { + HandleDisconnected(aConnection, aMode); + }, + [this](otbr::Dso::DsoAgent::DsoConnection *aConnection, const std::vector &aData) { + HandleReceive(aConnection, aData); + }); + } + + std::function RunMainLoop() + { + mShouldExit = false; + std::thread *mainLoopThread = new std::thread([this] { + while (!mShouldExit) + { + int rval; + otbr::MainloopContext mainloop{}; + + mainloop.mMaxFd = -1; + mainloop.mTimeout.tv_sec = 1; // 1 second + mainloop.mTimeout.tv_usec = 0; + + FD_ZERO(&mainloop.mReadFdSet); + FD_ZERO(&mainloop.mWriteFdSet); + FD_ZERO(&mainloop.mErrorFdSet); + + otbr::MainloopManager::GetInstance().Update(mainloop); + + rval = select(mainloop.mMaxFd + 1, &mainloop.mReadFdSet, &mainloop.mWriteFdSet, &mainloop.mErrorFdSet, + &mainloop.mTimeout); + + if (rval >= 0) + { + otbr::MainloopManager::GetInstance().Process(mainloop); + } + } + }); + + return [this, mainLoopThread]() { + mShouldExit = true; + mainLoopThread->join(); + delete mainLoopThread; + }; + } + + void RunMainLoopFor(const std::chrono::milliseconds &aMilliseconds) + { + auto quitMainLoop = RunMainLoop(); + + std::this_thread::sleep_for(aMilliseconds); + + quitMainLoop(); + } + + void TestServer(void) + { + mAgent.SetEnabled(mInstance, true); + + auto quitMainLoop = RunMainLoop(); + + mbedtls_net_context ctx; + + CHECK(!mbedtls_net_connect(&ctx, "localhost", "853", MBEDTLS_NET_PROTO_TCP)); + + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + auto conn = mAgent.FindOrCreateConnection(mPlatDsoConnections[0]); + + std::vector message1 = {0x61, 0x62, 0x63, 0x64, 0x31, 0x32, 0x33, 0x34}; // "abcd1234" + std::vector message2 = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + std::vector message3 = {0x31, 0x32, 0x33, 0x34, 0x61, 0x62, 0x63, 0x64}; // "1234abcd" + std::vector message4 = {0x41, 0x42, 0x43, 0x44, 0x45}; // "ABCDE" + + Send(ctx, message1); + Send(ctx, message2); + + conn->Send(message3); + conn->Send(message4); + + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + CHECK(Receive(ctx) == message3); + CHECK(Receive(ctx) == message4); + + mbedtls_net_free(&ctx); + + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + quitMainLoop(); + + CHECK(mEvents.size() == 4); + + int index = 0; + mEvents[index++].AssertConnected(mPlatDsoConnections[0]); + mEvents[index++].AssertReceive(mPlatDsoConnections[0], message1); + mEvents[index++].AssertReceive(mPlatDsoConnections[0], message2); + mEvents[index++].AssertDisconnected(mPlatDsoConnections[0], OT_PLAT_DSO_DISCONNECT_MODE_GRACEFULLY_CLOSE); + } + + void TestClient(void) + { + const char *kAddress = "::1"; + constexpr int kPort = 54321; + otSockAddr sockAddr; + mbedtls_net_context listeningCtx; + mbedtls_net_context clientCtx; + + mAgent.SetEnabled(mInstance, true); + + CHECK(!mbedtls_net_bind(&listeningCtx, kAddress, std::to_string(kPort).c_str(), MBEDTLS_NET_PROTO_TCP)); + + std::vector message1 = {0x61, 0x62, 0x63, 0x64, 0x31, 0x32, 0x33, 0x34}; // "abcd1234" + std::vector message2 = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + std::vector message3 = {0x31, 0x32, 0x33, 0x34, 0x61, 0x62, 0x63, 0x64}; // "1234abcd" + std::vector message4 = {0x41, 0x42, 0x43, 0x44, 0x45}; // "ABCDE" + + auto conn = mAgent.FindOrCreateConnection(reinterpret_cast(1)); + + SuccessOrDie(otIp6AddressFromString(kAddress, &sockAddr.mAddress), "Failed to parse IPv6 address"); + sockAddr.mPort = kPort; + + conn->Connect(&sockAddr); + + CHECK(!mbedtls_net_accept(&listeningCtx, &clientCtx, nullptr, 0, nullptr)); + + RunMainLoopFor(std::chrono::milliseconds(500)); + + CHECK(conn->GetState() == otbr::Dso::DsoAgent::DsoConnection::State::kConnected); + + conn->Send(message3); + conn->Send(message4); + + Send(clientCtx, message1); + Send(clientCtx, message2); + + RunMainLoopFor(std::chrono::milliseconds(500)); + + CHECK(Receive(clientCtx) == message3); + CHECK(Receive(clientCtx) == message4); + + mbedtls_net_free(&clientCtx); + + RunMainLoopFor(std::chrono::milliseconds(500)); + + mbedtls_net_free(&listeningCtx); + + CHECK(mEvents.size() == 4); + + int index = 0; + mEvents[index++].AssertConnected(reinterpret_cast(1)); + mEvents[index++].AssertReceive(reinterpret_cast(1), message1); + mEvents[index++].AssertReceive(reinterpret_cast(1), message2); + mEvents[index++].AssertDisconnected(reinterpret_cast(1), + OT_PLAT_DSO_DISCONNECT_MODE_GRACEFULLY_CLOSE); + } + + void TestDoubleConnect(void) + { + const char *kAddress = "::1"; + constexpr int kPort = 54321; + otSockAddr sockAddr; + mbedtls_net_context listeningCtx; + mbedtls_net_context clientCtx; + + mAgent.SetEnabled(mInstance, true); + + CHECK(!mbedtls_net_bind(&listeningCtx, kAddress, std::to_string(kPort).c_str(), MBEDTLS_NET_PROTO_TCP)); + + auto conn = mAgent.FindOrCreateConnection(reinterpret_cast(1)); + + SuccessOrDie(otIp6AddressFromString(kAddress, &sockAddr.mAddress), "Failed to parse IPv6 address"); + sockAddr.mPort = kPort; + + conn->Connect(&sockAddr); + conn->Connect(&sockAddr); + + CHECK(!mbedtls_net_accept(&listeningCtx, &clientCtx, nullptr, 0, nullptr)); + + RunMainLoopFor(std::chrono::milliseconds(500)); + + CHECK(conn->GetState() == otbr::Dso::DsoAgent::DsoConnection::State::kConnected); + } + + std::atomic_bool mShouldExit; + otInstance *mInstance = reinterpret_cast(1); + otbr::Dso::DsoAgent mAgent; + std::vector mPlatDsoConnections; + std::vector mEvents; + int mCurrentPlatDsoConnectionId = 10000; +}; + +TEST_GROUP(DsoTransport){}; + +TEST(DsoTransport, TestAll) +{ + { + DsoTestFixture fixture; + + fixture.SetUp(); + fixture.TestServer(); + } + { + DsoTestFixture fixture; + + fixture.SetUp(); + fixture.TestClient(); + } + + { + DsoTestFixture fixture; + + fixture.SetUp(); + fixture.TestDoubleConnect(); + } +} + +#endif // OTBR_ENABLE_DNS_DSO diff --git a/third_party/openthread/CMakeLists.txt b/third_party/openthread/CMakeLists.txt index 470adef615b..bc85cc612f9 100644 --- a/third_party/openthread/CMakeLists.txt +++ b/third_party/openthread/CMakeLists.txt @@ -43,6 +43,7 @@ set(OT_COMMISSIONER ON CACHE STRING "enable commissioner") set(OT_DAEMON ON CACHE STRING "enable daemon mode" FORCE) set(OT_DATASET_UPDATER ON CACHE STRING "enable dataset updater" FORCE) set(OT_DNS_CLIENT ON CACHE STRING "enable DNS client" FORCE) +set(OT_DNS_DSO ${OTBR_DNS_DSO} CACHE STRING "enable DSO support" FORCE) set(OT_DNSSD_SERVER ${OTBR_DNSSD_DISCOVERY_PROXY} CACHE STRING "enable DNS-SD server support" FORCE) set(OT_ECDSA ON CACHE STRING "enable ECDSA" FORCE) set(OT_FIREWALL ON CACHE STRING "enable firewall feature") @@ -86,6 +87,11 @@ if (NOT OT_LOG_LEVEL) endif() endif() +if (OTBR_SRP_REPLICATION) + set(OT_SRP_REPLICATION ON CACHE STRING "enable SRP replication" FORCE) + set(OT_DNS_DSO ON CACHE STRING "enable DSO support" FORCE) +endif() + if (NOT OT_POSIX_SETTINGS_PATH) set(OT_POSIX_SETTINGS_PATH "\"/var/lib/thread\"" CACHE STRING "set the directory to store Thread data" FORCE) endif()