• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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