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