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