• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2021, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #ifndef DNS_DSO_HPP_
30 #define DNS_DSO_HPP_
31 
32 #include "openthread-core-config.h"
33 
34 #if OPENTHREAD_CONFIG_DNS_DSO_ENABLE
35 
36 #include <openthread/platform/dso_transport.h>
37 
38 #include "common/array.hpp"
39 #include "common/as_core_type.hpp"
40 #include "common/const_cast.hpp"
41 #include "common/encoding.hpp"
42 #include "common/linked_list.hpp"
43 #include "common/locator.hpp"
44 #include "common/message.hpp"
45 #include "common/non_copyable.hpp"
46 #include "common/num_utils.hpp"
47 #include "common/timer.hpp"
48 #include "net/dns_types.hpp"
49 #include "net/socket.hpp"
50 
51 /**
52  * @file
53  *   This file includes definitions for the DNS Stateful Operations (DSO) per RFC-8490.
54  */
55 
56 struct otPlatDsoConnection
57 {
58 };
59 
60 namespace ot {
61 namespace Dns {
62 
63 extern "C" otPlatDsoConnection *otPlatDsoAccept(otInstance *aInstance, const otSockAddr *aPeerSockAddr);
64 
65 extern "C" void otPlatDsoHandleConnected(otPlatDsoConnection *aConnection);
66 extern "C" void otPlatDsoHandleReceive(otPlatDsoConnection *aConnection, otMessage *aMessage);
67 extern "C" void otPlatDsoHandleDisconnected(otPlatDsoConnection *aConnection, otPlatDsoDisconnectMode aMode);
68 
69 /**
70  * Implements DNS Stateful Operations (DSO).
71  */
72 class Dso : public InstanceLocator, private NonCopyable
73 {
74     friend otPlatDsoConnection *otPlatDsoAccept(otInstance *aInstance, const otSockAddr *aPeerSockAddr);
75 
76 public:
77     /**
78      * Infinite Keep Alive or Inactivity timeout value.
79      *
80      * This value can be used for either Keep Alive or Inactivity timeout interval. It practically disables the
81      * timeout.
82      */
83     static constexpr uint32_t kInfiniteTimeout = 0xffffffff;
84 
85     /**
86      * Default Keep Alive or Inactivity timeout value (in msec).
87      *
88      * On a new DSO session, if no explicit DSO Keep Alive message exchange has taken place, the default value for both
89      * timeouts is 15 seconds [RFC 8490 - 6.2].
90      */
91     static constexpr uint32_t kDefaultTimeout = TimeMilli::SecToMsec(15);
92 
93     /**
94      * The minimum allowed Keep Alive interval (in msec).
95      *
96      * Any value less than ten seconds is invalid [RFC 8490 - 6.5.2].
97      */
98     static constexpr uint32_t kMinKeepAliveInterval = TimeMilli::SecToMsec(10);
99 
100     /**
101      * The maximum wait time for a DSO response to a DSO request (in msec).
102      */
103     static constexpr uint32_t kResponseTimeout = OPENTHREAD_CONFIG_DNS_DSO_RESPONSE_TIMEOUT;
104 
105     /**
106      * The maximum wait time for a connection to be established (in msec).
107      */
108     static constexpr uint32_t kConnectingTimeout = OPENTHREAD_CONFIG_DNS_DSO_CONNECTING_TIMEOUT;
109 
110     /**
111      * The minimum Inactivity wait time on a server before closing a connection.
112      *
113      * A server will abort an idle session after five seconds or twice the inactivity timeout value, whichever is
114      * greater [RFC 8490 - 6.4.1].
115      */
116     static constexpr uint32_t kMinServerInactivityWaitTime = TimeMilli::SecToMsec(5);
117 
118     /**
119      * Represents a DSO TLV.
120      */
121     OT_TOOL_PACKED_BEGIN
122     class Tlv
123     {
124     public:
125         typedef uint16_t Type; ///< DSO TLV type.
126 
127         static constexpr Type kReservedType          = 0; ///< Reserved TLV type.
128         static constexpr Type kKeepAliveType         = 1; ///< Keep Alive TLV type.
129         static constexpr Type kRetryDelayType        = 2; ///< Retry Delay TLV type.
130         static constexpr Type kEncryptionPaddingType = 3; ///< Encryption Padding TLV type.
131 
132         /**
133          * Initializes the `Tlv` instance with a given type and length.
134          *
135          * @param[in] aType    The TLV type.
136          * @param[in] aLength  The TLV length.
137          */
Init(Type aType,uint16_t aLength)138         void Init(Type aType, uint16_t aLength)
139         {
140             mType   = BigEndian::HostSwap16(aType);
141             mLength = BigEndian::HostSwap16(aLength);
142         }
143 
144         /**
145          * Gets the TLV type.
146          *
147          * @returns The TLV type.
148          */
GetType(void) const149         Type GetType(void) const { return BigEndian::HostSwap16(mType); }
150 
151         /**
152          * Gets the TLV length.
153          *
154          * @returns The TLV length (in bytes).
155          */
GetLength(void) const156         uint16_t GetLength(void) const { return BigEndian::HostSwap16(mLength); }
157 
158         /**
159          * Returns the total size of the TLV (including the type and length fields).
160          *
161          * @returns The total size (number of bytes) of the TLV.
162          */
GetSize(void) const163         uint32_t GetSize(void) const { return sizeof(Tlv) + static_cast<uint32_t>(GetLength()); }
164 
165     private:
166         Type     mType;
167         uint16_t mLength;
168     } OT_TOOL_PACKED_END;
169 
170     /**
171      * Represents a DSO connection to a peer.
172      */
173     class Connection : public otPlatDsoConnection,
174                        public InstanceLocator,
175                        public LinkedListEntry<Connection>,
176                        private NonCopyable
177     {
178         friend class Dso;
179         friend class LinkedList<Connection>;
180         friend class LinkedListEntry<Connection>;
181         friend void otPlatDsoHandleConnected(otPlatDsoConnection *aConnection);
182         friend void otPlatDsoHandleReceive(otPlatDsoConnection *aConnection, otMessage *aMessage);
183         friend void otPlatDsoHandleDisconnected(otPlatDsoConnection *aConnection, otPlatDsoDisconnectMode aMode);
184 
185     public:
186         typedef uint16_t MessageId; ///< This type represents a DSO Message Identifier.
187 
188         /**
189          * Defines the `Connection` states.
190          */
191         enum State : uint8_t
192         {
193             kStateDisconnected,            ///< Disconnected.
194             kStateConnecting,              ///< Connecting to peer.
195             kStateConnectedButSessionless, ///< Connected but DSO session is not yet established.
196             kStateEstablishingSession,     ///< Establishing DSO session.
197             kStateSessionEstablished,      ///< DSO session is established.
198         };
199 
200         /**
201          * Defines the disconnect modes.
202          */
203         enum DisconnectMode : uint8_t
204         {
205             kGracefullyClose = OT_PLAT_DSO_DISCONNECT_MODE_GRACEFULLY_CLOSE, ///< Close the connection gracefully.
206             kForciblyAbort   = OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT,   ///< Forcibly abort the connection.
207         };
208 
209         /**
210          * Defines the disconnect reason.
211          */
212         enum DisconnectReason : uint8_t
213         {
214             kReasonFailedToConnect,         ///< Failed to connect (e.g., peer did not accept or timed out).
215             kReasonResponseTimeout,         ///< Response timeout (no response from peer after `kResponseTimeout`).
216             kReasonPeerDoesNotSupportDso,   ///< Peer does not support DSO.
217             kReasonPeerClosed,              ///< Peer closed the connection gracefully.
218             kReasonPeerAborted,             ///< Peer forcibly aborted the connection.
219             kReasonInactivityTimeout,       ///< Connection closed or aborted due to Inactivity timeout.
220             kReasonKeepAliveTimeout,        ///< Connection closed due to Keep Alive timeout.
221             kReasonServerRetryDelayRequest, ///< Connection closed due to server requesting retry delay.
222             kReasonPeerMisbehavior,         ///< Aborted due to peer misbehavior (fatal error).
223             kReasonUnknown,                 ///< Unknown reason.
224         };
225 
226         /**
227          * Defines the callback functions used by a `Connection`.
228          */
229         class Callbacks
230         {
231             friend class Connection;
232 
233         public:
234             /**
235              * This callback signals that the connection is established (entering `kStateConnectedButSessionless`).
236              *
237              * On a client, this callback can be used to send the first DSO request to start establishing the session.
238              *
239              * @param[in] aConnection   A reference to the connection.
240              */
241             typedef void (&HandleConnected)(Connection &aConnection);
242 
243             /**
244              * This callback signals that the DSO session is established (entering `kStateSessionEstablished`).
245              *
246              * @param[in] aConnection   A reference to the connection.
247              */
248             typedef void (&HandleSessionEstablished)(Connection &aConnection);
249 
250             /**
251              * This callback signals that the DSO session is disconnected by DSO module or the peer.
252              *
253              * After this callback is invoked the DSO module will no longer track the related `Connection` instance. It
254              * can be reclaimed by the caller, e.g., freed if it was heap allocated.
255              *
256              * The `Connection::GetDisconnectReason()` can be used the get the disconnect reason.
257              *
258              * @param[in] aConnection   A reference to the connection.
259              */
260             typedef void (&HandleDisconnected)(Connection &aConnection);
261 
262             /**
263              * This callback requests processing of a received DSO request message.
264              *
265              * If the processing is successful a response can be sent using `Connection::SendResponseMessage()` method.
266              *
267              * Note that @p aMessage is a `const` so the ownership of the message is not passed in this callback.
268              * The message will be freed by the `Connection` after returning from this callback, so if it needs to be
269              * persisted the callback implementation needs to create its own copy.
270              *
271              * The offset in @p aMessage is set to point to the start of the DSO TLVs. DSO module only reads and
272              * validates the first TLV (primary TLV) from the message. It is up to the callback implementation to parse
273              * and validate the rest of the TLVs in the message.
274              *
275              * @param[in] aConnection      A reference to the connection.
276              * @param[in] aMessageId       The message ID of the received request.
277              * @param[in] aMessage         The received message. Message offset is set to the start of the TLVs.
278              * @param[in] aPrimaryTlvType  The primary TLV type.
279              *
280              * @retval kErrorSuccess   The request message was processed successfully.
281              * @retval kErrorNotFound  The @p aPrimaryTlvType is not known (not supported). This error triggers a DNS
282              *                         response with error code 11 "DSO TLV TYPE not implemented" to be sent.
283              * @retval kErrorAbort     Fatal error (misbehavior by peer). This triggers aborting of the connection.
284              */
285             typedef Error (&ProcessRequestMessage)(Connection    &aConnection,
286                                                    MessageId      aMessageId,
287                                                    const Message &aMessage,
288                                                    Tlv::Type      aPrimaryTlvType);
289 
290             /**
291              * This callback requests processing of a received DSO unidirectional message.
292              *
293              * Similar to `ProcessRequestMessage()` the ownership of @p aMessage is not passed in this callback.
294              *
295              * The offset in @p aMessage is set to point to the start of the DSO TLVs. DSO module only reads and
296              * validates the first TLV (primary TLV) from the message. It is up to the callback implementation to parse
297              * and validate the rest of the TLVs in the message.
298              *
299              * @param[in] aConnection      A reference to the connection.
300              * @param[in] aMessage         The received message. Message offset is set to the start of the TLVs.
301              * @param[in] aPrimaryTlvType  The primary TLV type.
302              *
303              * @retval kErrorSuccess  The unidirectional message was processed successfully.
304              * @retval kErrorAbort    Fatal error (misbehavior by peer). This triggers aborting of the connection. If
305              *                        @p aPrimaryTlvType is not known in a unidirectional message, it is a fatal error.
306              */
307             typedef Error (&ProcessUnidirectionalMessage)(Connection    &aConnection,
308                                                           const Message &aMessage,
309                                                           Tlv::Type      aPrimaryTlvType);
310 
311             /**
312              * This callback requests processing of a received DSO response message.
313              *
314              * Before invoking this callback, the `Connection` implementation already verifies that:
315              *
316              *  (1) this response is for a pending previously sent request (based on the message ID),
317              *  (2) if no error response code in DNS @p aHeader and the response contains a response primary TLV, the
318              *      the response primary TLV matches the request primary TLV.
319              *
320              * Similar to `ProcessRequestMessage()` the ownership of @p aMessage is not passed in this callback.
321              *
322              * The offset in @p aMessage is set to point to the start of the DSO TLVs. DSO module only reads and
323              * validates the first TLV (primary TLV) from the message. It is up to the callback implementation to parse
324              * and validate the rest of the TLVs in the message.
325              *
326              * @param[in] aConnection        A reference to the connection.
327              * @param[in] aHeader            The DNS header of the received response.
328              * @param[in] aMessage           The received message. Message offset is set to the start of the TLVs.
329              * @param[in] aResponseTlvType   The primary TLV type in the response message, or `Tlv::kReservedType` if
330              *                               the response contains no TLV.
331              * @param[in] aRequestTlvType    The primary TLV type of the corresponding request message.
332              *
333              * @retval kErrorSuccess  The message was processed successfully.
334              * @retval kErrorAbort    Fatal error (misbehavior by peer). This triggers aborting of the connection.
335              */
336             typedef Error (&ProcessResponseMessage)(Connection        &aConnection,
337                                                     const Dns::Header &aHeader,
338                                                     const Message     &aMessage,
339                                                     Tlv::Type          aResponseTlvType,
340                                                     Tlv::Type          aRequestTlvType);
341             /**
342              * Initializes a `Callbacks` object setting all the callback functions.
343              *
344              * @param[in] aHandleConnected               The `HandleConnected` callback.
345              * @param[in] aHandleSessionEstablished      The `HandleSessionEstablished` callback.
346              * @param[in] aHandleDisconnected            The `HandleDisconnected` callback.
347              * @param[in] aProcessRequestMessage         The `ProcessRequestMessage` callback.
348              * @param[in] aProcessUnidirectionalMessage  The `ProcessUnidirectionalMessage` callback.
349              * @param[in] aProcessResponseMessage        The `ProcessResponseMessage` callback.
350              */
Callbacks(HandleConnected aHandleConnected,HandleSessionEstablished aHandleSessionEstablished,HandleDisconnected aHandleDisconnected,ProcessRequestMessage aProcessRequestMessage,ProcessUnidirectionalMessage aProcessUnidirectionalMessage,ProcessResponseMessage aProcessResponseMessage)351             Callbacks(HandleConnected              aHandleConnected,
352                       HandleSessionEstablished     aHandleSessionEstablished,
353                       HandleDisconnected           aHandleDisconnected,
354                       ProcessRequestMessage        aProcessRequestMessage,
355                       ProcessUnidirectionalMessage aProcessUnidirectionalMessage,
356                       ProcessResponseMessage       aProcessResponseMessage)
357                 : mHandleConnected(aHandleConnected)
358                 , mHandleSessionEstablished(aHandleSessionEstablished)
359                 , mHandleDisconnected(aHandleDisconnected)
360                 , mProcessRequestMessage(aProcessRequestMessage)
361                 , mProcessUnidirectionalMessage(aProcessUnidirectionalMessage)
362                 , mProcessResponseMessage(aProcessResponseMessage)
363             {
364             }
365 
366         private:
367             HandleConnected              mHandleConnected;
368             HandleSessionEstablished     mHandleSessionEstablished;
369             HandleDisconnected           mHandleDisconnected;
370             ProcessRequestMessage        mProcessRequestMessage;
371             ProcessUnidirectionalMessage mProcessUnidirectionalMessage;
372             ProcessResponseMessage       mProcessResponseMessage;
373         };
374 
375         /**
376          * Initializes a `Connection` instance.
377          *
378          * The `kDefaultTimeout` will be used for @p aInactivityTimeout and @p aKeepAliveInterval. The
379          * @p aKeepAliveInterval MUST NOT be less than `kMinKeepAliveInterval`.
380          *
381          * @param[in] aInstance           The OpenThread instance.
382          * @param[in] aPeerSockAddr       The peer socket address.
383          * @param[in] aCallbacks          A reference to the `Callbacks` instance used by the `Connection`.
384          * @param[in] aInactivityTimeout  The Inactivity timeout interval (in msec).
385          * @param[in] aKeepAliveInterval  The Keep Alive timeout interval (in msec).
386          */
387         Connection(Instance            &aInstance,
388                    const Ip6::SockAddr &aPeerSockAddr,
389                    Callbacks           &aCallbacks,
390                    uint32_t             aInactivityTimeout = kDefaultTimeout,
391                    uint32_t             aKeepAliveInterval = kDefaultTimeout);
392 
393         /**
394          * Gets the current state of the `Connection`.
395          *
396          * @returns The `Connection` state.
397          */
GetState(void) const398         State GetState(void) const { return mState; }
399 
400         /**
401          * Returns the `Connection` peer socket address.
402          *
403          * @returns The peer socket address.
404          */
GetPeerSockAddr(void) const405         const Ip6::SockAddr &GetPeerSockAddr(void) const { return mPeerSockAddr; }
406 
407         /**
408          * Indicates whether or not the device is acting as a DSO server on this `Connection`.
409          *
410          * Server is the software entity with a listening socket, awaiting incoming connection requests.
411          *
412          * @retval TRUE    Device is acting as a server on this connection.
413          * @retval FALSE   Device is acting as a client on this connection.
414          */
IsServer(void) const415         bool IsServer(void) const { return mIsServer; }
416 
417         /**
418          * Indicates whether or not the device is acting as a DSO client on this `Connection`.
419          *
420          * Client is the software entity that initiates a connection to the server's listening socket.
421          *
422          * @retval TRUE    Device is acting as a client on this connection.
423          * @retval FALSE   Device is acting as a server on this connection.
424          */
IsClient(void) const425         bool IsClient(void) const { return !mIsServer; }
426 
427         /**
428          * Allocates a new DSO message.
429          *
430          * @returns A pointer to the allocated message or `nullptr` if out of message buffers.
431          */
432         Message *NewMessage(void);
433 
434         /**
435          * Requests the device to initiate a connection (connect as a client) to the peer (acting as a
436          * server).
437          *
438          * MUST be called when `Connection` is `kStateDisconnected` state.
439          *
440          * After calling `Connect()`, either
441          * - `Callbacks::HandleConnected()` is invoked when connection is successfully established, or
442          * - `Callbacks::HandleDisconnected()` is invoked if the connection cannot be established (e.g., peer does not
443          *   accept it or we time out waiting for it). The disconnect reason is set to `kReasonFailedToConnect`.
444          *
445          * Calling `Connect()` passes the control and ownership of the `Connection` instance to the DSO module (which
446          * adds the `Connection` into a list of client connections - see `Dso::FindClientConnection()`). The ownership
447          * is passed back to the caller when the `Connection` gets disconnected, i.e., when either
448          *  - the user requests a disconnect by an explicit call to `Disconnect()` method, or,
449          *  - when `HandleDisconnected()` callback is invoked (after its is closed by DSO module itself or by peer).
450          */
451         void Connect(void);
452 
453         /**
454          * Requests the connection to be disconnected.
455          *
456          * Note that calling `Disconnect()` does not trigger the `Callbacks::HandleDisconnected()` to be invoked (as
457          * this callback is used when DSO module itself or the peer disconnects the connections).
458          *
459          * After the call to `Disconnect()` the caller can take back ownership of the `Connection` (e.g., can free the
460          * `Connection` instance if it was heap allocated).
461          *
462          * @param[in] aMode    Determines whether to close the connection gracefully or forcibly abort the connection.
463          * @param[in] aReason  The disconnect reason.
464          */
465         void Disconnect(DisconnectMode aMode, DisconnectReason aReason);
466 
467         /**
468          * Returns the last disconnect reason.
469          *
470          * @returns The last disconnect reason.
471          */
GetDisconnectReason(void) const472         DisconnectReason GetDisconnectReason(void) const { return mDisconnectReason; }
473 
474         /**
475          * Implicitly marks the DSO session as established (set state to `kStateSessionEstablished`).
476          *
477          * MUST be called when `Connection is in `kStateConnectedButSessionless` state.
478          *
479          * The DSO module itself will mark the session as established after the first successful DSO message exchange
480          * (sending a request message from client and receiving a response from server).
481          *
482          * Is intended for implicit DSO session establishment where it may be known in advance by some
483          * external means that both client and server support DSO and then the session may be established as soon as
484          * the connection is established.
485          */
486         void MarkSessionEstablished(void);
487 
488         /**
489          * Sends a DSO request message.
490          *
491          * MUST be called when `Connection` is in certain states depending on whether it is acting as a
492          * client or server:
493          * - On client, a request message can be sent after connection is established (`kStateConnectedButSessionless`).
494          *   The first request is used to establish the DSO session. While in `kStateEstablishingSession` or
495          *   `kStateSessionEstablished` other DSO request messages can be sent to the server.
496          * - On server, a request can be sent only after DSO session is established (`kStateSessionEstablished`).
497          *
498          * The prepared message needs to contain the DSO TLVs. The DNS header will be added by the DSO module itself.
499          * Also there is no need to append the "Encryption Padding TLV" to the message as it will be added by the DSO
500          * module before sending the message to the transport layer.
501          *
502          * On success (when this method returns `kErrorNone`) it takes the ownership of the @p aMessage. On failure the
503          * caller still owns the message and may need to free it.
504          *
505          * @param[in]  aMessage          The DSO request message to send.
506          * @param[out] aMessageId        A reference to output the message ID used for the transmission (may be used by
507          *                               the caller to track the response from `Callbacks::ProcessResponseMessage()`).
508          * @param[in]  aResponseTimeout  The response timeout in msec (default value is `kResponseTimeout`)
509          *
510          * @retval  kErrorNone      Successfully sent the DSO request message and updated @p aMessageId.
511          * @retval  kErrorNoBufs    Failed to allocate new buffer to prepare the message (append header or padding).
512          */
513         Error SendRequestMessage(Message   &aMessage,
514                                  MessageId &aMessageId,
515                                  uint32_t   aResponseTimeout = kResponseTimeout);
516 
517         /**
518          * Sends a DSO unidirectional message.
519          *
520          * MUST be called when session is established (in `kStateSessionEstablished` state).
521          *
522          * Similar to `SendRequestMessage()` method, only TLV(s) need to be included in the message. The DNS header and
523          * Encryption Padding TLV will be added by the DSO module.
524          *
525          * On success (when this method returns `kErrorNone`) it takes the ownership of the @p aMessage. On failure the
526          * caller still owns the message and may need to free it.
527          *
528          * @param[in] aMessage      The DSO unidirectional message to send.
529          *
530          * @retval  kErrorNone      Successfully sent the DSO message.
531          * @retval  kErrorNoBufs    Failed to allocate new buffer to prepare the message (append header or padding).
532          */
533         Error SendUnidirectionalMessage(Message &aMessage);
534 
535         /**
536          * Sends a DSO response message for a received request message.
537          *
538          * Similar to `SendRequestMessage()` method, only TLV(s) need to be included in the message. The DNS header and
539          * Encryption Padding TLV will be added by DSO module.
540          *
541          * On success (when this method returns `kErrorNone`) it takes the ownership of the @p aMessage. On failure the
542          * caller still owns the message and may need to free it.
543          *
544          * @param[in] aMessage      The DSO response message to send.
545          * @param[in] aResponseId   The message ID to use for the response.
546          *
547          * @retval  kErrorNone      Successfully sent the DSO response message.
548          * @retval  kErrorNoBufs    Failed to allocate new buffer to prepare the message (append header or padding).
549          */
550         Error SendResponseMessage(Message &aMessage, MessageId aResponseId);
551 
552         /**
553          * Returns the Keep Alive timeout interval (in msec).
554          *
555          * On client, this indicates the value granted by server, on server the value to grant.
556          *
557          * @returns The keep alive timeout interval (in msec).
558          */
GetKeepAliveInterval(void) const559         uint32_t GetKeepAliveInterval(void) const { return mKeepAlive.GetInterval(); }
560 
561         /**
562          * Returns the Inactivity timeout interval (in msec).
563          *
564          * On client, this indicates the value granted by server, on server the value to grant.
565          *
566          * @returns The inactivity timeout interval (in msec).
567          */
GetInactivityTimeout(void) const568         uint32_t GetInactivityTimeout(void) const { return mInactivity.GetInterval(); }
569 
570         /**
571          * Sends a Keep Alive message.
572          *
573          * MUST be called when `Connection` is in certain states depending on whether it is acting as a
574          * client or server:
575          * - On client, it can be called in any state after the connection is established. Sending Keep Alive message
576          *   can be used to initiate establishing DSO session.
577          * - On server, it can be used only after session is established (`kStateSessionEstablished`).
578          *
579          * On a client, the Keep Alive message is sent as a request message. On server it is sent as a unidirectional
580          * message.
581          *
582          * @retval  kErrorNone     Successfully prepared and sent a Keep Alive message.
583          * @retval  kErrorNoBufs   Failed to allocate message to send.
584          */
585         Error SendKeepAliveMessage(void);
586 
587         /**
588          * Sets the Inactivity and Keep Alive timeout intervals.
589          *
590          * On client, the specified timeout intervals are used in Keep Alive request message, i.e., they are the values
591          * that client would wish to get. On server, the given timeout intervals specify the values that server would
592          * grant to a client upon receiving a Keep Alive request from it.
593          *
594          * Can be called in any `Connection` state. If current state allows, calling this method will also
595          * trigger sending of a Keep Alive message (as if `SendKeepAliveMessage()` is also called). For states which
596          * trigger the tx, see `SendKeepAliveMessage()`.
597          *
598          * The special value `kInfiniteTimeout` can be used for either Inactivity or Keep Alive interval which disables
599          * the corresponding timer. The Keep Alive interval should be larger than or equal to minimum
600          * `kMinKeepAliveInterval`, otherwise `kErrorInvalidArgs` is returned.
601          *
602          * @param[in] aInactivityTimeout  The Inactivity timeout (in msec).
603          * @param[in] aKeepAliveInterval  The Keep Alive interval (in msec).
604          *
605          * @retval kErrorNone          Successfully set the timeouts and sent a Keep Alive message.
606          * @retval kErrorInvalidArgs   The given timeouts are not valid.
607          * @retval kErrorNoBufs        Failed to allocate message to send.
608          */
609         Error SetTimeouts(uint32_t aInactivityTimeout, uint32_t aKeepAliveInterval);
610 
611         /**
612          * Enables/disables long-lived operation on the session.
613          *
614          * When a long-lived operation is active, the Inactivity timeout is always cleared, i.e., the DSO session stays
615          * connected even if no messages are exchanged.
616          *
617          * @param[in] aLongLivedOperation    A boolean indicating whether or not a long-lived operation is active.
618          */
619         void SetLongLivedOperation(bool aLongLivedOperation);
620 
621         /**
622          * Sends a unidirectional Retry Delay message from server to client.
623          *
624          * MUST be used on a server only and when DSO session is already established, i.e., in state
625          * `kStateSessionEstablished`. It sends a unidirectional Retry Delay message to client requesting it to close
626          * the connection and not connect again for at least the specified delay amount.
627          *
628          * Note that calling `SendRetryDelayMessage()` does not by itself close the connection on server side. It is
629          * up to the user of the DSO module to implement a wait time delay before deciding to close/abort the connection
630          * from server side, in case the client does not close it upon receiving the Retry Delay message.
631          *
632          * @param[in] aDelay          The retry delay interval (in msec).
633          * @param[in] aResponseCode   The DNS RCODE to include in the Retry Delay message.
634          *
635          * @retval  kErrorNone        Successfully prepared and sent a Retry Delay message to client.
636          * @retval  kErrorNoBufs      Failed to allocate message to send.
637          */
638         Error SendRetryDelayMessage(uint32_t              aDelay,
639                                     Dns::Header::Response aResponseCode = Dns::Header::kResponseSuccess);
640 
641         /**
642          * Returns the requested retry delay interval (in msec) by server.
643          *
644          * MUST be used after a `HandleDisconnected()` callback with `kReasonServerRetryDelayRequest`
645          *
646          * @returns The retry delay interval requested by server.
647          */
GetRetryDelay(void) const648         uint32_t GetRetryDelay(void) const { return mRetryDelay; }
649 
650         /**
651          * Returns the DNS error code in the last retry delay message received on client from server.
652          *
653          * MUST be used after a `HandleDisconnected()` callback with `kReasonServerRetryDelayRequest`
654          *
655          * @returns The DNS error code in the last Retry Delay message received on client from server.
656          */
GetRetryDelayErrorCode(void) const657         Dns::Header::Response GetRetryDelayErrorCode(void) const { return mRetryDelayErrorCode; }
658 
659     private:
660         enum MessageType : uint8_t
661         {
662             kRequestMessage,
663             kResponseMessage,
664             kUnidirectionalMessage,
665         };
666 
667         // Info about pending request messages (message ID, primary TLV type, and response timeout).
668         class PendingRequests
669         {
670         public:
671             static constexpr uint8_t kMaxPendingRequests = OPENTHREAD_CONFIG_DNS_DSO_MAX_PENDING_REQUESTS;
672 
Clear(void)673             void  Clear(void) { mRequests.Clear(); }
IsEmpty(void) const674             bool  IsEmpty(void) const { return mRequests.IsEmpty(); }
675             bool  Contains(MessageId aMessageId, Tlv::Type &aPrimaryTlvType) const;
676             Error Add(MessageId aMessageId, Tlv::Type aPrimaryTlvType, TimeMilli aResponseTimeout);
677             void  Remove(MessageId aMessageId);
678             bool  HasAnyTimedOut(TimeMilli aNow) const;
679             void  UpdateNextFireTime(NextFireTime &aNextTime) const;
680 
681         private:
682             struct Entry
683             {
Matchesot::Dns::Dso::Connection::PendingRequests::Entry684                 bool Matches(MessageId aMessageId) const { return mMessageId == aMessageId; }
685 
686                 MessageId mMessageId;
687                 Tlv::Type mPrimaryTlvType;
688                 TimeMilli mTimeout; // Latest time by which a response is expected.
689             };
690 
691             Array<Entry, kMaxPendingRequests> mRequests;
692         };
693 
694         // Inactivity or KeepAlive timeout
695         class Timeout
696         {
697         public:
698             static constexpr uint32_t kInfinite = kInfiniteTimeout;
699             static constexpr uint32_t kDefault  = kDefaultTimeout;
700 
Timeout(uint32_t aInterval)701             explicit Timeout(uint32_t aInterval)
702                 : mInterval(aInterval)
703                 , mRequest(aInterval)
704             {
705             }
706 
707             // On client, timeout value granted by server. On server, value to grant.
GetInterval(void) const708             uint32_t GetInterval(void) const { return mInterval; }
SetInterval(uint32_t aInterval)709             void     SetInterval(uint32_t aInterval) { mInterval = LimitInterval(aInterval); }
710 
711             // On client, timeout value to request. Not used on server.
GetRequestInterval(void) const712             uint32_t GetRequestInterval(void) const { return mRequest; }
SetRequestInterval(uint32_t aInterval)713             void     SetRequestInterval(uint32_t aInterval) { mRequest = LimitInterval(aInterval); }
714 
GetExpirationTime(void) const715             TimeMilli GetExpirationTime(void) const { return mExpirationTime; }
SetExpirationTime(TimeMilli aTime)716             void      SetExpirationTime(TimeMilli aTime) { mExpirationTime = aTime; }
717 
IsUsed(void) const718             bool IsUsed(void) const { return (mInterval != kInfinite); }
IsExpired(TimeMilli aNow) const719             bool IsExpired(TimeMilli aNow) const { return (mExpirationTime <= aNow); }
720 
721         private:
722             static constexpr uint32_t kMaxInterval = TimerMilli::kMaxDelay / 2;
723 
LimitInterval(uint32_t aInterval) const724             uint32_t LimitInterval(uint32_t aInterval) const
725             {
726                 // If it is not infinite, limit the interval to `kMaxInterval`.
727                 // The max limit ensures that even twice the interval is less
728                 // than max OpenThread timer duration.
729                 return (aInterval == kInfinite) ? aInterval : Min(aInterval, kMaxInterval);
730             }
731 
732             uint32_t  mInterval;
733             uint32_t  mRequest;
734             TimeMilli mExpirationTime;
735         };
736 
737         void Init(bool aIsServer);
738         void SetState(State aState);
739         void SignalAnyStateChange(void);
740         void Accept(void);
741         void MarkAsConnecting(void);
742         void HandleConnected(void);
743         void HandleDisconnected(DisconnectMode aMode);
744         void MarkAsDisconnected(void);
745 
746         Error SendKeepAliveMessage(MessageType aMessageType, MessageId aResponseId);
747         Error SendMessage(Message              &aMessage,
748                           MessageType           aMessageType,
749                           MessageId            &aMessageId,
750                           Dns::Header::Response aResponseCode    = Dns::Header::kResponseSuccess,
751                           uint32_t              aResponseTimeout = kResponseTimeout);
752         void  HandleReceive(Message &aMessage);
753         Error ReadPrimaryTlv(const Message &aMessage, Tlv::Type &aPrimaryTlvType) const;
754         Error ProcessRequestOrUnidirectionalMessage(const Dns::Header &aHeader,
755                                                     const Message     &aMessage,
756                                                     Tlv::Type          aPrimaryTlvType);
757         Error ProcessResponseMessage(const Dns::Header &aHeader, const Message &aMessage, Tlv::Type aPrimaryTlvType);
758         Error ProcessKeepAliveMessage(const Dns::Header &aHeader, const Message &aMessage);
759         Error ProcessRetryDelayMessage(const Dns::Header &aHeader, const Message &aMessage);
760         void  SendErrorResponse(const Dns::Header &aHeader, Dns::Header::Response aResponseCode);
761         Error AppendPadding(Message &aMessage);
762 
763         void     AdjustInactivityTimeout(uint32_t aNewTimeout);
764         uint32_t CalculateServerInactivityWaitTime(void) const;
765         void     ResetTimeouts(bool aIsKeepAliveMessage);
766         void     UpdateNextFireTime(NextFireTime &aNextTime) const;
767         void     HandleTimer(NextFireTime &aNextTime);
768 
Matches(const Ip6::SockAddr & aPeerSockAddr) const769         bool Matches(const Ip6::SockAddr &aPeerSockAddr) const { return mPeerSockAddr == aPeerSockAddr; }
770 
771         static const char *StateToString(State aState);
772         static const char *MessageTypeToString(MessageType aMessageType);
773         static const char *DisconnectReasonToString(DisconnectReason aReason);
774 
775         Connection           *mNext;
776         Callbacks            &mCallbacks;
777         Ip6::SockAddr         mPeerSockAddr;
778         State                 mState;
779         MessageId             mNextMessageId;
780         PendingRequests       mPendingRequests;
781         bool                  mIsServer : 1;
782         bool                  mStateDidChange : 1;
783         bool                  mLongLivedOperation : 1;
784         Timeout               mInactivity;
785         Timeout               mKeepAlive;
786         uint32_t              mRetryDelay;
787         Dns::Header::Response mRetryDelayErrorCode;
788         DisconnectReason      mDisconnectReason;
789     };
790 
791     /**
792      * This callback function is used by DSO module to determine whether or not to accept a connection request from a
793      * peer.
794      *
795      * The function MUST return a non-null `Connection` pointer if the request is to be accepted. The returned
796      * `Connection` instance MUST be in `kStateDisconnected`. The DSO module will take the ownership of the `Connection`
797      * instance (adds it into a list of server connections - see `FindServerConnection()`). The ownership is passed
798      * back to the caller when the `Connection` gets disconnected, i.e., when either the user requests a disconnect by
799      * an explicit call to the method `Connection::Disconnect()`, or, if `HandleDisconnected()` callback is invoked
800      * (after connection is closed by the DSO module itself or by the peer).
801      *
802      * @param[in] aInstance      The OpenThread instance.
803      * @param[in] aPeerSockAddr  The peer socket address.
804      *
805      * @returns A pointer to the `Connection` to use if to accept, or `nullptr` if to reject the connection request.
806      */
807     typedef Connection *(*AcceptHandler)(Instance &aInstance, const Ip6::SockAddr &aPeerSockAddr);
808 
809     /**
810      * Initializes the `Dso` module.
811      */
812     explicit Dso(Instance &aInstance);
813 
814     /**
815      * Starts listening for DSO connection requests from peers.
816      *
817      * Once a connection request (from a peer) is received, the `Dso` module will invoke the `AcceptHandler` to
818      * determine whether to accept or reject the request.
819      *
820      * @param[in] aAcceptHandler   Accept handler callback.
821      */
822     void StartListening(AcceptHandler aAcceptHandler);
823 
824     /**
825      * Stops listening for DSO connection requests from peers.
826      */
827     void StopListening(void);
828 
829     /**
830      * Finds a client `Connection` instance (being currently managed by the `Dso` module) matching a given
831      * peer socket address.
832      *
833      * @param[in] aPeerSockAddr   The peer socket address.
834      *
835      * @returns A pointer to the matching `Connection` or `nullptr` if no match is found.
836      */
837     Connection *FindClientConnection(const Ip6::SockAddr &aPeerSockAddr);
838 
839     /**
840      * Finds a server `Connection` instance (being currently managed by the `Dso` module) matching a given
841      * peer socket address.
842      *
843      * @param[in] aPeerSockAddr   The peer socket address.
844      *
845      * @returns A pointer to the matching `Connection` or `nullptr` if no match is found.
846      */
847     Connection *FindServerConnection(const Ip6::SockAddr &aPeerSockAddr);
848 
849 private:
850     OT_TOOL_PACKED_BEGIN
851     class KeepAliveTlv : public Tlv
852     {
853     public:
854         static constexpr Type kType = kKeepAliveType;
855 
Init(void)856         void Init(void) { Tlv::Init(kType, sizeof(*this) - sizeof(Tlv)); }
857 
IsValid(void) const858         bool IsValid(void) const { return GetSize() >= sizeof(*this); }
859 
GetInactivityTimeout(void) const860         uint32_t GetInactivityTimeout(void) const { return BigEndian::HostSwap32(mInactivityTimeout); }
SetInactivityTimeout(uint32_t aTimeout)861         void     SetInactivityTimeout(uint32_t aTimeout) { mInactivityTimeout = BigEndian::HostSwap32(aTimeout); }
862 
GetKeepAliveInterval(void) const863         uint32_t GetKeepAliveInterval(void) const { return BigEndian::HostSwap32(mKeepAliveInterval); }
SetKeepAliveInterval(uint32_t aInterval)864         void     SetKeepAliveInterval(uint32_t aInterval) { mKeepAliveInterval = BigEndian::HostSwap32(aInterval); }
865 
866     private:
867         uint32_t mInactivityTimeout; // In msec
868         uint32_t mKeepAliveInterval; // In msec
869     } OT_TOOL_PACKED_END;
870 
871     OT_TOOL_PACKED_BEGIN
872     class RetryDelayTlv : public Tlv
873     {
874     public:
875         static constexpr Type kType = kRetryDelayType;
876 
Init(void)877         void Init(void) { Tlv::Init(kType, sizeof(*this) - sizeof(Tlv)); }
878 
IsValid(void) const879         bool IsValid(void) const { return GetSize() >= sizeof(*this); }
880 
GetRetryDelay(void) const881         uint32_t GetRetryDelay(void) const { return BigEndian::HostSwap32(mRetryDelay); }
SetRetryDelay(uint32_t aDelay)882         void     SetRetryDelay(uint32_t aDelay) { mRetryDelay = BigEndian::HostSwap32(aDelay); }
883 
884     private:
885         uint32_t mRetryDelay;
886     } OT_TOOL_PACKED_END;
887 
888     OT_TOOL_PACKED_BEGIN
889     class EncryptionPaddingTlv : public Tlv
890     {
891     public:
892         static constexpr Type kType = kEncryptionPaddingType;
893 
Init(uint16_t aPaddingLength)894         void Init(uint16_t aPaddingLength) { Tlv::Init(kType, aPaddingLength); }
895 
896     private:
897         // Value is padding bytes (zero) based on the length.
898     } OT_TOOL_PACKED_END;
899 
900     Connection *AcceptConnection(const Ip6::SockAddr &aPeerSockAddr);
901 
902     void HandleTimer(void);
903 
904     using DsoTimer = TimerMilliIn<Dso, &Dso::HandleTimer>;
905 
906     AcceptHandler          mAcceptHandler;
907     LinkedList<Connection> mClientConnections;
908     LinkedList<Connection> mServerConnections;
909     DsoTimer               mTimer;
910 };
911 
912 } // namespace Dns
913 
914 DefineCoreType(otPlatDsoConnection, Dns::Dso::Connection);
915 DefineMapEnum(otPlatDsoDisconnectMode, Dns::Dso::Connection::DisconnectMode);
916 
917 } // namespace ot
918 
919 #endif // OPENTHREAD_CONFIG_DNS_DSO_ENABLE
920 
921 #endif // DNS_DSO_HPP_
922