diff options
Diffstat (limited to 'osp/impl/discovery/mdns/mdns_responder_adapter_impl.cc')
-rw-r--r-- | osp/impl/discovery/mdns/mdns_responder_adapter_impl.cc | 1044 |
1 files changed, 0 insertions, 1044 deletions
diff --git a/osp/impl/discovery/mdns/mdns_responder_adapter_impl.cc b/osp/impl/discovery/mdns/mdns_responder_adapter_impl.cc deleted file mode 100644 index 205e125b..00000000 --- a/osp/impl/discovery/mdns/mdns_responder_adapter_impl.cc +++ /dev/null @@ -1,1044 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "osp/impl/discovery/mdns/mdns_responder_adapter_impl.h" - -#include <algorithm> -#include <cctype> -#include <cstring> -#include <iostream> -#include <memory> -#include <string> -#include <utility> - -#include "util/osp_logging.h" -#include "util/trace_logging.h" - -namespace openscreen { -namespace osp { -namespace { - -// RFC 1035 specifies a max string length of 256, including the leading length -// octet. -constexpr size_t kMaxDnsStringLength = 255; - -// RFC 6763 recommends a maximum key length of 9 characters. -constexpr size_t kMaxTxtKeyLength = 9; - -constexpr size_t kMaxStaticTxtDataSize = 256; - -static_assert(sizeof(std::declval<RData>().u.txt) == kMaxStaticTxtDataSize, - "mDNSResponder static TXT data size expected to be 256 bytes"); - -static_assert(sizeof(mDNSAddr::ip.v4.b) == 4u, - "mDNSResponder IPv4 address must be 4 bytes"); -static_assert(sizeof(mDNSAddr::ip.v6.b) == 16u, - "mDNSResponder IPv6 address must be 16 bytes"); - -void AssignMdnsPort(mDNSIPPort* mdns_port, uint16_t port) { - mdns_port->b[0] = (port >> 8) & 0xff; - mdns_port->b[1] = port & 0xff; -} - -uint16_t GetNetworkOrderPort(const mDNSOpaque16& port) { - return port.b[0] << 8 | port.b[1]; -} - -bool IsValidServiceName(const std::string& service_name) { - // Service name requirements come from RFC 6335: - // - No more than 16 characters. - // - Begin with '_'. - // - Next is a letter or digit and end with a letter or digit. - // - May contain hyphens, but no consecutive hyphens. - // - Must contain at least one letter. - if (service_name.size() <= 1 || service_name.size() > 16) - return false; - - if (service_name[0] != '_' || !std::isalnum(service_name[1]) || - !std::isalnum(service_name.back())) { - return false; - } - bool has_alpha = false; - bool previous_hyphen = false; - for (auto it = service_name.begin() + 1; it != service_name.end(); ++it) { - if (*it == '-' && previous_hyphen) - return false; - - previous_hyphen = *it == '-'; - has_alpha = has_alpha || std::isalpha(*it); - } - return has_alpha && !previous_hyphen; -} - -bool IsValidServiceProtocol(const std::string& protocol) { - // RFC 6763 requires _tcp be used for TCP services and _udp for all others. - return protocol == "_tcp" || protocol == "_udp"; -} - -void 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) { - MakeDomainLabelFromLiteralString(instance, service_instance.c_str()); - MakeDomainLabelFromLiteralString(name, service_name.c_str()); - MakeDomainLabelFromLiteralString(protocol, service_protocol.c_str()); - type->c[0] = 0; - AppendDomainLabel(type, name); - AppendDomainLabel(type, protocol); - const DomainName local_domain = DomainName::GetLocalDomain(); - std::copy(local_domain.domain_name().begin(), - local_domain.domain_name().end(), domain->c); -} - -void MakeSubnetMaskFromPrefixLengthV4(uint8_t mask[4], uint8_t prefix_length) { - for (int i = 0; i < 4; prefix_length -= 8, ++i) { - if (prefix_length >= 8) { - mask[i] = 0xff; - } else if (prefix_length > 0) { - mask[i] = 0xff << (8 - prefix_length); - } else { - mask[i] = 0; - } - } -} - -void MakeSubnetMaskFromPrefixLengthV6(uint8_t mask[16], uint8_t prefix_length) { - for (int i = 0; i < 16; prefix_length -= 8, ++i) { - if (prefix_length >= 8) { - mask[i] = 0xff; - } else if (prefix_length > 0) { - mask[i] = 0xff << (8 - prefix_length); - } else { - mask[i] = 0; - } - } -} - -bool IsValidTxtDataKey(const std::string& s) { - if (s.size() > kMaxTxtKeyLength) - return false; - for (unsigned char c : s) - if (c < 0x20 || c > 0x7e || c == '=') - return false; - return true; -} - -std::string MakeTxtData(const std::map<std::string, std::string>& txt_data) { - std::string txt; - txt.reserve(kMaxStaticTxtDataSize); - for (const auto& line : txt_data) { - const auto key_size = line.first.size(); - const auto value_size = line.second.size(); - const auto line_size = value_size ? (key_size + 1 + value_size) : key_size; - if (!IsValidTxtDataKey(line.first) || line_size > kMaxDnsStringLength || - (txt.size() + 1 + line_size) > kMaxStaticTxtDataSize) { - return {}; - } - txt.push_back(line_size); - txt += line.first; - if (value_size) { - txt.push_back('='); - txt += line.second; - } - } - return txt; -} - -MdnsResponderErrorCode MapMdnsError(int err) { - switch (err) { - case mStatus_NoError: - return MdnsResponderErrorCode::kNoError; - case mStatus_UnsupportedErr: - return MdnsResponderErrorCode::kUnsupportedError; - case mStatus_UnknownErr: - return MdnsResponderErrorCode::kUnknownError; - default: - break; - } - OSP_DLOG_WARN << "unmapped mDNSResponder error: " << err; - return MdnsResponderErrorCode::kUnknownError; -} - -std::vector<std::string> ParseTxtResponse( - const uint8_t data[kMaxStaticTxtDataSize], - uint16_t length) { - OSP_DCHECK(length <= kMaxStaticTxtDataSize); - if (length == 0) - return {}; - - std::vector<std::string> lines; - int total_pos = 0; - while (total_pos < length) { - uint8_t line_length = data[total_pos]; - if ((line_length > kMaxDnsStringLength) || - (total_pos + line_length >= length)) { - return {}; - } - lines.emplace_back(&data[total_pos + 1], - &data[total_pos + line_length + 1]); - total_pos += line_length + 1; - } - return lines; -} - -void MdnsStatusCallback(mDNS* mdns, mStatus result) { - OSP_LOG_INFO << "status good? " << (result == mStatus_NoError); -} - -} // namespace - -MdnsResponderAdapterImpl::MdnsResponderAdapterImpl() = default; -MdnsResponderAdapterImpl::~MdnsResponderAdapterImpl() = default; - -Error MdnsResponderAdapterImpl::Init() { - const auto err = - mDNS_Init(&mdns_, &platform_storage_, rr_cache_, kRrCacheSize, - mDNS_Init_DontAdvertiseLocalAddresses, &MdnsStatusCallback, - mDNS_Init_NoInitCallbackContext); - - return (err == mStatus_NoError) ? Error::None() - : Error::Code::kInitializationFailure; -} - -void MdnsResponderAdapterImpl::Close() { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::Close"); - mDNS_StartExit(&mdns_); - // Let all services send goodbyes. - while (!service_records_.empty()) { - RunTasks(); - } - mDNS_FinalExit(&mdns_); - - socket_to_questions_.clear(); - - responder_interface_info_.clear(); - - a_responses_.clear(); - aaaa_responses_.clear(); - ptr_responses_.clear(); - srv_responses_.clear(); - txt_responses_.clear(); - - service_records_.clear(); -} - -Error MdnsResponderAdapterImpl::SetHostLabel(const std::string& host_label) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::SetHostLabel"); - if (host_label.size() > DomainName::kDomainNameMaxLabelLength) - return Error::Code::kDomainNameTooLong; - - MakeDomainLabelFromLiteralString(&mdns_.hostlabel, host_label.c_str()); - mDNS_SetFQDN(&mdns_); - if (!service_records_.empty()) { - DeadvertiseInterfaces(); - AdvertiseInterfaces(); - } - return Error::None(); -} - -Error MdnsResponderAdapterImpl::RegisterInterface( - const InterfaceInfo& interface_info, - const IPSubnet& interface_address, - UdpSocket* socket) { - TRACE_SCOPED(TraceCategory::kMdns, - "MdnsResponderAdapterImpl::RegisterInterface"); - OSP_DCHECK(socket); - - const auto info_it = responder_interface_info_.find(socket); - if (info_it != responder_interface_info_.end()) - return Error::None(); - - NetworkInterfaceInfo& info = responder_interface_info_[socket]; - std::memset(&info, 0, sizeof(NetworkInterfaceInfo)); - info.InterfaceID = reinterpret_cast<decltype(info.InterfaceID)>(socket); - info.Advertise = mDNSfalse; - if (interface_address.address.IsV4()) { - info.ip.type = mDNSAddrType_IPv4; - interface_address.address.CopyToV4(info.ip.ip.v4.b); - info.mask.type = mDNSAddrType_IPv4; - MakeSubnetMaskFromPrefixLengthV4(info.mask.ip.v4.b, - interface_address.prefix_length); - } else { - info.ip.type = mDNSAddrType_IPv6; - interface_address.address.CopyToV6(info.ip.ip.v6.b); - info.mask.type = mDNSAddrType_IPv6; - MakeSubnetMaskFromPrefixLengthV6(info.mask.ip.v6.b, - interface_address.prefix_length); - } - - static_assert(sizeof(info.MAC.b) == sizeof(interface_info.hardware_address), - "MAC address size mismatch."); - memcpy(info.MAC.b, interface_info.hardware_address.data(), - sizeof(info.MAC.b)); - info.McastTxRx = 1; - platform_storage_.sockets.push_back(socket); - auto result = mDNS_RegisterInterface(&mdns_, &info, mDNSfalse); - OSP_LOG_IF(WARN, result != mStatus_NoError) - << "mDNS_RegisterInterface failed: " << result; - - return (result == mStatus_NoError) ? Error::None() - : Error::Code::kMdnsRegisterFailure; -} - -Error MdnsResponderAdapterImpl::DeregisterInterface(UdpSocket* socket) { - TRACE_SCOPED(TraceCategory::kMdns, - "MdnsResponderAdapterImpl::DeregisterInterface"); - const auto info_it = responder_interface_info_.find(socket); - if (info_it == responder_interface_info_.end()) - return Error::Code::kItemNotFound; - - const auto it = std::find(platform_storage_.sockets.begin(), - platform_storage_.sockets.end(), socket); - OSP_DCHECK(it != platform_storage_.sockets.end()); - platform_storage_.sockets.erase(it); - if (info_it->second.RR_A.namestorage.c[0]) { - mDNS_Deregister(&mdns_, &info_it->second.RR_A); - info_it->second.RR_A.namestorage.c[0] = 0; - } - mDNS_DeregisterInterface(&mdns_, &info_it->second, mDNSfalse); - responder_interface_info_.erase(info_it); - return Error::None(); -} -void MdnsResponderAdapterImpl::OnRead(UdpSocket* socket, - ErrorOr<UdpPacket> packet_or_error) { - if (packet_or_error.is_error()) { - return; - } - - UdpPacket packet = std::move(packet_or_error.value()); - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::OnRead"); - mDNSAddr src; - if (packet.source().address.IsV4()) { - src.type = mDNSAddrType_IPv4; - packet.source().address.CopyToV4(src.ip.v4.b); - } else { - src.type = mDNSAddrType_IPv6; - packet.source().address.CopyToV6(src.ip.v6.b); - } - mDNSIPPort srcport; - AssignMdnsPort(&srcport, packet.source().port); - - mDNSAddr dst; - if (packet.source().address.IsV4()) { - dst.type = mDNSAddrType_IPv4; - packet.destination().address.CopyToV4(dst.ip.v4.b); - } else { - dst.type = mDNSAddrType_IPv6; - packet.destination().address.CopyToV6(dst.ip.v6.b); - } - mDNSIPPort dstport; - AssignMdnsPort(&dstport, packet.destination().port); - - auto* packet_data = packet.data(); - mDNSCoreReceive(&mdns_, const_cast<uint8_t*>(packet_data), - packet_data + packet.size(), &src, srcport, &dst, dstport, - reinterpret_cast<mDNSInterfaceID>(packet.socket())); -} - -void MdnsResponderAdapterImpl::OnSendError(UdpSocket* socket, Error error) { - // TODO(crbug.com/openscreen/67): Implement this method. - OSP_UNIMPLEMENTED(); -} - -void MdnsResponderAdapterImpl::OnError(UdpSocket* socket, Error error) { - // TODO(crbug.com/openscreen/67): Implement this method. - OSP_UNIMPLEMENTED(); -} - -void MdnsResponderAdapterImpl::OnBound(UdpSocket* socket) { - // TODO(crbug.com/openscreen/67): Implement this method. - OSP_UNIMPLEMENTED(); -} - -Clock::duration MdnsResponderAdapterImpl::RunTasks() { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::RunTasks"); - - mDNS_Execute(&mdns_); - - // Using mDNS_Execute's response to determine the correct timespan before - // re-running this method doesn't work as expected. In the demo, under some - // cases (about 25% of demo runs), the response is set to an unreasonably - // large number (in the order of multiple days). - // - // From the mDNS documentation: "it is the responsibility [...] to set the - // timer according to the m->NextScheduledEvent value, and then when the timer - // fires, the timer callback function should call mDNS_Execute()" - for more - // details see third_party/mDNSResponder/src/mDNSCore/mDNS.c : 3390 - // - // Together, I understand these to mean that the mdns library code doesn't - // expect we need mDNS_Execute called again by the task runner, only in the - // other special cases it calls out in documentation (which we currently do - // correctly). In our code, when we call mDNS_Execute again outside of the - // task runner, the result is currently discarded. What we would need to do is - // reach into the Task Runner's task and update how long before the task runs - // again. That would require some large refactoring and changes. - // - // Additionally, beyond this, the mDNS code documents that there are cases - // where the return value for mDNS_Execute should be ignored because it may be - // stale. - // - // TODO(rwkeane): More accurately determine when the next run of this method - // should be. - constexpr auto seconds_before_next_run = 1; - - // Return as a duration. - return std::chrono::seconds(seconds_before_next_run); -} - -std::vector<PtrEvent> MdnsResponderAdapterImpl::TakePtrResponses() { - return std::move(ptr_responses_); -} - -std::vector<SrvEvent> MdnsResponderAdapterImpl::TakeSrvResponses() { - return std::move(srv_responses_); -} - -std::vector<TxtEvent> MdnsResponderAdapterImpl::TakeTxtResponses() { - return std::move(txt_responses_); -} - -std::vector<AEvent> MdnsResponderAdapterImpl::TakeAResponses() { - return std::move(a_responses_); -} - -std::vector<AaaaEvent> MdnsResponderAdapterImpl::TakeAaaaResponses() { - return std::move(aaaa_responses_); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::StartPtrQuery( - UdpSocket* socket, - const DomainName& service_type) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StartPtrQuery"); - auto& ptr_questions = socket_to_questions_[socket].ptr; - if (ptr_questions.find(service_type) != ptr_questions.end()) - return MdnsResponderErrorCode::kNoError; - - auto& question = ptr_questions[service_type]; - - question.InterfaceID = reinterpret_cast<mDNSInterfaceID>(socket); - question.Target = {0}; - if (service_type.EndsWithLocalDomain()) { - std::copy(service_type.domain_name().begin(), - service_type.domain_name().end(), question.qname.c); - } else { - const DomainName local_domain = DomainName::GetLocalDomain(); - ErrorOr<DomainName> service_type_with_local = - DomainName::Append(service_type, local_domain); - if (!service_type_with_local) { - return MdnsResponderErrorCode::kDomainOverflowError; - } - std::copy(service_type_with_local.value().domain_name().begin(), - service_type_with_local.value().domain_name().end(), - question.qname.c); - } - question.qtype = kDNSType_PTR; - question.qclass = kDNSClass_IN; - question.LongLived = mDNStrue; - question.ExpectUnique = mDNSfalse; - question.ForceMCast = mDNStrue; - question.ReturnIntermed = mDNSfalse; - question.SuppressUnusable = mDNSfalse; - question.RetryWithSearchDomains = mDNSfalse; - question.TimeoutQuestion = 0; - question.WakeOnResolve = 0; - question.SearchListIndex = 0; - question.AppendSearchDomains = 0; - question.AppendLocalSearchDomains = 0; - question.qnameOrig = nullptr; - question.QuestionCallback = &MdnsResponderAdapterImpl::PtrQueryCallback; - question.QuestionContext = this; - const auto err = mDNS_StartQuery(&mdns_, &question); - OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StartQuery failed: " << err; - return MapMdnsError(err); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::StartSrvQuery( - UdpSocket* socket, - const DomainName& service_instance) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StartSrvQuery"); - if (!service_instance.EndsWithLocalDomain()) - return MdnsResponderErrorCode::kInvalidParameters; - - auto& srv_questions = socket_to_questions_[socket].srv; - if (srv_questions.find(service_instance) != srv_questions.end()) - return MdnsResponderErrorCode::kNoError; - - auto& question = srv_questions[service_instance]; - - question.InterfaceID = reinterpret_cast<mDNSInterfaceID>(socket); - question.Target = {0}; - std::copy(service_instance.domain_name().begin(), - service_instance.domain_name().end(), question.qname.c); - question.qtype = kDNSType_SRV; - question.qclass = kDNSClass_IN; - question.LongLived = mDNStrue; - question.ExpectUnique = mDNSfalse; - question.ForceMCast = mDNStrue; - question.ReturnIntermed = mDNSfalse; - question.SuppressUnusable = mDNSfalse; - question.RetryWithSearchDomains = mDNSfalse; - question.TimeoutQuestion = 0; - question.WakeOnResolve = 0; - question.SearchListIndex = 0; - question.AppendSearchDomains = 0; - question.AppendLocalSearchDomains = 0; - question.qnameOrig = nullptr; - question.QuestionCallback = &MdnsResponderAdapterImpl::SrvQueryCallback; - question.QuestionContext = this; - const auto err = mDNS_StartQuery(&mdns_, &question); - OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StartQuery failed: " << err; - return MapMdnsError(err); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::StartTxtQuery( - UdpSocket* socket, - const DomainName& service_instance) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StartTxtQuery"); - if (!service_instance.EndsWithLocalDomain()) - return MdnsResponderErrorCode::kInvalidParameters; - - auto& txt_questions = socket_to_questions_[socket].txt; - if (txt_questions.find(service_instance) != txt_questions.end()) - return MdnsResponderErrorCode::kNoError; - - auto& question = txt_questions[service_instance]; - - question.InterfaceID = reinterpret_cast<mDNSInterfaceID>(socket); - question.Target = {0}; - std::copy(service_instance.domain_name().begin(), - service_instance.domain_name().end(), question.qname.c); - question.qtype = kDNSType_TXT; - question.qclass = kDNSClass_IN; - question.LongLived = mDNStrue; - question.ExpectUnique = mDNSfalse; - question.ForceMCast = mDNStrue; - question.ReturnIntermed = mDNSfalse; - question.SuppressUnusable = mDNSfalse; - question.RetryWithSearchDomains = mDNSfalse; - question.TimeoutQuestion = 0; - question.WakeOnResolve = 0; - question.SearchListIndex = 0; - question.AppendSearchDomains = 0; - question.AppendLocalSearchDomains = 0; - question.qnameOrig = nullptr; - question.QuestionCallback = &MdnsResponderAdapterImpl::TxtQueryCallback; - question.QuestionContext = this; - const auto err = mDNS_StartQuery(&mdns_, &question); - OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StartQuery failed: " << err; - return MapMdnsError(err); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::StartAQuery( - UdpSocket* socket, - const DomainName& domain_name) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StartAQuery"); - if (!domain_name.EndsWithLocalDomain()) - return MdnsResponderErrorCode::kInvalidParameters; - - auto& a_questions = socket_to_questions_[socket].a; - if (a_questions.find(domain_name) != a_questions.end()) - return MdnsResponderErrorCode::kNoError; - - auto& question = a_questions[domain_name]; - std::copy(domain_name.domain_name().begin(), domain_name.domain_name().end(), - question.qname.c); - - question.InterfaceID = reinterpret_cast<mDNSInterfaceID>(socket); - question.Target = {0}; - question.qtype = kDNSType_A; - question.qclass = kDNSClass_IN; - question.LongLived = mDNStrue; - question.ExpectUnique = mDNSfalse; - question.ForceMCast = mDNStrue; - question.ReturnIntermed = mDNSfalse; - question.SuppressUnusable = mDNSfalse; - question.RetryWithSearchDomains = mDNSfalse; - question.TimeoutQuestion = 0; - question.WakeOnResolve = 0; - question.SearchListIndex = 0; - question.AppendSearchDomains = 0; - question.AppendLocalSearchDomains = 0; - question.qnameOrig = nullptr; - question.QuestionCallback = &MdnsResponderAdapterImpl::AQueryCallback; - question.QuestionContext = this; - const auto err = mDNS_StartQuery(&mdns_, &question); - OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StartQuery failed: " << err; - return MapMdnsError(err); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::StartAaaaQuery( - UdpSocket* socket, - const DomainName& domain_name) { - TRACE_SCOPED(TraceCategory::kMdns, - "MdnsResponderAdapterImpl::StartAaaaQuery"); - if (!domain_name.EndsWithLocalDomain()) - return MdnsResponderErrorCode::kInvalidParameters; - - auto& aaaa_questions = socket_to_questions_[socket].aaaa; - if (aaaa_questions.find(domain_name) != aaaa_questions.end()) - return MdnsResponderErrorCode::kNoError; - - auto& question = aaaa_questions[domain_name]; - std::copy(domain_name.domain_name().begin(), domain_name.domain_name().end(), - question.qname.c); - - question.InterfaceID = reinterpret_cast<mDNSInterfaceID>(socket); - question.Target = {0}; - question.qtype = kDNSType_AAAA; - question.qclass = kDNSClass_IN; - question.LongLived = mDNStrue; - question.ExpectUnique = mDNSfalse; - question.ForceMCast = mDNStrue; - question.ReturnIntermed = mDNSfalse; - question.SuppressUnusable = mDNSfalse; - question.RetryWithSearchDomains = mDNSfalse; - question.TimeoutQuestion = 0; - question.WakeOnResolve = 0; - question.SearchListIndex = 0; - question.AppendSearchDomains = 0; - question.AppendLocalSearchDomains = 0; - question.qnameOrig = nullptr; - question.QuestionCallback = &MdnsResponderAdapterImpl::AaaaQueryCallback; - question.QuestionContext = this; - const auto err = mDNS_StartQuery(&mdns_, &question); - OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StartQuery failed: " << err; - return MapMdnsError(err); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::StopPtrQuery( - UdpSocket* socket, - const DomainName& service_type) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StopPtrQuery"); - auto interface_entry = socket_to_questions_.find(socket); - if (interface_entry == socket_to_questions_.end()) - return MdnsResponderErrorCode::kNoError; - auto entry = interface_entry->second.ptr.find(service_type); - if (entry == interface_entry->second.ptr.end()) - return MdnsResponderErrorCode::kNoError; - - const auto err = mDNS_StopQuery(&mdns_, &entry->second); - interface_entry->second.ptr.erase(entry); - OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StopQuery failed: " << err; - RemoveQuestionsIfEmpty(socket); - return MapMdnsError(err); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::StopSrvQuery( - UdpSocket* socket, - const DomainName& service_instance) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StopSrvQuery"); - auto interface_entry = socket_to_questions_.find(socket); - if (interface_entry == socket_to_questions_.end()) - return MdnsResponderErrorCode::kNoError; - auto entry = interface_entry->second.srv.find(service_instance); - if (entry == interface_entry->second.srv.end()) - return MdnsResponderErrorCode::kNoError; - - const auto err = mDNS_StopQuery(&mdns_, &entry->second); - interface_entry->second.srv.erase(entry); - OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StopQuery failed: " << err; - RemoveQuestionsIfEmpty(socket); - return MapMdnsError(err); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::StopTxtQuery( - UdpSocket* socket, - const DomainName& service_instance) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StopTxtQuery"); - auto interface_entry = socket_to_questions_.find(socket); - if (interface_entry == socket_to_questions_.end()) - return MdnsResponderErrorCode::kNoError; - auto entry = interface_entry->second.txt.find(service_instance); - if (entry == interface_entry->second.txt.end()) - return MdnsResponderErrorCode::kNoError; - - const auto err = mDNS_StopQuery(&mdns_, &entry->second); - interface_entry->second.txt.erase(entry); - OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StopQuery failed: " << err; - RemoveQuestionsIfEmpty(socket); - return MapMdnsError(err); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::StopAQuery( - UdpSocket* socket, - const DomainName& domain_name) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StopAQuery"); - auto interface_entry = socket_to_questions_.find(socket); - if (interface_entry == socket_to_questions_.end()) - return MdnsResponderErrorCode::kNoError; - auto entry = interface_entry->second.a.find(domain_name); - if (entry == interface_entry->second.a.end()) - return MdnsResponderErrorCode::kNoError; - - const auto err = mDNS_StopQuery(&mdns_, &entry->second); - interface_entry->second.a.erase(entry); - OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StopQuery failed: " << err; - RemoveQuestionsIfEmpty(socket); - return MapMdnsError(err); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::StopAaaaQuery( - UdpSocket* socket, - const DomainName& domain_name) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::StopAaaaQuery"); - auto interface_entry = socket_to_questions_.find(socket); - if (interface_entry == socket_to_questions_.end()) - return MdnsResponderErrorCode::kNoError; - auto entry = interface_entry->second.aaaa.find(domain_name); - if (entry == interface_entry->second.aaaa.end()) - return MdnsResponderErrorCode::kNoError; - - const auto err = mDNS_StopQuery(&mdns_, &entry->second); - interface_entry->second.aaaa.erase(entry); - OSP_LOG_IF(WARN, err != mStatus_NoError) << "mDNS_StopQuery failed: " << err; - RemoveQuestionsIfEmpty(socket); - return MapMdnsError(err); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::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) { - TRACE_SCOPED(TraceCategory::kMdns, - "MdnsResponderAdapterImpl::RegisterService"); - OSP_DCHECK(IsValidServiceName(service_name)); - OSP_DCHECK(IsValidServiceProtocol(service_protocol)); - service_records_.push_back(std::make_unique<ServiceRecordSet>()); - auto* service_record = service_records_.back().get(); - domainlabel instance; - domainlabel name; - domainlabel protocol; - domainname type; - domainname domain; - domainname host; - mDNSIPPort port; - - MakeLocalServiceNameParts(service_instance, service_name, service_protocol, - &instance, &name, &protocol, &type, &domain); - std::copy(target_host.domain_name().begin(), target_host.domain_name().end(), - host.c); - AssignMdnsPort(&port, target_port); - auto txt = MakeTxtData(txt_data); - if (txt.size() > kMaxStaticTxtDataSize) { - // Not handling oversized TXT records. - return MdnsResponderErrorCode::kUnsupportedError; - } - - if (service_records_.size() == 1) - AdvertiseInterfaces(); - - auto result = mDNS_RegisterService( - &mdns_, service_record, &instance, &type, &domain, &host, port, - reinterpret_cast<const uint8_t*>(txt.data()), txt.size(), nullptr, 0, - mDNSInterface_Any, &MdnsResponderAdapterImpl::ServiceCallback, this, 0); - - if (result != mStatus_NoError) { - service_records_.pop_back(); - if (service_records_.empty()) - DeadvertiseInterfaces(); - } - return MapMdnsError(result); -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::DeregisterService( - const std::string& service_instance, - const std::string& service_name, - const std::string& service_protocol) { - TRACE_SCOPED(TraceCategory::kMdns, - "MdnsResponderAdapterImpl::DeregisterService"); - domainlabel instance; - domainlabel name; - domainlabel protocol; - domainname type; - domainname domain; - domainname full_instance_name; - - MakeLocalServiceNameParts(service_instance, service_name, service_protocol, - &instance, &name, &protocol, &type, &domain); - if (!ConstructServiceName(&full_instance_name, &instance, &type, &domain)) - return MdnsResponderErrorCode::kInvalidParameters; - - for (auto it = service_records_.begin(); it != service_records_.end(); ++it) { - if (SameDomainName(&full_instance_name, &(*it)->RR_SRV.namestorage)) { - // |it| will be removed from |service_records_| in ServiceCallback, when - // mDNSResponder is done with the memory. - mDNS_DeregisterService(&mdns_, it->get()); - return MdnsResponderErrorCode::kNoError; - } - } - return MdnsResponderErrorCode::kNoError; -} - -MdnsResponderErrorCode MdnsResponderAdapterImpl::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) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderAdapterImpl::UpdateTxtData"); - domainlabel instance; - domainlabel name; - domainlabel protocol; - domainname type; - domainname domain; - domainname full_instance_name; - - MakeLocalServiceNameParts(service_instance, service_name, service_protocol, - &instance, &name, &protocol, &type, &domain); - if (!ConstructServiceName(&full_instance_name, &instance, &type, &domain)) - return MdnsResponderErrorCode::kInvalidParameters; - std::string txt = MakeTxtData(txt_data); - if (txt.size() > kMaxStaticTxtDataSize) { - // Not handling oversized TXT records. - return MdnsResponderErrorCode::kUnsupportedError; - } - - for (std::unique_ptr<ServiceRecordSet>& record : service_records_) { - if (SameDomainName(&full_instance_name, &record->RR_SRV.namestorage)) { - std::copy(txt.begin(), txt.end(), record->RR_TXT.rdatastorage.u.txt.c); - mDNS_Update(&mdns_, &record->RR_TXT, 0, txt.size(), - &record->RR_TXT.rdatastorage, nullptr); - return MdnsResponderErrorCode::kNoError; - } - } - return MdnsResponderErrorCode::kNoError; -} - -// static -void MdnsResponderAdapterImpl::AQueryCallback(mDNS* m, - DNSQuestion* question, - const ResourceRecord* answer, - QC_result added) { - TRACE_SCOPED(TraceCategory::kMdns, - "MdnsResponderAdapterImpl::AQueryCallback"); - OSP_DCHECK(question); - OSP_DCHECK(answer); - OSP_DCHECK_EQ(answer->rrtype, kDNSType_A); - DomainName domain(std::vector<uint8_t>( - question->qname.c, - question->qname.c + DomainNameLength(&question->qname))); - IPAddress address(answer->rdata->u.ipv4.b); - - auto* adapter = - reinterpret_cast<MdnsResponderAdapterImpl*>(question->QuestionContext); - OSP_DCHECK(adapter); - auto event_type = QueryEventHeader::Type::kAddedNoCache; - if (added == QC_add) { - event_type = QueryEventHeader::Type::kAdded; - } else if (added == QC_rmv) { - event_type = QueryEventHeader::Type::kRemoved; - } else { - OSP_DCHECK_EQ(added, QC_addnocache); - } - adapter->a_responses_.emplace_back( - QueryEventHeader{event_type, - reinterpret_cast<UdpSocket*>(answer->InterfaceID)}, - std::move(domain), address); -} - -// static -void MdnsResponderAdapterImpl::AaaaQueryCallback(mDNS* m, - DNSQuestion* question, - const ResourceRecord* answer, - QC_result added) { - TRACE_SCOPED(TraceCategory::kMdns, - "MdnsResponderAdapterImpl::AaaaQueryCallback"); - OSP_DCHECK(question); - OSP_DCHECK(answer); - OSP_DCHECK_EQ(answer->rrtype, kDNSType_A); - DomainName domain(std::vector<uint8_t>( - question->qname.c, - question->qname.c + DomainNameLength(&question->qname))); - IPAddress address(IPAddress::Version::kV6, answer->rdata->u.ipv6.b); - - auto* adapter = - reinterpret_cast<MdnsResponderAdapterImpl*>(question->QuestionContext); - OSP_DCHECK(adapter); - auto event_type = QueryEventHeader::Type::kAddedNoCache; - if (added == QC_add) { - event_type = QueryEventHeader::Type::kAdded; - } else if (added == QC_rmv) { - event_type = QueryEventHeader::Type::kRemoved; - } else { - OSP_DCHECK_EQ(added, QC_addnocache); - } - adapter->aaaa_responses_.emplace_back( - QueryEventHeader{event_type, - reinterpret_cast<UdpSocket*>(answer->InterfaceID)}, - std::move(domain), address); -} - -// static -void MdnsResponderAdapterImpl::PtrQueryCallback(mDNS* m, - DNSQuestion* question, - const ResourceRecord* answer, - QC_result added) { - TRACE_SCOPED(TraceCategory::kMdns, - "MdnsResponderAdapterImpl::PtrQueryCallback"); - OSP_DCHECK(question); - OSP_DCHECK(answer); - OSP_DCHECK_EQ(answer->rrtype, kDNSType_PTR); - DomainName result(std::vector<uint8_t>( - answer->rdata->u.name.c, - answer->rdata->u.name.c + DomainNameLength(&answer->rdata->u.name))); - - auto* adapter = - reinterpret_cast<MdnsResponderAdapterImpl*>(question->QuestionContext); - OSP_DCHECK(adapter); - auto event_type = QueryEventHeader::Type::kAddedNoCache; - if (added == QC_add) { - event_type = QueryEventHeader::Type::kAdded; - } else if (added == QC_rmv) { - event_type = QueryEventHeader::Type::kRemoved; - } else { - OSP_DCHECK_EQ(added, QC_addnocache); - } - adapter->ptr_responses_.emplace_back( - QueryEventHeader{event_type, - reinterpret_cast<UdpSocket*>(answer->InterfaceID)}, - std::move(result)); -} - -// static -void MdnsResponderAdapterImpl::SrvQueryCallback(mDNS* m, - DNSQuestion* question, - const ResourceRecord* answer, - QC_result added) { - TRACE_SCOPED(TraceCategory::kMdns, - "MdnsResponderAdapterImpl::SrvQueryCallback"); - OSP_DCHECK(question); - OSP_DCHECK(answer); - OSP_DCHECK_EQ(answer->rrtype, kDNSType_SRV); - DomainName service(std::vector<uint8_t>( - question->qname.c, - question->qname.c + DomainNameLength(&question->qname))); - DomainName result( - std::vector<uint8_t>(answer->rdata->u.srv.target.c, - answer->rdata->u.srv.target.c + - DomainNameLength(&answer->rdata->u.srv.target))); - - auto* adapter = - reinterpret_cast<MdnsResponderAdapterImpl*>(question->QuestionContext); - OSP_DCHECK(adapter); - auto event_type = QueryEventHeader::Type::kAddedNoCache; - if (added == QC_add) { - event_type = QueryEventHeader::Type::kAdded; - } else if (added == QC_rmv) { - event_type = QueryEventHeader::Type::kRemoved; - } else { - OSP_DCHECK_EQ(added, QC_addnocache); - } - adapter->srv_responses_.emplace_back( - QueryEventHeader{event_type, - reinterpret_cast<UdpSocket*>(answer->InterfaceID)}, - std::move(service), std::move(result), - GetNetworkOrderPort(answer->rdata->u.srv.port)); -} - -// static -void MdnsResponderAdapterImpl::TxtQueryCallback(mDNS* m, - DNSQuestion* question, - const ResourceRecord* answer, - QC_result added) { - OSP_DCHECK(question); - OSP_DCHECK(answer); - OSP_DCHECK_EQ(answer->rrtype, kDNSType_TXT); - DomainName service(std::vector<uint8_t>( - question->qname.c, - question->qname.c + DomainNameLength(&question->qname))); - auto lines = ParseTxtResponse(answer->rdata->u.txt.c, answer->rdlength); - - auto* adapter = - reinterpret_cast<MdnsResponderAdapterImpl*>(question->QuestionContext); - OSP_DCHECK(adapter); - auto event_type = QueryEventHeader::Type::kAddedNoCache; - if (added == QC_add) { - event_type = QueryEventHeader::Type::kAdded; - } else if (added == QC_rmv) { - event_type = QueryEventHeader::Type::kRemoved; - } else { - OSP_DCHECK_EQ(added, QC_addnocache); - } - adapter->txt_responses_.emplace_back( - QueryEventHeader{event_type, - reinterpret_cast<UdpSocket*>(answer->InterfaceID)}, - std::move(service), std::move(lines)); -} - -// static -void MdnsResponderAdapterImpl::ServiceCallback(mDNS* m, - ServiceRecordSet* service_record, - mStatus result) { - // TODO(btolsch): Handle mStatus_NameConflict. - if (result == mStatus_MemFree) { - OSP_DLOG_INFO << "free service record"; - auto* adapter = reinterpret_cast<MdnsResponderAdapterImpl*>( - service_record->ServiceContext); - auto& service_records = adapter->service_records_; - service_records.erase( - std::remove_if( - service_records.begin(), service_records.end(), - [service_record](const std::unique_ptr<ServiceRecordSet>& sr) { - return sr.get() == service_record; - }), - service_records.end()); - - if (service_records.empty()) - adapter->DeadvertiseInterfaces(); - } -} - -void MdnsResponderAdapterImpl::AdvertiseInterfaces() { - TRACE_SCOPED(TraceCategory::kMdns, - "MdnsResponderAdapterImpl::AdvertiseInterfaces"); - for (auto& info : responder_interface_info_) { - UdpSocket* socket = info.first; - NetworkInterfaceInfo& interface_info = info.second; - mDNS_SetupResourceRecord(&interface_info.RR_A, /** RDataStorage */ nullptr, - reinterpret_cast<mDNSInterfaceID>(socket), - kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, - AuthRecordAny, - /** Callback */ nullptr, /** Context */ nullptr); - AssignDomainName(&interface_info.RR_A.namestorage, - &mdns_.MulticastHostname); - if (interface_info.ip.type == mDNSAddrType_IPv4) { - interface_info.RR_A.resrec.rdata->u.ipv4 = interface_info.ip.ip.v4; - } else { - interface_info.RR_A.resrec.rdata->u.ipv6 = interface_info.ip.ip.v6; - } - mDNS_Register(&mdns_, &interface_info.RR_A); - } -} - -void MdnsResponderAdapterImpl::DeadvertiseInterfaces() { - // Both loops below use the A resource record's domain name to determine - // whether the record was advertised. AdvertiseInterfaces sets the domain - // name before registering the A record, and this clears it after - // deregistering. - for (auto& info : responder_interface_info_) { - NetworkInterfaceInfo& interface_info = info.second; - if (interface_info.RR_A.namestorage.c[0]) { - mDNS_Deregister(&mdns_, &interface_info.RR_A); - interface_info.RR_A.namestorage.c[0] = 0; - } - } -} - -void MdnsResponderAdapterImpl::RemoveQuestionsIfEmpty(UdpSocket* socket) { - auto entry = socket_to_questions_.find(socket); - bool empty = entry->second.a.empty() || entry->second.aaaa.empty() || - entry->second.ptr.empty() || entry->second.srv.empty() || - entry->second.txt.empty(); - if (empty) - socket_to_questions_.erase(entry); -} - -} // namespace osp -} // namespace openscreen |