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