xref: /aosp_15_r20/external/ot-br-posix/src/sdp_proxy/discovery_proxy.cpp (revision 4a64e381480ef79f0532b2421e44e6ee336b8e0d)
1 /*
2  *    Copyright (c) 2021, 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 DNS-SD Discovery Proxy.
32  */
33 
34 #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
35 
36 #define OTBR_LOG_TAG "DPROXY"
37 
38 #include "sdp_proxy/discovery_proxy.hpp"
39 
40 #include <algorithm>
41 #include <string>
42 
43 #include <assert.h>
44 
45 #include <openthread/dnssd_server.h>
46 
47 #include "common/code_utils.hpp"
48 #include "common/dns_utils.hpp"
49 #include "common/logging.hpp"
50 #include "utils/dns_utils.hpp"
51 #include "utils/string_utils.hpp"
52 
53 namespace otbr {
54 namespace Dnssd {
55 
DnsLabelsEqual(const std::string & aLabel1,const std::string & aLabel2)56 static inline bool DnsLabelsEqual(const std::string &aLabel1, const std::string &aLabel2)
57 {
58     return StringUtils::EqualCaseInsensitive(aLabel1, aLabel2);
59 }
60 
DiscoveryProxy(Ncp::RcpHost & aHost,Mdns::Publisher & aPublisher)61 DiscoveryProxy::DiscoveryProxy(Ncp::RcpHost &aHost, Mdns::Publisher &aPublisher)
62     : mHost(aHost)
63     , mMdnsPublisher(aPublisher)
64     , mIsEnabled(false)
65 {
66     mHost.RegisterResetHandler([this]() {
67         otDnssdQuerySetCallbacks(mHost.GetInstance(), &DiscoveryProxy::OnDiscoveryProxySubscribe,
68                                  &DiscoveryProxy::OnDiscoveryProxyUnsubscribe, this);
69     });
70 }
71 
SetEnabled(bool aIsEnabled)72 void DiscoveryProxy::SetEnabled(bool aIsEnabled)
73 {
74     VerifyOrExit(IsEnabled() != aIsEnabled);
75     mIsEnabled = aIsEnabled;
76     if (mIsEnabled)
77     {
78         Start();
79     }
80     else
81     {
82         Stop();
83     }
84 exit:
85     return;
86 }
87 
Start(void)88 void DiscoveryProxy::Start(void)
89 {
90     assert(mSubscriberId == 0);
91 
92     otDnssdQuerySetCallbacks(mHost.GetInstance(), &DiscoveryProxy::OnDiscoveryProxySubscribe,
93                              &DiscoveryProxy::OnDiscoveryProxyUnsubscribe, this);
94 
95     mSubscriberId = mMdnsPublisher.AddSubscriptionCallbacks(
96         [this](const std::string &aType, const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo) {
97             if (!aInstanceInfo.mRemoved)
98             {
99                 OnServiceDiscovered(aType, aInstanceInfo);
100             }
101         },
102 
103         [this](const std::string &aHostName, const Mdns::Publisher::DiscoveredHostInfo &aHostInfo) {
104             OnHostDiscovered(aHostName, aHostInfo);
105         });
106 
107     otbrLogInfo("Started");
108 }
109 
Stop(void)110 void DiscoveryProxy::Stop(void)
111 {
112     otDnssdQuerySetCallbacks(mHost.GetInstance(), nullptr, nullptr, nullptr);
113 
114     if (mSubscriberId > 0)
115     {
116         mMdnsPublisher.RemoveSubscriptionCallbacks(mSubscriberId);
117         mSubscriberId = 0;
118     }
119 
120     otbrLogInfo("Stopped");
121 }
122 
OnDiscoveryProxySubscribe(void * aContext,const char * aFullName)123 void DiscoveryProxy::OnDiscoveryProxySubscribe(void *aContext, const char *aFullName)
124 {
125     reinterpret_cast<DiscoveryProxy *>(aContext)->OnDiscoveryProxySubscribe(aFullName);
126 }
127 
OnDiscoveryProxySubscribe(const char * aFullName)128 void DiscoveryProxy::OnDiscoveryProxySubscribe(const char *aFullName)
129 {
130     std::string fullName(aFullName);
131     DnsNameInfo nameInfo = SplitFullDnsName(fullName);
132 
133     otbrLogInfo("Subscribe: %s", fullName.c_str());
134 
135     if (GetServiceSubscriptionCount(nameInfo) == 1)
136     {
137         if (nameInfo.mHostName.empty())
138         {
139             mMdnsPublisher.SubscribeService(nameInfo.mServiceName, nameInfo.mInstanceName);
140         }
141         else
142         {
143             mMdnsPublisher.SubscribeHost(nameInfo.mHostName);
144         }
145     }
146 }
147 
OnDiscoveryProxyUnsubscribe(void * aContext,const char * aFullName)148 void DiscoveryProxy::OnDiscoveryProxyUnsubscribe(void *aContext, const char *aFullName)
149 {
150     reinterpret_cast<DiscoveryProxy *>(aContext)->OnDiscoveryProxyUnsubscribe(aFullName);
151 }
152 
OnDiscoveryProxyUnsubscribe(const char * aFullName)153 void DiscoveryProxy::OnDiscoveryProxyUnsubscribe(const char *aFullName)
154 {
155     std::string fullName(aFullName);
156     DnsNameInfo nameInfo = SplitFullDnsName(fullName);
157 
158     otbrLogInfo("Unsubscribe: %s", fullName.c_str());
159 
160     if (GetServiceSubscriptionCount(nameInfo) == 1)
161     {
162         if (nameInfo.mHostName.empty())
163         {
164             mMdnsPublisher.UnsubscribeService(nameInfo.mServiceName, nameInfo.mInstanceName);
165         }
166         else
167         {
168             mMdnsPublisher.UnsubscribeHost(nameInfo.mHostName);
169         }
170     }
171 }
172 
OnServiceDiscovered(const std::string & aType,const Mdns::Publisher::DiscoveredInstanceInfo & aInstanceInfo)173 void DiscoveryProxy::OnServiceDiscovered(const std::string                             &aType,
174                                          const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo)
175 {
176     otDnssdServiceInstanceInfo instanceInfo;
177     const otDnssdQuery        *query                 = nullptr;
178     std::string                unescapedInstanceName = DnsUtils::UnescapeInstanceName(aInstanceInfo.mName);
179 
180     otbrLogInfo("Service discovered: %s, instance %s hostname %s addresses %zu port %d priority %d "
181                 "weight %d",
182                 aType.c_str(), aInstanceInfo.mName.c_str(), aInstanceInfo.mHostName.c_str(),
183                 aInstanceInfo.mAddresses.size(), aInstanceInfo.mPort, aInstanceInfo.mPriority, aInstanceInfo.mWeight);
184 
185     instanceInfo.mAddressNum = aInstanceInfo.mAddresses.size();
186 
187     if (!aInstanceInfo.mAddresses.empty())
188     {
189         instanceInfo.mAddresses = reinterpret_cast<const otIp6Address *>(&aInstanceInfo.mAddresses[0]);
190     }
191     else
192     {
193         instanceInfo.mAddresses = nullptr;
194     }
195 
196     instanceInfo.mPort      = aInstanceInfo.mPort;
197     instanceInfo.mPriority  = aInstanceInfo.mPriority;
198     instanceInfo.mWeight    = aInstanceInfo.mWeight;
199     instanceInfo.mTxtLength = static_cast<uint16_t>(aInstanceInfo.mTxtData.size());
200     instanceInfo.mTxtData   = aInstanceInfo.mTxtData.data();
201     instanceInfo.mTtl       = CapTtl(aInstanceInfo.mTtl);
202 
203     while ((query = otDnssdGetNextQuery(mHost.GetInstance(), query)) != nullptr)
204     {
205         std::string      instanceName;
206         std::string      serviceName;
207         std::string      hostName;
208         std::string      domain;
209         char             queryName[OT_DNS_MAX_NAME_SIZE];
210         otDnssdQueryType type = otDnssdGetQueryTypeAndName(query, &queryName);
211         otbrError        splitError;
212 
213         switch (type)
214         {
215         case OT_DNSSD_QUERY_TYPE_BROWSE:
216             splitError = SplitFullServiceName(queryName, serviceName, domain);
217             break;
218         case OT_DNSSD_QUERY_TYPE_RESOLVE:
219             splitError = SplitFullServiceInstanceName(queryName, instanceName, serviceName, domain);
220             break;
221         default:
222             splitError = OTBR_ERROR_NOT_FOUND;
223             break;
224         }
225         if (splitError != OTBR_ERROR_NONE)
226         {
227             // Incoming service/instance was not what current query wanted to see, move on.
228             continue;
229         }
230 
231         if (DnsLabelsEqual(serviceName, aType) &&
232             (instanceName.empty() || DnsLabelsEqual(instanceName, unescapedInstanceName)))
233         {
234             std::string serviceFullName    = aType + "." + domain;
235             std::string translatedHostName = TranslateDomain(aInstanceInfo.mHostName, domain);
236             std::string instanceFullName   = unescapedInstanceName + "." + serviceFullName;
237 
238             instanceInfo.mFullName = instanceFullName.c_str();
239             instanceInfo.mHostName = translatedHostName.c_str();
240 
241             otDnssdQueryHandleDiscoveredServiceInstance(mHost.GetInstance(), serviceFullName.c_str(), &instanceInfo);
242         }
243     }
244 }
245 
OnHostDiscovered(const std::string & aHostName,const Mdns::Publisher::DiscoveredHostInfo & aHostInfo)246 void DiscoveryProxy::OnHostDiscovered(const std::string                         &aHostName,
247                                       const Mdns::Publisher::DiscoveredHostInfo &aHostInfo)
248 {
249     otDnssdHostInfo     hostInfo;
250     const otDnssdQuery *query            = nullptr;
251     std::string         resolvedHostName = aHostInfo.mHostName;
252 
253     otbrLogInfo("Host discovered: %s hostname %s addresses %zu", aHostName.c_str(), aHostInfo.mHostName.c_str(),
254                 aHostInfo.mAddresses.size());
255 
256     if (resolvedHostName.empty())
257     {
258         resolvedHostName = aHostName + ".local.";
259     }
260 
261     hostInfo.mAddressNum = aHostInfo.mAddresses.size();
262     if (!aHostInfo.mAddresses.empty())
263     {
264         hostInfo.mAddresses = reinterpret_cast<const otIp6Address *>(&aHostInfo.mAddresses[0]);
265     }
266     else
267     {
268         hostInfo.mAddresses = nullptr;
269     }
270 
271     hostInfo.mTtl = CapTtl(aHostInfo.mTtl);
272 
273     while ((query = otDnssdGetNextQuery(mHost.GetInstance(), query)) != nullptr)
274     {
275         std::string      hostName, domain;
276         char             queryName[OT_DNS_MAX_NAME_SIZE];
277         otDnssdQueryType type = otDnssdGetQueryTypeAndName(query, &queryName);
278         otbrError        splitError;
279 
280         OTBR_UNUSED_VARIABLE(splitError);
281 
282         if (type != OT_DNSSD_QUERY_TYPE_RESOLVE_HOST)
283         {
284             continue;
285         }
286 
287         splitError = SplitFullHostName(queryName, hostName, domain);
288 
289         if (splitError != OTBR_ERROR_NONE)
290         {
291             continue;
292         }
293 
294         if (DnsLabelsEqual(hostName, aHostName))
295         {
296             std::string hostFullName = TranslateDomain(resolvedHostName, domain);
297 
298             otDnssdQueryHandleDiscoveredHost(mHost.GetInstance(), hostFullName.c_str(), &hostInfo);
299         }
300     }
301 }
302 
TranslateDomain(const std::string & aName,const std::string & aTargetDomain)303 std::string DiscoveryProxy::TranslateDomain(const std::string &aName, const std::string &aTargetDomain)
304 {
305     std::string targetName;
306     std::string hostName;
307     std::string domain;
308 
309     VerifyOrExit(OTBR_ERROR_NONE == SplitFullHostName(aName, hostName, domain), targetName = aName);
310     VerifyOrExit(DnsLabelsEqual(domain, "local."), targetName = aName);
311 
312     targetName = hostName + "." + aTargetDomain;
313 
314 exit:
315     otbrLogDebug("Translate domain: %s => %s", aName.c_str(), targetName.c_str());
316     return targetName;
317 }
318 
GetServiceSubscriptionCount(const DnsNameInfo & aNameInfo) const319 int DiscoveryProxy::GetServiceSubscriptionCount(const DnsNameInfo &aNameInfo) const
320 {
321     const otDnssdQuery *query = nullptr;
322     int                 count = 0;
323 
324     while ((query = otDnssdGetNextQuery(mHost.GetInstance(), query)) != nullptr)
325     {
326         char        queryName[OT_DNS_MAX_NAME_SIZE];
327         DnsNameInfo queryInfo;
328 
329         otDnssdGetQueryTypeAndName(query, &queryName);
330         queryInfo = SplitFullDnsName(queryName);
331 
332         count += (DnsLabelsEqual(aNameInfo.mInstanceName, queryInfo.mInstanceName) &&
333                   DnsLabelsEqual(aNameInfo.mServiceName, queryInfo.mServiceName) &&
334                   DnsLabelsEqual(aNameInfo.mHostName, queryInfo.mHostName));
335     }
336 
337     return count;
338 }
339 
CapTtl(uint32_t aTtl)340 uint32_t DiscoveryProxy::CapTtl(uint32_t aTtl)
341 {
342     return std::min(aTtl, static_cast<uint32_t>(kServiceTtlCapLimit));
343 }
344 
345 } // namespace Dnssd
346 } // namespace otbr
347 
348 #endif // OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
349