Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / platform / Linux / MdnsImpl.cpp
1 /*
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *
5  *    Licensed under the Apache License, Version 2.0 (the "License");
6  *    you may not use this file except in compliance with the License.
7  *    You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *    Unless required by applicable law or agreed to in writing, software
12  *    distributed under the License is distributed on an "AS IS" BASIS,
13  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *    See the License for the specific language governing permissions and
15  *    limitations under the License.
16  */
17
18 #include "MdnsImpl.h"
19
20 #include <algorithm>
21 #include <sstream>
22 #include <string.h>
23 #include <time.h>
24 #include <vector>
25
26 #include <netinet/in.h>
27
28 #include "support/CHIPMem.h"
29 #include "support/CodeUtils.h"
30
31 using chip::Mdns::kMdnsTypeMaxSize;
32 using chip::Mdns::MdnsServiceProtocol;
33 using chip::Mdns::TextEntry;
34 using std::chrono::duration_cast;
35 using std::chrono::microseconds;
36 using std::chrono::seconds;
37 using std::chrono::steady_clock;
38
39 namespace {
40
41 AvahiProtocol ToAvahiProtocol(chip::Inet::IPAddressType addressType)
42 {
43     AvahiProtocol protocol;
44
45     switch (addressType)
46     {
47     case chip::Inet::IPAddressType::kIPAddressType_IPv4:
48         protocol = AVAHI_PROTO_INET;
49         break;
50     case chip::Inet::IPAddressType::kIPAddressType_IPv6:
51         protocol = AVAHI_PROTO_INET6;
52         break;
53     default:
54         protocol = AVAHI_PROTO_UNSPEC;
55         break;
56     }
57
58     return protocol;
59 }
60
61 chip::Inet::IPAddressType ToAddressType(AvahiProtocol protocol)
62 {
63     chip::Inet::IPAddressType type;
64
65     switch (protocol)
66     {
67     case AVAHI_PROTO_INET:
68         type = chip::Inet::IPAddressType::kIPAddressType_IPv4;
69         break;
70     case AVAHI_PROTO_INET6:
71         type = chip::Inet::IPAddressType::kIPAddressType_IPv6;
72         break;
73     default:
74         type = chip::Inet::IPAddressType::kIPAddressType_Unknown;
75         break;
76     }
77
78     return type;
79 }
80
81 CHIP_ERROR MakeAvahiStringListFromTextEntries(TextEntry * entries, size_t size, AvahiStringList ** strListOut)
82 {
83     *strListOut = avahi_string_list_new(nullptr, nullptr);
84
85     for (size_t i = 0; i < size; i++)
86     {
87         uint8_t buf[kMdnsTypeMaxSize];
88         size_t offset = static_cast<size_t>(snprintf(reinterpret_cast<char *>(buf), sizeof(buf), "%s=", entries[i].mKey));
89
90         if (offset + entries[i].mDataSize > sizeof(buf))
91         {
92             avahi_string_list_free(*strListOut);
93             *strListOut = nullptr;
94             return CHIP_ERROR_INVALID_ARGUMENT;
95         }
96
97         memcpy(&buf[offset], entries[i].mData, entries[i].mDataSize);
98         *strListOut = avahi_string_list_add_arbitrary(*strListOut, buf, offset + entries[i].mDataSize);
99     }
100     return CHIP_NO_ERROR;
101 }
102
103 const char * GetProtocolString(MdnsServiceProtocol protocol)
104 {
105     return protocol == MdnsServiceProtocol::kMdnsProtocolUdp ? "_udp" : "_tcp";
106 }
107
108 std::string GetFullType(const char * type, MdnsServiceProtocol protocol)
109 {
110     std::ostringstream typeBuilder;
111     typeBuilder << type << "." << GetProtocolString(protocol);
112     return typeBuilder.str();
113 }
114
115 } // namespace
116
117 namespace chip {
118 namespace Mdns {
119
120 MdnsAvahi MdnsAvahi::sInstance;
121
122 constexpr uint64_t kUsPerSec = 1000 * 1000;
123
124 Poller::Poller()
125 {
126     mAvahiPoller.userdata         = this;
127     mAvahiPoller.watch_new        = WatchNew;
128     mAvahiPoller.watch_update     = WatchUpdate;
129     mAvahiPoller.watch_get_events = WatchGetEvents;
130     mAvahiPoller.watch_free       = WatchFree;
131
132     mAvahiPoller.timeout_new    = TimeoutNew;
133     mAvahiPoller.timeout_update = TimeoutUpdate;
134     mAvahiPoller.timeout_free   = TimeoutFree;
135 }
136
137 AvahiWatch * Poller::WatchNew(const struct AvahiPoll * poller, int fd, AvahiWatchEvent event, AvahiWatchCallback callback,
138                               void * context)
139 {
140     return reinterpret_cast<Poller *>(poller->userdata)->WatchNew(fd, event, callback, context);
141 }
142
143 AvahiWatch * Poller::WatchNew(int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void * context)
144 {
145     VerifyOrDie(callback != nullptr && fd >= 0);
146
147     mWatches.emplace_back(new AvahiWatch{ fd, event, 0, callback, context, this });
148
149     return mWatches.back().get();
150 }
151
152 void Poller::WatchUpdate(AvahiWatch * watch, AvahiWatchEvent event)
153 {
154     watch->mWatchEvents = event;
155 }
156
157 AvahiWatchEvent Poller::WatchGetEvents(AvahiWatch * watch)
158 {
159     return static_cast<AvahiWatchEvent>(watch->mHappenedEvents);
160 }
161
162 void Poller::WatchFree(AvahiWatch * watch)
163 {
164     reinterpret_cast<Poller *>(watch->mPoller)->WatchFree(*watch);
165 }
166
167 void Poller::WatchFree(AvahiWatch & watch)
168 {
169     mWatches.erase(std::remove_if(mWatches.begin(), mWatches.end(),
170                                   [&watch](const std::unique_ptr<AvahiWatch> & aValue) { return aValue.get() == &watch; }),
171                    mWatches.end());
172 }
173
174 AvahiTimeout * Poller::TimeoutNew(const AvahiPoll * poller, const struct timeval * timeout, AvahiTimeoutCallback callback,
175                                   void * context)
176 {
177     VerifyOrDie(poller != nullptr && callback != nullptr);
178
179     return static_cast<Poller *>(poller->userdata)->TimeoutNew(timeout, callback, context);
180 }
181
182 steady_clock::time_point GetAbsTimeout(const struct timeval * timeout)
183 {
184     steady_clock::time_point now        = steady_clock::now();
185     steady_clock::time_point absTimeout = now;
186
187     if (timeout != nullptr)
188     {
189         absTimeout += seconds(timeout->tv_sec);
190         absTimeout += microseconds(timeout->tv_usec);
191     }
192
193     return absTimeout;
194 }
195
196 AvahiTimeout * Poller::TimeoutNew(const struct timeval * timeout, AvahiTimeoutCallback callback, void * context)
197 {
198
199     mTimers.emplace_back(new AvahiTimeout{ GetAbsTimeout(timeout), callback, timeout != nullptr, context, this });
200     return mTimers.back().get();
201 }
202
203 void Poller::TimeoutUpdate(AvahiTimeout * timer, const struct timeval * timeout)
204 {
205     if (timeout)
206     {
207         timer->mAbsTimeout = GetAbsTimeout(timeout);
208         timer->mEnabled    = true;
209     }
210     else
211     {
212         timer->mEnabled = false;
213     }
214 }
215
216 void Poller::TimeoutFree(AvahiTimeout * timer)
217 {
218     static_cast<Poller *>(timer->mPoller)->TimeoutFree(*timer);
219 }
220
221 void Poller::TimeoutFree(AvahiTimeout & timer)
222 {
223     mTimers.erase(std::remove_if(mTimers.begin(), mTimers.end(),
224                                  [&timer](const std::unique_ptr<AvahiTimeout> & aValue) { return aValue.get() == &timer; }),
225                   mTimers.end());
226 }
227
228 void Poller::UpdateFdSet(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet, int & aMaxFd, timeval & timeout)
229 {
230     microseconds timeoutVal = seconds(timeout.tv_sec) + microseconds(timeout.tv_usec);
231
232     for (auto && watch : mWatches)
233     {
234         int fd                 = watch->mFd;
235         AvahiWatchEvent events = watch->mWatchEvents;
236
237         if (AVAHI_WATCH_IN & events)
238         {
239             FD_SET(fd, &readFdSet);
240         }
241
242         if (AVAHI_WATCH_OUT & events)
243         {
244             FD_SET(fd, &writeFdSet);
245         }
246
247         if (AVAHI_WATCH_ERR & events)
248         {
249             FD_SET(fd, &errorFdSet);
250         }
251
252         if (aMaxFd < fd)
253         {
254             aMaxFd = fd;
255         }
256
257         watch->mHappenedEvents = 0;
258     }
259
260     for (auto && timer : mTimers)
261     {
262         steady_clock::time_point absTimeout = timer->mAbsTimeout;
263         steady_clock::time_point now        = steady_clock::now();
264
265         if (!timer->mEnabled)
266         {
267             continue;
268         }
269         if (absTimeout < now)
270         {
271             timeoutVal = microseconds(0);
272             break;
273         }
274         else
275         {
276             timeoutVal = std::min(timeoutVal, duration_cast<microseconds>(absTimeout - now));
277         }
278     }
279
280     timeout.tv_sec  = static_cast<uint64_t>(timeoutVal.count()) / kUsPerSec;
281     timeout.tv_usec = static_cast<uint64_t>(timeoutVal.count()) % kUsPerSec;
282 }
283
284 void Poller::Process(const fd_set & readFdSet, const fd_set & writeFdSet, const fd_set & errorFdSet)
285 {
286     steady_clock::time_point now = steady_clock::now();
287
288     for (auto && watch : mWatches)
289     {
290         int fd                 = watch->mFd;
291         AvahiWatchEvent events = watch->mWatchEvents;
292
293         watch->mHappenedEvents = 0;
294
295         if ((AVAHI_WATCH_IN & events) && FD_ISSET(fd, &readFdSet))
296         {
297             watch->mHappenedEvents |= AVAHI_WATCH_IN;
298         }
299
300         if ((AVAHI_WATCH_OUT & events) && FD_ISSET(fd, &writeFdSet))
301         {
302             watch->mHappenedEvents |= AVAHI_WATCH_OUT;
303         }
304
305         if ((AVAHI_WATCH_ERR & events) && FD_ISSET(fd, &errorFdSet))
306         {
307             watch->mHappenedEvents |= AVAHI_WATCH_ERR;
308         }
309
310         if (watch->mHappenedEvents)
311         {
312             watch->mCallback(watch.get(), watch->mFd, static_cast<AvahiWatchEvent>(watch->mHappenedEvents), watch->mContext);
313         }
314     }
315
316     for (auto && timer : mTimers)
317     {
318         if (!timer->mEnabled)
319         {
320             continue;
321         }
322         if (timer->mAbsTimeout <= now)
323         {
324             timer->mCallback(timer.get(), timer->mContext);
325         }
326     }
327 }
328
329 CHIP_ERROR MdnsAvahi::Init(MdnsAsyncReturnCallback initCallback, MdnsAsyncReturnCallback errorCallback, void * context)
330 {
331     CHIP_ERROR error = CHIP_NO_ERROR;
332     int avahiError   = 0;
333
334     VerifyOrExit(initCallback != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT);
335     VerifyOrExit(errorCallback != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT);
336     VerifyOrExit(mClient == nullptr && mGroup == nullptr, error = CHIP_ERROR_INCORRECT_STATE);
337     mInitCallback       = initCallback;
338     mErrorCallback      = errorCallback;
339     mAsyncReturnContext = context;
340     mClient             = avahi_client_new(mPoller.GetAvahiPoll(), AVAHI_CLIENT_NO_FAIL, HandleClientState, this, &avahiError);
341     VerifyOrExit(mClient != nullptr, error = CHIP_ERROR_OPEN_FAILED);
342     VerifyOrExit(avahiError == 0, error = CHIP_ERROR_OPEN_FAILED);
343
344 exit:
345     return error;
346 }
347
348 CHIP_ERROR MdnsAvahi::SetHostname(const char * hostname)
349 {
350     CHIP_ERROR error = CHIP_NO_ERROR;
351     int avahiRet;
352
353     VerifyOrExit(mClient != nullptr, error = CHIP_ERROR_INCORRECT_STATE);
354     avahiRet = avahi_client_set_host_name(mClient, hostname);
355     if (avahiRet == AVAHI_ERR_ACCESS_DENIED)
356     {
357         ChipLogError(DeviceLayer, "Cannot set hostname on this system, continue anyway...");
358     }
359     else if (avahiRet != AVAHI_OK && avahiRet != AVAHI_ERR_NO_CHANGE)
360     {
361         error = CHIP_ERROR_INTERNAL;
362     }
363
364 exit:
365     return error;
366 }
367
368 void MdnsAvahi::HandleClientState(AvahiClient * client, AvahiClientState state, void * context)
369 {
370     static_cast<MdnsAvahi *>(context)->HandleClientState(client, state);
371 }
372
373 void MdnsAvahi::HandleClientState(AvahiClient * client, AvahiClientState state)
374 {
375     switch (state)
376     {
377     case AVAHI_CLIENT_S_RUNNING:
378         ChipLogProgress(DeviceLayer, "Avahi client registered");
379         mClient = client;
380         mGroup  = avahi_entry_group_new(client, HandleGroupState, this);
381         if (mGroup == nullptr)
382         {
383             ChipLogError(DeviceLayer, "Failed to create avahi group: %s", avahi_strerror(avahi_client_errno(client)));
384             mInitCallback(mAsyncReturnContext, CHIP_ERROR_OPEN_FAILED);
385         }
386         else
387         {
388             mInitCallback(mAsyncReturnContext, CHIP_NO_ERROR);
389         }
390         break;
391     case AVAHI_CLIENT_FAILURE:
392         ChipLogError(DeviceLayer, "Avahi client failure");
393         mErrorCallback(mAsyncReturnContext, CHIP_ERROR_INTERNAL);
394         break;
395     case AVAHI_CLIENT_S_COLLISION:
396     case AVAHI_CLIENT_S_REGISTERING:
397         ChipLogProgress(DeviceLayer, "Avahi re-register required");
398         if (mGroup != nullptr)
399         {
400             avahi_entry_group_reset(mGroup);
401             avahi_entry_group_free(mGroup);
402         }
403         mGroup = avahi_entry_group_new(client, HandleGroupState, this);
404         mPublishedServices.clear();
405         if (mGroup == nullptr)
406         {
407             ChipLogError(DeviceLayer, "Failed to create avahi group: %s", avahi_strerror(avahi_client_errno(client)));
408             mErrorCallback(mAsyncReturnContext, CHIP_ERROR_OPEN_FAILED);
409         }
410         else
411         {
412             mErrorCallback(mAsyncReturnContext, CHIP_ERROR_FORCED_RESET);
413         }
414         break;
415     case AVAHI_CLIENT_CONNECTING:
416         ChipLogProgress(DeviceLayer, "Avahi connecting");
417         break;
418     }
419 }
420
421 void MdnsAvahi::HandleGroupState(AvahiEntryGroup * group, AvahiEntryGroupState state, void * context)
422 {
423     static_cast<MdnsAvahi *>(context)->HandleGroupState(group, state);
424 }
425
426 void MdnsAvahi::HandleGroupState(AvahiEntryGroup * group, AvahiEntryGroupState state)
427 {
428     switch (state)
429     {
430     case AVAHI_ENTRY_GROUP_ESTABLISHED:
431         ChipLogProgress(DeviceLayer, "Avahi group established");
432         break;
433     case AVAHI_ENTRY_GROUP_COLLISION:
434         ChipLogError(DeviceLayer, "Avahi group collission");
435         mErrorCallback(mAsyncReturnContext, CHIP_ERROR_MDNS_COLLISSION);
436         break;
437     case AVAHI_ENTRY_GROUP_FAILURE:
438         ChipLogError(DeviceLayer, "Avahi group internal failure %s",
439                      avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(mGroup))));
440         mErrorCallback(mAsyncReturnContext, CHIP_ERROR_INTERNAL);
441         break;
442     case AVAHI_ENTRY_GROUP_UNCOMMITED:
443     case AVAHI_ENTRY_GROUP_REGISTERING:
444         break;
445     }
446 }
447
448 CHIP_ERROR MdnsAvahi::PublishService(const MdnsService & service)
449 {
450     std::ostringstream keyBuilder;
451     std::string key;
452     std::string type       = GetFullType(service.mType, service.mProtocol);
453     CHIP_ERROR error       = CHIP_NO_ERROR;
454     AvahiStringList * text = nullptr;
455     AvahiIfIndex interface =
456         service.mInterface == INET_NULL_INTERFACEID ? AVAHI_IF_UNSPEC : static_cast<AvahiIfIndex>(service.mInterface);
457
458     keyBuilder << service.mName << "." << type << service.mPort << "." << interface;
459     key = keyBuilder.str();
460     ChipLogProgress(DeviceLayer, "PublishService %s", key.c_str());
461
462     if (mPublishedServices.find(key) == mPublishedServices.end())
463     {
464         SuccessOrExit(error = MakeAvahiStringListFromTextEntries(service.mTextEntries, service.mTextEntrySize, &text));
465
466         mPublishedServices.emplace(key);
467         VerifyOrExit(avahi_entry_group_add_service_strlst(mGroup, interface, ToAvahiProtocol(service.mAddressType),
468                                                           static_cast<AvahiPublishFlags>(0), service.mName, type.c_str(), nullptr,
469                                                           nullptr, service.mPort, text) == 0,
470                      error = CHIP_ERROR_INTERNAL);
471         for (size_t i = 0; i < service.mSubTypeSize; i++)
472         {
473             std::ostringstream sstream;
474
475             sstream << service.mSubTypes[i] << "._sub." << type;
476
477             VerifyOrExit(avahi_entry_group_add_service_subtype(mGroup, interface, ToAvahiProtocol(service.mAddressType),
478                                                                static_cast<AvahiPublishFlags>(0), service.mName, type.c_str(),
479                                                                nullptr, sstream.str().c_str()) == 0,
480                          error = CHIP_ERROR_INTERNAL);
481         }
482     }
483     else
484     {
485         SuccessOrExit(error = MakeAvahiStringListFromTextEntries(service.mTextEntries, service.mTextEntrySize, &text));
486
487         VerifyOrExit(avahi_entry_group_update_service_txt_strlst(mGroup, interface, ToAvahiProtocol(service.mAddressType),
488                                                                  static_cast<AvahiPublishFlags>(0), service.mName, type.c_str(),
489                                                                  nullptr, text) == 0,
490                      error = CHIP_ERROR_INTERNAL);
491     }
492
493     VerifyOrExit(avahi_entry_group_commit(mGroup) == 0, error = CHIP_ERROR_INTERNAL);
494
495 exit:
496     if (text != nullptr)
497     {
498         avahi_string_list_free(text);
499     }
500     if (error != CHIP_NO_ERROR)
501     {
502         ChipLogError(DeviceLayer, "Avahi publish service failed: %d", static_cast<int>(error));
503     }
504
505     return error;
506 }
507
508 CHIP_ERROR MdnsAvahi::StopPublish()
509 {
510     CHIP_ERROR error = CHIP_NO_ERROR;
511
512     VerifyOrExit(avahi_entry_group_reset(mGroup) == 0, error = CHIP_ERROR_INTERNAL);
513 exit:
514     return error;
515 }
516
517 CHIP_ERROR MdnsAvahi::Browse(const char * type, MdnsServiceProtocol protocol, chip::Inet::IPAddressType addressType,
518                              chip::Inet::InterfaceId interface, MdnsBrowseCallback callback, void * context)
519 {
520     AvahiServiceBrowser * browser;
521     BrowseContext * browseContext = chip::Platform::New<BrowseContext>();
522     AvahiIfIndex avahiInterface   = static_cast<AvahiIfIndex>(interface);
523
524     browseContext->mInstance = this;
525     browseContext->mContext  = context;
526     browseContext->mCallback = callback;
527     if (interface == INET_NULL_INTERFACEID)
528     {
529         avahiInterface = AVAHI_IF_UNSPEC;
530     }
531
532     browser = avahi_service_browser_new(mClient, avahiInterface, ToAvahiProtocol(addressType), GetFullType(type, protocol).c_str(),
533                                         nullptr, static_cast<AvahiLookupFlags>(0), HandleBrowse, browseContext);
534     // Otherwise the browser will be freed in the callback
535     if (browser == nullptr)
536     {
537         chip::Platform::Delete(browseContext);
538     }
539
540     return browser == nullptr ? CHIP_ERROR_INTERNAL : CHIP_NO_ERROR;
541 }
542
543 MdnsServiceProtocol TruncateProtocolInType(char * type)
544 {
545     char * deliminator           = strrchr(type, '.');
546     MdnsServiceProtocol protocol = MdnsServiceProtocol::kMdnsProtocolUnknown;
547
548     if (deliminator != NULL)
549     {
550         if (strcmp("._tcp", deliminator) == 0)
551         {
552             protocol     = MdnsServiceProtocol::kMdnsProtocolTcp;
553             *deliminator = 0;
554         }
555         else if (strcmp("._udp", deliminator) == 0)
556         {
557             protocol     = MdnsServiceProtocol::kMdnsProtocolUdp;
558             *deliminator = 0;
559         }
560     }
561     return protocol;
562 }
563
564 void MdnsAvahi::HandleBrowse(AvahiServiceBrowser * browser, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event,
565                              const char * name, const char * type, const char * domain, AvahiLookupResultFlags /*flags*/,
566                              void * userdata)
567 {
568     BrowseContext * context = static_cast<BrowseContext *>(userdata);
569
570     switch (event)
571     {
572     case AVAHI_BROWSER_FAILURE:
573         context->mCallback(context->mContext, nullptr, 0, CHIP_ERROR_INTERNAL);
574         avahi_service_browser_free(browser);
575         chip::Platform::Delete(context);
576         break;
577     case AVAHI_BROWSER_NEW:
578         ChipLogProgress(DeviceLayer, "Avahi browse: cache new");
579         if (strcmp("local", domain) == 0)
580         {
581             MdnsService service = {};
582
583             strncpy(service.mName, name, sizeof(service.mName));
584             strncpy(service.mType, type, sizeof(service.mType));
585             service.mName[kMdnsNameMaxSize] = 0;
586             service.mType[kMdnsTypeMaxSize] = 0;
587             service.mProtocol               = TruncateProtocolInType(service.mType);
588             service.mAddressType            = ToAddressType(protocol);
589             context->mServices.push_back(service);
590         }
591         break;
592     case AVAHI_BROWSER_ALL_FOR_NOW:
593         ChipLogProgress(DeviceLayer, "Avahi browse: all for now");
594         context->mCallback(context->mContext, context->mServices.data(), context->mServices.size(), CHIP_NO_ERROR);
595         avahi_service_browser_free(browser);
596         chip::Platform::Delete(context);
597         break;
598     case AVAHI_BROWSER_REMOVE:
599         ChipLogProgress(DeviceLayer, "Avahi browse: remove");
600         if (strcmp("local", domain) == 0)
601         {
602             std::remove_if(context->mServices.begin(), context->mServices.end(), [name, type](const MdnsService & service) {
603                 return strcmp(name, service.mName) == 0 && type == GetFullType(service.mType, service.mProtocol);
604             });
605         }
606         break;
607     case AVAHI_BROWSER_CACHE_EXHAUSTED:
608         ChipLogProgress(DeviceLayer, "Avahi browse: cache exhausted");
609         break;
610     }
611 }
612
613 CHIP_ERROR MdnsAvahi::Resolve(const char * name, const char * type, MdnsServiceProtocol protocol,
614                               chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface,
615                               MdnsResolveCallback callback, void * context)
616 {
617     AvahiServiceResolver * resolver;
618     AvahiIfIndex avahiInterface     = static_cast<AvahiIfIndex>(interface);
619     ResolveContext * resolveContext = chip::Platform::New<ResolveContext>();
620     CHIP_ERROR error                = CHIP_NO_ERROR;
621
622     resolveContext->mInstance = this;
623     resolveContext->mCallback = callback;
624     resolveContext->mContext  = context;
625     if (interface == INET_NULL_INTERFACEID)
626     {
627         avahiInterface = AVAHI_IF_UNSPEC;
628     }
629     resolver = avahi_service_resolver_new(mClient, avahiInterface, ToAvahiProtocol(addressType), name,
630                                           GetFullType(type, protocol).c_str(), nullptr, ToAvahiProtocol(addressType),
631                                           static_cast<AvahiLookupFlags>(0), HandleResolve, resolveContext);
632     // Otherwise the resolver will be freed in the callback
633     if (resolver == nullptr)
634     {
635         error = CHIP_ERROR_INTERNAL;
636         chip::Platform::Delete(resolveContext);
637     }
638
639     return error;
640 }
641
642 void MdnsAvahi::HandleResolve(AvahiServiceResolver * resolver, AvahiIfIndex interface, AvahiProtocol protocol,
643                               AvahiResolverEvent event, const char * name, const char * type, const char * /*domain*/,
644                               const char * /*host_name*/, const AvahiAddress * address, uint16_t port, AvahiStringList * txt,
645                               AvahiLookupResultFlags flags, void * userdata)
646 {
647     ResolveContext * context = reinterpret_cast<ResolveContext *>(userdata);
648     std::vector<TextEntry> textEntries;
649
650     switch (event)
651     {
652     case AVAHI_RESOLVER_FAILURE:
653         ChipLogError(DeviceLayer, "Avahi resolve failed");
654         context->mCallback(context->mContext, nullptr, CHIP_ERROR_INTERNAL);
655         break;
656     case AVAHI_RESOLVER_FOUND:
657         MdnsService result = {};
658
659         result.mAddress.SetValue(chip::Inet::IPAddress());
660         ChipLogError(DeviceLayer, "Avahi resolve found");
661         strncpy(result.mName, name, sizeof(result.mName));
662         strncpy(result.mType, type, sizeof(result.mType));
663         result.mName[kMdnsNameMaxSize] = 0;
664         result.mType[kMdnsTypeMaxSize] = 0;
665         result.mProtocol               = TruncateProtocolInType(result.mType);
666         result.mPort                   = port;
667         result.mAddressType            = ToAddressType(protocol);
668
669         if (address)
670         {
671             switch (address->proto)
672             {
673             case AVAHI_PROTO_INET:
674                 struct in_addr addr4;
675
676                 memcpy(&addr4, &(address->data.ipv4), sizeof(addr4));
677                 result.mAddress.SetValue(chip::Inet::IPAddress::FromIPv4(addr4));
678                 break;
679             case AVAHI_PROTO_INET6:
680                 struct in6_addr addr6;
681
682                 memcpy(&addr6, &(address->data.ipv6), sizeof(addr6));
683                 result.mAddress.SetValue(chip::Inet::IPAddress::FromIPv6(addr6));
684                 break;
685             default:
686                 break;
687             }
688         }
689
690         while (txt != nullptr)
691         {
692             for (size_t i = 0; i < txt->size; i++)
693             {
694                 if (txt->text[i] == '=')
695                 {
696                     txt->text[i] = '\0';
697                     textEntries.push_back(TextEntry{ reinterpret_cast<char *>(txt->text), &txt->text[i + 1], txt->size - i - 1 });
698                     break;
699                 }
700             }
701             txt = txt->next;
702         }
703
704         if (!textEntries.empty())
705         {
706             result.mTextEntries = textEntries.data();
707         }
708         result.mTextEntrySize = textEntries.size();
709
710         context->mCallback(context->mContext, &result, CHIP_NO_ERROR);
711         break;
712     }
713
714     avahi_service_resolver_free(resolver);
715     chip::Platform::Delete(context);
716 }
717
718 MdnsAvahi::~MdnsAvahi()
719 {
720     if (mGroup)
721     {
722         avahi_entry_group_free(mGroup);
723     }
724     if (mClient)
725     {
726         avahi_client_free(mClient);
727     }
728 }
729
730 void UpdateMdnsDataset(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet, int & maxFd, timeval & timeout)
731 {
732     MdnsAvahi::GetInstance().GetPoller().UpdateFdSet(readFdSet, writeFdSet, errorFdSet, maxFd, timeout);
733 }
734
735 void ProcessMdns(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet)
736 {
737     MdnsAvahi::GetInstance().GetPoller().Process(readFdSet, writeFdSet, errorFdSet);
738 }
739
740 CHIP_ERROR ChipMdnsInit(MdnsAsyncReturnCallback initCallback, MdnsAsyncReturnCallback errorCallback, void * context)
741 {
742     return MdnsAvahi::GetInstance().Init(initCallback, errorCallback, context);
743 }
744
745 CHIP_ERROR ChipMdnsSetHostname(const char * hostname)
746 {
747     return MdnsAvahi::GetInstance().SetHostname(hostname);
748 }
749
750 CHIP_ERROR ChipMdnsPublishService(const MdnsService * service)
751 {
752     return MdnsAvahi::GetInstance().PublishService(*service);
753 }
754
755 CHIP_ERROR ChipMdnsStopPublish()
756 {
757     return MdnsAvahi::GetInstance().StopPublish();
758 }
759
760 CHIP_ERROR ChipMdnsBrowse(const char * type, MdnsServiceProtocol protocol, chip::Inet::IPAddressType addressType,
761                           chip::Inet::InterfaceId interface, MdnsBrowseCallback callback, void * context)
762 {
763     return MdnsAvahi::GetInstance().Browse(type, protocol, addressType, interface, callback, context);
764 }
765
766 CHIP_ERROR ChipMdnsResolve(MdnsService * browseResult, chip::Inet::InterfaceId interface, MdnsResolveCallback callback,
767                            void * context)
768
769 {
770     CHIP_ERROR error;
771
772     if (browseResult != nullptr)
773     {
774         error = MdnsAvahi::GetInstance().Resolve(browseResult->mName, browseResult->mType, browseResult->mProtocol,
775                                                  browseResult->mAddressType, interface, callback, context);
776     }
777     else
778     {
779         error = CHIP_ERROR_INVALID_ARGUMENT;
780     }
781     return error;
782 }
783
784 } // namespace Mdns
785 } // namespace chip