diff --git a/examples/minimal-mdns/AllInterfaceListener.h b/examples/minimal-mdns/AllInterfaceListener.h deleted file mode 100644 index 23a26592f90706..00000000000000 --- a/examples/minimal-mdns/AllInterfaceListener.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * - * Copyright (c) 2020 Project CHIP Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -namespace MdnsExample { - -/// Returns -/// - NULL interface for IPv4 -/// - all interfaces for IPv6 -class AllInterfaces : public mdns::Minimal::ListenIterator -{ -public: - AllInterfaces(bool enableIpV4) : mState(enableIpV4 ? State::kIpV4 : State::kIpV6) - { - if (!enableIpV4) - { - SkipToFirstValidInterface(); - } - } - - bool Next(chip::Inet::InterfaceId * id, chip::Inet::IPAddressType * type) override - { - if (mState == State::kIpV4) - { -#if INET_CONFIG_ENABLE_IPV4 - *id = chip::Inet::InterfaceId::Null(); - *type = chip::Inet::IPAddressType::kIPv4; -#endif - mState = State::kIpV6; - - SkipToFirstValidInterface(); - -#if INET_CONFIG_ENABLE_IPV4 - return true; -#endif - } - - if (!mIterator.HasCurrent()) - { - return false; - } - - *id = mIterator.GetInterfaceId(); - *type = chip::Inet::IPAddressType::kIPv6; - - for (mIterator.Next(); SkipCurrentInterface(); mIterator.Next()) - { - } - return true; - } - -private: - enum class State - { - kIpV4, - kIpV6, - }; - State mState; - chip::Inet::InterfaceIterator mIterator; - - void SkipToFirstValidInterface() - { - if (SkipCurrentInterface()) - { - while (mIterator.Next()) - { - if (!SkipCurrentInterface()) - { - break; - } - } - } - } - - bool SkipCurrentInterface() - { - if (!mIterator.HasCurrent()) - { - return false; // nothing to try. - } - - if (!mIterator.IsUp()) - { - return true; // not a usable interface - } - - char name[chip::Inet::InterfaceId::kMaxIfNameLength]; - if (mIterator.GetInterfaceName(name, sizeof(name)) != CHIP_NO_ERROR) - { - printf("!!!! FAILED TO GET INTERFACE NAME\n"); - return true; - } - - if (strncmp(name, "lo", 2) == 0) - { - printf("Skipping interface '%s' (assume local loopback)\n", name); - return true; - } - - printf("Usable interface: %s\n", name); - - return false; - } -}; - -} // namespace MdnsExample diff --git a/examples/minimal-mdns/BUILD.gn b/examples/minimal-mdns/BUILD.gn index 437a420a382b67..b6aef7386c9b3f 100644 --- a/examples/minimal-mdns/BUILD.gn +++ b/examples/minimal-mdns/BUILD.gn @@ -19,7 +19,6 @@ import("${chip_root}/build/chip/tools.gni") static_library("minimal-mdns-example-common") { sources = [ - "AllInterfaceListener.h", "PacketReporter.cpp", "PacketReporter.h", ] @@ -53,6 +52,7 @@ executable("minimal-mdns-server") { ":minimal-mdns-example-common", "${chip_root}/src/lib", "${chip_root}/src/lib/dnssd/minimal_mdns", + "${chip_root}/src/lib/dnssd/minimal_mdns:default_policy", "${chip_root}/src/lib/dnssd/minimal_mdns/responders", ] diff --git a/examples/minimal-mdns/client.cpp b/examples/minimal-mdns/client.cpp index 4b8aff95fda0e5..be4cae44759294 100644 --- a/examples/minimal-mdns/client.cpp +++ b/examples/minimal-mdns/client.cpp @@ -22,6 +22,8 @@ #include #include +#include +#include #include #include #include @@ -30,7 +32,6 @@ #include #include -#include "AllInterfaceListener.h" #include "PacketReporter.h" using namespace chip; @@ -39,7 +40,6 @@ namespace { struct Options { - bool enableIpV4 = false; bool unicastAnswers = true; uint32_t runtimeMs = 500; uint16_t querySendPort = 5353; @@ -53,9 +53,8 @@ constexpr size_t kMdnsMaxPacketSize = 1'024; using namespace chip::ArgParser; -constexpr uint16_t kOptionEnableIpV4 = '4'; -constexpr uint16_t kOptionQuery = 'q'; -constexpr uint16_t kOptionType = 't'; +constexpr uint16_t kOptionQuery = 'q'; +constexpr uint16_t kOptionType = 't'; // non-ascii options have no short option version constexpr uint16_t kOptionListenPort = 0x100; @@ -67,10 +66,6 @@ bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, { switch (aIdentifier) { - case kOptionEnableIpV4: - gOptions.enableIpV4 = true; - return true; - case kOptionListenPort: if (!ParseInt(aValue, gOptions.listenPort)) { @@ -145,7 +140,6 @@ bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, OptionDef cmdLineOptionsDef[] = { { "listen-port", kArgumentRequired, kOptionListenPort }, - { "enable-ip-v4", kNoArgument, kOptionEnableIpV4 }, { "query", kArgumentRequired, kOptionQuery }, { "type", kArgumentRequired, kOptionType }, { "query-port", kArgumentRequired, kOptionQueryPort }, @@ -157,9 +151,6 @@ OptionDef cmdLineOptionsDef[] = { OptionSet cmdLineOptions = { HandleOptions, cmdLineOptionsDef, "PROGRAM OPTIONS", " --listen-port \n" " The port number to listen on\n" - " -4\n" - " --enable-ip-v4\n" - " enable listening on IPv4\n" " -q\n" " --query\n" " The query to send\n" @@ -324,13 +315,13 @@ int main(int argc, char ** args) ReportDelegate reporter; CHIP_ERROR err; + mdns::Minimal::SetDefaultAddressPolicy(); gMdnsServer.SetDelegate(&reporter); { + auto endpoints = mdns::Minimal::GetAddressPolicy()->GetListenEndpoints(); - MdnsExample::AllInterfaces allInterfaces(gOptions.enableIpV4); - - err = gMdnsServer.Listen(chip::DeviceLayer::UDPEndPointManager(), &allInterfaces, gOptions.listenPort); + err = gMdnsServer.Listen(chip::DeviceLayer::UDPEndPointManager(), endpoints.get(), gOptions.listenPort); if (err != CHIP_NO_ERROR) { printf("Server failed to listen on all interfaces: %s\n", chip::ErrorStr(err)); diff --git a/examples/minimal-mdns/server.cpp b/examples/minimal-mdns/server.cpp index 38a68e100150d3..39168be3033d95 100644 --- a/examples/minimal-mdns/server.cpp +++ b/examples/minimal-mdns/server.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -36,7 +38,6 @@ #include #include -#include "AllInterfaceListener.h" #include "PacketReporter.h" using namespace chip; @@ -198,6 +199,8 @@ int main(int argc, char ** args) return 1; } + mdns::Minimal::SetDefaultAddressPolicy(); + printf("Running on port %d using %s...\n", gOptions.listenPort, gOptions.enableIpV4 ? "IPv4 AND IPv6" : "IPv6 ONLY"); mdns::Minimal::QueryResponder<16 /* maxRecords */> queryResponder; @@ -265,9 +268,9 @@ int main(int argc, char ** args) gMdnsServer.SetDelegate(&delegate); { - MdnsExample::AllInterfaces allInterfaces(gOptions.enableIpV4); + auto endpoints = mdns::Minimal::GetAddressPolicy()->GetListenEndpoints(); - if (gMdnsServer.Listen(DeviceLayer::UDPEndPointManager(), &allInterfaces, gOptions.listenPort) != CHIP_NO_ERROR) + if (gMdnsServer.Listen(DeviceLayer::UDPEndPointManager(), endpoints.get(), gOptions.listenPort) != CHIP_NO_ERROR) { printf("Server failed to listen on all interfaces\n"); return 1; diff --git a/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp b/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp index b37d824ece968b..cb59a4a81d4998 100644 --- a/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp +++ b/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,7 @@ namespace chip { namespace Dnssd { namespace { +using chip::Platform::UniquePtr; using namespace mdns::Minimal; #ifdef DETAIL_LOGGING @@ -861,13 +863,6 @@ bool AdvertiserMinMdns::ShouldAdvertiseOn(const chip::Inet::InterfaceId id, cons void AdvertiserMinMdns::AdvertiseRecords(BroadcastAdvertiseType type) { - chip::Inet::InterfaceAddressIterator interfaceAddress; - - if (!interfaceAddress.Next()) - { - return; - } - ResponseConfiguration responseConfiguration; if (type == BroadcastAdvertiseType::kRemovingAll) { @@ -875,68 +870,69 @@ void AdvertiserMinMdns::AdvertiseRecords(BroadcastAdvertiseType type) responseConfiguration.SetTtlSecondsOverride(0); } - for (; interfaceAddress.HasCurrent(); interfaceAddress.Next()) + UniquePtr allInterfaces = GetAddressPolicy()->GetListenEndpoints(); + + chip::Inet::InterfaceId interfaceId; + chip::Inet::IPAddressType addressType; + + while (allInterfaces->Next(&interfaceId, &addressType)) { - if (!Internal::IsCurrentInterfaceUsable(interfaceAddress)) - { - continue; - } + UniquePtr allIps = GetAddressPolicy()->GetIpAddressesForEndpoint(interfaceId, addressType); Inet::IPAddress ipAddress; - if (interfaceAddress.GetAddress(ipAddress) != CHIP_NO_ERROR) - { - continue; - } - if (!ShouldAdvertiseOn(interfaceAddress.GetInterfaceId(), ipAddress)) + while (allIps->Next(ipAddress)) { - continue; - } + if (!ShouldAdvertiseOn(interfaceId, ipAddress)) + { + continue; + } - chip::Inet::IPPacketInfo packetInfo; + chip::Inet::IPPacketInfo packetInfo; - packetInfo.Clear(); - packetInfo.SrcAddress = ipAddress; - if (ipAddress.IsIPv4()) - { - BroadcastIpAddresses::GetIpv4Into(packetInfo.DestAddress); - } - else - { - BroadcastIpAddresses::GetIpv6Into(packetInfo.DestAddress); - } - packetInfo.SrcPort = kMdnsPort; - packetInfo.DestPort = kMdnsPort; - packetInfo.Interface = interfaceAddress.GetInterfaceId(); - - // Advertise all records - // - // TODO: Consider advertising delta changes. - // - // Current advertisement does not have a concept of "delta" to only - // advertise changes. Current implementation is to always - // 1. advertise TTL=0 (clear all caches) - // 2. advertise available records (with longer TTL) - // - // It would be nice if we could selectively advertise what changes, like - // send TTL=0 for anything removed/about to be removed (and only those), - // then only advertise new items added. - // - // This optimization likely will take more logic and state storage, so - // for now it is not done. - QueryData queryData(QType::PTR, QClass::IN, false /* unicast */); - queryData.SetIsInternalBroadcast(true); - - for (auto & it : mOperationalResponders) - { - it.GetAllocator()->GetQueryResponder()->ClearBroadcastThrottle(); - } - mQueryResponderAllocatorCommissionable.GetQueryResponder()->ClearBroadcastThrottle(); - mQueryResponderAllocatorCommissioner.GetQueryResponder()->ClearBroadcastThrottle(); + packetInfo.Clear(); + packetInfo.SrcAddress = ipAddress; + if (ipAddress.IsIPv4()) + { + BroadcastIpAddresses::GetIpv4Into(packetInfo.DestAddress); + } + else + { + BroadcastIpAddresses::GetIpv6Into(packetInfo.DestAddress); + } + packetInfo.SrcPort = kMdnsPort; + packetInfo.DestPort = kMdnsPort; + packetInfo.Interface = interfaceId; + + // Advertise all records + // + // TODO: Consider advertising delta changes. + // + // Current advertisement does not have a concept of "delta" to only + // advertise changes. Current implementation is to always + // 1. advertise TTL=0 (clear all caches) + // 2. advertise available records (with longer TTL) + // + // It would be nice if we could selectively advertise what changes, like + // send TTL=0 for anything removed/about to be removed (and only those), + // then only advertise new items added. + // + // This optimization likely will take more logic and state storage, so + // for now it is not done. + QueryData queryData(QType::PTR, QClass::IN, false /* unicast */); + queryData.SetIsInternalBroadcast(true); + + for (auto & it : mOperationalResponders) + { + it.GetAllocator()->GetQueryResponder()->ClearBroadcastThrottle(); + } + mQueryResponderAllocatorCommissionable.GetQueryResponder()->ClearBroadcastThrottle(); + mQueryResponderAllocatorCommissioner.GetQueryResponder()->ClearBroadcastThrottle(); - CHIP_ERROR err = mResponseSender.Respond(0, queryData, &packetInfo, responseConfiguration); - if (err != CHIP_NO_ERROR) - { - ChipLogError(Discovery, "Failed to advertise records: %" CHIP_ERROR_FORMAT, err.Format()); + CHIP_ERROR err = mResponseSender.Respond(0, queryData, &packetInfo, responseConfiguration); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Discovery, "Failed to advertise records: %" CHIP_ERROR_FORMAT, err.Format()); + } } } diff --git a/src/lib/dnssd/MinimalMdnsServer.cpp b/src/lib/dnssd/MinimalMdnsServer.cpp index 042dc08303973e..a73f8830809b3a 100644 --- a/src/lib/dnssd/MinimalMdnsServer.cpp +++ b/src/lib/dnssd/MinimalMdnsServer.cpp @@ -16,81 +16,30 @@ */ #include "MinimalMdnsServer.h" -namespace chip { -namespace Dnssd { -namespace { - -using namespace mdns::Minimal; - -class AllInterfaces : public ListenIterator -{ -private: -public: - AllInterfaces() { SkipToFirstValidInterface(); } - - bool Next(chip::Inet::InterfaceId * id, chip::Inet::IPAddressType * type) override - { - if (!mIterator.HasCurrent()) - { - return false; - } - -#if INET_CONFIG_ENABLE_IPV4 - if (mState == State::kIpV4) - { - *id = mIterator.GetInterfaceId(); - *type = chip::Inet::IPAddressType::kIPv4; - mState = State::kIpV6; - return true; - } -#endif +#include - *id = mIterator.GetInterfaceId(); - *type = chip::Inet::IPAddressType::kIPv6; -#if INET_CONFIG_ENABLE_IPV4 - mState = State::kIpV4; +#ifndef CHIP_MINMDNS_DEFAULT_POLICY +#define CHIP_MINMDNS_DEFAULT_POLICY 0 #endif - for (mIterator.Next(); SkipCurrentInterface(); mIterator.Next()) - { - } - return true; - } - -private: -#if INET_CONFIG_ENABLE_IPV4 - enum class State - { - kIpV4, - kIpV6, - }; - State mState = State::kIpV4; +#if CHIP_MINMDNS_DEFAULT_POLICY +#include #endif - chip::Inet::InterfaceIterator mIterator; - void SkipToFirstValidInterface() - { - do - { - if (!SkipCurrentInterface()) - { - break; - } - } while (mIterator.Next()); - } +namespace chip { +namespace Dnssd { - bool SkipCurrentInterface() - { - if (!mIterator.HasCurrent()) - { - return false; // nothing to try. - } +using namespace mdns::Minimal; +using chip::Platform::UniquePtr; - return !Internal::IsCurrentInterfaceUsable(mIterator); - } -}; +GlobalMinimalMdnsServer::GlobalMinimalMdnsServer() +{ + mServer.SetDelegate(this); -} // namespace +#if CHIP_MINMDNS_DEFAULT_POLICY + mdns::Minimal::SetDefaultAddressPolicy(); +#endif +} GlobalMinimalMdnsServer & GlobalMinimalMdnsServer::Instance() { @@ -102,8 +51,10 @@ CHIP_ERROR GlobalMinimalMdnsServer::StartServer(chip::Inet::EndPointManager endpoints = GetAddressPolicy()->GetListenEndpoints(); + + return GlobalMinimalMdnsServer::Server().Listen(udpEndPointManager, endpoints.get(), port); } void GlobalMinimalMdnsServer::ShutdownServer() diff --git a/src/lib/dnssd/MinimalMdnsServer.h b/src/lib/dnssd/MinimalMdnsServer.h index b6aa48f456a435..8a392f1b3d2921 100644 --- a/src/lib/dnssd/MinimalMdnsServer.h +++ b/src/lib/dnssd/MinimalMdnsServer.h @@ -22,42 +22,6 @@ namespace chip { namespace Dnssd { -namespace Internal { - -/// Checks if the current interface is powered on -/// and not local loopback. -template -bool IsCurrentInterfaceUsable(T & iterator) -{ - if (!iterator.IsUp()) - { - return false; // not a usable interface - } - char name[chip::Inet::InterfaceId::kMaxIfNameLength]; - if (iterator.GetInterfaceName(name, sizeof(name)) != CHIP_NO_ERROR) - { - ChipLogError(Discovery, "Failed to get interface name."); - return false; - } - - // TODO: need a better way to ignore local loopback interfaces/addresses - // We do not want to listen on local loopback even though they are up and - // support multicast - // - // Some way to detect 'is local looback' that is smarter (e.g. at least - // strict string compare on linux instead of substring) would be better. - // - // This would reject likely valid interfaces like 'lollipop' or 'lostinspace' - if (strncmp(name, "lo", 2) == 0) - { - /// local loopback interface is not usable by MDNS - return false; - } - return true; -} - -} // namespace Internal - /// Generic receive delegate for a MDNS packet class MdnsPacketDelegate { @@ -76,7 +40,7 @@ class GlobalMinimalMdnsServer : public mdns::Minimal::ServerDelegate using ServerType = mdns::Minimal::Server; - GlobalMinimalMdnsServer() { mServer.SetDelegate(this); } + GlobalMinimalMdnsServer(); static GlobalMinimalMdnsServer & Instance(); static mdns::Minimal::ServerBase & Server() diff --git a/src/lib/dnssd/minimal_mdns/AddressPolicy.cpp b/src/lib/dnssd/minimal_mdns/AddressPolicy.cpp new file mode 100644 index 00000000000000..a3362fd2f832b6 --- /dev/null +++ b/src/lib/dnssd/minimal_mdns/AddressPolicy.cpp @@ -0,0 +1,41 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "AddressPolicy.h" + +#include + +namespace mdns { +namespace Minimal { + +namespace { +AddressPolicy * gAddressPolicy = nullptr; +} // namespace + +AddressPolicy * GetAddressPolicy() +{ + VerifyOrDie(gAddressPolicy != nullptr); + return gAddressPolicy; +} + +void SetAddressPolicy(AddressPolicy * policy) +{ + VerifyOrDie(policy != nullptr); + gAddressPolicy = policy; +} + +} // namespace Minimal +} // namespace mdns diff --git a/src/lib/dnssd/minimal_mdns/AddressPolicy.h b/src/lib/dnssd/minimal_mdns/AddressPolicy.h new file mode 100644 index 00000000000000..da8dd33f08e14f --- /dev/null +++ b/src/lib/dnssd/minimal_mdns/AddressPolicy.h @@ -0,0 +1,55 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +namespace mdns { +namespace Minimal { + +/// Describes a policy of endpoints and IP addresses to use for +/// MinMDNS listening and advertisement. +class AddressPolicy +{ +public: + virtual ~AddressPolicy() = default; + + /// Get all endpoints on which minimal MDNS should listen on. + virtual chip::Platform::UniquePtr GetListenEndpoints() = 0; + + /// Fetch all the IP addresses for the given interface which are valid + /// for DNSSD server advertisement. + /// + /// Generally this should skip invalid addresses even if reported by the + /// underlying operating system (e.g. linux could skip deprecated/temporary/dad-failed ones). + virtual chip::Platform::UniquePtr GetIpAddressesForEndpoint(chip::Inet::InterfaceId interfaceId, + chip::Inet::IPAddressType type) = 0; +}; + +/// Fetch the globally used MinMDNS address policy +AddressPolicy * GetAddressPolicy(); + +/// Update the global address policy. +/// +/// MUST be called before any minmdns functionality is used (e.g. server +/// startup) +void SetAddressPolicy(AddressPolicy * policy); + +} // namespace Minimal +} // namespace mdns diff --git a/src/lib/dnssd/AllInterfacesListenIterator.h b/src/lib/dnssd/minimal_mdns/AddressPolicy_DefaultImpl.cpp similarity index 57% rename from src/lib/dnssd/AllInterfacesListenIterator.h rename to src/lib/dnssd/minimal_mdns/AddressPolicy_DefaultImpl.cpp index 2134ea6f9d90a8..5e668966bfbf51 100644 --- a/src/lib/dnssd/AllInterfacesListenIterator.h +++ b/src/lib/dnssd/minimal_mdns/AddressPolicy_DefaultImpl.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include -#pragma once +#include -#include - -#include - -namespace chip { -namespace Dnssd { +namespace mdns { +namespace Minimal { +namespace { /// Checks if the current interface is powered on /// and not local loopback. @@ -58,7 +56,6 @@ bool IsCurrentInterfaceUsable(T & iterator) class AllInterfaces : public mdns::Minimal::ListenIterator { -private: public: AllInterfaces() { SkipToFirstValidInterface(); } @@ -92,15 +89,13 @@ class AllInterfaces : public mdns::Minimal::ListenIterator } private: +#if INET_CONFIG_ENABLE_IPV4 enum class State { kIpV4, kIpV6, }; -#if INET_CONFIG_ENABLE_IPV4 State mState = State::kIpV4; -#else - State mState = State::kIpV6; #endif chip::Inet::InterfaceIterator mIterator; @@ -126,5 +121,78 @@ class AllInterfaces : public mdns::Minimal::ListenIterator } }; -} // namespace Dnssd -} // namespace chip +class AllAddressesIterator : public mdns::Minimal::IpAddressIterator +{ +public: + AllAddressesIterator(chip::Inet::InterfaceId interfaceId, chip::Inet::IPAddressType addrType) : + mInterfaceIdFilter(interfaceId), mAddrType(addrType) + {} + + bool Next(chip::Inet::IPAddress & dest) + { + while (true) + { + if (!mIterator.HasCurrent()) + { + return false; + } + + if (mIterator.GetInterfaceId() != mInterfaceIdFilter) + { + mIterator.Next(); + continue; + } + + CHIP_ERROR err = mIterator.GetAddress(dest); + mIterator.Next(); + + if (mAddrType != chip::Inet::IPAddressType::kAny) + { + if (dest.Type() != mAddrType) + { + continue; + } + } + + if (err != CHIP_NO_ERROR) + { + ChipLogError(Discovery, "Failed to fetch interface IP address: %" CHIP_ERROR_FORMAT, err.Format()); + continue; + } + + return true; + } + } + +private: + const chip::Inet::InterfaceId mInterfaceIdFilter; + const chip::Inet::IPAddressType mAddrType; + chip::Inet::InterfaceAddressIterator mIterator; +}; + +class DefaultAddressPolicy : public AddressPolicy +{ +public: + chip::Platform::UniquePtr GetListenEndpoints() override + { + return chip::Platform::UniquePtr(chip::Platform::New()); + } + + chip::Platform::UniquePtr GetIpAddressesForEndpoint(chip::Inet::InterfaceId interfaceId, + chip::Inet::IPAddressType type) override + { + return chip::Platform::UniquePtr(chip::Platform::New(interfaceId, type)); + } +}; + +DefaultAddressPolicy gDefaultAddressPolicy; + +} // namespace + +void SetDefaultAddressPolicy() +{ + SetAddressPolicy(&gDefaultAddressPolicy); +} + +} // namespace Minimal +} // namespace mdns diff --git a/src/lib/dnssd/minimal_mdns/AddressPolicy_DefaultImpl.h b/src/lib/dnssd/minimal_mdns/AddressPolicy_DefaultImpl.h new file mode 100644 index 00000000000000..dddf32c80799e2 --- /dev/null +++ b/src/lib/dnssd/minimal_mdns/AddressPolicy_DefaultImpl.h @@ -0,0 +1,25 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +namespace mdns { +namespace Minimal { + +void SetDefaultAddressPolicy(); + +} // namespace Minimal +} // namespace mdns diff --git a/src/lib/dnssd/minimal_mdns/BUILD.gn b/src/lib/dnssd/minimal_mdns/BUILD.gn index 59f3550c659ab7..853e2359d2fb46 100644 --- a/src/lib/dnssd/minimal_mdns/BUILD.gn +++ b/src/lib/dnssd/minimal_mdns/BUILD.gn @@ -32,6 +32,9 @@ declare_args() { # This enables verbose logging of sent/received mDNS messages. However, will incur extra RAM and # time. chip_minmdns_high_verbosity = false + + # Compile in the default minmdns policy for endpoint and ip address fetching + chip_minmdns_default_policy = true } config("config") { @@ -48,6 +51,35 @@ config("config") { } else { defines += [ "CHIP_MINMDNS_HIGH_VERBOSITY=0" ] } + + if (chip_minmdns_default_policy) { + defines += [ "CHIP_MINMDNS_DEFAULT_POLICY=1" ] + } +} + +source_set("address_policy") { + sources = [ + "AddressPolicy.cpp", + "AddressPolicy.h", + "ServerIPAddresses.h", + ] + + public_deps = [ + "${chip_root}/src/inet", + "${chip_root}/src/lib/support", + ] +} + +static_library("default_policy") { + sources = [ + "AddressPolicy_DefaultImpl.cpp", + "AddressPolicy_DefaultImpl.h", + ] + + public_deps = [ + ":address_policy", + "${chip_root}/src/inet", + ] } static_library("minimal_mdns") { @@ -72,6 +104,7 @@ static_library("minimal_mdns") { } public_deps = [ + ":address_policy", "${chip_root}/src/inet", "${chip_root}/src/lib/core", "${chip_root}/src/lib/dnssd/minimal_mdns/core", @@ -79,5 +112,9 @@ static_library("minimal_mdns") { "${chip_root}/src/platform", ] + if (chip_minmdns_default_policy) { + public_deps += [ ":default_policy" ] + } + public_configs = [ ":config" ] } diff --git a/src/lib/dnssd/minimal_mdns/ListenIterator.h b/src/lib/dnssd/minimal_mdns/ListenIterator.h new file mode 100644 index 00000000000000..324351ef85e10d --- /dev/null +++ b/src/lib/dnssd/minimal_mdns/ListenIterator.h @@ -0,0 +1,45 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +namespace mdns { +namespace Minimal { + +/// Provides a list of intefaces to listen on. +/// +/// When listening on IP, both IP address type (IPv4 or IPv6) and interface id +/// are important. In particular, when using link-local IP addresses, the actual +/// interface matters (e.g. FF02::FB will care over which IPv6 interface it is sent) +/// +/// For MDNS in particular, you may want: +/// - IPv4 listen on InterfaceId::Null() +/// - IPv6 listen on every specific interface id available (except local loopback and other +/// not usable interfaces like docker) +class ListenIterator +{ +public: + virtual ~ListenIterator() {} + + // Get the next interface/address type to listen on + virtual bool Next(chip::Inet::InterfaceId * id, chip::Inet::IPAddressType * type) = 0; +}; + +} // namespace Minimal +} // namespace mdns diff --git a/src/lib/dnssd/minimal_mdns/Server.h b/src/lib/dnssd/minimal_mdns/Server.h index 0266b4625ebfb3..dfca413a463356 100644 --- a/src/lib/dnssd/minimal_mdns/Server.h +++ b/src/lib/dnssd/minimal_mdns/Server.h @@ -23,6 +23,7 @@ #include #include +#include #include namespace mdns { @@ -37,25 +38,6 @@ void GetIpv4Into(chip::Inet::IPAddress & dest); } // namespace BroadcastIpAddresses -/// Provides a list of intefaces to listen on. -/// -/// When listening on IP, both IP address type (IPv4 or IPv6) and interface id -/// are important. In particular, when using link-local IP addresses, the actual -/// interface matters (e.g. FF02::FB will care over which IPv6 interface it is sent) -/// -/// For MDNS in particular, you may want: -/// - IPv4 listen on InterfaceId::Null() -/// - IPv6 listen on every specific interface id available (except local loopback and other -/// not usable interfaces like docker) -class ListenIterator -{ -public: - virtual ~ListenIterator() {} - - // Get the next interface/address type to listen on - virtual bool Next(chip::Inet::InterfaceId * id, chip::Inet::IPAddressType * type) = 0; -}; - /// Handles mDNS Server Callbacks class ServerDelegate { diff --git a/src/lib/dnssd/minimal_mdns/ServerIPAddresses.h b/src/lib/dnssd/minimal_mdns/ServerIPAddresses.h new file mode 100644 index 00000000000000..81af512a9093a9 --- /dev/null +++ b/src/lib/dnssd/minimal_mdns/ServerIPAddresses.h @@ -0,0 +1,37 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +namespace mdns { +namespace Minimal { + +class IpAddressIterator +{ +public: + virtual ~IpAddressIterator() = default; + + /// Fetch the next IP address in the iterator. + /// + /// Returns true if an IP address is available, false if the iteration is complete. + virtual bool Next(chip::Inet::IPAddress & dest) = 0; +}; + +} // namespace Minimal +} // namespace mdns diff --git a/src/lib/dnssd/minimal_mdns/responders/BUILD.gn b/src/lib/dnssd/minimal_mdns/responders/BUILD.gn index efabe4895e7260..903c4ddae5d5fb 100644 --- a/src/lib/dnssd/minimal_mdns/responders/BUILD.gn +++ b/src/lib/dnssd/minimal_mdns/responders/BUILD.gn @@ -31,6 +31,7 @@ static_library("responders") { public_deps = [ "${chip_root}/src/inet", "${chip_root}/src/lib/core", + "${chip_root}/src/lib/dnssd/minimal_mdns:address_policy", "${chip_root}/src/lib/dnssd/minimal_mdns/core", "${chip_root}/src/lib/dnssd/minimal_mdns/records", "${chip_root}/src/platform", diff --git a/src/lib/dnssd/minimal_mdns/responders/IP.cpp b/src/lib/dnssd/minimal_mdns/responders/IP.cpp index 84c36bd1cb01cc..c509d818adf573 100644 --- a/src/lib/dnssd/minimal_mdns/responders/IP.cpp +++ b/src/lib/dnssd/minimal_mdns/responders/IP.cpp @@ -16,43 +16,51 @@ */ #include "IP.h" +#include + +#include #include namespace mdns { namespace Minimal { +using chip::Platform::UniquePtr; + void IPv4Responder::AddAllResponses(const chip::Inet::IPPacketInfo * source, ResponderDelegate * delegate, const ResponseConfiguration & configuration) { +#if INET_CONFIG_ENABLE_IPV4 chip::Inet::IPAddress addr; - for (chip::Inet::InterfaceAddressIterator it; it.HasCurrent(); it.Next()) + UniquePtr ips = + GetAddressPolicy()->GetIpAddressesForEndpoint(source->Interface, chip::Inet::IPAddressType::kIPv4); + VerifyOrDie(ips); + + while (ips->Next(addr)) { - if ((it.GetInterfaceId() == source->Interface) && (it.GetAddress(addr) == CHIP_NO_ERROR) && addr.IsIPv4()) - { - IPResourceRecord record(GetQName(), addr); - configuration.Adjust(record); - delegate->AddResponse(record); - } + assert(addr.IsIPv4()); + + IPResourceRecord record(GetQName(), addr); + configuration.Adjust(record); + delegate->AddResponse(record); } +#endif } void IPv6Responder::AddAllResponses(const chip::Inet::IPPacketInfo * source, ResponderDelegate * delegate, const ResponseConfiguration & configuration) { - for (chip::Inet::InterfaceAddressIterator it; it.HasCurrent(); it.Next()) + chip::Inet::IPAddress addr; + UniquePtr ips = + GetAddressPolicy()->GetIpAddressesForEndpoint(source->Interface, chip::Inet::IPAddressType::kIPv6); + VerifyOrDie(ips); + + while (ips->Next(addr)) { - if (it.GetInterfaceId() != source->Interface) - { - continue; - } - - chip::Inet::IPAddress addr; - if ((it.GetInterfaceId() == source->Interface) && (it.GetAddress(addr) == CHIP_NO_ERROR) && addr.IsIPv6()) - { - IPResourceRecord record(GetQName(), addr); - configuration.Adjust(record); - delegate->AddResponse(record); - } + assert(addr.IsIPv6()); + + IPResourceRecord record(GetQName(), addr); + configuration.Adjust(record); + delegate->AddResponse(record); } } diff --git a/src/lib/dnssd/minimal_mdns/responders/tests/BUILD.gn b/src/lib/dnssd/minimal_mdns/responders/tests/BUILD.gn index 8f7c0a455c84fc..a657151d1d9709 100644 --- a/src/lib/dnssd/minimal_mdns/responders/tests/BUILD.gn +++ b/src/lib/dnssd/minimal_mdns/responders/tests/BUILD.gn @@ -32,6 +32,7 @@ chip_test_suite("tests") { public_deps = [ "${chip_root}/src/lib/core", "${chip_root}/src/lib/dnssd/minimal_mdns", + "${chip_root}/src/lib/dnssd/minimal_mdns:default_policy", "${chip_root}/src/lib/dnssd/minimal_mdns/responders", "${chip_root}/src/lib/support:testing", "${nlunit_test_root}:nlunit-test", diff --git a/src/lib/dnssd/minimal_mdns/responders/tests/TestIPResponder.cpp b/src/lib/dnssd/minimal_mdns/responders/tests/TestIPResponder.cpp index 35fc5f7d642bfa..6a35323c5b51db 100644 --- a/src/lib/dnssd/minimal_mdns/responders/tests/TestIPResponder.cpp +++ b/src/lib/dnssd/minimal_mdns/responders/tests/TestIPResponder.cpp @@ -18,6 +18,8 @@ #include +#include +#include #include #include @@ -108,6 +110,21 @@ void TestIPv6(nlTestSuite * inSuite, void * inContext) responder.AddAllResponses(&packetInfo, &acc, ResponseConfiguration()); } +int Setup(void * inContext) +{ + mdns::Minimal::SetDefaultAddressPolicy(); + + CHIP_ERROR error = chip::Platform::MemoryInit(); + + return (error == CHIP_NO_ERROR) ? SUCCESS : FAILURE; +} + +int Teardown(void * inContext) +{ + chip::Platform::MemoryShutdown(); + return SUCCESS; +} + const nlTest sTests[] = { #if INET_CONFIG_ENABLE_IPV4 NL_TEST_DEF("TestIPv4", TestIPv4), // @@ -120,7 +137,7 @@ const nlTest sTests[] = { int TestIP(void) { - nlTestSuite theSuite = { "IP", sTests, nullptr, nullptr }; + nlTestSuite theSuite = { "IP", sTests, &Setup, &Teardown }; nlTestRunner(&theSuite, nullptr); return nlTestRunnerStats(&theSuite); }