Line data Source code
1 : /* 2 : * 3 : * Copyright (c) 2022 Project CHIP Authors 4 : * 5 : * Licensed under the Apache License, Version 2.0 (the "License"); 6 : * you may not use this file except in compliance with the License. 7 : * You may obtain a copy of the License at 8 : * 9 : * http://www.apache.org/licenses/LICENSE-2.0 10 : * 11 : * Unless required by applicable law or agreed to in writing, software 12 : * distributed under the License is distributed on an "AS IS" BASIS, 13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 : * See the License for the specific language governing permissions and 15 : * limitations under the License. 16 : */ 17 : #pragma once 18 : 19 : #include <lib/core/PeerId.h> 20 : #include <lib/support/IntrusiveList.h> 21 : #include <messaging/ReliableMessageProtocolConfig.h> 22 : #include <system/SystemClock.h> 23 : #include <system/SystemLayer.h> 24 : #include <transport/raw/PeerAddress.h> 25 : 26 : namespace chip { 27 : namespace AddressResolve { 28 : 29 : /// Contains resolve information received from nodes. Contains all information 30 : /// bits that are considered useful but does not contain a full DNSSD data 31 : /// structure since not all DNSSD data is useful during operational processing. 32 : struct ResolveResult 33 : { 34 : Transport::PeerAddress address; 35 : ReliableMessageProtocolConfig mrpRemoteConfig; 36 : bool supportsTcp = false; 37 : bool isICDOperatingAsLIT = false; 38 : 39 0 : ResolveResult() : address(Transport::Type::kUdp), mrpRemoteConfig(GetDefaultMRPConfig()) {} 40 : }; 41 : 42 : /// Represents an object interested in callbacks for a resolve operation. 43 : class NodeListener 44 : { 45 : public: 46 0 : NodeListener() = default; 47 0 : virtual ~NodeListener() = default; 48 : 49 : /// Callback executed once only for a lookup, when the final address of a 50 : /// node is considered to be the best choice for reachability. 51 : /// 52 : /// The callback is expected to be executed within the CHIP event loop 53 : /// thread. 54 : virtual void OnNodeAddressResolved(const PeerId & peerId, const ResolveResult & result) = 0; 55 : 56 : /// Node resolution failure - occurs only once for a lookup, when an address 57 : /// could not be resolved - generally due to a timeout or due to DNSSD 58 : /// infrastructure returning an error. 59 : /// 60 : /// The callback is expected to be executed within the CHIP event loop 61 : /// thread. 62 : virtual void OnNodeAddressResolutionFailed(const PeerId & peerId, CHIP_ERROR reason) = 0; 63 : }; 64 : 65 : /// Represents an active Address resolution lookup. 66 : /// 67 : /// Implementations extend this class with implementation-specific data like 68 : /// storing the 'last known good address' and 'scores' or any additional data 69 : /// required to figure out when a resolve is ok. 70 : class NodeLookupHandleBase : public IntrusiveListNodeBase<> 71 : { 72 : public: 73 0 : NodeLookupHandleBase() {} 74 0 : virtual ~NodeLookupHandleBase() {} 75 : 76 : // While active, resolve handles are maintained in an internal list 77 : // to be processed, so copying their values (i.e. pointers) is not 78 : // allowed. 79 : NodeLookupHandleBase(const NodeLookupHandleBase &) = delete; 80 : NodeLookupHandleBase & operator=(const NodeLookupHandleBase &) = delete; 81 : 82 0 : void SetListener(NodeListener * listener) { mListener = listener; } 83 0 : NodeListener * GetListener() { return mListener; } 84 : 85 : /// Convenience method that is more readable than 'IsInList' 86 0 : inline bool IsActive() const { return IntrusiveListNodeBase::IsInList(); } 87 : 88 : protected: 89 : NodeListener * mListener = nullptr; 90 : }; 91 : 92 : /// Represents a request to perform a single node lookup 93 : /// Contains all the information that should be looked for as well as 94 : /// extra timeout configurations. 95 : class NodeLookupRequest 96 : { 97 : public: 98 0 : NodeLookupRequest() {} 99 0 : NodeLookupRequest(const PeerId & peerId) : mPeerId(peerId) {} 100 : 101 : NodeLookupRequest(const NodeLookupRequest &) = default; 102 : NodeLookupRequest & operator=(const NodeLookupRequest &) = default; 103 : 104 0 : const PeerId & GetPeerId() const { return mPeerId; } 105 0 : System::Clock::Milliseconds32 GetMinLookupTime() const { return mMinLookupTimeMs; } 106 0 : System::Clock::Milliseconds32 GetMaxLookupTime() const { return mMaxLookupTimeMs; } 107 : 108 : /// The minimum lookup time is how much to wait for additional DNSSD 109 : /// queries even if a reply has already been received or to allow for 110 : /// additional heuristics regarding node choice to succeed. 111 : /// Example heuristics and considerations: 112 : /// - ping/ping6 could be used as an indicator of reachability. NOTE that 113 : /// not all devices may respond to ping, so this would only be an 114 : /// additional signal to accept/increase suitability score of an address 115 : /// and should NOT be used as a reject if no ping response 116 : /// - At lookup time, if the source ip of a dns reply is contained in the 117 : /// list of server ips, that is a great indication of routability and 118 : /// this minlookuptime could be bypassed altogether. 119 : /// 120 : /// Implementations for DNSSD may choose to return responses one by one 121 : /// for addresses (e.g. Platform mdns does this at the time this was written) 122 : /// or different interfaces will return separate 'done resolving' calls. 123 : /// 124 : /// If the min lookup time is set to 0, implementations are expected to call 125 : /// 'OnNodeAddressResolved' as soon as the first DNSSD response is received. 126 : NodeLookupRequest & SetMinLookupTime(System::Clock::Milliseconds32 value) 127 : { 128 : mMinLookupTimeMs = value; 129 : return *this; 130 : } 131 : 132 : /// The maximum lookup time is how much to wait until a TIMEOUT error is 133 : /// declared. 134 : /// 135 : /// If a DNSSD response is received before this max timeout, then 136 : /// OnNodeAddressResolved will be called on listener objects (immediately) 137 : /// if the first DNSSD reply arrives after MinLookupTimeMs has expired. 138 : NodeLookupRequest & SetMaxLookupTime(System::Clock::Milliseconds32 value) 139 : { 140 : mMaxLookupTimeMs = value; 141 : return *this; 142 : } 143 : 144 : private: 145 : static constexpr uint32_t kMinLookupTimeMsDefault = 200; 146 : static constexpr uint32_t kMaxLookupTimeMsDefault = 45000; 147 : 148 : PeerId mPeerId; 149 : System::Clock::Milliseconds32 mMinLookupTimeMs{ kMinLookupTimeMsDefault }; 150 : System::Clock::Milliseconds32 mMaxLookupTimeMs{ kMaxLookupTimeMsDefault }; 151 : }; 152 : 153 : /// These things are expected to be defined by the implementation header. 154 : namespace Impl { 155 : 156 : // The NodeLookup handle is a CONCRETE implementation that 157 : // MUST derive from NodeLookupHandleBase 158 : // 159 : // The underlying reason is that this handle is used to hold memory for 160 : // lookup metadata, so that resolvers do not need to maintain a likely unused 161 : // pool of 'active lookup' metadata. 162 : // 163 : // The side-effect of this is that the Impl::NodeLookupHandle is exposed to 164 : // clients for sizeof() memory purposes. 165 : // 166 : // Clients MUST only use the interface in NodeLookupHandleBase and assume all 167 : // other methods/content is implementation defined. 168 : class NodeLookupHandle; 169 : 170 : } // namespace Impl 171 : 172 : class Resolver 173 : { 174 : public: 175 : /// Enumeration defining how to handle cancel callbacks during operation 176 : /// cancellation. 177 : enum class FailureCallback 178 : { 179 : Call, // Call the failure callback 180 : Skip // do not call the failure callback (generally silent operation) 181 : }; 182 : 183 : virtual ~Resolver(); 184 : 185 : /// Expected to be called at least once before the resolver is ever 186 : /// used. 187 : /// 188 : /// Expected to override global setting of DNSSD callback for addres resolution 189 : /// and may use the underlying system layer for timers and other functionality. 190 : /// 191 : /// If called multiple times, it is expected that the input systemLayer does 192 : /// not change. 193 : virtual CHIP_ERROR Init(System::Layer * systemLayer) = 0; 194 : 195 : /// Initiate a node lookup for a particular node and use the specified 196 : /// Lookup handle to keep track of node resolution 197 : /// 198 : /// If this returns CHIP_NO_ERROR, the following is expected: 199 : /// - exactly one of the listener OnNodeAddressResolved 200 : /// or OnNodeAddressResolutionFailed will be called at a later time 201 : /// - handle must NOT be destroyed while the lookup is in progress (it 202 : /// is part of an internal 'lookup list') 203 : /// - handle must NOT be reused (the lookup is done on a per-node basis 204 : /// and maintains lookup data internally while the operation is still 205 : /// in progress) 206 : virtual CHIP_ERROR LookupNode(const NodeLookupRequest & request, Impl::NodeLookupHandle & handle) = 0; 207 : 208 : /// Inform the Lookup handle that the previous node lookup was not 209 : /// sufficient for the purpose of the caller (e.g establishing a session 210 : /// fails with the result of the previous lookup), and that more data is 211 : /// needed. 212 : /// 213 : /// This method must be called on a handle that is no longer active to 214 : /// succeed. 215 : /// 216 : /// If the handle is no longer active and has results that have not been 217 : /// delivered to the listener yet, the listener's OnNodeAddressResolved will 218 : /// be called synchronously before the method returns. Note that depending 219 : /// on the listener implementation this can end up destroying the handle 220 : /// and/or the listener. 221 : /// 222 : /// This method will return CHIP_NO_ERROR if and only if it has called 223 : /// OnNodeAddressResolved. 224 : /// 225 : /// This method will return CHIP_ERROR_INCORRECT_STATE if the handle is 226 : /// still active. 227 : /// 228 : /// This method will return CHIP_ERROR_NOT_FOUND if there are no more 229 : /// results. 230 : /// 231 : /// This method may return other errors in some cases. 232 : virtual CHIP_ERROR TryNextResult(Impl::NodeLookupHandle & handle) = 0; 233 : 234 : /// Stops an active lookup request. 235 : /// 236 : /// Caller controlls weather the `fail` callback of the handle is invoked or not by using 237 : /// the `cancel_method` argument. 238 : /// 239 : /// Note that there is no default cancel_method on purpose, so that the caller has to make 240 : /// a clear decision if the callback should or should not be invoked. 241 : virtual CHIP_ERROR CancelLookup(Impl::NodeLookupHandle & handle, FailureCallback cancel_method) = 0; 242 : 243 : /// Shut down any active resolves 244 : /// 245 : /// Will immediately fail any scheduled resolve calls and will refuse to register 246 : /// any new lookups until re-initialized. 247 : virtual void Shutdown() = 0; 248 : 249 : /// Expected to be provided by the implementation. 250 : static Resolver & Instance(); 251 : }; 252 : 253 : } // namespace AddressResolve 254 : } // namespace chip 255 : 256 : // outside the open space, include the required platform headers for the 257 : // actual implementation. 258 : // Expectations of this include: 259 : // - define the `Impl::NodeLookupHandle` deriving from NodeLookupHandleBase 260 : // - corresponding CPP file should provide a valid Resolver::Instance() 261 : // implementation 262 : #include CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER 263 : 264 : namespace chip { 265 : namespace AddressResolve { 266 : 267 : // Make the code easy to read: do not reach into Impl. 268 : using NodeLookupHandle = Impl::NodeLookupHandle; 269 : 270 : } // namespace AddressResolve 271 : } // namespace chip