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/message.hpp" 40 #include "common/non_copyable.hpp" 41 #include "common/timer.hpp" 42 #include "net/dns_types.hpp" 43 #include "net/ip6.hpp" 44 #include "net/netif.hpp" 45 #include "net/srp_server.hpp" 46 47 /** 48 * @file 49 * This file includes definitions for the DNS-SD server. 50 */ 51 52 namespace ot { 53 54 namespace Srp { 55 class Server; 56 } 57 58 namespace Dns { 59 namespace ServiceDiscovery { 60 61 /** 62 * This class implements DNS-SD server. 63 * 64 */ 65 class Server : public InstanceLocator, private NonCopyable 66 { 67 friend class Srp::Server; 68 69 public: 70 /** 71 * This class contains the counters of the DNS-SD server. 72 * 73 */ 74 class Counters : public otDnssdCounters, public Clearable<Counters> 75 { 76 }; 77 78 /** 79 * This enumeration specifies a DNS-SD query type. 80 * 81 */ 82 enum DnsQueryType : uint8_t 83 { 84 kDnsQueryNone = OT_DNSSD_QUERY_TYPE_NONE, ///< Service type unspecified. 85 kDnsQueryBrowse = OT_DNSSD_QUERY_TYPE_BROWSE, ///< Service type browse service. 86 kDnsQueryResolve = OT_DNSSD_QUERY_TYPE_RESOLVE, ///< Service type resolve service instance. 87 kDnsQueryResolveHost = OT_DNSSD_QUERY_TYPE_RESOLVE_HOST, ///< Service type resolve hostname. 88 }; 89 90 static constexpr uint16_t kPort = OPENTHREAD_CONFIG_DNSSD_SERVER_PORT; ///< The DNS-SD server port. 91 92 /** 93 * This constructor initializes the object. 94 * 95 * @param[in] aInstance A reference to the OpenThread instance. 96 * 97 */ 98 explicit Server(Instance &aInstance); 99 100 /** 101 * This method starts the DNS-SD server. 102 * 103 * @retval kErrorNone Successfully started the DNS-SD server. 104 * @retval kErrorFailed If failed to open or bind the UDP socket. 105 * 106 */ 107 Error Start(void); 108 109 /** 110 * This method stops the DNS-SD server. 111 * 112 */ 113 void Stop(void); 114 115 /** 116 * This method sets DNS-SD query callbacks. 117 * 118 * @param[in] aSubscribe A pointer to the callback function to subscribe a service or service instance. 119 * @param[in] aUnsubscribe A pointer to the callback function to unsubscribe a service or service instance. 120 * @param[in] aContext A pointer to the application-specific context. 121 * 122 */ 123 void SetQueryCallbacks(otDnssdQuerySubscribeCallback aSubscribe, 124 otDnssdQueryUnsubscribeCallback aUnsubscribe, 125 void * aContext); 126 127 /** 128 * This method notifies a discovered service instance. 129 * 130 * @param[in] aServiceFullName The null-terminated full service name. 131 * @param[in] aInstanceInfo A reference to the discovered service instance information. 132 * 133 */ 134 void HandleDiscoveredServiceInstance(const char *aServiceFullName, const otDnssdServiceInstanceInfo &aInstanceInfo); 135 136 /** 137 * This method notifies a discovered host. 138 * 139 * @param[in] aHostFullName The null-terminated full host name. 140 * @param[in] aHostInfo A reference to the discovered host information. 141 * 142 */ 143 void HandleDiscoveredHost(const char *aHostFullName, const otDnssdHostInfo &aHostInfo); 144 145 /** 146 * This method acquires the next query in the server. 147 * 148 * @param[in] aQuery The query pointer. Pass `nullptr` to get the first query. 149 * 150 * @returns A pointer to the query or `nullptr` if no more queries. 151 * 152 */ 153 const otDnssdQuery *GetNextQuery(const otDnssdQuery *aQuery) const; 154 155 /** 156 * This method acquires the DNS-SD query type and name for a specific query. 157 * 158 * @param[in] aQuery The query pointer. 159 * @param[out] aName The name output buffer. 160 * 161 * @returns The DNS-SD query type. 162 * 163 */ 164 static DnsQueryType GetQueryTypeAndName(const otDnssdQuery *aQuery, char (&aName)[Name::kMaxNameSize]); 165 166 /** 167 * This method returns the counters of the DNS-SD server. 168 * 169 * @returns A reference to the `Counters` instance. 170 * 171 */ GetCounters(void) const172 const Counters &GetCounters(void) const { return mCounters; }; 173 174 private: 175 class NameCompressInfo : public Clearable<NameCompressInfo> 176 { 177 public: 178 explicit NameCompressInfo(void) = default; 179 NameCompressInfo(const char * aDomainName)180 explicit NameCompressInfo(const char *aDomainName) 181 : mDomainName(aDomainName) 182 , mDomainNameOffset(kUnknownOffset) 183 , mServiceNameOffset(kUnknownOffset) 184 , mInstanceNameOffset(kUnknownOffset) 185 , mHostNameOffset(kUnknownOffset) 186 { 187 } 188 189 static constexpr uint16_t kUnknownOffset = 0; // Unknown offset value (used when offset is not yet set). 190 GetDomainNameOffset(void) const191 uint16_t GetDomainNameOffset(void) const { return mDomainNameOffset; } 192 SetDomainNameOffset(uint16_t aOffset)193 void SetDomainNameOffset(uint16_t aOffset) { mDomainNameOffset = aOffset; } 194 GetDomainName(void) const195 const char *GetDomainName(void) const { return mDomainName; } 196 GetServiceNameOffset(const Message & aMessage,const char * aServiceName) const197 uint16_t GetServiceNameOffset(const Message &aMessage, const char *aServiceName) const 198 { 199 return MatchCompressedName(aMessage, mServiceNameOffset, aServiceName) 200 ? mServiceNameOffset 201 : static_cast<uint16_t>(kUnknownOffset); 202 }; 203 SetServiceNameOffset(uint16_t aOffset)204 void SetServiceNameOffset(uint16_t aOffset) 205 { 206 if (mServiceNameOffset == kUnknownOffset) 207 { 208 mServiceNameOffset = aOffset; 209 } 210 } 211 GetInstanceNameOffset(const Message & aMessage,const char * aName) const212 uint16_t GetInstanceNameOffset(const Message &aMessage, const char *aName) const 213 { 214 return MatchCompressedName(aMessage, mInstanceNameOffset, aName) ? mInstanceNameOffset 215 : static_cast<uint16_t>(kUnknownOffset); 216 } 217 SetInstanceNameOffset(uint16_t aOffset)218 void SetInstanceNameOffset(uint16_t aOffset) 219 { 220 if (mInstanceNameOffset == kUnknownOffset) 221 { 222 mInstanceNameOffset = aOffset; 223 } 224 } 225 GetHostNameOffset(const Message & aMessage,const char * aName) const226 uint16_t GetHostNameOffset(const Message &aMessage, const char *aName) const 227 { 228 return MatchCompressedName(aMessage, mHostNameOffset, aName) ? mHostNameOffset 229 : static_cast<uint16_t>(kUnknownOffset); 230 } 231 SetHostNameOffset(uint16_t aOffset)232 void SetHostNameOffset(uint16_t aOffset) 233 { 234 if (mHostNameOffset == kUnknownOffset) 235 { 236 mHostNameOffset = aOffset; 237 } 238 } 239 240 private: MatchCompressedName(const Message & aMessage,uint16_t aOffset,const char * aName)241 static bool MatchCompressedName(const Message &aMessage, uint16_t aOffset, const char *aName) 242 { 243 return aOffset != kUnknownOffset && Name::CompareName(aMessage, aOffset, aName) == kErrorNone; 244 } 245 246 const char *mDomainName; // The serialized domain name. 247 uint16_t mDomainNameOffset; // Offset of domain name serialization into the response message. 248 uint16_t mServiceNameOffset; // Offset of service name serialization into the response message. 249 uint16_t mInstanceNameOffset; // Offset of instance name serialization into the response message. 250 uint16_t mHostNameOffset; // Offset of host name serialization into the response message. 251 }; 252 253 static constexpr bool kBindUnspecifiedNetif = OPENTHREAD_CONFIG_DNSSD_SERVER_BIND_UNSPECIFIED_NETIF; 254 static constexpr uint8_t kProtocolLabelLength = 4; 255 static constexpr uint8_t kSubTypeLabelLength = 4; 256 static constexpr uint16_t kMaxConcurrentQueries = 32; 257 258 // This structure represents the splitting information of a full name. 259 struct NameComponentsOffsetInfo 260 { 261 static constexpr uint8_t kNotPresent = 0xff; // Indicates the component is not present. 262 NameComponentsOffsetInfoot::Dns::ServiceDiscovery::Server::NameComponentsOffsetInfo263 explicit NameComponentsOffsetInfo(void) 264 : mDomainOffset(kNotPresent) 265 , mProtocolOffset(kNotPresent) 266 , mServiceOffset(kNotPresent) 267 , mSubTypeOffset(kNotPresent) 268 , mInstanceOffset(kNotPresent) 269 { 270 } 271 IsServiceInstanceNameot::Dns::ServiceDiscovery::Server::NameComponentsOffsetInfo272 bool IsServiceInstanceName(void) const { return mInstanceOffset != kNotPresent; } 273 IsServiceNameot::Dns::ServiceDiscovery::Server::NameComponentsOffsetInfo274 bool IsServiceName(void) const { return mServiceOffset != kNotPresent && mInstanceOffset == kNotPresent; } 275 IsHostNameot::Dns::ServiceDiscovery::Server::NameComponentsOffsetInfo276 bool IsHostName(void) const { return mProtocolOffset == kNotPresent && mDomainOffset != 0; } 277 278 uint8_t mDomainOffset; // The offset to the beginning of <Domain>. 279 uint8_t mProtocolOffset; // The offset to the beginning of <Protocol> (i.e. _tcp or _udp) or `kNotPresent` if 280 // the name is not a service or instance. 281 uint8_t mServiceOffset; // The offset to the beginning of <Service> or `kNotPresent` if the name is not a 282 // service or instance. 283 uint8_t mSubTypeOffset; // The offset to the beginning of sub-type label or `kNotPresent` is not a sub-type. 284 uint8_t mInstanceOffset; // The offset to the beginning of <Instance> or `kNotPresent` if the name is not a 285 // instance. 286 }; 287 288 /** 289 * This class contains the compress information for a dns packet. 290 * 291 */ 292 class QueryTransaction : public InstanceLocatorInit 293 { 294 public: QueryTransaction(void)295 explicit QueryTransaction(void) 296 : mResponseMessage(nullptr) 297 { 298 } 299 300 void Init(const Header & aResponseHeader, 301 Message & aResponseMessage, 302 const NameCompressInfo &aCompressInfo, 303 const Ip6::MessageInfo &aMessageInfo, 304 Instance & aInstance); IsValid(void) const305 bool IsValid(void) const { return mResponseMessage != nullptr; } GetMessageInfo(void) const306 const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; } GetResponseHeader(void) const307 const Header & GetResponseHeader(void) const { return mResponseHeader; } GetResponseHeader(void)308 Header & GetResponseHeader(void) { return mResponseHeader; } GetResponseMessage(void) const309 const Message & GetResponseMessage(void) const { return *mResponseMessage; } GetResponseMessage(void)310 Message & GetResponseMessage(void) { return *mResponseMessage; } GetStartTime(void) const311 TimeMilli GetStartTime(void) const { return mStartTime; } GetNameCompressInfo(void)312 NameCompressInfo & GetNameCompressInfo(void) { return mCompressInfo; }; 313 void Finalize(Header::Response aResponseMessage, Ip6::Udp::Socket &aSocket); 314 315 Header mResponseHeader; 316 Message * mResponseMessage; 317 NameCompressInfo mCompressInfo; 318 Ip6::MessageInfo mMessageInfo; 319 TimeMilli mStartTime; 320 }; 321 322 static constexpr uint32_t kQueryTimeout = OPENTHREAD_CONFIG_DNSSD_QUERY_TIMEOUT; 323 IsRunning(void) const324 bool IsRunning(void) const { return mSocket.IsBound(); } 325 static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); 326 void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); 327 void ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage, const Ip6::MessageInfo &aMessageInfo); 328 static Header::Response AddQuestions(const Header & aRequestHeader, 329 const Message & aRequestMessage, 330 Header & aResponseHeader, 331 Message & aResponseMessage, 332 NameCompressInfo &aCompressInfo); 333 static Error AppendQuestion(const char * aName, 334 const Question & aQuestion, 335 Message & aMessage, 336 NameCompressInfo &aCompressInfo); 337 static Error AppendPtrRecord(Message & aMessage, 338 const char * aServiceName, 339 const char * aInstanceName, 340 uint32_t aTtl, 341 NameCompressInfo &aCompressInfo); 342 static Error AppendSrvRecord(Message & aMessage, 343 const char * aInstanceName, 344 const char * aHostName, 345 uint32_t aTtl, 346 uint16_t aPriority, 347 uint16_t aWeight, 348 uint16_t aPort, 349 NameCompressInfo &aCompressInfo); 350 static Error AppendTxtRecord(Message & aMessage, 351 const char * aInstanceName, 352 const void * aTxtData, 353 uint16_t aTxtLength, 354 uint32_t aTtl, 355 NameCompressInfo &aCompressInfo); 356 static Error AppendAaaaRecord(Message & aMessage, 357 const char * aHostName, 358 const Ip6::Address &aAddress, 359 uint32_t aTtl, 360 NameCompressInfo & aCompressInfo); 361 static Error AppendServiceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo); 362 static Error AppendInstanceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo); 363 static Error AppendHostName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo); 364 static void IncResourceRecordCount(Header &aHeader, bool aAdditional); 365 static Error FindNameComponents(const char *aName, const char *aDomain, NameComponentsOffsetInfo &aInfo); 366 static Error FindPreviousLabel(const char *aName, uint8_t &aStart, uint8_t &aStop); 367 void SendResponse(Header aHeader, 368 Header::Response aResponseCode, 369 Message & aMessage, 370 const Ip6::MessageInfo &aMessageInfo, 371 Ip6::Udp::Socket & aSocket); 372 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE 373 Header::Response ResolveBySrp(Header & aResponseHeader, 374 Message & aResponseMessage, 375 Server::NameCompressInfo &aCompressInfo); 376 Header::Response ResolveQuestionBySrp(const char * aName, 377 const Question & aQuestion, 378 Header & aResponseHeader, 379 Message & aResponseMessage, 380 NameCompressInfo &aCompressInfo, 381 bool aAdditional); 382 const Srp::Server::Host * GetNextSrpHost(const Srp::Server::Host *aHost); 383 static const Srp::Server::Service *GetNextSrpService(const Srp::Server::Host & aHost, 384 const Srp::Server::Service *aService); 385 #endif 386 387 Error ResolveByQueryCallbacks(Header & aResponseHeader, 388 Message & aResponseMessage, 389 NameCompressInfo & aCompressInfo, 390 const Ip6::MessageInfo &aMessageInfo); 391 QueryTransaction *NewQuery(const Header & aResponseHeader, 392 Message & aResponseMessage, 393 const NameCompressInfo &aCompressInfo, 394 const Ip6::MessageInfo &aMessageInfo); 395 static bool CanAnswerQuery(const QueryTransaction & aQuery, 396 const char * aServiceFullName, 397 const otDnssdServiceInstanceInfo &aInstanceInfo); 398 void AnswerQuery(QueryTransaction & aQuery, 399 const char * aServiceFullName, 400 const otDnssdServiceInstanceInfo &aInstanceInfo); 401 static bool CanAnswerQuery(const Server::QueryTransaction &aQuery, const char *aHostFullName); 402 void AnswerQuery(QueryTransaction &aQuery, const char *aHostFullName, const otDnssdHostInfo &aHostInfo); 403 void FinalizeQuery(QueryTransaction &aQuery, Header::Response aResponseCode); 404 static DnsQueryType GetQueryTypeAndName(const Header & aHeader, 405 const Message &aMessage, 406 char (&aName)[Name::kMaxNameSize]); 407 static bool HasQuestion(const Header &aHeader, const Message &aMessage, const char *aName, uint16_t aQuestionType); 408 static void HandleTimer(Timer &aTimer); 409 void HandleTimer(void); 410 void ResetTimer(void); 411 412 void UpdateResponseCounters(Header::Response aResponseCode); 413 414 static const char kDnssdProtocolUdp[]; 415 static const char kDnssdProtocolTcp[]; 416 static const char kDnssdSubTypeLabel[]; 417 static const char kDefaultDomainName[]; 418 Ip6::Udp::Socket mSocket; 419 420 QueryTransaction mQueryTransactions[kMaxConcurrentQueries]; 421 void * mQueryCallbackContext; 422 otDnssdQuerySubscribeCallback mQuerySubscribe; 423 otDnssdQueryUnsubscribeCallback mQueryUnsubscribe; 424 TimerMilli mTimer; 425 426 Counters mCounters; 427 }; 428 429 } // namespace ServiceDiscovery 430 } // namespace Dns 431 432 DefineMapEnum(otDnssdQueryType, Dns::ServiceDiscovery::Server::DnsQueryType); 433 434 } // namespace ot 435 436 #endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE 437 438 #endif // DNS_SERVER_HPP_ 439