| /* |
| * |
| * 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 <lib/core/PeerId.h> |
| #include <lib/support/IntrusiveList.h> |
| #include <messaging/ReliableMessageProtocolConfig.h> |
| #include <system/SystemClock.h> |
| #include <system/SystemLayer.h> |
| #include <transport/raw/PeerAddress.h> |
| |
| 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 <lib/address_resolve/AddressResolve_DefaultImpl.h> |
| #endif |
| |
| namespace chip { |
| namespace AddressResolve { |
| |
| // Make the code easy to read: do not reach into Impl. |
| using NodeLookupHandle = Impl::NodeLookupHandle; |
| |
| } // namespace AddressResolve |
| } // namespace chip |