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