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