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