xref: /aosp_15_r20/external/ot-br-posix/src/sdp_proxy/advertising_proxy.cpp (revision 4a64e381480ef79f0532b2421e44e6ee336b8e0d)
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