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