40c6cf25169a78ed88760253e37527beb3ecccdb
[platform/upstream/connectedhomeip.git] / src / lib / mdns / DiscoveryManager.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 "DiscoveryManager.h"
19
20 #include <inttypes.h>
21
22 #include "lib/mdns/platform/Mdns.h"
23 #include "lib/support/logging/CHIPLogging.h"
24 #include "platform/CHIPDeviceConfig.h"
25 #include "platform/CHIPDeviceLayer.h"
26 #include "support/CodeUtils.h"
27 #include "support/ErrorStr.h"
28 #include "support/RandUtils.h"
29
30 #if CHIP_ENABLE_MDNS
31
32 namespace {
33
34 uint8_t HexToInt(char c)
35 {
36     if ('0' <= c && c <= '9')
37     {
38         return static_cast<uint8_t>(c - '0');
39     }
40     else if ('a' <= c && c <= 'f')
41     {
42         return static_cast<uint8_t>(0x0a + c - 'a');
43     }
44     else if ('A' <= c && c <= 'F')
45     {
46         return static_cast<uint8_t>(0x0a + c - 'A');
47     }
48
49     return UINT8_MAX;
50 }
51
52 constexpr uint64_t kUndefinedNodeId = 0;
53
54 } // namespace
55 #endif
56
57 namespace chip {
58 namespace Mdns {
59
60 DiscoveryManager DiscoveryManager::sManager;
61
62 CHIP_ERROR DiscoveryManager::Init()
63 {
64 #if CHIP_ENABLE_MDNS
65     CHIP_ERROR error;
66
67     mUnprovisionedInstanceName = GetRandU64();
68     SuccessOrExit(error = ChipMdnsInit(HandleMdnsInit, HandleMdnsError, this));
69 exit:
70     return error;
71 #else
72     return CHIP_NO_ERROR;
73 #endif // CHIP_ENABLE_MDNS
74 }
75
76 void DiscoveryManager::HandleMdnsInit(void * context, CHIP_ERROR initError)
77 {
78 #if CHIP_ENABLE_MDNS
79     DiscoveryManager * publisher = static_cast<DiscoveryManager *>(context);
80
81     if (initError == CHIP_NO_ERROR)
82     {
83         publisher->mMdnsInitialized = true;
84     }
85     else
86     {
87         ChipLogError(Discovery, "mDNS initialization failed with %s", chip::ErrorStr(initError));
88     }
89 #endif // CHIP_ENABLE_MDNS
90 }
91
92 void DiscoveryManager::HandleMdnsError(void * context, CHIP_ERROR error)
93 {
94 #if CHIP_ENABLE_MDNS
95     DiscoveryManager * publisher = static_cast<DiscoveryManager *>(context);
96     if (error == CHIP_ERROR_FORCED_RESET && publisher->mIsPublishing)
97     {
98         publisher->StartPublishDevice();
99     }
100     else
101     {
102         ChipLogError(Discovery, "mDNS error: %s", chip::ErrorStr(error));
103     }
104 #endif // CHIP_ENABLE_MDNS
105 }
106
107 CHIP_ERROR DiscoveryManager::StartPublishDevice(chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface)
108 {
109 #if CHIP_ENABLE_MDNS
110     CHIP_ERROR error;
111
112     // TODO: after multi-admin is decided we may need to publish both _chipc._udp and _chip._tcp service
113     if (!mIsPublishing)
114     {
115         SuccessOrExit(error = SetupHostname());
116     }
117     else if (chip::DeviceLayer::ConfigurationMgr().IsFullyProvisioned() != mIsPublishingProvisionedDevice)
118     {
119         SuccessOrExit(error = StopPublishDevice());
120         // Set hostname again in case the mac address changes when shifting from soft-AP to station
121         SuccessOrExit(error = SetupHostname());
122     }
123     mIsPublishingProvisionedDevice = chip::DeviceLayer::ConfigurationMgr().IsFullyProvisioned();
124
125     if (mIsPublishingProvisionedDevice)
126     {
127         error = PublishProvisionedDevice(addressType, interface);
128     }
129     else
130     {
131         error = PublishUnprovisionedDevice(addressType, interface);
132     }
133     mIsPublishing = true;
134 exit:
135     return error;
136 #else
137     return CHIP_ERROR_NOT_IMPLEMENTED;
138 #endif // CHIP_ENABLE_MDNS
139 }
140
141 CHIP_ERROR DiscoveryManager::SetupHostname()
142 {
143 #if CHIP_ENABLE_MDNS
144     uint8_t mac[6];    // 6 byte wifi mac
145     char hostname[13]; // Hostname will be the hex representation of mac.
146     CHIP_ERROR error;
147
148     SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetPrimaryWiFiMACAddress(mac));
149     for (size_t i = 0; i < sizeof(mac); i++)
150     {
151         snprintf(&hostname[i * 2], sizeof(hostname) - i * 2, "%02X", mac[i]);
152     }
153     SuccessOrExit(error = ChipMdnsSetHostname(hostname));
154
155 exit:
156     return error;
157 #else
158     return CHIP_ERROR_NOT_IMPLEMENTED;
159 #endif // CHIP_ENABLE_MDNS
160 }
161
162 CHIP_ERROR DiscoveryManager::PublishUnprovisionedDevice(chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface)
163 {
164 #if CHIP_ENABLE_MDNS
165     CHIP_ERROR error = CHIP_NO_ERROR;
166     MdnsService service;
167     uint16_t discriminator;
168     uint16_t vendorID;
169     uint16_t productID;
170     char discriminatorBuf[5];  // hex representation of 16-bit discriminator
171     char vendorProductBuf[10]; // "FFFF+FFFF"
172     // TODO: The text entry will be updated in the spec, update accordingly.
173     TextEntry entries[2] = {
174         { "D", nullptr, 0 },
175         { "VP", nullptr, 0 },
176     };
177
178     VerifyOrExit(mMdnsInitialized, error = CHIP_ERROR_INCORRECT_STATE);
179     ChipLogProgress(Discovery, "setup mdns service");
180     SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetSetupDiscriminator(discriminator));
181     snprintf(service.mName, sizeof(service.mName), "%016" PRIX64, mUnprovisionedInstanceName);
182     strncpy(service.mType, "_chipc", sizeof(service.mType));
183     service.mProtocol = MdnsServiceProtocol::kMdnsProtocolUdp;
184     SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetVendorId(vendorID));
185     SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetProductId(productID));
186     snprintf(discriminatorBuf, sizeof(discriminatorBuf), "%04X", discriminator);
187     snprintf(vendorProductBuf, sizeof(vendorProductBuf), "%04X+%04X", vendorID, productID);
188     entries[0].mData       = reinterpret_cast<const uint8_t *>(discriminatorBuf);
189     entries[0].mDataSize   = strnlen(discriminatorBuf, sizeof(discriminatorBuf));
190     entries[1].mData       = reinterpret_cast<const uint8_t *>(vendorProductBuf);
191     entries[1].mDataSize   = strnlen(discriminatorBuf, sizeof(vendorProductBuf));
192     service.mTextEntryies  = entries;
193     service.mTextEntrySize = sizeof(entries) / sizeof(TextEntry);
194     service.mPort          = CHIP_PORT;
195     service.mInterface     = interface;
196     service.mAddressType   = addressType;
197     error                  = ChipMdnsPublishService(&service);
198
199 exit:
200     return error;
201 #else
202     return CHIP_ERROR_NOT_IMPLEMENTED;
203 #endif // CHIP_ENABLE_MDNS
204 }
205
206 CHIP_ERROR DiscoveryManager::PublishProvisionedDevice(chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface)
207 {
208 #if CHIP_ENABLE_MDNS
209     uint64_t deviceId;
210     uint64_t fabricId;
211     MdnsService service;
212     CHIP_ERROR error = CHIP_NO_ERROR;
213
214     // TODO: There may be multilple device/fabrid ids after multi-admin.
215     SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetFabricId(fabricId));
216     SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetDeviceId(deviceId));
217     snprintf(service.mName, sizeof(service.mName), "%" PRIX64 "-%" PRIX64, deviceId, fabricId);
218     strncpy(service.mType, "_chip", sizeof(service.mType));
219     service.mProtocol      = MdnsServiceProtocol::kMdnsProtocolTcp;
220     service.mPort          = CHIP_PORT;
221     service.mTextEntryies  = nullptr;
222     service.mTextEntrySize = 0;
223     service.mInterface     = interface;
224     service.mAddressType   = addressType;
225     error                  = ChipMdnsPublishService(&service);
226
227 exit:
228     return error;
229 #else
230     return CHIP_ERROR_NOT_IMPLEMENTED;
231 #endif // CHIP_ENABLE_MDNS
232 }
233
234 CHIP_ERROR DiscoveryManager::StopPublishDevice()
235 {
236 #if CHIP_ENABLE_MDNS
237     mIsPublishing = false;
238     return ChipMdnsStopPublish();
239 #else
240     return CHIP_ERROR_NOT_IMPLEMENTED;
241 #endif // CHIP_ENABLE_MDNS
242 }
243
244 CHIP_ERROR DiscoveryManager::RegisterResolveDelegate(ResolveDelegate * delegate)
245 {
246     if (mResolveDelegate != nullptr)
247     {
248         return CHIP_ERROR_INCORRECT_STATE;
249     }
250     else
251     {
252         mResolveDelegate = delegate;
253         return CHIP_NO_ERROR;
254     }
255 }
256
257 CHIP_ERROR DiscoveryManager::ResolveNodeId(uint64_t nodeId, uint64_t fabricId, Inet::IPAddressType type)
258 {
259 #if CHIP_ENABLE_MDNS
260     MdnsService service;
261
262     snprintf(service.mName, sizeof(service.mName), "%" PRIX64 "-%" PRIX64, nodeId, fabricId);
263     strncpy(service.mType, "_chip", sizeof(service.mType));
264     service.mProtocol    = MdnsServiceProtocol::kMdnsProtocolTcp;
265     service.mAddressType = type;
266     return ChipMdnsResolve(&service, INET_NULL_INTERFACEID, HandleNodeIdResolve, this);
267 #else
268     return CHIP_ERROR_NOT_IMPLEMENTED;
269 #endif // CHIP_ENABLE_MDNS
270 }
271
272 void DiscoveryManager::HandleNodeIdResolve(void * context, MdnsService * result, CHIP_ERROR error)
273 {
274 #if CHIP_ENABLE_MDNS
275     DiscoveryManager * mgr = static_cast<DiscoveryManager *>(context);
276
277     if (mgr->mResolveDelegate == nullptr)
278     {
279         return;
280     }
281     if (error != CHIP_NO_ERROR)
282     {
283         ChipLogError(Discovery, "Node ID resolved failed with %s", chip::ErrorStr(error));
284         mgr->mResolveDelegate->HandleNodeIdResolve(error, kUndefinedNodeId, MdnsService{});
285     }
286     else if (result == nullptr)
287     {
288         ChipLogError(Discovery, "Node ID resolve not found");
289         mgr->mResolveDelegate->HandleNodeIdResolve(CHIP_ERROR_UNKNOWN_RESOURCE_ID, kUndefinedNodeId, MdnsService{});
290     }
291     else
292     {
293         // Parse '%x-%x' from the name
294         uint64_t nodeId       = 0;
295         bool deliminatorFound = false;
296
297         for (size_t i = 0; i < sizeof(result->mName) && result->mName[i] != 0; i++)
298         {
299             if (result->mName[i] == '-')
300             {
301                 deliminatorFound = true;
302                 break;
303             }
304             else
305             {
306                 uint8_t val = HexToInt(result->mName[i]);
307
308                 if (val == UINT8_MAX)
309                 {
310                     break;
311                 }
312                 else
313                 {
314                     nodeId = nodeId * 16 + val;
315                 }
316             }
317         }
318
319         if (deliminatorFound)
320         {
321             ChipLogProgress(Discovery, "Node ID resolved for %" PRIX64, nodeId);
322             mgr->mResolveDelegate->HandleNodeIdResolve(error, nodeId, *result);
323         }
324         else
325         {
326             ChipLogProgress(Discovery, "Invalid service entry from node %" PRIX64, nodeId);
327             mgr->mResolveDelegate->HandleNodeIdResolve(error, kUndefinedNodeId, *result);
328         }
329     }
330 #endif // CHIP_ENABLE_MDNS
331 }
332
333 } // namespace Mdns
334 } // namespace chip