• 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 struct AvahiWatch
57 {
58     int                mFd;       ///< The file descriptor to watch.
59     AvahiWatchEvent    mEvents;   ///< The interested events.
60     int                mHappened; ///< The events happened.
61     AvahiWatchCallback mCallback; ///< The function to be called when interested events happened on mFd.
62     void *             mContext;  ///< A pointer to application-specific context.
63     void *             mPoller;   ///< The poller created this watch.
64 
65     /**
66      * The constructor to initialize an Avahi watch.
67      *
68      * @param[in] aFd        The file descriptor to watch.
69      * @param[in] aEvents    The events to watch.
70      * @param[in] aCallback  The function to be called when events happend on this file descriptor.
71      * @param[in] aContext   A pointer to application-specific context.
72      * @param[in] aPoller    The AvahiPoller this watcher belongs to.
73      *
74      */
AvahiWatchAvahiWatch75     AvahiWatch(int aFd, AvahiWatchEvent aEvents, AvahiWatchCallback aCallback, void *aContext, void *aPoller)
76         : mFd(aFd)
77         , mEvents(aEvents)
78         , mCallback(aCallback)
79         , mContext(aContext)
80         , mPoller(aPoller)
81     {
82     }
83 };
84 
85 /**
86  * This structure implements the AvahiTimeout.
87  *
88  */
89 struct AvahiTimeout
90 {
91     otbr::Timepoint      mTimeout;  ///< Absolute time when this timer timeout.
92     AvahiTimeoutCallback mCallback; ///< The function to be called when timeout.
93     void *               mContext;  ///< The pointer to application-specific context.
94     void *               mPoller;   ///< The poller created this timer.
95 
96     /**
97      * The constructor to initialize an AvahiTimeout.
98      *
99      * @param[in] aTimeout   A pointer to the time after which the callback should be called.
100      * @param[in] aCallback  The function to be called after timeout.
101      * @param[in] aContext   A pointer to application-specific context.
102      * @param[in] aPoller    The AvahiPoller this timeout belongs to.
103      *
104      */
AvahiTimeoutAvahiTimeout105     AvahiTimeout(const struct timeval *aTimeout, AvahiTimeoutCallback aCallback, void *aContext, void *aPoller)
106         : mCallback(aCallback)
107         , mContext(aContext)
108         , mPoller(aPoller)
109     {
110         if (aTimeout)
111         {
112             mTimeout = otbr::Clock::now() + otbr::FromTimeval<otbr::Microseconds>(*aTimeout);
113         }
114         else
115         {
116             mTimeout = otbr::Timepoint::min();
117         }
118     }
119 };
120 
121 namespace otbr {
122 
123 namespace Mdns {
124 
DnsErrorToOtbrError(int aAvahiError)125 static otbrError DnsErrorToOtbrError(int aAvahiError)
126 {
127     otbrError error;
128 
129     switch (aAvahiError)
130     {
131     case AVAHI_OK:
132     case AVAHI_ERR_INVALID_ADDRESS:
133         error = OTBR_ERROR_NONE;
134         break;
135 
136     case AVAHI_ERR_NOT_FOUND:
137         error = OTBR_ERROR_NOT_FOUND;
138         break;
139 
140     case AVAHI_ERR_INVALID_ARGUMENT:
141         error = OTBR_ERROR_INVALID_ARGS;
142         break;
143 
144     case AVAHI_ERR_COLLISION:
145         error = OTBR_ERROR_DUPLICATED;
146         break;
147 
148     case AVAHI_ERR_DNS_NOTIMP:
149     case AVAHI_ERR_NOT_SUPPORTED:
150         error = OTBR_ERROR_NOT_IMPLEMENTED;
151         break;
152 
153     default:
154         error = OTBR_ERROR_MDNS;
155         break;
156     }
157 
158     return error;
159 }
160 
161 class AvahiPoller : public MainloopProcessor
162 {
163 public:
164     AvahiPoller(void);
165 
166     // Implementation of MainloopProcessor.
167 
168     void Update(MainloopContext &aMainloop) override;
169     void Process(const MainloopContext &aMainloop) override;
170 
GetAvahiPoll(void) const171     const AvahiPoll *GetAvahiPoll(void) const { return &mAvahiPoller; }
172 
173 private:
174     typedef std::vector<AvahiWatch *>   Watches;
175     typedef std::vector<AvahiTimeout *> Timers;
176 
177     static AvahiWatch *    WatchNew(const struct AvahiPoll *aPoller,
178                                     int                     aFd,
179                                     AvahiWatchEvent         aEvent,
180                                     AvahiWatchCallback      aCallback,
181                                     void *                  aContext);
182     AvahiWatch *           WatchNew(int aFd, AvahiWatchEvent aEvent, AvahiWatchCallback aCallback, void *aContext);
183     static void            WatchUpdate(AvahiWatch *aWatch, AvahiWatchEvent aEvent);
184     static AvahiWatchEvent WatchGetEvents(AvahiWatch *aWatch);
185     static void            WatchFree(AvahiWatch *aWatch);
186     void                   WatchFree(AvahiWatch &aWatch);
187     static AvahiTimeout *  TimeoutNew(const AvahiPoll *     aPoller,
188                                       const struct timeval *aTimeout,
189                                       AvahiTimeoutCallback  aCallback,
190                                       void *                aContext);
191     AvahiTimeout *         TimeoutNew(const struct timeval *aTimeout, AvahiTimeoutCallback aCallback, void *aContext);
192     static void            TimeoutUpdate(AvahiTimeout *aTimer, const struct timeval *aTimeout);
193     static void            TimeoutFree(AvahiTimeout *aTimer);
194     void                   TimeoutFree(AvahiTimeout &aTimer);
195 
196     Watches   mWatches;
197     Timers    mTimers;
198     AvahiPoll mAvahiPoller;
199 };
200 
AvahiPoller(void)201 AvahiPoller::AvahiPoller(void)
202 {
203     mAvahiPoller.userdata         = this;
204     mAvahiPoller.watch_new        = WatchNew;
205     mAvahiPoller.watch_update     = WatchUpdate;
206     mAvahiPoller.watch_get_events = WatchGetEvents;
207     mAvahiPoller.watch_free       = WatchFree;
208 
209     mAvahiPoller.timeout_new    = TimeoutNew;
210     mAvahiPoller.timeout_update = TimeoutUpdate;
211     mAvahiPoller.timeout_free   = TimeoutFree;
212 }
213 
WatchNew(const struct AvahiPoll * aPoller,int aFd,AvahiWatchEvent aEvent,AvahiWatchCallback aCallback,void * aContext)214 AvahiWatch *AvahiPoller::WatchNew(const struct AvahiPoll *aPoller,
215                                   int                     aFd,
216                                   AvahiWatchEvent         aEvent,
217                                   AvahiWatchCallback      aCallback,
218                                   void *                  aContext)
219 {
220     return reinterpret_cast<AvahiPoller *>(aPoller->userdata)->WatchNew(aFd, aEvent, aCallback, aContext);
221 }
222 
WatchNew(int aFd,AvahiWatchEvent aEvent,AvahiWatchCallback aCallback,void * aContext)223 AvahiWatch *AvahiPoller::WatchNew(int aFd, AvahiWatchEvent aEvent, AvahiWatchCallback aCallback, void *aContext)
224 {
225     assert(aEvent && aCallback && aFd >= 0);
226 
227     mWatches.push_back(new AvahiWatch(aFd, aEvent, aCallback, aContext, this));
228 
229     return mWatches.back();
230 }
231 
WatchUpdate(AvahiWatch * aWatch,AvahiWatchEvent aEvent)232 void AvahiPoller::WatchUpdate(AvahiWatch *aWatch, AvahiWatchEvent aEvent)
233 {
234     aWatch->mEvents = aEvent;
235 }
236 
WatchGetEvents(AvahiWatch * aWatch)237 AvahiWatchEvent AvahiPoller::WatchGetEvents(AvahiWatch *aWatch)
238 {
239     return static_cast<AvahiWatchEvent>(aWatch->mHappened);
240 }
241 
WatchFree(AvahiWatch * aWatch)242 void AvahiPoller::WatchFree(AvahiWatch *aWatch)
243 {
244     reinterpret_cast<AvahiPoller *>(aWatch->mPoller)->WatchFree(*aWatch);
245 }
246 
WatchFree(AvahiWatch & aWatch)247 void AvahiPoller::WatchFree(AvahiWatch &aWatch)
248 {
249     for (Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it)
250     {
251         if (*it == &aWatch)
252         {
253             mWatches.erase(it);
254             delete &aWatch;
255             break;
256         }
257     }
258 }
259 
TimeoutNew(const AvahiPoll * aPoller,const struct timeval * aTimeout,AvahiTimeoutCallback aCallback,void * aContext)260 AvahiTimeout *AvahiPoller::TimeoutNew(const AvahiPoll *     aPoller,
261                                       const struct timeval *aTimeout,
262                                       AvahiTimeoutCallback  aCallback,
263                                       void *                aContext)
264 {
265     assert(aPoller && aCallback);
266     return static_cast<AvahiPoller *>(aPoller->userdata)->TimeoutNew(aTimeout, aCallback, aContext);
267 }
268 
TimeoutNew(const struct timeval * aTimeout,AvahiTimeoutCallback aCallback,void * aContext)269 AvahiTimeout *AvahiPoller::TimeoutNew(const struct timeval *aTimeout, AvahiTimeoutCallback aCallback, void *aContext)
270 {
271     mTimers.push_back(new AvahiTimeout(aTimeout, aCallback, aContext, this));
272     return mTimers.back();
273 }
274 
TimeoutUpdate(AvahiTimeout * aTimer,const struct timeval * aTimeout)275 void AvahiPoller::TimeoutUpdate(AvahiTimeout *aTimer, const struct timeval *aTimeout)
276 {
277     if (aTimeout == nullptr)
278     {
279         aTimer->mTimeout = Timepoint::min();
280     }
281     else
282     {
283         aTimer->mTimeout = Clock::now() + FromTimeval<Microseconds>(*aTimeout);
284     }
285 }
286 
TimeoutFree(AvahiTimeout * aTimer)287 void AvahiPoller::TimeoutFree(AvahiTimeout *aTimer)
288 {
289     static_cast<AvahiPoller *>(aTimer->mPoller)->TimeoutFree(*aTimer);
290 }
291 
TimeoutFree(AvahiTimeout & aTimer)292 void AvahiPoller::TimeoutFree(AvahiTimeout &aTimer)
293 {
294     for (Timers::iterator it = mTimers.begin(); it != mTimers.end(); ++it)
295     {
296         if (*it == &aTimer)
297         {
298             mTimers.erase(it);
299             delete &aTimer;
300             break;
301         }
302     }
303 }
304 
Update(MainloopContext & aMainloop)305 void AvahiPoller::Update(MainloopContext &aMainloop)
306 {
307     Timepoint now = Clock::now();
308 
309     for (Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it)
310     {
311         int             fd     = (*it)->mFd;
312         AvahiWatchEvent events = (*it)->mEvents;
313 
314         if (AVAHI_WATCH_IN & events)
315         {
316             FD_SET(fd, &aMainloop.mReadFdSet);
317         }
318 
319         if (AVAHI_WATCH_OUT & events)
320         {
321             FD_SET(fd, &aMainloop.mWriteFdSet);
322         }
323 
324         if (AVAHI_WATCH_ERR & events)
325         {
326             FD_SET(fd, &aMainloop.mErrorFdSet);
327         }
328 
329         if (AVAHI_WATCH_HUP & events)
330         {
331             // TODO what do with this event type?
332         }
333 
334         aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, fd);
335 
336         (*it)->mHappened = 0;
337     }
338 
339     for (Timers::iterator it = mTimers.begin(); it != mTimers.end(); ++it)
340     {
341         Timepoint timeout = (*it)->mTimeout;
342 
343         if (timeout == Timepoint::min())
344         {
345             continue;
346         }
347 
348         if (timeout <= now)
349         {
350             aMainloop.mTimeout = ToTimeval(Microseconds::zero());
351             break;
352         }
353         else
354         {
355             auto delay = std::chrono::duration_cast<Microseconds>(timeout - now);
356 
357             if (delay < FromTimeval<Microseconds>(aMainloop.mTimeout))
358             {
359                 aMainloop.mTimeout = ToTimeval(delay);
360             }
361         }
362     }
363 }
364 
Process(const MainloopContext & aMainloop)365 void AvahiPoller::Process(const MainloopContext &aMainloop)
366 {
367     Timepoint                   now = Clock::now();
368     std::vector<AvahiTimeout *> expired;
369 
370     for (Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it)
371     {
372         int             fd     = (*it)->mFd;
373         AvahiWatchEvent events = (*it)->mEvents;
374 
375         (*it)->mHappened = 0;
376 
377         if ((AVAHI_WATCH_IN & events) && FD_ISSET(fd, &aMainloop.mReadFdSet))
378         {
379             (*it)->mHappened |= AVAHI_WATCH_IN;
380         }
381 
382         if ((AVAHI_WATCH_OUT & events) && FD_ISSET(fd, &aMainloop.mWriteFdSet))
383         {
384             (*it)->mHappened |= AVAHI_WATCH_OUT;
385         }
386 
387         if ((AVAHI_WATCH_ERR & events) && FD_ISSET(fd, &aMainloop.mErrorFdSet))
388         {
389             (*it)->mHappened |= AVAHI_WATCH_ERR;
390         }
391 
392         // TODO hup events
393         if ((*it)->mHappened)
394         {
395             (*it)->mCallback(*it, (*it)->mFd, static_cast<AvahiWatchEvent>((*it)->mHappened), (*it)->mContext);
396         }
397     }
398 
399     for (Timers::iterator it = mTimers.begin(); it != mTimers.end(); ++it)
400     {
401         if ((*it)->mTimeout == Timepoint::min())
402         {
403             continue;
404         }
405 
406         if ((*it)->mTimeout <= now)
407         {
408             expired.push_back(*it);
409         }
410     }
411 
412     for (std::vector<AvahiTimeout *>::iterator it = expired.begin(); it != expired.end(); ++it)
413     {
414         AvahiTimeout *avahiTimeout = *it;
415 
416         avahiTimeout->mCallback(avahiTimeout, avahiTimeout->mContext);
417     }
418 }
419 
PublisherAvahi(StateCallback aStateCallback)420 PublisherAvahi::PublisherAvahi(StateCallback aStateCallback)
421     : mClient(nullptr)
422     , mPoller(MakeUnique<AvahiPoller>())
423     , mState(State::kIdle)
424     , mStateCallback(std::move(aStateCallback))
425 {
426 }
427 
~PublisherAvahi(void)428 PublisherAvahi::~PublisherAvahi(void)
429 {
430     Stop();
431 }
432 
~AvahiServiceRegistration(void)433 PublisherAvahi::AvahiServiceRegistration::~AvahiServiceRegistration(void)
434 {
435     ReleaseGroup(mEntryGroup);
436 }
437 
~AvahiHostRegistration(void)438 PublisherAvahi::AvahiHostRegistration::~AvahiHostRegistration(void)
439 {
440     ReleaseGroup(mEntryGroup);
441 }
442 
Start(void)443 otbrError PublisherAvahi::Start(void)
444 {
445     otbrError error      = OTBR_ERROR_NONE;
446     int       avahiError = AVAHI_OK;
447 
448     assert(mClient == nullptr);
449 
450     mClient = avahi_client_new(mPoller->GetAvahiPoll(), AVAHI_CLIENT_NO_FAIL, HandleClientState, this, &avahiError);
451 
452     if (avahiError != AVAHI_OK)
453     {
454         otbrLogErr("Failed to create avahi client: %s!", avahi_strerror(avahiError));
455         error = OTBR_ERROR_MDNS;
456     }
457 
458     return error;
459 }
460 
IsStarted(void) const461 bool PublisherAvahi::IsStarted(void) const
462 {
463     return mClient != nullptr;
464 }
465 
Stop(void)466 void PublisherAvahi::Stop(void)
467 {
468     mServiceRegistrations.clear();
469     mHostRegistrations.clear();
470 
471     mSubscribedServices.clear();
472     mSubscribedHosts.clear();
473 
474     if (mClient)
475     {
476         avahi_client_free(mClient);
477         mClient = nullptr;
478     }
479 
480     mState = Mdns::Publisher::State::kIdle;
481 }
482 
HandleClientState(AvahiClient * aClient,AvahiClientState aState,void * aContext)483 void PublisherAvahi::HandleClientState(AvahiClient *aClient, AvahiClientState aState, void *aContext)
484 {
485     static_cast<PublisherAvahi *>(aContext)->HandleClientState(aClient, aState);
486 }
487 
HandleGroupState(AvahiEntryGroup * aGroup,AvahiEntryGroupState aState,void * aContext)488 void PublisherAvahi::HandleGroupState(AvahiEntryGroup *aGroup, AvahiEntryGroupState aState, void *aContext)
489 {
490     static_cast<PublisherAvahi *>(aContext)->HandleGroupState(aGroup, aState);
491 }
492 
HandleGroupState(AvahiEntryGroup * aGroup,AvahiEntryGroupState aState)493 void PublisherAvahi::HandleGroupState(AvahiEntryGroup *aGroup, AvahiEntryGroupState aState)
494 {
495     switch (aState)
496     {
497     case AVAHI_ENTRY_GROUP_ESTABLISHED:
498         otbrLogInfo("Avahi group (@%p) is established", aGroup);
499         CallHostOrServiceCallback(aGroup, OTBR_ERROR_NONE);
500         break;
501 
502     case AVAHI_ENTRY_GROUP_COLLISION:
503         otbrLogInfo("Avahi group (@%p) name conflicted", aGroup);
504         CallHostOrServiceCallback(aGroup, OTBR_ERROR_DUPLICATED);
505         break;
506 
507     case AVAHI_ENTRY_GROUP_FAILURE:
508         otbrLogErr("Avahi group (@%p) failed: %s!", aGroup,
509                    avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(aGroup))));
510         CallHostOrServiceCallback(aGroup, OTBR_ERROR_MDNS);
511         break;
512 
513     case AVAHI_ENTRY_GROUP_UNCOMMITED:
514     case AVAHI_ENTRY_GROUP_REGISTERING:
515         break;
516 
517     default:
518         assert(false);
519         break;
520     }
521 }
522 
CallHostOrServiceCallback(AvahiEntryGroup * aGroup,otbrError aError)523 void PublisherAvahi::CallHostOrServiceCallback(AvahiEntryGroup *aGroup, otbrError aError)
524 {
525     ServiceRegistration *serviceReg;
526     HostRegistration *   hostReg;
527 
528     if ((serviceReg = FindServiceRegistration(aGroup)) != nullptr)
529     {
530         if (aError == OTBR_ERROR_NONE)
531         {
532             serviceReg->Complete(aError);
533         }
534         else
535         {
536             RemoveServiceRegistration(serviceReg->mName, serviceReg->mType, aError);
537         }
538     }
539     else if ((hostReg = FindHostRegistration(aGroup)) != nullptr)
540     {
541         if (aError == OTBR_ERROR_NONE)
542         {
543             hostReg->Complete(aError);
544         }
545         else
546         {
547             RemoveHostRegistration(hostReg->mName, aError);
548         }
549     }
550     else
551     {
552         otbrLogWarning("No registered service or host matches avahi group @%p", aGroup);
553     }
554 }
555 
CreateGroup(AvahiClient * aClient)556 AvahiEntryGroup *PublisherAvahi::CreateGroup(AvahiClient *aClient)
557 {
558     AvahiEntryGroup *group = avahi_entry_group_new(aClient, HandleGroupState, this);
559 
560     if (group == nullptr)
561     {
562         otbrLogErr("Failed to create entry avahi group: %s", avahi_strerror(avahi_client_errno(aClient)));
563     }
564 
565     return group;
566 }
567 
ReleaseGroup(AvahiEntryGroup * aGroup)568 void PublisherAvahi::ReleaseGroup(AvahiEntryGroup *aGroup)
569 {
570     int error;
571 
572     otbrLogInfo("Releasing avahi entry group @%p", aGroup);
573 
574     error = avahi_entry_group_reset(aGroup);
575 
576     if (error != 0)
577     {
578         otbrLogErr("Failed to reset entry group for avahi error: %s", avahi_strerror(error));
579     }
580 
581     error = avahi_entry_group_free(aGroup);
582     if (error != 0)
583     {
584         otbrLogErr("Failed to free entry group for avahi error: %s", avahi_strerror(error));
585     }
586 }
587 
HandleClientState(AvahiClient * aClient,AvahiClientState aState)588 void PublisherAvahi::HandleClientState(AvahiClient *aClient, AvahiClientState aState)
589 {
590     otbrLogInfo("Avahi client state changed to %d", aState);
591 
592     switch (aState)
593     {
594     case AVAHI_CLIENT_S_RUNNING:
595         // The server has startup successfully and registered its host
596         // name on the network, so it's time to create our services.
597         otbrLogInfo("Avahi client is ready");
598         mClient = aClient;
599         mState  = State::kReady;
600         mStateCallback(mState);
601         break;
602 
603     case AVAHI_CLIENT_FAILURE:
604         otbrLogErr("Avahi client failed to start: %s", avahi_strerror(avahi_client_errno(aClient)));
605         mState = State::kIdle;
606         mStateCallback(mState);
607         Stop();
608         Start();
609         break;
610 
611     case AVAHI_CLIENT_S_COLLISION:
612         // Let's drop our registered services. When the server is back
613         // in AVAHI_SERVER_RUNNING state we will register them again
614         // with the new host name.
615         otbrLogErr("Avahi client collision detected: %s", avahi_strerror(avahi_client_errno(aClient)));
616 
617         // fall through
618 
619     case AVAHI_CLIENT_S_REGISTERING:
620         // The server records are now being established. This might be
621         // caused by a host name change. We need to wait for our own
622         // records to register until the host name is properly established.
623         mServiceRegistrations.clear();
624         mHostRegistrations.clear();
625         break;
626 
627     case AVAHI_CLIENT_CONNECTING:
628         otbrLogInfo("Avahi client is connecting to the server");
629         break;
630 
631     default:
632         assert(false);
633         break;
634     }
635 }
636 
PublishServiceImpl(const std::string & aHostName,const std::string & aName,const std::string & aType,const SubTypeList & aSubTypeList,uint16_t aPort,const TxtList & aTxtList,ResultCallback && aCallback)637 void PublisherAvahi::PublishServiceImpl(const std::string &aHostName,
638                                         const std::string &aName,
639                                         const std::string &aType,
640                                         const SubTypeList &aSubTypeList,
641                                         uint16_t           aPort,
642                                         const TxtList &    aTxtList,
643                                         ResultCallback &&  aCallback)
644 {
645     otbrError         error             = OTBR_ERROR_NONE;
646     int               avahiError        = AVAHI_OK;
647     SubTypeList       sortedSubTypeList = SortSubTypeList(aSubTypeList);
648     TxtList           sortedTxtList     = SortTxtList(aTxtList);
649     const std::string logHostName       = !aHostName.empty() ? aHostName : "localhost";
650     std::string       fullHostName;
651     AvahiEntryGroup * group = nullptr;
652 
653     // Aligned with AvahiStringList
654     AvahiStringList  txtBuffer[(kMaxSizeOfTxtRecord - 1) / sizeof(AvahiStringList) + 1];
655     AvahiStringList *txtHead = nullptr;
656 
657     VerifyOrExit(mState == State::kReady, error = OTBR_ERROR_INVALID_STATE);
658     VerifyOrExit(mClient != nullptr, error = OTBR_ERROR_INVALID_STATE);
659 
660     if (!aHostName.empty())
661     {
662         fullHostName = MakeFullHostName(aHostName);
663     }
664 
665     aCallback = HandleDuplicateServiceRegistration(aHostName, aName, aType, sortedSubTypeList, aPort, sortedTxtList,
666                                                    std::move(aCallback));
667     VerifyOrExit(!aCallback.IsNull());
668 
669     SuccessOrExit(error = TxtListToAvahiStringList(aTxtList, txtBuffer, sizeof(txtBuffer), txtHead));
670     VerifyOrExit((group = CreateGroup(mClient)) != nullptr, error = OTBR_ERROR_MDNS);
671     avahiError = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AvahiPublishFlags{},
672                                                       aName.c_str(), aType.c_str(),
673                                                       /* domain */ nullptr, fullHostName.c_str(), aPort, txtHead);
674     VerifyOrExit(avahiError == AVAHI_OK);
675 
676     for (const std::string &subType : aSubTypeList)
677     {
678         otbrLogInfo("Add subtype %s for service %s.%s", subType.c_str(), aName.c_str(), aType.c_str());
679         std::string fullSubType = subType + "._sub." + aType;
680         avahiError              = avahi_entry_group_add_service_subtype(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
681                                                            AvahiPublishFlags{}, aName.c_str(), aType.c_str(),
682                                                            /* domain */ nullptr, fullSubType.c_str());
683         VerifyOrExit(avahiError == AVAHI_OK);
684     }
685 
686     otbrLogInfo("Commit avahi service %s.%s", aName.c_str(), aType.c_str());
687     avahiError = avahi_entry_group_commit(group);
688     VerifyOrExit(avahiError == AVAHI_OK);
689 
690     AddServiceRegistration(std::unique_ptr<AvahiServiceRegistration>(new AvahiServiceRegistration(
691         aHostName, aName, aType, sortedSubTypeList, aPort, sortedTxtList, std::move(aCallback), group, this)));
692 
693 exit:
694     if (avahiError != AVAHI_OK || error != OTBR_ERROR_NONE)
695     {
696         if (avahiError != AVAHI_OK)
697         {
698             error = OTBR_ERROR_MDNS;
699             otbrLogErr("Failed to publish service for avahi error: %s!", avahi_strerror(avahiError));
700         }
701 
702         if (group != nullptr)
703         {
704             ReleaseGroup(group);
705         }
706         std::move(aCallback)(error);
707     }
708 }
709 
UnpublishService(const std::string & aName,const std::string & aType,ResultCallback && aCallback)710 void PublisherAvahi::UnpublishService(const std::string &aName, const std::string &aType, ResultCallback &&aCallback)
711 {
712     otbrError error = OTBR_ERROR_NONE;
713 
714     VerifyOrExit(mState == Publisher::State::kReady, error = OTBR_ERROR_INVALID_STATE);
715     RemoveServiceRegistration(aName, aType, OTBR_ERROR_ABORTED);
716 
717 exit:
718     std::move(aCallback)(error);
719 }
720 
PublishHostImpl(const std::string & aName,const std::vector<uint8_t> & aAddress,ResultCallback && aCallback)721 void PublisherAvahi::PublishHostImpl(const std::string &         aName,
722                                      const std::vector<uint8_t> &aAddress,
723                                      ResultCallback &&           aCallback)
724 {
725     otbrError        error      = OTBR_ERROR_NONE;
726     int              avahiError = AVAHI_OK;
727     std::string      fullHostName;
728     AvahiAddress     address;
729     AvahiEntryGroup *group = nullptr;
730 
731     VerifyOrExit(mState == State::kReady, error = OTBR_ERROR_INVALID_STATE);
732     VerifyOrExit(mClient != nullptr, error = OTBR_ERROR_INVALID_STATE);
733     VerifyOrExit(aAddress.size() == sizeof(address.data.ipv6.address), error = OTBR_ERROR_INVALID_ARGS);
734 
735     aCallback = HandleDuplicateHostRegistration(aName, aAddress, std::move(aCallback));
736     VerifyOrExit(!aCallback.IsNull());
737 
738     address.proto = AVAHI_PROTO_INET6;
739     memcpy(address.data.ipv6.address, aAddress.data(), aAddress.size());
740     fullHostName = MakeFullHostName(aName);
741 
742     VerifyOrExit((group = CreateGroup(mClient)) != nullptr, error = OTBR_ERROR_MDNS);
743     avahiError = avahi_entry_group_add_address(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_REVERSE,
744                                                fullHostName.c_str(), &address);
745     VerifyOrExit(avahiError == AVAHI_OK);
746 
747     otbrLogInfo("Commit avahi host %s", aName.c_str());
748     avahiError = avahi_entry_group_commit(group);
749     VerifyOrExit(avahiError == AVAHI_OK);
750 
751     AddHostRegistration(std::unique_ptr<AvahiHostRegistration>(
752         new AvahiHostRegistration(aName, aAddress, std::move(aCallback), group, this)));
753 
754 exit:
755     if (avahiError != AVAHI_OK || error != OTBR_ERROR_NONE)
756     {
757         if (avahiError != AVAHI_OK)
758         {
759             error = OTBR_ERROR_MDNS;
760             otbrLogErr("Failed to publish host for avahi error: %s!", avahi_strerror(avahiError));
761         }
762 
763         if (group != nullptr)
764         {
765             ReleaseGroup(group);
766         }
767         std::move(aCallback)(error);
768     }
769 }
770 
UnpublishHost(const std::string & aName,ResultCallback && aCallback)771 void PublisherAvahi::UnpublishHost(const std::string &aName, ResultCallback &&aCallback)
772 {
773     otbrError error = OTBR_ERROR_NONE;
774 
775     VerifyOrExit(mState == Publisher::State::kReady, error = OTBR_ERROR_INVALID_STATE);
776     RemoveHostRegistration(aName, OTBR_ERROR_ABORTED);
777 
778 exit:
779     std::move(aCallback)(error);
780 }
781 
TxtListToAvahiStringList(const TxtList & aTxtList,AvahiStringList * aBuffer,size_t aBufferSize,AvahiStringList * & aHead)782 otbrError PublisherAvahi::TxtListToAvahiStringList(const TxtList &   aTxtList,
783                                                    AvahiStringList * aBuffer,
784                                                    size_t            aBufferSize,
785                                                    AvahiStringList *&aHead)
786 {
787     otbrError        error = OTBR_ERROR_NONE;
788     size_t           used  = 0;
789     AvahiStringList *last  = nullptr;
790     AvahiStringList *curr  = aBuffer;
791 
792     aHead = nullptr;
793     for (const auto &txtEntry : aTxtList)
794     {
795         const char *   name        = txtEntry.mName.c_str();
796         size_t         nameLength  = txtEntry.mName.length();
797         const uint8_t *value       = txtEntry.mValue.data();
798         size_t         valueLength = txtEntry.mValue.size();
799         // +1 for the size of "=", avahi doesn't need '\0' at the end of the entry
800         size_t needed = sizeof(AvahiStringList) - sizeof(AvahiStringList::text) + nameLength + valueLength + 1;
801 
802         VerifyOrExit(used + needed <= aBufferSize, error = OTBR_ERROR_INVALID_ARGS);
803         curr->next = last;
804         last       = curr;
805         memcpy(curr->text, name, nameLength);
806         curr->text[nameLength] = '=';
807         memcpy(curr->text + nameLength + 1, value, valueLength);
808         curr->size = nameLength + valueLength + 1;
809         {
810             const uint8_t *next = curr->text + curr->size;
811             curr                = OTBR_ALIGNED(next, AvahiStringList *);
812         }
813         used = static_cast<size_t>(reinterpret_cast<uint8_t *>(curr) - reinterpret_cast<uint8_t *>(aBuffer));
814     }
815     SuccessOrExit(error);
816     aHead = last;
817 exit:
818     return error;
819 }
820 
FindServiceRegistration(const AvahiEntryGroup * aEntryGroup)821 Publisher::ServiceRegistration *PublisherAvahi::FindServiceRegistration(const AvahiEntryGroup *aEntryGroup)
822 {
823     ServiceRegistration *result = nullptr;
824 
825     for (const auto &kv : mServiceRegistrations)
826     {
827         const auto &serviceReg = static_cast<const AvahiServiceRegistration &>(*kv.second);
828         if (serviceReg.GetEntryGroup() == aEntryGroup)
829         {
830             result = kv.second.get();
831             break;
832         }
833     }
834 
835     return result;
836 }
837 
FindHostRegistration(const AvahiEntryGroup * aEntryGroup)838 Publisher::HostRegistration *PublisherAvahi::FindHostRegistration(const AvahiEntryGroup *aEntryGroup)
839 {
840     HostRegistration *result = nullptr;
841 
842     for (const auto &kv : mHostRegistrations)
843     {
844         const auto &hostReg = static_cast<const AvahiHostRegistration &>(*kv.second);
845         if (hostReg.GetEntryGroup() == aEntryGroup)
846         {
847             result = kv.second.get();
848             break;
849         }
850     }
851 
852     return result;
853 }
854 
SubscribeService(const std::string & aType,const std::string & aInstanceName)855 void PublisherAvahi::SubscribeService(const std::string &aType, const std::string &aInstanceName)
856 {
857     auto service = MakeUnique<ServiceSubscription>(*this, aType, aInstanceName);
858 
859     VerifyOrExit(mState == Publisher::State::kReady);
860     mSubscribedServices.push_back(std::move(service));
861 
862     otbrLogInfo("Subscribe service %s.%s (total %zu)", aInstanceName.c_str(), aType.c_str(),
863                 mSubscribedServices.size());
864 
865     if (aInstanceName.empty())
866     {
867         mSubscribedServices.back()->Browse();
868     }
869     else
870     {
871         mSubscribedServices.back()->Resolve(AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, aInstanceName, aType);
872     }
873 
874 exit:
875     return;
876 }
877 
UnsubscribeService(const std::string & aType,const std::string & aInstanceName)878 void PublisherAvahi::UnsubscribeService(const std::string &aType, const std::string &aInstanceName)
879 {
880     ServiceSubscriptionList::iterator it;
881 
882     VerifyOrExit(mState == Publisher::State::kReady);
883     it = std::find_if(mSubscribedServices.begin(), mSubscribedServices.end(),
884                       [&aType, &aInstanceName](const std::unique_ptr<ServiceSubscription> &aService) {
885                           return aService->mType == aType && aService->mInstanceName == aInstanceName;
886                       });
887 
888     assert(it != mSubscribedServices.end());
889 
890     {
891         std::unique_ptr<ServiceSubscription> service = std::move(*it);
892 
893         mSubscribedServices.erase(it);
894         service->Release();
895     }
896 
897     otbrLogInfo("Unsubscribe service %s.%s (left %zu)", aInstanceName.c_str(), aType.c_str(),
898                 mSubscribedServices.size());
899 
900 exit:
901     return;
902 }
903 
OnServiceResolveFailedImpl(const std::string & aType,const std::string & aInstanceName,int32_t aErrorCode)904 void PublisherAvahi::OnServiceResolveFailedImpl(const std::string &aType,
905                                                 const std::string &aInstanceName,
906                                                 int32_t            aErrorCode)
907 {
908     otbrLogWarning("Resolve service %s.%s failed: %s", aInstanceName.c_str(), aType.c_str(),
909                    avahi_strerror(aErrorCode));
910 }
911 
OnHostResolveFailedImpl(const std::string & aHostName,int32_t aErrorCode)912 void PublisherAvahi::OnHostResolveFailedImpl(const std::string &aHostName, int32_t aErrorCode)
913 {
914     otbrLogWarning("Resolve host %s failed: %s", aHostName.c_str(), avahi_strerror(aErrorCode));
915 }
916 
DnsErrorToOtbrError(int32_t aErrorCode)917 otbrError PublisherAvahi::DnsErrorToOtbrError(int32_t aErrorCode)
918 {
919     return otbr::Mdns::DnsErrorToOtbrError(aErrorCode);
920 }
921 
SubscribeHost(const std::string & aHostName)922 void PublisherAvahi::SubscribeHost(const std::string &aHostName)
923 {
924     auto host = MakeUnique<HostSubscription>(*this, aHostName);
925 
926     VerifyOrExit(mState == Publisher::State::kReady);
927 
928     mSubscribedHosts.push_back(std::move(host));
929 
930     otbrLogInfo("Subscribe host %s (total %zu)", aHostName.c_str(), mSubscribedHosts.size());
931 
932     mSubscribedHosts.back()->Resolve();
933 
934 exit:
935     return;
936 }
937 
UnsubscribeHost(const std::string & aHostName)938 void PublisherAvahi::UnsubscribeHost(const std::string &aHostName)
939 {
940     HostSubscriptionList::iterator it;
941 
942     VerifyOrExit(mState == Publisher::State::kReady);
943     it = std::find_if(
944         mSubscribedHosts.begin(), mSubscribedHosts.end(),
945         [&aHostName](const std::unique_ptr<HostSubscription> &aHost) { return aHost->mHostName == aHostName; });
946 
947     assert(it != mSubscribedHosts.end());
948 
949     {
950         std::unique_ptr<HostSubscription> host = std::move(*it);
951 
952         mSubscribedHosts.erase(it);
953         host->Release();
954     }
955 
956     otbrLogInfo("Unsubscribe host %s (remaining %zu)", aHostName.c_str(), mSubscribedHosts.size());
957 
958 exit:
959     return;
960 }
961 
Create(StateCallback aStateCallback)962 Publisher *Publisher::Create(StateCallback aStateCallback)
963 {
964     return new PublisherAvahi(std::move(aStateCallback));
965 }
966 
Destroy(Publisher * aPublisher)967 void Publisher::Destroy(Publisher *aPublisher)
968 {
969     delete static_cast<PublisherAvahi *>(aPublisher);
970 }
971 
Browse(void)972 void PublisherAvahi::ServiceSubscription::Browse(void)
973 {
974     assert(mPublisherAvahi->mClient != nullptr);
975 
976     otbrLogInfo("Browse service %s", mType.c_str());
977     mServiceBrowser =
978         avahi_service_browser_new(mPublisherAvahi->mClient, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, mType.c_str(),
979                                   /* domain */ nullptr, static_cast<AvahiLookupFlags>(0), HandleBrowseResult, this);
980     if (!mServiceBrowser)
981     {
982         otbrLogWarning("Failed to browse service %s: %s", mType.c_str(),
983                        avahi_strerror(avahi_client_errno(mPublisherAvahi->mClient)));
984     }
985 }
986 
Release(void)987 void PublisherAvahi::ServiceSubscription::Release(void)
988 {
989     for (AvahiServiceResolver *resolver : mServiceResolvers)
990     {
991         avahi_service_resolver_free(resolver);
992     }
993 
994     mServiceResolvers.clear();
995 
996     if (mServiceBrowser != nullptr)
997     {
998         avahi_service_browser_free(mServiceBrowser);
999         mServiceBrowser = nullptr;
1000     }
1001 }
1002 
HandleBrowseResult(AvahiServiceBrowser * aServiceBrowser,AvahiIfIndex aInterfaceIndex,AvahiProtocol aProtocol,AvahiBrowserEvent aEvent,const char * aName,const char * aType,const char * aDomain,AvahiLookupResultFlags aFlags,void * aContext)1003 void PublisherAvahi::ServiceSubscription::HandleBrowseResult(AvahiServiceBrowser *  aServiceBrowser,
1004                                                              AvahiIfIndex           aInterfaceIndex,
1005                                                              AvahiProtocol          aProtocol,
1006                                                              AvahiBrowserEvent      aEvent,
1007                                                              const char *           aName,
1008                                                              const char *           aType,
1009                                                              const char *           aDomain,
1010                                                              AvahiLookupResultFlags aFlags,
1011                                                              void *                 aContext)
1012 {
1013     static_cast<PublisherAvahi::ServiceSubscription *>(aContext)->HandleBrowseResult(
1014         aServiceBrowser, aInterfaceIndex, aProtocol, aEvent, aName, aType, aDomain, aFlags);
1015 }
1016 
HandleBrowseResult(AvahiServiceBrowser * aServiceBrowser,AvahiIfIndex aInterfaceIndex,AvahiProtocol aProtocol,AvahiBrowserEvent aEvent,const char * aName,const char * aType,const char * aDomain,AvahiLookupResultFlags aFlags)1017 void PublisherAvahi::ServiceSubscription::HandleBrowseResult(AvahiServiceBrowser *  aServiceBrowser,
1018                                                              AvahiIfIndex           aInterfaceIndex,
1019                                                              AvahiProtocol          aProtocol,
1020                                                              AvahiBrowserEvent      aEvent,
1021                                                              const char *           aName,
1022                                                              const char *           aType,
1023                                                              const char *           aDomain,
1024                                                              AvahiLookupResultFlags aFlags)
1025 {
1026     OTBR_UNUSED_VARIABLE(aServiceBrowser);
1027     OTBR_UNUSED_VARIABLE(aProtocol);
1028     OTBR_UNUSED_VARIABLE(aDomain);
1029 
1030     assert(mServiceBrowser == aServiceBrowser);
1031 
1032     otbrLogInfo("Browse service reply: %s.%s proto %d inf %u event %d flags %d", aName, aType, aProtocol,
1033                 aInterfaceIndex, static_cast<int>(aEvent), static_cast<int>(aFlags));
1034 
1035     switch (aEvent)
1036     {
1037     case AVAHI_BROWSER_NEW:
1038         Resolve(aInterfaceIndex, aProtocol, aName, aType);
1039         break;
1040     case AVAHI_BROWSER_REMOVE:
1041         mPublisherAvahi->OnServiceRemoved(static_cast<uint32_t>(aInterfaceIndex), aType, aName);
1042         break;
1043     case AVAHI_BROWSER_CACHE_EXHAUSTED:
1044     case AVAHI_BROWSER_ALL_FOR_NOW:
1045         // do nothing
1046         break;
1047     case AVAHI_BROWSER_FAILURE:
1048         mPublisherAvahi->OnServiceResolveFailed(aType, aName, avahi_client_errno(mPublisherAvahi->mClient));
1049         break;
1050     }
1051 }
1052 
Resolve(uint32_t aInterfaceIndex,AvahiProtocol aProtocol,const std::string & aInstanceName,const std::string & aType)1053 void PublisherAvahi::ServiceSubscription::Resolve(uint32_t           aInterfaceIndex,
1054                                                   AvahiProtocol      aProtocol,
1055                                                   const std::string &aInstanceName,
1056                                                   const std::string &aType)
1057 {
1058     AvahiServiceResolver *resolver;
1059 
1060     mPublisherAvahi->mServiceInstanceResolutionBeginTime[std::make_pair(aInstanceName, aType)] = Clock::now();
1061 
1062     otbrLogInfo("Resolve service %s.%s inf %" PRIu32, aInstanceName.c_str(), aType.c_str(), aInterfaceIndex);
1063 
1064     resolver = avahi_service_resolver_new(
1065         mPublisherAvahi->mClient, aInterfaceIndex, aProtocol, aInstanceName.c_str(), aType.c_str(),
1066         /* domain */ nullptr, AVAHI_PROTO_INET6, static_cast<AvahiLookupFlags>(0), HandleResolveResult, this);
1067     if (resolver != nullptr)
1068     {
1069         AddServiceResolver(resolver);
1070     }
1071     else
1072     {
1073         otbrLogErr("Failed to resolve serivce %s: %s", mType.c_str(),
1074                    avahi_strerror(avahi_client_errno(mPublisherAvahi->mClient)));
1075     }
1076 }
1077 
HandleResolveResult(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)1078 void PublisherAvahi::ServiceSubscription::HandleResolveResult(AvahiServiceResolver * aServiceResolver,
1079                                                               AvahiIfIndex           aInterfaceIndex,
1080                                                               AvahiProtocol          aProtocol,
1081                                                               AvahiResolverEvent     aEvent,
1082                                                               const char *           aName,
1083                                                               const char *           aType,
1084                                                               const char *           aDomain,
1085                                                               const char *           aHostName,
1086                                                               const AvahiAddress *   aAddress,
1087                                                               uint16_t               aPort,
1088                                                               AvahiStringList *      aTxt,
1089                                                               AvahiLookupResultFlags aFlags,
1090                                                               void *                 aContext)
1091 {
1092     static_cast<PublisherAvahi::ServiceSubscription *>(aContext)->HandleResolveResult(
1093         aServiceResolver, aInterfaceIndex, aProtocol, aEvent, aName, aType, aDomain, aHostName, aAddress, aPort, aTxt,
1094         aFlags);
1095 }
1096 
HandleResolveResult(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)1097 void PublisherAvahi::ServiceSubscription::HandleResolveResult(AvahiServiceResolver * aServiceResolver,
1098                                                               AvahiIfIndex           aInterfaceIndex,
1099                                                               AvahiProtocol          aProtocol,
1100                                                               AvahiResolverEvent     aEvent,
1101                                                               const char *           aName,
1102                                                               const char *           aType,
1103                                                               const char *           aDomain,
1104                                                               const char *           aHostName,
1105                                                               const AvahiAddress *   aAddress,
1106                                                               uint16_t               aPort,
1107                                                               AvahiStringList *      aTxt,
1108                                                               AvahiLookupResultFlags aFlags)
1109 {
1110     OT_UNUSED_VARIABLE(aServiceResolver);
1111     OT_UNUSED_VARIABLE(aInterfaceIndex);
1112     OT_UNUSED_VARIABLE(aProtocol);
1113     OT_UNUSED_VARIABLE(aType);
1114     OT_UNUSED_VARIABLE(aDomain);
1115 
1116     char                   addrBuf[AVAHI_ADDRESS_STR_MAX] = "";
1117     Ip6Address             address;
1118     size_t                 totalTxtSize = 0;
1119     DiscoveredInstanceInfo instanceInfo;
1120     bool                   resolved   = false;
1121     int                    avahiError = AVAHI_OK;
1122 
1123     otbrLog(aEvent == AVAHI_RESOLVER_FOUND ? OTBR_LOG_INFO : OTBR_LOG_WARNING, OTBR_LOG_TAG,
1124             "Resolve service reply: protocol %d %s.%s.%s = host %s port %" PRIu16 " flags %d event %d", aProtocol,
1125             aName, aType, aDomain, aHostName, aPort, static_cast<int>(aFlags), static_cast<int>(aEvent));
1126 
1127     VerifyOrExit(aEvent == AVAHI_RESOLVER_FOUND, avahiError = avahi_client_errno(mPublisherAvahi->mClient));
1128     avahi_address_snprint(addrBuf, sizeof(addrBuf), aAddress);
1129     otbrLogInfo("Resolve service reply: address %s", addrBuf);
1130 
1131     RemoveServiceResolver(aServiceResolver);
1132     VerifyOrExit(aHostName != nullptr, avahiError = AVAHI_ERR_INVALID_HOST_NAME);
1133 
1134     instanceInfo.mNetifIndex = static_cast<uint32_t>(aInterfaceIndex);
1135     instanceInfo.mName       = aName;
1136     instanceInfo.mHostName   = std::string(aHostName) + ".";
1137     instanceInfo.mPort       = aPort;
1138     VerifyOrExit(otbrError::OTBR_ERROR_NONE == Ip6Address::FromString(addrBuf, address),
1139                  otbrLogErr("Failed to parse the IP address: %s", addrBuf), avahiError = AVAHI_ERR_INVALID_ADDRESS);
1140 
1141     otbrLogInfo("Resolve service reply: flags=%u, host=%s", aFlags, aHostName);
1142 
1143     VerifyOrExit(!address.IsLinkLocal() && !address.IsMulticast() && !address.IsLoopback() && !address.IsUnspecified(),
1144                  otbrLogInfo("Ignoring address %s", address.ToString().c_str()),
1145                  avahiError = AVAHI_ERR_INVALID_ADDRESS);
1146 
1147     instanceInfo.mAddresses.push_back(address);
1148 
1149     // TODO priority
1150     // TODO weight
1151     // TODO use a more proper TTL
1152     instanceInfo.mTtl = kDefaultTtl;
1153     for (auto p = aTxt; p; p = avahi_string_list_get_next(p))
1154     {
1155         totalTxtSize += avahi_string_list_get_size(p) + 1;
1156     }
1157     instanceInfo.mTxtData.resize(totalTxtSize);
1158     avahi_string_list_serialize(aTxt, instanceInfo.mTxtData.data(), totalTxtSize);
1159 
1160     otbrLogInfo("Resolve service reply: address=%s, ttl=%" PRIu32, address.ToString().c_str(), instanceInfo.mTtl);
1161 
1162     resolved = true;
1163 
1164 exit:
1165     if (resolved)
1166     {
1167         // NOTE: This `ServiceSubscrption` object may be freed in `OnServiceResolved`.
1168         mPublisherAvahi->OnServiceResolved(mType, instanceInfo);
1169     }
1170     else if (avahiError != AVAHI_OK)
1171     {
1172         mPublisherAvahi->OnServiceResolveFailed(aType, aName, avahiError);
1173     }
1174 }
1175 
AddServiceResolver(AvahiServiceResolver * aServiceResolver)1176 void PublisherAvahi::ServiceSubscription::AddServiceResolver(AvahiServiceResolver *aServiceResolver)
1177 {
1178     assert(aServiceResolver != nullptr);
1179     mServiceResolvers.insert(aServiceResolver);
1180 }
1181 
RemoveServiceResolver(AvahiServiceResolver * aServiceResolver)1182 void PublisherAvahi::ServiceSubscription::RemoveServiceResolver(AvahiServiceResolver *aServiceResolver)
1183 {
1184     assert(aServiceResolver != nullptr);
1185     assert(mServiceResolvers.find(aServiceResolver) != mServiceResolvers.end());
1186 
1187     avahi_service_resolver_free(aServiceResolver);
1188     mServiceResolvers.erase(aServiceResolver);
1189 }
1190 
Release(void)1191 void PublisherAvahi::HostSubscription::Release(void)
1192 {
1193     if (mRecordBrowser != nullptr)
1194     {
1195         avahi_record_browser_free(mRecordBrowser);
1196         mRecordBrowser = nullptr;
1197     }
1198 }
1199 
Resolve(void)1200 void PublisherAvahi::HostSubscription::Resolve(void)
1201 {
1202     std::string fullHostName = MakeFullHostName(mHostName);
1203 
1204     mPublisherAvahi->mHostResolutionBeginTime[mHostName] = Clock::now();
1205 
1206     otbrLogInfo("Resolve host %s inf %d", fullHostName.c_str(), static_cast<int>(AVAHI_IF_UNSPEC));
1207     mRecordBrowser = avahi_record_browser_new(mPublisherAvahi->mClient, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
1208                                               fullHostName.c_str(), AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA,
1209                                               static_cast<AvahiLookupFlags>(0), HandleResolveResult, this);
1210     if (!mRecordBrowser)
1211     {
1212         otbrLogErr("Failed to resolve host %s: %s", fullHostName.c_str(),
1213                    avahi_strerror(avahi_client_errno(mPublisherAvahi->mClient)));
1214     }
1215 }
1216 
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)1217 void PublisherAvahi::HostSubscription::HandleResolveResult(AvahiRecordBrowser *   aRecordBrowser,
1218                                                            AvahiIfIndex           aInterfaceIndex,
1219                                                            AvahiProtocol          aProtocol,
1220                                                            AvahiBrowserEvent      aEvent,
1221                                                            const char *           aName,
1222                                                            uint16_t               aClazz,
1223                                                            uint16_t               aType,
1224                                                            const void *           aRdata,
1225                                                            size_t                 aSize,
1226                                                            AvahiLookupResultFlags aFlags,
1227                                                            void *                 aContext)
1228 {
1229     static_cast<PublisherAvahi::HostSubscription *>(aContext)->HandleResolveResult(
1230         aRecordBrowser, aInterfaceIndex, aProtocol, aEvent, aName, aClazz, aType, aRdata, aSize, aFlags);
1231 }
1232 
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)1233 void PublisherAvahi::HostSubscription::HandleResolveResult(AvahiRecordBrowser *   aRecordBrowser,
1234                                                            AvahiIfIndex           aInterfaceIndex,
1235                                                            AvahiProtocol          aProtocol,
1236                                                            AvahiBrowserEvent      aEvent,
1237                                                            const char *           aName,
1238                                                            uint16_t               aClazz,
1239                                                            uint16_t               aType,
1240                                                            const void *           aRdata,
1241                                                            size_t                 aSize,
1242                                                            AvahiLookupResultFlags aFlags)
1243 {
1244     OTBR_UNUSED_VARIABLE(aRecordBrowser);
1245     OTBR_UNUSED_VARIABLE(aInterfaceIndex);
1246     OTBR_UNUSED_VARIABLE(aProtocol);
1247     OTBR_UNUSED_VARIABLE(aEvent);
1248     OTBR_UNUSED_VARIABLE(aClazz);
1249     OTBR_UNUSED_VARIABLE(aType);
1250     OTBR_UNUSED_VARIABLE(aFlags);
1251 
1252     Ip6Address address;
1253     bool       resolved   = false;
1254     int        avahiError = AVAHI_OK;
1255 
1256     otbrLog(aEvent != AVAHI_BROWSER_FAILURE ? OTBR_LOG_INFO : OTBR_LOG_WARNING, OTBR_LOG_TAG,
1257             "Resolve host reply: %s inf %d protocol %d class %" PRIu16 " type %" PRIu16 " size %zu flags %d event %d",
1258             aName, aInterfaceIndex, aProtocol, aClazz, aType, aSize, static_cast<int>(aFlags),
1259             static_cast<int>(aEvent));
1260 
1261     VerifyOrExit(aEvent == AVAHI_BROWSER_NEW);
1262     VerifyOrExit(aSize == OTBR_IP6_ADDRESS_SIZE || aSize == OTBR_IP4_ADDRESS_SIZE,
1263                  otbrLogErr("Unexpected address data length: %zu", aSize), avahiError = AVAHI_ERR_INVALID_ADDRESS);
1264     VerifyOrExit(aSize == OTBR_IP6_ADDRESS_SIZE, otbrLogInfo("IPv4 address ignored"),
1265                  avahiError = AVAHI_ERR_INVALID_ADDRESS);
1266     address = Ip6Address(*static_cast<const uint8_t(*)[OTBR_IP6_ADDRESS_SIZE]>(aRdata));
1267 
1268     VerifyOrExit(!address.IsLinkLocal() && !address.IsMulticast() && !address.IsLoopback() && !address.IsUnspecified(),
1269                  avahiError = AVAHI_ERR_INVALID_ADDRESS);
1270     otbrLogInfo("Resolved host address: %s", address.ToString().c_str());
1271 
1272     mHostInfo.mHostName = std::string(aName) + ".";
1273     mHostInfo.mAddresses.push_back(std::move(address));
1274     // TODO: Use a more proper TTL
1275     mHostInfo.mTtl = kDefaultTtl;
1276     resolved       = true;
1277 
1278 exit:
1279     if (resolved)
1280     {
1281         // NOTE: This `HostSubscrption` object may be freed in `OnHostResolved`.
1282         mPublisherAvahi->OnHostResolved(mHostName, mHostInfo);
1283     }
1284     else if (avahiError != AVAHI_OK)
1285     {
1286         mPublisherAvahi->OnHostResolveFailed(mHostName, avahiError);
1287     }
1288 }
1289 
1290 } // namespace Mdns
1291 
1292 } // namespace otbr
1293