• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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