• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *    Copyright (c) 2017, 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 /**
30  * @file
31  *   This file implements mDNS publisher based on avahi.
32  */
33 
34 #define OTBR_LOG_TAG "MDNS"
35 
36 #include "mdns/mdns_avahi.hpp"
37 
38 #include <algorithm>
39 
40 #include <avahi-client/client.h>
41 #include <avahi-common/alternative.h>
42 #include <avahi-common/error.h>
43 #include <avahi-common/malloc.h>
44 #include <avahi-common/timeval.h>
45 #include <errno.h>
46 #include <inttypes.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <sys/socket.h>
51 
52 #include "common/code_utils.hpp"
53 #include "common/logging.hpp"
54 #include "common/time.hpp"
55 
56 namespace otbr {
57 namespace Mdns {
58 
59 class AvahiPoller;
60 
61 } // namespace Mdns
62 } // namespace otbr
63 
64 struct AvahiWatch
65 {
66     typedef otbr::Mdns::AvahiPoller AvahiPoller;
67 
68     int                mFd;           ///< The file descriptor to watch.
69     AvahiWatchEvent    mEvents;       ///< The interested events.
70     int                mHappened;     ///< The events happened.
71     AvahiWatchCallback mCallback;     ///< The function to be called to report events happened on `mFd`.
72     void              *mContext;      ///< A pointer to application-specific context to use with `mCallback`.
73     bool               mShouldReport; ///< Whether or not we need to report events (invoking callback).
74     AvahiPoller       &mPoller;       ///< The poller owning this watch.
75 
76     /**
77      * The constructor to initialize an Avahi watch.
78      *
79      * @param[in] aFd        The file descriptor to watch.
80      * @param[in] aEvents    The events to watch.
81      * @param[in] aCallback  The function to be called when events happened on this file descriptor.
82      * @param[in] aContext   A pointer to application-specific context.
83      * @param[in] aPoller    The AvahiPoller this watcher belongs to.
84      *
85      */
AvahiWatchAvahiWatch86     AvahiWatch(int aFd, AvahiWatchEvent aEvents, AvahiWatchCallback aCallback, void *aContext, AvahiPoller &aPoller)
87         : mFd(aFd)
88         , mEvents(aEvents)
89         , mCallback(aCallback)
90         , mContext(aContext)
91         , mShouldReport(false)
92         , mPoller(aPoller)
93     {
94     }
95 };
96 
97 /**
98  * This structure implements the AvahiTimeout.
99  *
100  */
101 struct AvahiTimeout
102 {
103     typedef otbr::Mdns::AvahiPoller AvahiPoller;
104 
105     otbr::Timepoint      mTimeout;      ///< Absolute time when this timer timeout.
106     AvahiTimeoutCallback mCallback;     ///< The function to be called when timeout.
107     void                *mContext;      ///< The pointer to application-specific context.
108     bool                 mShouldReport; ///< Whether or not timeout occurred and need to reported (invoking callback).
109     AvahiPoller         &mPoller;       ///< The poller created this timer.
110 
111     /**
112      * The constructor to initialize an AvahiTimeout.
113      *
114      * @param[in] aTimeout   A pointer to the time after which the callback should be called.
115      * @param[in] aCallback  The function to be called after timeout.
116      * @param[in] aContext   A pointer to application-specific context.
117      * @param[in] aPoller    The AvahiPoller this timeout belongs to.
118      *
119      */
AvahiTimeoutAvahiTimeout120     AvahiTimeout(const struct timeval *aTimeout, AvahiTimeoutCallback aCallback, void *aContext, AvahiPoller &aPoller)
121         : mCallback(aCallback)
122         , mContext(aContext)
123         , mShouldReport(false)
124         , mPoller(aPoller)
125     {
126         if (aTimeout)
127         {
128             mTimeout = otbr::Clock::now() + otbr::FromTimeval<otbr::Microseconds>(*aTimeout);
129         }
130         else
131         {
132             mTimeout = otbr::Timepoint::min();
133         }
134     }
135 };
136 
137 namespace otbr {
138 
139 namespace Mdns {
140 
DnsErrorToOtbrError(int aAvahiError)141 static otbrError DnsErrorToOtbrError(int aAvahiError)
142 {
143     otbrError error;
144 
145     switch (aAvahiError)
146     {
147     case AVAHI_OK:
148     case AVAHI_ERR_INVALID_ADDRESS:
149         error = OTBR_ERROR_NONE;
150         break;
151 
152     case AVAHI_ERR_NOT_FOUND:
153         error = OTBR_ERROR_NOT_FOUND;
154         break;
155 
156     case AVAHI_ERR_INVALID_ARGUMENT:
157         error = OTBR_ERROR_INVALID_ARGS;
158         break;
159 
160     case AVAHI_ERR_COLLISION:
161         error = OTBR_ERROR_DUPLICATED;
162         break;
163 
164     case AVAHI_ERR_DNS_NOTIMP:
165     case AVAHI_ERR_NOT_SUPPORTED:
166         error = OTBR_ERROR_NOT_IMPLEMENTED;
167         break;
168 
169     default:
170         error = OTBR_ERROR_MDNS;
171         break;
172     }
173 
174     return error;
175 }
176 
177 class AvahiPoller : public MainloopProcessor
178 {
179 public:
180     AvahiPoller(void);
181 
182     // Implementation of MainloopProcessor.
183 
184     void Update(MainloopContext &aMainloop) override;
185     void Process(const MainloopContext &aMainloop) override;
186 
GetAvahiPoll(void) const187     const AvahiPoll *GetAvahiPoll(void) const { return &mAvahiPoll; }
188 
189 private:
190     typedef std::vector<AvahiWatch *>   Watches;
191     typedef std::vector<AvahiTimeout *> Timers;
192 
193     static AvahiWatch     *WatchNew(const struct AvahiPoll *aPoll,
194                                     int                     aFd,
195                                     AvahiWatchEvent         aEvent,
196                                     AvahiWatchCallback      aCallback,
197                                     void                   *aContext);
198     AvahiWatch            *WatchNew(int aFd, AvahiWatchEvent aEvent, AvahiWatchCallback aCallback, void *aContext);
199     static void            WatchUpdate(AvahiWatch *aWatch, AvahiWatchEvent aEvent);
200     static AvahiWatchEvent WatchGetEvents(AvahiWatch *aWatch);
201     static void            WatchFree(AvahiWatch *aWatch);
202     void                   WatchFree(AvahiWatch &aWatch);
203     static AvahiTimeout   *TimeoutNew(const AvahiPoll      *aPoll,
204                                       const struct timeval *aTimeout,
205                                       AvahiTimeoutCallback  aCallback,
206                                       void                 *aContext);
207     AvahiTimeout          *TimeoutNew(const struct timeval *aTimeout, AvahiTimeoutCallback aCallback, void *aContext);
208     static void            TimeoutUpdate(AvahiTimeout *aTimer, const struct timeval *aTimeout);
209     static void            TimeoutFree(AvahiTimeout *aTimer);
210     void                   TimeoutFree(AvahiTimeout &aTimer);
211 
212     Watches   mWatches;
213     Timers    mTimers;
214     AvahiPoll mAvahiPoll;
215 };
216 
AvahiPoller(void)217 AvahiPoller::AvahiPoller(void)
218 {
219     mAvahiPoll.userdata         = this;
220     mAvahiPoll.watch_new        = WatchNew;
221     mAvahiPoll.watch_update     = WatchUpdate;
222     mAvahiPoll.watch_get_events = WatchGetEvents;
223     mAvahiPoll.watch_free       = WatchFree;
224 
225     mAvahiPoll.timeout_new    = TimeoutNew;
226     mAvahiPoll.timeout_update = TimeoutUpdate;
227     mAvahiPoll.timeout_free   = TimeoutFree;
228 }
229 
WatchNew(const struct AvahiPoll * aPoll,int aFd,AvahiWatchEvent aEvent,AvahiWatchCallback aCallback,void * aContext)230 AvahiWatch *AvahiPoller::WatchNew(const struct AvahiPoll *aPoll,
231                                   int                     aFd,
232                                   AvahiWatchEvent         aEvent,
233                                   AvahiWatchCallback      aCallback,
234                                   void                   *aContext)
235 {
236     return reinterpret_cast<AvahiPoller *>(aPoll->userdata)->WatchNew(aFd, aEvent, aCallback, aContext);
237 }
238 
WatchNew(int aFd,AvahiWatchEvent aEvent,AvahiWatchCallback aCallback,void * aContext)239 AvahiWatch *AvahiPoller::WatchNew(int aFd, AvahiWatchEvent aEvent, AvahiWatchCallback aCallback, void *aContext)
240 {
241     assert(aEvent && aCallback && aFd >= 0);
242 
243     mWatches.push_back(new AvahiWatch(aFd, aEvent, aCallback, aContext, *this));
244 
245     return mWatches.back();
246 }
247 
WatchUpdate(AvahiWatch * aWatch,AvahiWatchEvent aEvent)248 void AvahiPoller::WatchUpdate(AvahiWatch *aWatch, AvahiWatchEvent aEvent)
249 {
250     aWatch->mEvents = aEvent;
251 }
252 
WatchGetEvents(AvahiWatch * aWatch)253 AvahiWatchEvent AvahiPoller::WatchGetEvents(AvahiWatch *aWatch)
254 {
255     return static_cast<AvahiWatchEvent>(aWatch->mHappened);
256 }
257 
WatchFree(AvahiWatch * aWatch)258 void AvahiPoller::WatchFree(AvahiWatch *aWatch)
259 {
260     aWatch->mPoller.WatchFree(*aWatch);
261 }
262 
WatchFree(AvahiWatch & aWatch)263 void AvahiPoller::WatchFree(AvahiWatch &aWatch)
264 {
265     for (Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it)
266     {
267         if (*it == &aWatch)
268         {
269             mWatches.erase(it);
270             delete &aWatch;
271             break;
272         }
273     }
274 }
275 
TimeoutNew(const AvahiPoll * aPoll,const struct timeval * aTimeout,AvahiTimeoutCallback aCallback,void * aContext)276 AvahiTimeout *AvahiPoller::TimeoutNew(const AvahiPoll      *aPoll,
277                                       const struct timeval *aTimeout,
278                                       AvahiTimeoutCallback  aCallback,
279                                       void                 *aContext)
280 {
281     assert(aPoll && aCallback);
282     return static_cast<AvahiPoller *>(aPoll->userdata)->TimeoutNew(aTimeout, aCallback, aContext);
283 }
284 
TimeoutNew(const struct timeval * aTimeout,AvahiTimeoutCallback aCallback,void * aContext)285 AvahiTimeout *AvahiPoller::TimeoutNew(const struct timeval *aTimeout, AvahiTimeoutCallback aCallback, void *aContext)
286 {
287     mTimers.push_back(new AvahiTimeout(aTimeout, aCallback, aContext, *this));
288     return mTimers.back();
289 }
290 
TimeoutUpdate(AvahiTimeout * aTimer,const struct timeval * aTimeout)291 void AvahiPoller::TimeoutUpdate(AvahiTimeout *aTimer, const struct timeval *aTimeout)
292 {
293     if (aTimeout == nullptr)
294     {
295         aTimer->mTimeout = Timepoint::min();
296     }
297     else
298     {
299         aTimer->mTimeout = Clock::now() + FromTimeval<Microseconds>(*aTimeout);
300     }
301 }
302 
TimeoutFree(AvahiTimeout * aTimer)303 void AvahiPoller::TimeoutFree(AvahiTimeout *aTimer)
304 {
305     aTimer->mPoller.TimeoutFree(*aTimer);
306 }
307 
TimeoutFree(AvahiTimeout & aTimer)308 void AvahiPoller::TimeoutFree(AvahiTimeout &aTimer)
309 {
310     for (Timers::iterator it = mTimers.begin(); it != mTimers.end(); ++it)
311     {
312         if (*it == &aTimer)
313         {
314             mTimers.erase(it);
315             delete &aTimer;
316             break;
317         }
318     }
319 }
320 
Update(MainloopContext & aMainloop)321 void AvahiPoller::Update(MainloopContext &aMainloop)
322 {
323     Timepoint now = Clock::now();
324 
325     for (AvahiWatch *watch : mWatches)
326     {
327         int             fd     = watch->mFd;
328         AvahiWatchEvent events = watch->mEvents;
329 
330         if (AVAHI_WATCH_IN & events)
331         {
332             FD_SET(fd, &aMainloop.mReadFdSet);
333         }
334 
335         if (AVAHI_WATCH_OUT & events)
336         {
337             FD_SET(fd, &aMainloop.mWriteFdSet);
338         }
339 
340         if (AVAHI_WATCH_ERR & events)
341         {
342             FD_SET(fd, &aMainloop.mErrorFdSet);
343         }
344 
345         if (AVAHI_WATCH_HUP & events)
346         {
347             // TODO what do with this event type?
348         }
349 
350         aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, fd);
351 
352         watch->mHappened = 0;
353     }
354 
355     for (AvahiTimeout *timer : mTimers)
356     {
357         Timepoint timeout = timer->mTimeout;
358 
359         if (timeout == Timepoint::min())
360         {
361             continue;
362         }
363 
364         if (timeout <= now)
365         {
366             aMainloop.mTimeout = ToTimeval(Microseconds::zero());
367             break;
368         }
369         else
370         {
371             auto delay = std::chrono::duration_cast<Microseconds>(timeout - now);
372 
373             if (delay < FromTimeval<Microseconds>(aMainloop.mTimeout))
374             {
375                 aMainloop.mTimeout = ToTimeval(delay);
376             }
377         }
378     }
379 }
380 
Process(const MainloopContext & aMainloop)381 void AvahiPoller::Process(const MainloopContext &aMainloop)
382 {
383     Timepoint now          = Clock::now();
384     bool      shouldReport = false;
385 
386     for (AvahiWatch *watch : mWatches)
387     {
388         int             fd     = watch->mFd;
389         AvahiWatchEvent events = watch->mEvents;
390 
391         watch->mHappened = 0;
392 
393         if ((AVAHI_WATCH_IN & events) && FD_ISSET(fd, &aMainloop.mReadFdSet))
394         {
395             watch->mHappened |= AVAHI_WATCH_IN;
396         }
397 
398         if ((AVAHI_WATCH_OUT & events) && FD_ISSET(fd, &aMainloop.mWriteFdSet))
399         {
400             watch->mHappened |= AVAHI_WATCH_OUT;
401         }
402 
403         if ((AVAHI_WATCH_ERR & events) && FD_ISSET(fd, &aMainloop.mErrorFdSet))
404         {
405             watch->mHappened |= AVAHI_WATCH_ERR;
406         }
407 
408         if (watch->mHappened != 0)
409         {
410             watch->mShouldReport = true;
411             shouldReport         = true;
412         }
413     }
414 
415     // When we invoke the callback for an `AvahiWatch` or `AvahiTimeout`,
416     // the Avahi module can call any of `mAvahiPoll` APIs we provided to
417     // it. For example, it can update or free any of `AvahiWatch/Timeout`
418     // entries, which in turn, modifies our `mWatches` or `mTimers` list.
419     // So, before invoking the callback, we update the entry's state and
420     // then restart the iteration over the `mWacthes` list to find the
421     // next entry to report, as the list may have changed.
422 
423     while (shouldReport)
424     {
425         shouldReport = false;
426 
427         for (AvahiWatch *watch : mWatches)
428         {
429             if (watch->mShouldReport)
430             {
431                 shouldReport         = true;
432                 watch->mShouldReport = false;
433                 watch->mCallback(watch, watch->mFd, WatchGetEvents(watch), watch->mContext);
434 
435                 break;
436             }
437         }
438     }
439 
440     for (AvahiTimeout *timer : mTimers)
441     {
442         if (timer->mTimeout == Timepoint::min())
443         {
444             continue;
445         }
446 
447         if (timer->mTimeout <= now)
448         {
449             timer->mShouldReport = true;
450             shouldReport         = true;
451         }
452     }
453 
454     while (shouldReport)
455     {
456         shouldReport = false;
457 
458         for (AvahiTimeout *timer : mTimers)
459         {
460             if (timer->mShouldReport)
461             {
462                 shouldReport         = true;
463                 timer->mShouldReport = false;
464                 timer->mCallback(timer, timer->mContext);
465 
466                 break;
467             }
468         }
469     }
470 }
471 
PublisherAvahi(StateCallback aStateCallback)472 PublisherAvahi::PublisherAvahi(StateCallback aStateCallback)
473     : mClient(nullptr)
474     , mPoller(MakeUnique<AvahiPoller>())
475     , mState(State::kIdle)
476     , mStateCallback(std::move(aStateCallback))
477 {
478 }
479 
~PublisherAvahi(void)480 PublisherAvahi::~PublisherAvahi(void)
481 {
482     Stop();
483 }
484 
~AvahiServiceRegistration(void)485 PublisherAvahi::AvahiServiceRegistration::~AvahiServiceRegistration(void)
486 {
487     ReleaseGroup(mEntryGroup);
488 }
489 
~AvahiHostRegistration(void)490 PublisherAvahi::AvahiHostRegistration::~AvahiHostRegistration(void)
491 {
492     ReleaseGroup(mEntryGroup);
493 }
494 
~AvahiKeyRegistration(void)495 PublisherAvahi::AvahiKeyRegistration::~AvahiKeyRegistration(void)
496 {
497     ReleaseGroup(mEntryGroup);
498 }
499 
Start(void)500 otbrError PublisherAvahi::Start(void)
501 {
502     otbrError error      = OTBR_ERROR_NONE;
503     int       avahiError = AVAHI_OK;
504 
505     assert(mClient == nullptr);
506 
507     mClient = avahi_client_new(mPoller->GetAvahiPoll(), AVAHI_CLIENT_NO_FAIL, HandleClientState, this, &avahiError);
508 
509     if (avahiError != AVAHI_OK)
510     {
511         otbrLogErr("Failed to create avahi client: %s!", avahi_strerror(avahiError));
512         error = OTBR_ERROR_MDNS;
513     }
514 
515     return error;
516 }
517 
IsStarted(void) const518 bool PublisherAvahi::IsStarted(void) const
519 {
520     return mClient != nullptr;
521 }
522 
Stop(void)523 void PublisherAvahi::Stop(void)
524 {
525     mServiceRegistrations.clear();
526     mHostRegistrations.clear();
527 
528     mSubscribedServices.clear();
529     mSubscribedHosts.clear();
530 
531     if (mClient)
532     {
533         avahi_client_free(mClient);
534         mClient = nullptr;
535     }
536 
537     mState = Mdns::Publisher::State::kIdle;
538 }
539 
HandleClientState(AvahiClient * aClient,AvahiClientState aState,void * aContext)540 void PublisherAvahi::HandleClientState(AvahiClient *aClient, AvahiClientState aState, void *aContext)
541 {
542     static_cast<PublisherAvahi *>(aContext)->HandleClientState(aClient, aState);
543 }
544 
HandleGroupState(AvahiEntryGroup * aGroup,AvahiEntryGroupState aState,void * aContext)545 void PublisherAvahi::HandleGroupState(AvahiEntryGroup *aGroup, AvahiEntryGroupState aState, void *aContext)
546 {
547     static_cast<PublisherAvahi *>(aContext)->HandleGroupState(aGroup, aState);
548 }
549 
HandleGroupState(AvahiEntryGroup * aGroup,AvahiEntryGroupState aState)550 void PublisherAvahi::HandleGroupState(AvahiEntryGroup *aGroup, AvahiEntryGroupState aState)
551 {
552     switch (aState)
553     {
554     case AVAHI_ENTRY_GROUP_ESTABLISHED:
555         otbrLogInfo("Avahi group (@%p) is established", aGroup);
556         CallHostOrServiceCallback(aGroup, OTBR_ERROR_NONE);
557         break;
558 
559     case AVAHI_ENTRY_GROUP_COLLISION:
560         otbrLogInfo("Avahi group (@%p) name conflicted", aGroup);
561         CallHostOrServiceCallback(aGroup, OTBR_ERROR_DUPLICATED);
562         break;
563 
564     case AVAHI_ENTRY_GROUP_FAILURE:
565         otbrLogErr("Avahi group (@%p) failed: %s!", aGroup,
566                    avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(aGroup))));
567         CallHostOrServiceCallback(aGroup, OTBR_ERROR_MDNS);
568         break;
569 
570     case AVAHI_ENTRY_GROUP_UNCOMMITED:
571     case AVAHI_ENTRY_GROUP_REGISTERING:
572         break;
573     }
574 }
575 
CallHostOrServiceCallback(AvahiEntryGroup * aGroup,otbrError aError)576 void PublisherAvahi::CallHostOrServiceCallback(AvahiEntryGroup *aGroup, otbrError aError)
577 {
578     ServiceRegistration *serviceReg;
579     HostRegistration    *hostReg;
580     KeyRegistration     *keyReg;
581 
582     if ((serviceReg = FindServiceRegistration(aGroup)) != nullptr)
583     {
584         if (aError == OTBR_ERROR_NONE)
585         {
586             serviceReg->Complete(aError);
587         }
588         else
589         {
590             RemoveServiceRegistration(serviceReg->mName, serviceReg->mType, aError);
591         }
592     }
593     else if ((hostReg = FindHostRegistration(aGroup)) != nullptr)
594     {
595         if (aError == OTBR_ERROR_NONE)
596         {
597             hostReg->Complete(aError);
598         }
599         else
600         {
601             RemoveHostRegistration(hostReg->mName, aError);
602         }
603     }
604     else if ((keyReg = FindKeyRegistration(aGroup)) != nullptr)
605     {
606         if (aError == OTBR_ERROR_NONE)
607         {
608             keyReg->Complete(aError);
609         }
610         else
611         {
612             RemoveKeyRegistration(keyReg->mName, aError);
613         }
614     }
615     else
616     {
617         otbrLogWarning("No registered service or host matches avahi group @%p", aGroup);
618     }
619 }
620 
CreateGroup(AvahiClient * aClient)621 AvahiEntryGroup *PublisherAvahi::CreateGroup(AvahiClient *aClient)
622 {
623     AvahiEntryGroup *group = avahi_entry_group_new(aClient, HandleGroupState, this);
624 
625     if (group == nullptr)
626     {
627         otbrLogErr("Failed to create entry avahi group: %s", avahi_strerror(avahi_client_errno(aClient)));
628     }
629 
630     return group;
631 }
632 
ReleaseGroup(AvahiEntryGroup * aGroup)633 void PublisherAvahi::ReleaseGroup(AvahiEntryGroup *aGroup)
634 {
635     int error;
636 
637     otbrLogInfo("Releasing avahi entry group @%p", aGroup);
638 
639     error = avahi_entry_group_reset(aGroup);
640 
641     if (error != 0)
642     {
643         otbrLogErr("Failed to reset entry group for avahi error: %s", avahi_strerror(error));
644     }
645 
646     error = avahi_entry_group_free(aGroup);
647     if (error != 0)
648     {
649         otbrLogErr("Failed to free entry group for avahi error: %s", avahi_strerror(error));
650     }
651 }
652 
HandleClientState(AvahiClient * aClient,AvahiClientState aState)653 void PublisherAvahi::HandleClientState(AvahiClient *aClient, AvahiClientState aState)
654 {
655     otbrLogInfo("Avahi client state changed to %d", aState);
656 
657     switch (aState)
658     {
659     case AVAHI_CLIENT_S_RUNNING:
660         // The server has startup successfully and registered its host
661         // name on the network, so it's time to create our services.
662         otbrLogInfo("Avahi client is ready");
663         mClient = aClient;
664         mState  = State::kReady;
665         mStateCallback(mState);
666         break;
667 
668     case AVAHI_CLIENT_FAILURE:
669         otbrLogErr("Avahi client failed to start: %s", avahi_strerror(avahi_client_errno(aClient)));
670         mState = State::kIdle;
671         mStateCallback(mState);
672         Stop();
673         Start();
674         break;
675 
676     case AVAHI_CLIENT_S_COLLISION:
677         // Let's drop our registered services. When the server is back
678         // in AVAHI_SERVER_RUNNING state we will register them again
679         // with the new host name.
680         otbrLogErr("Avahi client collision detected: %s", avahi_strerror(avahi_client_errno(aClient)));
681 
682         // fall through
683 
684     case AVAHI_CLIENT_S_REGISTERING:
685         // The server records are now being established. This might be
686         // caused by a host name change. We need to wait for our own
687         // records to register until the host name is properly established.
688         mServiceRegistrations.clear();
689         mHostRegistrations.clear();
690         break;
691 
692     case AVAHI_CLIENT_CONNECTING:
693         otbrLogInfo("Avahi client is connecting to the server");
694         break;
695     }
696 }
697 
PublishServiceImpl(const std::string & aHostName,const std::string & aName,const std::string & aType,const SubTypeList & aSubTypeList,uint16_t aPort,const TxtData & aTxtData,ResultCallback && aCallback)698 otbrError PublisherAvahi::PublishServiceImpl(const std::string &aHostName,
699                                              const std::string &aName,
700                                              const std::string &aType,
701                                              const SubTypeList &aSubTypeList,
702                                              uint16_t           aPort,
703                                              const TxtData     &aTxtData,
704                                              ResultCallback   &&aCallback)
705 {
706     otbrError         error             = OTBR_ERROR_NONE;
707     int               avahiError        = AVAHI_OK;
708     SubTypeList       sortedSubTypeList = SortSubTypeList(aSubTypeList);
709     const std::string logHostName       = !aHostName.empty() ? aHostName : "localhost";
710     std::string       fullHostName;
711     std::string       serviceName = aName;
712     AvahiEntryGroup  *group       = nullptr;
713 
714     // Aligned with AvahiStringList
715     AvahiStringList  txtBuffer[(kMaxSizeOfTxtRecord - 1) / sizeof(AvahiStringList) + 1];
716     AvahiStringList *txtHead = nullptr;
717 
718     VerifyOrExit(mState == State::kReady, error = OTBR_ERROR_INVALID_STATE);
719     VerifyOrExit(mClient != nullptr, error = OTBR_ERROR_INVALID_STATE);
720 
721     if (!aHostName.empty())
722     {
723         fullHostName = MakeFullHostName(aHostName);
724     }
725     if (serviceName.empty())
726     {
727         serviceName = avahi_client_get_host_name(mClient);
728     }
729 
730     aCallback = HandleDuplicateServiceRegistration(aHostName, serviceName, aType, sortedSubTypeList, aPort, aTxtData,
731                                                    std::move(aCallback));
732     VerifyOrExit(!aCallback.IsNull());
733 
734     SuccessOrExit(error = TxtDataToAvahiStringList(aTxtData, txtBuffer, sizeof(txtBuffer), txtHead));
735     VerifyOrExit((group = CreateGroup(mClient)) != nullptr, error = OTBR_ERROR_MDNS);
736     avahiError = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AvahiPublishFlags{},
737                                                       serviceName.c_str(), aType.c_str(),
738                                                       /* domain */ nullptr, fullHostName.c_str(), aPort, txtHead);
739     VerifyOrExit(avahiError == AVAHI_OK);
740 
741     for (const std::string &subType : aSubTypeList)
742     {
743         otbrLogInfo("Add subtype %s for service %s.%s", subType.c_str(), serviceName.c_str(), aType.c_str());
744         std::string fullSubType = subType + "._sub." + aType;
745         avahiError              = avahi_entry_group_add_service_subtype(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
746                                                                         AvahiPublishFlags{}, serviceName.c_str(), aType.c_str(),
747                                                                         /* domain */ nullptr, fullSubType.c_str());
748         VerifyOrExit(avahiError == AVAHI_OK);
749     }
750 
751     otbrLogInfo("Commit avahi service %s.%s", serviceName.c_str(), aType.c_str());
752     avahiError = avahi_entry_group_commit(group);
753     VerifyOrExit(avahiError == AVAHI_OK);
754 
755     AddServiceRegistration(std::unique_ptr<AvahiServiceRegistration>(new AvahiServiceRegistration(
756         aHostName, serviceName, aType, sortedSubTypeList, aPort, aTxtData, std::move(aCallback), group, this)));
757 
758 exit:
759     if (avahiError != AVAHI_OK || error != OTBR_ERROR_NONE)
760     {
761         if (avahiError != AVAHI_OK)
762         {
763             error = OTBR_ERROR_MDNS;
764             otbrLogErr("Failed to publish service for avahi error: %s!", avahi_strerror(avahiError));
765         }
766 
767         if (group != nullptr)
768         {
769             ReleaseGroup(group);
770         }
771         std::move(aCallback)(error);
772     }
773     return error;
774 }
775 
UnpublishService(const std::string & aName,const std::string & aType,ResultCallback && aCallback)776 void PublisherAvahi::UnpublishService(const std::string &aName, const std::string &aType, ResultCallback &&aCallback)
777 {
778     otbrError error = OTBR_ERROR_NONE;
779 
780     VerifyOrExit(mState == Publisher::State::kReady, error = OTBR_ERROR_INVALID_STATE);
781     RemoveServiceRegistration(aName, aType, OTBR_ERROR_ABORTED);
782 
783 exit:
784     std::move(aCallback)(error);
785 }
786 
PublishHostImpl(const std::string & aName,const AddressList & aAddresses,ResultCallback && aCallback)787 otbrError PublisherAvahi::PublishHostImpl(const std::string &aName,
788                                           const AddressList &aAddresses,
789                                           ResultCallback   &&aCallback)
790 {
791     otbrError        error      = OTBR_ERROR_NONE;
792     int              avahiError = AVAHI_OK;
793     std::string      fullHostName;
794     AvahiEntryGroup *group = nullptr;
795 
796     VerifyOrExit(mState == State::kReady, error = OTBR_ERROR_INVALID_STATE);
797     VerifyOrExit(mClient != nullptr, error = OTBR_ERROR_INVALID_STATE);
798 
799     aCallback = HandleDuplicateHostRegistration(aName, aAddresses, std::move(aCallback));
800     VerifyOrExit(!aCallback.IsNull());
801     VerifyOrExit(!aAddresses.empty(), std::move(aCallback)(OTBR_ERROR_NONE));
802 
803     VerifyOrExit((group = CreateGroup(mClient)) != nullptr, error = OTBR_ERROR_MDNS);
804 
805     fullHostName = MakeFullHostName(aName);
806     for (const auto &address : aAddresses)
807     {
808         AvahiAddress avahiAddress;
809 
810         avahiAddress.proto = AVAHI_PROTO_INET6;
811         memcpy(avahiAddress.data.ipv6.address, address.m8, sizeof(address.m8));
812         avahiError = avahi_entry_group_add_address(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_REVERSE,
813                                                    fullHostName.c_str(), &avahiAddress);
814         VerifyOrExit(avahiError == AVAHI_OK);
815     }
816 
817     otbrLogInfo("Commit avahi host %s", aName.c_str());
818     avahiError = avahi_entry_group_commit(group);
819     VerifyOrExit(avahiError == AVAHI_OK);
820 
821     AddHostRegistration(std::unique_ptr<AvahiHostRegistration>(
822         new AvahiHostRegistration(aName, aAddresses, std::move(aCallback), group, this)));
823 
824 exit:
825     if (avahiError != AVAHI_OK || error != OTBR_ERROR_NONE)
826     {
827         if (avahiError != AVAHI_OK)
828         {
829             error = OTBR_ERROR_MDNS;
830             otbrLogErr("Failed to publish host for avahi error: %s!", avahi_strerror(avahiError));
831         }
832 
833         if (group != nullptr)
834         {
835             ReleaseGroup(group);
836         }
837         std::move(aCallback)(error);
838     }
839     return error;
840 }
841 
UnpublishHost(const std::string & aName,ResultCallback && aCallback)842 void PublisherAvahi::UnpublishHost(const std::string &aName, ResultCallback &&aCallback)
843 {
844     otbrError error = OTBR_ERROR_NONE;
845 
846     VerifyOrExit(mState == Publisher::State::kReady, error = OTBR_ERROR_INVALID_STATE);
847     RemoveHostRegistration(aName, OTBR_ERROR_ABORTED);
848 
849 exit:
850     std::move(aCallback)(error);
851 }
852 
PublishKeyImpl(const std::string & aName,const KeyData & aKeyData,ResultCallback && aCallback)853 otbrError PublisherAvahi::PublishKeyImpl(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback)
854 {
855     otbrError        error      = OTBR_ERROR_NONE;
856     int              avahiError = AVAHI_OK;
857     std::string      fullKeyName;
858     AvahiEntryGroup *group = nullptr;
859 
860     VerifyOrExit(mState == State::kReady, error = OTBR_ERROR_INVALID_STATE);
861     VerifyOrExit(mClient != nullptr, error = OTBR_ERROR_INVALID_STATE);
862 
863     aCallback = HandleDuplicateKeyRegistration(aName, aKeyData, std::move(aCallback));
864     VerifyOrExit(!aCallback.IsNull());
865 
866     VerifyOrExit((group = CreateGroup(mClient)) != nullptr, error = OTBR_ERROR_MDNS);
867 
868     fullKeyName = MakeFullKeyName(aName);
869 
870     avahiError = avahi_entry_group_add_record(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE,
871                                               fullKeyName.c_str(), AVAHI_DNS_CLASS_IN, kDnsKeyRecordType, kDefaultTtl,
872                                               aKeyData.data(), aKeyData.size());
873     VerifyOrExit(avahiError == AVAHI_OK);
874 
875     otbrLogInfo("Commit avahi key record for %s", aName.c_str());
876     avahiError = avahi_entry_group_commit(group);
877     VerifyOrExit(avahiError == AVAHI_OK);
878 
879     AddKeyRegistration(std::unique_ptr<AvahiKeyRegistration>(
880         new AvahiKeyRegistration(aName, aKeyData, std::move(aCallback), group, this)));
881 
882 exit:
883     if (avahiError != AVAHI_OK || error != OTBR_ERROR_NONE)
884     {
885         if (avahiError != AVAHI_OK)
886         {
887             error = OTBR_ERROR_MDNS;
888             otbrLogErr("Failed to publish key record - avahi error: %s!", avahi_strerror(avahiError));
889         }
890 
891         if (group != nullptr)
892         {
893             ReleaseGroup(group);
894         }
895         std::move(aCallback)(error);
896     }
897     return error;
898 }
899 
UnpublishKey(const std::string & aName,ResultCallback && aCallback)900 void PublisherAvahi::UnpublishKey(const std::string &aName, ResultCallback &&aCallback)
901 {
902     otbrError error = OTBR_ERROR_NONE;
903 
904     VerifyOrExit(mState == Publisher::State::kReady, error = OTBR_ERROR_INVALID_STATE);
905     RemoveKeyRegistration(aName, OTBR_ERROR_ABORTED);
906 
907 exit:
908     std::move(aCallback)(error);
909 }
910 
TxtDataToAvahiStringList(const TxtData & aTxtData,AvahiStringList * aBuffer,size_t aBufferSize,AvahiStringList * & aHead)911 otbrError PublisherAvahi::TxtDataToAvahiStringList(const TxtData    &aTxtData,
912                                                    AvahiStringList  *aBuffer,
913                                                    size_t            aBufferSize,
914                                                    AvahiStringList *&aHead)
915 {
916     otbrError        error = OTBR_ERROR_NONE;
917     size_t           used  = 0;
918     AvahiStringList *last  = nullptr;
919     AvahiStringList *curr  = aBuffer;
920     const uint8_t   *next;
921     const uint8_t   *data    = aTxtData.data();
922     const uint8_t   *dataEnd = aTxtData.data() + aTxtData.size();
923 
924     aHead = nullptr;
925 
926     while (data < dataEnd)
927     {
928         uint8_t entryLength = *data++;
929         size_t  needed      = sizeof(AvahiStringList) - sizeof(AvahiStringList::text) + entryLength;
930 
931         if (entryLength == 0)
932         {
933             continue;
934         }
935 
936         VerifyOrExit(data + entryLength <= dataEnd, error = OTBR_ERROR_PARSE);
937 
938         VerifyOrExit(used + needed <= aBufferSize, error = OTBR_ERROR_INVALID_ARGS);
939         curr->next = last;
940         last       = curr;
941 
942         memcpy(curr->text, data, entryLength);
943         curr->size = entryLength;
944 
945         data += entryLength;
946 
947         next = curr->text + curr->size;
948         curr = OTBR_ALIGNED(next, AvahiStringList *);
949         used = static_cast<size_t>(reinterpret_cast<uint8_t *>(curr) - reinterpret_cast<uint8_t *>(aBuffer));
950     }
951 
952     aHead = last;
953 
954 exit:
955     return error;
956 }
957 
FindServiceRegistration(const AvahiEntryGroup * aEntryGroup)958 Publisher::ServiceRegistration *PublisherAvahi::FindServiceRegistration(const AvahiEntryGroup *aEntryGroup)
959 {
960     ServiceRegistration *result = nullptr;
961 
962     for (const auto &kv : mServiceRegistrations)
963     {
964         const auto &serviceReg = static_cast<const AvahiServiceRegistration &>(*kv.second);
965         if (serviceReg.GetEntryGroup() == aEntryGroup)
966         {
967             result = kv.second.get();
968             break;
969         }
970     }
971 
972     return result;
973 }
974 
FindHostRegistration(const AvahiEntryGroup * aEntryGroup)975 Publisher::HostRegistration *PublisherAvahi::FindHostRegistration(const AvahiEntryGroup *aEntryGroup)
976 {
977     HostRegistration *result = nullptr;
978 
979     for (const auto &kv : mHostRegistrations)
980     {
981         const auto &hostReg = static_cast<const AvahiHostRegistration &>(*kv.second);
982         if (hostReg.GetEntryGroup() == aEntryGroup)
983         {
984             result = kv.second.get();
985             break;
986         }
987     }
988 
989     return result;
990 }
991 
FindKeyRegistration(const AvahiEntryGroup * aEntryGroup)992 Publisher::KeyRegistration *PublisherAvahi::FindKeyRegistration(const AvahiEntryGroup *aEntryGroup)
993 {
994     KeyRegistration *result = nullptr;
995 
996     for (const auto &entry : mKeyRegistrations)
997     {
998         const auto &keyReg = static_cast<const AvahiKeyRegistration &>(*entry.second);
999         if (keyReg.GetEntryGroup() == aEntryGroup)
1000         {
1001             result = entry.second.get();
1002             break;
1003         }
1004     }
1005 
1006     return result;
1007 }
1008 
SubscribeService(const std::string & aType,const std::string & aInstanceName)1009 void PublisherAvahi::SubscribeService(const std::string &aType, const std::string &aInstanceName)
1010 {
1011     auto service = MakeUnique<ServiceSubscription>(*this, aType, aInstanceName);
1012 
1013     VerifyOrExit(mState == Publisher::State::kReady);
1014     mSubscribedServices.push_back(std::move(service));
1015 
1016     otbrLogInfo("Subscribe service %s.%s (total %zu)", aInstanceName.c_str(), aType.c_str(),
1017                 mSubscribedServices.size());
1018 
1019     if (aInstanceName.empty())
1020     {
1021         mSubscribedServices.back()->Browse();
1022     }
1023     else
1024     {
1025         mSubscribedServices.back()->Resolve(AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, aInstanceName, aType);
1026     }
1027 
1028 exit:
1029     return;
1030 }
1031 
UnsubscribeService(const std::string & aType,const std::string & aInstanceName)1032 void PublisherAvahi::UnsubscribeService(const std::string &aType, const std::string &aInstanceName)
1033 {
1034     ServiceSubscriptionList::iterator it;
1035 
1036     VerifyOrExit(mState == Publisher::State::kReady);
1037     it = std::find_if(mSubscribedServices.begin(), mSubscribedServices.end(),
1038                       [&aType, &aInstanceName](const std::unique_ptr<ServiceSubscription> &aService) {
1039                           return aService->mType == aType && aService->mInstanceName == aInstanceName;
1040                       });
1041 
1042     VerifyOrExit(it != mSubscribedServices.end());
1043 
1044     {
1045         std::unique_ptr<ServiceSubscription> service = std::move(*it);
1046 
1047         mSubscribedServices.erase(it);
1048         service->Release();
1049     }
1050 
1051     otbrLogInfo("Unsubscribe service %s.%s (left %zu)", aInstanceName.c_str(), aType.c_str(),
1052                 mSubscribedServices.size());
1053 
1054 exit:
1055     return;
1056 }
1057 
OnServiceResolveFailedImpl(const std::string & aType,const std::string & aInstanceName,int32_t aErrorCode)1058 void PublisherAvahi::OnServiceResolveFailedImpl(const std::string &aType,
1059                                                 const std::string &aInstanceName,
1060                                                 int32_t            aErrorCode)
1061 {
1062     otbrLogWarning("Resolve service %s.%s failed: %s", aInstanceName.c_str(), aType.c_str(),
1063                    avahi_strerror(aErrorCode));
1064 }
1065 
OnHostResolveFailedImpl(const std::string & aHostName,int32_t aErrorCode)1066 void PublisherAvahi::OnHostResolveFailedImpl(const std::string &aHostName, int32_t aErrorCode)
1067 {
1068     otbrLogWarning("Resolve host %s failed: %s", aHostName.c_str(), avahi_strerror(aErrorCode));
1069 }
1070 
DnsErrorToOtbrError(int32_t aErrorCode)1071 otbrError PublisherAvahi::DnsErrorToOtbrError(int32_t aErrorCode)
1072 {
1073     return otbr::Mdns::DnsErrorToOtbrError(aErrorCode);
1074 }
1075 
SubscribeHost(const std::string & aHostName)1076 void PublisherAvahi::SubscribeHost(const std::string &aHostName)
1077 {
1078     auto host = MakeUnique<HostSubscription>(*this, aHostName);
1079 
1080     VerifyOrExit(mState == Publisher::State::kReady);
1081 
1082     mSubscribedHosts.push_back(std::move(host));
1083 
1084     otbrLogInfo("Subscribe host %s (total %zu)", aHostName.c_str(), mSubscribedHosts.size());
1085 
1086     mSubscribedHosts.back()->Resolve();
1087 
1088 exit:
1089     return;
1090 }
1091 
UnsubscribeHost(const std::string & aHostName)1092 void PublisherAvahi::UnsubscribeHost(const std::string &aHostName)
1093 {
1094     HostSubscriptionList::iterator it;
1095 
1096     VerifyOrExit(mState == Publisher::State::kReady);
1097     it = std::find_if(
1098         mSubscribedHosts.begin(), mSubscribedHosts.end(),
1099         [&aHostName](const std::unique_ptr<HostSubscription> &aHost) { return aHost->mHostName == aHostName; });
1100 
1101     VerifyOrExit(it != mSubscribedHosts.end());
1102 
1103     {
1104         std::unique_ptr<HostSubscription> host = std::move(*it);
1105 
1106         mSubscribedHosts.erase(it);
1107         host->Release();
1108     }
1109 
1110     otbrLogInfo("Unsubscribe host %s (remaining %zu)", aHostName.c_str(), mSubscribedHosts.size());
1111 
1112 exit:
1113     return;
1114 }
1115 
Create(StateCallback aStateCallback)1116 Publisher *Publisher::Create(StateCallback aStateCallback)
1117 {
1118     return new PublisherAvahi(std::move(aStateCallback));
1119 }
1120 
Destroy(Publisher * aPublisher)1121 void Publisher::Destroy(Publisher *aPublisher)
1122 {
1123     delete static_cast<PublisherAvahi *>(aPublisher);
1124 }
1125 
Browse(void)1126 void PublisherAvahi::ServiceSubscription::Browse(void)
1127 {
1128     assert(mPublisherAvahi->mClient != nullptr);
1129 
1130     otbrLogInfo("Browse service %s", mType.c_str());
1131     mServiceBrowser =
1132         avahi_service_browser_new(mPublisherAvahi->mClient, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, mType.c_str(),
1133                                   /* domain */ nullptr, static_cast<AvahiLookupFlags>(0), HandleBrowseResult, this);
1134     if (!mServiceBrowser)
1135     {
1136         otbrLogWarning("Failed to browse service %s: %s", mType.c_str(),
1137                        avahi_strerror(avahi_client_errno(mPublisherAvahi->mClient)));
1138     }
1139 }
1140 
Release(void)1141 void PublisherAvahi::ServiceSubscription::Release(void)
1142 {
1143     std::vector<std::string> instanceNames;
1144 
1145     for (const auto &resolvers : mServiceResolvers)
1146     {
1147         instanceNames.push_back(resolvers.first);
1148     }
1149     for (const auto &name : instanceNames)
1150     {
1151         RemoveServiceResolver(name);
1152     }
1153 
1154     if (mServiceBrowser != nullptr)
1155     {
1156         avahi_service_browser_free(mServiceBrowser);
1157         mServiceBrowser = nullptr;
1158     }
1159 }
1160 
HandleBrowseResult(AvahiServiceBrowser * aServiceBrowser,AvahiIfIndex aInterfaceIndex,AvahiProtocol aProtocol,AvahiBrowserEvent aEvent,const char * aName,const char * aType,const char * aDomain,AvahiLookupResultFlags aFlags,void * aContext)1161 void PublisherAvahi::ServiceSubscription::HandleBrowseResult(AvahiServiceBrowser   *aServiceBrowser,
1162                                                              AvahiIfIndex           aInterfaceIndex,
1163                                                              AvahiProtocol          aProtocol,
1164                                                              AvahiBrowserEvent      aEvent,
1165                                                              const char            *aName,
1166                                                              const char            *aType,
1167                                                              const char            *aDomain,
1168                                                              AvahiLookupResultFlags aFlags,
1169                                                              void                  *aContext)
1170 {
1171     static_cast<PublisherAvahi::ServiceSubscription *>(aContext)->HandleBrowseResult(
1172         aServiceBrowser, aInterfaceIndex, aProtocol, aEvent, aName, aType, aDomain, aFlags);
1173 }
1174 
HandleBrowseResult(AvahiServiceBrowser * aServiceBrowser,AvahiIfIndex aInterfaceIndex,AvahiProtocol aProtocol,AvahiBrowserEvent aEvent,const char * aName,const char * aType,const char * aDomain,AvahiLookupResultFlags aFlags)1175 void PublisherAvahi::ServiceSubscription::HandleBrowseResult(AvahiServiceBrowser   *aServiceBrowser,
1176                                                              AvahiIfIndex           aInterfaceIndex,
1177                                                              AvahiProtocol          aProtocol,
1178                                                              AvahiBrowserEvent      aEvent,
1179                                                              const char            *aName,
1180                                                              const char            *aType,
1181                                                              const char            *aDomain,
1182                                                              AvahiLookupResultFlags aFlags)
1183 {
1184     OTBR_UNUSED_VARIABLE(aServiceBrowser);
1185     OTBR_UNUSED_VARIABLE(aProtocol);
1186     OTBR_UNUSED_VARIABLE(aDomain);
1187 
1188     assert(mServiceBrowser == aServiceBrowser);
1189 
1190     otbrLogInfo("Browse service reply: %s.%s proto %d inf %u event %d flags %d", aName, aType, aProtocol,
1191                 aInterfaceIndex, static_cast<int>(aEvent), static_cast<int>(aFlags));
1192 
1193     switch (aEvent)
1194     {
1195     case AVAHI_BROWSER_NEW:
1196         Resolve(aInterfaceIndex, aProtocol, aName, aType);
1197         break;
1198     case AVAHI_BROWSER_REMOVE:
1199         mPublisherAvahi->OnServiceRemoved(static_cast<uint32_t>(aInterfaceIndex), aType, aName);
1200         RemoveServiceResolver(aName);
1201         break;
1202     case AVAHI_BROWSER_CACHE_EXHAUSTED:
1203     case AVAHI_BROWSER_ALL_FOR_NOW:
1204         // do nothing
1205         break;
1206     case AVAHI_BROWSER_FAILURE:
1207         mPublisherAvahi->OnServiceResolveFailed(aType, aName, avahi_client_errno(mPublisherAvahi->mClient));
1208         break;
1209     }
1210 }
1211 
Resolve(uint32_t aInterfaceIndex,AvahiProtocol aProtocol,const std::string & aInstanceName,const std::string & aType)1212 void PublisherAvahi::ServiceSubscription::Resolve(uint32_t           aInterfaceIndex,
1213                                                   AvahiProtocol      aProtocol,
1214                                                   const std::string &aInstanceName,
1215                                                   const std::string &aType)
1216 {
1217     auto serviceResolver = MakeUnique<ServiceResolver>();
1218 
1219     mPublisherAvahi->mServiceInstanceResolutionBeginTime[std::make_pair(aInstanceName, aType)] = Clock::now();
1220 
1221     otbrLogInfo("Resolve service %s.%s inf %" PRIu32, aInstanceName.c_str(), aType.c_str(), aInterfaceIndex);
1222 
1223     serviceResolver->mType            = aType;
1224     serviceResolver->mPublisherAvahi  = this->mPublisherAvahi;
1225     serviceResolver->mServiceResolver = avahi_service_resolver_new(
1226         mPublisherAvahi->mClient, aInterfaceIndex, aProtocol, aInstanceName.c_str(), aType.c_str(),
1227         /* domain */ nullptr, AVAHI_PROTO_UNSPEC, static_cast<AvahiLookupFlags>(AVAHI_LOOKUP_NO_ADDRESS),
1228         &ServiceResolver::HandleResolveServiceResult, serviceResolver.get());
1229 
1230     if (serviceResolver->mServiceResolver != nullptr)
1231     {
1232         AddServiceResolver(aInstanceName, serviceResolver.release());
1233     }
1234     else
1235     {
1236         otbrLogErr("Failed to resolve serivce %s: %s", mType.c_str(),
1237                    avahi_strerror(avahi_client_errno(mPublisherAvahi->mClient)));
1238     }
1239 }
1240 
HandleResolveServiceResult(AvahiServiceResolver * aServiceResolver,AvahiIfIndex aInterfaceIndex,AvahiProtocol aProtocol,AvahiResolverEvent aEvent,const char * aName,const char * aType,const char * aDomain,const char * aHostName,const AvahiAddress * aAddress,uint16_t aPort,AvahiStringList * aTxt,AvahiLookupResultFlags aFlags,void * aContext)1241 void PublisherAvahi::ServiceResolver::HandleResolveServiceResult(AvahiServiceResolver  *aServiceResolver,
1242                                                                  AvahiIfIndex           aInterfaceIndex,
1243                                                                  AvahiProtocol          aProtocol,
1244                                                                  AvahiResolverEvent     aEvent,
1245                                                                  const char            *aName,
1246                                                                  const char            *aType,
1247                                                                  const char            *aDomain,
1248                                                                  const char            *aHostName,
1249                                                                  const AvahiAddress    *aAddress,
1250                                                                  uint16_t               aPort,
1251                                                                  AvahiStringList       *aTxt,
1252                                                                  AvahiLookupResultFlags aFlags,
1253                                                                  void                  *aContext)
1254 {
1255     static_cast<PublisherAvahi::ServiceResolver *>(aContext)->HandleResolveServiceResult(
1256         aServiceResolver, aInterfaceIndex, aProtocol, aEvent, aName, aType, aDomain, aHostName, aAddress, aPort, aTxt,
1257         aFlags);
1258 }
1259 
HandleResolveServiceResult(AvahiServiceResolver * aServiceResolver,AvahiIfIndex aInterfaceIndex,AvahiProtocol aProtocol,AvahiResolverEvent aEvent,const char * aName,const char * aType,const char * aDomain,const char * aHostName,const AvahiAddress * aAddress,uint16_t aPort,AvahiStringList * aTxt,AvahiLookupResultFlags aFlags)1260 void PublisherAvahi::ServiceResolver::HandleResolveServiceResult(AvahiServiceResolver  *aServiceResolver,
1261                                                                  AvahiIfIndex           aInterfaceIndex,
1262                                                                  AvahiProtocol          aProtocol,
1263                                                                  AvahiResolverEvent     aEvent,
1264                                                                  const char            *aName,
1265                                                                  const char            *aType,
1266                                                                  const char            *aDomain,
1267                                                                  const char            *aHostName,
1268                                                                  const AvahiAddress    *aAddress,
1269                                                                  uint16_t               aPort,
1270                                                                  AvahiStringList       *aTxt,
1271                                                                  AvahiLookupResultFlags aFlags)
1272 {
1273     OT_UNUSED_VARIABLE(aServiceResolver);
1274     OT_UNUSED_VARIABLE(aInterfaceIndex);
1275     OT_UNUSED_VARIABLE(aProtocol);
1276     OT_UNUSED_VARIABLE(aType);
1277     OT_UNUSED_VARIABLE(aDomain);
1278     OT_UNUSED_VARIABLE(aAddress);
1279 
1280     size_t totalTxtSize = 0;
1281     bool   resolved     = false;
1282     int    avahiError   = AVAHI_OK;
1283 
1284     otbrLog(aEvent == AVAHI_RESOLVER_FOUND ? OTBR_LOG_INFO : OTBR_LOG_WARNING, OTBR_LOG_TAG,
1285             "Resolve service reply: protocol %d %s.%s.%s = host %s port %" PRIu16 " flags %d event %d", aProtocol,
1286             aName, aType, aDomain, aHostName, aPort, static_cast<int>(aFlags), static_cast<int>(aEvent));
1287 
1288     VerifyOrExit(aEvent == AVAHI_RESOLVER_FOUND, avahiError = avahi_client_errno(mPublisherAvahi->mClient));
1289     VerifyOrExit(aHostName != nullptr, avahiError = AVAHI_ERR_INVALID_HOST_NAME);
1290 
1291     mInstanceInfo.mNetifIndex = static_cast<uint32_t>(aInterfaceIndex);
1292     mInstanceInfo.mName       = aName;
1293     mInstanceInfo.mHostName   = std::string(aHostName) + ".";
1294     mInstanceInfo.mPort       = aPort;
1295 
1296     otbrLogInfo("Resolve service reply: flags=%u, host=%s", aFlags, aHostName);
1297 
1298     // TODO priority
1299     // TODO weight
1300     // TODO use a more proper TTL
1301     mInstanceInfo.mTtl = kDefaultTtl;
1302     for (auto p = aTxt; p; p = avahi_string_list_get_next(p))
1303     {
1304         totalTxtSize += avahi_string_list_get_size(p) + 1;
1305     }
1306     mInstanceInfo.mTxtData.resize(totalTxtSize);
1307     avahi_string_list_serialize(aTxt, mInstanceInfo.mTxtData.data(), totalTxtSize);
1308 
1309     // NOTE: Avahi only returns one of the host's addresses in the service resolution callback. However, the address may
1310     // be link-local so it may not be preferred from Thread's perspective. We want to go through the complete list of
1311     // addresses associated with the host and choose a routable address. Therefore, as below we will resolve the host
1312     // and go through all its addresses.
1313 
1314     resolved = true;
1315 
1316 exit:
1317     if (resolved)
1318     {
1319         // In case the callback is triggered when a service instance is updated, there may already be a record browser.
1320         // We should free it before switching to the new record browser.
1321         if (mRecordBrowser)
1322         {
1323             avahi_record_browser_free(mRecordBrowser);
1324             mRecordBrowser = nullptr;
1325             mInstanceInfo.mAddresses.clear();
1326         }
1327         // NOTE: This `ServiceResolver` object may be freed in `OnServiceResolved`.
1328         mRecordBrowser = avahi_record_browser_new(mPublisherAvahi->mClient, aInterfaceIndex, AVAHI_PROTO_UNSPEC,
1329                                                   aHostName, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA,
1330                                                   static_cast<AvahiLookupFlags>(0), HandleResolveHostResult, this);
1331         if (!mRecordBrowser)
1332         {
1333             resolved   = false;
1334             avahiError = avahi_client_errno(mPublisherAvahi->mClient);
1335         }
1336     }
1337     if (!resolved && avahiError != AVAHI_OK)
1338     {
1339         mPublisherAvahi->OnServiceResolveFailed(aType, aName, avahiError);
1340     }
1341 }
1342 
HandleResolveHostResult(AvahiRecordBrowser * aRecordBrowser,AvahiIfIndex aInterfaceIndex,AvahiProtocol aProtocol,AvahiBrowserEvent aEvent,const char * aName,uint16_t aClazz,uint16_t aType,const void * aRdata,size_t aSize,AvahiLookupResultFlags aFlags,void * aContext)1343 void PublisherAvahi::ServiceResolver::HandleResolveHostResult(AvahiRecordBrowser    *aRecordBrowser,
1344                                                               AvahiIfIndex           aInterfaceIndex,
1345                                                               AvahiProtocol          aProtocol,
1346                                                               AvahiBrowserEvent      aEvent,
1347                                                               const char            *aName,
1348                                                               uint16_t               aClazz,
1349                                                               uint16_t               aType,
1350                                                               const void            *aRdata,
1351                                                               size_t                 aSize,
1352                                                               AvahiLookupResultFlags aFlags,
1353                                                               void                  *aContext)
1354 {
1355     static_cast<PublisherAvahi::ServiceResolver *>(aContext)->HandleResolveHostResult(
1356         aRecordBrowser, aInterfaceIndex, aProtocol, aEvent, aName, aClazz, aType, aRdata, aSize, aFlags);
1357 }
1358 
HandleResolveHostResult(AvahiRecordBrowser * aRecordBrowser,AvahiIfIndex aInterfaceIndex,AvahiProtocol aProtocol,AvahiBrowserEvent aEvent,const char * aName,uint16_t aClazz,uint16_t aType,const void * aRdata,size_t aSize,AvahiLookupResultFlags aFlags)1359 void PublisherAvahi::ServiceResolver::HandleResolveHostResult(AvahiRecordBrowser    *aRecordBrowser,
1360                                                               AvahiIfIndex           aInterfaceIndex,
1361                                                               AvahiProtocol          aProtocol,
1362                                                               AvahiBrowserEvent      aEvent,
1363                                                               const char            *aName,
1364                                                               uint16_t               aClazz,
1365                                                               uint16_t               aType,
1366                                                               const void            *aRdata,
1367                                                               size_t                 aSize,
1368                                                               AvahiLookupResultFlags aFlags)
1369 {
1370     OTBR_UNUSED_VARIABLE(aRecordBrowser);
1371     OTBR_UNUSED_VARIABLE(aInterfaceIndex);
1372     OTBR_UNUSED_VARIABLE(aProtocol);
1373     OTBR_UNUSED_VARIABLE(aEvent);
1374     OTBR_UNUSED_VARIABLE(aClazz);
1375     OTBR_UNUSED_VARIABLE(aType);
1376     OTBR_UNUSED_VARIABLE(aFlags);
1377 
1378     Ip6Address address;
1379     bool       resolved   = false;
1380     int        avahiError = AVAHI_OK;
1381 
1382     otbrLog(aEvent != AVAHI_BROWSER_FAILURE ? OTBR_LOG_INFO : OTBR_LOG_WARNING, OTBR_LOG_TAG,
1383             "Resolve host reply: %s inf %d protocol %d class %" PRIu16 " type %" PRIu16 " size %zu flags %d event %d",
1384             aName, aInterfaceIndex, aProtocol, aClazz, aType, aSize, static_cast<int>(aFlags),
1385             static_cast<int>(aEvent));
1386 
1387     VerifyOrExit(aEvent == AVAHI_BROWSER_NEW || aEvent == AVAHI_BROWSER_REMOVE);
1388     VerifyOrExit(aSize == OTBR_IP6_ADDRESS_SIZE || aSize == OTBR_IP4_ADDRESS_SIZE,
1389                  otbrLogErr("Unexpected address data length: %zu", aSize), avahiError = AVAHI_ERR_INVALID_ADDRESS);
1390     VerifyOrExit(aSize == OTBR_IP6_ADDRESS_SIZE, otbrLogInfo("IPv4 address ignored"),
1391                  avahiError = AVAHI_ERR_INVALID_ADDRESS);
1392     address = Ip6Address(*static_cast<const uint8_t(*)[OTBR_IP6_ADDRESS_SIZE]>(aRdata));
1393 
1394     VerifyOrExit(!address.IsLinkLocal() && !address.IsMulticast() && !address.IsLoopback() && !address.IsUnspecified(),
1395                  avahiError = AVAHI_ERR_INVALID_ADDRESS);
1396     otbrLogInfo("Resolved host address: %s %s", aEvent == AVAHI_BROWSER_NEW ? "add" : "remove",
1397                 address.ToString().c_str());
1398     if (aEvent == AVAHI_BROWSER_NEW)
1399     {
1400         mInstanceInfo.AddAddress(address);
1401     }
1402     else
1403     {
1404         mInstanceInfo.RemoveAddress(address);
1405     }
1406     resolved = true;
1407 
1408 exit:
1409     if (resolved)
1410     {
1411         // NOTE: This `HostSubscrption` object may be freed in `OnHostResolved`.
1412         mPublisherAvahi->OnServiceResolved(mType, mInstanceInfo);
1413     }
1414     else if (avahiError != AVAHI_OK)
1415     {
1416         mPublisherAvahi->OnServiceResolveFailed(mType, mInstanceInfo.mName, avahiError);
1417     }
1418 }
1419 
AddServiceResolver(const std::string & aInstanceName,ServiceResolver * aServiceResolver)1420 void PublisherAvahi::ServiceSubscription::AddServiceResolver(const std::string &aInstanceName,
1421                                                              ServiceResolver   *aServiceResolver)
1422 {
1423     assert(aServiceResolver != nullptr);
1424     mServiceResolvers[aInstanceName].insert(aServiceResolver);
1425 
1426     otbrLogDebug("Added service resolver for instance %s", aInstanceName.c_str());
1427 }
1428 
RemoveServiceResolver(const std::string & aInstanceName)1429 void PublisherAvahi::ServiceSubscription::RemoveServiceResolver(const std::string &aInstanceName)
1430 {
1431     int numResolvers = 0;
1432 
1433     VerifyOrExit(mServiceResolvers.find(aInstanceName) != mServiceResolvers.end());
1434 
1435     numResolvers = mServiceResolvers[aInstanceName].size();
1436 
1437     for (auto resolver : mServiceResolvers[aInstanceName])
1438     {
1439         delete resolver;
1440     }
1441 
1442     mServiceResolvers.erase(aInstanceName);
1443 
1444 exit:
1445     otbrLogDebug("Removed %d service resolver for instance %s", numResolvers, aInstanceName.c_str());
1446     return;
1447 }
1448 
Release(void)1449 void PublisherAvahi::HostSubscription::Release(void)
1450 {
1451     if (mRecordBrowser != nullptr)
1452     {
1453         avahi_record_browser_free(mRecordBrowser);
1454         mRecordBrowser = nullptr;
1455     }
1456 }
1457 
Resolve(void)1458 void PublisherAvahi::HostSubscription::Resolve(void)
1459 {
1460     std::string fullHostName = MakeFullHostName(mHostName);
1461 
1462     mPublisherAvahi->mHostResolutionBeginTime[mHostName] = Clock::now();
1463 
1464     otbrLogInfo("Resolve host %s inf %d", fullHostName.c_str(), static_cast<int>(AVAHI_IF_UNSPEC));
1465     mRecordBrowser = avahi_record_browser_new(mPublisherAvahi->mClient, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
1466                                               fullHostName.c_str(), AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA,
1467                                               static_cast<AvahiLookupFlags>(0), HandleResolveResult, this);
1468     if (!mRecordBrowser)
1469     {
1470         otbrLogErr("Failed to resolve host %s: %s", fullHostName.c_str(),
1471                    avahi_strerror(avahi_client_errno(mPublisherAvahi->mClient)));
1472     }
1473 }
1474 
HandleResolveResult(AvahiRecordBrowser * aRecordBrowser,AvahiIfIndex aInterfaceIndex,AvahiProtocol aProtocol,AvahiBrowserEvent aEvent,const char * aName,uint16_t aClazz,uint16_t aType,const void * aRdata,size_t aSize,AvahiLookupResultFlags aFlags,void * aContext)1475 void PublisherAvahi::HostSubscription::HandleResolveResult(AvahiRecordBrowser    *aRecordBrowser,
1476                                                            AvahiIfIndex           aInterfaceIndex,
1477                                                            AvahiProtocol          aProtocol,
1478                                                            AvahiBrowserEvent      aEvent,
1479                                                            const char            *aName,
1480                                                            uint16_t               aClazz,
1481                                                            uint16_t               aType,
1482                                                            const void            *aRdata,
1483                                                            size_t                 aSize,
1484                                                            AvahiLookupResultFlags aFlags,
1485                                                            void                  *aContext)
1486 {
1487     static_cast<PublisherAvahi::HostSubscription *>(aContext)->HandleResolveResult(
1488         aRecordBrowser, aInterfaceIndex, aProtocol, aEvent, aName, aClazz, aType, aRdata, aSize, aFlags);
1489 }
1490 
HandleResolveResult(AvahiRecordBrowser * aRecordBrowser,AvahiIfIndex aInterfaceIndex,AvahiProtocol aProtocol,AvahiBrowserEvent aEvent,const char * aName,uint16_t aClazz,uint16_t aType,const void * aRdata,size_t aSize,AvahiLookupResultFlags aFlags)1491 void PublisherAvahi::HostSubscription::HandleResolveResult(AvahiRecordBrowser    *aRecordBrowser,
1492                                                            AvahiIfIndex           aInterfaceIndex,
1493                                                            AvahiProtocol          aProtocol,
1494                                                            AvahiBrowserEvent      aEvent,
1495                                                            const char            *aName,
1496                                                            uint16_t               aClazz,
1497                                                            uint16_t               aType,
1498                                                            const void            *aRdata,
1499                                                            size_t                 aSize,
1500                                                            AvahiLookupResultFlags aFlags)
1501 {
1502     OTBR_UNUSED_VARIABLE(aRecordBrowser);
1503     OTBR_UNUSED_VARIABLE(aProtocol);
1504     OTBR_UNUSED_VARIABLE(aEvent);
1505     OTBR_UNUSED_VARIABLE(aClazz);
1506     OTBR_UNUSED_VARIABLE(aType);
1507     OTBR_UNUSED_VARIABLE(aFlags);
1508 
1509     Ip6Address address;
1510     bool       resolved   = false;
1511     int        avahiError = AVAHI_OK;
1512 
1513     otbrLog(aEvent != AVAHI_BROWSER_FAILURE ? OTBR_LOG_INFO : OTBR_LOG_WARNING, OTBR_LOG_TAG,
1514             "Resolve host reply: %s inf %d protocol %d class %" PRIu16 " type %" PRIu16 " size %zu flags %d event %d",
1515             aName, aInterfaceIndex, aProtocol, aClazz, aType, aSize, static_cast<int>(aFlags),
1516             static_cast<int>(aEvent));
1517 
1518     VerifyOrExit(aEvent == AVAHI_BROWSER_NEW || aEvent == AVAHI_BROWSER_REMOVE);
1519     VerifyOrExit(aSize == OTBR_IP6_ADDRESS_SIZE || aSize == OTBR_IP4_ADDRESS_SIZE,
1520                  otbrLogErr("Unexpected address data length: %zu", aSize), avahiError = AVAHI_ERR_INVALID_ADDRESS);
1521     VerifyOrExit(aSize == OTBR_IP6_ADDRESS_SIZE, otbrLogInfo("IPv4 address ignored"),
1522                  avahiError = AVAHI_ERR_INVALID_ADDRESS);
1523     address = Ip6Address(*static_cast<const uint8_t(*)[OTBR_IP6_ADDRESS_SIZE]>(aRdata));
1524 
1525     VerifyOrExit(!address.IsLinkLocal() && !address.IsMulticast() && !address.IsLoopback() && !address.IsUnspecified(),
1526                  avahiError = AVAHI_ERR_INVALID_ADDRESS);
1527     otbrLogInfo("Resolved host address: %s %s", aEvent == AVAHI_BROWSER_NEW ? "add" : "remove",
1528                 address.ToString().c_str());
1529 
1530     mHostInfo.mHostName = std::string(aName) + ".";
1531     if (aEvent == AVAHI_BROWSER_NEW)
1532     {
1533         mHostInfo.AddAddress(address);
1534     }
1535     else
1536     {
1537         mHostInfo.RemoveAddress(address);
1538     }
1539     mHostInfo.mNetifIndex = static_cast<uint32_t>(aInterfaceIndex);
1540     // TODO: Use a more proper TTL
1541     mHostInfo.mTtl = kDefaultTtl;
1542     resolved       = true;
1543 
1544 exit:
1545     if (resolved)
1546     {
1547         // NOTE: This `HostSubscrption` object may be freed in `OnHostResolved`.
1548         mPublisherAvahi->OnHostResolved(mHostName, mHostInfo);
1549     }
1550     else if (avahiError != AVAHI_OK)
1551     {
1552         mPublisherAvahi->OnHostResolveFailed(mHostName, avahiError);
1553     }
1554 }
1555 
1556 } // namespace Mdns
1557 
1558 } // namespace otbr
1559