3 * Copyright (c) 2021 Project CHIP Authors
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "MdnsError.h"
26 #include <platform/CHIPDeviceLayer.h>
27 #include <support/CHIPMem.h>
28 #include <support/CodeUtils.h>
29 #include <support/SafeInt.h>
30 #include <support/logging/CHIPLogging.h>
32 using namespace chip::Mdns;
36 constexpr const char * kLocalDomain = "local.";
37 constexpr const char * kProtocolTcp = "._tcp";
38 constexpr const char * kProtocolUdp = "._udp";
39 constexpr uint8_t kMdnsKeyMaxSize = 32;
41 bool IsSupportedProtocol(MdnsServiceProtocol protocol)
43 return (protocol == MdnsServiceProtocol::kMdnsProtocolUdp) || (protocol == MdnsServiceProtocol::kMdnsProtocolTcp);
46 uint32_t GetInterfaceId(chip::Inet::InterfaceId interfaceId)
48 return INET_NULL_INTERFACEID ? 0 : interfaceId;
51 std::string GetFullType(const char * type, MdnsServiceProtocol protocol)
53 std::ostringstream typeBuilder;
55 typeBuilder << (protocol == MdnsServiceProtocol::kMdnsProtocolUdp ? kProtocolUdp : kProtocolTcp);
56 return typeBuilder.str();
64 MdnsContexts MdnsContexts::sInstance;
66 void MdnsContexts::Delete(GenericContext * context)
68 if (context->type == ContextType::GetAddrInfo)
70 GetAddrInfoContext * addrInfoContext = reinterpret_cast<GetAddrInfoContext *>(context);
71 std::vector<TextEntry>::iterator textEntry;
72 for (textEntry = addrInfoContext->textEntries.begin(); textEntry != addrInfoContext->textEntries.end(); textEntry++)
74 free(const_cast<char *>(textEntry->mKey));
75 free(const_cast<uint8_t *>(textEntry->mData));
79 DNSServiceRefDeallocate(context->serviceRef);
80 chip::Platform::Delete(context);
83 MdnsContexts::~MdnsContexts()
85 std::vector<GenericContext *>::const_iterator iter = mContexts.cbegin();
86 while (iter != mContexts.cend())
89 mContexts.erase(iter);
93 CHIP_ERROR MdnsContexts::Add(GenericContext * context, DNSServiceRef sdRef)
95 VerifyOrReturnError(context != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
96 VerifyOrReturnError(sdRef != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
98 context->serviceRef = sdRef;
99 mContexts.push_back(context);
101 return CHIP_NO_ERROR;
104 CHIP_ERROR MdnsContexts::Remove(GenericContext * context)
108 std::vector<GenericContext *>::const_iterator iter = mContexts.cbegin();
109 while (iter != mContexts.cend())
111 if (*iter != context)
118 mContexts.erase(iter);
123 return found ? CHIP_NO_ERROR : CHIP_ERROR_KEY_NOT_FOUND;
126 CHIP_ERROR MdnsContexts::Removes(ContextType type)
130 std::vector<GenericContext *>::const_iterator iter = mContexts.cbegin();
131 while (iter != mContexts.cend())
133 if ((*iter)->type != type)
140 mContexts.erase(iter);
144 return found ? CHIP_NO_ERROR : CHIP_ERROR_KEY_NOT_FOUND;
147 CHIP_ERROR MdnsContexts::Get(ContextType type, GenericContext * context)
150 std::vector<GenericContext *>::iterator iter;
152 for (iter = mContexts.begin(); iter != mContexts.end(); iter++)
154 if ((*iter)->type == type)
157 if (context != nullptr)
164 return found ? CHIP_NO_ERROR : CHIP_ERROR_KEY_NOT_FOUND;
167 void MdnsContexts::PrepareSelect(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet, int & maxFd, timeval & timeout)
169 std::vector<DNSServiceRef> serviceRefs(mContexts.size());
170 std::transform(mContexts.begin(), mContexts.end(), serviceRefs.begin(),
171 [](GenericContext * context) { return context->serviceRef; });
173 std::vector<DNSServiceRef>::iterator iter;
174 for (iter = serviceRefs.begin(); iter != serviceRefs.end(); iter++)
176 int fd = DNSServiceRefSockFD(*iter);
177 FD_SET(fd, &readFdSet);
185 void MdnsContexts::HandleSelectResult(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet)
187 std::vector<DNSServiceRef> serviceRefs(mContexts.size());
188 std::transform(mContexts.begin(), mContexts.end(), serviceRefs.begin(),
189 [](GenericContext * context) { return context->serviceRef; });
191 std::vector<DNSServiceRef>::iterator iter;
192 for (iter = serviceRefs.begin(); iter != serviceRefs.end(); iter++)
194 int fd = DNSServiceRefSockFD(*iter);
195 if (FD_ISSET(fd, &readFdSet))
197 DNSServiceProcessResult(*iter);
202 CHIP_ERROR PopulateTextRecord(TXTRecordRef * record, char * buffer, uint16_t bufferLen, TextEntry * textEntries,
203 size_t textEntrySize)
205 VerifyOrReturnError(textEntrySize <= kMdnsTextMaxSize, CHIP_ERROR_INVALID_ARGUMENT);
207 DNSServiceErrorType err;
208 TXTRecordCreate(record, bufferLen, buffer);
210 for (size_t i = 0; i < textEntrySize; i++)
212 TextEntry entry = textEntries[i];
213 VerifyOrReturnError(chip::CanCastTo<uint8_t>(entry.mDataSize), CHIP_ERROR_INVALID_ARGUMENT);
215 err = TXTRecordSetValue(record, entry.mKey, static_cast<uint8_t>(entry.mDataSize), entry.mData);
216 VerifyOrReturnError(err == kDNSServiceErr_NoError, CHIP_ERROR_INVALID_ARGUMENT);
219 return CHIP_NO_ERROR;
222 bool CheckForSuccess(GenericContext * context, const char * name, DNSServiceErrorType err, bool useCallback = false)
224 if (context == nullptr)
226 ChipLogError(DeviceLayer, "%s (%s)", name, "Mdns context is null.");
230 if (kDNSServiceErr_NoError != err)
232 ChipLogError(DeviceLayer, "%s (%s)", name, Error::ToString(err));
236 switch (context->type)
238 case ContextType::Register:
239 // Nothing special to do. Maybe ChipMdnsPublishService should take a callback ?
241 case ContextType::Browse: {
242 BrowseContext * browseContext = reinterpret_cast<BrowseContext *>(context);
243 browseContext->callback(browseContext->context, nullptr, 0, CHIP_ERROR_INTERNAL);
246 case ContextType::Resolve: {
247 ResolveContext * resolveContext = reinterpret_cast<ResolveContext *>(context);
248 resolveContext->callback(resolveContext->context, nullptr, CHIP_ERROR_INTERNAL);
251 case ContextType::GetAddrInfo: {
252 GetAddrInfoContext * resolveContext = reinterpret_cast<GetAddrInfoContext *>(context);
253 resolveContext->callback(resolveContext->context, nullptr, CHIP_ERROR_INTERNAL);
259 if (CHIP_ERROR_KEY_NOT_FOUND == MdnsContexts::GetInstance().Remove(context))
261 chip::Platform::Delete(context);
270 static void OnRegister(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType err, const char * name, const char * type,
271 const char * domain, void * context)
273 RegisterContext * sdCtx = reinterpret_cast<RegisterContext *>(context);
274 VerifyOrReturn(CheckForSuccess(sdCtx, __func__, err));
276 ChipLogDetail(DeviceLayer, "Mdns: %s name: %s, type: %s, domain: %s, flags: %d", __func__, name, type, domain, flags);
279 CHIP_ERROR Register(uint32_t interfaceId, const char * type, const char * name, uint16_t port, TXTRecordRef * recordRef)
281 DNSServiceErrorType err;
283 RegisterContext * sdCtx = nullptr;
285 uint16_t recordLen = TXTRecordGetLength(recordRef);
286 const void * recordBytesPtr = TXTRecordGetBytesPtr(recordRef);
288 if (CHIP_NO_ERROR == MdnsContexts::GetInstance().Get(ContextType::Register, sdCtx))
290 err = DNSServiceUpdateRecord(sdCtx->serviceRef, NULL, 0 /* flags */, recordLen, recordBytesPtr, 0 /* ttl */);
291 TXTRecordDeallocate(recordRef);
292 VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err), CHIP_ERROR_INTERNAL);
293 return CHIP_NO_ERROR;
296 sdCtx = chip::Platform::New<RegisterContext>(nullptr);
297 err = DNSServiceRegister(&sdRef, 0 /* flags */, interfaceId, name, type, kLocalDomain, NULL, port, recordLen, recordBytesPtr,
299 TXTRecordDeallocate(recordRef);
301 VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err), CHIP_ERROR_INTERNAL);
303 return MdnsContexts::GetInstance().Add(sdCtx, sdRef);
306 void OnBrowseAdd(BrowseContext * context, const char * name, const char * type, const char * domain, uint32_t interfaceId)
308 ChipLogDetail(DeviceLayer, "Mdns: %s name: %s, type: %s, domain: %s, interface: %d", __func__, name, type, domain,
311 VerifyOrReturn(strcmp(kLocalDomain, domain) == 0);
313 char * tokens = strdup(type);
314 char * regtype = strtok(tokens, ".");
317 MdnsService service = {};
318 service.mInterface = interfaceId;
319 service.mProtocol = context->protocol;
321 strncpy(service.mName, name, sizeof(service.mName));
322 service.mName[kMdnsNameMaxSize] = 0;
324 strncpy(service.mType, regtype, sizeof(service.mType));
325 service.mType[kMdnsTypeMaxSize] = 0;
327 context->services.push_back(service);
330 void OnBrowseRemove(BrowseContext * context, const char * name, const char * type, const char * domain, uint32_t interfaceId)
332 ChipLogDetail(DeviceLayer, "Mdns: %s name: %s, type: %s, domain: %s, interface: %d", __func__, name, type, domain,
335 VerifyOrReturn(strcmp(kLocalDomain, domain) == 0);
337 std::remove_if(context->services.begin(), context->services.end(), [name, type, interfaceId](const MdnsService & service) {
338 return strcmp(name, service.mName) == 0 && type == GetFullType(service.mType, service.mProtocol) &&
339 service.mInterface == interfaceId;
343 static void OnBrowse(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, DNSServiceErrorType err, const char * name,
344 const char * type, const char * domain, void * context)
346 BrowseContext * sdCtx = reinterpret_cast<BrowseContext *>(context);
347 VerifyOrReturn(CheckForSuccess(sdCtx, __func__, err, true));
349 (flags & kDNSServiceFlagsAdd) ? OnBrowseAdd(sdCtx, name, type, domain, interfaceId)
350 : OnBrowseRemove(sdCtx, name, type, domain, interfaceId);
352 if (!(flags & kDNSServiceFlagsMoreComing))
354 sdCtx->callback(sdCtx->context, sdCtx->services.data(), sdCtx->services.size(), CHIP_NO_ERROR);
355 MdnsContexts::GetInstance().Remove(sdCtx);
359 CHIP_ERROR Browse(void * context, MdnsBrowseCallback callback, uint32_t interfaceId, const char * type,
360 MdnsServiceProtocol protocol)
362 DNSServiceErrorType err;
364 BrowseContext * sdCtx;
366 sdCtx = chip::Platform::New<BrowseContext>(context, callback, protocol);
367 err = DNSServiceBrowse(&sdRef, 0 /* flags */, interfaceId, type, kLocalDomain, OnBrowse, sdCtx);
368 VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err), CHIP_ERROR_INTERNAL);
370 return MdnsContexts::GetInstance().Add(sdCtx, sdRef);
373 static void OnGetAddrInfo(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, DNSServiceErrorType err,
374 const char * hostname, const struct sockaddr * address, uint32_t ttl, void * context)
376 GetAddrInfoContext * sdCtx = reinterpret_cast<GetAddrInfoContext *>(context);
377 VerifyOrReturn(CheckForSuccess(sdCtx, __func__, err, true));
379 ChipLogDetail(DeviceLayer, "Mdns: %s hostname:%s", __func__, hostname);
381 MdnsService service = {};
382 service.mPort = sdCtx->port;
383 service.mTextEntries = sdCtx->textEntries.empty() ? nullptr : sdCtx->textEntries.data();
384 service.mTextEntrySize = sdCtx->textEntries.empty() ? 0 : sdCtx->textEntries.size();
385 service.mAddress.SetValue(chip::Inet::IPAddress::FromSockAddr(*address));
387 sdCtx->callback(sdCtx->context, &service, CHIP_NO_ERROR);
388 MdnsContexts::GetInstance().Remove(sdCtx);
391 CHIP_ERROR GetAddrInfo(void * context, MdnsResolveCallback callback, uint32_t interfaceId, const char * hostname, uint16_t port,
392 uint16_t txtLen, const unsigned char * txtRecord)
394 DNSServiceErrorType err;
396 GetAddrInfoContext * sdCtx;
398 sdCtx = chip::Platform::New<GetAddrInfoContext>(context, callback, port);
400 char key[kMdnsKeyMaxSize];
401 char value[kMdnsTextMaxSize];
403 const void * valuePtr;
405 uint16_t recordCount = TXTRecordGetCount(txtLen, txtRecord);
406 for (uint16_t i = 0; i < recordCount; i++)
408 err = TXTRecordGetItemAtIndex(txtLen, txtRecord, i, kMdnsKeyMaxSize, key, &valueLen, &valuePtr);
409 VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err, true), CHIP_ERROR_INTERNAL);
411 strncpy(value, reinterpret_cast<const char *>(valuePtr), valueLen);
414 sdCtx->textEntries.push_back(TextEntry{ strdup(key), reinterpret_cast<const uint8_t *>(strdup(value)), valueLen });
417 err = DNSServiceGetAddrInfo(&sdRef, 0 /* flags */, interfaceId, kDNSServiceProtocol_IPv4, hostname, OnGetAddrInfo, sdCtx);
418 VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err, true), CHIP_ERROR_INTERNAL);
420 return MdnsContexts::GetInstance().Add(sdCtx, sdRef);
423 static void OnResolve(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, DNSServiceErrorType err,
424 const char * fullname, const char * hostname, uint16_t port, uint16_t txtLen, const unsigned char * txtRecord,
427 ResolveContext * sdCtx = reinterpret_cast<ResolveContext *>(context);
428 VerifyOrReturn(CheckForSuccess(sdCtx, __func__, err, true));
430 GetAddrInfo(sdCtx->context, sdCtx->callback, interfaceId, hostname, port, txtLen, txtRecord);
431 MdnsContexts::GetInstance().Remove(sdCtx);
434 CHIP_ERROR Resolve(void * context, MdnsResolveCallback callback, uint32_t interfaceId, const char * type, const char * name)
436 DNSServiceErrorType err;
438 ResolveContext * sdCtx;
440 sdCtx = chip::Platform::New<ResolveContext>(context, callback);
441 err = DNSServiceResolve(&sdRef, 0 /* flags */, interfaceId, name, type, kLocalDomain, OnResolve, sdCtx);
442 VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err), CHIP_ERROR_INTERNAL);
444 return MdnsContexts::GetInstance().Add(sdCtx, sdRef);
447 CHIP_ERROR ChipMdnsInit(MdnsAsyncReturnCallback successCallback, MdnsAsyncReturnCallback errorCallback, void * context)
449 VerifyOrReturnError(successCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
450 VerifyOrReturnError(errorCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
452 successCallback(context, CHIP_NO_ERROR);
453 return CHIP_NO_ERROR;
456 CHIP_ERROR ChipMdnsSetHostname(const char * hostname)
458 MdnsContexts::GetInstance().SetHostname(hostname);
459 return CHIP_NO_ERROR;
462 CHIP_ERROR ChipMdnsPublishService(const MdnsService * service)
464 VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
465 VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT);
467 std::string regtype = GetFullType(service->mType, service->mProtocol);
468 uint32_t interfaceId = GetInterfaceId(service->mInterface);
471 char buffer[kMdnsTextMaxSize];
472 ReturnErrorOnFailure(PopulateTextRecord(&record, buffer, sizeof(buffer), service->mTextEntries, service->mTextEntrySize));
474 return Register(interfaceId, regtype.c_str(), service->mName, service->mPort, &record);
477 CHIP_ERROR ChipMdnsStopPublish()
479 return MdnsContexts::GetInstance().Removes(ContextType::Register);
482 CHIP_ERROR ChipMdnsBrowse(const char * type, MdnsServiceProtocol protocol, chip::Inet::IPAddressType addressType,
483 chip::Inet::InterfaceId interface, MdnsBrowseCallback callback, void * context)
485 VerifyOrReturnError(type != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
486 VerifyOrReturnError(callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
487 VerifyOrReturnError(IsSupportedProtocol(protocol), CHIP_ERROR_INVALID_ARGUMENT);
489 std::string regtype = GetFullType(type, protocol);
490 uint32_t interfaceId = GetInterfaceId(interface);
492 return Browse(context, callback, interfaceId, regtype.c_str(), protocol);
495 CHIP_ERROR ChipMdnsResolve(MdnsService * service, chip::Inet::InterfaceId interface, MdnsResolveCallback callback, void * context)
497 VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
498 VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT);
500 std::string regtype = GetFullType(service->mType, service->mProtocol);
501 uint32_t interfaceId = GetInterfaceId(service->mInterface);
503 return Resolve(context, callback, interfaceId, regtype.c_str(), service->mName);
506 void UpdateMdnsDataset(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet, int & maxFd, timeval & timeout)
508 MdnsContexts::GetInstance().PrepareSelect(readFdSet, writeFdSet, errorFdSet, maxFd, timeout);
511 void ProcessMdns(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet)
513 MdnsContexts::GetInstance().HandleSelectResult(readFdSet, writeFdSet, errorFdSet);