/* * Copyright (c) 2017-2021, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef DNS_CLIENT_HPP_ #define DNS_CLIENT_HPP_ #include "openthread-core-config.h" #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE #include #include "common/as_core_type.hpp" #include "common/clearable.hpp" #include "common/message.hpp" #include "common/non_copyable.hpp" #include "common/timer.hpp" #include "net/dns_types.hpp" #include "net/ip6.hpp" #include "net/netif.hpp" /** * @file * This file includes definitions for the DNS client. */ #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE #if !OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE #error "DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE requires OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE" #endif #if !OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE #error "DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE requires OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE" #endif #endif #if !OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE #error "OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE requires OPENTHREAD_CONFIG_TCP_ENABLE" #endif /** * This struct represents an opaque (and empty) type for a response to an address resolution DNS query. * */ struct otDnsAddressResponse { }; #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE /** * This struct represents an opaque (and empty) type for a response to browse (service instance enumeration) DNS query. * */ struct otDnsBrowseResponse { }; /** * This struct represents an opaque (and empty) type for a response to service inst resolution DNS query. * */ struct otDnsServiceResponse { }; #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE namespace ot { namespace Srp { class Client; } namespace Dns { /** * This class implements DNS client. * */ class Client : public InstanceLocator, private NonCopyable { friend class ot::Srp::Client; typedef Message Query; // `Message` is used to save `Query` related info. public: /** * This type represents a DNS query configuration (e.g., server address, response wait timeout, etc). * */ class QueryConfig : public otDnsQueryConfig, public Clearable { friend class Client; public: /** * This enumeration type represents the "Recursion Desired" (RD) flag in a `otDnsQueryConfig`. * */ enum RecursionFlag : uint8_t { kFlagUnspecified = OT_DNS_FLAG_UNSPECIFIED, ///< The flag is not specified. kFlagRecursionDesired = OT_DNS_FLAG_RECURSION_DESIRED, ///< Server can resolve the query recursively. kFlagNoRecursion = OT_DNS_FLAG_NO_RECURSION, ///< Server can not resolve the query recursively. }; #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE /** * This enumeration type represents the NAT64 mode. * */ enum Nat64Mode : uint8_t { kNat64Unspecified = OT_DNS_NAT64_UNSPECIFIED, ///< NAT64 mode is not specified. Use default NAT64 mode. kNat64Allow = OT_DNS_NAT64_ALLOW, ///< Allow NAT64 address translation. kNat64Disallow = OT_DNS_NAT64_DISALLOW, ///< Disallow NAT64 address translation. }; #endif /** * This enumeration type represents the service resolution mode. * */ enum ServiceMode : uint8_t { kServiceModeUnspecified = OT_DNS_SERVICE_MODE_UNSPECIFIED, ///< Unspecified. Use default. kServiceModeSrv = OT_DNS_SERVICE_MODE_SRV, ///< SRV record only. kServiceModeTxt = OT_DNS_SERVICE_MODE_TXT, ///< TXT record only. kServiceModeSrvTxt = OT_DNS_SERVICE_MODE_SRV_TXT, ///< SRV and TXT same msg. kServiceModeSrvTxtSeparate = OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE, ///< SRV and TXT separate msgs. kServiceModeSrvTxtOptimize = OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE, ///< Same msg first, if fail separate. }; /** * This enumeration type represents the DNS transport protocol selection. * */ enum TransportProto : uint8_t { kDnsTransportUnspecified = OT_DNS_TRANSPORT_UNSPECIFIED, /// Dns transport is unspecified. kDnsTransportUdp = OT_DNS_TRANSPORT_UDP, /// Dns query should be sent via UDP. kDnsTransportTcp = OT_DNS_TRANSPORT_TCP, /// Dns query should be sent via TCP. }; /** * This is the default constructor for `QueryConfig` object. * */ QueryConfig(void) = default; /** * This method gets the server socket address (IPv6 address and port number). * * @returns The server socket address. * */ const Ip6::SockAddr &GetServerSockAddr(void) const { return static_cast(mServerSockAddr); } /** * This method gets the wait time to receive response from server (in msec). * * @returns The timeout interval in msec. * */ uint32_t GetResponseTimeout(void) const { return mResponseTimeout; } /** * This method gets the maximum number of query transmit attempts before reporting failure. * * @returns The maximum number of query transmit attempts. * */ uint8_t GetMaxTxAttempts(void) const { return mMaxTxAttempts; } /** * This method gets the recursion flag indicating whether the server can resolve the query recursively or not. * * @returns The recursion flag. * */ RecursionFlag GetRecursionFlag(void) const { return static_cast(mRecursionFlag); } #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE /** * This method gets the NAT64 mode. * * @returns The NAT64 mode. * */ Nat64Mode GetNat64Mode(void) const { return static_cast(mNat64Mode); } #endif /** * This method gets the service resolution mode. * * @returns The service resolution mode. * */ ServiceMode GetServiceMode(void) const { return static_cast(mServiceMode); } /** * This method gets the transport protocol. * * @returns The transport protocol. * */ TransportProto GetTransportProto(void) const { return static_cast(mTransportProto); }; private: static constexpr uint32_t kDefaultResponseTimeout = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RESPONSE_TIMEOUT; static constexpr uint16_t kDefaultServerPort = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_PORT; static constexpr uint8_t kDefaultMaxTxAttempts = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_MAX_TX_ATTEMPTS; static constexpr bool kDefaultRecursionDesired = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RECURSION_DESIRED_FLAG; static constexpr ServiceMode kDefaultServiceMode = static_cast(OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE); static_assert(kDefaultServiceMode != kServiceModeUnspecified, "Invalid default service mode"); #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE static constexpr bool kDefaultNat64Allowed = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_NAT64_ALLOWED; #endif enum InitMode : uint8_t { kInitFromDefaults, }; static const char kDefaultServerAddressString[]; explicit QueryConfig(InitMode aMode); Ip6::SockAddr &GetServerSockAddr(void) { return AsCoreType(&mServerSockAddr); } void SetResponseTimeout(uint32_t aResponseTimeout) { mResponseTimeout = aResponseTimeout; } void SetMaxTxAttempts(uint8_t aMaxTxAttempts) { mMaxTxAttempts = aMaxTxAttempts; } void SetRecursionFlag(RecursionFlag aFlag) { mRecursionFlag = static_cast(aFlag); } void SetServiceMode(ServiceMode aMode) { mServiceMode = static_cast(aMode); } #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE void SetNat64Mode(Nat64Mode aMode) { mNat64Mode = static_cast(aMode); } #endif void SetTransportProto(TransportProto aTransportProto) { mTransportProto = static_cast(aTransportProto); } void SetFrom(const QueryConfig *aConfig, const QueryConfig &aDefaultConfig); }; #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE /** * This structure provides info for a DNS service instance. * */ typedef otDnsServiceInfo ServiceInfo; #endif /** * This class represents a DNS query response. * */ class Response : public otDnsAddressResponse, #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE public otDnsBrowseResponse, public otDnsServiceResponse, #endif public Clearable { friend class Client; protected: enum Section : uint8_t { kAnswerSection, kAdditionalDataSection, }; Response(void) { Clear(); } Error GetName(char *aNameBuffer, uint16_t aNameBufferSize) const; void SelectSection(Section aSection, uint16_t &aOffset, uint16_t &aNumRecord) const; Error CheckForHostNameAlias(Section aSection, Name &aHostName) const; Error FindHostAddress(Section aSection, const Name &aHostName, uint16_t aIndex, Ip6::Address &aAddress, uint32_t &aTtl) const; #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE Error FindARecord(Section aSection, const Name &aHostName, uint16_t aIndex, ARecord &aARecord) const; #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE void InitServiceInfo(ServiceInfo &aServiceInfo) const; Error ReadServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const; Error ReadTxtRecord(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const; #endif void PopulateFrom(const Message &aMessage); Instance *mInstance; // The OpenThread instance. Query *mQuery; // The associated query. const Message *mMessage; // The response message. Response *mNext; // The next response when we have related queries. uint16_t mAnswerOffset; // Answer section offset in `mMessage`. uint16_t mAnswerRecordCount; // Number of records in answer section. uint16_t mAdditionalOffset; // Additional data section offset in `mMessage`. uint16_t mAdditionalRecordCount; // Number of records in additional data section. #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE // This flag is only used in an IPv6 address query response. // It indicates that the response does not contain any IPv6 // addresses but server provided at least one IPv4 address // in the additional data section for NAT64 address synthesis. bool mIp6QueryResponseRequiresNat64; #endif }; /** * This type represents the function pointer callback which is called when a DNS response for an address resolution * query is received. * */ typedef otDnsAddressCallback AddressCallback; /** * This type represents an address resolution query DNS response. * */ class AddressResponse : public Response { friend class Client; public: /** * This method gets the host name associated with an address resolution DNS response. * * This method MUST only be used from `AddressCallback`. * * @param[out] aNameBuffer A buffer to char array to output the host name. * @param[in] aNameBufferSize The size of @p aNameBuffer. * * @retval kErrorNone The host name was read successfully. * @retval kErrorNoBufs The name does not fit in @p aNameBuffer. * */ Error GetHostName(char *aNameBuffer, uint16_t aNameBufferSize) const { return GetName(aNameBuffer, aNameBufferSize); } /** * This method gets the IPv6 address associated with an address resolution DNS response. * * This method MUST only be used from `AddressCallback`. * * The response may include multiple IPv6 address records. @p aIndex can be used to iterate through the list of * addresses. Index zero gets the the first address and so on. When we reach end of the list, this method * returns `kErrorNotFound`. * * @param[in] aIndex The address record index to retrieve. * @param[out] aAddress A reference to an IPv6 address to output the address. * @param[out] aTtl A reference to a `uint32_t` to output TTL for the address. * * @retval kErrorNone The address was read successfully. * @retval kErrorNotFound No address record at @p aIndex. * @retval kErrorParse Could not parse the records. * @retval kErrorInvalidState No NAT64 prefix (applicable only when NAT64 is allowed). * */ Error GetAddress(uint16_t aIndex, Ip6::Address &aAddress, uint32_t &aTtl) const; private: #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE Error GetNat64Prefix(Ip6::Prefix &aPrefix) const; #endif }; #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE /** * This type represents the function pointer callback which is called when a response for a browse (service * instance enumeration) DNS query is received. * */ typedef otDnsBrowseCallback BrowseCallback; /** * This type represents a browse (service instance enumeration) DNS response. * */ class BrowseResponse : public Response { friend class Client; public: /** * This method gets the service name associated with a DNS browse response. * * This method MUST only be used from `BrowseCallback`. * * @param[out] aNameBuffer A buffer to char array to output the host name. * @param[in] aNameBufferSize The size of @p aNameBuffer. * * @retval kErrorNone The host name was read successfully. * @retval kErrorNoBufs The name does not fit in @p aNameBuffer. * */ Error GetServiceName(char *aNameBuffer, uint16_t aNameBufferSize) const { return GetName(aNameBuffer, aNameBufferSize); } /** * This method gets a service instance associated with a DNS browse (service instance enumeration) response. * * This method MUST only be used from `BrowseCallback`. * * A response may include multiple service instance records. @p aIndex can be used to iterate through the list. * Index zero gives the the first record. When we reach end of the list, `kErrorNotFound` is returned. * * Note that this method gets the service instance label and not the full service instance name which is of the * form `..`. * * @param[in] aIndex The service instance record index to retrieve. * @param[out] aLabelBuffer A char array to output the service instance label (MUST NOT be NULL). * @param[in] aLabelBufferSize The size of @p aLabelBuffer. * * @retval kErrorNone The service instance was read successfully. * @retval kErrorNoBufs The name does not fit in @p aNameBuffer. * @retval kErrorNotFound No service instance record at @p aIndex. * @retval kErrorParse Could not parse the records. * */ Error GetServiceInstance(uint16_t aIndex, char *aLabelBuffer, uint8_t aLabelBufferSize) const; /** * This method gets info for a service instance from a DNS browse (service instance enumeration) response. * * This method MUST only be used from `BrowseCallback`. * * A browse DNS response should include the SRV, TXT, and AAAA records for the service instances that are * enumerated (note that it is a SHOULD and not a MUST requirement). This method tries to retrieve this info * for a given service instance. * * - If no matching SRV record is found, `kErrorNotFound` is returned. * - If a matching SRV record is found, @p aServiceInfo is updated returning `kErrorNone`. * - If no matching TXT record is found, `mTxtDataSize` in @p aServiceInfo is set to zero. * - If no matching AAAA record is found, `mHostAddress is set to all zero or unspecified address. * - If there are multiple AAAA records for the host name `mHostAddress` is set to the first one. The other * addresses can be retrieved using `GetHostAddress()` method. * * @param[in] aInstanceLabel The service instance label (MUST NOT be `nullptr`). * @param[out] aServiceInfo A `ServiceInfo` to output the service instance information. * * @retval kErrorNone The service instance info was read. @p aServiceInfo is updated. * @retval kErrorNotFound Could not find a matching SRV record for @p aInstanceLabel. * @retval kErrorNoBufs The host name and/or the TXT data could not fit in given buffers. * @retval kErrorParse Could not parse the records. * */ Error GetServiceInfo(const char *aInstanceLabel, ServiceInfo &aServiceInfo) const; /** * This method gets the host IPv6 address from a DNS browse (service instance enumeration) response. * * This method MUST only be used from `BrowseCallback`. * * The response can include zero or more IPv6 address records. @p aIndex can be used to iterate through the * list of addresses. Index zero gets the first address and so on. When we reach end of the list, this method * returns `kErrorNotFound`. * * @param[in] aHostName The host name to get the address (MUST NOT be `nullptr`). * @param[in] aIndex The address record index to retrieve. * @param[out] aAddress A reference to an IPv6 address to output the address. * @param[out] aTtl A reference to a `uint32_t` to output TTL for the address. * * @retval kErrorNone The address was read successfully. * @retval kErrorNotFound No address record for @p aHostname at @p aIndex. * @retval kErrorParse Could not parse the records. * */ Error GetHostAddress(const char *aHostName, uint16_t aIndex, Ip6::Address &aAddress, uint32_t &aTtl) const; private: Error FindPtrRecord(const char *aInstanceLabel, Name &aInstanceName) const; }; /** * This type represents the function pointer callback which is called when a response for a service instance * resolution DNS query is received. * */ typedef otDnsServiceCallback ServiceCallback; /** * This type represents a service instance resolution DNS response. * */ class ServiceResponse : public Response { friend class Client; public: /** * This method gets the service instance name associated with a DNS service instance resolution response. * * This method MUST only be used from `ServiceCallback`. * * @param[out] aLabelBuffer A buffer to char array to output the service instance label (MUST NOT be NULL). * @param[in] aLabelBufferSize The size of @p aLabelBuffer. * @param[out] aNameBuffer A buffer to char array to output the rest of service name (can be NULL if user * is not interested in getting the name). * @param[in] aNameBufferSize The size of @p aNameBuffer. * * @retval kErrorNone The service instance name was read successfully. * @retval kErrorNoBufs Either the label or name does not fit in the given buffers. * */ Error GetServiceName(char *aLabelBuffer, uint8_t aLabelBufferSize, char *aNameBuffer, uint16_t aNameBufferSize) const; /** * This method gets info for a service instance from a DNS service instance resolution response. * * This method MUST only be used from `ServiceCallback`. * * - If no matching SRV record is found, `kErrorNotFound` is returned. * - If a matching SRV record is found, @p aServiceInfo is updated and `kErrorNone` is returned. * - If no matching TXT record is found, `mTxtDataSize` in @p aServiceInfo is set to zero. * - If no matching AAAA record is found, `mHostAddress is set to all zero or unspecified address. * - If there are multiple AAAA records for the host name, `mHostAddress` is set to the first one. The other * addresses can be retrieved using `GetHostAddress()` method. * * @param[out] aServiceInfo A `ServiceInfo` to output the service instance information * * @retval kErrorNone The service instance info was read. @p aServiceInfo is updated. * @retval kErrorNotFound Could not find a matching SRV record. * @retval kErrorNoBufs The host name and/or TXT data could not fit in the given buffers. * @retval kErrorParse Could not parse the records in the @p aResponse. * */ Error GetServiceInfo(ServiceInfo &aServiceInfo) const; /** * This method gets the host IPv6 address from a DNS service instance resolution response. * * This method MUST only be used from `ServiceCallback`. * * The response can include zero or more IPv6 address records. @p aIndex can be used to iterate through the * list of addresses. Index zero gets the first address and so on. When we reach end of the list, this method * returns `kErrorNotFound`. * * @param[in] aHostName The host name to get the address (MUST NOT be `nullptr`). * @param[in] aIndex The address record index to retrieve. * @param[out] aAddress A reference to an IPv6 address to output the address. * @param[out] aTtl A reference to a `uint32_t` to output TTL for the address. * * @retval kErrorNone The address was read successfully. * @retval kErrorNotFound No address record for @p aHostname at @p aIndex. * @retval kErrorParse Could not parse the records. * */ Error GetHostAddress(const char *aHostName, uint16_t aIndex, Ip6::Address &aAddress, uint32_t &aTtl) const; }; #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE /** * This constructor initializes the object. * * @param[in] aInstance A reference to the OpenThread instance. * */ explicit Client(Instance &aInstance); /** * This method starts the DNS client. * * @retval kErrorNone Successfully started the DNS client. * @retval kErrorAlready The socket is already open. * */ Error Start(void); /** * This method stops the DNS client. * */ void Stop(void); /** * This method gets the current default query config being used by DNS client. * * @returns The current default query config. * */ const QueryConfig &GetDefaultConfig(void) const { return mDefaultConfig; } /** * This method sets the default query config. * * @param[in] aQueryConfig The new default query config. * */ void SetDefaultConfig(const QueryConfig &aQueryConfig); /** * This method resets the default config to the config used when the OpenThread stack starts. * * When OpenThread stack starts, the default DNS query config is determined from a set of OT config options such as * `OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_IP6_ADDRESS`, `_DEFAULT_SERVER_PORT`, or `_DEFAULT_RESPONSE_TIMEOUT` * etc. (see `config/dns_client.h` for all related config options). * */ void ResetDefaultConfig(void); /** * This method sends an address resolution DNS query for AAAA (IPv6) record for a given host name. * * The @p aConfig can be nullptr. In this case the default config (from `GetDefaultConfig()`) will be used as * the config for this query. In a non-nullptr @p aConfig, some of the fields can be left unspecified (value zero). * The unspecified fields are then replaced by the values from the default config. * * @param[in] aHostName The host name for which to query the address (MUST NOT be `nullptr`). * @param[in] aCallback A callback function pointer to report the result of query. * @param[in] aContext A pointer to arbitrary context information passed to @p aCallback. * @param[in] aConfig The config to use for this query. * * @retval kErrorNone Successfully sent DNS query. * @retval kErrorNoBufs Failed to allocate retransmission data. * @retval kErrorInvalidArgs The host name is not valid format. * @retval kErrorInvalidState Cannot send query since Thread interface is not up. * */ Error ResolveAddress(const char *aHostName, AddressCallback aCallback, void *aContext, const QueryConfig *aConfig = nullptr); #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE /** * This method sends an address resolution DNS query for A (IPv4) record for a given host name. * * When a successful response is received, the addresses are returned from @p aCallback as NAT64 IPv6 translated * versions of the IPv4 addresses from the query response. * * The @p aConfig can be nullptr. In this case the default config (from `GetDefaultConfig()`) will be used as * the config for this query. In a non-nullptr @p aConfig, some of the fields can be left unspecified (value zero). * The unspecified fields are then replaced by the values from the default config. * * @param[in] aHostName The host name for which to query the address (MUST NOT be `nullptr`). * @param[in] aCallback A callback function pointer to report the result of query. * @param[in] aContext A pointer to arbitrary context information passed to @p aCallback. * @param[in] aConfig The config to use for this query. * * @retval kErrorNone Successfully sent DNS query. * @retval kErrorNoBufs Failed to allocate retransmission data. * @retval kErrorInvalidArgs The host name is not valid format or NAT64 is not enabled in config. * @retval kErrorInvalidState Cannot send query since Thread interface is not up, or there is no NAT64 prefix. * */ Error ResolveIp4Address(const char *aHostName, AddressCallback aCallback, void *aContext, const QueryConfig *aConfig = nullptr); #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE /** * This method sends a browse (service instance enumeration) DNS query for a given service name. * * The @p aConfig can be nullptr. In this case the default config (from `GetDefaultConfig()`) will be used as * the config for this query. In a non-nullptr @p aConfig, some of the fields can be left unspecified (value zero). * The unspecified fields are then replaced by the values from the default config. * * @param[in] aServiceName The service name to query for (MUST NOT be `nullptr`). * @param[in] aCallback The callback to report the response or errors (such as time-out). * @param[in] aContext A pointer to arbitrary context information. * @param[in] aConfig The config to use for this query. * * @retval kErrorNone Query sent successfully. @p aCallback will be invoked to report the status. * @retval kErrorNoBufs Insufficient buffer to prepare and send query. * */ Error Browse(const char *aServiceName, BrowseCallback aCallback, void *aContext, const QueryConfig *aConfig = nullptr); /** * This method starts a DNS service instance resolution for a given service instance. * * The @p aConfig can be `nullptr`. In this case the default config (from `GetDefaultConfig()`) will be used as * the config for this query. In a non-`nullptr` @p aConfig, some of the fields can be left unspecified (value * zero). The unspecified fields are then replaced by the values from the default config. * * @param[in] aInstanceLabel The service instance label. * @param[in] aServiceName The service name (together with @p aInstanceLabel form full instance name). * @param[in] aCallback A function pointer that shall be called on response reception or time-out. * @param[in] aContext A pointer to arbitrary context information. * @param[in] aConfig The config to use for this query. * * @retval kErrorNone Query sent successfully. @p aCallback will be invoked to report the status. * @retval kErrorNoBufs Insufficient buffer to prepare and send query. * @retval kErrorInvalidArgs @p aInstanceLabel is `nullptr`. * */ Error ResolveService(const char *aInstanceLabel, const char *aServiceName, otDnsServiceCallback aCallback, void *aContext, const QueryConfig *aConfig = nullptr); /** * This method starts a DNS service instance resolution for a given service instance, with a potential follow-up * host name resolution (if the server/resolver does not provide AAAA/A records for the host name in the response * to SRV query). * * The @p aConfig can be `nullptr`. In this case the default config (from `GetDefaultConfig()`) will be used as * the config for this query. In a non-`nullptr` @p aConfig, some of the fields can be left unspecified (value * zero). The unspecified fields are then replaced by the values from the default config. * * @param[in] aInstanceLabel The service instance label. * @param[in] aServiceName The service name (together with @p aInstanceLabel form full instance name). * @param[in] aCallback A function pointer that shall be called on response reception or time-out. * @param[in] aContext A pointer to arbitrary context information. * @param[in] aConfig The config to use for this query. * * @retval kErrorNone Query sent successfully. @p aCallback will be invoked to report the status. * @retval kErrorNoBufs Insufficient buffer to prepare and send query. * @retval kErrorInvalidArgs @p aInstanceLabel is `nullptr` or the @p aConfig is invalid. * */ Error ResolveServiceAndHostAddress(const char *aInstanceLabel, const char *aServiceName, ServiceCallback aCallback, void *aContext, const QueryConfig *aConfig = nullptr); #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE private: enum QueryType : uint8_t { kIp6AddressQuery, // IPv6 Address resolution. #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE kIp4AddressQuery, // IPv4 Address resolution #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE kBrowseQuery, // Browse (service instance enumeration). kServiceQuerySrvTxt, // Service instance resolution both SRV and TXT records. kServiceQuerySrv, // Service instance resolution SRV record only. kServiceQueryTxt, // Service instance resolution TXT record only. #endif kNoQuery, }; #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE enum TcpState : uint8_t { kTcpUninitialized = 0, kTcpConnecting, kTcpConnectedIdle, kTcpConnectedSending, }; #endif union Callback { AddressCallback mAddressCallback; #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE BrowseCallback mBrowseCallback; ServiceCallback mServiceCallback; #endif }; typedef MessageQueue QueryList; // List of queries. struct QueryInfo : public Clearable // Query related Info { void ReadFrom(const Query &aQuery) { IgnoreError(aQuery.Read(0, *this)); } QueryType mQueryType; uint16_t mMessageId; Callback mCallback; void *mCallbackContext; TimeMilli mRetransmissionTime; QueryConfig mConfig; uint8_t mTransmissionCount; bool mShouldResolveHostAddr; Query *mMainQuery; Query *mNextQuery; Message *mSavedResponse; // Followed by the name (service, host, instance) encoded as a `Dns::Name`. }; static constexpr uint16_t kNameOffsetInQuery = sizeof(QueryInfo); Error StartQuery(QueryInfo &aInfo, const char *aLabel, const char *aName, QueryType aSecondType = kNoQuery); Error AllocateQuery(const QueryInfo &aInfo, const char *aLabel, const char *aName, Query *&aQuery); void FreeQuery(Query &aQuery); void UpdateQuery(Query &aQuery, const QueryInfo &aInfo) { aQuery.Write(0, aInfo); } Query &FindMainQuery(Query &aQuery); Error SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer); void FinalizeQuery(Query &aQuery, Error aError); void FinalizeQuery(Response &Response, Error aError); static void GetQueryTypeAndCallback(const Query &aQuery, QueryType &aType, Callback &aCallback, void *&aContext); Error AppendNameFromQuery(const Query &aQuery, Message &aMessage); Query *FindQueryById(uint16_t aMessageId); static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMsgInfo); void ProcessResponse(const Message &aResponseMessage); Error ParseResponse(const Message &aResponseMessage, Query *&aQuery, Error &aResponseError); bool CanFinalizeQuery(Query &aQuery); void SaveQueryResponse(Query &aQuery, const Message &aResponseMessage); Query *PopulateResponse(Response &aResponse, Query &aQuery, const Message &aResponseMessage); void PrepareResponseAndFinalize(Query &aQuery, const Message &aResponseMessage, Response *aPrevResponse); void HandleTimer(void); #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE Error ReplaceWithIp4Query(Query &aQuery); #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE Error Resolve(const char *aInstanceLabel, const char *aServiceName, ServiceCallback aCallback, void *aContext, const QueryConfig *aConfig, bool aShouldResolveHostAddr); Error ReplaceWithSeparateSrvTxtQueries(Query &aQuery); void ResolveHostAddressIfNeeded(Query &aQuery, const Message &aResponseMessage); #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE void UpdateDefaultConfigAddress(void); #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE static void HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint); static void HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); static void HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); static void HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint, size_t aBytesAvailable, bool aEndOfStream, size_t aBytesRemaining); void HandleTcpEstablished(otTcpEndpoint *aEndpoint); void HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); void HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); void HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, size_t aBytesAvailable, bool aEndOfStream, size_t aBytesRemaining); Error InitTcpSocket(void); Error ReadFromLinkBuffer(const otLinkedBuffer *&aLinkedBuffer, size_t &aOffset, Message &aMessage, uint16_t aLength); void PrepareTcpMessage(Message &aMessage); #endif // OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE static const uint8_t kQuestionCount[]; static const uint16_t *const kQuestionRecordTypes[]; static const uint16_t kIp6AddressQueryRecordTypes[]; #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE static const uint16_t kIp4AddressQueryRecordTypes[]; #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE static const uint16_t kBrowseQueryRecordTypes[]; static const uint16_t kServiceQueryRecordTypes[]; #endif static constexpr uint16_t kUdpQueryMaxSize = 512; using RetryTimer = TimerMilliIn; Ip6::Udp::Socket mSocket; #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE Ip6::Tcp::Endpoint mEndpoint; otLinkedBuffer mSendLink; uint8_t mSendBufferBytes[OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE]; uint8_t mReceiveBufferBytes[OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE]; TcpState mTcpState; #endif QueryList mMainQueries; RetryTimer mTimer; QueryConfig mDefaultConfig; #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE bool mUserDidSetDefaultAddress; #endif }; } // namespace Dns DefineCoreType(otDnsQueryConfig, Dns::Client::QueryConfig); DefineCoreType(otDnsAddressResponse, Dns::Client::AddressResponse); #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE DefineCoreType(otDnsBrowseResponse, Dns::Client::BrowseResponse); DefineCoreType(otDnsServiceResponse, Dns::Client::ServiceResponse); DefineCoreType(otDnsServiceInfo, Dns::Client::ServiceInfo); #endif } // namespace ot #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE #endif // DNS_CLIENT_HPP_