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