1 /* 2 * Copyright (c) 2017-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_CLIENT_HPP_ 30 #define DNS_CLIENT_HPP_ 31 32 #include "openthread-core-config.h" 33 34 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE 35 36 #include <openthread/dns_client.h> 37 38 #include "common/as_core_type.hpp" 39 #include "common/clearable.hpp" 40 #include "common/message.hpp" 41 #include "common/non_copyable.hpp" 42 #include "common/timer.hpp" 43 #include "net/dns_types.hpp" 44 #include "net/ip6.hpp" 45 #include "net/netif.hpp" 46 47 /** 48 * @file 49 * This file includes definitions for the DNS client. 50 */ 51 52 #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE 53 54 #if !OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE 55 #error "DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE requires OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE" 56 #endif 57 58 #if !OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE 59 #error "DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE requires OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE" 60 #endif 61 62 #endif 63 64 #if !OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE 65 #error "OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE requires OPENTHREAD_CONFIG_TCP_ENABLE" 66 #endif 67 68 /** 69 * This struct represents an opaque (and empty) type for a response to an address resolution DNS query. 70 * 71 */ 72 struct otDnsAddressResponse 73 { 74 }; 75 76 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 77 78 /** 79 * This struct represents an opaque (and empty) type for a response to browse (service instance enumeration) DNS query. 80 * 81 */ 82 struct otDnsBrowseResponse 83 { 84 }; 85 86 /** 87 * This struct represents an opaque (and empty) type for a response to service inst resolution DNS query. 88 * 89 */ 90 struct otDnsServiceResponse 91 { 92 }; 93 94 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 95 96 namespace ot { 97 98 namespace Srp { 99 class Client; 100 } 101 102 namespace Dns { 103 104 /** 105 * This class implements DNS client. 106 * 107 */ 108 class Client : public InstanceLocator, private NonCopyable 109 { 110 friend class ot::Srp::Client; 111 112 typedef Message Query; // `Message` is used to save `Query` related info. 113 114 public: 115 /** 116 * This type represents a DNS query configuration (e.g., server address, response wait timeout, etc). 117 * 118 */ 119 class QueryConfig : public otDnsQueryConfig, public Clearable<QueryConfig> 120 { 121 friend class Client; 122 123 public: 124 /** 125 * This enumeration type represents the "Recursion Desired" (RD) flag in a `otDnsQueryConfig`. 126 * 127 */ 128 enum RecursionFlag : uint8_t 129 { 130 kFlagUnspecified = OT_DNS_FLAG_UNSPECIFIED, ///< The flag is not specified. 131 kFlagRecursionDesired = OT_DNS_FLAG_RECURSION_DESIRED, ///< Server can resolve the query recursively. 132 kFlagNoRecursion = OT_DNS_FLAG_NO_RECURSION, ///< Server can not resolve the query recursively. 133 }; 134 135 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE 136 /** 137 * This enumeration type represents the NAT64 mode. 138 * 139 */ 140 enum Nat64Mode : uint8_t 141 { 142 kNat64Unspecified = OT_DNS_NAT64_UNSPECIFIED, ///< NAT64 mode is not specified. Use default NAT64 mode. 143 kNat64Allow = OT_DNS_NAT64_ALLOW, ///< Allow NAT64 address translation. 144 kNat64Disallow = OT_DNS_NAT64_DISALLOW, ///< Disallow NAT64 address translation. 145 }; 146 #endif 147 148 /** 149 * This enumeration type represents the service resolution mode. 150 * 151 */ 152 enum ServiceMode : uint8_t 153 { 154 kServiceModeUnspecified = OT_DNS_SERVICE_MODE_UNSPECIFIED, ///< Unspecified. Use default. 155 kServiceModeSrv = OT_DNS_SERVICE_MODE_SRV, ///< SRV record only. 156 kServiceModeTxt = OT_DNS_SERVICE_MODE_TXT, ///< TXT record only. 157 kServiceModeSrvTxt = OT_DNS_SERVICE_MODE_SRV_TXT, ///< SRV and TXT same msg. 158 kServiceModeSrvTxtSeparate = OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE, ///< SRV and TXT separate msgs. 159 kServiceModeSrvTxtOptimize = OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE, ///< Same msg first, if fail separate. 160 }; 161 162 /** 163 * This enumeration type represents the DNS transport protocol selection. 164 * 165 */ 166 enum TransportProto : uint8_t 167 { 168 kDnsTransportUnspecified = OT_DNS_TRANSPORT_UNSPECIFIED, /// Dns transport is unspecified. 169 kDnsTransportUdp = OT_DNS_TRANSPORT_UDP, /// Dns query should be sent via UDP. 170 kDnsTransportTcp = OT_DNS_TRANSPORT_TCP, /// Dns query should be sent via TCP. 171 }; 172 173 /** 174 * This is the default constructor for `QueryConfig` object. 175 * 176 */ 177 QueryConfig(void) = default; 178 179 /** 180 * This method gets the server socket address (IPv6 address and port number). 181 * 182 * @returns The server socket address. 183 * 184 */ GetServerSockAddr(void) const185 const Ip6::SockAddr &GetServerSockAddr(void) const 186 { 187 return static_cast<const Ip6::SockAddr &>(mServerSockAddr); 188 } 189 190 /** 191 * This method gets the wait time to receive response from server (in msec). 192 * 193 * @returns The timeout interval in msec. 194 * 195 */ GetResponseTimeout(void) const196 uint32_t GetResponseTimeout(void) const { return mResponseTimeout; } 197 198 /** 199 * This method gets the maximum number of query transmit attempts before reporting failure. 200 * 201 * @returns The maximum number of query transmit attempts. 202 * 203 */ GetMaxTxAttempts(void) const204 uint8_t GetMaxTxAttempts(void) const { return mMaxTxAttempts; } 205 206 /** 207 * This method gets the recursion flag indicating whether the server can resolve the query recursively or not. 208 * 209 * @returns The recursion flag. 210 * 211 */ GetRecursionFlag(void) const212 RecursionFlag GetRecursionFlag(void) const { return static_cast<RecursionFlag>(mRecursionFlag); } 213 214 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE 215 /** 216 * This method gets the NAT64 mode. 217 * 218 * @returns The NAT64 mode. 219 * 220 */ GetNat64Mode(void) const221 Nat64Mode GetNat64Mode(void) const { return static_cast<Nat64Mode>(mNat64Mode); } 222 #endif 223 /** 224 * This method gets the service resolution mode. 225 * 226 * @returns The service resolution mode. 227 * 228 */ GetServiceMode(void) const229 ServiceMode GetServiceMode(void) const { return static_cast<ServiceMode>(mServiceMode); } 230 231 /** 232 * This method gets the transport protocol. 233 * 234 * @returns The transport protocol. 235 * 236 */ GetTransportProto(void) const237 TransportProto GetTransportProto(void) const { return static_cast<TransportProto>(mTransportProto); }; 238 239 private: 240 static constexpr uint32_t kDefaultResponseTimeout = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RESPONSE_TIMEOUT; 241 static constexpr uint16_t kDefaultServerPort = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_PORT; 242 static constexpr uint8_t kDefaultMaxTxAttempts = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_MAX_TX_ATTEMPTS; 243 static constexpr bool kDefaultRecursionDesired = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RECURSION_DESIRED_FLAG; 244 static constexpr ServiceMode kDefaultServiceMode = 245 static_cast<ServiceMode>(OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE); 246 247 static_assert(kDefaultServiceMode != kServiceModeUnspecified, "Invalid default service mode"); 248 249 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE 250 static constexpr bool kDefaultNat64Allowed = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_NAT64_ALLOWED; 251 #endif 252 253 enum InitMode : uint8_t 254 { 255 kInitFromDefaults, 256 }; 257 258 static const char kDefaultServerAddressString[]; 259 260 explicit QueryConfig(InitMode aMode); 261 GetServerSockAddr(void)262 Ip6::SockAddr &GetServerSockAddr(void) { return AsCoreType(&mServerSockAddr); } 263 SetResponseTimeout(uint32_t aResponseTimeout)264 void SetResponseTimeout(uint32_t aResponseTimeout) { mResponseTimeout = aResponseTimeout; } SetMaxTxAttempts(uint8_t aMaxTxAttempts)265 void SetMaxTxAttempts(uint8_t aMaxTxAttempts) { mMaxTxAttempts = aMaxTxAttempts; } SetRecursionFlag(RecursionFlag aFlag)266 void SetRecursionFlag(RecursionFlag aFlag) { mRecursionFlag = static_cast<otDnsRecursionFlag>(aFlag); } SetServiceMode(ServiceMode aMode)267 void SetServiceMode(ServiceMode aMode) { mServiceMode = static_cast<otDnsServiceMode>(aMode); } 268 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE SetNat64Mode(Nat64Mode aMode)269 void SetNat64Mode(Nat64Mode aMode) { mNat64Mode = static_cast<otDnsNat64Mode>(aMode); } 270 #endif SetTransportProto(TransportProto aTransportProto)271 void SetTransportProto(TransportProto aTransportProto) 272 { 273 mTransportProto = static_cast<otDnsTransportProto>(aTransportProto); 274 } 275 276 void SetFrom(const QueryConfig *aConfig, const QueryConfig &aDefaultConfig); 277 }; 278 279 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 280 /** 281 * This structure provides info for a DNS service instance. 282 * 283 */ 284 typedef otDnsServiceInfo ServiceInfo; 285 #endif 286 287 /** 288 * This class represents a DNS query response. 289 * 290 */ 291 class Response : public otDnsAddressResponse, 292 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 293 public otDnsBrowseResponse, 294 public otDnsServiceResponse, 295 #endif 296 public Clearable<Response> 297 { 298 friend class Client; 299 300 protected: 301 enum Section : uint8_t 302 { 303 kAnswerSection, 304 kAdditionalDataSection, 305 }; 306 Response(void)307 Response(void) { Clear(); } 308 309 Error GetName(char *aNameBuffer, uint16_t aNameBufferSize) const; 310 void SelectSection(Section aSection, uint16_t &aOffset, uint16_t &aNumRecord) const; 311 Error CheckForHostNameAlias(Section aSection, Name &aHostName) const; 312 Error FindHostAddress(Section aSection, 313 const Name &aHostName, 314 uint16_t aIndex, 315 Ip6::Address &aAddress, 316 uint32_t &aTtl) const; 317 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE 318 Error FindARecord(Section aSection, const Name &aHostName, uint16_t aIndex, ARecord &aARecord) const; 319 #endif 320 321 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 322 void InitServiceInfo(ServiceInfo &aServiceInfo) const; 323 Error ReadServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const; 324 Error ReadTxtRecord(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const; 325 #endif 326 void PopulateFrom(const Message &aMessage); 327 328 Instance *mInstance; // The OpenThread instance. 329 Query *mQuery; // The associated query. 330 const Message *mMessage; // The response message. 331 Response *mNext; // The next response when we have related queries. 332 uint16_t mAnswerOffset; // Answer section offset in `mMessage`. 333 uint16_t mAnswerRecordCount; // Number of records in answer section. 334 uint16_t mAdditionalOffset; // Additional data section offset in `mMessage`. 335 uint16_t mAdditionalRecordCount; // Number of records in additional data section. 336 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE 337 // This flag is only used in an IPv6 address query response. 338 // It indicates that the response does not contain any IPv6 339 // addresses but server provided at least one IPv4 address 340 // in the additional data section for NAT64 address synthesis. 341 bool mIp6QueryResponseRequiresNat64; 342 #endif 343 }; 344 345 /** 346 * This type represents the function pointer callback which is called when a DNS response for an address resolution 347 * query is received. 348 * 349 */ 350 typedef otDnsAddressCallback AddressCallback; 351 352 /** 353 * This type represents an address resolution query DNS response. 354 * 355 */ 356 class AddressResponse : public Response 357 { 358 friend class Client; 359 360 public: 361 /** 362 * This method gets the host name associated with an address resolution DNS response. 363 * 364 * This method MUST only be used from `AddressCallback`. 365 * 366 * @param[out] aNameBuffer A buffer to char array to output the host name. 367 * @param[in] aNameBufferSize The size of @p aNameBuffer. 368 * 369 * @retval kErrorNone The host name was read successfully. 370 * @retval kErrorNoBufs The name does not fit in @p aNameBuffer. 371 * 372 */ GetHostName(char * aNameBuffer,uint16_t aNameBufferSize) const373 Error GetHostName(char *aNameBuffer, uint16_t aNameBufferSize) const 374 { 375 return GetName(aNameBuffer, aNameBufferSize); 376 } 377 378 /** 379 * This method gets the IPv6 address associated with an address resolution DNS response. 380 * 381 * This method MUST only be used from `AddressCallback`. 382 * 383 * The response may include multiple IPv6 address records. @p aIndex can be used to iterate through the list of 384 * addresses. Index zero gets the the first address and so on. When we reach end of the list, this method 385 * returns `kErrorNotFound`. 386 * 387 * @param[in] aIndex The address record index to retrieve. 388 * @param[out] aAddress A reference to an IPv6 address to output the address. 389 * @param[out] aTtl A reference to a `uint32_t` to output TTL for the address. 390 * 391 * @retval kErrorNone The address was read successfully. 392 * @retval kErrorNotFound No address record at @p aIndex. 393 * @retval kErrorParse Could not parse the records. 394 * @retval kErrorInvalidState No NAT64 prefix (applicable only when NAT64 is allowed). 395 * 396 */ 397 Error GetAddress(uint16_t aIndex, Ip6::Address &aAddress, uint32_t &aTtl) const; 398 399 private: 400 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE 401 Error GetNat64Prefix(Ip6::Prefix &aPrefix) const; 402 #endif 403 }; 404 405 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 406 407 /** 408 * This type represents the function pointer callback which is called when a response for a browse (service 409 * instance enumeration) DNS query is received. 410 * 411 */ 412 typedef otDnsBrowseCallback BrowseCallback; 413 414 /** 415 * This type represents a browse (service instance enumeration) DNS response. 416 * 417 */ 418 class BrowseResponse : public Response 419 { 420 friend class Client; 421 422 public: 423 /** 424 * This method gets the service name associated with a DNS browse response. 425 * 426 * This method MUST only be used from `BrowseCallback`. 427 * 428 * @param[out] aNameBuffer A buffer to char array to output the host name. 429 * @param[in] aNameBufferSize The size of @p aNameBuffer. 430 * 431 * @retval kErrorNone The host name was read successfully. 432 * @retval kErrorNoBufs The name does not fit in @p aNameBuffer. 433 * 434 */ GetServiceName(char * aNameBuffer,uint16_t aNameBufferSize) const435 Error GetServiceName(char *aNameBuffer, uint16_t aNameBufferSize) const 436 { 437 return GetName(aNameBuffer, aNameBufferSize); 438 } 439 440 /** 441 * This method gets a service instance associated with a DNS browse (service instance enumeration) response. 442 * 443 * This method MUST only be used from `BrowseCallback`. 444 * 445 * A response may include multiple service instance records. @p aIndex can be used to iterate through the list. 446 * Index zero gives the the first record. When we reach end of the list, `kErrorNotFound` is returned. 447 * 448 * Note that this method gets the service instance label and not the full service instance name which is of the 449 * form `<Instance>.<Service>.<Domain>`. 450 * 451 * @param[in] aIndex The service instance record index to retrieve. 452 * @param[out] aLabelBuffer A char array to output the service instance label (MUST NOT be NULL). 453 * @param[in] aLabelBufferSize The size of @p aLabelBuffer. 454 * 455 * @retval kErrorNone The service instance was read successfully. 456 * @retval kErrorNoBufs The name does not fit in @p aNameBuffer. 457 * @retval kErrorNotFound No service instance record at @p aIndex. 458 * @retval kErrorParse Could not parse the records. 459 * 460 */ 461 Error GetServiceInstance(uint16_t aIndex, char *aLabelBuffer, uint8_t aLabelBufferSize) const; 462 463 /** 464 * This method gets info for a service instance from a DNS browse (service instance enumeration) response. 465 * 466 * This method MUST only be used from `BrowseCallback`. 467 * 468 * A browse DNS response should include the SRV, TXT, and AAAA records for the service instances that are 469 * enumerated (note that it is a SHOULD and not a MUST requirement). This method tries to retrieve this info 470 * for a given service instance. 471 * 472 * - If no matching SRV record is found, `kErrorNotFound` is returned. 473 * - If a matching SRV record is found, @p aServiceInfo is updated returning `kErrorNone`. 474 * - If no matching TXT record is found, `mTxtDataSize` in @p aServiceInfo is set to zero. 475 * - If no matching AAAA record is found, `mHostAddress is set to all zero or unspecified address. 476 * - If there are multiple AAAA records for the host name `mHostAddress` is set to the first one. The other 477 * addresses can be retrieved using `GetHostAddress()` method. 478 * 479 * @param[in] aInstanceLabel The service instance label (MUST NOT be `nullptr`). 480 * @param[out] aServiceInfo A `ServiceInfo` to output the service instance information. 481 * 482 * @retval kErrorNone The service instance info was read. @p aServiceInfo is updated. 483 * @retval kErrorNotFound Could not find a matching SRV record for @p aInstanceLabel. 484 * @retval kErrorNoBufs The host name and/or the TXT data could not fit in given buffers. 485 * @retval kErrorParse Could not parse the records. 486 * 487 */ 488 Error GetServiceInfo(const char *aInstanceLabel, ServiceInfo &aServiceInfo) const; 489 490 /** 491 * This method gets the host IPv6 address from a DNS browse (service instance enumeration) response. 492 * 493 * This method MUST only be used from `BrowseCallback`. 494 * 495 * The response can include zero or more IPv6 address records. @p aIndex can be used to iterate through the 496 * list of addresses. Index zero gets the first address and so on. When we reach end of the list, this method 497 * returns `kErrorNotFound`. 498 * 499 * @param[in] aHostName The host name to get the address (MUST NOT be `nullptr`). 500 * @param[in] aIndex The address record index to retrieve. 501 * @param[out] aAddress A reference to an IPv6 address to output the address. 502 * @param[out] aTtl A reference to a `uint32_t` to output TTL for the address. 503 * 504 * @retval kErrorNone The address was read successfully. 505 * @retval kErrorNotFound No address record for @p aHostname at @p aIndex. 506 * @retval kErrorParse Could not parse the records. 507 * 508 */ 509 Error GetHostAddress(const char *aHostName, uint16_t aIndex, Ip6::Address &aAddress, uint32_t &aTtl) const; 510 511 private: 512 Error FindPtrRecord(const char *aInstanceLabel, Name &aInstanceName) const; 513 }; 514 515 /** 516 * This type represents the function pointer callback which is called when a response for a service instance 517 * resolution DNS query is received. 518 * 519 */ 520 typedef otDnsServiceCallback ServiceCallback; 521 522 /** 523 * This type represents a service instance resolution DNS response. 524 * 525 */ 526 class ServiceResponse : public Response 527 { 528 friend class Client; 529 530 public: 531 /** 532 * This method gets the service instance name associated with a DNS service instance resolution response. 533 * 534 * This method MUST only be used from `ServiceCallback`. 535 * 536 * @param[out] aLabelBuffer A buffer to char array to output the service instance label (MUST NOT be NULL). 537 * @param[in] aLabelBufferSize The size of @p aLabelBuffer. 538 * @param[out] aNameBuffer A buffer to char array to output the rest of service name (can be NULL if user 539 * is not interested in getting the name). 540 * @param[in] aNameBufferSize The size of @p aNameBuffer. 541 * 542 * @retval kErrorNone The service instance name was read successfully. 543 * @retval kErrorNoBufs Either the label or name does not fit in the given buffers. 544 * 545 */ 546 Error GetServiceName(char *aLabelBuffer, 547 uint8_t aLabelBufferSize, 548 char *aNameBuffer, 549 uint16_t aNameBufferSize) const; 550 551 /** 552 * This method gets info for a service instance from a DNS service instance resolution response. 553 * 554 * This method MUST only be used from `ServiceCallback`. 555 * 556 * - If no matching SRV record is found, `kErrorNotFound` is returned. 557 * - If a matching SRV record is found, @p aServiceInfo is updated and `kErrorNone` is returned. 558 * - If no matching TXT record is found, `mTxtDataSize` in @p aServiceInfo is set to zero. 559 * - If no matching AAAA record is found, `mHostAddress is set to all zero or unspecified address. 560 * - If there are multiple AAAA records for the host name, `mHostAddress` is set to the first one. The other 561 * addresses can be retrieved using `GetHostAddress()` method. 562 * 563 * @param[out] aServiceInfo A `ServiceInfo` to output the service instance information 564 * 565 * @retval kErrorNone The service instance info was read. @p aServiceInfo is updated. 566 * @retval kErrorNotFound Could not find a matching SRV record. 567 * @retval kErrorNoBufs The host name and/or TXT data could not fit in the given buffers. 568 * @retval kErrorParse Could not parse the records in the @p aResponse. 569 * 570 */ 571 Error GetServiceInfo(ServiceInfo &aServiceInfo) const; 572 573 /** 574 * This method gets the host IPv6 address from a DNS service instance resolution response. 575 * 576 * This method MUST only be used from `ServiceCallback`. 577 * 578 * The response can include zero or more IPv6 address records. @p aIndex can be used to iterate through the 579 * list of addresses. Index zero gets the first address and so on. When we reach end of the list, this method 580 * returns `kErrorNotFound`. 581 * 582 * @param[in] aHostName The host name to get the address (MUST NOT be `nullptr`). 583 * @param[in] aIndex The address record index to retrieve. 584 * @param[out] aAddress A reference to an IPv6 address to output the address. 585 * @param[out] aTtl A reference to a `uint32_t` to output TTL for the address. 586 * 587 * @retval kErrorNone The address was read successfully. 588 * @retval kErrorNotFound No address record for @p aHostname at @p aIndex. 589 * @retval kErrorParse Could not parse the records. 590 * 591 */ 592 Error GetHostAddress(const char *aHostName, uint16_t aIndex, Ip6::Address &aAddress, uint32_t &aTtl) const; 593 }; 594 595 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 596 597 /** 598 * This constructor initializes the object. 599 * 600 * @param[in] aInstance A reference to the OpenThread instance. 601 * 602 */ 603 explicit Client(Instance &aInstance); 604 605 /** 606 * This method starts the DNS client. 607 * 608 * @retval kErrorNone Successfully started the DNS client. 609 * @retval kErrorAlready The socket is already open. 610 * 611 */ 612 Error Start(void); 613 614 /** 615 * This method stops the DNS client. 616 * 617 */ 618 void Stop(void); 619 620 /** 621 * This method gets the current default query config being used by DNS client. 622 * 623 * @returns The current default query config. 624 * 625 */ GetDefaultConfig(void) const626 const QueryConfig &GetDefaultConfig(void) const { return mDefaultConfig; } 627 628 /** 629 * This method sets the default query config. 630 * 631 * @param[in] aQueryConfig The new default query config. 632 * 633 */ 634 void SetDefaultConfig(const QueryConfig &aQueryConfig); 635 636 /** 637 * This method resets the default config to the config used when the OpenThread stack starts. 638 * 639 * When OpenThread stack starts, the default DNS query config is determined from a set of OT config options such as 640 * `OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_IP6_ADDRESS`, `_DEFAULT_SERVER_PORT`, or `_DEFAULT_RESPONSE_TIMEOUT` 641 * etc. (see `config/dns_client.h` for all related config options). 642 * 643 */ 644 void ResetDefaultConfig(void); 645 646 /** 647 * This method sends an address resolution DNS query for AAAA (IPv6) record for a given host name. 648 * 649 * The @p aConfig can be nullptr. In this case the default config (from `GetDefaultConfig()`) will be used as 650 * the config for this query. In a non-nullptr @p aConfig, some of the fields can be left unspecified (value zero). 651 * The unspecified fields are then replaced by the values from the default config. 652 * 653 * @param[in] aHostName The host name for which to query the address (MUST NOT be `nullptr`). 654 * @param[in] aCallback A callback function pointer to report the result of query. 655 * @param[in] aContext A pointer to arbitrary context information passed to @p aCallback. 656 * @param[in] aConfig The config to use for this query. 657 * 658 * @retval kErrorNone Successfully sent DNS query. 659 * @retval kErrorNoBufs Failed to allocate retransmission data. 660 * @retval kErrorInvalidArgs The host name is not valid format. 661 * @retval kErrorInvalidState Cannot send query since Thread interface is not up. 662 * 663 */ 664 Error ResolveAddress(const char *aHostName, 665 AddressCallback aCallback, 666 void *aContext, 667 const QueryConfig *aConfig = nullptr); 668 669 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE 670 /** 671 * This method sends an address resolution DNS query for A (IPv4) record for a given host name. 672 * 673 * When a successful response is received, the addresses are returned from @p aCallback as NAT64 IPv6 translated 674 * versions of the IPv4 addresses from the query response. 675 * 676 * The @p aConfig can be nullptr. In this case the default config (from `GetDefaultConfig()`) will be used as 677 * the config for this query. In a non-nullptr @p aConfig, some of the fields can be left unspecified (value zero). 678 * The unspecified fields are then replaced by the values from the default config. 679 * 680 * @param[in] aHostName The host name for which to query the address (MUST NOT be `nullptr`). 681 * @param[in] aCallback A callback function pointer to report the result of query. 682 * @param[in] aContext A pointer to arbitrary context information passed to @p aCallback. 683 * @param[in] aConfig The config to use for this query. 684 * 685 * @retval kErrorNone Successfully sent DNS query. 686 * @retval kErrorNoBufs Failed to allocate retransmission data. 687 * @retval kErrorInvalidArgs The host name is not valid format or NAT64 is not enabled in config. 688 * @retval kErrorInvalidState Cannot send query since Thread interface is not up, or there is no NAT64 prefix. 689 * 690 */ 691 Error ResolveIp4Address(const char *aHostName, 692 AddressCallback aCallback, 693 void *aContext, 694 const QueryConfig *aConfig = nullptr); 695 #endif 696 697 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 698 699 /** 700 * This method sends a browse (service instance enumeration) DNS query for a given service name. 701 * 702 * The @p aConfig can be nullptr. In this case the default config (from `GetDefaultConfig()`) will be used as 703 * the config for this query. In a non-nullptr @p aConfig, some of the fields can be left unspecified (value zero). 704 * The unspecified fields are then replaced by the values from the default config. 705 * 706 * @param[in] aServiceName The service name to query for (MUST NOT be `nullptr`). 707 * @param[in] aCallback The callback to report the response or errors (such as time-out). 708 * @param[in] aContext A pointer to arbitrary context information. 709 * @param[in] aConfig The config to use for this query. 710 * 711 * @retval kErrorNone Query sent successfully. @p aCallback will be invoked to report the status. 712 * @retval kErrorNoBufs Insufficient buffer to prepare and send query. 713 * 714 */ 715 Error Browse(const char *aServiceName, 716 BrowseCallback aCallback, 717 void *aContext, 718 const QueryConfig *aConfig = nullptr); 719 720 /** 721 * This method starts a DNS service instance resolution for a given service instance. 722 * 723 * The @p aConfig can be `nullptr`. In this case the default config (from `GetDefaultConfig()`) will be used as 724 * the config for this query. In a non-`nullptr` @p aConfig, some of the fields can be left unspecified (value 725 * zero). The unspecified fields are then replaced by the values from the default config. 726 * 727 * @param[in] aInstanceLabel The service instance label. 728 * @param[in] aServiceName The service name (together with @p aInstanceLabel form full instance name). 729 * @param[in] aCallback A function pointer that shall be called on response reception or time-out. 730 * @param[in] aContext A pointer to arbitrary context information. 731 * @param[in] aConfig The config to use for this query. 732 * 733 * @retval kErrorNone Query sent successfully. @p aCallback will be invoked to report the status. 734 * @retval kErrorNoBufs Insufficient buffer to prepare and send query. 735 * @retval kErrorInvalidArgs @p aInstanceLabel is `nullptr`. 736 * 737 */ 738 Error ResolveService(const char *aInstanceLabel, 739 const char *aServiceName, 740 otDnsServiceCallback aCallback, 741 void *aContext, 742 const QueryConfig *aConfig = nullptr); 743 744 /** 745 * This method starts a DNS service instance resolution for a given service instance, with a potential follow-up 746 * host name resolution (if the server/resolver does not provide AAAA/A records for the host name in the response 747 * to SRV query). 748 * 749 * The @p aConfig can be `nullptr`. In this case the default config (from `GetDefaultConfig()`) will be used as 750 * the config for this query. In a non-`nullptr` @p aConfig, some of the fields can be left unspecified (value 751 * zero). The unspecified fields are then replaced by the values from the default config. 752 * 753 * @param[in] aInstanceLabel The service instance label. 754 * @param[in] aServiceName The service name (together with @p aInstanceLabel form full instance name). 755 * @param[in] aCallback A function pointer that shall be called on response reception or time-out. 756 * @param[in] aContext A pointer to arbitrary context information. 757 * @param[in] aConfig The config to use for this query. 758 * 759 * @retval kErrorNone Query sent successfully. @p aCallback will be invoked to report the status. 760 * @retval kErrorNoBufs Insufficient buffer to prepare and send query. 761 * @retval kErrorInvalidArgs @p aInstanceLabel is `nullptr` or the @p aConfig is invalid. 762 * 763 */ 764 Error ResolveServiceAndHostAddress(const char *aInstanceLabel, 765 const char *aServiceName, 766 ServiceCallback aCallback, 767 void *aContext, 768 const QueryConfig *aConfig = nullptr); 769 770 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 771 772 private: 773 enum QueryType : uint8_t 774 { 775 kIp6AddressQuery, // IPv6 Address resolution. 776 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE 777 kIp4AddressQuery, // IPv4 Address resolution 778 #endif 779 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 780 kBrowseQuery, // Browse (service instance enumeration). 781 kServiceQuerySrvTxt, // Service instance resolution both SRV and TXT records. 782 kServiceQuerySrv, // Service instance resolution SRV record only. 783 kServiceQueryTxt, // Service instance resolution TXT record only. 784 #endif 785 kNoQuery, 786 }; 787 788 #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE 789 enum TcpState : uint8_t 790 { 791 kTcpUninitialized = 0, 792 kTcpConnecting, 793 kTcpConnectedIdle, 794 kTcpConnectedSending, 795 }; 796 #endif 797 798 union Callback 799 { 800 AddressCallback mAddressCallback; 801 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 802 BrowseCallback mBrowseCallback; 803 ServiceCallback mServiceCallback; 804 #endif 805 }; 806 807 typedef MessageQueue QueryList; // List of queries. 808 809 struct QueryInfo : public Clearable<QueryInfo> // Query related Info 810 { ReadFromot::Dns::Client::QueryInfo811 void ReadFrom(const Query &aQuery) { IgnoreError(aQuery.Read(0, *this)); } 812 813 QueryType mQueryType; 814 uint16_t mMessageId; 815 Callback mCallback; 816 void *mCallbackContext; 817 TimeMilli mRetransmissionTime; 818 QueryConfig mConfig; 819 uint8_t mTransmissionCount; 820 bool mShouldResolveHostAddr; 821 Query *mMainQuery; 822 Query *mNextQuery; 823 Message *mSavedResponse; 824 // Followed by the name (service, host, instance) encoded as a `Dns::Name`. 825 }; 826 827 static constexpr uint16_t kNameOffsetInQuery = sizeof(QueryInfo); 828 829 Error StartQuery(QueryInfo &aInfo, const char *aLabel, const char *aName, QueryType aSecondType = kNoQuery); 830 Error AllocateQuery(const QueryInfo &aInfo, const char *aLabel, const char *aName, Query *&aQuery); 831 void FreeQuery(Query &aQuery); UpdateQuery(Query & aQuery,const QueryInfo & aInfo)832 void UpdateQuery(Query &aQuery, const QueryInfo &aInfo) { aQuery.Write(0, aInfo); } 833 Query &FindMainQuery(Query &aQuery); 834 Error SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer); 835 void FinalizeQuery(Query &aQuery, Error aError); 836 void FinalizeQuery(Response &Response, Error aError); 837 static void GetQueryTypeAndCallback(const Query &aQuery, QueryType &aType, Callback &aCallback, void *&aContext); 838 Error AppendNameFromQuery(const Query &aQuery, Message &aMessage); 839 Query *FindQueryById(uint16_t aMessageId); 840 static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMsgInfo); 841 void ProcessResponse(const Message &aResponseMessage); 842 Error ParseResponse(const Message &aResponseMessage, Query *&aQuery, Error &aResponseError); 843 bool CanFinalizeQuery(Query &aQuery); 844 void SaveQueryResponse(Query &aQuery, const Message &aResponseMessage); 845 Query *PopulateResponse(Response &aResponse, Query &aQuery, const Message &aResponseMessage); 846 void PrepareResponseAndFinalize(Query &aQuery, const Message &aResponseMessage, Response *aPrevResponse); 847 void HandleTimer(void); 848 849 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE 850 Error ReplaceWithIp4Query(Query &aQuery); 851 #endif 852 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 853 Error Resolve(const char *aInstanceLabel, 854 const char *aServiceName, 855 ServiceCallback aCallback, 856 void *aContext, 857 const QueryConfig *aConfig, 858 bool aShouldResolveHostAddr); 859 Error ReplaceWithSeparateSrvTxtQueries(Query &aQuery); 860 void ResolveHostAddressIfNeeded(Query &aQuery, const Message &aResponseMessage); 861 #endif 862 863 #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE 864 void UpdateDefaultConfigAddress(void); 865 #endif 866 867 #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE 868 static void HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint); 869 static void HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); 870 static void HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); 871 static void HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint, 872 size_t aBytesAvailable, 873 bool aEndOfStream, 874 size_t aBytesRemaining); 875 876 void HandleTcpEstablished(otTcpEndpoint *aEndpoint); 877 void HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); 878 void HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); 879 void HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, 880 size_t aBytesAvailable, 881 bool aEndOfStream, 882 size_t aBytesRemaining); 883 Error InitTcpSocket(void); 884 Error ReadFromLinkBuffer(const otLinkedBuffer *&aLinkedBuffer, 885 size_t &aOffset, 886 Message &aMessage, 887 uint16_t aLength); 888 void PrepareTcpMessage(Message &aMessage); 889 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE 890 891 static const uint8_t kQuestionCount[]; 892 static const uint16_t *const kQuestionRecordTypes[]; 893 894 static const uint16_t kIp6AddressQueryRecordTypes[]; 895 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE 896 static const uint16_t kIp4AddressQueryRecordTypes[]; 897 #endif 898 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 899 static const uint16_t kBrowseQueryRecordTypes[]; 900 static const uint16_t kServiceQueryRecordTypes[]; 901 #endif 902 903 static constexpr uint16_t kUdpQueryMaxSize = 512; 904 905 using RetryTimer = TimerMilliIn<Client, &Client::HandleTimer>; 906 907 Ip6::Udp::Socket mSocket; 908 909 #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE 910 Ip6::Tcp::Endpoint mEndpoint; 911 912 otLinkedBuffer mSendLink; 913 uint8_t mSendBufferBytes[OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE]; 914 uint8_t mReceiveBufferBytes[OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE]; 915 916 TcpState mTcpState; 917 #endif 918 919 QueryList mMainQueries; 920 RetryTimer mTimer; 921 QueryConfig mDefaultConfig; 922 #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE 923 bool mUserDidSetDefaultAddress; 924 #endif 925 }; 926 927 } // namespace Dns 928 929 DefineCoreType(otDnsQueryConfig, Dns::Client::QueryConfig); 930 DefineCoreType(otDnsAddressResponse, Dns::Client::AddressResponse); 931 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE 932 DefineCoreType(otDnsBrowseResponse, Dns::Client::BrowseResponse); 933 DefineCoreType(otDnsServiceResponse, Dns::Client::ServiceResponse); 934 DefineCoreType(otDnsServiceInfo, Dns::Client::ServiceInfo); 935 #endif 936 937 } // namespace ot 938 939 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE 940 941 #endif // DNS_CLIENT_HPP_ 942