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