• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(Host::RcpHost & aHost,Mdns::Publisher & aPublisher)61 DiscoveryProxy::DiscoveryProxy(Host::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 
FilterLinkLocalAddresses(const AddressList & aAddrList,AddressList & aFilteredList)173 void DiscoveryProxy::FilterLinkLocalAddresses(const AddressList &aAddrList, AddressList &aFilteredList)
174 {
175     aFilteredList.clear();
176 
177     for (const Ip6Address &address : aAddrList)
178     {
179         if (address.IsLinkLocal())
180         {
181             continue;
182         }
183 
184         aFilteredList.push_back(address);
185     }
186 }
187 
OnServiceDiscovered(const std::string & aType,const Mdns::Publisher::DiscoveredInstanceInfo & aInstanceInfo)188 void DiscoveryProxy::OnServiceDiscovered(const std::string                             &aType,
189                                          const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo)
190 {
191     otDnssdServiceInstanceInfo instanceInfo;
192     const otDnssdQuery        *query                 = nullptr;
193     std::string                unescapedInstanceName = DnsUtils::UnescapeInstanceName(aInstanceInfo.mName);
194     AddressList                filteredAddrList;
195 
196     FilterLinkLocalAddresses(aInstanceInfo.mAddresses, filteredAddrList);
197 
198     otbrLogInfo("Service discovered: %s, instance %s hostname %s addresses %zu port %d priority %d "
199                 "weight %d",
200                 aType.c_str(), aInstanceInfo.mName.c_str(), aInstanceInfo.mHostName.c_str(), filteredAddrList.size(),
201                 aInstanceInfo.mPort, aInstanceInfo.mPriority, aInstanceInfo.mWeight);
202 
203     instanceInfo.mAddressNum = filteredAddrList.size();
204 
205     if (!filteredAddrList.empty())
206     {
207         instanceInfo.mAddresses = reinterpret_cast<const otIp6Address *>(&filteredAddrList[0]);
208     }
209     else
210     {
211         instanceInfo.mAddresses = nullptr;
212     }
213 
214     instanceInfo.mPort      = aInstanceInfo.mPort;
215     instanceInfo.mPriority  = aInstanceInfo.mPriority;
216     instanceInfo.mWeight    = aInstanceInfo.mWeight;
217     instanceInfo.mTxtLength = static_cast<uint16_t>(aInstanceInfo.mTxtData.size());
218     instanceInfo.mTxtData   = aInstanceInfo.mTxtData.data();
219     instanceInfo.mTtl       = CapTtl(aInstanceInfo.mTtl);
220 
221     while ((query = otDnssdGetNextQuery(mHost.GetInstance(), query)) != nullptr)
222     {
223         std::string      instanceName;
224         std::string      serviceName;
225         std::string      hostName;
226         std::string      domain;
227         char             queryName[OT_DNS_MAX_NAME_SIZE];
228         otDnssdQueryType type = otDnssdGetQueryTypeAndName(query, &queryName);
229         otbrError        splitError;
230 
231         switch (type)
232         {
233         case OT_DNSSD_QUERY_TYPE_BROWSE:
234             splitError = SplitFullServiceName(queryName, serviceName, domain);
235             break;
236         case OT_DNSSD_QUERY_TYPE_RESOLVE:
237             splitError = SplitFullServiceInstanceName(queryName, instanceName, serviceName, domain);
238             break;
239         default:
240             splitError = OTBR_ERROR_NOT_FOUND;
241             break;
242         }
243         if (splitError != OTBR_ERROR_NONE)
244         {
245             // Incoming service/instance was not what current query wanted to see, move on.
246             continue;
247         }
248 
249         if (DnsLabelsEqual(serviceName, aType) &&
250             (instanceName.empty() || DnsLabelsEqual(instanceName, unescapedInstanceName)))
251         {
252             std::string serviceFullName    = aType + "." + domain;
253             std::string translatedHostName = TranslateDomain(aInstanceInfo.mHostName, domain);
254             std::string instanceFullName   = unescapedInstanceName + "." + serviceFullName;
255 
256             instanceInfo.mFullName = instanceFullName.c_str();
257             instanceInfo.mHostName = translatedHostName.c_str();
258 
259             otDnssdQueryHandleDiscoveredServiceInstance(mHost.GetInstance(), serviceFullName.c_str(), &instanceInfo);
260         }
261     }
262 }
263 
OnHostDiscovered(const std::string & aHostName,const Mdns::Publisher::DiscoveredHostInfo & aHostInfo)264 void DiscoveryProxy::OnHostDiscovered(const std::string                         &aHostName,
265                                       const Mdns::Publisher::DiscoveredHostInfo &aHostInfo)
266 {
267     otDnssdHostInfo     hostInfo;
268     const otDnssdQuery *query            = nullptr;
269     std::string         resolvedHostName = aHostInfo.mHostName;
270     AddressList         filteredAddrList;
271 
272     FilterLinkLocalAddresses(aHostInfo.mAddresses, filteredAddrList);
273     VerifyOrExit(!filteredAddrList.empty());
274 
275     otbrLogInfo("Host discovered: %s hostname %s addresses %zu", aHostName.c_str(), aHostInfo.mHostName.c_str(),
276                 filteredAddrList.size());
277 
278     if (resolvedHostName.empty())
279     {
280         resolvedHostName = aHostName + ".local.";
281     }
282 
283     hostInfo.mAddressNum = filteredAddrList.size();
284     hostInfo.mAddresses  = reinterpret_cast<const otIp6Address *>(&filteredAddrList[0]);
285 
286     hostInfo.mTtl = CapTtl(aHostInfo.mTtl);
287 
288     while ((query = otDnssdGetNextQuery(mHost.GetInstance(), query)) != nullptr)
289     {
290         std::string      hostName, domain;
291         char             queryName[OT_DNS_MAX_NAME_SIZE];
292         otDnssdQueryType type = otDnssdGetQueryTypeAndName(query, &queryName);
293         otbrError        splitError;
294 
295         OTBR_UNUSED_VARIABLE(splitError);
296 
297         if (type != OT_DNSSD_QUERY_TYPE_RESOLVE_HOST)
298         {
299             continue;
300         }
301 
302         splitError = SplitFullHostName(queryName, hostName, domain);
303 
304         if (splitError != OTBR_ERROR_NONE)
305         {
306             continue;
307         }
308 
309         if (DnsLabelsEqual(hostName, aHostName))
310         {
311             std::string hostFullName = TranslateDomain(resolvedHostName, domain);
312 
313             otDnssdQueryHandleDiscoveredHost(mHost.GetInstance(), hostFullName.c_str(), &hostInfo);
314         }
315     }
316 
317 exit:
318     return;
319 }
320 
TranslateDomain(const std::string & aName,const std::string & aTargetDomain)321 std::string DiscoveryProxy::TranslateDomain(const std::string &aName, const std::string &aTargetDomain)
322 {
323     std::string targetName;
324     std::string hostName;
325     std::string domain;
326 
327     VerifyOrExit(OTBR_ERROR_NONE == SplitFullHostName(aName, hostName, domain), targetName = aName);
328     VerifyOrExit(DnsLabelsEqual(domain, "local."), targetName = aName);
329 
330     targetName = hostName + "." + aTargetDomain;
331 
332 exit:
333     otbrLogDebug("Translate domain: %s => %s", aName.c_str(), targetName.c_str());
334     return targetName;
335 }
336 
GetServiceSubscriptionCount(const DnsNameInfo & aNameInfo) const337 int DiscoveryProxy::GetServiceSubscriptionCount(const DnsNameInfo &aNameInfo) const
338 {
339     const otDnssdQuery *query = nullptr;
340     int                 count = 0;
341 
342     while ((query = otDnssdGetNextQuery(mHost.GetInstance(), query)) != nullptr)
343     {
344         char        queryName[OT_DNS_MAX_NAME_SIZE];
345         DnsNameInfo queryInfo;
346 
347         otDnssdGetQueryTypeAndName(query, &queryName);
348         queryInfo = SplitFullDnsName(queryName);
349 
350         count += (DnsLabelsEqual(aNameInfo.mInstanceName, queryInfo.mInstanceName) &&
351                   DnsLabelsEqual(aNameInfo.mServiceName, queryInfo.mServiceName) &&
352                   DnsLabelsEqual(aNameInfo.mHostName, queryInfo.mHostName));
353     }
354 
355     return count;
356 }
357 
CapTtl(uint32_t aTtl)358 uint32_t DiscoveryProxy::CapTtl(uint32_t aTtl)
359 {
360     return std::min(aTtl, static_cast<uint32_t>(kServiceTtlCapLimit));
361 }
362 
363 } // namespace Dnssd
364 } // namespace otbr
365 
366 #endif // OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
367