1 /*
2 * Copyright (c) 2020, 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 * The file implements the Advertising Proxy.
32 */
33
34 #define OTBR_LOG_TAG "ADPROXY"
35
36 #include "sdp_proxy/advertising_proxy.hpp"
37
38 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
39
40 #if !OTBR_ENABLE_MDNS_AVAHI && !OTBR_ENABLE_MDNS_MDNSSD && !OTBR_ENABLE_MDNS_MOJO
41 #error "The Advertising Proxy requires OTBR_ENABLE_MDNS_AVAHI, OTBR_ENABLE_MDNS_MDNSSD or OTBR_ENABLE_MDNS_MOJO"
42 #endif
43
44 #include <string>
45
46 #include <assert.h>
47
48 #include "common/code_utils.hpp"
49 #include "common/dns_utils.hpp"
50 #include "common/logging.hpp"
51
52 namespace otbr {
53
OtbrErrorToOtError(otbrError aError)54 static otError OtbrErrorToOtError(otbrError aError)
55 {
56 otError error;
57
58 switch (aError)
59 {
60 case OTBR_ERROR_NONE:
61 error = OT_ERROR_NONE;
62 break;
63
64 case OTBR_ERROR_NOT_FOUND:
65 error = OT_ERROR_NOT_FOUND;
66 break;
67
68 case OTBR_ERROR_PARSE:
69 error = OT_ERROR_PARSE;
70 break;
71
72 case OTBR_ERROR_NOT_IMPLEMENTED:
73 error = OT_ERROR_NOT_IMPLEMENTED;
74 break;
75
76 case OTBR_ERROR_INVALID_ARGS:
77 error = OT_ERROR_INVALID_ARGS;
78 break;
79
80 case OTBR_ERROR_DUPLICATED:
81 error = OT_ERROR_DUPLICATED;
82 break;
83
84 case OTBR_ERROR_INVALID_STATE:
85 error = OT_ERROR_INVALID_STATE;
86 break;
87
88 default:
89 error = OT_ERROR_FAILED;
90 break;
91 }
92
93 return error;
94 }
95
AdvertisingProxy(Ncp::ControllerOpenThread & aNcp,Mdns::Publisher & aPublisher)96 AdvertisingProxy::AdvertisingProxy(Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher)
97 : mNcp(aNcp)
98 , mPublisher(aPublisher)
99 , mIsEnabled(false)
100 {
101 mNcp.RegisterResetHandler(
102 [this]() { otSrpServerSetServiceUpdateHandler(GetInstance(), AdvertisingHandler, this); });
103 }
104
SetEnabled(bool aIsEnabled)105 void AdvertisingProxy::SetEnabled(bool aIsEnabled)
106 {
107 VerifyOrExit(aIsEnabled != IsEnabled());
108 mIsEnabled = aIsEnabled;
109 if (mIsEnabled)
110 {
111 Start();
112 }
113 else
114 {
115 Stop();
116 }
117
118 exit:
119 return;
120 }
121
Start(void)122 void AdvertisingProxy::Start(void)
123 {
124 otSrpServerSetServiceUpdateHandler(GetInstance(), AdvertisingHandler, this);
125
126 otbrLogInfo("Started");
127 }
128
Stop(void)129 void AdvertisingProxy::Stop(void)
130 {
131 // Outstanding updates will fail on the SRP server because of timeout.
132 // TODO: handle this case gracefully.
133
134 // Stop receiving SRP server events.
135 if (GetInstance() != nullptr)
136 {
137 otSrpServerSetServiceUpdateHandler(GetInstance(), nullptr, nullptr);
138 }
139
140 otbrLogInfo("Stopped");
141 }
142
AdvertisingHandler(otSrpServerServiceUpdateId aId,const otSrpServerHost * aHost,uint32_t aTimeout,void * aContext)143 void AdvertisingProxy::AdvertisingHandler(otSrpServerServiceUpdateId aId,
144 const otSrpServerHost *aHost,
145 uint32_t aTimeout,
146 void *aContext)
147 {
148 static_cast<AdvertisingProxy *>(aContext)->AdvertisingHandler(aId, aHost, aTimeout);
149 }
150
AdvertisingHandler(otSrpServerServiceUpdateId aId,const otSrpServerHost * aHost,uint32_t aTimeout)151 void AdvertisingProxy::AdvertisingHandler(otSrpServerServiceUpdateId aId,
152 const otSrpServerHost *aHost,
153 uint32_t aTimeout)
154 {
155 OTBR_UNUSED_VARIABLE(aTimeout);
156
157 OutstandingUpdate *update = nullptr;
158 otbrError error = OTBR_ERROR_NONE;
159
160 VerifyOrExit(IsEnabled());
161
162 mOutstandingUpdates.emplace_back();
163 update = &mOutstandingUpdates.back();
164 update->mId = aId;
165
166 error = PublishHostAndItsServices(aHost, update);
167
168 if (error != OTBR_ERROR_NONE || update->mCallbackCount == 0)
169 {
170 mOutstandingUpdates.pop_back();
171 otSrpServerHandleServiceUpdateResult(GetInstance(), aId, OtbrErrorToOtError(error));
172 }
173
174 exit:
175 return;
176 }
177
OnMdnsPublishResult(otSrpServerServiceUpdateId aUpdateId,otbrError aError)178 void AdvertisingProxy::OnMdnsPublishResult(otSrpServerServiceUpdateId aUpdateId, otbrError aError)
179 {
180 for (auto update = mOutstandingUpdates.begin(); update != mOutstandingUpdates.end(); ++update)
181 {
182 if (update->mId != aUpdateId)
183 {
184 continue;
185 }
186
187 if (aError != OTBR_ERROR_NONE || update->mCallbackCount == 1)
188 {
189 // Erase before notifying OpenThread, because there are chances that new
190 // elements may be added to `otSrpServerHandleServiceUpdateResult` and
191 // the iterator will be invalidated.
192 mOutstandingUpdates.erase(update);
193 otSrpServerHandleServiceUpdateResult(GetInstance(), aUpdateId, OtbrErrorToOtError(aError));
194 }
195 else
196 {
197 --update->mCallbackCount;
198 otbrLogInfo("Waiting for more publishing callbacks %d", update->mCallbackCount);
199 }
200 break;
201 }
202 }
203
GetEligibleAddresses(const otIp6Address * aHostAddresses,uint8_t aHostAddressNum)204 std::vector<Ip6Address> AdvertisingProxy::GetEligibleAddresses(const otIp6Address *aHostAddresses,
205 uint8_t aHostAddressNum)
206 {
207 std::vector<Ip6Address> addresses;
208 const otIp6Address *meshLocalEid = otThreadGetMeshLocalEid(GetInstance());
209
210 addresses.reserve(aHostAddressNum);
211 for (size_t i = 0; i < aHostAddressNum; ++i)
212 {
213 Ip6Address address(aHostAddresses[i].mFields.m8);
214
215 if (otIp6PrefixMatch(meshLocalEid, &aHostAddresses[i]) >= OT_IP6_PREFIX_BITSIZE)
216 {
217 continue;
218 }
219 if (address.IsLinkLocal())
220 {
221 continue;
222 }
223 addresses.push_back(address);
224 }
225
226 return addresses;
227 }
228
HandleMdnsState(Mdns::Publisher::State aState)229 void AdvertisingProxy::HandleMdnsState(Mdns::Publisher::State aState)
230 {
231 VerifyOrExit(IsEnabled());
232 VerifyOrExit(aState == Mdns::Publisher::State::kReady);
233
234 PublishAllHostsAndServices();
235
236 exit:
237 return;
238 }
239
PublishAllHostsAndServices(void)240 void AdvertisingProxy::PublishAllHostsAndServices(void)
241 {
242 const otSrpServerHost *host = nullptr;
243
244 VerifyOrExit(IsEnabled());
245 VerifyOrExit(mPublisher.IsStarted());
246
247 otbrLogInfo("Publish all hosts and services");
248 while ((host = otSrpServerGetNextHost(GetInstance(), host)))
249 {
250 PublishHostAndItsServices(host, nullptr);
251 }
252
253 exit:
254 return;
255 }
256
PublishHostAndItsServices(const otSrpServerHost * aHost,OutstandingUpdate * aUpdate)257 otbrError AdvertisingProxy::PublishHostAndItsServices(const otSrpServerHost *aHost, OutstandingUpdate *aUpdate)
258 {
259 otbrError error = OTBR_ERROR_NONE;
260 std::string hostName;
261 std::string hostDomain;
262 const otIp6Address *hostAddresses;
263 uint8_t hostAddressNum;
264 bool hostDeleted;
265 const otSrpServerService *service;
266 otSrpServerServiceUpdateId updateId = 0;
267 bool hasUpdate = false;
268 std::string fullHostName = otSrpServerHostGetFullName(aHost);
269
270 otbrLogInfo("Advertise SRP service updates: host=%s", fullHostName.c_str());
271
272 SuccessOrExit(error = SplitFullHostName(fullHostName, hostName, hostDomain));
273 hostAddresses = otSrpServerHostGetAddresses(aHost, &hostAddressNum);
274 hostDeleted = otSrpServerHostIsDeleted(aHost);
275
276 if (aUpdate)
277 {
278 hasUpdate = true;
279 updateId = aUpdate->mId;
280 aUpdate->mCallbackCount++;
281 aUpdate->mHostName = hostName;
282 service = nullptr;
283 while ((service = otSrpServerHostGetNextService(aHost, service)) != nullptr)
284 {
285 aUpdate->mCallbackCount++;
286 }
287 }
288
289 service = nullptr;
290 while ((service = otSrpServerHostGetNextService(aHost, service)) != nullptr)
291 {
292 std::string fullServiceName = otSrpServerServiceGetInstanceName(service);
293 std::string serviceName;
294 std::string serviceType;
295 std::string serviceDomain;
296
297 SuccessOrExit(error = SplitFullServiceInstanceName(fullServiceName, serviceName, serviceType, serviceDomain));
298
299 if (!hostDeleted && !otSrpServerServiceIsDeleted(service))
300 {
301 Mdns::Publisher::TxtData txtData = MakeTxtData(service);
302 Mdns::Publisher::SubTypeList subTypeList = MakeSubTypeList(service);
303
304 otbrLogDebug("Publish SRP service '%s'", fullServiceName.c_str());
305 mPublisher.PublishService(
306 hostName, serviceName, serviceType, subTypeList, otSrpServerServiceGetPort(service), txtData,
307 [this, hasUpdate, updateId, fullServiceName](otbrError aError) {
308 otbrLogResult(aError, "Handle publish SRP service '%s'", fullServiceName.c_str());
309 if (hasUpdate)
310 {
311 OnMdnsPublishResult(updateId, aError);
312 }
313 });
314 }
315 else
316 {
317 otbrLogDebug("Unpublish SRP service '%s'", fullServiceName.c_str());
318 mPublisher.UnpublishService(
319 serviceName, serviceType, [this, hasUpdate, updateId, fullServiceName](otbrError aError) {
320 // Treat `NOT_FOUND` as success when unpublishing service
321 aError = (aError == OTBR_ERROR_NOT_FOUND) ? OTBR_ERROR_NONE : aError;
322 otbrLogResult(aError, "Handle unpublish SRP service '%s'", fullServiceName.c_str());
323 if (hasUpdate)
324 {
325 OnMdnsPublishResult(updateId, aError);
326 }
327 });
328 }
329 }
330
331 if (!hostDeleted)
332 {
333 std::vector<Ip6Address> addresses;
334
335 // TODO: select a preferred address or advertise all addresses from SRP client.
336 otbrLogDebug("Publish SRP host '%s'", fullHostName.c_str());
337
338 addresses = GetEligibleAddresses(hostAddresses, hostAddressNum);
339 mPublisher.PublishHost(
340 hostName, addresses,
341 Mdns::Publisher::ResultCallback([this, hasUpdate, updateId, fullHostName](otbrError aError) {
342 otbrLogResult(aError, "Handle publish SRP host '%s'", fullHostName.c_str());
343 if (hasUpdate)
344 {
345 OnMdnsPublishResult(updateId, aError);
346 }
347 }));
348 }
349 else
350 {
351 otbrLogDebug("Unpublish SRP host '%s'", fullHostName.c_str());
352 mPublisher.UnpublishHost(hostName, [this, hasUpdate, updateId, fullHostName](otbrError aError) {
353 // Treat `NOT_FOUND` as success when unpublishing host.
354 aError = (aError == OTBR_ERROR_NOT_FOUND) ? OTBR_ERROR_NONE : aError;
355 otbrLogResult(aError, "Handle unpublish SRP host '%s'", fullHostName.c_str());
356 if (hasUpdate)
357 {
358 OnMdnsPublishResult(updateId, aError);
359 }
360 });
361 }
362
363 exit:
364 if (error != OTBR_ERROR_NONE)
365 {
366 if (hasUpdate)
367 {
368 otbrLogInfo("Failed to advertise SRP service updates (id = %u)", updateId);
369 }
370 }
371 return error;
372 }
373
MakeTxtData(const otSrpServerService * aSrpService)374 Mdns::Publisher::TxtData AdvertisingProxy::MakeTxtData(const otSrpServerService *aSrpService)
375 {
376 const uint8_t *data;
377 uint16_t length = 0;
378
379 data = otSrpServerServiceGetTxtData(aSrpService, &length);
380
381 return Mdns::Publisher::TxtData(data, data + length);
382 }
383
MakeSubTypeList(const otSrpServerService * aSrpService)384 Mdns::Publisher::SubTypeList AdvertisingProxy::MakeSubTypeList(const otSrpServerService *aSrpService)
385 {
386 Mdns::Publisher::SubTypeList subTypeList;
387
388 for (uint16_t index = 0;; index++)
389 {
390 const char *subTypeName = otSrpServerServiceGetSubTypeServiceNameAt(aSrpService, index);
391 char subLabel[OT_DNS_MAX_LABEL_SIZE];
392
393 VerifyOrExit(subTypeName != nullptr);
394 SuccessOrExit(otSrpServerParseSubTypeServiceName(subTypeName, subLabel, sizeof(subLabel)));
395 subTypeList.emplace_back(subLabel);
396 }
397
398 exit:
399 return subTypeList;
400 }
401
402 } // namespace otbr
403
404 #endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY
405