/* * * 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 #include #include #include namespace chip { namespace AddressResolve { /// Contains resolve information received from nodes. Contains all information /// bits that are considered useful but does not contain a full DNSSD data /// structure since not all DNSSD data is useful during operational processing. struct ResolveResult { Transport::PeerAddress address; ReliableMessageProtocolConfig mrpRemoteConfig; bool supportsTcpServer = false; bool supportsTcpClient = false; bool isICDOperatingAsLIT = false; ResolveResult() : address(Transport::Type::kUdp), mrpRemoteConfig(GetDefaultMRPConfig()) {} }; /// Represents an object interested in callbacks for a resolve operation. class NodeListener { public: NodeListener() = default; virtual ~NodeListener() = default; /// Callback executed once only for a lookup, when the final address of a /// node is considered to be the best choice for reachability. /// /// The callback is expected to be executed within the CHIP event loop /// thread. virtual void OnNodeAddressResolved(const PeerId & peerId, const ResolveResult & result) = 0; /// Node resolution failure - occurs only once for a lookup, when an address /// could not be resolved - generally due to a timeout or due to DNSSD /// infrastructure returning an error. /// /// The callback is expected to be executed within the CHIP event loop /// thread. virtual void OnNodeAddressResolutionFailed(const PeerId & peerId, CHIP_ERROR reason) = 0; }; /// Represents an active Address resolution lookup. /// /// Implementations extend this class with implementation-specific data like /// storing the 'last known good address' and 'scores' or any additional data /// required to figure out when a resolve is ok. class NodeLookupHandleBase : public IntrusiveListNodeBase<> { public: NodeLookupHandleBase() {} virtual ~NodeLookupHandleBase() {} // While active, resolve handles are maintained in an internal list // to be processed, so copying their values (i.e. pointers) is not // allowed. NodeLookupHandleBase(const NodeLookupHandleBase &) = delete; NodeLookupHandleBase & operator=(const NodeLookupHandleBase &) = delete; void SetListener(NodeListener * listener) { mListener = listener; } NodeListener * GetListener() { return mListener; } /// Convenience method that is more readable than 'IsInList' inline bool IsActive() const { return IntrusiveListNodeBase::IsInList(); } protected: NodeListener * mListener = nullptr; }; /// Represents a request to perform a single node lookup /// Contains all the information that should be looked for as well as /// extra timeout configurations. class NodeLookupRequest { public: NodeLookupRequest() {} NodeLookupRequest(const PeerId & peerId) : mPeerId(peerId) {} NodeLookupRequest(const NodeLookupRequest &) = default; NodeLookupRequest & operator=(const NodeLookupRequest &) = default; const PeerId & GetPeerId() const { return mPeerId; } System::Clock::Milliseconds32 GetMinLookupTime() const { return mMinLookupTimeMs; } System::Clock::Milliseconds32 GetMaxLookupTime() const { return mMaxLookupTimeMs; } /// The minimum lookup time is how much to wait for additional DNSSD /// queries even if a reply has already been received or to allow for /// additional heuristics regarding node choice to succeed. /// Example heuristics and considerations: /// - ping/ping6 could be used as an indicator of reachability. NOTE that /// not all devices may respond to ping, so this would only be an /// additional signal to accept/increase suitability score of an address /// and should NOT be used as a reject if no ping response /// - At lookup time, if the source ip of a dns reply is contained in the /// list of server ips, that is a great indication of routability and /// this minlookuptime could be bypassed altogether. /// /// Implementations for DNSSD may choose to return responses one by one /// for addresses (e.g. Platform mdns does this at the time this was written) /// or different interfaces will return separate 'done resolving' calls. /// /// If the min lookup time is set to 0, implementations are expected to call /// 'OnNodeAddressResolved' as soon as the first DNSSD response is received. NodeLookupRequest & SetMinLookupTime(System::Clock::Milliseconds32 value) { mMinLookupTimeMs = value; return *this; } /// The maximum lookup time is how much to wait until a TIMEOUT error is /// declared. /// /// If a DNSSD response is received before this max timeout, then /// OnNodeAddressResolved will be called on listener objects (immediately) /// if the first DNSSD reply arrives after MinLookupTimeMs has expired. NodeLookupRequest & SetMaxLookupTime(System::Clock::Milliseconds32 value) { mMaxLookupTimeMs = value; return *this; } private: static_assert((CHIP_CONFIG_ADDRESS_RESOLVE_MIN_LOOKUP_TIME_MS) <= (CHIP_CONFIG_ADDRESS_RESOLVE_MAX_LOOKUP_TIME_MS), "AddressResolveMinLookupTime must be equal or less than AddressResolveMaxLookupTime"); static_assert((CHIP_CONFIG_ADDRESS_RESOLVE_MIN_LOOKUP_TIME_MS) >= 0, "AddressResolveMinLookupTime must be equal or greater than 0"); static_assert((CHIP_CONFIG_ADDRESS_RESOLVE_MAX_LOOKUP_TIME_MS) < UINT32_MAX, "AddressResolveMaxLookupTime must be less than UINT32_MAX"); static constexpr uint32_t kMinLookupTimeMsDefault = CHIP_CONFIG_ADDRESS_RESOLVE_MIN_LOOKUP_TIME_MS; static constexpr uint32_t kMaxLookupTimeMsDefault = CHIP_CONFIG_ADDRESS_RESOLVE_MAX_LOOKUP_TIME_MS; PeerId mPeerId; System::Clock::Milliseconds32 mMinLookupTimeMs{ kMinLookupTimeMsDefault }; System::Clock::Milliseconds32 mMaxLookupTimeMs{ kMaxLookupTimeMsDefault }; }; /// These things are expected to be defined by the implementation header. namespace Impl { // The NodeLookup handle is a CONCRETE implementation that // MUST derive from NodeLookupHandleBase // // The underlying reason is that this handle is used to hold memory for // lookup metadata, so that resolvers do not need to maintain a likely unused // pool of 'active lookup' metadata. // // The side-effect of this is that the Impl::NodeLookupHandle is exposed to // clients for sizeof() memory purposes. // // Clients MUST only use the interface in NodeLookupHandleBase and assume all // other methods/content is implementation defined. class NodeLookupHandle; } // namespace Impl class Resolver { public: /// Enumeration defining how to handle cancel callbacks during operation /// cancellation. enum class FailureCallback { Call, // Call the failure callback Skip // do not call the failure callback (generally silent operation) }; virtual ~Resolver(); /// Expected to be called at least once before the resolver is ever /// used. /// /// Expected to override global setting of DNSSD callback for addres resolution /// and may use the underlying system layer for timers and other functionality. /// /// If called multiple times, it is expected that the input systemLayer does /// not change. virtual CHIP_ERROR Init(System::Layer * systemLayer) = 0; /// Initiate a node lookup for a particular node and use the specified /// Lookup handle to keep track of node resolution /// /// If this returns CHIP_NO_ERROR, the following is expected: /// - exactly one of the listener OnNodeAddressResolved /// or OnNodeAddressResolutionFailed will be called at a later time /// - handle must NOT be destroyed while the lookup is in progress (it /// is part of an internal 'lookup list') /// - handle must NOT be reused (the lookup is done on a per-node basis /// and maintains lookup data internally while the operation is still /// in progress) virtual CHIP_ERROR LookupNode(const NodeLookupRequest & request, Impl::NodeLookupHandle & handle) = 0; /// Inform the Lookup handle that the previous node lookup was not /// sufficient for the purpose of the caller (e.g establishing a session /// fails with the result of the previous lookup), and that more data is /// needed. /// /// This method must be called on a handle that is no longer active to /// succeed. /// /// If the handle is no longer active and has results that have not been /// delivered to the listener yet, the listener's OnNodeAddressResolved will /// be called synchronously before the method returns. Note that depending /// on the listener implementation this can end up destroying the handle /// and/or the listener. /// /// This method will return CHIP_NO_ERROR if and only if it has called /// OnNodeAddressResolved. /// /// This method will return CHIP_ERROR_INCORRECT_STATE if the handle is /// still active. /// /// This method will return CHIP_ERROR_NOT_FOUND if there are no more /// results. /// /// This method may return other errors in some cases. virtual CHIP_ERROR TryNextResult(Impl::NodeLookupHandle & handle) = 0; /// Stops an active lookup request. /// /// Caller controlls weather the `fail` callback of the handle is invoked or not by using /// the `cancel_method` argument. /// /// Note that there is no default cancel_method on purpose, so that the caller has to make /// a clear decision if the callback should or should not be invoked. virtual CHIP_ERROR CancelLookup(Impl::NodeLookupHandle & handle, FailureCallback cancel_method) = 0; /// Shut down any active resolves /// /// Will immediately fail any scheduled resolve calls and will refuse to register /// any new lookups until re-initialized. virtual void Shutdown() = 0; /// Expected to be provided by the implementation. static Resolver & Instance(); }; } // namespace AddressResolve } // namespace chip // Include the required platform header for the actual implementation, if defined. // Otherwise assume the default implementation is being used. // Expectations of this include: // - define the `Impl::NodeLookupHandle` deriving from NodeLookupHandleBase // - corresponding CPP file should provide a valid Resolver::Instance() // implementation #ifdef CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER #include CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER #else #include #endif namespace chip { namespace AddressResolve { // Make the code easy to read: do not reach into Impl. using NodeLookupHandle = Impl::NodeLookupHandle; } // namespace AddressResolve } // namespace chip