• 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 
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