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