1 /* 2 * Copyright (c) 2021, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #ifndef DNS_SERVER_HPP_ 30 #define DNS_SERVER_HPP_ 31 32 #include "openthread-core-config.h" 33 34 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE 35 36 #include <openthread/dnssd_server.h> 37 38 #include "common/as_core_type.hpp" 39 #include "common/callback.hpp" 40 #include "common/message.hpp" 41 #include "common/non_copyable.hpp" 42 #include "common/owned_ptr.hpp" 43 #include "common/timer.hpp" 44 #include "net/dns_types.hpp" 45 #include "net/ip6.hpp" 46 #include "net/netif.hpp" 47 #include "net/srp_server.hpp" 48 49 /** 50 * @file 51 * This file includes definitions for the DNS-SD server. 52 */ 53 54 struct otPlatDnsUpstreamQuery 55 { 56 }; 57 58 namespace ot { 59 60 namespace Srp { 61 class Server; 62 } 63 64 namespace Dns { 65 namespace ServiceDiscovery { 66 67 /** 68 * Implements DNS-SD server. 69 * 70 */ 71 class Server : public InstanceLocator, private NonCopyable 72 { 73 friend class Srp::Server; 74 75 public: 76 /** 77 * Contains the counters of the DNS-SD server. 78 * 79 */ 80 class Counters : public otDnssdCounters, public Clearable<Counters> 81 { 82 }; 83 84 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE 85 /** 86 * Represents an upstream query transaction. The methods should only be used by 87 * `Dns::ServiceDiscovery::Server`. 88 * 89 */ 90 class UpstreamQueryTransaction : public otPlatDnsUpstreamQuery 91 { 92 public: 93 /** 94 * Returns whether the transaction is valid. 95 * 96 * @retval TRUE The transaction is valid. 97 * @retval FALSE The transaction is not valid. 98 * 99 */ IsValid(void) const100 bool IsValid(void) const { return mValid; } 101 102 /** 103 * Returns the time when the transaction expires. 104 * 105 * @returns The expire time of the transaction. 106 * 107 */ GetExpireTime(void) const108 TimeMilli GetExpireTime(void) const { return mExpireTime; } 109 110 /** 111 * Resets the transaction with a reason. The transaction will be invalid and can be reused for 112 * another upstream query after this call. 113 * 114 */ Reset(void)115 void Reset(void) { mValid = false; } 116 117 /** 118 * Initializes the transaction. 119 * 120 * @param[in] aMessageInfo The IP message info of the query. 121 * 122 */ 123 void Init(const Ip6::MessageInfo &aMessageInfo); 124 125 /** 126 * Returns the message info of the query. 127 * 128 * @returns The message info of the query. 129 * 130 */ GetMessageInfo(void) const131 const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; } 132 133 private: 134 Ip6::MessageInfo mMessageInfo; 135 TimeMilli mExpireTime; 136 bool mValid; 137 }; 138 #endif 139 140 /** 141 * Specifies a DNS-SD query type. 142 * 143 */ 144 enum DnsQueryType : uint8_t 145 { 146 kDnsQueryNone = OT_DNSSD_QUERY_TYPE_NONE, ///< Service type unspecified. 147 kDnsQueryBrowse = OT_DNSSD_QUERY_TYPE_BROWSE, ///< Service type browse service. 148 kDnsQueryResolve = OT_DNSSD_QUERY_TYPE_RESOLVE, ///< Service type resolve service instance. 149 kDnsQueryResolveHost = OT_DNSSD_QUERY_TYPE_RESOLVE_HOST, ///< Service type resolve hostname. 150 }; 151 152 typedef otDnssdServiceInstanceInfo ServiceInstanceInfo; ///< A discovered service instance for a DNS-SD query. 153 typedef otDnssdHostInfo HostInfo; ///< A discover host for a DNS-SD query. 154 155 typedef otDnssdQuerySubscribeCallback SubscribeCallback; 156 typedef otDnssdQueryUnsubscribeCallback UnsubscribeCallback; 157 158 static constexpr uint16_t kPort = OPENTHREAD_CONFIG_DNSSD_SERVER_PORT; ///< The DNS-SD server port. 159 160 /** 161 * Initializes the object. 162 * 163 * @param[in] aInstance A reference to the OpenThread instance. 164 * 165 */ 166 explicit Server(Instance &aInstance); 167 168 /** 169 * Starts the DNS-SD server. 170 * 171 * @retval kErrorNone Successfully started the DNS-SD server. 172 * @retval kErrorFailed If failed to open or bind the UDP socket. 173 * 174 */ 175 Error Start(void); 176 177 /** 178 * Stops the DNS-SD server. 179 * 180 */ 181 void Stop(void); 182 183 /** 184 * Sets DNS-SD query callbacks. 185 * 186 * @param[in] aSubscribe A pointer to the callback function to subscribe a service or service instance. 187 * @param[in] aUnsubscribe A pointer to the callback function to unsubscribe a service or service instance. 188 * @param[in] aContext A pointer to the application-specific context. 189 * 190 */ 191 void SetQueryCallbacks(SubscribeCallback aSubscribe, UnsubscribeCallback aUnsubscribe, void *aContext); 192 193 /** 194 * Notifies a discovered service instance. 195 * 196 * @param[in] aServiceFullName The null-terminated full service name. 197 * @param[in] aInstanceInfo A reference to the discovered service instance information. 198 * 199 */ 200 void HandleDiscoveredServiceInstance(const char *aServiceFullName, const ServiceInstanceInfo &aInstanceInfo); 201 202 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE 203 /** 204 * Notifies an answer of an upstream DNS query. 205 * 206 * The Transaction will be released. 207 * 208 * @param[in] aQueryTransaction A reference to upstream DNS query transaction. 209 * @param[in] aResponseMessage A pointer to response UDP message, should be allocated from Udp::NewMessage. 210 * Passing a nullptr means close the transaction without a response. 211 * 212 */ 213 void OnUpstreamQueryDone(UpstreamQueryTransaction &aQueryTransaction, Message *aResponseMessage); 214 215 /** 216 * Indicates whether the server will forward DNS queries to platform DNS upstream API. 217 * 218 * @retval TRUE If the server will forward DNS queries. 219 * @retval FALSE If the server will not forward DNS queries. 220 * 221 */ IsUpstreamQueryEnabled(void) const222 bool IsUpstreamQueryEnabled(void) const { return mEnableUpstreamQuery; } 223 224 /** 225 * Enables or disables forwarding DNS queries to platform DNS upstream API. 226 * 227 * @param[in] aEnabled A boolean to enable/disable forwarding DNS queries to upstream. 228 * 229 */ SetUpstreamQueryEnabled(bool aEnabled)230 void SetUpstreamQueryEnabled(bool aEnabled) { mEnableUpstreamQuery = aEnabled; } 231 #endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE 232 233 /** 234 * Notifies a discovered host. 235 * 236 * @param[in] aHostFullName The null-terminated full host name. 237 * @param[in] aHostInfo A reference to the discovered host information. 238 * 239 */ 240 void HandleDiscoveredHost(const char *aHostFullName, const HostInfo &aHostInfo); 241 242 /** 243 * Acquires the next query in the server. 244 * 245 * @param[in] aQuery The query pointer. Pass `nullptr` to get the first query. 246 * 247 * @returns A pointer to the query or `nullptr` if no more queries. 248 * 249 */ 250 const otDnssdQuery *GetNextQuery(const otDnssdQuery *aQuery) const; 251 252 /** 253 * Acquires the DNS-SD query type and name for a specific query. 254 * 255 * @param[in] aQuery The query pointer. 256 * @param[out] aName The name output buffer. 257 * 258 * @returns The DNS-SD query type. 259 * 260 */ 261 static DnsQueryType GetQueryTypeAndName(const otDnssdQuery *aQuery, Dns::Name::Buffer &aName); 262 263 /** 264 * Returns the counters of the DNS-SD server. 265 * 266 * @returns A reference to the `Counters` instance. 267 * 268 */ GetCounters(void) const269 const Counters &GetCounters(void) const { return mCounters; }; 270 271 /** 272 * Represents different test mode flags for use in `SetTestMode()`. 273 * 274 */ 275 enum TestModeFlags : uint8_t 276 { 277 kTestModeSingleQuestionOnly = 1 << 0, ///< Allow single question in query, send `FormatError` otherwise. 278 kTestModeEmptyAdditionalSection = 1 << 1, ///< Do not include any RR in additional section. 279 }; 280 281 static constexpr uint8_t kTestModeDisabled = 0; ///< Test mode is disabled (no flags). 282 283 /** 284 * Sets the test mode for `Server`. 285 * 286 * The test mode flags are intended for testing the client by having server behave in certain ways, e.g., reject 287 * messages with certain format (e.g., more than one question in query). 288 * 289 * @param[in] aTestMode The new test mode (combination of `TestModeFlags`). 290 * 291 */ SetTestMode(uint8_t aTestMode)292 void SetTestMode(uint8_t aTestMode) { mTestMode = aTestMode; } 293 294 private: 295 static constexpr bool kBindUnspecifiedNetif = OPENTHREAD_CONFIG_DNSSD_SERVER_BIND_UNSPECIFIED_NETIF; 296 static constexpr uint32_t kQueryTimeout = OPENTHREAD_CONFIG_DNSSD_QUERY_TIMEOUT; 297 static constexpr uint16_t kMaxConcurrentUpstreamQueries = 32; 298 299 typedef Header::Response ResponseCode; 300 301 typedef Message ProxyQuery; 302 typedef MessageQueue ProxyQueryList; 303 304 enum QueryType : uint8_t 305 { 306 kPtrQuery, 307 kSrvQuery, 308 kTxtQuery, 309 kSrvTxtQuery, 310 kAaaaQuery, 311 }; 312 313 enum Section : uint8_t 314 { 315 kAnswerSection, 316 kAdditionalDataSection, 317 }; 318 319 struct Request 320 { 321 ResponseCode ParseQuestions(uint8_t aTestMode); 322 323 const Message *mMessage; 324 const Ip6::MessageInfo *mMessageInfo; 325 Header mHeader; 326 QueryType mType; 327 }; 328 329 struct ProxyQueryInfo; 330 331 struct NameOffsets : public Clearable<NameOffsets> 332 { 333 uint16_t mDomainName; 334 uint16_t mServiceName; 335 uint16_t mInstanceName; 336 uint16_t mHostName; 337 }; 338 339 class Response : public InstanceLocator, private NonCopyable 340 { 341 public: 342 explicit Response(Instance &aInstance); 343 Error AllocateAndInitFrom(const Request &aRequest); 344 void InitFrom(ProxyQuery &aQuery, const ProxyQueryInfo &aInfo); SetResponseCode(ResponseCode aResponseCode)345 void SetResponseCode(ResponseCode aResponseCode) { mHeader.SetResponseCode(aResponseCode); } 346 ResponseCode AddQuestionsFrom(const Request &aRequest); 347 Error ParseQueryName(void); 348 void ReadQueryName(Name::Buffer &aName) const; 349 bool QueryNameMatches(const char *aName) const; 350 Error AppendQueryName(void); 351 Error AppendPtrRecord(const char *aInstanceLabel, uint32_t aTtl); 352 Error AppendSrvRecord(const ServiceInstanceInfo &aInstanceInfo); 353 Error AppendSrvRecord(const char *aHostName, 354 uint32_t aTtl, 355 uint16_t aPriority, 356 uint16_t aWeight, 357 uint16_t aPort); 358 Error AppendTxtRecord(const ServiceInstanceInfo &aInstanceInfo); 359 Error AppendTxtRecord(const void *aTxtData, uint16_t aTxtLength, uint32_t aTtl); 360 Error AppendHostAddresses(const HostInfo &aHostInfo); 361 Error AppendHostAddresses(const ServiceInstanceInfo &aInstanceInfo); 362 Error AppendHostAddresses(const Ip6::Address *aAddrs, uint16_t aAddrsLength, uint32_t aTtl); 363 void UpdateRecordLength(ResourceRecord &aRecord, uint16_t aOffset); 364 void IncResourceRecordCount(void); 365 void Send(const Ip6::MessageInfo &aMessageInfo); 366 void Answer(const HostInfo &aHostInfo, const Ip6::MessageInfo &aMessageInfo); 367 void Answer(const ServiceInstanceInfo &aInstanceInfo, const Ip6::MessageInfo &aMessageInfo); 368 Error ExtractServiceInstanceLabel(const char *aInstanceName, Name::LabelBuffer &aLabel); 369 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE 370 Error ResolveBySrp(void); 371 bool QueryNameMatchesService(const Srp::Server::Service &aService) const; 372 Error AppendSrvRecord(const Srp::Server::Service &aService); 373 Error AppendTxtRecord(const Srp::Server::Service &aService); 374 Error AppendHostAddresses(const Srp::Server::Host &aHost); 375 #endif 376 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) 377 void Log(void) const; 378 static const char *QueryTypeToString(QueryType aType); 379 #endif 380 381 OwnedPtr<Message> mMessage; 382 Header mHeader; 383 QueryType mType; 384 Section mSection; 385 NameOffsets mOffsets; 386 }; 387 388 struct ProxyQueryInfo 389 { 390 void ReadFrom(const ProxyQuery &aQuery); 391 void RemoveFrom(ProxyQuery &aQuery) const; 392 void UpdateIn(ProxyQuery &aQuery) const; 393 394 QueryType mType; 395 Ip6::MessageInfo mMessageInfo; 396 TimeMilli mExpireTime; 397 NameOffsets mOffsets; 398 }; 399 IsRunning(void) const400 bool IsRunning(void) const { return mSocket.IsBound(); } 401 static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); 402 void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); 403 void ProcessQuery(Request &aRequest); 404 static uint8_t GetNameLength(const char *aName); 405 406 void ResolveByProxy(Response &aResponse, const Ip6::MessageInfo &aMessageInfo); 407 void RemoveQueryAndPrepareResponse(ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Response &aResponse); 408 void Finalize(ProxyQuery &aQuery, ResponseCode aResponseCode); 409 static void ReadQueryName(const Message &aQuery, Name::Buffer &aName); 410 static bool QueryNameMatches(const Message &aQuery, const char *aName); 411 412 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE 413 static bool ShouldForwardToUpstream(const Request &aRequest); 414 UpstreamQueryTransaction *AllocateUpstreamQueryTransaction(const Ip6::MessageInfo &aMessageInfo); 415 void ResetUpstreamQueryTransaction(UpstreamQueryTransaction &aTxn, Error aError); 416 Error ResolveByUpstream(const Request &aRequest); 417 #endif 418 419 void HandleTimer(void); 420 void ResetTimer(void); 421 422 void UpdateResponseCounters(ResponseCode aResponseCode); 423 424 using ServerTimer = TimerMilliIn<Server, &Server::HandleTimer>; 425 426 static const char kDefaultDomainName[]; 427 static const char kSubLabel[]; 428 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE 429 static const char *kBlockedDomains[]; 430 #endif 431 432 Ip6::Udp::Socket mSocket; 433 434 ProxyQueryList mProxyQueries; 435 Callback<SubscribeCallback> mQuerySubscribe; 436 Callback<UnsubscribeCallback> mQueryUnsubscribe; 437 438 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE 439 bool mEnableUpstreamQuery; 440 UpstreamQueryTransaction mUpstreamQueryTransactions[kMaxConcurrentUpstreamQueries]; 441 #endif 442 443 ServerTimer mTimer; 444 Counters mCounters; 445 uint8_t mTestMode; 446 }; 447 448 } // namespace ServiceDiscovery 449 } // namespace Dns 450 451 DefineMapEnum(otDnssdQueryType, Dns::ServiceDiscovery::Server::DnsQueryType); 452 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE 453 DefineCoreType(otPlatDnsUpstreamQuery, Dns::ServiceDiscovery::Server::UpstreamQueryTransaction); 454 #endif 455 456 } // namespace ot 457 458 #endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE 459 460 #endif // DNS_SERVER_HPP_ 461