3 * Copyright (c) 2020 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.
18 #include "Discovery_ImplPlatform.h"
22 #include "ServiceNaming.h"
23 #include "lib/core/CHIPSafeCasts.h"
24 #include "lib/mdns/platform/Mdns.h"
25 #include "lib/support/logging/CHIPLogging.h"
26 #include "platform/CHIPDeviceConfig.h"
27 #include "platform/CHIPDeviceLayer.h"
28 #include "setup_payload/AdditionalDataPayloadGenerator.h"
29 #include "support/CodeUtils.h"
30 #include "support/ErrorStr.h"
31 #include "support/RandUtils.h"
35 uint8_t HexToInt(char c)
37 if ('0' <= c && c <= '9')
39 return static_cast<uint8_t>(c - '0');
41 else if ('a' <= c && c <= 'f')
43 return static_cast<uint8_t>(0x0a + c - 'a');
45 else if ('A' <= c && c <= 'F')
47 return static_cast<uint8_t>(0x0a + c - 'A');
53 constexpr uint64_t kUndefinedNodeId = 0;
60 DiscoveryImplPlatform DiscoveryImplPlatform::sManager;
62 DiscoveryImplPlatform::DiscoveryImplPlatform() = default;
64 CHIP_ERROR DiscoveryImplPlatform::Init()
66 if (!mMdnsInitialized)
68 ReturnErrorOnFailure(ChipMdnsInit(HandleMdnsInit, HandleMdnsError, this));
69 mCommissionInstanceName = GetRandU64();
70 mMdnsInitialized = true;
76 CHIP_ERROR DiscoveryImplPlatform::Start(Inet::InetLayer * inetLayer, uint16_t port)
78 ReturnErrorOnFailure(Init());
80 CHIP_ERROR error = ChipMdnsStopPublish();
82 if (error != CHIP_NO_ERROR)
84 ChipLogError(Discovery, "Failed to initialize platform mdns: %s", ErrorStr(error));
87 error = SetupHostname();
88 if (error != CHIP_NO_ERROR)
90 ChipLogError(Discovery, "Failed to setup mdns hostname: %s", ErrorStr(error));
96 void DiscoveryImplPlatform::HandleMdnsInit(void * context, CHIP_ERROR initError)
98 DiscoveryImplPlatform * publisher = static_cast<DiscoveryImplPlatform *>(context);
100 if (initError == CHIP_NO_ERROR)
102 publisher->mMdnsInitialized = true;
106 ChipLogError(Discovery, "mDNS initialization failed with %s", chip::ErrorStr(initError));
107 publisher->mMdnsInitialized = false;
111 void DiscoveryImplPlatform::HandleMdnsError(void * context, CHIP_ERROR error)
113 DiscoveryImplPlatform * publisher = static_cast<DiscoveryImplPlatform *>(context);
114 if (error == CHIP_ERROR_FORCED_RESET)
116 if (publisher->mIsOperationalPublishing)
118 publisher->Advertise(publisher->mOperationalAdvertisingParams);
120 if (publisher->mIsCommissionalPublishing)
122 publisher->Advertise(publisher->mCommissioningdvertisingParams);
127 ChipLogError(Discovery, "mDNS error: %s", chip::ErrorStr(error));
131 #if CHIP_ENABLE_ROTATING_DEVICE_ID
132 CHIP_ERROR DiscoveryImplPlatform::GenerateRotatingDeviceId(char rotatingDeviceIdHexBuffer[], size_t & rotatingDeviceIdHexBufferSize)
134 CHIP_ERROR error = CHIP_NO_ERROR;
135 char serialNumber[chip::DeviceLayer::ConfigurationManager::kMaxSerialNumberLength + 1];
136 size_t serialNumberSize = 0;
137 uint16_t lifetimeCounter = 0;
138 SuccessOrExit(error =
139 chip::DeviceLayer::ConfigurationMgr().GetSerialNumber(serialNumber, sizeof(serialNumber), serialNumberSize));
140 SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetLifetimeCounter(lifetimeCounter));
141 SuccessOrExit(error = AdditionalDataPayloadGenerator().generateRotatingDeviceId(
142 lifetimeCounter, serialNumber, serialNumberSize, rotatingDeviceIdHexBuffer, rotatingDeviceIdHexBufferSize,
143 rotatingDeviceIdHexBufferSize));
149 CHIP_ERROR DiscoveryImplPlatform::SetupHostname()
151 #if CHIP_DEVICE_CONFIG_ENABLE_THREAD
152 static char hostname[17]; // Hostname is 64-bit EUI-64 expressed as a 16-character hexadecimal string.
154 chip::DeviceLayer::ThreadStackMgr().GetFactoryAssignedEUI64(eui64);
155 snprintf(hostname, sizeof(hostname), "%02X%02X%02X%02X%02X%02X%02X%02X", eui64[0], eui64[1], eui64[2], eui64[3], eui64[4],
156 eui64[5], eui64[6], eui64[7]);
158 uint8_t mac[6]; // 6 byte wifi mac
159 char hostname[13]; // Hostname will be the hex representation of mac.
161 ReturnErrorOnFailure(chip::DeviceLayer::ConfigurationMgr().GetPrimaryWiFiMACAddress(mac));
162 for (size_t i = 0; i < sizeof(mac); i++)
164 snprintf(&hostname[i * 2], sizeof(hostname) - i * 2, "%02X", mac[i]);
168 ReturnErrorOnFailure(ChipMdnsSetHostname(hostname));
170 return CHIP_NO_ERROR;
173 CHIP_ERROR DiscoveryImplPlatform::Advertise(const CommissionAdvertisingParameters & params)
175 CHIP_ERROR error = CHIP_NO_ERROR;
177 char discriminatorBuf[6];
178 char vendorProductBuf[12];
179 char pairingInstrBuf[128];
180 TextEntry textEntries[4];
181 size_t textEntrySize = 0;
182 char shortDiscriminatorSubtype[6];
183 char longDiscriminatorSubtype[8];
184 char vendorSubType[8];
185 const char * subTypes[3];
186 size_t subTypeSize = 0;
187 #if CHIP_ENABLE_ROTATING_DEVICE_ID
188 char rotatingDeviceIdHexBuffer[RotatingDeviceId::kHexMaxLength];
189 size_t rotatingDeviceIdHexBufferSize = 0;
192 if (!mMdnsInitialized)
194 return CHIP_ERROR_INCORRECT_STATE;
196 snprintf(service.mName, sizeof(service.mName), "%016" PRIX64, mCommissionInstanceName);
197 if (params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissioning)
199 strncpy(service.mType, "_chipc", sizeof(service.mType));
203 strncpy(service.mType, "_chipd", sizeof(service.mType));
205 service.mProtocol = MdnsServiceProtocol::kMdnsProtocolUdp;
207 snprintf(discriminatorBuf, sizeof(discriminatorBuf), "%04u", params.GetLongDiscriminator());
208 textEntries[textEntrySize++] = { "D", reinterpret_cast<const uint8_t *>(discriminatorBuf),
209 strnlen(discriminatorBuf, sizeof(discriminatorBuf)) };
210 if (params.GetVendorId().HasValue())
212 if (params.GetProductId().HasValue())
214 snprintf(vendorProductBuf, sizeof(vendorProductBuf), "%u+%u", params.GetVendorId().Value(),
215 params.GetProductId().Value());
219 snprintf(vendorProductBuf, sizeof(vendorProductBuf), "%u", params.GetVendorId().Value());
221 textEntries[textEntrySize++] = { "VP", reinterpret_cast<const uint8_t *>(vendorProductBuf),
222 strnlen(vendorProductBuf, sizeof(vendorProductBuf)) };
224 #if CHIP_ENABLE_ROTATING_DEVICE_ID
225 if (textEntrySize < ArraySize(textEntries))
227 ReturnErrorOnFailure(GenerateRotatingDeviceId(rotatingDeviceIdHexBuffer, rotatingDeviceIdHexBufferSize));
228 // Rotating Device ID
230 textEntries[textEntrySize++] = { "RI", Uint8::from_const_char(rotatingDeviceIdHexBuffer), rotatingDeviceIdHexBufferSize };
234 return CHIP_ERROR_INVALID_LIST_LENGTH;
237 if (params.GetPairingHint().HasValue() && params.GetPairingInstr().HasValue())
239 snprintf(pairingInstrBuf, sizeof(pairingInstrBuf), "%s+%u", params.GetPairingInstr().Value(),
240 params.GetPairingHint().Value());
241 textEntries[textEntrySize++] = { "P", reinterpret_cast<const uint8_t *>(pairingInstrBuf),
242 strnlen(pairingInstrBuf, sizeof(pairingInstrBuf)) };
245 snprintf(shortDiscriminatorSubtype, sizeof(shortDiscriminatorSubtype), "_S%03u", params.GetShortDiscriminator());
246 subTypes[subTypeSize++] = shortDiscriminatorSubtype;
247 snprintf(longDiscriminatorSubtype, sizeof(longDiscriminatorSubtype), "_L%04u", params.GetLongDiscriminator());
248 subTypes[subTypeSize++] = longDiscriminatorSubtype;
249 if (params.GetVendorId().HasValue())
251 snprintf(vendorSubType, sizeof(vendorSubType), "_V%u", params.GetVendorId().Value());
252 subTypes[subTypeSize++] = vendorSubType;
255 service.mTextEntries = textEntries;
256 service.mTextEntrySize = textEntrySize;
257 service.mPort = CHIP_PORT;
258 service.mInterface = INET_NULL_INTERFACEID;
259 service.mSubTypes = subTypes;
260 service.mSubTypeSize = subTypeSize;
261 service.mAddressType = Inet::kIPAddressType_Any;
262 error = ChipMdnsPublishService(&service);
267 CHIP_ERROR DiscoveryImplPlatform::Advertise(const OperationalAdvertisingParameters & params)
270 CHIP_ERROR error = CHIP_NO_ERROR;
272 mOperationalAdvertisingParams = params;
273 // TODO: There may be multilple device/fabrid ids after multi-admin.
275 ReturnErrorOnFailure(MakeInstanceName(service.mName, sizeof(service.mName), params.GetFabricId(), params.GetNodeId()));
276 strncpy(service.mType, "_chip", sizeof(service.mType));
277 service.mProtocol = MdnsServiceProtocol::kMdnsProtocolTcp;
278 service.mPort = CHIP_PORT;
279 service.mTextEntries = nullptr;
280 service.mTextEntrySize = 0;
281 service.mInterface = INET_NULL_INTERFACEID;
282 service.mAddressType = Inet::kIPAddressType_Any;
283 error = ChipMdnsPublishService(&service);
288 CHIP_ERROR DiscoveryImplPlatform::StopPublishDevice()
290 mIsOperationalPublishing = false;
291 mIsCommissionalPublishing = false;
292 return ChipMdnsStopPublish();
295 CHIP_ERROR DiscoveryImplPlatform::SetResolverDelegate(ResolverDelegate * delegate)
297 VerifyOrReturnError(delegate == nullptr || mResolverDelegate == nullptr, CHIP_ERROR_INCORRECT_STATE);
298 mResolverDelegate = delegate;
299 return CHIP_NO_ERROR;
302 CHIP_ERROR DiscoveryImplPlatform::ResolveNodeId(uint64_t nodeId, uint64_t fabricId, Inet::IPAddressType type)
304 ReturnErrorOnFailure(Init());
308 ReturnErrorOnFailure(MakeInstanceName(service.mName, sizeof(service.mName), fabricId, nodeId));
309 strncpy(service.mType, "_chip", sizeof(service.mType));
310 service.mProtocol = MdnsServiceProtocol::kMdnsProtocolTcp;
311 service.mAddressType = type;
312 return ChipMdnsResolve(&service, INET_NULL_INTERFACEID, HandleNodeIdResolve, this);
315 void DiscoveryImplPlatform::HandleNodeIdResolve(void * context, MdnsService * result, CHIP_ERROR error)
317 DiscoveryImplPlatform * mgr = static_cast<DiscoveryImplPlatform *>(context);
319 if (mgr->mResolverDelegate == nullptr)
324 if (error != CHIP_NO_ERROR)
326 ChipLogError(Discovery, "Node ID resolved failed with %s", chip::ErrorStr(error));
327 mgr->mResolverDelegate->OnNodeIdResolutionFailed(kUndefinedNodeId, error);
331 if (result == nullptr)
333 ChipLogError(Discovery, "Node ID resolve not found");
334 mgr->mResolverDelegate->OnNodeIdResolutionFailed(kUndefinedNodeId, CHIP_ERROR_UNKNOWN_RESOURCE_ID);
338 // Parse '%x-%x' from the name
340 bool deliminatorFound = false;
342 for (size_t i = 0; i < sizeof(result->mName) && result->mName[i] != 0; i++)
344 if (result->mName[i] == '-')
346 deliminatorFound = true;
351 uint8_t val = HexToInt(result->mName[i]);
353 if (val == UINT8_MAX)
359 nodeId = nodeId * 16 + val;
364 ResolvedNodeData nodeData;
365 nodeData.mInterfaceId = result->mInterface;
366 nodeData.mAddress = result->mAddress.ValueOr({});
367 nodeData.mPort = result->mPort;
369 if (deliminatorFound)
371 ChipLogProgress(Discovery, "Node ID resolved for %" PRIX64, nodeId);
372 mgr->mResolverDelegate->OnNodeIdResolved(nodeId, nodeData);
376 ChipLogProgress(Discovery, "Invalid service entry from node %" PRIX64, nodeId);
377 mgr->mResolverDelegate->OnNodeIdResolved(kUndefinedNodeId, nodeData);
381 DiscoveryImplPlatform & DiscoveryImplPlatform::GetInstance()
386 ServiceAdvertiser & chip::Mdns::ServiceAdvertiser::Instance()
388 return DiscoveryImplPlatform::GetInstance();
391 Resolver & chip::Mdns::Resolver::Instance()
393 return DiscoveryImplPlatform::GetInstance();