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