1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "osp/impl/discovery/mdns/mdns_responder_adapter_impl.h"
6 
7 #include <algorithm>
8 #include <cctype>
9 #include <cstring>
10 #include <iostream>
11 #include <memory>
12 #include <string>
13 #include <utility>
14 
15 #include "util/osp_logging.h"
16 #include "util/trace_logging.h"
17 
18 namespace openscreen {
19 namespace osp {
20 namespace {
21 
22 // RFC 1035 specifies a max string length of 256, including the leading length
23 // octet.
24 constexpr size_t kMaxDnsStringLength = 255;
25 
26 // RFC 6763 recommends a maximum key length of 9 characters.
27 constexpr size_t kMaxTxtKeyLength = 9;
28 
29 constexpr size_t kMaxStaticTxtDataSize = 256;
30 
31 static_assert(sizeof(std::declval<RData>().u.txt) == kMaxStaticTxtDataSize,
32               "mDNSResponder static TXT data size expected to be 256 bytes");
33 
34 static_assert(sizeof(mDNSAddr::ip.v4.b) == 4u,
35               "mDNSResponder IPv4 address must be 4 bytes");
36 static_assert(sizeof(mDNSAddr::ip.v6.b) == 16u,
37               "mDNSResponder IPv6 address must be 16 bytes");
38 
AssignMdnsPort(mDNSIPPort * mdns_port,uint16_t port)39 void AssignMdnsPort(mDNSIPPort* mdns_port, uint16_t port) {
40   mdns_port->b[0] = (port >> 8) & 0xff;
41   mdns_port->b[1] = port & 0xff;
42 }
43 
GetNetworkOrderPort(const mDNSOpaque16 & port)44 uint16_t GetNetworkOrderPort(const mDNSOpaque16& port) {
45   return port.b[0] << 8 | port.b[1];
46 }
47 
IsValidServiceName(const std::string & service_name)48 bool IsValidServiceName(const std::string& service_name) {
49   // Service name requirements come from RFC 6335:
50   //  - No more than 16 characters.
51   //  - Begin with '_'.
52   //  - Next is a letter or digit and end with a letter or digit.
53   //  - May contain hyphens, but no consecutive hyphens.
54   //  - Must contain at least one letter.
55   if (service_name.size() <= 1 || service_name.size() > 16)
56     return false;
57 
58   if (service_name[0] != '_' || !std::isalnum(service_name[1]) ||
59       !std::isalnum(service_name.back())) {
60     return false;
61   }
62   bool has_alpha = false;
63   bool previous_hyphen = false;
64   for (auto it = service_name.begin() + 1; it != service_name.end(); ++it) {
65     if (*it == '-' && previous_hyphen)
66       return false;
67 
68     previous_hyphen = *it == '-';
69     has_alpha = has_alpha || std::isalpha(*it);
70   }
71   return has_alpha && !previous_hyphen;
72 }
73 
IsValidServiceProtocol(const std::string & protocol)74 bool IsValidServiceProtocol(const std::string& protocol) {
75   // RFC 6763 requires _tcp be used for TCP services and _udp for all others.
76   return protocol == "_tcp" || protocol == "_udp";
77 }
78 
MakeLocalServiceNameParts(const std::string & service_instance,const std::string & service_name,const std::string & service_protocol,domainlabel * instance,domainlabel * name,domainlabel * protocol,domainname * type,domainname * domain)79 void MakeLocalServiceNameParts(const std::string& service_instance,
80                                const std::string& service_name,
81                                const std::string& service_protocol,
82                                domainlabel* instance,
83                                domainlabel* name,
84                                domainlabel* protocol,
85                                domainname* type,
86                                domainname* domain) {
87   MakeDomainLabelFromLiteralString(instance, service_instance.c_str());
88   MakeDomainLabelFromLiteralString(name, service_name.c_str());
89   MakeDomainLabelFromLiteralString(protocol, service_protocol.c_str());
90   type->c[0] = 0;
91   AppendDomainLabel(type, name);
92   AppendDomainLabel(type, protocol);
93   const DomainName local_domain = DomainName::GetLocalDomain();
94   std::copy(local_domain.domain_name().begin(),
95             local_domain.domain_name().end(), domain->c);
96 }
97 
MakeSubnetMaskFromPrefixLengthV4(uint8_t mask[4],uint8_t prefix_length)98 void MakeSubnetMaskFromPrefixLengthV4(uint8_t mask[4], uint8_t prefix_length) {
99   for (int i = 0; i < 4; prefix_length -= 8, ++i) {
100     if (prefix_length >= 8) {
101       mask[i] = 0xff;
102     } else if (prefix_length > 0) {
103       mask[i] = 0xff << (8 - prefix_length);
104     } else {
105       mask[i] = 0;
106     }
107   }
108 }
109 
MakeSubnetMaskFromPrefixLengthV6(uint8_t mask[16],uint8_t prefix_length)110 void MakeSubnetMaskFromPrefixLengthV6(uint8_t mask[16], uint8_t prefix_length) {
111   for (int i = 0; i < 16; prefix_length -= 8, ++i) {
112     if (prefix_length >= 8) {
113       mask[i] = 0xff;
114     } else if (prefix_length > 0) {
115       mask[i] = 0xff << (8 - prefix_length);
116     } else {
117       mask[i] = 0;
118     }
119   }
120 }
121 
IsValidTxtDataKey(const std::string & s)122 bool IsValidTxtDataKey(const std::string& s) {
123   if (s.size() > kMaxTxtKeyLength)
124     return false;
125   for (unsigned char c : s)
126     if (c < 0x20 || c > 0x7e || c == '=')
127       return false;
128   return true;
129 }
130 
MakeTxtData(const std::map<std::string,std::string> & txt_data)131 std::string MakeTxtData(const std::map<std::string, std::string>& txt_data) {
132   std::string txt;
133   txt.reserve(kMaxStaticTxtDataSize);
134   for (const auto& line : txt_data) {
135     const auto key_size = line.first.size();
136     const auto value_size = line.second.size();
137     const auto line_size = value_size ? (key_size + 1 + value_size) : key_size;
138     if (!IsValidTxtDataKey(line.first) || line_size > kMaxDnsStringLength ||
139         (txt.size() + 1 + line_size) > kMaxStaticTxtDataSize) {
140       return {};
141     }
142     txt.push_back(line_size);
143     txt += line.first;
144     if (value_size) {
145       txt.push_back('=');
146       txt += line.second;
147     }
148   }
149   return txt;
150 }
151 
MapMdnsError(int err)152 MdnsResponderErrorCode MapMdnsError(int err) {
153   switch (err) {
154     case mStatus_NoError:
155       return MdnsResponderErrorCode::kNoError;
156     case mStatus_UnsupportedErr:
157       return MdnsResponderErrorCode::kUnsupportedError;
158     case mStatus_UnknownErr:
159       return MdnsResponderErrorCode::kUnknownError;
160     default:
161       break;
162   }
163   OSP_DLOG_WARN << "unmapped mDNSResponder error: " << err;
164   return MdnsResponderErrorCode::kUnknownError;
165 }
166 
ParseTxtResponse(const uint8_t data[kMaxStaticTxtDataSize],uint16_t length)167 std::vector<std::string> ParseTxtResponse(
168     const uint8_t data[kMaxStaticTxtDataSize],
169     uint16_t length) {
170   OSP_DCHECK(length <= kMaxStaticTxtDataSize);
171   if (length == 0)
172     return {};
173 
174   std::vector<std::string> lines;
175   int total_pos = 0;
176   while (total_pos < length) {
177     uint8_t line_length = data[total_pos];
178     if ((line_length > kMaxDnsStringLength) ||
179         (total_pos + line_length >= length)) {
180       return {};
181     }
182     lines.emplace_back(&data[total_pos + 1],
183                        &data[total_pos + line_length + 1]);
184     total_pos += line_length + 1;
185   }
186   return lines;
187 }
188 
MdnsStatusCallback(mDNS * mdns,mStatus result)189 void MdnsStatusCallback(mDNS* mdns, mStatus result) {
190   OSP_LOG_INFO << "status good? " << (result == mStatus_NoError);
191 }
192 
193 }  // namespace
194 
195 MdnsResponderAdapterImpl::MdnsResponderAdapterImpl() = default;
196 MdnsResponderAdapterImpl::~MdnsResponderAdapterImpl() = default;
197 
Init()198 Error MdnsResponderAdapterImpl::Init() {
199   const auto err =
200       mDNS_Init(&mdns_, &platform_storage_, rr_cache_, kRrCacheSize,
201                 mDNS_Init_DontAdvertiseLocalAddresses, &MdnsStatusCallback,
202                 mDNS_Init_NoInitCallbackContext);
203 
204   return (err == mStatus_NoError) ? Error::None()
205                                   : Error::Code::kInitializationFailure;
206 }
207 
Close()208 void MdnsResponderAdapterImpl::Close() {
209   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::Close");
210   mDNS_StartExit(&mdns_);
211   // Let all services send goodbyes.
212   while (!service_records_.empty()) {
213     RunTasks();
214   }
215   mDNS_FinalExit(&mdns_);
216 
217   socket_to_questions_.clear();
218 
219   responder_interface_info_.clear();
220 
221   a_responses_.clear();
222   aaaa_responses_.clear();
223   ptr_responses_.clear();
224   srv_responses_.clear();
225   txt_responses_.clear();
226 
227   service_records_.clear();
228 }
229 
SetHostLabel(const std::string & host_label)230 Error MdnsResponderAdapterImpl::SetHostLabel(const std::string& host_label) {
231   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::SetHostLabel");
232   if (host_label.size() > DomainName::kDomainNameMaxLabelLength)
233     return Error::Code::kDomainNameTooLong;
234 
235   MakeDomainLabelFromLiteralString(&mdns_.hostlabel, host_label.c_str());
236   mDNS_SetFQDN(&mdns_);
237   if (!service_records_.empty()) {
238     DeadvertiseInterfaces();
239     AdvertiseInterfaces();
240   }
241   return Error::None();
242 }
243 
RegisterInterface(const InterfaceInfo & interface_info,const IPSubnet & interface_address,UdpSocket * socket)244 Error MdnsResponderAdapterImpl::RegisterInterface(
245     const InterfaceInfo& interface_info,
246     const IPSubnet& interface_address,
247     UdpSocket* socket) {
248   TRACE_SCOPED(TraceCategory::kMdns,
249                "MdnsResponderAdapterImpl::RegisterInterface");
250   OSP_DCHECK(socket);
251 
252   const auto info_it = responder_interface_info_.find(socket);
253   if (info_it != responder_interface_info_.end())
254     return Error::None();
255 
256   NetworkInterfaceInfo& info = responder_interface_info_[socket];
257   std::memset(&info, 0, sizeof(NetworkInterfaceInfo));
258   info.InterfaceID = reinterpret_cast<decltype(info.InterfaceID)>(socket);
259   info.Advertise = mDNSfalse;
260   if (interface_address.address.IsV4()) {
261     info.ip.type = mDNSAddrType_IPv4;
262     interface_address.address.CopyToV4(info.ip.ip.v4.b);
263     info.mask.type = mDNSAddrType_IPv4;
264     MakeSubnetMaskFromPrefixLengthV4(info.mask.ip.v4.b,
265                                      interface_address.prefix_length);
266   } else {
267     info.ip.type = mDNSAddrType_IPv6;
268     interface_address.address.CopyToV6(info.ip.ip.v6.b);
269     info.mask.type = mDNSAddrType_IPv6;
270     MakeSubnetMaskFromPrefixLengthV6(info.mask.ip.v6.b,
271                                      interface_address.prefix_length);
272   }
273 
274   static_assert(sizeof(info.MAC.b) == sizeof(interface_info.hardware_address),
275                 "MAC address size mismatch.");
276   memcpy(info.MAC.b, interface_info.hardware_address.data(),
277          sizeof(info.MAC.b));
278   info.McastTxRx = 1;
279   platform_storage_.sockets.push_back(socket);
280   auto result = mDNS_RegisterInterface(&mdns_, &info, mDNSfalse);
281   OSP_LOG_IF(WARN, result != mStatus_NoError)
282       << "mDNS_RegisterInterface failed: " << result;
283 
284   return (result == mStatus_NoError) ? Error::None()
285                                      : Error::Code::kMdnsRegisterFailure;
286 }
287 
DeregisterInterface(UdpSocket * socket)288 Error MdnsResponderAdapterImpl::DeregisterInterface(UdpSocket* socket) {
289   TRACE_SCOPED(TraceCategory::kMdns,
290                "MdnsResponderAdapterImpl::DeregisterInterface");
291   const auto info_it = responder_interface_info_.find(socket);
292   if (info_it == responder_interface_info_.end())
293     return Error::Code::kItemNotFound;
294 
295   const auto it = std::find(platform_storage_.sockets.begin(),
296                             platform_storage_.sockets.end(), socket);
297   OSP_DCHECK(it != platform_storage_.sockets.end());
298   platform_storage_.sockets.erase(it);
299   if (info_it->second.RR_A.namestorage.c[0]) {
300     mDNS_Deregister(&mdns_, &info_it->second.RR_A);
301     info_it->second.RR_A.namestorage.c[0] = 0;
302   }
303   mDNS_DeregisterInterface(&mdns_, &info_it->second, mDNSfalse);
304   responder_interface_info_.erase(info_it);
305   return Error::None();
306 }
OnRead(UdpSocket * socket,ErrorOr<UdpPacket> packet_or_error)307 void MdnsResponderAdapterImpl::OnRead(UdpSocket* socket,
308                                       ErrorOr<UdpPacket> packet_or_error) {
309   if (packet_or_error.is_error()) {
310     return;
311   }
312 
313   UdpPacket packet = std::move(packet_or_error.value());
314   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::OnRead");
315   mDNSAddr src;
316   if (packet.source().address.IsV4()) {
317     src.type = mDNSAddrType_IPv4;
318     packet.source().address.CopyToV4(src.ip.v4.b);
319   } else {
320     src.type = mDNSAddrType_IPv6;
321     packet.source().address.CopyToV6(src.ip.v6.b);
322   }
323   mDNSIPPort srcport;
324   AssignMdnsPort(&srcport, packet.source().port);
325 
326   mDNSAddr dst;
327   if (packet.source().address.IsV4()) {
328     dst.type = mDNSAddrType_IPv4;
329     packet.destination().address.CopyToV4(dst.ip.v4.b);
330   } else {
331     dst.type = mDNSAddrType_IPv6;
332     packet.destination().address.CopyToV6(dst.ip.v6.b);
333   }
334   mDNSIPPort dstport;
335   AssignMdnsPort(&dstport, packet.destination().port);
336 
337   auto* packet_data = packet.data();
338   mDNSCoreReceive(&mdns_, const_cast<uint8_t*>(packet_data),
339                   packet_data + packet.size(), &src, srcport, &dst, dstport,
340                   reinterpret_cast<mDNSInterfaceID>(packet.socket()));
341 }
342 
OnSendError(UdpSocket * socket,Error error)343 void MdnsResponderAdapterImpl::OnSendError(UdpSocket* socket, Error error) {
344   // TODO(crbug.com/openscreen/67): Implement this method.
345   OSP_UNIMPLEMENTED();
346 }
347 
OnError(UdpSocket * socket,Error error)348 void MdnsResponderAdapterImpl::OnError(UdpSocket* socket, Error error) {
349   // TODO(crbug.com/openscreen/67): Implement this method.
350   OSP_UNIMPLEMENTED();
351 }
352 
OnBound(UdpSocket * socket)353 void MdnsResponderAdapterImpl::OnBound(UdpSocket* socket) {
354   // TODO(crbug.com/openscreen/67): Implement this method.
355   OSP_UNIMPLEMENTED();
356 }
357 
RunTasks()358 Clock::duration MdnsResponderAdapterImpl::RunTasks() {
359   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::RunTasks");
360 
361   mDNS_Execute(&mdns_);
362 
363   // Using mDNS_Execute's response to determine the correct timespan before
364   // re-running this method doesn't work as expected. In the demo, under some
365   // cases (about 25% of demo runs), the response is set to an unreasonably
366   // large number (in the order of multiple days).
367   //
368   // From the mDNS documentation: "it is the responsibility [...] to set the
369   // timer according to the m->NextScheduledEvent value, and then when the timer
370   // fires, the timer callback function should call mDNS_Execute()" - for more
371   // details see third_party/mDNSResponder/src/mDNSCore/mDNS.c : 3390
372   //
373   // Together, I understand these to mean that the mdns library code doesn't
374   // expect we need mDNS_Execute called again by the task runner, only in the
375   // other special cases it calls out in documentation (which we currently do
376   // correctly). In our code, when we call mDNS_Execute again outside of the
377   // task runner, the result is currently discarded. What we would need to do is
378   // reach into the Task Runner's task and update how long before the task runs
379   // again. That would require some large refactoring and changes.
380   //
381   // Additionally, beyond this, the mDNS code documents that there are cases
382   // where the return value for mDNS_Execute should be ignored because it may be
383   // stale.
384   //
385   // TODO(rwkeane): More accurately determine when the next run of this method
386   // should be.
387   constexpr auto seconds_before_next_run = 1;
388 
389   // Return as a duration.
390   return std::chrono::seconds(seconds_before_next_run);
391 }
392 
TakePtrResponses()393 std::vector<PtrEvent> MdnsResponderAdapterImpl::TakePtrResponses() {
394   return std::move(ptr_responses_);
395 }
396 
TakeSrvResponses()397 std::vector<SrvEvent> MdnsResponderAdapterImpl::TakeSrvResponses() {
398   return std::move(srv_responses_);
399 }
400 
TakeTxtResponses()401 std::vector<TxtEvent> MdnsResponderAdapterImpl::TakeTxtResponses() {
402   return std::move(txt_responses_);
403 }
404 
TakeAResponses()405 std::vector<AEvent> MdnsResponderAdapterImpl::TakeAResponses() {
406   return std::move(a_responses_);
407 }
408 
TakeAaaaResponses()409 std::vector<AaaaEvent> MdnsResponderAdapterImpl::TakeAaaaResponses() {
410   return std::move(aaaa_responses_);
411 }
412 
StartPtrQuery(UdpSocket * socket,const DomainName & service_type)413 MdnsResponderErrorCode MdnsResponderAdapterImpl::StartPtrQuery(
414     UdpSocket* socket,
415     const DomainName& service_type) {
416   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StartPtrQuery");
417   auto& ptr_questions = socket_to_questions_[socket].ptr;
418   if (ptr_questions.find(service_type) != ptr_questions.end())
419     return MdnsResponderErrorCode::kNoError;
420 
421   auto& question = ptr_questions[service_type];
422 
423   question.InterfaceID = reinterpret_cast<mDNSInterfaceID>(socket);
424   question.Target = {0};
425   if (service_type.EndsWithLocalDomain()) {
426     std::copy(service_type.domain_name().begin(),
427               service_type.domain_name().end(), question.qname.c);
428   } else {
429     const DomainName local_domain = DomainName::GetLocalDomain();
430     ErrorOr<DomainName> service_type_with_local =
431         DomainName::Append(service_type, local_domain);
432     if (!service_type_with_local) {
433       return MdnsResponderErrorCode::kDomainOverflowError;
434     }
435     std::copy(service_type_with_local.value().domain_name().begin(),
436               service_type_with_local.value().domain_name().end(),
437               question.qname.c);
438   }
439   question.qtype = kDNSType_PTR;
440   question.qclass = kDNSClass_IN;
441   question.LongLived = mDNStrue;
442   question.ExpectUnique = mDNSfalse;
443   question.ForceMCast = mDNStrue;
444   question.ReturnIntermed = mDNSfalse;
445   question.SuppressUnusable = mDNSfalse;
446   question.RetryWithSearchDomains = mDNSfalse;
447   question.TimeoutQuestion = 0;
448   question.WakeOnResolve = 0;
449   question.SearchListIndex = 0;
450   question.AppendSearchDomains = 0;
451   question.AppendLocalSearchDomains = 0;
452   question.qnameOrig = nullptr;
453   question.QuestionCallback = &MdnsResponderAdapterImpl::PtrQueryCallback;
454   question.QuestionContext = this;
455   const auto err = mDNS_StartQuery(&mdns_, &question);
456   OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StartQuery failed: " << err;
457   return MapMdnsError(err);
458 }
459 
StartSrvQuery(UdpSocket * socket,const DomainName & service_instance)460 MdnsResponderErrorCode MdnsResponderAdapterImpl::StartSrvQuery(
461     UdpSocket* socket,
462     const DomainName& service_instance) {
463   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StartSrvQuery");
464   if (!service_instance.EndsWithLocalDomain())
465     return MdnsResponderErrorCode::kInvalidParameters;
466 
467   auto& srv_questions = socket_to_questions_[socket].srv;
468   if (srv_questions.find(service_instance) != srv_questions.end())
469     return MdnsResponderErrorCode::kNoError;
470 
471   auto& question = srv_questions[service_instance];
472 
473   question.InterfaceID = reinterpret_cast<mDNSInterfaceID>(socket);
474   question.Target = {0};
475   std::copy(service_instance.domain_name().begin(),
476             service_instance.domain_name().end(), question.qname.c);
477   question.qtype = kDNSType_SRV;
478   question.qclass = kDNSClass_IN;
479   question.LongLived = mDNStrue;
480   question.ExpectUnique = mDNSfalse;
481   question.ForceMCast = mDNStrue;
482   question.ReturnIntermed = mDNSfalse;
483   question.SuppressUnusable = mDNSfalse;
484   question.RetryWithSearchDomains = mDNSfalse;
485   question.TimeoutQuestion = 0;
486   question.WakeOnResolve = 0;
487   question.SearchListIndex = 0;
488   question.AppendSearchDomains = 0;
489   question.AppendLocalSearchDomains = 0;
490   question.qnameOrig = nullptr;
491   question.QuestionCallback = &MdnsResponderAdapterImpl::SrvQueryCallback;
492   question.QuestionContext = this;
493   const auto err = mDNS_StartQuery(&mdns_, &question);
494   OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StartQuery failed: " << err;
495   return MapMdnsError(err);
496 }
497 
StartTxtQuery(UdpSocket * socket,const DomainName & service_instance)498 MdnsResponderErrorCode MdnsResponderAdapterImpl::StartTxtQuery(
499     UdpSocket* socket,
500     const DomainName& service_instance) {
501   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StartTxtQuery");
502   if (!service_instance.EndsWithLocalDomain())
503     return MdnsResponderErrorCode::kInvalidParameters;
504 
505   auto& txt_questions = socket_to_questions_[socket].txt;
506   if (txt_questions.find(service_instance) != txt_questions.end())
507     return MdnsResponderErrorCode::kNoError;
508 
509   auto& question = txt_questions[service_instance];
510 
511   question.InterfaceID = reinterpret_cast<mDNSInterfaceID>(socket);
512   question.Target = {0};
513   std::copy(service_instance.domain_name().begin(),
514             service_instance.domain_name().end(), question.qname.c);
515   question.qtype = kDNSType_TXT;
516   question.qclass = kDNSClass_IN;
517   question.LongLived = mDNStrue;
518   question.ExpectUnique = mDNSfalse;
519   question.ForceMCast = mDNStrue;
520   question.ReturnIntermed = mDNSfalse;
521   question.SuppressUnusable = mDNSfalse;
522   question.RetryWithSearchDomains = mDNSfalse;
523   question.TimeoutQuestion = 0;
524   question.WakeOnResolve = 0;
525   question.SearchListIndex = 0;
526   question.AppendSearchDomains = 0;
527   question.AppendLocalSearchDomains = 0;
528   question.qnameOrig = nullptr;
529   question.QuestionCallback = &MdnsResponderAdapterImpl::TxtQueryCallback;
530   question.QuestionContext = this;
531   const auto err = mDNS_StartQuery(&mdns_, &question);
532   OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StartQuery failed: " << err;
533   return MapMdnsError(err);
534 }
535 
StartAQuery(UdpSocket * socket,const DomainName & domain_name)536 MdnsResponderErrorCode MdnsResponderAdapterImpl::StartAQuery(
537     UdpSocket* socket,
538     const DomainName& domain_name) {
539   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StartAQuery");
540   if (!domain_name.EndsWithLocalDomain())
541     return MdnsResponderErrorCode::kInvalidParameters;
542 
543   auto& a_questions = socket_to_questions_[socket].a;
544   if (a_questions.find(domain_name) != a_questions.end())
545     return MdnsResponderErrorCode::kNoError;
546 
547   auto& question = a_questions[domain_name];
548   std::copy(domain_name.domain_name().begin(), domain_name.domain_name().end(),
549             question.qname.c);
550 
551   question.InterfaceID = reinterpret_cast<mDNSInterfaceID>(socket);
552   question.Target = {0};
553   question.qtype = kDNSType_A;
554   question.qclass = kDNSClass_IN;
555   question.LongLived = mDNStrue;
556   question.ExpectUnique = mDNSfalse;
557   question.ForceMCast = mDNStrue;
558   question.ReturnIntermed = mDNSfalse;
559   question.SuppressUnusable = mDNSfalse;
560   question.RetryWithSearchDomains = mDNSfalse;
561   question.TimeoutQuestion = 0;
562   question.WakeOnResolve = 0;
563   question.SearchListIndex = 0;
564   question.AppendSearchDomains = 0;
565   question.AppendLocalSearchDomains = 0;
566   question.qnameOrig = nullptr;
567   question.QuestionCallback = &MdnsResponderAdapterImpl::AQueryCallback;
568   question.QuestionContext = this;
569   const auto err = mDNS_StartQuery(&mdns_, &question);
570   OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StartQuery failed: " << err;
571   return MapMdnsError(err);
572 }
573 
StartAaaaQuery(UdpSocket * socket,const DomainName & domain_name)574 MdnsResponderErrorCode MdnsResponderAdapterImpl::StartAaaaQuery(
575     UdpSocket* socket,
576     const DomainName& domain_name) {
577   TRACE_SCOPED(TraceCategory::kMdns,
578                "MdnsResponderAdapterImpl::StartAaaaQuery");
579   if (!domain_name.EndsWithLocalDomain())
580     return MdnsResponderErrorCode::kInvalidParameters;
581 
582   auto& aaaa_questions = socket_to_questions_[socket].aaaa;
583   if (aaaa_questions.find(domain_name) != aaaa_questions.end())
584     return MdnsResponderErrorCode::kNoError;
585 
586   auto& question = aaaa_questions[domain_name];
587   std::copy(domain_name.domain_name().begin(), domain_name.domain_name().end(),
588             question.qname.c);
589 
590   question.InterfaceID = reinterpret_cast<mDNSInterfaceID>(socket);
591   question.Target = {0};
592   question.qtype = kDNSType_AAAA;
593   question.qclass = kDNSClass_IN;
594   question.LongLived = mDNStrue;
595   question.ExpectUnique = mDNSfalse;
596   question.ForceMCast = mDNStrue;
597   question.ReturnIntermed = mDNSfalse;
598   question.SuppressUnusable = mDNSfalse;
599   question.RetryWithSearchDomains = mDNSfalse;
600   question.TimeoutQuestion = 0;
601   question.WakeOnResolve = 0;
602   question.SearchListIndex = 0;
603   question.AppendSearchDomains = 0;
604   question.AppendLocalSearchDomains = 0;
605   question.qnameOrig = nullptr;
606   question.QuestionCallback = &MdnsResponderAdapterImpl::AaaaQueryCallback;
607   question.QuestionContext = this;
608   const auto err = mDNS_StartQuery(&mdns_, &question);
609   OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StartQuery failed: " << err;
610   return MapMdnsError(err);
611 }
612 
StopPtrQuery(UdpSocket * socket,const DomainName & service_type)613 MdnsResponderErrorCode MdnsResponderAdapterImpl::StopPtrQuery(
614     UdpSocket* socket,
615     const DomainName& service_type) {
616   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StopPtrQuery");
617   auto interface_entry = socket_to_questions_.find(socket);
618   if (interface_entry == socket_to_questions_.end())
619     return MdnsResponderErrorCode::kNoError;
620   auto entry = interface_entry->second.ptr.find(service_type);
621   if (entry == interface_entry->second.ptr.end())
622     return MdnsResponderErrorCode::kNoError;
623 
624   const auto err = mDNS_StopQuery(&mdns_, &entry->second);
625   interface_entry->second.ptr.erase(entry);
626   OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StopQuery failed: " << err;
627   RemoveQuestionsIfEmpty(socket);
628   return MapMdnsError(err);
629 }
630 
StopSrvQuery(UdpSocket * socket,const DomainName & service_instance)631 MdnsResponderErrorCode MdnsResponderAdapterImpl::StopSrvQuery(
632     UdpSocket* socket,
633     const DomainName& service_instance) {
634   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StopSrvQuery");
635   auto interface_entry = socket_to_questions_.find(socket);
636   if (interface_entry == socket_to_questions_.end())
637     return MdnsResponderErrorCode::kNoError;
638   auto entry = interface_entry->second.srv.find(service_instance);
639   if (entry == interface_entry->second.srv.end())
640     return MdnsResponderErrorCode::kNoError;
641 
642   const auto err = mDNS_StopQuery(&mdns_, &entry->second);
643   interface_entry->second.srv.erase(entry);
644   OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StopQuery failed: " << err;
645   RemoveQuestionsIfEmpty(socket);
646   return MapMdnsError(err);
647 }
648 
StopTxtQuery(UdpSocket * socket,const DomainName & service_instance)649 MdnsResponderErrorCode MdnsResponderAdapterImpl::StopTxtQuery(
650     UdpSocket* socket,
651     const DomainName& service_instance) {
652   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StopTxtQuery");
653   auto interface_entry = socket_to_questions_.find(socket);
654   if (interface_entry == socket_to_questions_.end())
655     return MdnsResponderErrorCode::kNoError;
656   auto entry = interface_entry->second.txt.find(service_instance);
657   if (entry == interface_entry->second.txt.end())
658     return MdnsResponderErrorCode::kNoError;
659 
660   const auto err = mDNS_StopQuery(&mdns_, &entry->second);
661   interface_entry->second.txt.erase(entry);
662   OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StopQuery failed: " << err;
663   RemoveQuestionsIfEmpty(socket);
664   return MapMdnsError(err);
665 }
666 
StopAQuery(UdpSocket * socket,const DomainName & domain_name)667 MdnsResponderErrorCode MdnsResponderAdapterImpl::StopAQuery(
668     UdpSocket* socket,
669     const DomainName& domain_name) {
670   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StopAQuery");
671   auto interface_entry = socket_to_questions_.find(socket);
672   if (interface_entry == socket_to_questions_.end())
673     return MdnsResponderErrorCode::kNoError;
674   auto entry = interface_entry->second.a.find(domain_name);
675   if (entry == interface_entry->second.a.end())
676     return MdnsResponderErrorCode::kNoError;
677 
678   const auto err = mDNS_StopQuery(&mdns_, &entry->second);
679   interface_entry->second.a.erase(entry);
680   OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StopQuery failed: " << err;
681   RemoveQuestionsIfEmpty(socket);
682   return MapMdnsError(err);
683 }
684 
StopAaaaQuery(UdpSocket * socket,const DomainName & domain_name)685 MdnsResponderErrorCode MdnsResponderAdapterImpl::StopAaaaQuery(
686     UdpSocket* socket,
687     const DomainName& domain_name) {
688   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StopAaaaQuery");
689   auto interface_entry = socket_to_questions_.find(socket);
690   if (interface_entry == socket_to_questions_.end())
691     return MdnsResponderErrorCode::kNoError;
692   auto entry = interface_entry->second.aaaa.find(domain_name);
693   if (entry == interface_entry->second.aaaa.end())
694     return MdnsResponderErrorCode::kNoError;
695 
696   const auto err = mDNS_StopQuery(&mdns_, &entry->second);
697   interface_entry->second.aaaa.erase(entry);
698   OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StopQuery failed: " << err;
699   RemoveQuestionsIfEmpty(socket);
700   return MapMdnsError(err);
701 }
702 
RegisterService(const std::string & service_instance,const std::string & service_name,const std::string & service_protocol,const DomainName & target_host,uint16_t target_port,const std::map<std::string,std::string> & txt_data)703 MdnsResponderErrorCode MdnsResponderAdapterImpl::RegisterService(
704     const std::string& service_instance,
705     const std::string& service_name,
706     const std::string& service_protocol,
707     const DomainName& target_host,
708     uint16_t target_port,
709     const std::map<std::string, std::string>& txt_data) {
710   TRACE_SCOPED(TraceCategory::kMdns,
711                "MdnsResponderAdapterImpl::RegisterService");
712   OSP_DCHECK(IsValidServiceName(service_name));
713   OSP_DCHECK(IsValidServiceProtocol(service_protocol));
714   service_records_.push_back(std::make_unique<ServiceRecordSet>());
715   auto* service_record = service_records_.back().get();
716   domainlabel instance;
717   domainlabel name;
718   domainlabel protocol;
719   domainname type;
720   domainname domain;
721   domainname host;
722   mDNSIPPort port;
723 
724   MakeLocalServiceNameParts(service_instance, service_name, service_protocol,
725                             &instance, &name, &protocol, &type, &domain);
726   std::copy(target_host.domain_name().begin(), target_host.domain_name().end(),
727             host.c);
728   AssignMdnsPort(&port, target_port);
729   auto txt = MakeTxtData(txt_data);
730   if (txt.size() > kMaxStaticTxtDataSize) {
731     // Not handling oversized TXT records.
732     return MdnsResponderErrorCode::kUnsupportedError;
733   }
734 
735   if (service_records_.size() == 1)
736     AdvertiseInterfaces();
737 
738   auto result = mDNS_RegisterService(
739       &mdns_, service_record, &instance, &type, &domain, &host, port,
740       reinterpret_cast<const uint8_t*>(txt.data()), txt.size(), nullptr, 0,
741       mDNSInterface_Any, &MdnsResponderAdapterImpl::ServiceCallback, this, 0);
742 
743   if (result != mStatus_NoError) {
744     service_records_.pop_back();
745     if (service_records_.empty())
746       DeadvertiseInterfaces();
747   }
748   return MapMdnsError(result);
749 }
750 
DeregisterService(const std::string & service_instance,const std::string & service_name,const std::string & service_protocol)751 MdnsResponderErrorCode MdnsResponderAdapterImpl::DeregisterService(
752     const std::string& service_instance,
753     const std::string& service_name,
754     const std::string& service_protocol) {
755   TRACE_SCOPED(TraceCategory::kMdns,
756                "MdnsResponderAdapterImpl::DeregisterService");
757   domainlabel instance;
758   domainlabel name;
759   domainlabel protocol;
760   domainname type;
761   domainname domain;
762   domainname full_instance_name;
763 
764   MakeLocalServiceNameParts(service_instance, service_name, service_protocol,
765                             &instance, &name, &protocol, &type, &domain);
766   if (!ConstructServiceName(&full_instance_name, &instance, &type, &domain))
767     return MdnsResponderErrorCode::kInvalidParameters;
768 
769   for (auto it = service_records_.begin(); it != service_records_.end(); ++it) {
770     if (SameDomainName(&full_instance_name, &(*it)->RR_SRV.namestorage)) {
771       // |it| will be removed from |service_records_| in ServiceCallback, when
772       // mDNSResponder is done with the memory.
773       mDNS_DeregisterService(&mdns_, it->get());
774       return MdnsResponderErrorCode::kNoError;
775     }
776   }
777   return MdnsResponderErrorCode::kNoError;
778 }
779 
UpdateTxtData(const std::string & service_instance,const std::string & service_name,const std::string & service_protocol,const std::map<std::string,std::string> & txt_data)780 MdnsResponderErrorCode MdnsResponderAdapterImpl::UpdateTxtData(
781     const std::string& service_instance,
782     const std::string& service_name,
783     const std::string& service_protocol,
784     const std::map<std::string, std::string>& txt_data) {
785   TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::UpdateTxtData");
786   domainlabel instance;
787   domainlabel name;
788   domainlabel protocol;
789   domainname type;
790   domainname domain;
791   domainname full_instance_name;
792 
793   MakeLocalServiceNameParts(service_instance, service_name, service_protocol,
794                             &instance, &name, &protocol, &type, &domain);
795   if (!ConstructServiceName(&full_instance_name, &instance, &type, &domain))
796     return MdnsResponderErrorCode::kInvalidParameters;
797   std::string txt = MakeTxtData(txt_data);
798   if (txt.size() > kMaxStaticTxtDataSize) {
799     // Not handling oversized TXT records.
800     return MdnsResponderErrorCode::kUnsupportedError;
801   }
802 
803   for (std::unique_ptr<ServiceRecordSet>& record : service_records_) {
804     if (SameDomainName(&full_instance_name, &record->RR_SRV.namestorage)) {
805       std::copy(txt.begin(), txt.end(), record->RR_TXT.rdatastorage.u.txt.c);
806       mDNS_Update(&mdns_, &record->RR_TXT, 0, txt.size(),
807                   &record->RR_TXT.rdatastorage, nullptr);
808       return MdnsResponderErrorCode::kNoError;
809     }
810   }
811   return MdnsResponderErrorCode::kNoError;
812 }
813 
814 // static
AQueryCallback(mDNS * m,DNSQuestion * question,const ResourceRecord * answer,QC_result added)815 void MdnsResponderAdapterImpl::AQueryCallback(mDNS* m,
816                                               DNSQuestion* question,
817                                               const ResourceRecord* answer,
818                                               QC_result added) {
819   TRACE_SCOPED(TraceCategory::kMdns,
820                "MdnsResponderAdapterImpl::AQueryCallback");
821   OSP_DCHECK(question);
822   OSP_DCHECK(answer);
823   OSP_DCHECK_EQ(answer->rrtype, kDNSType_A);
824   DomainName domain(std::vector<uint8_t>(
825       question->qname.c,
826       question->qname.c + DomainNameLength(&question->qname)));
827   IPAddress address(answer->rdata->u.ipv4.b);
828 
829   auto* adapter =
830       reinterpret_cast<MdnsResponderAdapterImpl*>(question->QuestionContext);
831   OSP_DCHECK(adapter);
832   auto event_type = QueryEventHeader::Type::kAddedNoCache;
833   if (added == QC_add) {
834     event_type = QueryEventHeader::Type::kAdded;
835   } else if (added == QC_rmv) {
836     event_type = QueryEventHeader::Type::kRemoved;
837   } else {
838     OSP_DCHECK_EQ(added, QC_addnocache);
839   }
840   adapter->a_responses_.emplace_back(
841       QueryEventHeader{event_type,
842                        reinterpret_cast<UdpSocket*>(answer->InterfaceID)},
843       std::move(domain), address);
844 }
845 
846 // static
AaaaQueryCallback(mDNS * m,DNSQuestion * question,const ResourceRecord * answer,QC_result added)847 void MdnsResponderAdapterImpl::AaaaQueryCallback(mDNS* m,
848                                                  DNSQuestion* question,
849                                                  const ResourceRecord* answer,
850                                                  QC_result added) {
851   TRACE_SCOPED(TraceCategory::kMdns,
852                "MdnsResponderAdapterImpl::AaaaQueryCallback");
853   OSP_DCHECK(question);
854   OSP_DCHECK(answer);
855   OSP_DCHECK_EQ(answer->rrtype, kDNSType_A);
856   DomainName domain(std::vector<uint8_t>(
857       question->qname.c,
858       question->qname.c + DomainNameLength(&question->qname)));
859   IPAddress address(IPAddress::Version::kV6, answer->rdata->u.ipv6.b);
860 
861   auto* adapter =
862       reinterpret_cast<MdnsResponderAdapterImpl*>(question->QuestionContext);
863   OSP_DCHECK(adapter);
864   auto event_type = QueryEventHeader::Type::kAddedNoCache;
865   if (added == QC_add) {
866     event_type = QueryEventHeader::Type::kAdded;
867   } else if (added == QC_rmv) {
868     event_type = QueryEventHeader::Type::kRemoved;
869   } else {
870     OSP_DCHECK_EQ(added, QC_addnocache);
871   }
872   adapter->aaaa_responses_.emplace_back(
873       QueryEventHeader{event_type,
874                        reinterpret_cast<UdpSocket*>(answer->InterfaceID)},
875       std::move(domain), address);
876 }
877 
878 // static
PtrQueryCallback(mDNS * m,DNSQuestion * question,const ResourceRecord * answer,QC_result added)879 void MdnsResponderAdapterImpl::PtrQueryCallback(mDNS* m,
880                                                 DNSQuestion* question,
881                                                 const ResourceRecord* answer,
882                                                 QC_result added) {
883   TRACE_SCOPED(TraceCategory::kMdns,
884                "MdnsResponderAdapterImpl::PtrQueryCallback");
885   OSP_DCHECK(question);
886   OSP_DCHECK(answer);
887   OSP_DCHECK_EQ(answer->rrtype, kDNSType_PTR);
888   DomainName result(std::vector<uint8_t>(
889       answer->rdata->u.name.c,
890       answer->rdata->u.name.c + DomainNameLength(&answer->rdata->u.name)));
891 
892   auto* adapter =
893       reinterpret_cast<MdnsResponderAdapterImpl*>(question->QuestionContext);
894   OSP_DCHECK(adapter);
895   auto event_type = QueryEventHeader::Type::kAddedNoCache;
896   if (added == QC_add) {
897     event_type = QueryEventHeader::Type::kAdded;
898   } else if (added == QC_rmv) {
899     event_type = QueryEventHeader::Type::kRemoved;
900   } else {
901     OSP_DCHECK_EQ(added, QC_addnocache);
902   }
903   adapter->ptr_responses_.emplace_back(
904       QueryEventHeader{event_type,
905                        reinterpret_cast<UdpSocket*>(answer->InterfaceID)},
906       std::move(result));
907 }
908 
909 // static
SrvQueryCallback(mDNS * m,DNSQuestion * question,const ResourceRecord * answer,QC_result added)910 void MdnsResponderAdapterImpl::SrvQueryCallback(mDNS* m,
911                                                 DNSQuestion* question,
912                                                 const ResourceRecord* answer,
913                                                 QC_result added) {
914   TRACE_SCOPED(TraceCategory::kMdns,
915                "MdnsResponderAdapterImpl::SrvQueryCallback");
916   OSP_DCHECK(question);
917   OSP_DCHECK(answer);
918   OSP_DCHECK_EQ(answer->rrtype, kDNSType_SRV);
919   DomainName service(std::vector<uint8_t>(
920       question->qname.c,
921       question->qname.c + DomainNameLength(&question->qname)));
922   DomainName result(
923       std::vector<uint8_t>(answer->rdata->u.srv.target.c,
924                            answer->rdata->u.srv.target.c +
925                                DomainNameLength(&answer->rdata->u.srv.target)));
926 
927   auto* adapter =
928       reinterpret_cast<MdnsResponderAdapterImpl*>(question->QuestionContext);
929   OSP_DCHECK(adapter);
930   auto event_type = QueryEventHeader::Type::kAddedNoCache;
931   if (added == QC_add) {
932     event_type = QueryEventHeader::Type::kAdded;
933   } else if (added == QC_rmv) {
934     event_type = QueryEventHeader::Type::kRemoved;
935   } else {
936     OSP_DCHECK_EQ(added, QC_addnocache);
937   }
938   adapter->srv_responses_.emplace_back(
939       QueryEventHeader{event_type,
940                        reinterpret_cast<UdpSocket*>(answer->InterfaceID)},
941       std::move(service), std::move(result),
942       GetNetworkOrderPort(answer->rdata->u.srv.port));
943 }
944 
945 // static
TxtQueryCallback(mDNS * m,DNSQuestion * question,const ResourceRecord * answer,QC_result added)946 void MdnsResponderAdapterImpl::TxtQueryCallback(mDNS* m,
947                                                 DNSQuestion* question,
948                                                 const ResourceRecord* answer,
949                                                 QC_result added) {
950   OSP_DCHECK(question);
951   OSP_DCHECK(answer);
952   OSP_DCHECK_EQ(answer->rrtype, kDNSType_TXT);
953   DomainName service(std::vector<uint8_t>(
954       question->qname.c,
955       question->qname.c + DomainNameLength(&question->qname)));
956   auto lines = ParseTxtResponse(answer->rdata->u.txt.c, answer->rdlength);
957 
958   auto* adapter =
959       reinterpret_cast<MdnsResponderAdapterImpl*>(question->QuestionContext);
960   OSP_DCHECK(adapter);
961   auto event_type = QueryEventHeader::Type::kAddedNoCache;
962   if (added == QC_add) {
963     event_type = QueryEventHeader::Type::kAdded;
964   } else if (added == QC_rmv) {
965     event_type = QueryEventHeader::Type::kRemoved;
966   } else {
967     OSP_DCHECK_EQ(added, QC_addnocache);
968   }
969   adapter->txt_responses_.emplace_back(
970       QueryEventHeader{event_type,
971                        reinterpret_cast<UdpSocket*>(answer->InterfaceID)},
972       std::move(service), std::move(lines));
973 }
974 
975 // static
ServiceCallback(mDNS * m,ServiceRecordSet * service_record,mStatus result)976 void MdnsResponderAdapterImpl::ServiceCallback(mDNS* m,
977                                                ServiceRecordSet* service_record,
978                                                mStatus result) {
979   // TODO(btolsch): Handle mStatus_NameConflict.
980   if (result == mStatus_MemFree) {
981     OSP_DLOG_INFO << "free service record";
982     auto* adapter = reinterpret_cast<MdnsResponderAdapterImpl*>(
983         service_record->ServiceContext);
984     auto& service_records = adapter->service_records_;
985     service_records.erase(
986         std::remove_if(
987             service_records.begin(), service_records.end(),
988             [service_record](const std::unique_ptr<ServiceRecordSet>& sr) {
989               return sr.get() == service_record;
990             }),
991         service_records.end());
992 
993     if (service_records.empty())
994       adapter->DeadvertiseInterfaces();
995   }
996 }
997 
AdvertiseInterfaces()998 void MdnsResponderAdapterImpl::AdvertiseInterfaces() {
999   TRACE_SCOPED(TraceCategory::kMdns,
1000                "MdnsResponderAdapterImpl::AdvertiseInterfaces");
1001   for (auto& info : responder_interface_info_) {
1002     UdpSocket* socket = info.first;
1003     NetworkInterfaceInfo& interface_info = info.second;
1004     mDNS_SetupResourceRecord(&interface_info.RR_A, /** RDataStorage */ nullptr,
1005                              reinterpret_cast<mDNSInterfaceID>(socket),
1006                              kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique,
1007                              AuthRecordAny,
1008                              /** Callback */ nullptr, /** Context */ nullptr);
1009     AssignDomainName(&interface_info.RR_A.namestorage,
1010                      &mdns_.MulticastHostname);
1011     if (interface_info.ip.type == mDNSAddrType_IPv4) {
1012       interface_info.RR_A.resrec.rdata->u.ipv4 = interface_info.ip.ip.v4;
1013     } else {
1014       interface_info.RR_A.resrec.rdata->u.ipv6 = interface_info.ip.ip.v6;
1015     }
1016     mDNS_Register(&mdns_, &interface_info.RR_A);
1017   }
1018 }
1019 
DeadvertiseInterfaces()1020 void MdnsResponderAdapterImpl::DeadvertiseInterfaces() {
1021   // Both loops below use the A resource record's domain name to determine
1022   // whether the record was advertised.  AdvertiseInterfaces sets the domain
1023   // name before registering the A record, and this clears it after
1024   // deregistering.
1025   for (auto& info : responder_interface_info_) {
1026     NetworkInterfaceInfo& interface_info = info.second;
1027     if (interface_info.RR_A.namestorage.c[0]) {
1028       mDNS_Deregister(&mdns_, &interface_info.RR_A);
1029       interface_info.RR_A.namestorage.c[0] = 0;
1030     }
1031   }
1032 }
1033 
RemoveQuestionsIfEmpty(UdpSocket * socket)1034 void MdnsResponderAdapterImpl::RemoveQuestionsIfEmpty(UdpSocket* socket) {
1035   auto entry = socket_to_questions_.find(socket);
1036   bool empty = entry->second.a.empty() || entry->second.aaaa.empty() ||
1037                entry->second.ptr.empty() || entry->second.srv.empty() ||
1038                entry->second.txt.empty();
1039   if (empty)
1040     socket_to_questions_.erase(entry);
1041 }
1042 
1043 }  // namespace osp
1044 }  // namespace openscreen
1045