diff options
author | mark a. foltz <mfoltz@chromium.org> | 2021-07-15 12:25:07 -0700 |
---|---|---|
committer | Openscreen LUCI CQ <openscreen-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2021-07-16 19:21:35 +0000 |
commit | d29ea545e02197c5166c0e0c76b3c181b3e3b370 (patch) | |
tree | 746c47238a40687f7d46d4280af6499b52688c2e | |
parent | 3eca605e12cb57e49eea49dc0a2ca80a79f93836 (diff) | |
download | openscreen-d29ea545e02197c5166c0e0c76b3c181b3e3b370.tar.gz |
[Open Screen] Replace mDNSResponder.
This replaces the mDNSResponder-based publisher implementation in OSP
with the the Open Screen Library's dnssd module.
third_party/mDNSResponder is removed as well as all of the glue/support
code.
Caveats:
- The OSP listener is not ported to dnssd, that will be done in a future
change.
- Resuming the publisher after Suspend() does not seem to be working
yet.
- The publisher configuration hostname is not used by dnssd.
Bug: b/174207562
Change-Id: I9c7a6339a0f405550effc33ad0d5b5a79fa8e919
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/2535076
Commit-Queue: mark a. foltz <mfoltz@chromium.org>
Reviewed-by: Ryan Keane <rwkeane@google.com>
52 files changed, 365 insertions, 6582 deletions
@@ -37,10 +37,6 @@ group("gn_all") { "osp/msgs", ] - if (use_mdns_responder) { - deps += [ "osp/impl/discovery/mdns:mdns_demo" ] - } - if (use_chromium_quic) { deps += [ "third_party/chromium_quic", @@ -49,7 +45,7 @@ group("gn_all") { ] } - if (use_chromium_quic && use_mdns_responder) { + if (use_chromium_quic) { deps += [ "osp:osp_demo" ] } } @@ -95,15 +91,6 @@ source_set("openscreen_unittests_all") { "osp:unittests", "osp/msgs:unittests", ] - - if (use_mdns_responder) { - public_deps += [ - "osp/impl/discovery/mdns:unittests", - - # Currently this target only includes mDNS tests. - "osp/impl/testing:unittests", - ] - } } } @@ -108,14 +108,6 @@ deps = { 'condition': 'not build_with_chromium', }, - 'third_party/mDNSResponder/src': { - # NOTE: this fork of mDNSResponder is ancient (9 years old), but since - # we are moving away from mDNSResponder we will not be updating this. - 'url': Var('github') + '/jevinskie/mDNSResponder.git' + - '@' + '2942dde61f920fbbf96ff9a3840567ebbe7cb1b6', - 'condition': 'not build_with_chromium', - }, - # Note about updating BoringSSL: after changing this hash, run the update # script in BoringSSL's util folder for generating build files from the # <openscreen src-dir>/third_party/boringssl directory: diff --git a/osp/BUILD.gn b/osp/BUILD.gn index 14bb1ae1..5a2feb44 100644 --- a/osp/BUILD.gn +++ b/osp/BUILD.gn @@ -48,20 +48,13 @@ source_set("unittests") { "public", "public:test_support", ] - - if (use_mdns_responder) { - sources += [ "impl/mdns_responder_service_unittest.cc" ] - - deps += [ "impl/testing" ] - } } -if (use_chromium_quic && use_mdns_responder) { +if (use_chromium_quic) { executable("osp_demo") { sources = [ "demo/osp_demo.cc" ] deps = [ ":osp_with_chromium_quic", - "//osp/impl/discovery/mdns", "//platform", "//util", ] diff --git a/osp/build/config/services.gni b/osp/build/config/services.gni index 1d3d3466..808c123b 100644 --- a/osp/build/config/services.gni +++ b/osp/build/config/services.gni @@ -5,9 +5,7 @@ import("//build_overrides/build.gni") use_chromium_quic = true -use_mdns_responder = true if (build_with_chromium) { use_chromium_quic = false - use_mdns_responder = false } diff --git a/osp/demo/osp_demo.cc b/osp/demo/osp_demo.cc index 952a925f..93f593fa 100644 --- a/osp/demo/osp_demo.cc +++ b/osp/demo/osp_demo.cc @@ -16,7 +16,6 @@ #include "absl/strings/string_view.h" #include "osp/msgs/osp_messages.h" #include "osp/public/mdns_service_listener_factory.h" -#include "osp/public/mdns_service_publisher_factory.h" #include "osp/public/message_demuxer.h" #include "osp/public/network_service_manager.h" #include "osp/public/presentation/presentation_controller.h" @@ -27,6 +26,7 @@ #include "osp/public/protocol_connection_server_factory.h" #include "osp/public/service_listener.h" #include "osp/public/service_publisher.h" +#include "osp/public/service_publisher_factory.h" #include "platform/api/network_interface.h" #include "platform/api/time.h" #include "platform/impl/logging.h" @@ -152,7 +152,9 @@ class DemoPublisherObserver final : public ServicePublisher::Observer { void OnStopped() override { OSP_LOG_INFO << "publisher stopped!"; } void OnSuspended() override { OSP_LOG_INFO << "publisher suspended!"; } - void OnError(ServicePublisherError) override {} + void OnError(Error error) override { + OSP_LOG_ERROR << "publisher error: " << error; + } void OnMetrics(ServicePublisher::Metrics) override {} }; @@ -457,7 +459,10 @@ void HandleReceiverCommand(absl::string_view command, DemoReceiverDelegate& delegate, NetworkServiceManager* manager) { if (command == "avail") { - ServicePublisher* publisher = manager->GetMdnsServicePublisher(); + ServicePublisher* publisher = manager->GetServicePublisher(); + + OSP_LOG_INFO << "publisher->state() == " + << static_cast<int>(publisher->state()); if (publisher->state() == ServicePublisher::State::kSuspended) { publisher->Resume(); @@ -497,7 +502,7 @@ void RunReceiverPollLoop(pollfd& file_descriptor, void CleanupPublisherDemo(NetworkServiceManager* manager) { Receiver::Get()->SetReceiverDelegate(nullptr); Receiver::Get()->Deinit(); - manager->GetMdnsServicePublisher()->Stop(); + manager->GetServicePublisher()->Stop(); manager->GetProtocolConnectionServer()->Stop(); NetworkServiceManager::Dispose(); @@ -508,7 +513,6 @@ void PublisherDemo(absl::string_view friendly_name) { constexpr uint16_t server_port = 6667; - DemoPublisherObserver publisher_observer; // TODO(btolsch): aggregate initialization probably better? ServicePublisher::Config publisher_config; publisher_config.friendly_name = std::string(friendly_name); @@ -516,21 +520,23 @@ void PublisherDemo(absl::string_view friendly_name) { publisher_config.service_instance_name = "deadbeef"; publisher_config.connection_server_port = server_port; - auto mdns_publisher = MdnsServicePublisherFactory::Create( - publisher_config, &publisher_observer, - PlatformClientPosix::GetInstance()->GetTaskRunner()); - ServerConfig server_config; for (const InterfaceInfo& interface : GetNetworkInterfaces()) { OSP_VLOG << "Found interface: " << interface; if (!interface.addresses.empty()) { server_config.connection_endpoints.push_back( IPEndpoint{interface.addresses[0].address, server_port}); + publisher_config.network_interfaces.push_back(interface); } } OSP_LOG_IF(WARN, server_config.connection_endpoints.empty()) << "No network interfaces had usable addresses for mDNS publishing."; + DemoPublisherObserver publisher_observer; + auto service_publisher = ServicePublisherFactory::Create( + publisher_config, &publisher_observer, + PlatformClientPosix::GetInstance()->GetTaskRunner()); + MessageDemuxer demuxer(Clock::now, MessageDemuxer::kDefaultBufferLimit); DemoConnectionServerObserver server_observer; auto connection_server = ProtocolConnectionServerFactory::Create( @@ -538,13 +544,13 @@ void PublisherDemo(absl::string_view friendly_name) { PlatformClientPosix::GetInstance()->GetTaskRunner()); auto* network_service = - NetworkServiceManager::Create(nullptr, std::move(mdns_publisher), nullptr, - std::move(connection_server)); + NetworkServiceManager::Create(nullptr, std::move(service_publisher), + nullptr, std::move(connection_server)); DemoReceiverDelegate receiver_delegate; Receiver::Get()->Init(); Receiver::Get()->SetReceiverDelegate(&receiver_delegate); - network_service->GetMdnsServicePublisher()->Start(); + network_service->GetServicePublisher()->Start(); network_service->GetProtocolConnectionServer()->Start(); pollfd stdin_pollfd{STDIN_FILENO, POLLIN}; diff --git a/osp/impl/BUILD.gn b/osp/impl/BUILD.gn index dcccfc19..8b50164f 100644 --- a/osp/impl/BUILD.gn +++ b/osp/impl/BUILD.gn @@ -6,8 +6,9 @@ import("../../osp/build/config/services.gni") source_set("impl") { sources = [ - "mdns_platform_service.cc", - "mdns_platform_service.h", + "dns_sd_publisher_client.cc", + "dns_sd_publisher_client.h", + "dns_sd_service_publisher_factory.cc", "message_demuxer.cc", "network_service_manager.cc", "presentation/presentation_common.cc", @@ -31,23 +32,14 @@ source_set("impl") { "../public", ] deps = [ + "../../discovery:common", + "../../discovery:dnssd", + "../../discovery:public", "../../platform", "../../third_party/abseil", "../../util", "quic", ] - - if (use_mdns_responder) { - sources += [ - "internal_services.cc", - "internal_services.h", - "mdns_responder_service.cc", - "mdns_responder_service.h", - "mdns_service_listener_factory.cc", - "mdns_service_publisher_factory.cc", - ] - deps += [ "discovery/mdns:mdns" ] - } } if (use_chromium_quic) { diff --git a/osp/impl/DEPS b/osp/impl/DEPS index 63752366..cd004f79 100644 --- a/osp/impl/DEPS +++ b/osp/impl/DEPS @@ -1,7 +1,9 @@ -# Copyright (c) 2019 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. +# -*- Mode: Python; -*- include_rules = [ - '+osp/impl/discovery/mdns', + # Allowed to use discovery module. + '+discovery/public', + '+discovery/dnssd/public', + # Also necessary to implement discovery APIs. + '+discovery/common', ] diff --git a/osp/impl/discovery/mdns/BUILD.gn b/osp/impl/discovery/mdns/BUILD.gn deleted file mode 100644 index 3ef05106..00000000 --- a/osp/impl/discovery/mdns/BUILD.gn +++ /dev/null @@ -1,62 +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. - -import("../../../build/config/services.gni") -assert(use_mdns_responder) - -source_set("mdns_interface") { - sources = [ - "domain_name.cc", - "domain_name.h", - "mdns_responder_adapter.cc", - "mdns_responder_adapter.h", - ] - - public_deps = [ - "../../../../platform", - "../../../../third_party/abseil", - "../../../../util", - ] -} - -source_set("unittests") { - testonly = true - - sources = [ "domain_name_unittest.cc" ] - - deps = [ - ":mdns_interface", - "../../../../third_party/googletest:gmock", - "../../../../third_party/googletest:gtest", - ] - - sources += [ "mdns_responder_adapter_impl_unittest.cc" ] - deps += [ ":mdns" ] -} - -executable("mdns_demo") { - sources = [ "mdns_demo.cc" ] - - deps = [ - ":mdns", - "../../../../platform:standalone_impl", - ] -} - -source_set("mdns") { - sources = [ - "mdns_responder_adapter_impl.cc", - "mdns_responder_adapter_impl.h", - "mdns_responder_platform.cc", - "mdns_responder_platform.h", - ] - - public_deps = [ - ":mdns_interface", - "../../../../platform", - "../../../../util", - ] - - deps = [ "../../../../third_party/mDNSResponder:core" ] -} diff --git a/osp/impl/discovery/mdns/DEPS b/osp/impl/discovery/mdns/DEPS deleted file mode 100644 index 96a7209c..00000000 --- a/osp/impl/discovery/mdns/DEPS +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright 2019 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_rules = [ - '+platform/impl', # Needed by embedder_demo.cc -] diff --git a/osp/impl/discovery/mdns/domain_name.cc b/osp/impl/discovery/mdns/domain_name.cc deleted file mode 100644 index c574793a..00000000 --- a/osp/impl/discovery/mdns/domain_name.cc +++ /dev/null @@ -1,132 +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/domain_name.h" - -#include <algorithm> -#include <iterator> - -#include "util/stringprintf.h" - -namespace openscreen { -namespace osp { - -// static -DomainName DomainName::GetLocalDomain() { - return DomainName{{5, 'l', 'o', 'c', 'a', 'l', 0}}; -} - -// static -ErrorOr<DomainName> DomainName::Append(const DomainName& first, - const DomainName& second) { - OSP_CHECK(first.domain_name_.size()); - OSP_CHECK(second.domain_name_.size()); - - // Both vectors should represent null terminated domain names. - OSP_DCHECK_EQ(first.domain_name_.back(), '\0'); - OSP_DCHECK_EQ(second.domain_name_.back(), '\0'); - if ((first.domain_name_.size() + second.domain_name_.size() - 1) > - kDomainNameMaxLength) { - return Error::Code::kDomainNameTooLong; - } - - DomainName result; - result.domain_name_.clear(); - result.domain_name_.insert(result.domain_name_.begin(), - first.domain_name_.begin(), - first.domain_name_.end()); - result.domain_name_.insert(result.domain_name_.end() - 1, - second.domain_name_.begin(), - second.domain_name_.end() - 1); - return result; -} - -DomainName::DomainName() : domain_name_{0u} {} -DomainName::DomainName(std::vector<uint8_t>&& domain_name) - : domain_name_(std::move(domain_name)) { - OSP_CHECK_LE(domain_name_.size(), kDomainNameMaxLength); -} -DomainName::DomainName(const DomainName&) = default; -DomainName::DomainName(DomainName&&) noexcept = default; -DomainName::~DomainName() = default; -DomainName& DomainName::operator=(const DomainName&) = default; -DomainName& DomainName::operator=(DomainName&&) noexcept = default; - -bool DomainName::operator==(const DomainName& other) const { - if (domain_name_.size() != other.domain_name_.size()) { - return false; - } - for (size_t i = 0; i < domain_name_.size(); ++i) { - if (tolower(domain_name_[i]) != tolower(other.domain_name_[i])) { - return false; - } - } - return true; -} - -bool DomainName::operator!=(const DomainName& other) const { - return !(*this == other); -} - -bool DomainName::EndsWithLocalDomain() const { - const DomainName local_domain = GetLocalDomain(); - if (domain_name_.size() < local_domain.domain_name_.size()) - return false; - - return std::equal(local_domain.domain_name_.begin(), - local_domain.domain_name_.end(), - domain_name_.end() - local_domain.domain_name_.size()); -} - -Error DomainName::Append(const DomainName& after) { - OSP_CHECK(after.domain_name_.size()); - OSP_DCHECK_EQ(after.domain_name_.back(), 0u); - - if ((domain_name_.size() + after.domain_name_.size() - 1) > - kDomainNameMaxLength) { - return Error::Code::kDomainNameTooLong; - } - - domain_name_.insert(domain_name_.end() - 1, after.domain_name_.begin(), - after.domain_name_.end() - 1); - return Error::None(); -} - -std::vector<absl::string_view> DomainName::GetLabels() const { - OSP_DCHECK_GT(domain_name_.size(), 0u); - OSP_DCHECK_LT(domain_name_.size(), kDomainNameMaxLength); - - std::vector<absl::string_view> result; - const uint8_t* data = domain_name_.data(); - while (*data != 0) { - const size_t label_length = *data; - OSP_DCHECK_LT(label_length, kDomainNameMaxLabelLength); - - ++data; - result.emplace_back(reinterpret_cast<const char*>(data), label_length); - data += label_length; - } - return result; -} - -bool DomainNameComparator::operator()(const DomainName& a, - const DomainName& b) const { - return a.domain_name() < b.domain_name(); -} - -std::ostream& operator<<(std::ostream& os, const DomainName& domain_name) { - const auto& data = domain_name.domain_name(); - OSP_DCHECK_GT(data.size(), 0u); - auto it = data.begin(); - while (*it != 0) { - size_t length = *it++; - PrettyPrintAsciiHex(os, it, it + length); - it += length; - os << "."; - } - return os; -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/discovery/mdns/domain_name.h b/osp/impl/discovery/mdns/domain_name.h deleted file mode 100644 index c29ef9db..00000000 --- a/osp/impl/discovery/mdns/domain_name.h +++ /dev/null @@ -1,91 +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. - -#ifndef OSP_IMPL_DISCOVERY_MDNS_DOMAIN_NAME_H_ -#define OSP_IMPL_DISCOVERY_MDNS_DOMAIN_NAME_H_ - -#include <cstdint> -#include <ostream> -#include <string> -#include <vector> - -#include "absl/strings/string_view.h" -#include "platform/base/error.h" -#include "util/osp_logging.h" - -namespace openscreen { -namespace osp { - -struct DomainName { - static ErrorOr<DomainName> Append(const DomainName& first, - const DomainName& second); - - template <typename It> - static ErrorOr<DomainName> FromLabels(It first, It last) { - size_t total_length = 1; - for (auto label = first; label != last; ++label) { - if (label->size() > kDomainNameMaxLabelLength) - return Error::Code::kDomainNameLabelTooLong; - - total_length += label->size() + 1; - } - if (total_length > kDomainNameMaxLength) - return Error::Code::kDomainNameTooLong; - - DomainName result; - result.domain_name_.resize(total_length); - auto result_it = result.domain_name_.begin(); - for (auto label = first; label != last; ++label) { - *result_it++ = static_cast<uint8_t>(label->size()); - result_it = std::copy(label->begin(), label->end(), result_it); - } - *result_it = 0; - return std::move(result); - } - - static DomainName GetLocalDomain(); - - static constexpr uint8_t kDomainNameMaxLabelLength = 63u; - static constexpr uint16_t kDomainNameMaxLength = 256u; - - DomainName(); - explicit DomainName(std::vector<uint8_t>&& domain_name); - DomainName(const DomainName&); - DomainName(DomainName&&) noexcept; - ~DomainName(); - DomainName& operator=(const DomainName&); - DomainName& operator=(DomainName&&) noexcept; - - bool operator==(const DomainName& other) const; - bool operator!=(const DomainName& other) const; - - bool EndsWithLocalDomain() const; - bool IsEmpty() const { return domain_name_.size() == 1 && !domain_name_[0]; } - - Error Append(const DomainName& after); - std::vector<absl::string_view> GetLabels() const; - - const std::vector<uint8_t>& domain_name() const { return domain_name_; } - - private: - // RFC 1035 domain name format: sequence of 1 octet label length followed by - // label data, ending with a 0 octet. May not exceed 256 bytes (including - // terminating 0). - // For example, openscreen.org would be encoded as: - // {10, 'o', 'p', 'e', 'n', 's', 'c', 'r', 'e', 'e', 'n', - // 3, 'o', 'r', 'g', 0} - std::vector<uint8_t> domain_name_; -}; - -class DomainNameComparator { - public: - bool operator()(const DomainName& a, const DomainName& b) const; -}; - -std::ostream& operator<<(std::ostream& os, const DomainName& domain_name); - -} // namespace osp -} // namespace openscreen - -#endif // OSP_IMPL_DISCOVERY_MDNS_DOMAIN_NAME_H_ diff --git a/osp/impl/discovery/mdns/domain_name_unittest.cc b/osp/impl/discovery/mdns/domain_name_unittest.cc deleted file mode 100644 index 76f31003..00000000 --- a/osp/impl/discovery/mdns/domain_name_unittest.cc +++ /dev/null @@ -1,194 +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/domain_name.h" - -#include <sstream> - -#include "gtest/gtest.h" -#include "platform/base/error.h" - -namespace openscreen { -namespace osp { - -namespace { - -ErrorOr<DomainName> FromLabels(const std::vector<std::string>& labels) { - return DomainName::FromLabels(labels.begin(), labels.end()); -} - -template <typename T> -T UnpackErrorOr(ErrorOr<T> error_or) { - EXPECT_TRUE(error_or); - return std::move(error_or.value()); -} - -} // namespace - -TEST(DomainNameTest, Constructors) { - DomainName empty; - - ASSERT_EQ(1u, empty.domain_name().size()); - EXPECT_EQ(0, empty.domain_name()[0]); - - DomainName original({10, 'o', 'p', 'e', 'n', 's', 'c', 'r', 'e', 'e', 'n', 3, - 'o', 'r', 'g', 0}); - ASSERT_EQ(16u, original.domain_name().size()); - - auto data_copy = original.domain_name(); - DomainName direct_ctor(std::move(data_copy)); - EXPECT_EQ(direct_ctor.domain_name(), original.domain_name()); - - DomainName copy_ctor(original); - EXPECT_EQ(copy_ctor.domain_name(), original.domain_name()); - - DomainName move_ctor(std::move(copy_ctor)); - EXPECT_EQ(move_ctor.domain_name(), original.domain_name()); - - DomainName copy_assign; - copy_assign = move_ctor; - EXPECT_EQ(copy_assign.domain_name(), original.domain_name()); - - DomainName move_assign; - move_assign = std::move(move_ctor); - EXPECT_EQ(move_assign.domain_name(), original.domain_name()); -} - -TEST(DomainNameTest, FromLabels) { - const auto typical = - std::vector<uint8_t>{10, 'o', 'p', 'e', 'n', 's', 'c', 'r', - 'e', 'e', 'n', 3, 'o', 'r', 'g', 0}; - DomainName result = UnpackErrorOr(FromLabels({"openscreen", "org"})); - EXPECT_EQ(result.domain_name(), typical); - - const auto includes_dot = - std::vector<uint8_t>{11, 'o', 'p', 'e', 'n', '.', 's', 'c', 'r', - 'e', 'e', 'n', 3, 'o', 'r', 'g', 0}; - result = UnpackErrorOr(FromLabels({"open.screen", "org"})); - EXPECT_EQ(result.domain_name(), includes_dot); - - const auto includes_non_ascii = - std::vector<uint8_t>{11, 'o', 'p', 'e', 'n', 7, 's', 'c', 'r', - 'e', 'e', 'n', 3, 'o', 'r', 'g', 0}; - result = UnpackErrorOr(FromLabels({"open\7screen", "org"})); - EXPECT_EQ(result.domain_name(), includes_non_ascii); - - ASSERT_FALSE( - FromLabels({"extremely-long-label-that-is-actually-too-long-" - "for-rfc-1034-and-will-not-generate"})); - - ASSERT_FALSE(FromLabels({ - "extremely-long-domain-name-that-is-made-of", - "valid-labels", - "however-overall-it-is-too-long-for-rfc-1034", - "so-it-should-fail-to-generate", - "filler-filler-filler-filler-filler", - "filler-filler-filler-filler-filler", - "filler-filler-filler-filler-filler", - "filler-filler-filler-filler-filler", - })); -} - -TEST(DomainNameTest, Equality) { - DomainName alpha = UnpackErrorOr(FromLabels({"alpha", "openscreen", "org"})); - DomainName beta = UnpackErrorOr(FromLabels({"beta", "openscreen", "org"})); - - const DomainName alpha_copy = alpha; - - EXPECT_TRUE(alpha == alpha); - EXPECT_FALSE(alpha != alpha); - EXPECT_TRUE(alpha == alpha_copy); - EXPECT_FALSE(alpha != alpha_copy); - EXPECT_FALSE(alpha == beta); - EXPECT_TRUE(alpha != beta); -} - -TEST(DomainNameTest, EndsWithLocalDomain) { - DomainName alpha; - EXPECT_FALSE(alpha.EndsWithLocalDomain()); - - alpha = UnpackErrorOr(FromLabels({"alpha", "openscreen", "org"})); - DomainName beta = UnpackErrorOr(FromLabels({"beta", "local"})); - - EXPECT_FALSE(alpha.EndsWithLocalDomain()); - EXPECT_TRUE(beta.EndsWithLocalDomain()); -} - -TEST(DomainNameTest, IsEmpty) { - DomainName alpha; - DomainName beta(std::vector<uint8_t>{0}); - - EXPECT_TRUE(alpha.IsEmpty()); - EXPECT_TRUE(beta.IsEmpty()); - - alpha = UnpackErrorOr(FromLabels({"alpha", "openscreen", "org"})); - EXPECT_FALSE(alpha.IsEmpty()); -} - -TEST(DomainNameTest, Append) { - const auto expected_service_name = - std::vector<uint8_t>{5, 'a', 'l', 'p', 'h', 'a', '\0'}; - const auto expected_service_type_initial = std::vector<uint8_t>{ - 11, '_', 'o', 'p', 'e', 'n', 's', 'c', 'r', 'e', 'e', 'n', '\0'}; - const auto expected_protocol = - std::vector<uint8_t>{5, '_', 'q', 'u', 'i', 'c', '\0'}; - const auto expected_service_type = - std::vector<uint8_t>{11, '_', 'o', 'p', 'e', 'n', 's', 'c', 'r', 'e', - 'e', 'n', 5, '_', 'q', 'u', 'i', 'c', '\0'}; - const auto total_expected = std::vector<uint8_t>{ - 5, 'a', 'l', 'p', 'h', 'a', 11, '_', 'o', 'p', 'e', 'n', 's', - 'c', 'r', 'e', 'e', 'n', 5, '_', 'q', 'u', 'i', 'c', '\0'}; - - DomainName service_name = UnpackErrorOr(FromLabels({"alpha"})); - EXPECT_EQ(service_name.domain_name(), expected_service_name); - - DomainName service_type = UnpackErrorOr(FromLabels({"_openscreen"})); - EXPECT_EQ(service_type.domain_name(), expected_service_type_initial); - - DomainName protocol = UnpackErrorOr(FromLabels({"_quic"})); - EXPECT_EQ(protocol.domain_name(), expected_protocol); - - EXPECT_TRUE(service_type.Append(protocol).ok()); - EXPECT_EQ(service_type.domain_name(), expected_service_type); - - DomainName result = - UnpackErrorOr(DomainName::Append(service_name, service_type)); - EXPECT_EQ(result.domain_name(), total_expected); -} - -TEST(DomainNameTest, GetLabels) { - const auto labels = std::vector<std::string>{"alpha", "beta", "gamma", "org"}; - DomainName domain_name = UnpackErrorOr(FromLabels(labels)); - - const auto actual_labels = domain_name.GetLabels(); - for (size_t i = 0; i < labels.size(); ++i) { - EXPECT_EQ(labels[i], actual_labels[i]); - } -} - -TEST(DomainNameTest, StreamEscaping) { - { - std::stringstream ss; - ss << DomainName(std::vector<uint8_t>{1, 0, 0}); - EXPECT_EQ(ss.str(), "\\x00."); - } - { - std::stringstream ss; - ss << DomainName(std::vector<uint8_t>{1, 1, 0}); - EXPECT_EQ(ss.str(), "\\x01."); - } - { - std::stringstream ss; - ss << DomainName(std::vector<uint8_t>{1, 18, 0}); - EXPECT_EQ(ss.str(), "\\x12."); - } - { - std::stringstream ss; - ss << DomainName(std::vector<uint8_t>{1, 255, 0}); - EXPECT_EQ(ss.str(), "\\xff."); - } -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/discovery/mdns/mdns_demo.cc b/osp/impl/discovery/mdns/mdns_demo.cc deleted file mode 100644 index 1fc1513e..00000000 --- a/osp/impl/discovery/mdns/mdns_demo.cc +++ /dev/null @@ -1,374 +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 <signal.h> -#include <unistd.h> - -#include <algorithm> -#include <map> -#include <memory> -#include <vector> - -// TODO(rwkeane): Remove references to platform/impl -#include "osp/impl/discovery/mdns/mdns_responder_adapter_impl.h" -#include "platform/api/network_interface.h" -#include "platform/api/time.h" -#include "platform/base/error.h" -#include "platform/impl/logging.h" -#include "platform/impl/platform_client_posix.h" -#include "platform/impl/task_runner.h" -#include "platform/impl/udp_socket_reader_posix.h" - -// This file contains a demo of our mDNSResponder wrapper code. It can both -// listen for mDNS services and advertise an mDNS service. The command-line -// usage is: -// mdns_demo [service_type] [service_instance_name] -// service_type defaults to '_openscreen._udp' and service_instance_name -// defaults to ''. service_type determines services the program listens for and -// when service_instance_name is not empty, a service of -// 'service_instance_name.service_type' is also advertised. -// -// The program will print a list of discovered services when it receives a USR1 -// or INT signal. The pid is printed at the beginning of the program to -// facilitate this. -// -// There are a few known bugs around the handling of record events, so this -// shouldn't be expected to be a source of truth, nor should it be expected to -// be correct after running for a long time. - -namespace openscreen { -namespace osp { -namespace { - -bool g_done = false; -bool g_dump_services = false; - -struct Service { - explicit Service(DomainName service_instance) - : service_instance(std::move(service_instance)) {} - ~Service() = default; - - DomainName service_instance; - DomainName domain_name; - IPAddress address; - uint16_t port; - std::vector<std::string> txt; -}; - -class DemoSocketClient : public UdpSocket::Client { - public: - explicit DemoSocketClient(MdnsResponderAdapterImpl* mdns) : mdns_(mdns) {} - - void OnError(UdpSocket* socket, Error error) override { - // TODO(crbug.com/openscreen/66): Change to OSP_LOG_FATAL. - OSP_LOG_ERROR << "configuration failed for interface " << error.message(); - OSP_CHECK(false); - } - - void OnSendError(UdpSocket* socket, Error error) override { - OSP_UNIMPLEMENTED(); - } - - void OnRead(UdpSocket* socket, ErrorOr<UdpPacket> packet) override { - mdns_->OnRead(socket, std::move(packet)); - } - - private: - MdnsResponderAdapterImpl* mdns_; -}; - -using ServiceMap = std::map<DomainName, Service, DomainNameComparator>; -ServiceMap* g_services = nullptr; - -void sigusr1_dump_services(int) { - g_dump_services = true; -} - -void sigint_stop(int) { - OSP_LOG_INFO << "caught SIGINT, exiting..."; - g_done = true; -} - -std::vector<std::string> SplitByDot(const std::string& domain_part) { - std::vector<std::string> result; - auto copy_it = domain_part.begin(); - for (auto it = domain_part.begin(); it != domain_part.end(); ++it) { - if (*it == '.') { - result.emplace_back(copy_it, it); - copy_it = it + 1; - } - } - if (copy_it != domain_part.end()) - result.emplace_back(copy_it, domain_part.end()); - - return result; -} - -void SignalThings() { - struct sigaction usr1_sa; - struct sigaction int_sa; - struct sigaction unused; - - usr1_sa.sa_handler = &sigusr1_dump_services; - sigemptyset(&usr1_sa.sa_mask); - usr1_sa.sa_flags = 0; - - int_sa.sa_handler = &sigint_stop; - sigemptyset(&int_sa.sa_mask); - int_sa.sa_flags = 0; - - sigaction(SIGUSR1, &usr1_sa, &unused); - sigaction(SIGINT, &int_sa, &unused); - - OSP_LOG_INFO << "signal handlers setup" << std::endl << "pid: " << getpid(); -} - -std::vector<std::unique_ptr<UdpSocket>> SetUpMulticastSockets( - TaskRunner* task_runner, - const std::vector<NetworkInterfaceIndex>& index_list, - UdpSocket::Client* client) { - std::vector<std::unique_ptr<UdpSocket>> sockets; - for (const auto ifindex : index_list) { - auto create_result = - UdpSocket::Create(task_runner, client, IPEndpoint{{}, 5353}); - if (!create_result) { - OSP_LOG_ERROR << "failed to create IPv4 socket for interface " << ifindex - << ": " << create_result.error().message(); - continue; - } - std::unique_ptr<UdpSocket> socket = std::move(create_result.value()); - - socket->JoinMulticastGroup(IPAddress{224, 0, 0, 251}, ifindex); - socket->SetMulticastOutboundInterface(ifindex); - socket->Bind(); - - OSP_LOG_INFO << "listening on interface " << ifindex; - sockets.emplace_back(std::move(socket)); - } - return sockets; -} - -void LogService(const Service& s) { - OSP_LOG_INFO << "PTR: (" << s.service_instance << ")" << std::endl - << "SRV: " << s.domain_name << ":" << s.port << std::endl - << "TXT:"; - - for (const auto& l : s.txt) { - OSP_LOG_INFO << " | " << l; - } - OSP_LOG_INFO << "A: " << s.address; -} - -void HandleEvents(MdnsResponderAdapterImpl* mdns_adapter) { - for (auto& ptr_event : mdns_adapter->TakePtrResponses()) { - auto it = g_services->find(ptr_event.service_instance); - switch (ptr_event.header.response_type) { - case QueryEventHeader::Type::kAdded: - case QueryEventHeader::Type::kAddedNoCache: - mdns_adapter->StartSrvQuery(ptr_event.header.socket, - ptr_event.service_instance); - mdns_adapter->StartTxtQuery(ptr_event.header.socket, - ptr_event.service_instance); - if (it == g_services->end()) { - g_services->emplace(ptr_event.service_instance, - Service(ptr_event.service_instance)); - } - break; - case QueryEventHeader::Type::kRemoved: - // PTR may be removed and added without updating related entries (SRV - // and friends) so this simple logic is actually broken, but I don't - // want to do a better design or pointer hell for just a demo. - OSP_LOG_WARN << "ptr-remove: " << ptr_event.service_instance; - if (it != g_services->end()) - g_services->erase(it); - - break; - } - } - for (auto& srv_event : mdns_adapter->TakeSrvResponses()) { - auto it = g_services->find(srv_event.service_instance); - if (it == g_services->end()) - continue; - - switch (srv_event.header.response_type) { - case QueryEventHeader::Type::kAdded: - case QueryEventHeader::Type::kAddedNoCache: - mdns_adapter->StartAQuery(srv_event.header.socket, - srv_event.domain_name); - it->second.domain_name = std::move(srv_event.domain_name); - it->second.port = srv_event.port; - break; - case QueryEventHeader::Type::kRemoved: - OSP_LOG_WARN << "srv-remove: " << srv_event.service_instance; - it->second.domain_name = DomainName(); - it->second.port = 0; - break; - } - } - for (auto& txt_event : mdns_adapter->TakeTxtResponses()) { - auto it = g_services->find(txt_event.service_instance); - if (it == g_services->end()) - continue; - - switch (txt_event.header.response_type) { - case QueryEventHeader::Type::kAdded: - case QueryEventHeader::Type::kAddedNoCache: - it->second.txt = std::move(txt_event.txt_info); - break; - case QueryEventHeader::Type::kRemoved: - OSP_LOG_WARN << "txt-remove: " << txt_event.service_instance; - it->second.txt.clear(); - break; - } - } - for (const auto& a_event : mdns_adapter->TakeAResponses()) { - // TODO(btolsch): If multiple SRV records specify the same domain, the A - // will only update the first. I didn't think this would happen but I - // noticed this happens for cast groups. - auto it = std::find_if(g_services->begin(), g_services->end(), - [&a_event](const std::pair<DomainName, Service>& s) { - return s.second.domain_name == a_event.domain_name; - }); - if (it == g_services->end()) - continue; - - switch (a_event.header.response_type) { - case QueryEventHeader::Type::kAdded: - case QueryEventHeader::Type::kAddedNoCache: - it->second.address = a_event.address; - break; - case QueryEventHeader::Type::kRemoved: - OSP_LOG_WARN << "a-remove: " << a_event.domain_name; - it->second.address = IPAddress(0, 0, 0, 0); - break; - } - } -} - -void BrowseDemo(TaskRunner* task_runner, - const std::string& service_name, - const std::string& service_protocol, - const std::string& service_instance) { - SignalThings(); - - std::vector<std::string> labels{service_name, service_protocol}; - ErrorOr<DomainName> service_type = - DomainName::FromLabels(labels.begin(), labels.end()); - - if (!service_type) { - OSP_LOG_ERROR << "bad domain labels: " << service_name << ", " - << service_protocol; - return; - } - - auto mdns_adapter = std::make_unique<MdnsResponderAdapterImpl>(); - mdns_adapter->Init(); - mdns_adapter->SetHostLabel("gigliorononomicon"); - const std::vector<InterfaceInfo> interfaces = GetNetworkInterfaces(); - std::vector<NetworkInterfaceIndex> index_list; - for (const auto& interface : interfaces) { - OSP_LOG_INFO << "Found interface: " << interface; - if (!interface.addresses.empty()) { - index_list.push_back(interface.index); - } - } - OSP_LOG_IF(WARN, index_list.empty()) - << "No network interfaces had usable addresses for mDNS."; - - DemoSocketClient client(mdns_adapter.get()); - auto sockets = SetUpMulticastSockets(task_runner, index_list, &client); - // The code below assumes the elements in |sockets| is in exact 1:1 - // correspondence with the elements in |index_list|. Crash the demo if any - // sockets are missing (i.e., failed to be set up). - OSP_CHECK_EQ(sockets.size(), index_list.size()); - - // Listen on all interfaces. - auto socket_it = sockets.begin(); - for (NetworkInterfaceIndex index : index_list) { - const auto& interface = - *std::find_if(interfaces.begin(), interfaces.end(), - [index](const openscreen::InterfaceInfo& info) { - return info.index == index; - }); - // Pick any address for the given interface. - mdns_adapter->RegisterInterface(interface, interface.addresses.front(), - socket_it->get()); - ++socket_it; - } - - if (!service_instance.empty()) { - mdns_adapter->RegisterService(service_instance, service_name, - service_protocol, DomainName(), 12345, - {{"k1", "yurtle"}, {"k2", "turtle"}}); - } - - for (const std::unique_ptr<UdpSocket>& socket : sockets) { - mdns_adapter->StartPtrQuery(socket.get(), service_type.value()); - } - - while (!g_done) { - HandleEvents(mdns_adapter.get()); - if (g_dump_services) { - OSP_LOG_INFO << "num services: " << g_services->size(); - for (const auto& s : *g_services) { - LogService(s.second); - } - if (!service_instance.empty()) { - mdns_adapter->UpdateTxtData( - service_instance, service_name, service_protocol, - {{"k1", "oogley"}, {"k2", "moogley"}, {"k3", "googley"}}); - } - g_dump_services = false; - } - mdns_adapter->RunTasks(); - } - OSP_LOG_INFO << "num services: " << g_services->size(); - for (const auto& s : *g_services) { - LogService(s.second); - } - for (const std::unique_ptr<UdpSocket>& socket : sockets) { - mdns_adapter->DeregisterInterface(socket.get()); - } - mdns_adapter->Close(); -} - -} // namespace -} // namespace osp -} // namespace openscreen - -int main(int argc, char** argv) { - using openscreen::Clock; - using openscreen::PlatformClientPosix; - - openscreen::SetLogLevel(openscreen::LogLevel::kVerbose); - - std::string service_instance; - std::string service_type("_openscreen._udp"); - if (argc >= 2) - service_type = argv[1]; - - if (argc >= 3) - service_instance = argv[2]; - - if (service_type.size() && service_type[0] == '.') - return 1; - - auto labels = openscreen::osp::SplitByDot(service_type); - if (labels.size() != 2) - return 1; - - openscreen::osp::ServiceMap services; - openscreen::osp::g_services = &services; - - PlatformClientPosix::Create(std::chrono::milliseconds(50)); - - openscreen::osp::BrowseDemo( - PlatformClientPosix::GetInstance()->GetTaskRunner(), labels[0], labels[1], - service_instance); - - PlatformClientPosix::ShutDown(); - - openscreen::osp::g_services = nullptr; - return 0; -} diff --git a/osp/impl/discovery/mdns/mdns_responder_adapter.cc b/osp/impl/discovery/mdns/mdns_responder_adapter.cc deleted file mode 100644 index edf25503..00000000 --- a/osp/impl/discovery/mdns/mdns_responder_adapter.cc +++ /dev/null @@ -1,77 +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.h" - -namespace openscreen { -namespace osp { - -QueryEventHeader::QueryEventHeader() = default; -QueryEventHeader::QueryEventHeader(QueryEventHeader::Type response_type, - UdpSocket* socket) - : response_type(response_type), socket(socket) {} -QueryEventHeader::QueryEventHeader(QueryEventHeader&&) noexcept = default; -QueryEventHeader::~QueryEventHeader() = default; -QueryEventHeader& QueryEventHeader::operator=(QueryEventHeader&&) noexcept = - default; - -AEvent::AEvent() = default; -AEvent::AEvent(QueryEventHeader header, - DomainName domain_name, - IPAddress address) - : header(std::move(header)), - domain_name(std::move(domain_name)), - address(std::move(address)) {} -AEvent::AEvent(AEvent&&) noexcept = default; -AEvent::~AEvent() = default; -AEvent& AEvent::operator=(AEvent&&) noexcept = default; - -AaaaEvent::AaaaEvent() = default; -AaaaEvent::AaaaEvent(QueryEventHeader header, - DomainName domain_name, - IPAddress address) - : header(std::move(header)), - domain_name(std::move(domain_name)), - address(std::move(address)) {} -AaaaEvent::AaaaEvent(AaaaEvent&&) noexcept = default; -AaaaEvent::~AaaaEvent() = default; -AaaaEvent& AaaaEvent::operator=(AaaaEvent&&) noexcept = default; - -PtrEvent::PtrEvent() = default; -PtrEvent::PtrEvent(QueryEventHeader header, DomainName service_instance) - : header(std::move(header)), - service_instance(std::move(service_instance)) {} -PtrEvent::PtrEvent(PtrEvent&&) noexcept = default; -PtrEvent::~PtrEvent() = default; -PtrEvent& PtrEvent::operator=(PtrEvent&&) noexcept = default; - -SrvEvent::SrvEvent() = default; -SrvEvent::SrvEvent(QueryEventHeader header, - DomainName service_instance, - DomainName domain_name, - uint16_t port) - : header(std::move(header)), - service_instance(std::move(service_instance)), - domain_name(std::move(domain_name)), - port(port) {} -SrvEvent::SrvEvent(SrvEvent&&) noexcept = default; -SrvEvent::~SrvEvent() = default; -SrvEvent& SrvEvent::operator=(SrvEvent&&) noexcept = default; - -TxtEvent::TxtEvent() = default; -TxtEvent::TxtEvent(QueryEventHeader header, - DomainName service_instance, - std::vector<std::string> txt_info) - : header(std::move(header)), - service_instance(std::move(service_instance)), - txt_info(std::move(txt_info)) {} -TxtEvent::TxtEvent(TxtEvent&&) noexcept = default; -TxtEvent::~TxtEvent() = default; -TxtEvent& TxtEvent::operator=(TxtEvent&&) noexcept = default; - -MdnsResponderAdapter::MdnsResponderAdapter() = default; -MdnsResponderAdapter::~MdnsResponderAdapter() = default; - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/discovery/mdns/mdns_responder_adapter.h b/osp/impl/discovery/mdns/mdns_responder_adapter.h deleted file mode 100644 index 66083d57..00000000 --- a/osp/impl/discovery/mdns/mdns_responder_adapter.h +++ /dev/null @@ -1,258 +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. - -#ifndef OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_ADAPTER_H_ -#define OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_ADAPTER_H_ - -#include <cstdint> -#include <map> -#include <string> -#include <vector> - -#include "osp/impl/discovery/mdns/domain_name.h" -#include "osp/impl/discovery/mdns/mdns_responder_platform.h" -#include "platform/api/network_interface.h" -#include "platform/api/time.h" -#include "platform/api/udp_socket.h" -#include "platform/base/error.h" -#include "platform/base/ip_address.h" - -namespace openscreen { -namespace osp { - -struct QueryEventHeader { - enum class Type { - kAdded = 0, - kAddedNoCache, - kRemoved, - }; - - QueryEventHeader(); - QueryEventHeader(Type response_type, UdpSocket* socket); - QueryEventHeader(QueryEventHeader&&) noexcept; - ~QueryEventHeader(); - QueryEventHeader& operator=(QueryEventHeader&&) noexcept; - - Type response_type; - UdpSocket* socket; -}; - -struct PtrEvent { - PtrEvent(); - PtrEvent(QueryEventHeader header, DomainName service_instance); - PtrEvent(PtrEvent&&) noexcept; - ~PtrEvent(); - PtrEvent& operator=(PtrEvent&&) noexcept; - - QueryEventHeader header; - DomainName service_instance; -}; - -struct SrvEvent { - SrvEvent(); - SrvEvent(QueryEventHeader header, - DomainName service_instance, - DomainName domain_name, - uint16_t port); - SrvEvent(SrvEvent&&) noexcept; - ~SrvEvent(); - SrvEvent& operator=(SrvEvent&&) noexcept; - - QueryEventHeader header; - DomainName service_instance; - DomainName domain_name; - uint16_t port; -}; - -struct TxtEvent { - TxtEvent(); - TxtEvent(QueryEventHeader header, - DomainName service_instance, - std::vector<std::string> txt_info); - TxtEvent(TxtEvent&&) noexcept; - ~TxtEvent(); - TxtEvent& operator=(TxtEvent&&) noexcept; - - QueryEventHeader header; - DomainName service_instance; - - // NOTE: mDNS does not specify a character encoding for the data in TXT - // records. - std::vector<std::string> txt_info; -}; - -struct AEvent { - AEvent(); - AEvent(QueryEventHeader header, DomainName domain_name, IPAddress address); - AEvent(AEvent&&) noexcept; - ~AEvent(); - AEvent& operator=(AEvent&&) noexcept; - - QueryEventHeader header; - DomainName domain_name; - IPAddress address; -}; - -struct AaaaEvent { - AaaaEvent(); - AaaaEvent(QueryEventHeader header, DomainName domain_name, IPAddress address); - AaaaEvent(AaaaEvent&&) noexcept; - ~AaaaEvent(); - AaaaEvent& operator=(AaaaEvent&&) noexcept; - - QueryEventHeader header; - DomainName domain_name; - IPAddress address; -}; - -enum class MdnsResponderErrorCode { - kNoError = 0, - kUnsupportedError, - kDomainOverflowError, - kInvalidParameters, - kUnknownError, -}; - -// This interface wraps all the functionality of mDNSResponder, which includes -// both listening and publishing. As a result, some methods are only used by -// listeners, some are only used by publishers, and some are used by both. -// -// Listening for records might look like this: -// adapter->Init(); -// -// // Once for each interface, the meaning of false is described below. -// adapter->RegisterInterface(..., false); -// -// adapter->StartPtrQuery("_openscreen._udp"); -// adapter->RunTasks(); -// -// // When receiving multicast UDP traffic from port 5353. -// adapter->OnDataReceived(...); -// adapter->RunTasks(); -// -// // Check |ptrs| for responses after pulling. -// auto ptrs = adapter->TakePtrResponses(); -// -// // Eventually... -// adapter->StopPtrQuery("_openscreen._udp"); -// -// Publishing a service might look like this: -// adapter->Init(); -// -// // Once for each interface, the meaning of true is described below. -// adapter->RegisterInterface(..., true); -// -// adapter->SetHostLabel("deadbeef"); -// adapter->RegisterService("living-room", "_openscreen._udp", ...); -// adapter->RunTasks(); -// -// // When receiving multicast UDP traffic from port 5353. -// adapter->OnDataReceived(...); -// adapter->RunTasks(); -// -// // Eventually... -// adapter->DeregisterService("living-room", "_openscreen", "_udp"); -// -// Additionally, it's important to understand that mDNSResponder may defer some -// tasks (e.g. parsing responses, sending queries, etc.) and those deferred -// tasks are only run when RunTasks is called. Therefore, RunTasks should be -// called after any sequence of calls to mDNSResponder. It also returns a -// timeout value, after which it must be called again (e.g. for maintaining its -// cache). -class MdnsResponderAdapter : public UdpSocket::Client { - public: - MdnsResponderAdapter(); - virtual ~MdnsResponderAdapter() = 0; - - // Initializes mDNSResponder. This should be called before any queries or - // service registrations are made. - virtual Error Init() = 0; - - // Stops all open queries and service registrations. If this is not called - // before destruction, any registered services will not send their goodbye - // messages. - virtual void Close() = 0; - - // Called to change the name published by the A and AAAA records for the host - // when any service is active (via RegisterService). Returns true if the - // label was set successfully, false otherwise (e.g. the label did not meet - // DNS name requirements). - virtual Error SetHostLabel(const std::string& host_label) = 0; - - // The following methods register and deregister a network interface with - // mDNSResponder. |socket| will be used to identify which interface received - // the data in OnDataReceived and will be used to send data via the platform - // layer. - virtual Error RegisterInterface(const InterfaceInfo& interface_info, - const IPSubnet& interface_address, - UdpSocket* socket) = 0; - virtual Error DeregisterInterface(UdpSocket* socket) = 0; - - // Returns the time period after which this method must be called again, if - // any. - virtual Clock::duration RunTasks() = 0; - - virtual std::vector<PtrEvent> TakePtrResponses() = 0; - virtual std::vector<SrvEvent> TakeSrvResponses() = 0; - virtual std::vector<TxtEvent> TakeTxtResponses() = 0; - virtual std::vector<AEvent> TakeAResponses() = 0; - virtual std::vector<AaaaEvent> TakeAaaaResponses() = 0; - - virtual MdnsResponderErrorCode StartPtrQuery( - UdpSocket* socket, - const DomainName& service_type) = 0; - virtual MdnsResponderErrorCode StartSrvQuery( - UdpSocket* socket, - const DomainName& service_instance) = 0; - virtual MdnsResponderErrorCode StartTxtQuery( - UdpSocket* socket, - const DomainName& service_instance) = 0; - virtual MdnsResponderErrorCode StartAQuery(UdpSocket* socket, - const DomainName& domain_name) = 0; - virtual MdnsResponderErrorCode StartAaaaQuery( - UdpSocket* socket, - const DomainName& domain_name) = 0; - - virtual MdnsResponderErrorCode StopPtrQuery( - UdpSocket* socket, - const DomainName& service_type) = 0; - virtual MdnsResponderErrorCode StopSrvQuery( - UdpSocket* socket, - const DomainName& service_instance) = 0; - virtual MdnsResponderErrorCode StopTxtQuery( - UdpSocket* socket, - const DomainName& service_instance) = 0; - virtual MdnsResponderErrorCode StopAQuery(UdpSocket* socket, - const DomainName& domain_name) = 0; - virtual MdnsResponderErrorCode StopAaaaQuery( - UdpSocket* socket, - const DomainName& domain_name) = 0; - - // The following methods concern advertising a service via mDNS. The - // arguments correspond to values needed in the PTR, SRV, and TXT records that - // will be published for the service. An A or AAAA record will also be - // published with the service for each active interface known to mDNSResponder - // via RegisterInterface. - virtual MdnsResponderErrorCode 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) = 0; - virtual MdnsResponderErrorCode DeregisterService( - const std::string& service_instance, - const std::string& service_name, - const std::string& service_protocol) = 0; - virtual MdnsResponderErrorCode 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) = 0; -}; - -} // namespace osp -} // namespace openscreen - -#endif // OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_ADAPTER_H_ 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 diff --git a/osp/impl/discovery/mdns/mdns_responder_adapter_impl.h b/osp/impl/discovery/mdns/mdns_responder_adapter_impl.h deleted file mode 100644 index d0dd55a1..00000000 --- a/osp/impl/discovery/mdns/mdns_responder_adapter_impl.h +++ /dev/null @@ -1,159 +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. - -#ifndef OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_ADAPTER_IMPL_H_ -#define OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_ADAPTER_IMPL_H_ - -#include <map> -#include <memory> -#include <string> -#include <vector> - -#include "osp/impl/discovery/mdns/mdns_responder_adapter.h" -#include "platform/api/udp_socket.h" -#include "platform/base/error.h" -#include "third_party/mDNSResponder/src/mDNSCore/mDNSEmbeddedAPI.h" - -namespace openscreen { -namespace osp { - -class MdnsResponderAdapterImpl final : public MdnsResponderAdapter { - public: - static constexpr int kRrCacheSize = 500; - - MdnsResponderAdapterImpl(); - ~MdnsResponderAdapterImpl() override; - - Error Init() override; - void Close() override; - - Error SetHostLabel(const std::string& host_label) override; - - Error RegisterInterface(const InterfaceInfo& interface_info, - const IPSubnet& interface_address, - UdpSocket* socket) override; - Error DeregisterInterface(UdpSocket* socket) override; - - void OnRead(UdpSocket* socket, ErrorOr<UdpPacket> packet) override; - void OnSendError(UdpSocket* socket, Error error) override; - void OnError(UdpSocket* socket, Error error) override; - void OnBound(UdpSocket* socket) override; - - Clock::duration RunTasks() override; - - std::vector<PtrEvent> TakePtrResponses() override; - std::vector<SrvEvent> TakeSrvResponses() override; - std::vector<TxtEvent> TakeTxtResponses() override; - std::vector<AEvent> TakeAResponses() override; - std::vector<AaaaEvent> TakeAaaaResponses() override; - - MdnsResponderErrorCode StartPtrQuery(UdpSocket* socket, - const DomainName& service_type) override; - MdnsResponderErrorCode StartSrvQuery( - UdpSocket* socket, - const DomainName& service_instance) override; - MdnsResponderErrorCode StartTxtQuery( - UdpSocket* socket, - const DomainName& service_instance) override; - MdnsResponderErrorCode StartAQuery(UdpSocket* socket, - const DomainName& domain_name) override; - MdnsResponderErrorCode StartAaaaQuery(UdpSocket* socket, - const DomainName& domain_name) override; - MdnsResponderErrorCode StopPtrQuery(UdpSocket* socket, - const DomainName& service_type) override; - MdnsResponderErrorCode StopSrvQuery( - UdpSocket* socket, - const DomainName& service_instance) override; - MdnsResponderErrorCode StopTxtQuery( - UdpSocket* socket, - const DomainName& service_instance) override; - MdnsResponderErrorCode StopAQuery(UdpSocket* socket, - const DomainName& domain_name) override; - MdnsResponderErrorCode StopAaaaQuery(UdpSocket* socket, - const DomainName& domain_name) override; - - MdnsResponderErrorCode 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) override; - MdnsResponderErrorCode DeregisterService( - const std::string& service_instance, - const std::string& service_name, - const std::string& service_protocol) override; - MdnsResponderErrorCode 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) override; - - private: - struct Questions { - std::map<DomainName, DNSQuestion, DomainNameComparator> a; - std::map<DomainName, DNSQuestion, DomainNameComparator> aaaa; - std::map<DomainName, DNSQuestion, DomainNameComparator> ptr; - std::map<DomainName, DNSQuestion, DomainNameComparator> srv; - std::map<DomainName, DNSQuestion, DomainNameComparator> txt; - }; - - static void AQueryCallback(mDNS* m, - DNSQuestion* question, - const ResourceRecord* answer, - QC_result added); - static void AaaaQueryCallback(mDNS* m, - DNSQuestion* question, - const ResourceRecord* answer, - QC_result added); - static void PtrQueryCallback(mDNS* m, - DNSQuestion* question, - const ResourceRecord* answer, - QC_result added); - static void SrvQueryCallback(mDNS* m, - DNSQuestion* question, - const ResourceRecord* answer, - QC_result added); - static void TxtQueryCallback(mDNS* m, - DNSQuestion* question, - const ResourceRecord* answer, - QC_result added); - static void ServiceCallback(mDNS* m, - ServiceRecordSet* service_record, - mStatus result); - - void AdvertiseInterfaces(); - void DeadvertiseInterfaces(); - void RemoveQuestionsIfEmpty(UdpSocket* socket); - - CacheEntity rr_cache_[kRrCacheSize]; - - // The main context structure for mDNSResponder. - mDNS mdns_; - - // Our own storage that is placed inside |mdns_|. The intent in C is to allow - // us access to our own state during callbacks. Here we just use it to group - // platform sockets. - mDNS_PlatformSupport platform_storage_; - - std::map<UdpSocket*, Questions> socket_to_questions_; - - std::map<UdpSocket*, NetworkInterfaceInfo> responder_interface_info_; - - std::vector<AEvent> a_responses_; - std::vector<AaaaEvent> aaaa_responses_; - std::vector<PtrEvent> ptr_responses_; - std::vector<SrvEvent> srv_responses_; - std::vector<TxtEvent> txt_responses_; - - // A list of services we are advertising. ServiceRecordSet is an - // mDNSResponder structure which holds all the resource record data - // (PTR/SRV/TXT/A and misc.) that is necessary to advertise a service. - std::vector<std::unique_ptr<ServiceRecordSet>> service_records_; -}; - -} // namespace osp -} // namespace openscreen - -#endif // OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_ADAPTER_IMPL_H_ diff --git a/osp/impl/discovery/mdns/mdns_responder_adapter_impl_unittest.cc b/osp/impl/discovery/mdns/mdns_responder_adapter_impl_unittest.cc deleted file mode 100644 index 29b76679..00000000 --- a/osp/impl/discovery/mdns/mdns_responder_adapter_impl_unittest.cc +++ /dev/null @@ -1,94 +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 <memory> -#include <string> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace openscreen { -namespace osp { -namespace { - -using ::testing::ElementsAre; -using ::testing::ElementsAreArray; - -// Example response for _openscreen._udp. Contains PTR, SRV, TXT, A records. -uint8_t data[] = { - 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, - 0x06, 0x74, 0x75, 0x72, 0x74, 0x6c, 0x65, 0x0b, 0x5f, 0x6f, 0x70, 0x65, - 0x6e, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x04, 0x5f, 0x75, 0x64, 0x70, - 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x10, 0x80, 0x01, 0x00, - 0x00, 0x11, 0x94, 0x00, 0x0e, 0x06, 0x79, 0x75, 0x72, 0x74, 0x6c, 0x65, - 0x06, 0x74, 0x75, 0x72, 0x74, 0x6c, 0x65, 0x09, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x07, 0x5f, 0x64, 0x6e, 0x73, 0x2d, 0x73, - 0x64, 0xc0, 0x1f, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x11, 0x94, 0x00, - 0x02, 0xc0, 0x13, 0xc0, 0x13, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x11, - 0x94, 0x00, 0x02, 0xc0, 0x0c, 0x11, 0x67, 0x69, 0x67, 0x6c, 0x69, 0x6f, - 0x72, 0x6f, 0x6e, 0x6f, 0x6e, 0x6f, 0x6d, 0x69, 0x63, 0x6f, 0x6e, 0xc0, - 0x24, 0x00, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x04, 0xac, - 0x11, 0x20, 0x96, 0xc0, 0x0c, 0x00, 0x21, 0x80, 0x01, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39, 0xc0, 0x71, 0xc0, - 0x0c, 0x00, 0x2f, 0x80, 0x01, 0x00, 0x00, 0x11, 0x94, 0x00, 0x09, 0xc0, - 0x0c, 0x00, 0x05, 0x00, 0x00, 0x80, 0x00, 0x40, 0xc0, 0x71, 0x00, 0x2f, - 0x80, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05, 0xc0, 0x71, 0x00, 0x01, - 0x40, 0x00, 0x00, 0x29, 0x05, 0xa0, 0x00, 0x00, 0x11, 0x94, 0x00, 0x12, - 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x50, 0x65, 0xf3, 0x41, 0x27, 0x01, -}; - -} // namespace - -TEST(MdnsResponderAdapterImplTest, ExampleData) { - const DomainName openscreen_service{{11, '_', 'o', 'p', 'e', 'n', 's', 'c', - 'r', 'e', 'e', 'n', 4, '_', 'u', 'd', - 'p', 5, 'l', 'o', 'c', 'a', 'l', 0}}; - const IPEndpoint mdns_endpoint{{224, 0, 0, 251}, 5353}; - - UdpPacket packet(std::begin(data), std::end(data)); - packet.set_source({{192, 168, 0, 2}, 6556}); - packet.set_destination(mdns_endpoint); - packet.set_socket(nullptr); - - auto mdns_adapter = - std::unique_ptr<MdnsResponderAdapter>(new MdnsResponderAdapterImpl); - mdns_adapter->Init(); - mdns_adapter->StartPtrQuery(0, openscreen_service); - mdns_adapter->OnRead(nullptr, std::move(packet)); - mdns_adapter->RunTasks(); - - auto ptr = mdns_adapter->TakePtrResponses(); - ASSERT_EQ(1u, ptr.size()); - ASSERT_THAT(ptr[0].service_instance.GetLabels(), - ElementsAre("turtle", "_openscreen", "_udp", "local")); - mdns_adapter->StartSrvQuery(0, ptr[0].service_instance); - mdns_adapter->StartTxtQuery(0, ptr[0].service_instance); - mdns_adapter->RunTasks(); - - auto srv = mdns_adapter->TakeSrvResponses(); - ASSERT_EQ(1u, srv.size()); - ASSERT_THAT(srv[0].domain_name.GetLabels(), - ElementsAre("gigliorononomicon", "local")); - EXPECT_EQ(12345, srv[0].port); - - auto txt = mdns_adapter->TakeTxtResponses(); - ASSERT_EQ(1u, txt.size()); - const std::string expected_txt[] = {"yurtle", "turtle"}; - EXPECT_THAT(txt[0].txt_info, ElementsAreArray(expected_txt)); - - mdns_adapter->StartAQuery(0, srv[0].domain_name); - mdns_adapter->RunTasks(); - - auto a = mdns_adapter->TakeAResponses(); - ASSERT_EQ(1u, a.size()); - EXPECT_EQ((IPAddress{172, 17, 32, 150}), a[0].address); - - mdns_adapter->Close(); -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/discovery/mdns/mdns_responder_platform.cc b/osp/impl/discovery/mdns/mdns_responder_platform.cc deleted file mode 100644 index 14204ff5..00000000 --- a/osp/impl/discovery/mdns/mdns_responder_platform.cc +++ /dev/null @@ -1,290 +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_platform.h" - -#include <algorithm> -#include <chrono> -#include <cstring> -#include <limits> -#include <vector> - -#include "platform/api/network_interface.h" -#include "platform/api/time.h" -#include "platform/api/udp_socket.h" -#include "platform/base/error.h" -#include "platform/base/ip_address.h" -#include "third_party/mDNSResponder/src/mDNSCore/mDNSEmbeddedAPI.h" -#include "util/osp_logging.h" - -namespace { - -using std::chrono::duration_cast; -using std::chrono::hours; -using std::chrono::milliseconds; -using std::chrono::seconds; - -} // namespace - -extern "C" { - -const char ProgramName[] = "openscreen"; - -mDNSs32 mDNSPlatformOneSecond = 1000; - -mStatus mDNSPlatformInit(mDNS* m) { - mDNSCoreInitComplete(m, mStatus_NoError); - return mStatus_NoError; -} - -void mDNSPlatformClose(mDNS* m) {} - -mStatus mDNSPlatformSendUDP(const mDNS* m, - const void* msg, - const mDNSu8* last, - mDNSInterfaceID InterfaceID, - UDPSocket* src, - const mDNSAddr* dst, - mDNSIPPort dstport) { - auto* const socket = reinterpret_cast<openscreen::UdpSocket*>(InterfaceID); - const auto socket_it = - std::find(m->p->sockets.begin(), m->p->sockets.end(), socket); - if (socket_it == m->p->sockets.end()) - return mStatus_BadInterfaceErr; - - openscreen::IPEndpoint dest{ - openscreen::IPAddress{dst->type == mDNSAddrType_IPv4 - ? openscreen::IPAddress::Version::kV4 - : openscreen::IPAddress::Version::kV6, - dst->ip.v4.b}, - static_cast<uint16_t>((dstport.b[0] << 8) | dstport.b[1])}; - const int64_t length = last - static_cast<const uint8_t*>(msg); - if (length < 0 || length > std::numeric_limits<ssize_t>::max()) { - return mStatus_BadParamErr; - } - - // UDP is inherently lossy, so don't worry about async failures and let the - // underlying protocol handle it. - (*socket_it)->SendMessage(msg, length, dest); - return mStatus_NoError; -} - -void mDNSPlatformLock(const mDNS* m) { - // We're single threaded. -} - -void mDNSPlatformUnlock(const mDNS* m) {} - -void mDNSPlatformStrCopy(void* dst, const void* src) { - // Unfortunately, the caller is responsible for making sure that dst - // if of sufficient length to store the src string. Otherwise we may - // cause an access violation. - std::strcpy(static_cast<char*>(dst), // NOLINT - static_cast<const char*>(src)); -} - -mDNSu32 mDNSPlatformStrLen(const void* src) { - return std::strlen(static_cast<const char*>(src)); -} - -void mDNSPlatformMemCopy(void* dst, const void* src, mDNSu32 len) { - std::memcpy(dst, src, len); -} - -mDNSBool mDNSPlatformMemSame(const void* dst, const void* src, mDNSu32 len) { - return std::memcmp(dst, src, len) == 0 ? mDNStrue : mDNSfalse; -} - -void mDNSPlatformMemZero(void* dst, mDNSu32 len) { - std::memset(dst, 0, len); -} - -void* mDNSPlatformMemAllocate(mDNSu32 len) { - return malloc(len); -} - -void mDNSPlatformMemFree(void* mem) { - free(mem); -} - -mDNSu32 mDNSPlatformRandomSeed() { - return std::chrono::steady_clock::now().time_since_epoch().count(); -} - -mStatus mDNSPlatformTimeInit() { - return mStatus_NoError; -} - -mDNSs32 mDNSPlatformRawTime() { - using openscreen::Clock; - - const Clock::time_point now = Clock::now(); - - // A signed 32-bit integer counting milliseconds only gives ~24.8 days of - // range. Thus, the first time this function is called, record a new origin - // timestamp to subtract from the raw monotonic clock values. The "one hour - // before now" value is used to keep the results well-ahead of zero because - // the mDNS library assumes this is the time since kernel boot and has hacks - // to disable certain things in the first few minutes. :-/ - static const Clock::time_point origin = now - hours(1); - - const int64_t millis_since_origin = - duration_cast<milliseconds>(now - origin).count(); - OSP_CHECK_LE(millis_since_origin, std::numeric_limits<mDNSs32>::max()); - return static_cast<mDNSs32>(millis_since_origin); -} - -mDNSs32 mDNSPlatformUTC() { - const auto seconds_since_epoch = - duration_cast<seconds>(openscreen::GetWallTimeSinceUnixEpoch()).count(); - - // The return type will cause overflow in early 2038. Warn future developers - // a year ahead of time. - constexpr mDNSs32 a_year_before_overflow = - std::numeric_limits<mDNSs32>::max() - - duration_cast<seconds>(365 * hours(24)).count(); - OSP_DCHECK_LE(seconds_since_epoch, a_year_before_overflow); - - return static_cast<mDNSs32>(seconds_since_epoch); -} - -void mDNSPlatformWriteDebugMsg(const char* msg) { - OSP_DVLOG << __func__ << ": " << msg; -} - -void mDNSPlatformWriteLogMsg(const char* ident, - const char* msg, - mDNSLogLevel_t loglevel) { - OSP_VLOG << __func__ << ": " << msg; -} - -TCPSocket* mDNSPlatformTCPSocket(mDNS* const m, - TCPSocketFlags flags, - mDNSIPPort* port) { - OSP_UNIMPLEMENTED(); - return nullptr; -} - -TCPSocket* mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd) { - OSP_UNIMPLEMENTED(); - return nullptr; -} - -int mDNSPlatformTCPGetFD(TCPSocket* sock) { - OSP_UNIMPLEMENTED(); - return 0; -} - -mStatus mDNSPlatformTCPConnect(TCPSocket* sock, - const mDNSAddr* dst, - mDNSOpaque16 dstport, - domainname* hostname, - mDNSInterfaceID InterfaceID, - TCPConnectionCallback callback, - void* context) { - OSP_UNIMPLEMENTED(); - return mStatus_NoError; -} - -void mDNSPlatformTCPCloseConnection(TCPSocket* sock) { - OSP_UNIMPLEMENTED(); -} - -long mDNSPlatformReadTCP(TCPSocket* sock, // NOLINT - void* buf, - unsigned long buflen, // NOLINT - mDNSBool* closed) { - OSP_UNIMPLEMENTED(); - return 0; -} - -long mDNSPlatformWriteTCP(TCPSocket* sock, // NOLINT - const char* msg, - unsigned long len) { // NOLINT - OSP_UNIMPLEMENTED(); - return 0; -} - -UDPSocket* mDNSPlatformUDPSocket(mDNS* const m, - const mDNSIPPort requestedport) { - OSP_UNIMPLEMENTED(); - return nullptr; -} - -void mDNSPlatformUDPClose(UDPSocket* sock) { - OSP_UNIMPLEMENTED(); -} - -void mDNSPlatformReceiveBPF_fd(mDNS* const m, int fd) { - OSP_UNIMPLEMENTED(); -} - -void mDNSPlatformUpdateProxyList(mDNS* const m, - const mDNSInterfaceID InterfaceID) { - OSP_UNIMPLEMENTED(); -} - -void mDNSPlatformSendRawPacket(const void* const msg, - const mDNSu8* const end, - mDNSInterfaceID InterfaceID) { - OSP_UNIMPLEMENTED(); -} - -void mDNSPlatformSetLocalAddressCacheEntry(mDNS* const m, - const mDNSAddr* const tpa, - const mDNSEthAddr* const tha, - mDNSInterfaceID InterfaceID) {} - -void mDNSPlatformSourceAddrForDest(mDNSAddr* const src, - const mDNSAddr* const dst) {} - -mStatus mDNSPlatformTLSSetupCerts(void) { - OSP_UNIMPLEMENTED(); - return mStatus_NoError; -} - -void mDNSPlatformTLSTearDownCerts(void) { - OSP_UNIMPLEMENTED(); -} - -void mDNSPlatformSetDNSConfig(mDNS* const m, - mDNSBool setservers, - mDNSBool setsearch, - domainname* const fqdn, - DNameListElem** RegDomains, - DNameListElem** BrowseDomains) { - if (fqdn) { - std::memset(fqdn, 0, sizeof(*fqdn)); - } -} - -mStatus mDNSPlatformGetPrimaryInterface(mDNS* const m, - mDNSAddr* v4, - mDNSAddr* v6, - mDNSAddr* router) { - return mStatus_NoError; -} - -void mDNSPlatformDynDNSHostNameStatusChanged(const domainname* const dname, - const mStatus status) {} - -void mDNSPlatformSetAllowSleep(mDNS* const m, - mDNSBool allowSleep, - const char* reason) {} - -void mDNSPlatformSendWakeupPacket(mDNS* const m, - mDNSInterfaceID InterfaceID, - char* EthAddr, - char* IPAddr, - int iteration) { - OSP_UNIMPLEMENTED(); -} - -mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord* rr, - const NetworkInterfaceInfo* intf) { - OSP_UNIMPLEMENTED(); - return mDNStrue; -} - -} // extern "C" diff --git a/osp/impl/discovery/mdns/mdns_responder_platform.h b/osp/impl/discovery/mdns/mdns_responder_platform.h deleted file mode 100644 index 342913fe..00000000 --- a/osp/impl/discovery/mdns/mdns_responder_platform.h +++ /dev/null @@ -1,16 +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. - -#ifndef OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_PLATFORM_H_ -#define OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_PLATFORM_H_ - -#include <vector> - -#include "platform/api/udp_socket.h" - -struct mDNS_PlatformSupport_struct { - std::vector<openscreen::UdpSocket*> sockets; -}; - -#endif // OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_PLATFORM_H_ diff --git a/osp/impl/dns_sd_publisher_client.cc b/osp/impl/dns_sd_publisher_client.cc new file mode 100644 index 00000000..322bd4e0 --- /dev/null +++ b/osp/impl/dns_sd_publisher_client.cc @@ -0,0 +1,131 @@ +// Copyright 2021 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/dns_sd_publisher_client.h" + +#include <utility> + +#include "discovery/common/config.h" +#include "discovery/dnssd/public/dns_sd_instance.h" +#include "discovery/dnssd/public/dns_sd_txt_record.h" +#include "discovery/public/dns_sd_service_factory.h" +#include "osp/public/service_info.h" +#include "platform/base/macros.h" +#include "util/osp_logging.h" + +namespace openscreen { +namespace osp { + +using State = ServicePublisher::State; + +namespace { + +constexpr char kFriendlyNameTxtKey[] = "fn"; +constexpr char kDnsSdDomainId[] = "local"; + +discovery::DnsSdInstance ServiceConfigToDnsSdInstance( + const ServicePublisher::Config& config) { + discovery::DnsSdTxtRecord txt; + const bool did_set_everything = + txt.SetValue(kFriendlyNameTxtKey, config.friendly_name).ok(); + OSP_DCHECK(did_set_everything); + + // NOTE: Not totally clear how we should be using config.hostname, which in + // principle is already part of config.service_instance_name. + return discovery::DnsSdInstance( + config.service_instance_name, kOpenScreenServiceName, kDnsSdDomainId, + std::move(txt), config.connection_server_port); +} + +} // namespace + +DnsSdPublisherClient::DnsSdPublisherClient(ServicePublisher::Observer* observer, + openscreen::TaskRunner* task_runner) + : observer_(observer), task_runner_(task_runner) { + OSP_DCHECK(observer_); + OSP_DCHECK(task_runner_); +} + +DnsSdPublisherClient::~DnsSdPublisherClient() = default; + +void DnsSdPublisherClient::StartPublisher( + const ServicePublisher::Config& config) { + OSP_LOG_INFO << "StartPublisher with " << config.network_interfaces.size() + << " interfaces"; + StartPublisherInternal(config); + Error result = dns_sd_publisher_->Register(config); + if (result.ok()) { + SetState(State::kRunning); + } else { + OnFatalError(result); + SetState(State::kStopped); + } +} + +void DnsSdPublisherClient::StartAndSuspendPublisher( + const ServicePublisher::Config& config) { + StartPublisherInternal(config); + SetState(State::kSuspended); +} + +void DnsSdPublisherClient::StopPublisher() { + dns_sd_publisher_.reset(); + SetState(State::kStopped); +} + +void DnsSdPublisherClient::SuspendPublisher() { + OSP_DCHECK(dns_sd_publisher_); + dns_sd_publisher_->DeregisterAll(); + SetState(State::kSuspended); +} + +void DnsSdPublisherClient::ResumePublisher( + const ServicePublisher::Config& config) { + OSP_DCHECK(dns_sd_publisher_); + dns_sd_publisher_->Register(config); + SetState(State::kRunning); +} + +void DnsSdPublisherClient::OnFatalError(Error error) { + observer_->OnError(error); +} + +void DnsSdPublisherClient::OnRecoverableError(Error error) { + observer_->OnError(error); +} + +void DnsSdPublisherClient::StartPublisherInternal( + const ServicePublisher::Config& config) { + OSP_DCHECK(!dns_sd_publisher_); + if (!dns_sd_service_) { + dns_sd_service_ = CreateDnsSdServiceInternal(config); + } + dns_sd_publisher_ = std::make_unique<OspDnsSdPublisher>( + dns_sd_service_.get(), kOpenScreenServiceName, + ServiceConfigToDnsSdInstance); +} + +SerialDeletePtr<discovery::DnsSdService> +DnsSdPublisherClient::CreateDnsSdServiceInternal( + const ServicePublisher::Config& config) { + // NOTE: With the current API, the client cannot customize the behavior of + // DNS-SD beyond the interface list. + openscreen::discovery::Config dns_sd_config; + dns_sd_config.enable_querying = false; + dns_sd_config.network_info = config.network_interfaces; + + // NOTE: + // It's desirable for the DNS-SD publisher and the DNS-SD listener for OSP to + // share the underlying mDNS socket and state, to avoid the agent from + // binding 2 sockets per network interface. + // + // This can be accomplished by having the agent use a shared instance of the + // discovery::DnsSdService, e.g. through a ref-counting handle, so that the + // OSP publisher and the OSP listener don't have to coordinate through an + // additional object. + return CreateDnsSdService(task_runner_, this, dns_sd_config); +} + +} // namespace osp +} // namespace openscreen diff --git a/osp/impl/dns_sd_publisher_client.h b/osp/impl/dns_sd_publisher_client.h new file mode 100644 index 00000000..9b055ed5 --- /dev/null +++ b/osp/impl/dns_sd_publisher_client.h @@ -0,0 +1,62 @@ +// Copyright 2021 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. + +#ifndef OSP_IMPL_DNS_SD_PUBLISHER_CLIENT_H_ +#define OSP_IMPL_DNS_SD_PUBLISHER_CLIENT_H_ + +#include <memory> + +#include "discovery/common/reporting_client.h" +#include "discovery/dnssd/public/dns_sd_service.h" +#include "discovery/public/dns_sd_service_publisher.h" +#include "osp/impl/service_publisher_impl.h" +#include "platform/api/serial_delete_ptr.h" + +namespace openscreen { + +class TaskRunner; + +namespace osp { + +class DnsSdPublisherClient final : public ServicePublisherImpl::Delegate, + openscreen::discovery::ReportingClient { + public: + DnsSdPublisherClient(ServicePublisher::Observer* observer, + openscreen::TaskRunner* task_runner); + ~DnsSdPublisherClient() override; + + // ServicePublisherImpl::Delegate overrides. + void StartPublisher(const ServicePublisher::Config& config) override; + void StartAndSuspendPublisher( + const ServicePublisher::Config& config) override; + void StopPublisher() override; + void SuspendPublisher() override; + void ResumePublisher(const ServicePublisher::Config& config) override; + + private: + DnsSdPublisherClient(const DnsSdPublisherClient&) = delete; + DnsSdPublisherClient(DnsSdPublisherClient&&) noexcept = delete; + + // openscreen::discovery::ReportingClient overrides. + void OnFatalError(Error) override; + void OnRecoverableError(Error) override; + + void StartPublisherInternal(const ServicePublisher::Config& config); + SerialDeletePtr<discovery::DnsSdService> CreateDnsSdServiceInternal( + const ServicePublisher::Config& config); + + ServicePublisher::Observer* const observer_; + TaskRunner* const task_runner_; + SerialDeletePtr<discovery::DnsSdService> dns_sd_service_; + + using OspDnsSdPublisher = + discovery::DnsSdServicePublisher<ServicePublisher::Config>; + + std::unique_ptr<OspDnsSdPublisher> dns_sd_publisher_; +}; + +} // namespace osp +} // namespace openscreen + +#endif // OSP_IMPL_DNS_SD_PUBLISHER_CLIENT_H_ diff --git a/osp/impl/dns_sd_service_publisher_factory.cc b/osp/impl/dns_sd_service_publisher_factory.cc new file mode 100644 index 00000000..5c63dc83 --- /dev/null +++ b/osp/impl/dns_sd_service_publisher_factory.cc @@ -0,0 +1,34 @@ +// Copyright 2021 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 <algorithm> +#include <memory> + +#include "discovery/dnssd/public/dns_sd_publisher.h" +#include "osp/impl/dns_sd_publisher_client.h" +#include "osp/impl/service_publisher_impl.h" +#include "osp/public/service_publisher.h" +#include "osp/public/service_publisher_factory.h" + +namespace openscreen { + +class TaskRunner; + +namespace osp { + +// static +std::unique_ptr<ServicePublisher> ServicePublisherFactory::Create( + const ServicePublisher::Config& config, + ServicePublisher::Observer* observer, + TaskRunner* task_runner) { + auto dns_sd_client = + std::make_unique<DnsSdPublisherClient>(observer, task_runner); + auto publisher_impl = std::make_unique<ServicePublisherImpl>( + observer, std::move(dns_sd_client)); + publisher_impl->SetConfig(config); + return publisher_impl; +} + +} // namespace osp +} // namespace openscreen diff --git a/osp/impl/internal_services.cc b/osp/impl/internal_services.cc deleted file mode 100644 index 19b55927..00000000 --- a/osp/impl/internal_services.cc +++ /dev/null @@ -1,229 +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/internal_services.h" - -#include <algorithm> -#include <utility> - -#include "osp/impl/discovery/mdns/mdns_responder_adapter_impl.h" -#include "osp/impl/mdns_responder_service.h" -#include "platform/api/udp_socket.h" -#include "platform/base/error.h" -#include "util/osp_logging.h" - -namespace openscreen { -namespace osp { -namespace { - -constexpr char kServiceName[] = "_openscreen"; -constexpr char kServiceProtocol[] = "_udp"; -const IPAddress kMulticastAddress{224, 0, 0, 251}; -const IPAddress kMulticastIPv6Address{ - // ff02::fb - 0xff02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00fb, -}; -const uint16_t kMulticastListeningPort = 5353; - -class MdnsResponderAdapterImplFactory final - : public MdnsResponderAdapterFactory { - public: - MdnsResponderAdapterImplFactory() = default; - ~MdnsResponderAdapterImplFactory() override = default; - - std::unique_ptr<MdnsResponderAdapter> Create() override { - return std::make_unique<MdnsResponderAdapterImpl>(); - } -}; - -Error SetUpMulticastSocket(UdpSocket* socket, NetworkInterfaceIndex ifindex) { - const IPAddress broadcast_address = - socket->IsIPv6() ? kMulticastIPv6Address : kMulticastAddress; - - socket->JoinMulticastGroup(broadcast_address, ifindex); - socket->SetMulticastOutboundInterface(ifindex); - socket->Bind(); - - return Error::None(); -} - -// Ref-counted singleton instance of InternalServices. This lives only as long -// as there is at least one ServiceListener and/or ServicePublisher alive. -InternalServices* g_instance = nullptr; -int g_instance_ref_count = 0; - -} // namespace - -// static -std::unique_ptr<ServiceListener> InternalServices::CreateListener( - const MdnsServiceListenerConfig& config, - ServiceListener::Observer* observer, - TaskRunner* task_runner) { - auto* services = ReferenceSingleton(task_runner); - auto listener = - std::make_unique<ServiceListenerImpl>(&services->mdns_service_); - listener->AddObserver(observer); - listener->SetDestructionCallback(&InternalServices::DereferenceSingleton, - services); - return listener; -} - -// static -std::unique_ptr<ServicePublisher> InternalServices::CreatePublisher( - const ServicePublisher::Config& config, - ServicePublisher::Observer* observer, - TaskRunner* task_runner) { - auto* services = ReferenceSingleton(task_runner); - services->mdns_service_.SetServiceConfig( - config.hostname, config.service_instance_name, - config.connection_server_port, config.network_interface_indices, - {{"fn", config.friendly_name}}); - auto publisher = std::make_unique<ServicePublisherImpl>( - observer, &services->mdns_service_); - publisher->SetDestructionCallback(&InternalServices::DereferenceSingleton, - services); - return publisher; -} - -InternalServices::InternalPlatformLinkage::InternalPlatformLinkage( - InternalServices* parent) - : parent_(parent) {} - -InternalServices::InternalPlatformLinkage::~InternalPlatformLinkage() { - // If there are open sockets, then there will be dangling references to - // destroyed objects after destruction. - OSP_CHECK(open_sockets_.empty()); -} - -std::vector<MdnsPlatformService::BoundInterface> -InternalServices::InternalPlatformLinkage::RegisterInterfaces( - const std::vector<NetworkInterfaceIndex>& allowlist) { - const std::vector<InterfaceInfo> interfaces = GetNetworkInterfaces(); - const bool do_filter_using_allowlist = !allowlist.empty(); - std::vector<NetworkInterfaceIndex> index_list; - for (const auto& interface : interfaces) { - OSP_VLOG << "Found interface: " << interface; - if (do_filter_using_allowlist && - std::find(allowlist.begin(), allowlist.end(), interface.index) == - allowlist.end()) { - OSP_VLOG << "Ignoring interface not in allowed list: " << interface; - continue; - } - if (!interface.addresses.empty()) - index_list.push_back(interface.index); - } - OSP_LOG_IF(WARN, index_list.empty()) - << "No network interfaces had usable addresses for mDNS."; - - // Set up sockets to send and listen to mDNS multicast traffic on all - // interfaces. - std::vector<BoundInterface> result; - for (NetworkInterfaceIndex index : index_list) { - const auto& interface = *std::find_if( - interfaces.begin(), interfaces.end(), - [index](const InterfaceInfo& info) { return info.index == index; }); - if (interface.addresses.empty()) { - continue; - } - - // Pick any address for the given interface. - const IPSubnet& primary_subnet = interface.addresses.front(); - - auto create_result = - UdpSocket::Create(parent_->task_runner_, parent_, - IPEndpoint{{}, kMulticastListeningPort}); - if (!create_result) { - OSP_LOG_ERROR << "failed to create socket for interface " << index << ": " - << create_result.error().message(); - continue; - } - std::unique_ptr<UdpSocket> socket = std::move(create_result.value()); - if (!SetUpMulticastSocket(socket.get(), index).ok()) { - continue; - } - result.emplace_back(interface, primary_subnet, socket.get()); - parent_->RegisterMdnsSocket(socket.get()); - - open_sockets_.emplace_back(std::move(socket)); - } - - return result; -} - -void InternalServices::InternalPlatformLinkage::DeregisterInterfaces( - const std::vector<BoundInterface>& registered_interfaces) { - for (const auto& interface : registered_interfaces) { - UdpSocket* const socket = interface.socket; - parent_->DeregisterMdnsSocket(socket); - - const auto it = std::find_if(open_sockets_.begin(), open_sockets_.end(), - [socket](const std::unique_ptr<UdpSocket>& s) { - return s.get() == socket; - }); - OSP_DCHECK(it != open_sockets_.end()); - open_sockets_.erase(it); - } -} - -InternalServices::InternalServices(ClockNowFunctionPtr now_function, - TaskRunner* task_runner) - : mdns_service_(now_function, - task_runner, - kServiceName, - kServiceProtocol, - std::make_unique<MdnsResponderAdapterImplFactory>(), - std::make_unique<InternalPlatformLinkage>(this)), - task_runner_(task_runner) {} - -InternalServices::~InternalServices() = default; - -void InternalServices::RegisterMdnsSocket(UdpSocket* socket) { - OSP_CHECK(g_instance) << "No listener or publisher is alive."; - // TODO(rwkeane): Hook this up to the new mDNS library once we swap out the - // mDNSResponder. -} - -void InternalServices::DeregisterMdnsSocket(UdpSocket* socket) { - // TODO(rwkeane): Hook this up to the new mDNS library once we swap out the - // mDNSResponder. -} - -// static -InternalServices* InternalServices::ReferenceSingleton( - TaskRunner* task_runner) { - if (!g_instance) { - OSP_CHECK_EQ(g_instance_ref_count, 0); - g_instance = new InternalServices(&Clock::now, task_runner); - } - ++g_instance_ref_count; - return g_instance; -} - -// static -void InternalServices::DereferenceSingleton(void* instance) { - OSP_CHECK_EQ(static_cast<InternalServices*>(instance), g_instance); - OSP_CHECK_GT(g_instance_ref_count, 0); - --g_instance_ref_count; - if (g_instance_ref_count == 0) { - delete g_instance; - g_instance = nullptr; - } -} - -void InternalServices::OnError(UdpSocket* socket, Error error) { - OSP_LOG_ERROR << "failed to configure socket " << error.message(); - this->DeregisterMdnsSocket(socket); -} - -void InternalServices::OnSendError(UdpSocket* socket, Error error) { - // TODO(crbug.com/openscreen/67): Implement this method. - OSP_UNIMPLEMENTED(); -} - -void InternalServices::OnRead(UdpSocket* socket, ErrorOr<UdpPacket> packet) { - g_instance->mdns_service_.OnRead(socket, std::move(packet)); -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/internal_services.h b/osp/impl/internal_services.h deleted file mode 100644 index 042be4c1..00000000 --- a/osp/impl/internal_services.h +++ /dev/null @@ -1,90 +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. - -#ifndef OSP_IMPL_INTERNAL_SERVICES_H_ -#define OSP_IMPL_INTERNAL_SERVICES_H_ - -#include <memory> -#include <vector> - -#include "osp/impl/mdns_platform_service.h" -#include "osp/impl/mdns_responder_service.h" -#include "osp/impl/quic/quic_connection_factory.h" -#include "osp/impl/service_listener_impl.h" -#include "osp/impl/service_publisher_impl.h" -#include "osp/public/mdns_service_listener_factory.h" -#include "osp/public/mdns_service_publisher_factory.h" -#include "osp/public/protocol_connection_client.h" -#include "osp/public/protocol_connection_server.h" -#include "platform/api/network_interface.h" -#include "platform/api/time.h" -#include "platform/api/udp_socket.h" -#include "platform/base/ip_address.h" -#include "platform/base/macros.h" - -namespace openscreen { - -class TaskRunner; - -namespace osp { - -// Factory for ServiceListener and ServicePublisher instances; owns internal -// objects needed to instantiate them such as MdnsResponderService and runs an -// event loop. -// TODO(btolsch): This may be renamed and/or split up once QUIC code lands and -// this use case is more concrete. -class InternalServices : UdpSocket::Client { - public: - static std::unique_ptr<ServiceListener> CreateListener( - const MdnsServiceListenerConfig& config, - ServiceListener::Observer* observer, - TaskRunner* task_runner); - static std::unique_ptr<ServicePublisher> CreatePublisher( - const ServicePublisher::Config& config, - ServicePublisher::Observer* observer, - TaskRunner* task_runner); - - // UdpSocket::Client overrides. - void OnError(UdpSocket* socket, Error error) override; - void OnSendError(UdpSocket* socket, Error error) override; - void OnRead(UdpSocket* socket, ErrorOr<UdpPacket> packet) override; - - private: - class InternalPlatformLinkage final : public MdnsPlatformService { - public: - explicit InternalPlatformLinkage(InternalServices* parent); - ~InternalPlatformLinkage() override; - - std::vector<BoundInterface> RegisterInterfaces( - const std::vector<NetworkInterfaceIndex>& allowlist) override; - void DeregisterInterfaces( - const std::vector<BoundInterface>& registered_interfaces) override; - - private: - InternalServices* const parent_; - std::vector<std::unique_ptr<UdpSocket>> open_sockets_; - }; - - // The TaskRunner provided here should live for the duration of this - // InternalService object's lifetime. - InternalServices(ClockNowFunctionPtr now_function, TaskRunner* task_runner); - ~InternalServices() override; - - void RegisterMdnsSocket(UdpSocket* socket); - void DeregisterMdnsSocket(UdpSocket* socket); - - static InternalServices* ReferenceSingleton(TaskRunner* task_runner); - static void DereferenceSingleton(void* instance); - - MdnsResponderService mdns_service_; - - TaskRunner* const task_runner_; - - OSP_DISALLOW_COPY_AND_ASSIGN(InternalServices); -}; - -} // namespace osp -} // namespace openscreen - -#endif // OSP_IMPL_INTERNAL_SERVICES_H_ diff --git a/osp/impl/mdns_platform_service.cc b/osp/impl/mdns_platform_service.cc deleted file mode 100644 index 4968c259..00000000 --- a/osp/impl/mdns_platform_service.cc +++ /dev/null @@ -1,46 +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/mdns_platform_service.h" - -#include <cstring> - -#include "util/osp_logging.h" - -namespace openscreen { -namespace osp { - -MdnsPlatformService::BoundInterface::BoundInterface( - const InterfaceInfo& interface_info, - const IPSubnet& subnet, - UdpSocket* socket) - : interface_info(interface_info), subnet(subnet), socket(socket) { - OSP_DCHECK(socket); -} - -MdnsPlatformService::BoundInterface::~BoundInterface() = default; - -bool MdnsPlatformService::BoundInterface::operator==( - const MdnsPlatformService::BoundInterface& other) const { - if (interface_info.index != other.interface_info.index) - return false; - - if (subnet.address != other.subnet.address || - subnet.prefix_length != other.subnet.prefix_length) { - return false; - } - - if (socket != other.socket) - return false; - - return true; -} - -bool MdnsPlatformService::BoundInterface::operator!=( - const MdnsPlatformService::BoundInterface& other) const { - return !(*this == other); -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/mdns_platform_service.h b/osp/impl/mdns_platform_service.h deleted file mode 100644 index aca4ffd7..00000000 --- a/osp/impl/mdns_platform_service.h +++ /dev/null @@ -1,43 +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. - -#ifndef OSP_IMPL_MDNS_PLATFORM_SERVICE_H_ -#define OSP_IMPL_MDNS_PLATFORM_SERVICE_H_ - -#include <vector> - -#include "platform/api/network_interface.h" -#include "platform/api/udp_socket.h" - -namespace openscreen { -namespace osp { - -class MdnsPlatformService { - public: - struct BoundInterface { - BoundInterface(const InterfaceInfo& interface_info, - const IPSubnet& subnet, - UdpSocket* socket); - ~BoundInterface(); - - bool operator==(const BoundInterface& other) const; - bool operator!=(const BoundInterface& other) const; - - InterfaceInfo interface_info; - IPSubnet subnet; - UdpSocket* socket; - }; - - virtual ~MdnsPlatformService() = default; - - virtual std::vector<BoundInterface> RegisterInterfaces( - const std::vector<NetworkInterfaceIndex>& allowlist) = 0; - virtual void DeregisterInterfaces( - const std::vector<BoundInterface>& registered_interfaces) = 0; -}; - -} // namespace osp -} // namespace openscreen - -#endif // OSP_IMPL_MDNS_PLATFORM_SERVICE_H_ diff --git a/osp/impl/mdns_responder_service.cc b/osp/impl/mdns_responder_service.cc deleted file mode 100644 index f9a80fae..00000000 --- a/osp/impl/mdns_responder_service.cc +++ /dev/null @@ -1,664 +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/mdns_responder_service.h" - -#include <algorithm> -#include <memory> -#include <utility> - -#include "osp/impl/internal_services.h" -#include "platform/base/error.h" -#include "util/osp_logging.h" -#include "util/trace_logging.h" - -namespace openscreen { -namespace osp { -namespace { - -// TODO(btolsch): This should probably at least also contain network identity -// information. -std::string ServiceIdFromServiceInstanceName( - const DomainName& service_instance) { - std::string service_id; - service_id.assign( - reinterpret_cast<const char*>(service_instance.domain_name().data()), - service_instance.domain_name().size()); - return service_id; -} - -} // namespace - -MdnsResponderService::MdnsResponderService( - ClockNowFunctionPtr now_function, - TaskRunner* task_runner, - const std::string& service_name, - const std::string& service_protocol, - std::unique_ptr<MdnsResponderAdapterFactory> mdns_responder_factory, - std::unique_ptr<MdnsPlatformService> platform) - : service_type_{{service_name, service_protocol}}, - mdns_responder_factory_(std::move(mdns_responder_factory)), - platform_(std::move(platform)), - task_runner_(task_runner), - background_tasks_alarm_(now_function, task_runner) {} - -MdnsResponderService::~MdnsResponderService() = default; - -void MdnsResponderService::SetServiceConfig( - const std::string& hostname, - const std::string& instance, - uint16_t port, - const std::vector<NetworkInterfaceIndex> allowlist, - const std::map<std::string, std::string>& txt_data) { - OSP_DCHECK(!hostname.empty()); - OSP_DCHECK(!instance.empty()); - OSP_DCHECK_NE(0, port); - service_hostname_ = hostname; - service_instance_name_ = instance; - service_port_ = port; - interface_index_allowlist_ = allowlist; - service_txt_data_ = txt_data; -} - -void MdnsResponderService::OnRead(UdpSocket* socket, - ErrorOr<UdpPacket> packet) { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderService::OnRead"); - if (!mdns_responder_) { - return; - } - - mdns_responder_->OnRead(socket, std::move(packet)); - HandleMdnsEvents(); -} - -void MdnsResponderService::OnSendError(UdpSocket* socket, Error error) { - mdns_responder_->OnSendError(socket, std::move(error)); -} - -void MdnsResponderService::OnError(UdpSocket* socket, Error error) { - mdns_responder_->OnError(socket, std::move(error)); -} - -void MdnsResponderService::StartListener() { - task_runner_->PostTask([this]() { this->StartListenerInternal(); }); -} - -void MdnsResponderService::StartAndSuspendListener() { - task_runner_->PostTask([this]() { this->StartAndSuspendListenerInternal(); }); -} - -void MdnsResponderService::StopListener() { - task_runner_->PostTask([this]() { this->StopListenerInternal(); }); -} - -void MdnsResponderService::SuspendListener() { - task_runner_->PostTask([this]() { this->SuspendListenerInternal(); }); -} - -void MdnsResponderService::ResumeListener() { - task_runner_->PostTask([this]() { this->ResumeListenerInternal(); }); -} - -void MdnsResponderService::SearchNow(ServiceListener::State from) { - task_runner_->PostTask([this, from]() { this->SearchNowInternal(from); }); -} - -void MdnsResponderService::StartPublisher() { - task_runner_->PostTask([this]() { this->StartPublisherInternal(); }); -} - -void MdnsResponderService::StartAndSuspendPublisher() { - task_runner_->PostTask( - [this]() { this->StartAndSuspendPublisherInternal(); }); -} - -void MdnsResponderService::StopPublisher() { - task_runner_->PostTask([this]() { this->StopPublisherInternal(); }); -} - -void MdnsResponderService::SuspendPublisher() { - task_runner_->PostTask([this]() { this->SuspendPublisherInternal(); }); -} - -void MdnsResponderService::ResumePublisher() { - task_runner_->PostTask([this]() { this->ResumePublisherInternal(); }); -} - -void MdnsResponderService::StartListenerInternal() { - if (!mdns_responder_) { - mdns_responder_ = mdns_responder_factory_->Create(); - } - - StartListening(); - ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kRunning); - RunBackgroundTasks(); -} - -void MdnsResponderService::StartAndSuspendListenerInternal() { - mdns_responder_ = mdns_responder_factory_->Create(); - ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kSuspended); -} - -void MdnsResponderService::StopListenerInternal() { - StopListening(); - if (!publisher_ || publisher_->state() == ServicePublisher::State::kStopped || - publisher_->state() == ServicePublisher::State::kSuspended) { - StopMdnsResponder(); - if (!publisher_ || publisher_->state() == ServicePublisher::State::kStopped) - mdns_responder_.reset(); - } - ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kStopped); -} - -void MdnsResponderService::SuspendListenerInternal() { - StopMdnsResponder(); - ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kSuspended); -} - -void MdnsResponderService::ResumeListenerInternal() { - StartListening(); - ServiceListenerImpl::Delegate::SetState(ServiceListener::State::kRunning); -} - -void MdnsResponderService::SearchNowInternal(ServiceListener::State from) { - ServiceListenerImpl::Delegate::SetState(from); -} - -void MdnsResponderService::StartPublisherInternal() { - if (!mdns_responder_) { - mdns_responder_ = mdns_responder_factory_->Create(); - } - - StartService(); - ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kRunning); - RunBackgroundTasks(); -} - -void MdnsResponderService::StartAndSuspendPublisherInternal() { - mdns_responder_ = mdns_responder_factory_->Create(); - ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kSuspended); -} - -void MdnsResponderService::StopPublisherInternal() { - StopService(); - if (!listener_ || listener_->state() == ServiceListener::State::kStopped || - listener_->state() == ServiceListener::State::kSuspended) { - StopMdnsResponder(); - if (!listener_ || listener_->state() == ServiceListener::State::kStopped) - mdns_responder_.reset(); - } - ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kStopped); -} - -void MdnsResponderService::SuspendPublisherInternal() { - StopService(); - ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kSuspended); -} - -void MdnsResponderService::ResumePublisherInternal() { - StartService(); - ServicePublisherImpl::Delegate::SetState(ServicePublisher::State::kRunning); -} - -bool MdnsResponderService::NetworkScopedDomainNameComparator::operator()( - const NetworkScopedDomainName& a, - const NetworkScopedDomainName& b) const { - if (a.socket != b.socket) { - return (a.socket - b.socket) < 0; - } - return DomainNameComparator()(a.domain_name, b.domain_name); -} - -void MdnsResponderService::HandleMdnsEvents() { - TRACE_SCOPED(TraceCategory::kMdns, "MdnsResponderService::HandleMdnsEvents"); - // NOTE: In the common case, we will get a single combined packet for - // PTR/SRV/TXT/A and then no other packets. If we don't loop here, we would - // start SRV/TXT queries based on the PTR response, but never check for events - // again. This should no longer be a problem when we have correct scheduling - // of RunTasks. - bool events_possible = false; - // NOTE: This set will track which service instances were changed by all the - // events throughout all the loop iterations. At the end, we can dispatch our - // ServiceInfo updates to |listener_| just once (e.g. instead of - // OnReceiverChanged, OnReceiverChanged, ..., just a single - // OnReceiverChanged). - InstanceNameSet modified_instance_names; - do { - events_possible = false; - for (auto& ptr_event : mdns_responder_->TakePtrResponses()) { - events_possible = HandlePtrEvent(ptr_event, &modified_instance_names) || - events_possible; - } - for (auto& srv_event : mdns_responder_->TakeSrvResponses()) { - events_possible = HandleSrvEvent(srv_event, &modified_instance_names) || - events_possible; - } - for (auto& txt_event : mdns_responder_->TakeTxtResponses()) { - events_possible = HandleTxtEvent(txt_event, &modified_instance_names) || - events_possible; - } - for (const auto& a_event : mdns_responder_->TakeAResponses()) { - events_possible = - HandleAEvent(a_event, &modified_instance_names) || events_possible; - } - for (const auto& aaaa_event : mdns_responder_->TakeAaaaResponses()) { - events_possible = HandleAaaaEvent(aaaa_event, &modified_instance_names) || - events_possible; - } - if (events_possible) { - // NOTE: This still needs to be called here, even though it runs in the - // background regularly, because we just finished processing MDNS events. - RunBackgroundTasks(); - } - } while (events_possible); - - for (const auto& instance_name : modified_instance_names) { - auto service_entry = service_by_name_.find(instance_name); - std::unique_ptr<ServiceInstance>& service = service_entry->second; - - std::string service_id = ServiceIdFromServiceInstanceName(instance_name); - auto receiver_info_entry = receiver_info_.find(service_id); - HostInfo* host = GetHostInfo(service->ptr_socket, service->domain_name); - if (!IsServiceReady(*service, host)) { - if (receiver_info_entry != receiver_info_.end()) { - const ServiceInfo& receiver_info = receiver_info_entry->second; - listener_->OnReceiverRemoved(receiver_info); - receiver_info_.erase(receiver_info_entry); - } - if (!service->has_ptr_record && !service->has_srv()) - service_by_name_.erase(service_entry); - continue; - } - - // TODO(btolsch): Verify UTF-8 here. - std::string friendly_name(instance_name.GetLabels()[0]); - - if (receiver_info_entry == receiver_info_.end()) { - ServiceInfo receiver_info{ - std::move(service_id), - std::move(friendly_name), - GetNetworkInterfaceIndexFromSocket(service->ptr_socket), - {host->v4_address, service->port}, - {host->v6_address, service->port}}; - listener_->OnReceiverAdded(receiver_info); - receiver_info_.emplace(receiver_info.service_id, - std::move(receiver_info)); - } else { - ServiceInfo& receiver_info = receiver_info_entry->second; - if (receiver_info.Update( - std::move(friendly_name), - GetNetworkInterfaceIndexFromSocket(service->ptr_socket), - {host->v4_address, service->port}, - {host->v6_address, service->port})) { - listener_->OnReceiverChanged(receiver_info); - } - } - } -} - -void MdnsResponderService::StartListening() { - // TODO(btolsch): This needs the same |interface_index_allowlist_| logic as - // StartService, but this can also wait until the network-change TODO is - // addressed. - if (bound_interfaces_.empty()) { - mdns_responder_->Init(); - bound_interfaces_ = platform_->RegisterInterfaces({}); - for (auto& interface : bound_interfaces_) { - mdns_responder_->RegisterInterface(interface.interface_info, - interface.subnet, interface.socket); - } - } - ErrorOr<DomainName> service_type = - DomainName::FromLabels(service_type_.begin(), service_type_.end()); - OSP_CHECK(service_type); - for (const auto& interface : bound_interfaces_) { - mdns_responder_->StartPtrQuery(interface.socket, service_type.value()); - } -} - -void MdnsResponderService::StopListening() { - ErrorOr<DomainName> service_type = - DomainName::FromLabels(service_type_.begin(), service_type_.end()); - OSP_CHECK(service_type); - for (const auto& kv : network_scoped_domain_to_host_) { - const NetworkScopedDomainName& scoped_domain = kv.first; - - mdns_responder_->StopAQuery(scoped_domain.socket, - scoped_domain.domain_name); - mdns_responder_->StopAaaaQuery(scoped_domain.socket, - scoped_domain.domain_name); - } - network_scoped_domain_to_host_.clear(); - for (const auto& service : service_by_name_) { - UdpSocket* const socket = service.second->ptr_socket; - mdns_responder_->StopSrvQuery(socket, service.first); - mdns_responder_->StopTxtQuery(socket, service.first); - } - service_by_name_.clear(); - for (const auto& interface : bound_interfaces_) { - mdns_responder_->StopPtrQuery(interface.socket, service_type.value()); - } - RemoveAllReceivers(); -} - -void MdnsResponderService::StartService() { - // TODO(crbug.com/openscreen/45): This should really be a library-wide - // allowed list. - if (!bound_interfaces_.empty() && !interface_index_allowlist_.empty()) { - // TODO(btolsch): New interfaces won't be picked up on this path, but this - // also highlights a larger issue of the interface list being frozen while - // no state transitions are being made. There should be another interface - // on MdnsPlatformService for getting network interface updates. - std::vector<MdnsPlatformService::BoundInterface> deregistered_interfaces; - for (auto it = bound_interfaces_.begin(); it != bound_interfaces_.end();) { - if (std::find(interface_index_allowlist_.begin(), - interface_index_allowlist_.end(), - it->interface_info.index) == - interface_index_allowlist_.end()) { - mdns_responder_->DeregisterInterface(it->socket); - deregistered_interfaces.push_back(*it); - it = bound_interfaces_.erase(it); - } else { - ++it; - } - } - platform_->DeregisterInterfaces(deregistered_interfaces); - } else if (bound_interfaces_.empty()) { - mdns_responder_->Init(); - mdns_responder_->SetHostLabel(service_hostname_); - bound_interfaces_ = - platform_->RegisterInterfaces(interface_index_allowlist_); - for (auto& interface : bound_interfaces_) { - mdns_responder_->RegisterInterface(interface.interface_info, - interface.subnet, interface.socket); - } - } - - ErrorOr<DomainName> domain_name = - DomainName::FromLabels(&service_hostname_, &service_hostname_ + 1); - OSP_CHECK(domain_name) << "bad hostname configured: " << service_hostname_; - DomainName name = std::move(domain_name.value()); - - Error error = name.Append(DomainName::GetLocalDomain()); - OSP_CHECK(error.ok()); - - mdns_responder_->RegisterService(service_instance_name_, service_type_[0], - service_type_[1], name, service_port_, - service_txt_data_); -} - -void MdnsResponderService::StopService() { - mdns_responder_->DeregisterService(service_instance_name_, service_type_[0], - service_type_[1]); -} - -void MdnsResponderService::StopMdnsResponder() { - mdns_responder_->Close(); - platform_->DeregisterInterfaces(bound_interfaces_); - bound_interfaces_.clear(); - network_scoped_domain_to_host_.clear(); - service_by_name_.clear(); - RemoveAllReceivers(); -} - -void MdnsResponderService::UpdatePendingServiceInfoSet( - InstanceNameSet* modified_instance_names, - const DomainName& domain_name) { - for (auto& entry : service_by_name_) { - const auto& instance_name = entry.first; - const auto& instance = entry.second; - if (instance->domain_name == domain_name) { - modified_instance_names->emplace(instance_name); - } - } -} - -void MdnsResponderService::RemoveAllReceivers() { - bool had_receivers = !receiver_info_.empty(); - receiver_info_.clear(); - if (had_receivers) - listener_->OnAllReceiversRemoved(); -} - -bool MdnsResponderService::HandlePtrEvent( - const PtrEvent& ptr_event, - InstanceNameSet* modified_instance_names) { - bool events_possible = false; - const auto& instance_name = ptr_event.service_instance; - UdpSocket* const socket = ptr_event.header.socket; - auto entry = service_by_name_.find(ptr_event.service_instance); - switch (ptr_event.header.response_type) { - case QueryEventHeader::Type::kAddedNoCache: - break; - case QueryEventHeader::Type::kAdded: { - if (entry != service_by_name_.end()) { - entry->second->has_ptr_record = true; - modified_instance_names->emplace(instance_name); - break; - } - mdns_responder_->StartSrvQuery(socket, instance_name); - mdns_responder_->StartTxtQuery(socket, instance_name); - events_possible = true; - - auto new_instance = std::make_unique<ServiceInstance>(); - new_instance->ptr_socket = socket; - new_instance->has_ptr_record = true; - modified_instance_names->emplace(instance_name); - service_by_name_.emplace(std::move(instance_name), - std::move(new_instance)); - } break; - case QueryEventHeader::Type::kRemoved: - if (entry == service_by_name_.end()) - break; - if (entry->second->ptr_socket != socket) - break; - entry->second->has_ptr_record = false; - // NOTE: Occasionally, we can observe this situation in the wild where the - // PTR for a service is removed and then immediately re-added (like an odd - // refresh). Additionally, the recommended TTL of PTR records is much - // shorter than the other records. This means that short network drops or - // latency spikes could cause the PTR refresh queries and/or responses to - // be lost so the record isn't quite refreshed in time. The solution here - // and in HandleSrvEvent is to only remove the service records completely - // when both the PTR and SRV have been removed. - if (!entry->second->has_srv()) { - mdns_responder_->StopSrvQuery(socket, instance_name); - mdns_responder_->StopTxtQuery(socket, instance_name); - } - modified_instance_names->emplace(std::move(instance_name)); - break; - } - return events_possible; -} - -bool MdnsResponderService::HandleSrvEvent( - const SrvEvent& srv_event, - InstanceNameSet* modified_instance_names) { - bool events_possible = false; - auto& domain_name = srv_event.domain_name; - const auto& instance_name = srv_event.service_instance; - UdpSocket* const socket = srv_event.header.socket; - auto entry = service_by_name_.find(srv_event.service_instance); - if (entry == service_by_name_.end()) - return events_possible; - switch (srv_event.header.response_type) { - case QueryEventHeader::Type::kAddedNoCache: - break; - case QueryEventHeader::Type::kAdded: { - NetworkScopedDomainName scoped_domain_name{socket, domain_name}; - auto host_entry = network_scoped_domain_to_host_.find(scoped_domain_name); - if (host_entry == network_scoped_domain_to_host_.end()) { - mdns_responder_->StartAQuery(socket, domain_name); - mdns_responder_->StartAaaaQuery(socket, domain_name); - events_possible = true; - auto result = network_scoped_domain_to_host_.emplace( - std::move(scoped_domain_name), HostInfo{}); - host_entry = result.first; - } - auto& dependent_services = host_entry->second.services; - if (std::find_if(dependent_services.begin(), dependent_services.end(), - [entry](ServiceInstance* instance) { - return instance == entry->second.get(); - }) == dependent_services.end()) { - dependent_services.push_back(entry->second.get()); - } - entry->second->domain_name = std::move(domain_name); - entry->second->port = srv_event.port; - modified_instance_names->emplace(std::move(instance_name)); - } break; - case QueryEventHeader::Type::kRemoved: { - NetworkScopedDomainName scoped_domain_name{socket, domain_name}; - auto host_entry = network_scoped_domain_to_host_.find(scoped_domain_name); - if (host_entry != network_scoped_domain_to_host_.end()) { - auto& dependent_services = host_entry->second.services; - dependent_services.erase( - std::remove_if(dependent_services.begin(), dependent_services.end(), - [entry](ServiceInstance* instance) { - return instance == entry->second.get(); - }), - dependent_services.end()); - if (dependent_services.empty()) { - mdns_responder_->StopAQuery(socket, domain_name); - mdns_responder_->StopAaaaQuery(socket, domain_name); - network_scoped_domain_to_host_.erase(host_entry); - } - } - entry->second->domain_name = DomainName(); - entry->second->port = 0; - if (!entry->second->has_ptr_record) { - mdns_responder_->StopSrvQuery(socket, instance_name); - mdns_responder_->StopTxtQuery(socket, instance_name); - } - modified_instance_names->emplace(std::move(instance_name)); - } break; - } - return events_possible; -} - -bool MdnsResponderService::HandleTxtEvent( - const TxtEvent& txt_event, - InstanceNameSet* modified_instance_names) { - bool events_possible = false; - const auto& instance_name = txt_event.service_instance; - auto entry = service_by_name_.find(instance_name); - if (entry == service_by_name_.end()) - return events_possible; - switch (txt_event.header.response_type) { - case QueryEventHeader::Type::kAddedNoCache: - break; - case QueryEventHeader::Type::kAdded: - modified_instance_names->emplace(instance_name); - if (entry == service_by_name_.end()) { - auto result = service_by_name_.emplace( - std::move(instance_name), std::make_unique<ServiceInstance>()); - entry = result.first; - } - entry->second->txt_info = std::move(txt_event.txt_info); - break; - case QueryEventHeader::Type::kRemoved: - entry->second->txt_info.clear(); - modified_instance_names->emplace(std::move(instance_name)); - break; - } - return events_possible; -} - -bool MdnsResponderService::HandleAddressEvent( - UdpSocket* socket, - QueryEventHeader::Type response_type, - const DomainName& domain_name, - bool a_event, - const IPAddress& address, - InstanceNameSet* modified_instance_names) { - bool events_possible = false; - switch (response_type) { - case QueryEventHeader::Type::kAddedNoCache: - break; - case QueryEventHeader::Type::kAdded: { - HostInfo* host = AddOrGetHostInfo(socket, domain_name); - if (a_event) - host->v4_address = address; - else - host->v6_address = address; - UpdatePendingServiceInfoSet(modified_instance_names, domain_name); - } break; - case QueryEventHeader::Type::kRemoved: { - HostInfo* host = GetHostInfo(socket, domain_name); - - if (a_event) - host->v4_address = IPAddress(); - else - host->v6_address = IPAddress(); - - if (host->v4_address || host->v6_address) - UpdatePendingServiceInfoSet(modified_instance_names, domain_name); - } break; - } - return events_possible; -} - -bool MdnsResponderService::HandleAEvent( - const AEvent& a_event, - InstanceNameSet* modified_instance_names) { - return HandleAddressEvent(a_event.header.socket, a_event.header.response_type, - a_event.domain_name, true, a_event.address, - modified_instance_names); -} - -bool MdnsResponderService::HandleAaaaEvent( - const AaaaEvent& aaaa_event, - InstanceNameSet* modified_instance_names) { - return HandleAddressEvent(aaaa_event.header.socket, - aaaa_event.header.response_type, - aaaa_event.domain_name, false, aaaa_event.address, - modified_instance_names); -} - -MdnsResponderService::HostInfo* MdnsResponderService::AddOrGetHostInfo( - UdpSocket* socket, - const DomainName& domain_name) { - return &network_scoped_domain_to_host_[NetworkScopedDomainName{socket, - domain_name}]; -} - -MdnsResponderService::HostInfo* MdnsResponderService::GetHostInfo( - UdpSocket* socket, - const DomainName& domain_name) { - auto kv = network_scoped_domain_to_host_.find( - NetworkScopedDomainName{socket, domain_name}); - if (kv == network_scoped_domain_to_host_.end()) - return nullptr; - - return &kv->second; -} - -bool MdnsResponderService::IsServiceReady(const ServiceInstance& instance, - HostInfo* host) const { - return (host && instance.has_ptr_record && instance.has_srv() && - !instance.txt_info.empty() && (host->v4_address || host->v6_address)); -} - -NetworkInterfaceIndex MdnsResponderService::GetNetworkInterfaceIndexFromSocket( - const UdpSocket* socket) const { - auto it = std::find_if( - bound_interfaces_.begin(), bound_interfaces_.end(), - [socket](const MdnsPlatformService::BoundInterface& interface) { - return interface.socket == socket; - }); - if (it == bound_interfaces_.end()) - return kInvalidNetworkInterfaceIndex; - return it->interface_info.index; -} - -void MdnsResponderService::RunBackgroundTasks() { - if (!mdns_responder_) { - return; - } - const auto delay_until_next_run = mdns_responder_->RunTasks(); - background_tasks_alarm_.ScheduleFromNow([this] { RunBackgroundTasks(); }, - delay_until_next_run); -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/mdns_responder_service.h b/osp/impl/mdns_responder_service.h deleted file mode 100644 index ddcd0dbd..00000000 --- a/osp/impl/mdns_responder_service.h +++ /dev/null @@ -1,209 +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. - -#ifndef OSP_IMPL_MDNS_RESPONDER_SERVICE_H_ -#define OSP_IMPL_MDNS_RESPONDER_SERVICE_H_ - -#include <array> -#include <map> -#include <memory> -#include <set> -#include <string> -#include <vector> - -#include "osp/impl/discovery/mdns/mdns_responder_adapter.h" -#include "osp/impl/mdns_platform_service.h" -#include "osp/impl/service_listener_impl.h" -#include "osp/impl/service_publisher_impl.h" -#include "platform/api/network_interface.h" -#include "platform/api/task_runner.h" -#include "platform/api/time.h" -#include "platform/base/ip_address.h" -#include "util/alarm.h" - -namespace openscreen { -namespace osp { - -class MdnsResponderAdapterFactory { - public: - virtual ~MdnsResponderAdapterFactory() = default; - - virtual std::unique_ptr<MdnsResponderAdapter> Create() = 0; -}; - -class MdnsResponderService : public ServiceListenerImpl::Delegate, - public ServicePublisherImpl::Delegate, - public UdpSocket::Client { - public: - MdnsResponderService( - ClockNowFunctionPtr now_function, - TaskRunner* task_runner, - const std::string& service_name, - const std::string& service_protocol, - std::unique_ptr<MdnsResponderAdapterFactory> mdns_responder_factory, - std::unique_ptr<MdnsPlatformService> platform); - ~MdnsResponderService() override; - - void SetServiceConfig(const std::string& hostname, - const std::string& instance, - uint16_t port, - const std::vector<NetworkInterfaceIndex> allowlist, - const std::map<std::string, std::string>& txt_data); - - // UdpSocket::Client overrides. - void OnRead(UdpSocket* socket, ErrorOr<UdpPacket> packet) override; - void OnSendError(UdpSocket* socket, Error error) override; - void OnError(UdpSocket* socket, Error error) override; - - // ServiceListenerImpl::Delegate overrides. - void StartListener() override; - void StartAndSuspendListener() override; - void StopListener() override; - void SuspendListener() override; - void ResumeListener() override; - void SearchNow(ServiceListener::State from) override; - - // ServicePublisherImpl::Delegate overrides. - void StartPublisher() override; - void StartAndSuspendPublisher() override; - void StopPublisher() override; - void SuspendPublisher() override; - void ResumePublisher() override; - - protected: - void HandleMdnsEvents(); - - std::unique_ptr<MdnsResponderAdapter> mdns_responder_; - - private: - // Create internal versions of all public methods. These are used to push all - // calls to these methods to the task runner. - // TODO(rwkeane): Clean up these methods. Some result in multiple pushes to - // the task runner when just one would suffice. - // ServiceListenerImpl::Delegate overrides. - void StartListenerInternal(); - void StartAndSuspendListenerInternal(); - void StopListenerInternal(); - void SuspendListenerInternal(); - void ResumeListenerInternal(); - void SearchNowInternal(ServiceListener::State from); - void StartPublisherInternal(); - void StartAndSuspendPublisherInternal(); - void StopPublisherInternal(); - void SuspendPublisherInternal(); - void ResumePublisherInternal(); - - // NOTE: service_instance implicit in map key. - struct ServiceInstance { - UdpSocket* ptr_socket = nullptr; - DomainName domain_name; - uint16_t port = 0; - bool has_ptr_record = false; - std::vector<std::string> txt_info; - - // |port| == 0 signals that we have no SRV record. - bool has_srv() const { return port != 0; } - }; - - // NOTE: hostname implicit in map key. - struct HostInfo { - std::vector<ServiceInstance*> services; - IPAddress v4_address; - IPAddress v6_address; - }; - - struct NetworkScopedDomainName { - UdpSocket* socket; - DomainName domain_name; - }; - - struct NetworkScopedDomainNameComparator { - bool operator()(const NetworkScopedDomainName& a, - const NetworkScopedDomainName& b) const; - }; - - using InstanceNameSet = std::set<DomainName, DomainNameComparator>; - - void StartListening(); - void StopListening(); - void StartService(); - void StopService(); - void StopMdnsResponder(); - void UpdatePendingServiceInfoSet(InstanceNameSet* modified_instance_names, - const DomainName& domain_name); - void RemoveAllReceivers(); - - // NOTE: |modified_instance_names| is used to track which service instances - // are modified by the record events. See HandleMdnsEvents for more details. - bool HandlePtrEvent(const PtrEvent& ptr_event, - InstanceNameSet* modified_instance_names); - bool HandleSrvEvent(const SrvEvent& srv_event, - InstanceNameSet* modified_instance_names); - bool HandleTxtEvent(const TxtEvent& txt_event, - InstanceNameSet* modified_instance_names); - bool HandleAddressEvent(UdpSocket* socket, - QueryEventHeader::Type response_type, - const DomainName& domain_name, - bool a_event, - const IPAddress& address, - InstanceNameSet* modified_instance_names); - bool HandleAEvent(const AEvent& a_event, - InstanceNameSet* modified_instance_names); - bool HandleAaaaEvent(const AaaaEvent& aaaa_event, - InstanceNameSet* modified_instance_names); - - HostInfo* AddOrGetHostInfo(UdpSocket* socket, const DomainName& domain_name); - HostInfo* GetHostInfo(UdpSocket* socket, const DomainName& domain_name); - bool IsServiceReady(const ServiceInstance& instance, HostInfo* host) const; - NetworkInterfaceIndex GetNetworkInterfaceIndexFromSocket( - const UdpSocket* socket) const; - - // Runs background tasks to manage the internal mDNS state. - void RunBackgroundTasks(); - - // Service type separated as service name and service protocol for both - // listening and publishing (e.g. {"_openscreen", "_udp"}). - std::array<std::string, 2> service_type_; - - // The following variables all relate to what MdnsResponderService publishes, - // if anything. - std::string service_hostname_; - std::string service_instance_name_; - uint16_t service_port_; - std::vector<NetworkInterfaceIndex> interface_index_allowlist_; - std::map<std::string, std::string> service_txt_data_; - - std::unique_ptr<MdnsResponderAdapterFactory> mdns_responder_factory_; - std::unique_ptr<MdnsPlatformService> platform_; - std::vector<MdnsPlatformService::BoundInterface> bound_interfaces_; - - // A map of service information collected from PTR, SRV, and TXT records. It - // is keyed by service instance names. - std::map<DomainName, std::unique_ptr<ServiceInstance>, DomainNameComparator> - service_by_name_; - - // The map key is a combination of the interface to which the address records - // belong and the hostname of the address records. The values are IPAddresses - // for the given hostname on the given network and pointers to dependent - // service instances. The service instance pointers act as a reference count - // to keep the A/AAAA queries alive, when more than one service refers to the - // same hostname. This is not currently used by openscreen, but is used by - // Cast, so may be supported in openscreen in the future. - std::map<NetworkScopedDomainName, HostInfo, NetworkScopedDomainNameComparator> - network_scoped_domain_to_host_; - - std::map<std::string, ServiceInfo> receiver_info_; - - TaskRunner* const task_runner_; - - // Scheduled to run periodic background tasks. - Alarm background_tasks_alarm_; - - friend class TestingMdnsResponderService; -}; - -} // namespace osp -} // namespace openscreen - -#endif // OSP_IMPL_MDNS_RESPONDER_SERVICE_H_ diff --git a/osp/impl/mdns_responder_service_unittest.cc b/osp/impl/mdns_responder_service_unittest.cc deleted file mode 100644 index 1d542a3e..00000000 --- a/osp/impl/mdns_responder_service_unittest.cc +++ /dev/null @@ -1,884 +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/mdns_responder_service.h" - -#include <cstdint> -#include <iostream> -#include <memory> -#include <utility> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "osp/impl/service_listener_impl.h" -#include "osp/impl/testing/fake_mdns_platform_service.h" -#include "osp/impl/testing/fake_mdns_responder_adapter.h" -#include "platform/test/fake_task_runner.h" - -namespace openscreen { -namespace osp { - -// Child of the MdnsResponderService for testing purposes. Only difference -// betweeen this and the base class is that methods on this class are executed -// synchronously, rather than pushed to the task runner for later execution. -class TestingMdnsResponderService final : public MdnsResponderService { - public: - TestingMdnsResponderService( - FakeTaskRunner* task_runner, - const std::string& service_name, - const std::string& service_protocol, - std::unique_ptr<MdnsResponderAdapterFactory> mdns_responder_factory, - std::unique_ptr<MdnsPlatformService> platform_service) - : MdnsResponderService(&FakeClock::now, - task_runner, - service_name, - service_protocol, - std::move(mdns_responder_factory), - std::move(platform_service)) {} - ~TestingMdnsResponderService() = default; - - // Override the default ServiceListenerImpl and ServicePublisherImpl - // implementations. These call the internal implementations of each of the - // methods provided, meaning that the end result of the call is the same, but - // without pushing to the task runner and waiting for it to be pulled off - // again. - // ServiceListenerImpl::Delegate overrides. - void StartListener() override { StartListenerInternal(); } - void StartAndSuspendListener() override { StartAndSuspendListenerInternal(); } - void StopListener() override { StopListenerInternal(); } - void SuspendListener() override { SuspendListenerInternal(); } - void ResumeListener() override { ResumeListenerInternal(); } - void SearchNow(ServiceListener::State from) override { - SearchNowInternal(from); - } - - // ServicePublisherImpl::Delegate overrides. - void StartPublisher() override { StartPublisherInternal(); } - void StartAndSuspendPublisher() override { - StartAndSuspendPublisherInternal(); - } - void StopPublisher() override { StopPublisherInternal(); } - void SuspendPublisher() override { SuspendPublisherInternal(); } - void ResumePublisher() override { ResumePublisherInternal(); } - - // Handles new events as OnRead does, but without the need of a TaskRunner. - void HandleNewEvents() { - if (!mdns_responder_) { - return; - } - - mdns_responder_->RunTasks(); - HandleMdnsEvents(); - } -}; - -class FakeMdnsResponderAdapterFactory final - : public MdnsResponderAdapterFactory, - public FakeMdnsResponderAdapter::LifetimeObserver { - public: - ~FakeMdnsResponderAdapterFactory() override = default; - - std::unique_ptr<MdnsResponderAdapter> Create() override { - auto mdns = std::make_unique<FakeMdnsResponderAdapter>(); - mdns->SetLifetimeObserver(this); - last_mdns_responder_ = mdns.get(); - ++instances_; - return mdns; - } - - void OnDestroyed() override { - last_running_ = last_mdns_responder_->running(); - last_registered_services_size_ = - last_mdns_responder_->registered_services().size(); - last_mdns_responder_ = nullptr; - } - - FakeMdnsResponderAdapter* last_mdns_responder() { - return last_mdns_responder_; - } - - int32_t instances() const { return instances_; } - bool last_running() const { return last_running_; } - size_t last_registered_services_size() const { - return last_registered_services_size_; - } - - private: - FakeMdnsResponderAdapter* last_mdns_responder_ = nullptr; - int32_t instances_ = 0; - bool last_running_ = false; - size_t last_registered_services_size_ = 0; -}; - -namespace { - -using ::testing::_; - -constexpr char kTestServiceInstance[] = "turtle"; -constexpr char kTestServiceName[] = "_foo"; -constexpr char kTestServiceProtocol[] = "_udp"; -constexpr char kTestHostname[] = "hostname"; -constexpr uint16_t kTestPort = 12345; - -// Wrapper around the above class. In MdnsResponderServiceTest, we need to both -// pass a unique_ptr to the created MdnsResponderService and to maintain a -// local pointer as well. Doing this with the same object causes a race -// condition, where ~FakeMdnsResponderAdapter() calls observer_->OnDestroyed() -// after the object is already deleted, resulting in a seg fault. This is to -// prevent that race condition. -class WrapperMdnsResponderAdapterFactory final - : public MdnsResponderAdapterFactory, - public FakeMdnsResponderAdapter::LifetimeObserver { - public: - explicit WrapperMdnsResponderAdapterFactory( - FakeMdnsResponderAdapterFactory* ptr) - : other_(ptr) {} - - std::unique_ptr<MdnsResponderAdapter> Create() override { - return other_->Create(); - } - - void OnDestroyed() override { other_->OnDestroyed(); } - - private: - FakeMdnsResponderAdapterFactory* other_; -}; - -class MockServiceListenerObserver final : public ServiceListener::Observer { - public: - ~MockServiceListenerObserver() override = default; - - MOCK_METHOD0(OnStarted, void()); - MOCK_METHOD0(OnStopped, void()); - MOCK_METHOD0(OnSuspended, void()); - MOCK_METHOD0(OnSearching, void()); - - MOCK_METHOD1(OnReceiverAdded, void(const ServiceInfo&)); - MOCK_METHOD1(OnReceiverChanged, void(const ServiceInfo&)); - MOCK_METHOD1(OnReceiverRemoved, void(const ServiceInfo&)); - MOCK_METHOD0(OnAllReceiversRemoved, void()); - - MOCK_METHOD1(OnError, void(ServiceListenerError)); - MOCK_METHOD1(OnMetrics, void(ServiceListener::Metrics)); -}; - -class MockServicePublisherObserver final : public ServicePublisher::Observer { - public: - ~MockServicePublisherObserver() override = default; - - MOCK_METHOD0(OnStarted, void()); - MOCK_METHOD0(OnStopped, void()); - MOCK_METHOD0(OnSuspended, void()); - MOCK_METHOD1(OnError, void(ServicePublisherError)); - MOCK_METHOD1(OnMetrics, void(ServicePublisher::Metrics)); -}; - -UdpSocket* const kDefaultSocket = - reinterpret_cast<UdpSocket*>(static_cast<uintptr_t>(16)); -UdpSocket* const kSecondSocket = - reinterpret_cast<UdpSocket*>(static_cast<uintptr_t>(24)); - -class MdnsResponderServiceTest : public ::testing::Test { - protected: - void SetUp() override { - mdns_responder_factory_ = - std::make_unique<FakeMdnsResponderAdapterFactory>(); - auto wrapper_factory = std::make_unique<WrapperMdnsResponderAdapterFactory>( - mdns_responder_factory_.get()); - clock_ = std::make_unique<FakeClock>(Clock::now()); - task_runner_ = std::make_unique<FakeTaskRunner>(clock_.get()); - auto platform_service = std::make_unique<FakeMdnsPlatformService>(); - fake_platform_service_ = platform_service.get(); - fake_platform_service_->set_interfaces(bound_interfaces_); - mdns_service_ = std::make_unique<TestingMdnsResponderService>( - task_runner_.get(), kTestServiceName, kTestServiceProtocol, - std::move(wrapper_factory), std::move(platform_service)); - service_listener_ = - std::make_unique<ServiceListenerImpl>(mdns_service_.get()); - service_listener_->AddObserver(&observer_); - - mdns_service_->SetServiceConfig(kTestHostname, kTestServiceInstance, - kTestPort, {}, {{"model", "shifty"}}); - service_publisher_ = std::make_unique<ServicePublisherImpl>( - &publisher_observer_, mdns_service_.get()); - } - - std::unique_ptr<FakeClock> clock_; - std::unique_ptr<FakeTaskRunner> task_runner_; - MockServiceListenerObserver observer_; - FakeMdnsPlatformService* fake_platform_service_; - std::unique_ptr<FakeMdnsResponderAdapterFactory> mdns_responder_factory_; - std::unique_ptr<TestingMdnsResponderService> mdns_service_; - std::unique_ptr<ServiceListenerImpl> service_listener_; - MockServicePublisherObserver publisher_observer_; - std::unique_ptr<ServicePublisherImpl> service_publisher_; - const uint8_t default_mac_[6] = {0, 11, 22, 33, 44, 55}; - const uint8_t second_mac_[6] = {55, 33, 22, 33, 44, 77}; - const IPSubnet default_subnet_{IPAddress{192, 168, 3, 2}, 24}; - const IPSubnet second_subnet_{IPAddress{10, 0, 0, 3}, 24}; - std::vector<MdnsPlatformService::BoundInterface> bound_interfaces_{ - MdnsPlatformService::BoundInterface{ - InterfaceInfo{1, - default_mac_, - "eth0", - InterfaceInfo::Type::kEthernet, - {default_subnet_}}, - default_subnet_, kDefaultSocket}, - MdnsPlatformService::BoundInterface{ - InterfaceInfo{2, - second_mac_, - "eth1", - InterfaceInfo::Type::kEthernet, - {second_subnet_}}, - second_subnet_, kSecondSocket}, - }; -}; - -} // namespace - -TEST_F(MdnsResponderServiceTest, BasicServiceStates) { - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - - AddEventsForNewService(mdns_responder, kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", kTestPort, - {"model=shifty", "id=asdf"}, IPAddress{192, 168, 3, 7}, - kDefaultSocket); - - std::string service_id; - EXPECT_CALL(observer_, OnReceiverAdded(_)) - .WillOnce(::testing::Invoke([&service_id](const ServiceInfo& info) { - service_id = info.service_id; - EXPECT_EQ(kTestServiceInstance, info.friendly_name); - EXPECT_EQ((IPEndpoint{{192, 168, 3, 7}, kTestPort}), info.v4_endpoint); - EXPECT_FALSE(info.v6_endpoint.address); - })); - mdns_service_->HandleNewEvents(); - - mdns_responder->AddAEvent(MakeAEvent( - "gigliorononomicon", IPAddress{192, 168, 3, 8}, kDefaultSocket)); - - EXPECT_CALL(observer_, OnReceiverChanged(_)) - .WillOnce(::testing::Invoke([&service_id](const ServiceInfo& info) { - EXPECT_EQ(service_id, info.service_id); - EXPECT_EQ(kTestServiceInstance, info.friendly_name); - EXPECT_EQ((IPEndpoint{{192, 168, 3, 8}, kTestPort}), info.v4_endpoint); - EXPECT_FALSE(info.v6_endpoint.address); - })); - mdns_service_->HandleNewEvents(); - - auto ptr_remove = MakePtrEvent(kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, kDefaultSocket); - ptr_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddPtrEvent(std::move(ptr_remove)); - - EXPECT_CALL(observer_, OnReceiverRemoved(_)) - .WillOnce(::testing::Invoke([&service_id](const ServiceInfo& info) { - EXPECT_EQ(service_id, info.service_id); - })); - mdns_service_->HandleNewEvents(); -} - -TEST_F(MdnsResponderServiceTest, NetworkNetworkInterfaceIndex) { - constexpr uint8_t mac[6] = {12, 34, 56, 78, 90}; - const IPSubnet subnet{IPAddress{10, 0, 0, 2}, 24}; - bound_interfaces_.emplace_back( - InterfaceInfo{2, mac, "wlan0", InterfaceInfo::Type::kWifi, {subnet}}, - subnet, kSecondSocket); - fake_platform_service_->set_interfaces(bound_interfaces_); - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - - AddEventsForNewService(mdns_responder, kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", kTestPort, - {"model=shifty", "id=asdf"}, IPAddress{192, 168, 3, 7}, - kSecondSocket); - - EXPECT_CALL(observer_, OnReceiverAdded(_)) - .WillOnce(::testing::Invoke([](const ServiceInfo& info) { - EXPECT_EQ(2, info.network_interface_index); - })); - mdns_service_->HandleNewEvents(); -} - -TEST_F(MdnsResponderServiceTest, SimultaneousFieldChanges) { - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - - AddEventsForNewService(mdns_responder, kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", kTestPort, - {"model=shifty", "id=asdf"}, IPAddress{192, 168, 3, 7}, - kDefaultSocket); - - EXPECT_CALL(observer_, OnReceiverAdded(_)); - mdns_service_->HandleNewEvents(); - - mdns_responder->AddSrvEvent( - MakeSrvEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - "gigliorononomicon", 54321, kDefaultSocket)); - auto a_remove = MakeAEvent("gigliorononomicon", IPAddress{192, 168, 3, 7}, - kDefaultSocket); - a_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddAEvent(std::move(a_remove)); - mdns_responder->AddAEvent(MakeAEvent( - "gigliorononomicon", IPAddress{192, 168, 3, 8}, kDefaultSocket)); - - EXPECT_CALL(observer_, OnReceiverChanged(_)) - .WillOnce(::testing::Invoke([](const ServiceInfo& info) { - EXPECT_EQ((IPAddress{192, 168, 3, 8}), info.v4_endpoint.address); - EXPECT_EQ(54321, info.v4_endpoint.port); - EXPECT_FALSE(info.v6_endpoint.address); - })); - mdns_service_->HandleNewEvents(); -} - -TEST_F(MdnsResponderServiceTest, SimultaneousHostAndAddressChange) { - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - - AddEventsForNewService(mdns_responder, kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", kTestPort, - {"model=shifty", "id=asdf"}, IPAddress{192, 168, 3, 7}, - kDefaultSocket); - - EXPECT_CALL(observer_, OnReceiverAdded(_)); - mdns_service_->HandleNewEvents(); - - auto srv_remove = - MakeSrvEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - "gigliorononomicon", kTestPort, kDefaultSocket); - srv_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddSrvEvent(std::move(srv_remove)); - mdns_responder->AddSrvEvent( - MakeSrvEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - "alpha", kTestPort, kDefaultSocket)); - mdns_responder->AddAEvent(MakeAEvent( - "gigliorononomicon", IPAddress{192, 168, 3, 8}, kDefaultSocket)); - mdns_responder->AddAEvent( - MakeAEvent("alpha", IPAddress{192, 168, 3, 10}, kDefaultSocket)); - - EXPECT_CALL(observer_, OnReceiverChanged(_)) - .WillOnce(::testing::Invoke([](const ServiceInfo& info) { - EXPECT_EQ((IPAddress{192, 168, 3, 10}), info.v4_endpoint.address); - EXPECT_FALSE(info.v6_endpoint.address); - })); - mdns_service_->HandleNewEvents(); -} - -TEST_F(MdnsResponderServiceTest, ListenerStateTransitions) { - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - - EXPECT_CALL(observer_, OnSuspended()); - service_listener_->Suspend(); - ASSERT_EQ(mdns_responder, mdns_responder_factory_->last_mdns_responder()); - EXPECT_FALSE(mdns_responder->running()); - - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Resume(); - ASSERT_EQ(mdns_responder, mdns_responder_factory_->last_mdns_responder()); - EXPECT_TRUE(mdns_responder->running()); - - EXPECT_CALL(observer_, OnStopped()); - service_listener_->Stop(); - ASSERT_FALSE(mdns_responder_factory_->last_mdns_responder()); - - EXPECT_CALL(observer_, OnSuspended()); - auto instances = mdns_responder_factory_->instances(); - service_listener_->StartAndSuspend(); - EXPECT_EQ(instances + 1, mdns_responder_factory_->instances()); - mdns_responder = mdns_responder_factory_->last_mdns_responder(); - EXPECT_FALSE(mdns_responder->running()); - - EXPECT_CALL(observer_, OnStopped()); - service_listener_->Stop(); - ASSERT_FALSE(mdns_responder_factory_->last_mdns_responder()); -} - -TEST_F(MdnsResponderServiceTest, BasicServicePublish) { - EXPECT_CALL(publisher_observer_, OnStarted()); - service_publisher_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - - const auto& services = mdns_responder->registered_services(); - ASSERT_EQ(1u, services.size()); - EXPECT_EQ(kTestServiceInstance, services[0].service_instance); - EXPECT_EQ(kTestServiceName, services[0].service_name); - EXPECT_EQ(kTestServiceProtocol, services[0].service_protocol); - auto host_labels = services[0].target_host.GetLabels(); - ASSERT_EQ(2u, host_labels.size()); - EXPECT_EQ(kTestHostname, host_labels[0]); - EXPECT_EQ("local", host_labels[1]); - EXPECT_EQ(kTestPort, services[0].target_port); - - EXPECT_CALL(publisher_observer_, OnStopped()); - service_publisher_->Stop(); - - EXPECT_FALSE(mdns_responder_factory_->last_mdns_responder()); - EXPECT_EQ(0u, mdns_responder_factory_->last_registered_services_size()); -} - -TEST_F(MdnsResponderServiceTest, PublisherStateTransitions) { - EXPECT_CALL(publisher_observer_, OnStarted()); - service_publisher_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - EXPECT_EQ(1u, mdns_responder->registered_services().size()); - - EXPECT_CALL(publisher_observer_, OnSuspended()); - service_publisher_->Suspend(); - EXPECT_EQ(0u, mdns_responder->registered_services().size()); - - EXPECT_CALL(publisher_observer_, OnStarted()); - service_publisher_->Resume(); - EXPECT_EQ(1u, mdns_responder->registered_services().size()); - - EXPECT_CALL(publisher_observer_, OnStopped()); - service_publisher_->Stop(); - EXPECT_EQ(0u, mdns_responder_factory_->last_registered_services_size()); - - EXPECT_CALL(publisher_observer_, OnStarted()); - service_publisher_->Start(); - mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - EXPECT_EQ(1u, mdns_responder->registered_services().size()); - EXPECT_CALL(publisher_observer_, OnSuspended()); - service_publisher_->Suspend(); - EXPECT_EQ(0u, mdns_responder->registered_services().size()); - EXPECT_CALL(publisher_observer_, OnStopped()); - service_publisher_->Stop(); - EXPECT_FALSE(mdns_responder_factory_->last_mdns_responder()); - EXPECT_EQ(0u, mdns_responder_factory_->last_registered_services_size()); -} - -TEST_F(MdnsResponderServiceTest, PublisherObeysInterfaceAllowlist) { - { - mdns_service_->SetServiceConfig(kTestHostname, kTestServiceInstance, - kTestPort, {}, {{"model", "shifty"}}); - - EXPECT_CALL(publisher_observer_, OnStarted()); - service_publisher_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - auto interfaces = mdns_responder->registered_interfaces(); - ASSERT_EQ(2u, interfaces.size()); - EXPECT_EQ(kDefaultSocket, interfaces[0].socket); - EXPECT_EQ(kSecondSocket, interfaces[1].socket); - - EXPECT_CALL(publisher_observer_, OnStopped()); - service_publisher_->Stop(); - } - { - mdns_service_->SetServiceConfig(kTestHostname, kTestServiceInstance, - kTestPort, {1, 2}, {{"model", "shifty"}}); - - EXPECT_CALL(publisher_observer_, OnStarted()); - service_publisher_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - auto interfaces = mdns_responder->registered_interfaces(); - ASSERT_EQ(2u, interfaces.size()); - EXPECT_EQ(kDefaultSocket, interfaces[0].socket); - EXPECT_EQ(kSecondSocket, interfaces[1].socket); - - EXPECT_CALL(publisher_observer_, OnStopped()); - service_publisher_->Stop(); - } - { - mdns_service_->SetServiceConfig(kTestHostname, kTestServiceInstance, - kTestPort, {2}, {{"model", "shifty"}}); - - EXPECT_CALL(publisher_observer_, OnStarted()); - service_publisher_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - auto interfaces = mdns_responder->registered_interfaces(); - ASSERT_EQ(1u, interfaces.size()); - EXPECT_EQ(kSecondSocket, interfaces[0].socket); - - EXPECT_CALL(publisher_observer_, OnStopped()); - service_publisher_->Stop(); - } -} - -TEST_F(MdnsResponderServiceTest, ListenAndPublish) { - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - - { - auto interfaces = mdns_responder->registered_interfaces(); - ASSERT_EQ(2u, interfaces.size()); - EXPECT_EQ(kDefaultSocket, interfaces[0].socket); - EXPECT_EQ(kSecondSocket, interfaces[1].socket); - } - - mdns_service_->SetServiceConfig(kTestHostname, kTestServiceInstance, - kTestPort, {2}, {{"model", "shifty"}}); - - auto instances = mdns_responder_factory_->instances(); - EXPECT_CALL(publisher_observer_, OnStarted()); - service_publisher_->Start(); - - EXPECT_EQ(instances, mdns_responder_factory_->instances()); - ASSERT_TRUE(mdns_responder->running()); - { - auto interfaces = mdns_responder->registered_interfaces(); - ASSERT_EQ(1u, interfaces.size()); - EXPECT_EQ(kSecondSocket, interfaces[0].socket); - } - - EXPECT_CALL(observer_, OnStopped()); - service_listener_->Stop(); - ASSERT_TRUE(mdns_responder->running()); - EXPECT_EQ(1u, mdns_responder->registered_interfaces().size()); - - EXPECT_CALL(publisher_observer_, OnStopped()); - service_publisher_->Stop(); - EXPECT_FALSE(mdns_responder_factory_->last_mdns_responder()); - EXPECT_EQ(0u, mdns_responder_factory_->last_registered_services_size()); -} - -TEST_F(MdnsResponderServiceTest, PublishAndListen) { - mdns_service_->SetServiceConfig(kTestHostname, kTestServiceInstance, - kTestPort, {2}, {{"model", "shifty"}}); - - EXPECT_CALL(publisher_observer_, OnStarted()); - service_publisher_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - { - auto interfaces = mdns_responder->registered_interfaces(); - ASSERT_EQ(1u, interfaces.size()); - EXPECT_EQ(kSecondSocket, interfaces[0].socket); - } - - auto instances = mdns_responder_factory_->instances(); - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - EXPECT_EQ(instances, mdns_responder_factory_->instances()); - ASSERT_TRUE(mdns_responder->running()); - { - auto interfaces = mdns_responder->registered_interfaces(); - ASSERT_EQ(1u, interfaces.size()); - EXPECT_EQ(kSecondSocket, interfaces[0].socket); - } - - EXPECT_CALL(publisher_observer_, OnStopped()); - service_publisher_->Stop(); - ASSERT_TRUE(mdns_responder->running()); - EXPECT_EQ(1u, mdns_responder->registered_interfaces().size()); - - EXPECT_CALL(observer_, OnStopped()); - service_listener_->Stop(); - EXPECT_FALSE(mdns_responder_factory_->last_mdns_responder()); - EXPECT_EQ(0u, mdns_responder_factory_->last_registered_services_size()); -} - -TEST_F(MdnsResponderServiceTest, AddressQueryStopped) { - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - - AddEventsForNewService(mdns_responder, kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", kTestPort, - {"model=shifty", "id=asdf"}, IPAddress{192, 168, 3, 7}, - kDefaultSocket); - - EXPECT_CALL(observer_, OnReceiverAdded(_)); - mdns_service_->HandleNewEvents(); - - auto srv_remove = - MakeSrvEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - "gigliorononomicon", kTestPort, kDefaultSocket); - srv_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddSrvEvent(std::move(srv_remove)); - - EXPECT_CALL(observer_, OnReceiverRemoved(_)); - mdns_service_->HandleNewEvents(); - - EXPECT_FALSE(mdns_responder->ptr_queries_empty()); - EXPECT_FALSE(mdns_responder->srv_queries_empty()); - EXPECT_FALSE(mdns_responder->txt_queries_empty()); - EXPECT_TRUE(mdns_responder->a_queries_empty()); - EXPECT_TRUE(mdns_responder->aaaa_queries_empty()); -} - -TEST_F(MdnsResponderServiceTest, AddressQueryRefCount) { - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - - AddEventsForNewService(mdns_responder, kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", kTestPort, - {"model=shifty", "id=asdf"}, IPAddress{192, 168, 3, 7}, - kDefaultSocket); - AddEventsForNewService(mdns_responder, "instance-2", kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", 4321, - {"model=shwofty", "id=asdf"}, - IPAddress{192, 168, 3, 7}, kDefaultSocket); - - EXPECT_CALL(observer_, OnReceiverAdded(_)).Times(2); - mdns_service_->HandleNewEvents(); - - auto srv_remove = - MakeSrvEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - "gigliorononomicon", kTestPort, kDefaultSocket); - srv_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddSrvEvent(std::move(srv_remove)); - - EXPECT_CALL(observer_, OnReceiverRemoved(_)); - mdns_service_->HandleNewEvents(); - - EXPECT_FALSE(mdns_responder->ptr_queries_empty()); - EXPECT_FALSE(mdns_responder->srv_queries_empty()); - EXPECT_FALSE(mdns_responder->txt_queries_empty()); - EXPECT_FALSE(mdns_responder->a_queries_empty()); - EXPECT_FALSE(mdns_responder->aaaa_queries_empty()); - - srv_remove = - MakeSrvEvent("instance-2", kTestServiceName, kTestServiceProtocol, - "gigliorononomicon", 4321, kDefaultSocket); - srv_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddSrvEvent(std::move(srv_remove)); - - EXPECT_CALL(observer_, OnReceiverRemoved(_)); - mdns_service_->HandleNewEvents(); - - EXPECT_FALSE(mdns_responder->ptr_queries_empty()); - EXPECT_FALSE(mdns_responder->srv_queries_empty()); - EXPECT_FALSE(mdns_responder->txt_queries_empty()); - EXPECT_TRUE(mdns_responder->a_queries_empty()); - EXPECT_TRUE(mdns_responder->aaaa_queries_empty()); -} - -TEST_F(MdnsResponderServiceTest, ServiceQueriesStoppedSrvFirst) { - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - - AddEventsForNewService(mdns_responder, kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", kTestPort, - {"model=shifty", "id=asdf"}, IPAddress{192, 168, 3, 7}, - kDefaultSocket); - - EXPECT_CALL(observer_, OnReceiverAdded(_)); - mdns_service_->HandleNewEvents(); - - auto srv_remove = - MakeSrvEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - "gigliorononomicon", kTestPort, kDefaultSocket); - srv_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddSrvEvent(std::move(srv_remove)); - - EXPECT_CALL(observer_, OnReceiverRemoved(_)); - mdns_service_->HandleNewEvents(); - - EXPECT_FALSE(mdns_responder->ptr_queries_empty()); - EXPECT_FALSE(mdns_responder->srv_queries_empty()); - EXPECT_FALSE(mdns_responder->txt_queries_empty()); - EXPECT_TRUE(mdns_responder->a_queries_empty()); - EXPECT_TRUE(mdns_responder->aaaa_queries_empty()); - - auto ptr_remove = MakePtrEvent(kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, kDefaultSocket); - ptr_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddPtrEvent(std::move(ptr_remove)); - mdns_service_->HandleNewEvents(); - - EXPECT_FALSE(mdns_responder->ptr_queries_empty()); - EXPECT_TRUE(mdns_responder->srv_queries_empty()); - EXPECT_TRUE(mdns_responder->txt_queries_empty()); - EXPECT_TRUE(mdns_responder->a_queries_empty()); - EXPECT_TRUE(mdns_responder->aaaa_queries_empty()); -} - -TEST_F(MdnsResponderServiceTest, ServiceQueriesStoppedPtrFirst) { - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - - AddEventsForNewService(mdns_responder, kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", kTestPort, - {"model=shifty", "id=asdf"}, IPAddress{192, 168, 3, 7}, - kDefaultSocket); - - EXPECT_CALL(observer_, OnReceiverAdded(_)); - mdns_service_->HandleNewEvents(); - - auto ptr_remove = MakePtrEvent(kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, kDefaultSocket); - ptr_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddPtrEvent(std::move(ptr_remove)); - - EXPECT_CALL(observer_, OnReceiverRemoved(_)); - mdns_service_->HandleNewEvents(); - - EXPECT_FALSE(mdns_responder->ptr_queries_empty()); - EXPECT_FALSE(mdns_responder->srv_queries_empty()); - EXPECT_FALSE(mdns_responder->txt_queries_empty()); - EXPECT_FALSE(mdns_responder->a_queries_empty()); - EXPECT_FALSE(mdns_responder->aaaa_queries_empty()); - - auto srv_remove = - MakeSrvEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - "gigliorononomicon", kTestPort, kDefaultSocket); - srv_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddSrvEvent(std::move(srv_remove)); - mdns_service_->HandleNewEvents(); - - EXPECT_FALSE(mdns_responder->ptr_queries_empty()); - EXPECT_TRUE(mdns_responder->srv_queries_empty()); - EXPECT_TRUE(mdns_responder->txt_queries_empty()); - EXPECT_TRUE(mdns_responder->a_queries_empty()); - EXPECT_TRUE(mdns_responder->aaaa_queries_empty()); -} - -TEST_F(MdnsResponderServiceTest, MultipleInterfaceRemove) { - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - - AddEventsForNewService(mdns_responder, kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", kTestPort, - {"model=shifty", "id=asdf"}, IPAddress{192, 168, 3, 7}, - kDefaultSocket); - AddEventsForNewService(mdns_responder, kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", kTestPort, - {"model=shifty", "id=asdf"}, IPAddress{192, 168, 3, 7}, - kSecondSocket); - - EXPECT_CALL(observer_, OnReceiverAdded(_)); - mdns_service_->HandleNewEvents(); - - auto srv_remove1 = - MakeSrvEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - "gigliorononomicon", kTestPort, kSecondSocket); - srv_remove1.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddSrvEvent(std::move(srv_remove1)); - EXPECT_CALL(observer_, OnReceiverChanged(_)).Times(0); - EXPECT_CALL(observer_, OnReceiverRemoved(_)).Times(0); - mdns_service_->HandleNewEvents(); - - auto srv_remove2 = - MakeSrvEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - "gigliorononomicon", kTestPort, kDefaultSocket); - srv_remove2.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddSrvEvent(std::move(srv_remove2)); - EXPECT_CALL(observer_, OnReceiverRemoved(_)); - mdns_service_->HandleNewEvents(); - EXPECT_TRUE(mdns_responder->a_queries_empty()); - - auto ptr_remove = MakePtrEvent(kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, kDefaultSocket); - ptr_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddPtrEvent(std::move(ptr_remove)); - mdns_service_->HandleNewEvents(); - - EXPECT_FALSE(mdns_responder->ptr_queries_empty()); - EXPECT_TRUE(mdns_responder->srv_queries_empty()); - EXPECT_TRUE(mdns_responder->txt_queries_empty()); - EXPECT_TRUE(mdns_responder->a_queries_empty()); - EXPECT_TRUE(mdns_responder->aaaa_queries_empty()); -} - -TEST_F(MdnsResponderServiceTest, ResumeService) { - EXPECT_CALL(publisher_observer_, OnStarted()); - service_publisher_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - ASSERT_TRUE(mdns_responder); - ASSERT_TRUE(mdns_responder->running()); - - EXPECT_EQ(2u, mdns_responder->registered_interfaces().size()); - ASSERT_EQ(1u, mdns_responder->registered_services().size()); - - EXPECT_CALL(publisher_observer_, OnSuspended()); - service_publisher_->Suspend(); - - EXPECT_TRUE(mdns_responder_factory_->last_mdns_responder()); - EXPECT_EQ(0u, mdns_responder->registered_services().size()); - - EXPECT_CALL(publisher_observer_, OnStarted()); - service_publisher_->Resume(); - - EXPECT_EQ(2u, mdns_responder->registered_interfaces().size()); - ASSERT_EQ(1u, mdns_responder->registered_services().size()); -} - -TEST_F(MdnsResponderServiceTest, RestorePtrNotifiesObserver) { - EXPECT_CALL(observer_, OnStarted()); - service_listener_->Start(); - - auto* mdns_responder = mdns_responder_factory_->last_mdns_responder(); - - AddEventsForNewService(mdns_responder, kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, "gigliorononomicon", kTestPort, - {"model=shifty", "id=asdf"}, IPAddress{192, 168, 3, 7}, - kDefaultSocket); - - EXPECT_CALL(observer_, OnReceiverAdded(_)); - mdns_service_->HandleNewEvents(); - - auto ptr_remove = MakePtrEvent(kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, kDefaultSocket); - ptr_remove.header.response_type = QueryEventHeader::Type::kRemoved; - mdns_responder->AddPtrEvent(std::move(ptr_remove)); - - EXPECT_CALL(observer_, OnReceiverRemoved(_)); - mdns_service_->HandleNewEvents(); - - auto ptr_add = MakePtrEvent(kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, kDefaultSocket); - mdns_responder->AddPtrEvent(std::move(ptr_add)); - - EXPECT_CALL(observer_, OnReceiverAdded(_)); - mdns_service_->HandleNewEvents(); -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/mdns_service_listener_factory.cc b/osp/impl/mdns_service_listener_factory.cc deleted file mode 100644 index cae4a341..00000000 --- a/osp/impl/mdns_service_listener_factory.cc +++ /dev/null @@ -1,24 +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/public/mdns_service_listener_factory.h" - -#include "osp/impl/internal_services.h" - -namespace openscreen { - -class TaskRunner; - -namespace osp { - -// static -std::unique_ptr<ServiceListener> MdnsServiceListenerFactory::Create( - const MdnsServiceListenerConfig& config, - ServiceListener::Observer* observer, - TaskRunner* task_runner) { - return InternalServices::CreateListener(config, observer, task_runner); -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/mdns_service_publisher_factory.cc b/osp/impl/mdns_service_publisher_factory.cc deleted file mode 100644 index f055e772..00000000 --- a/osp/impl/mdns_service_publisher_factory.cc +++ /dev/null @@ -1,24 +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/public/mdns_service_publisher_factory.h" - -#include "osp/impl/internal_services.h" - -namespace openscreen { - -class TaskRunner; - -namespace osp { - -// static -std::unique_ptr<ServicePublisher> MdnsServicePublisherFactory::Create( - const ServicePublisher::Config& config, - ServicePublisher::Observer* observer, - TaskRunner* task_runner) { - return InternalServices::CreatePublisher(config, observer, task_runner); -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/network_service_manager.cc b/osp/impl/network_service_manager.cc index d05192c0..10890ca8 100644 --- a/osp/impl/network_service_manager.cc +++ b/osp/impl/network_service_manager.cc @@ -17,14 +17,14 @@ namespace osp { // static NetworkServiceManager* NetworkServiceManager::Create( std::unique_ptr<ServiceListener> mdns_listener, - std::unique_ptr<ServicePublisher> mdns_publisher, + std::unique_ptr<ServicePublisher> service_publisher, std::unique_ptr<ProtocolConnectionClient> connection_client, std::unique_ptr<ProtocolConnectionServer> connection_server) { // TODO(mfoltz): Convert to assertion failure if (g_network_service_manager_instance) return nullptr; g_network_service_manager_instance = new NetworkServiceManager( - std::move(mdns_listener), std::move(mdns_publisher), + std::move(mdns_listener), std::move(service_publisher), std::move(connection_client), std::move(connection_server)); return g_network_service_manager_instance; } @@ -50,8 +50,8 @@ ServiceListener* NetworkServiceManager::GetMdnsServiceListener() { return mdns_listener_.get(); } -ServicePublisher* NetworkServiceManager::GetMdnsServicePublisher() { - return mdns_publisher_.get(); +ServicePublisher* NetworkServiceManager::GetServicePublisher() { + return service_publisher_.get(); } ProtocolConnectionClient* NetworkServiceManager::GetProtocolConnectionClient() { @@ -64,11 +64,11 @@ ProtocolConnectionServer* NetworkServiceManager::GetProtocolConnectionServer() { NetworkServiceManager::NetworkServiceManager( std::unique_ptr<ServiceListener> mdns_listener, - std::unique_ptr<ServicePublisher> mdns_publisher, + std::unique_ptr<ServicePublisher> service_publisher, std::unique_ptr<ProtocolConnectionClient> connection_client, std::unique_ptr<ProtocolConnectionServer> connection_server) : mdns_listener_(std::move(mdns_listener)), - mdns_publisher_(std::move(mdns_publisher)), + service_publisher_(std::move(service_publisher)), connection_client_(std::move(connection_client)), connection_server_(std::move(connection_server)) {} diff --git a/osp/impl/service_publisher_impl.cc b/osp/impl/service_publisher_impl.cc index bde3e528..8a984969 100644 --- a/osp/impl/service_publisher_impl.cc +++ b/osp/impl/service_publisher_impl.cc @@ -4,6 +4,8 @@ #include "osp/impl/service_publisher_impl.h" +#include <utility> + #include "util/osp_logging.h" namespace openscreen { @@ -44,8 +46,8 @@ void ServicePublisherImpl::Delegate::SetPublisherImpl( } ServicePublisherImpl::ServicePublisherImpl(Observer* observer, - Delegate* delegate) - : ServicePublisher(observer), delegate_(delegate) { + std::unique_ptr<Delegate> delegate) + : ServicePublisher(observer), delegate_(std::move(delegate)) { delegate_->SetPublisherImpl(this); } @@ -55,14 +57,14 @@ bool ServicePublisherImpl::Start() { if (state_ != State::kStopped) return false; state_ = State::kStarting; - delegate_->StartPublisher(); + delegate_->StartPublisher(config_); return true; } bool ServicePublisherImpl::StartAndSuspend() { if (state_ != State::kStopped) return false; state_ = State::kStarting; - delegate_->StartAndSuspendPublisher(); + delegate_->StartAndSuspendPublisher(config_); return true; } bool ServicePublisherImpl::Stop() { @@ -84,7 +86,7 @@ bool ServicePublisherImpl::Resume() { if (state_ != State::kSuspended) return false; - delegate_->ResumePublisher(); + delegate_->ResumePublisher(config_); return true; } diff --git a/osp/impl/service_publisher_impl.h b/osp/impl/service_publisher_impl.h index d15e4dc9..fa2c3890 100644 --- a/osp/impl/service_publisher_impl.h +++ b/osp/impl/service_publisher_impl.h @@ -5,6 +5,8 @@ #ifndef OSP_IMPL_SERVICE_PUBLISHER_IMPL_H_ #define OSP_IMPL_SERVICE_PUBLISHER_IMPL_H_ +#include <memory> + #include "osp/impl/with_destruction_callback.h" #include "osp/public/service_publisher.h" #include "platform/base/macros.h" @@ -18,18 +20,18 @@ class ServicePublisherImpl final : public ServicePublisher, class Delegate { public: Delegate(); + virtual ~Delegate(); void SetPublisherImpl(ServicePublisherImpl* publisher); - virtual void StartPublisher() = 0; - virtual void StartAndSuspendPublisher() = 0; + virtual void StartPublisher(const ServicePublisher::Config& config) = 0; + virtual void StartAndSuspendPublisher( + const ServicePublisher::Config& config) = 0; virtual void StopPublisher() = 0; virtual void SuspendPublisher() = 0; - virtual void ResumePublisher() = 0; + virtual void ResumePublisher(const ServicePublisher::Config& config) = 0; protected: - virtual ~Delegate(); - void SetState(State state) { publisher_->SetState(state); } ServicePublisherImpl* publisher_ = nullptr; @@ -38,7 +40,7 @@ class ServicePublisherImpl final : public ServicePublisher, // |observer| is optional. If it is provided, it will receive appropriate // notifications about this ServicePublisher. |delegate| is required and // is used to implement state transitions. - ServicePublisherImpl(Observer* observer, Delegate* delegate); + ServicePublisherImpl(Observer* observer, std::unique_ptr<Delegate> delegate); ~ServicePublisherImpl() override; // ServicePublisher overrides. @@ -57,7 +59,7 @@ class ServicePublisherImpl final : public ServicePublisher, // by the observer interface. void MaybeNotifyObserver(); - Delegate* const delegate_; + std::unique_ptr<Delegate> delegate_; OSP_DISALLOW_COPY_AND_ASSIGN(ServicePublisherImpl); }; diff --git a/osp/impl/service_publisher_impl_unittest.cc b/osp/impl/service_publisher_impl_unittest.cc index 8c8bc9ec..b77a8aa5 100644 --- a/osp/impl/service_publisher_impl_unittest.cc +++ b/osp/impl/service_publisher_impl_unittest.cc @@ -5,6 +5,7 @@ #include "osp/impl/service_publisher_impl.h" #include <memory> +#include <utility> #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -13,6 +14,7 @@ namespace openscreen { namespace osp { namespace { +using ::testing::_; using ::testing::Expectation; using ::testing::NiceMock; @@ -26,7 +28,7 @@ class MockObserver final : public ServicePublisher::Observer { MOCK_METHOD0(OnStopped, void()); MOCK_METHOD0(OnSuspended, void()); - MOCK_METHOD1(OnError, void(ServicePublisherError)); + MOCK_METHOD1(OnError, void(Error)); MOCK_METHOD1(OnMetrics, void(ServicePublisher::Metrics)); }; @@ -38,23 +40,27 @@ class MockMdnsDelegate : public ServicePublisherImpl::Delegate { using ServicePublisherImpl::Delegate::SetState; - MOCK_METHOD0(StartPublisher, void()); - MOCK_METHOD0(StartAndSuspendPublisher, void()); + MOCK_METHOD1(StartPublisher, void(const ServicePublisher::Config&)); + MOCK_METHOD1(StartAndSuspendPublisher, void(const ServicePublisher::Config&)); MOCK_METHOD0(StopPublisher, void()); MOCK_METHOD0(SuspendPublisher, void()); - MOCK_METHOD0(ResumePublisher, void()); + MOCK_METHOD1(ResumePublisher, void(const ServicePublisher::Config&)); MOCK_METHOD0(RunTasksPublisher, void()); }; class ServicePublisherImplTest : public ::testing::Test { protected: void SetUp() override { - service_publisher_ = - std::make_unique<ServicePublisherImpl>(nullptr, &mock_delegate_); + auto mock_delegate = std::make_unique<NiceMock<MockMdnsDelegate>>(); + mock_delegate_ = mock_delegate.get(); + service_publisher_ = std::make_unique<ServicePublisherImpl>( + nullptr, std::move(mock_delegate)); + service_publisher_->SetConfig(config); } - NiceMock<MockMdnsDelegate> mock_delegate_; + NiceMock<MockMdnsDelegate>* mock_delegate_ = nullptr; std::unique_ptr<ServicePublisherImpl> service_publisher_; + ServicePublisher::Config config; }; } // namespace @@ -62,99 +68,100 @@ class ServicePublisherImplTest : public ::testing::Test { TEST_F(ServicePublisherImplTest, NormalStartStop) { ASSERT_EQ(State::kStopped, service_publisher_->state()); - EXPECT_CALL(mock_delegate_, StartPublisher()); + EXPECT_CALL(*mock_delegate_, StartPublisher(_)); EXPECT_TRUE(service_publisher_->Start()); EXPECT_FALSE(service_publisher_->Start()); EXPECT_EQ(State::kStarting, service_publisher_->state()); - mock_delegate_.SetState(State::kRunning); + mock_delegate_->SetState(State::kRunning); EXPECT_EQ(State::kRunning, service_publisher_->state()); - EXPECT_CALL(mock_delegate_, StopPublisher()); + EXPECT_CALL(*mock_delegate_, StopPublisher()); EXPECT_TRUE(service_publisher_->Stop()); EXPECT_FALSE(service_publisher_->Stop()); EXPECT_EQ(State::kStopping, service_publisher_->state()); - mock_delegate_.SetState(State::kStopped); + mock_delegate_->SetState(State::kStopped); EXPECT_EQ(State::kStopped, service_publisher_->state()); } TEST_F(ServicePublisherImplTest, StopBeforeRunning) { - EXPECT_CALL(mock_delegate_, StartPublisher()); + EXPECT_CALL(*mock_delegate_, StartPublisher(_)); EXPECT_TRUE(service_publisher_->Start()); EXPECT_EQ(State::kStarting, service_publisher_->state()); - EXPECT_CALL(mock_delegate_, StopPublisher()); + EXPECT_CALL(*mock_delegate_, StopPublisher()); EXPECT_TRUE(service_publisher_->Stop()); EXPECT_FALSE(service_publisher_->Stop()); EXPECT_EQ(State::kStopping, service_publisher_->state()); - mock_delegate_.SetState(State::kStopped); + mock_delegate_->SetState(State::kStopped); EXPECT_EQ(State::kStopped, service_publisher_->state()); } TEST_F(ServicePublisherImplTest, StartSuspended) { - EXPECT_CALL(mock_delegate_, StartAndSuspendPublisher()); - EXPECT_CALL(mock_delegate_, StartPublisher()).Times(0); + EXPECT_CALL(*mock_delegate_, StartAndSuspendPublisher(_)); + EXPECT_CALL(*mock_delegate_, StartPublisher(_)).Times(0); EXPECT_TRUE(service_publisher_->StartAndSuspend()); EXPECT_FALSE(service_publisher_->Start()); EXPECT_EQ(State::kStarting, service_publisher_->state()); - mock_delegate_.SetState(State::kSuspended); + mock_delegate_->SetState(State::kSuspended); EXPECT_EQ(State::kSuspended, service_publisher_->state()); } TEST_F(ServicePublisherImplTest, SuspendAndResume) { EXPECT_TRUE(service_publisher_->Start()); - mock_delegate_.SetState(State::kRunning); + mock_delegate_->SetState(State::kRunning); - EXPECT_CALL(mock_delegate_, ResumePublisher()).Times(0); - EXPECT_CALL(mock_delegate_, SuspendPublisher()).Times(2); + EXPECT_CALL(*mock_delegate_, ResumePublisher(_)).Times(0); + EXPECT_CALL(*mock_delegate_, SuspendPublisher()).Times(2); EXPECT_FALSE(service_publisher_->Resume()); EXPECT_TRUE(service_publisher_->Suspend()); EXPECT_TRUE(service_publisher_->Suspend()); - mock_delegate_.SetState(State::kSuspended); + mock_delegate_->SetState(State::kSuspended); EXPECT_EQ(State::kSuspended, service_publisher_->state()); - EXPECT_CALL(mock_delegate_, StartPublisher()).Times(0); - EXPECT_CALL(mock_delegate_, SuspendPublisher()).Times(0); - EXPECT_CALL(mock_delegate_, ResumePublisher()).Times(2); + EXPECT_CALL(*mock_delegate_, StartPublisher(_)).Times(0); + EXPECT_CALL(*mock_delegate_, SuspendPublisher()).Times(0); + EXPECT_CALL(*mock_delegate_, ResumePublisher(_)).Times(2); EXPECT_FALSE(service_publisher_->Start()); EXPECT_FALSE(service_publisher_->Suspend()); EXPECT_TRUE(service_publisher_->Resume()); EXPECT_TRUE(service_publisher_->Resume()); - mock_delegate_.SetState(State::kRunning); + mock_delegate_->SetState(State::kRunning); EXPECT_EQ(State::kRunning, service_publisher_->state()); - EXPECT_CALL(mock_delegate_, ResumePublisher()).Times(0); + EXPECT_CALL(*mock_delegate_, ResumePublisher(_)).Times(0); EXPECT_FALSE(service_publisher_->Resume()); } TEST_F(ServicePublisherImplTest, ObserverTransitions) { MockObserver observer; - NiceMock<MockMdnsDelegate> mock_delegate; - service_publisher_ = - std::make_unique<ServicePublisherImpl>(&observer, &mock_delegate); + auto mock_delegate = std::make_unique<NiceMock<MockMdnsDelegate>>(); + NiceMock<MockMdnsDelegate>* const mock_delegate_ptr = mock_delegate.get(); + auto service_publisher = std::make_unique<ServicePublisherImpl>( + &observer, std::move(mock_delegate)); - service_publisher_->Start(); + service_publisher->Start(); Expectation start_from_stopped = EXPECT_CALL(observer, OnStarted()); - mock_delegate.SetState(State::kRunning); + mock_delegate_ptr->SetState(State::kRunning); - service_publisher_->Suspend(); + service_publisher->Suspend(); Expectation suspend_from_running = EXPECT_CALL(observer, OnSuspended()).After(start_from_stopped); - mock_delegate.SetState(State::kSuspended); + mock_delegate_ptr->SetState(State::kSuspended); - service_publisher_->Resume(); + service_publisher->Resume(); Expectation resume_from_suspended = EXPECT_CALL(observer, OnStarted()).After(suspend_from_running); - mock_delegate.SetState(State::kRunning); + mock_delegate_ptr->SetState(State::kRunning); - service_publisher_->Stop(); + service_publisher->Stop(); EXPECT_CALL(observer, OnStopped()).After(resume_from_suspended); - mock_delegate.SetState(State::kStopped); + mock_delegate_ptr->SetState(State::kStopped); } } // namespace osp diff --git a/osp/impl/testing/BUILD.gn b/osp/impl/testing/BUILD.gn deleted file mode 100644 index 94bcc504..00000000 --- a/osp/impl/testing/BUILD.gn +++ /dev/null @@ -1,38 +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. - -import("../../build/config/services.gni") -assert(use_mdns_responder) - -source_set("testing") { - testonly = true - sources = [ - "fake_mdns_platform_service.cc", - "fake_mdns_platform_service.h", - "fake_mdns_responder_adapter.cc", - "fake_mdns_responder_adapter.h", - ] - - deps = [ - "../discovery/mdns:mdns_interface", - ] - - public_deps = [ - "../../../platform", - ] -} - -source_set("unittests") { - testonly = true - sources = [ - "fake_mdns_platform_service_unittest.cc", - "fake_mdns_responder_adapter_unittest.cc", - ] - - deps = [ - ":testing", - "../../../third_party/abseil", - "../../../third_party/googletest:gtest", - ] -} diff --git a/osp/impl/testing/fake_mdns_platform_service.cc b/osp/impl/testing/fake_mdns_platform_service.cc deleted file mode 100644 index 8ce6faf7..00000000 --- a/osp/impl/testing/fake_mdns_platform_service.cc +++ /dev/null @@ -1,50 +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/testing/fake_mdns_platform_service.h" - -#include <algorithm> - -#include "util/osp_logging.h" - -namespace openscreen { -namespace osp { - -FakeMdnsPlatformService::FakeMdnsPlatformService() = default; -FakeMdnsPlatformService::~FakeMdnsPlatformService() = default; - -std::vector<MdnsPlatformService::BoundInterface> -FakeMdnsPlatformService::RegisterInterfaces( - const std::vector<NetworkInterfaceIndex>& allowlist) { - OSP_CHECK(registered_interfaces_.empty()); - if (allowlist.empty()) { - registered_interfaces_ = interfaces_; - } else { - for (const auto& interface : interfaces_) { - if (std::find(allowlist.begin(), allowlist.end(), - interface.interface_info.index) != allowlist.end()) { - registered_interfaces_.push_back(interface); - } - } - } - return registered_interfaces_; -} - -void FakeMdnsPlatformService::DeregisterInterfaces( - const std::vector<BoundInterface>& interfaces) { - for (const auto& interface : interfaces) { - auto index = interface.interface_info.index; - auto it = std::find_if(registered_interfaces_.begin(), - registered_interfaces_.end(), - [index](const BoundInterface& interface) { - return interface.interface_info.index == index; - }); - OSP_CHECK(it != registered_interfaces_.end()) - << "Must deregister a previously returned interface: " << index; - registered_interfaces_.erase(it); - } -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/testing/fake_mdns_platform_service.h b/osp/impl/testing/fake_mdns_platform_service.h deleted file mode 100644 index a21c48cd..00000000 --- a/osp/impl/testing/fake_mdns_platform_service.h +++ /dev/null @@ -1,39 +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. - -#ifndef OSP_IMPL_TESTING_FAKE_MDNS_PLATFORM_SERVICE_H_ -#define OSP_IMPL_TESTING_FAKE_MDNS_PLATFORM_SERVICE_H_ - -#include <vector> - -#include "osp/impl/mdns_platform_service.h" - -namespace openscreen { -namespace osp { - -class FakeMdnsPlatformService final : public MdnsPlatformService { - public: - FakeMdnsPlatformService(); - ~FakeMdnsPlatformService() override; - - void set_interfaces(const std::vector<BoundInterface>& interfaces) { - interfaces_ = interfaces; - } - - // PlatformService overrides. - std::vector<BoundInterface> RegisterInterfaces( - const std::vector<NetworkInterfaceIndex>& interface_index_allowlist) - override; - void DeregisterInterfaces( - const std::vector<BoundInterface>& registered_interfaces) override; - - private: - std::vector<BoundInterface> registered_interfaces_; - std::vector<BoundInterface> interfaces_; -}; - -} // namespace osp -} // namespace openscreen - -#endif // OSP_IMPL_TESTING_FAKE_MDNS_PLATFORM_SERVICE_H_ diff --git a/osp/impl/testing/fake_mdns_platform_service_unittest.cc b/osp/impl/testing/fake_mdns_platform_service_unittest.cc deleted file mode 100644 index 5a1d5f4c..00000000 --- a/osp/impl/testing/fake_mdns_platform_service_unittest.cc +++ /dev/null @@ -1,112 +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/testing/fake_mdns_platform_service.h" - -#include <cstdint> - -#include "gtest/gtest.h" - -namespace openscreen { -namespace osp { -namespace { - -UdpSocket* const kDefaultSocket = - reinterpret_cast<UdpSocket*>(static_cast<uintptr_t>(16)); -UdpSocket* const kSecondSocket = - reinterpret_cast<UdpSocket*>(static_cast<uintptr_t>(24)); - -class FakeMdnsPlatformServiceTest : public ::testing::Test { - protected: - const uint8_t mac1_[6] = {11, 22, 33, 44, 55, 66}; - const uint8_t mac2_[6] = {12, 23, 34, 45, 56, 67}; - const IPSubnet subnet1_{IPAddress{192, 168, 3, 2}, 24}; - const IPSubnet subnet2_{ - IPAddress{0x0102, 0x0304, 0x0504, 0x0302, 0x0102, 0x0304, 0x0506, 0x0708}, - 24}; - std::vector<MdnsPlatformService::BoundInterface> bound_interfaces_{ - MdnsPlatformService::BoundInterface{ - InterfaceInfo{1, - mac1_, - "eth0", - InterfaceInfo::Type::kEthernet, - {subnet1_}}, - subnet1_, kDefaultSocket}, - MdnsPlatformService::BoundInterface{ - InterfaceInfo{2, - mac2_, - "eth1", - InterfaceInfo::Type::kEthernet, - {subnet2_}}, - subnet2_, kSecondSocket}}; -}; - -} // namespace - -TEST_F(FakeMdnsPlatformServiceTest, SimpleRegistration) { - FakeMdnsPlatformService platform_service; - std::vector<MdnsPlatformService::BoundInterface> bound_interfaces{ - bound_interfaces_[0]}; - - platform_service.set_interfaces(bound_interfaces); - - auto registered_interfaces = platform_service.RegisterInterfaces({}); - EXPECT_EQ(bound_interfaces, registered_interfaces); - platform_service.DeregisterInterfaces(registered_interfaces); - - registered_interfaces = platform_service.RegisterInterfaces({}); - EXPECT_EQ(bound_interfaces, registered_interfaces); - platform_service.DeregisterInterfaces(registered_interfaces); - platform_service.set_interfaces({}); - - registered_interfaces = platform_service.RegisterInterfaces({}); - EXPECT_TRUE(registered_interfaces.empty()); - platform_service.DeregisterInterfaces(registered_interfaces); - - std::vector<MdnsPlatformService::BoundInterface> new_interfaces{ - bound_interfaces_[1]}; - - platform_service.set_interfaces(new_interfaces); - - registered_interfaces = platform_service.RegisterInterfaces({}); - EXPECT_EQ(new_interfaces, registered_interfaces); - platform_service.DeregisterInterfaces(registered_interfaces); -} - -TEST_F(FakeMdnsPlatformServiceTest, ObeyIndexAllowlist) { - FakeMdnsPlatformService platform_service; - platform_service.set_interfaces(bound_interfaces_); - - auto eth0_only = platform_service.RegisterInterfaces({1}); - EXPECT_EQ( - (std::vector<MdnsPlatformService::BoundInterface>{bound_interfaces_[0]}), - eth0_only); - platform_service.DeregisterInterfaces(eth0_only); - - auto eth1_only = platform_service.RegisterInterfaces({2}); - EXPECT_EQ( - (std::vector<MdnsPlatformService::BoundInterface>{bound_interfaces_[1]}), - eth1_only); - platform_service.DeregisterInterfaces(eth1_only); - - auto both = platform_service.RegisterInterfaces({1, 2}); - EXPECT_EQ(bound_interfaces_, both); - platform_service.DeregisterInterfaces(both); -} - -TEST_F(FakeMdnsPlatformServiceTest, PartialDeregister) { - FakeMdnsPlatformService platform_service; - platform_service.set_interfaces(bound_interfaces_); - - auto both = platform_service.RegisterInterfaces({}); - std::vector<MdnsPlatformService::BoundInterface> eth0_only{ - bound_interfaces_[0]}; - std::vector<MdnsPlatformService::BoundInterface> eth1_only{ - bound_interfaces_[1]}; - platform_service.DeregisterInterfaces(eth0_only); - platform_service.DeregisterInterfaces(eth1_only); -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/testing/fake_mdns_responder_adapter.cc b/osp/impl/testing/fake_mdns_responder_adapter.cc deleted file mode 100644 index 7b5a3b5e..00000000 --- a/osp/impl/testing/fake_mdns_responder_adapter.cc +++ /dev/null @@ -1,600 +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/testing/fake_mdns_responder_adapter.h" - -#include <algorithm> -#include <map> -#include <string> -#include <utility> - -#include "platform/base/error.h" -#include "util/osp_logging.h" - -namespace openscreen { -namespace osp { - -constexpr char kLocalDomain[] = "local"; - -PtrEvent MakePtrEvent(const std::string& service_instance, - const std::string& service_type, - const std::string& service_protocol, - UdpSocket* socket) { - const auto labels = std::vector<std::string>{service_instance, service_type, - service_protocol, kLocalDomain}; - ErrorOr<DomainName> full_instance_name = - DomainName::FromLabels(labels.begin(), labels.end()); - OSP_CHECK(full_instance_name); - PtrEvent result{QueryEventHeader{QueryEventHeader::Type::kAdded, socket}, - full_instance_name.value()}; - return result; -} - -SrvEvent MakeSrvEvent(const std::string& service_instance, - const std::string& service_type, - const std::string& service_protocol, - const std::string& hostname, - uint16_t port, - UdpSocket* socket) { - const auto instance_labels = std::vector<std::string>{ - service_instance, service_type, service_protocol, kLocalDomain}; - ErrorOr<DomainName> full_instance_name = - DomainName::FromLabels(instance_labels.begin(), instance_labels.end()); - OSP_CHECK(full_instance_name); - - const auto host_labels = std::vector<std::string>{hostname, kLocalDomain}; - ErrorOr<DomainName> domain_name = - DomainName::FromLabels(host_labels.begin(), host_labels.end()); - OSP_CHECK(domain_name); - - SrvEvent result{QueryEventHeader{QueryEventHeader::Type::kAdded, socket}, - full_instance_name.value(), domain_name.value(), port}; - return result; -} - -TxtEvent MakeTxtEvent(const std::string& service_instance, - const std::string& service_type, - const std::string& service_protocol, - const std::vector<std::string>& txt_lines, - UdpSocket* socket) { - const auto labels = std::vector<std::string>{service_instance, service_type, - service_protocol, kLocalDomain}; - ErrorOr<DomainName> domain_name = - DomainName::FromLabels(labels.begin(), labels.end()); - OSP_CHECK(domain_name); - TxtEvent result{QueryEventHeader{QueryEventHeader::Type::kAdded, socket}, - domain_name.value(), txt_lines}; - return result; -} - -AEvent MakeAEvent(const std::string& hostname, - IPAddress address, - UdpSocket* socket) { - const auto labels = std::vector<std::string>{hostname, kLocalDomain}; - ErrorOr<DomainName> domain_name = - DomainName::FromLabels(labels.begin(), labels.end()); - OSP_CHECK(domain_name); - AEvent result{QueryEventHeader{QueryEventHeader::Type::kAdded, socket}, - domain_name.value(), address}; - return result; -} - -AaaaEvent MakeAaaaEvent(const std::string& hostname, - IPAddress address, - UdpSocket* socket) { - const auto labels = std::vector<std::string>{hostname, kLocalDomain}; - ErrorOr<DomainName> domain_name = - DomainName::FromLabels(labels.begin(), labels.end()); - OSP_CHECK(domain_name); - AaaaEvent result{QueryEventHeader{QueryEventHeader::Type::kAdded, socket}, - domain_name.value(), address}; - return result; -} - -void AddEventsForNewService(FakeMdnsResponderAdapter* mdns_responder, - const std::string& service_instance, - const std::string& service_name, - const std::string& service_protocol, - const std::string& hostname, - uint16_t port, - const std::vector<std::string>& txt_lines, - const IPAddress& address, - UdpSocket* socket) { - mdns_responder->AddPtrEvent( - MakePtrEvent(service_instance, service_name, service_protocol, socket)); - mdns_responder->AddSrvEvent(MakeSrvEvent(service_instance, service_name, - service_protocol, hostname, port, - socket)); - mdns_responder->AddTxtEvent(MakeTxtEvent( - service_instance, service_name, service_protocol, txt_lines, socket)); - mdns_responder->AddAEvent(MakeAEvent(hostname, address, socket)); -} - -FakeMdnsResponderAdapter::~FakeMdnsResponderAdapter() { - if (observer_) { - observer_->OnDestroyed(); - } -} - -void FakeMdnsResponderAdapter::AddPtrEvent(PtrEvent&& ptr_event) { - if (running_) - ptr_events_.push_back(std::move(ptr_event)); -} - -void FakeMdnsResponderAdapter::AddSrvEvent(SrvEvent&& srv_event) { - if (running_) - srv_events_.push_back(std::move(srv_event)); -} - -void FakeMdnsResponderAdapter::AddTxtEvent(TxtEvent&& txt_event) { - if (running_) - txt_events_.push_back(std::move(txt_event)); -} - -void FakeMdnsResponderAdapter::AddAEvent(AEvent&& a_event) { - if (running_) - a_events_.push_back(std::move(a_event)); -} - -void FakeMdnsResponderAdapter::AddAaaaEvent(AaaaEvent&& aaaa_event) { - if (running_) - aaaa_events_.push_back(std::move(aaaa_event)); -} - -bool FakeMdnsResponderAdapter::ptr_queries_empty() const { - for (const auto& queries : queries_) { - if (!queries.second.ptr_queries.empty()) - return false; - } - return true; -} - -bool FakeMdnsResponderAdapter::srv_queries_empty() const { - for (const auto& queries : queries_) { - if (!queries.second.srv_queries.empty()) - return false; - } - return true; -} - -bool FakeMdnsResponderAdapter::txt_queries_empty() const { - for (const auto& queries : queries_) { - if (!queries.second.txt_queries.empty()) - return false; - } - return true; -} - -bool FakeMdnsResponderAdapter::a_queries_empty() const { - for (const auto& queries : queries_) { - if (!queries.second.a_queries.empty()) - return false; - } - return true; -} - -bool FakeMdnsResponderAdapter::aaaa_queries_empty() const { - for (const auto& queries : queries_) { - if (!queries.second.aaaa_queries.empty()) - return false; - } - return true; -} - -Error FakeMdnsResponderAdapter::Init() { - OSP_CHECK(!running_); - running_ = true; - return Error::None(); -} - -void FakeMdnsResponderAdapter::Close() { - queries_.clear(); - ptr_events_.clear(); - srv_events_.clear(); - txt_events_.clear(); - a_events_.clear(); - aaaa_events_.clear(); - registered_interfaces_.clear(); - registered_services_.clear(); - running_ = false; -} - -Error FakeMdnsResponderAdapter::SetHostLabel(const std::string& host_label) { - return Error::Code::kNotImplemented; -} - -Error FakeMdnsResponderAdapter::RegisterInterface( - const InterfaceInfo& interface_info, - const IPSubnet& interface_address, - UdpSocket* socket) { - if (!running_) - return Error::Code::kOperationInvalid; - - if (std::find_if(registered_interfaces_.begin(), registered_interfaces_.end(), - [&socket](const RegisteredInterface& interface) { - return interface.socket == socket; - }) != registered_interfaces_.end()) { - return Error::Code::kItemNotFound; - } - registered_interfaces_.push_back({interface_info, interface_address, socket}); - return Error::None(); -} - -Error FakeMdnsResponderAdapter::DeregisterInterface(UdpSocket* socket) { - auto it = - std::find_if(registered_interfaces_.begin(), registered_interfaces_.end(), - [&socket](const RegisteredInterface& interface) { - return interface.socket == socket; - }); - if (it == registered_interfaces_.end()) - return Error::Code::kItemNotFound; - - registered_interfaces_.erase(it); - return Error::None(); -} - -void FakeMdnsResponderAdapter::OnRead(UdpSocket* socket, - ErrorOr<UdpPacket> packet) { - OSP_NOTREACHED(); -} - -void FakeMdnsResponderAdapter::OnSendError(UdpSocket* socket, Error error) { - OSP_NOTREACHED(); -} - -void FakeMdnsResponderAdapter::OnError(UdpSocket* socket, Error error) { - OSP_NOTREACHED(); -} - -void FakeMdnsResponderAdapter::OnBound(UdpSocket* socket) { - OSP_NOTREACHED(); -} - -Clock::duration FakeMdnsResponderAdapter::RunTasks() { - return std::chrono::seconds(1); -} - -std::vector<PtrEvent> FakeMdnsResponderAdapter::TakePtrResponses() { - std::vector<PtrEvent> result; - for (auto& queries : queries_) { - const auto query_it = std::stable_partition( - ptr_events_.begin(), ptr_events_.end(), - [&queries](const PtrEvent& ptr_event) { - const auto instance_labels = ptr_event.service_instance.GetLabels(); - for (const auto& query : queries.second.ptr_queries) { - const auto query_labels = query.GetLabels(); - // TODO(btolsch): Just use qname if it's added to PtrEvent. - if (ptr_event.header.socket == queries.first && - std::equal(instance_labels.begin() + 1, instance_labels.end(), - query_labels.begin())) { - return false; - } - } - return true; - }); - for (auto it = query_it; it != ptr_events_.end(); ++it) { - result.push_back(std::move(*it)); - } - ptr_events_.erase(query_it, ptr_events_.end()); - } - OSP_LOG_INFO << "taking " << result.size() << " ptr response(s)"; - return result; -} - -std::vector<SrvEvent> FakeMdnsResponderAdapter::TakeSrvResponses() { - std::vector<SrvEvent> result; - for (auto& queries : queries_) { - const auto query_it = std::stable_partition( - srv_events_.begin(), srv_events_.end(), - [&queries](const SrvEvent& srv_event) { - for (const auto& query : queries.second.srv_queries) { - if (srv_event.header.socket == queries.first && - srv_event.service_instance == query) - return false; - } - return true; - }); - for (auto it = query_it; it != srv_events_.end(); ++it) { - result.push_back(std::move(*it)); - } - srv_events_.erase(query_it, srv_events_.end()); - } - OSP_LOG_INFO << "taking " << result.size() << " srv response(s)"; - return result; -} - -std::vector<TxtEvent> FakeMdnsResponderAdapter::TakeTxtResponses() { - std::vector<TxtEvent> result; - for (auto& queries : queries_) { - const auto query_it = std::stable_partition( - txt_events_.begin(), txt_events_.end(), - [&queries](const TxtEvent& txt_event) { - for (const auto& query : queries.second.txt_queries) { - if (txt_event.header.socket == queries.first && - txt_event.service_instance == query) { - return false; - } - } - return true; - }); - for (auto it = query_it; it != txt_events_.end(); ++it) { - result.push_back(std::move(*it)); - } - txt_events_.erase(query_it, txt_events_.end()); - } - OSP_LOG_INFO << "taking " << result.size() << " txt response(s)"; - return result; -} - -std::vector<AEvent> FakeMdnsResponderAdapter::TakeAResponses() { - std::vector<AEvent> result; - for (auto& queries : queries_) { - const auto query_it = std::stable_partition( - a_events_.begin(), a_events_.end(), [&queries](const AEvent& a_event) { - for (const auto& query : queries.second.a_queries) { - if (a_event.header.socket == queries.first && - a_event.domain_name == query) { - return false; - } - } - return true; - }); - for (auto it = query_it; it != a_events_.end(); ++it) { - result.push_back(std::move(*it)); - } - a_events_.erase(query_it, a_events_.end()); - } - OSP_LOG_INFO << "taking " << result.size() << " a response(s)"; - return result; -} - -std::vector<AaaaEvent> FakeMdnsResponderAdapter::TakeAaaaResponses() { - std::vector<AaaaEvent> result; - for (auto& queries : queries_) { - const auto query_it = std::stable_partition( - aaaa_events_.begin(), aaaa_events_.end(), - [&queries](const AaaaEvent& aaaa_event) { - for (const auto& query : queries.second.aaaa_queries) { - if (aaaa_event.header.socket == queries.first && - aaaa_event.domain_name == query) { - return false; - } - } - return true; - }); - for (auto it = query_it; it != aaaa_events_.end(); ++it) { - result.push_back(std::move(*it)); - } - aaaa_events_.erase(query_it, aaaa_events_.end()); - } - OSP_LOG_INFO << "taking " << result.size() << " a response(s)"; - return result; -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::StartPtrQuery( - UdpSocket* socket, - const DomainName& service_type) { - if (!running_) - return MdnsResponderErrorCode::kUnknownError; - - auto canonical_service_type = service_type; - if (!canonical_service_type.EndsWithLocalDomain()) - OSP_CHECK(canonical_service_type.Append(DomainName::GetLocalDomain()).ok()); - - auto maybe_inserted = - queries_[socket].ptr_queries.insert(canonical_service_type); - if (maybe_inserted.second) { - return MdnsResponderErrorCode::kNoError; - } else { - return MdnsResponderErrorCode::kUnknownError; - } -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::StartSrvQuery( - UdpSocket* socket, - const DomainName& service_instance) { - if (!running_) - return MdnsResponderErrorCode::kUnknownError; - - auto maybe_inserted = queries_[socket].srv_queries.insert(service_instance); - if (maybe_inserted.second) { - return MdnsResponderErrorCode::kNoError; - } else { - return MdnsResponderErrorCode::kUnknownError; - } -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::StartTxtQuery( - UdpSocket* socket, - const DomainName& service_instance) { - if (!running_) - return MdnsResponderErrorCode::kUnknownError; - - auto maybe_inserted = queries_[socket].txt_queries.insert(service_instance); - if (maybe_inserted.second) { - return MdnsResponderErrorCode::kNoError; - } else { - return MdnsResponderErrorCode::kUnknownError; - } -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::StartAQuery( - UdpSocket* socket, - const DomainName& domain_name) { - if (!running_) - return MdnsResponderErrorCode::kUnknownError; - - auto maybe_inserted = queries_[socket].a_queries.insert(domain_name); - if (maybe_inserted.second) { - return MdnsResponderErrorCode::kNoError; - } else { - return MdnsResponderErrorCode::kUnknownError; - } -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::StartAaaaQuery( - UdpSocket* socket, - const DomainName& domain_name) { - if (!running_) - return MdnsResponderErrorCode::kUnknownError; - - auto maybe_inserted = queries_[socket].aaaa_queries.insert(domain_name); - if (maybe_inserted.second) { - return MdnsResponderErrorCode::kNoError; - } else { - return MdnsResponderErrorCode::kUnknownError; - } -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::StopPtrQuery( - UdpSocket* socket, - const DomainName& service_type) { - auto interface_entry = queries_.find(socket); - if (interface_entry == queries_.end()) - return MdnsResponderErrorCode::kUnknownError; - auto& ptr_queries = interface_entry->second.ptr_queries; - auto canonical_service_type = service_type; - if (!canonical_service_type.EndsWithLocalDomain()) - OSP_CHECK(canonical_service_type.Append(DomainName::GetLocalDomain()).ok()); - - auto it = ptr_queries.find(canonical_service_type); - if (it == ptr_queries.end()) - return MdnsResponderErrorCode::kUnknownError; - - ptr_queries.erase(it); - return MdnsResponderErrorCode::kNoError; -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::StopSrvQuery( - UdpSocket* socket, - const DomainName& service_instance) { - auto interface_entry = queries_.find(socket); - if (interface_entry == queries_.end()) - return MdnsResponderErrorCode::kUnknownError; - auto& srv_queries = interface_entry->second.srv_queries; - auto it = srv_queries.find(service_instance); - if (it == srv_queries.end()) - return MdnsResponderErrorCode::kUnknownError; - - srv_queries.erase(it); - return MdnsResponderErrorCode::kNoError; -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::StopTxtQuery( - UdpSocket* socket, - const DomainName& service_instance) { - auto interface_entry = queries_.find(socket); - if (interface_entry == queries_.end()) - return MdnsResponderErrorCode::kUnknownError; - auto& txt_queries = interface_entry->second.txt_queries; - auto it = txt_queries.find(service_instance); - if (it == txt_queries.end()) - return MdnsResponderErrorCode::kUnknownError; - - txt_queries.erase(it); - return MdnsResponderErrorCode::kNoError; -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::StopAQuery( - UdpSocket* socket, - const DomainName& domain_name) { - auto interface_entry = queries_.find(socket); - if (interface_entry == queries_.end()) - return MdnsResponderErrorCode::kUnknownError; - auto& a_queries = interface_entry->second.a_queries; - auto it = a_queries.find(domain_name); - if (it == a_queries.end()) - return MdnsResponderErrorCode::kUnknownError; - - a_queries.erase(it); - return MdnsResponderErrorCode::kNoError; -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::StopAaaaQuery( - UdpSocket* socket, - const DomainName& domain_name) { - auto interface_entry = queries_.find(socket); - if (interface_entry == queries_.end()) - return MdnsResponderErrorCode::kUnknownError; - auto& aaaa_queries = interface_entry->second.aaaa_queries; - auto it = aaaa_queries.find(domain_name); - if (it == aaaa_queries.end()) - return MdnsResponderErrorCode::kUnknownError; - - aaaa_queries.erase(it); - return MdnsResponderErrorCode::kNoError; -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::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) { - if (!running_) - return MdnsResponderErrorCode::kUnknownError; - - if (std::find_if(registered_services_.begin(), registered_services_.end(), - [&service_instance, &service_name, - &service_protocol](const RegisteredService& service) { - return service.service_instance == service_instance && - service.service_name == service_name && - service.service_protocol == service_protocol; - }) != registered_services_.end()) { - return MdnsResponderErrorCode::kUnknownError; - } - registered_services_.push_back({service_instance, service_name, - service_protocol, target_host, target_port, - txt_data}); - return MdnsResponderErrorCode::kNoError; -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::DeregisterService( - const std::string& service_instance, - const std::string& service_name, - const std::string& service_protocol) { - if (!running_) - return MdnsResponderErrorCode::kUnknownError; - - auto it = - std::find_if(registered_services_.begin(), registered_services_.end(), - [&service_instance, &service_name, - &service_protocol](const RegisteredService& service) { - return service.service_instance == service_instance && - service.service_name == service_name && - service.service_protocol == service_protocol; - }); - if (it == registered_services_.end()) - return MdnsResponderErrorCode::kUnknownError; - - registered_services_.erase(it); - return MdnsResponderErrorCode::kNoError; -} - -MdnsResponderErrorCode FakeMdnsResponderAdapter::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) { - if (!running_) - return MdnsResponderErrorCode::kUnknownError; - - auto it = - std::find_if(registered_services_.begin(), registered_services_.end(), - [&service_instance, &service_name, - &service_protocol](const RegisteredService& service) { - return service.service_instance == service_instance && - service.service_name == service_name && - service.service_protocol == service_protocol; - }); - if (it == registered_services_.end()) - return MdnsResponderErrorCode::kUnknownError; - - it->txt_data = txt_data; - return MdnsResponderErrorCode::kNoError; -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/impl/testing/fake_mdns_responder_adapter.h b/osp/impl/testing/fake_mdns_responder_adapter.h deleted file mode 100644 index ecdb21cc..00000000 --- a/osp/impl/testing/fake_mdns_responder_adapter.h +++ /dev/null @@ -1,202 +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. - -#ifndef OSP_IMPL_TESTING_FAKE_MDNS_RESPONDER_ADAPTER_H_ -#define OSP_IMPL_TESTING_FAKE_MDNS_RESPONDER_ADAPTER_H_ - -#include <map> -#include <set> -#include <string> -#include <vector> - -#include "osp/impl/discovery/mdns/mdns_responder_adapter.h" - -namespace openscreen { -namespace osp { - -class FakeMdnsResponderAdapter; - -PtrEvent MakePtrEvent(const std::string& service_instance, - const std::string& service_type, - const std::string& service_protocol, - UdpSocket* socket); - -SrvEvent MakeSrvEvent(const std::string& service_instance, - const std::string& service_type, - const std::string& service_protocol, - const std::string& hostname, - uint16_t port, - UdpSocket* socket); - -TxtEvent MakeTxtEvent(const std::string& service_instance, - const std::string& service_type, - const std::string& service_protocol, - const std::vector<std::string>& txt_lines, - UdpSocket* socket); - -AEvent MakeAEvent(const std::string& hostname, - IPAddress address, - UdpSocket* socket); - -AaaaEvent MakeAaaaEvent(const std::string& hostname, - IPAddress address, - UdpSocket* socket); - -void AddEventsForNewService(FakeMdnsResponderAdapter* mdns_responder, - const std::string& service_instance, - const std::string& service_name, - const std::string& service_protocol, - const std::string& hostname, - uint16_t port, - const std::vector<std::string>& txt_lines, - const IPAddress& address, - UdpSocket* socket); - -class FakeMdnsResponderAdapter final : public MdnsResponderAdapter { - public: - struct RegisteredInterface { - InterfaceInfo interface_info; - IPSubnet interface_address; - UdpSocket* socket; - }; - - struct RegisteredService { - std::string service_instance; - std::string service_name; - std::string service_protocol; - DomainName target_host; - uint16_t target_port; - std::map<std::string, std::string> txt_data; - }; - - class LifetimeObserver { - public: - virtual ~LifetimeObserver() = default; - - virtual void OnDestroyed() = 0; - }; - - ~FakeMdnsResponderAdapter() override; - - void SetLifetimeObserver(LifetimeObserver* observer) { observer_ = observer; } - - void AddPtrEvent(PtrEvent&& ptr_event); - void AddSrvEvent(SrvEvent&& srv_event); - void AddTxtEvent(TxtEvent&& txt_event); - void AddAEvent(AEvent&& a_event); - void AddAaaaEvent(AaaaEvent&& aaaa_event); - - const std::vector<RegisteredInterface>& registered_interfaces() { - return registered_interfaces_; - } - const std::vector<RegisteredService>& registered_services() { - return registered_services_; - } - bool ptr_queries_empty() const; - bool srv_queries_empty() const; - bool txt_queries_empty() const; - bool a_queries_empty() const; - bool aaaa_queries_empty() const; - bool running() const { return running_; } - - // UdpSocket::Client overrides. - void OnRead(UdpSocket* socket, ErrorOr<UdpPacket> packet) override; - void OnSendError(UdpSocket* socket, Error error) override; - void OnError(UdpSocket* socket, Error error) override; - void OnBound(UdpSocket* socket) override; - - // MdnsResponderAdapter overrides. - Error Init() override; - void Close() override; - - Error SetHostLabel(const std::string& host_label) override; - - // TODO(btolsch): Reject/OSP_CHECK events that don't match any registered - // interface? - Error RegisterInterface(const InterfaceInfo& interface_info, - const IPSubnet& interface_address, - UdpSocket* socket) override; - Error DeregisterInterface(UdpSocket* socket) override; - - Clock::duration RunTasks() override; - - std::vector<PtrEvent> TakePtrResponses() override; - std::vector<SrvEvent> TakeSrvResponses() override; - std::vector<TxtEvent> TakeTxtResponses() override; - std::vector<AEvent> TakeAResponses() override; - std::vector<AaaaEvent> TakeAaaaResponses() override; - - MdnsResponderErrorCode StartPtrQuery(UdpSocket* socket, - const DomainName& service_type) override; - MdnsResponderErrorCode StartSrvQuery( - UdpSocket* socket, - const DomainName& service_instance) override; - MdnsResponderErrorCode StartTxtQuery( - UdpSocket* socket, - const DomainName& service_instance) override; - MdnsResponderErrorCode StartAQuery(UdpSocket* socket, - const DomainName& domain_name) override; - MdnsResponderErrorCode StartAaaaQuery(UdpSocket* socket, - const DomainName& domain_name) override; - - MdnsResponderErrorCode StopPtrQuery(UdpSocket* socket, - const DomainName& service_type) override; - MdnsResponderErrorCode StopSrvQuery( - UdpSocket* socket, - const DomainName& service_instance) override; - MdnsResponderErrorCode StopTxtQuery( - UdpSocket* socket, - const DomainName& service_instance) override; - MdnsResponderErrorCode StopAQuery(UdpSocket* socket, - const DomainName& domain_name) override; - MdnsResponderErrorCode StopAaaaQuery(UdpSocket* socket, - const DomainName& domain_name) override; - - MdnsResponderErrorCode 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) override; - MdnsResponderErrorCode DeregisterService( - const std::string& service_instance, - const std::string& service_name, - const std::string& service_protocol) override; - MdnsResponderErrorCode 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) override; - - private: - struct InterfaceQueries { - std::set<DomainName, DomainNameComparator> a_queries; - std::set<DomainName, DomainNameComparator> aaaa_queries; - std::set<DomainName, DomainNameComparator> ptr_queries; - std::set<DomainName, DomainNameComparator> srv_queries; - std::set<DomainName, DomainNameComparator> txt_queries; - }; - - bool running_ = false; - LifetimeObserver* observer_ = nullptr; - - std::map<UdpSocket*, InterfaceQueries> queries_; - // NOTE: One of many simplifications here is that there is no cache. This - // means that calling StartQuery, StopQuery, StartQuery will only return an - // event the first time, unless the test also adds the event a second time. - std::vector<PtrEvent> ptr_events_; - std::vector<SrvEvent> srv_events_; - std::vector<TxtEvent> txt_events_; - std::vector<AEvent> a_events_; - std::vector<AaaaEvent> aaaa_events_; - - std::vector<RegisteredInterface> registered_interfaces_; - std::vector<RegisteredService> registered_services_; -}; - -} // namespace osp -} // namespace openscreen - -#endif // OSP_IMPL_TESTING_FAKE_MDNS_RESPONDER_ADAPTER_H_ diff --git a/osp/impl/testing/fake_mdns_responder_adapter_unittest.cc b/osp/impl/testing/fake_mdns_responder_adapter_unittest.cc deleted file mode 100644 index 06cec89a..00000000 --- a/osp/impl/testing/fake_mdns_responder_adapter_unittest.cc +++ /dev/null @@ -1,319 +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/testing/fake_mdns_responder_adapter.h" - -#include "gtest/gtest.h" - -namespace openscreen { -namespace osp { - -namespace { - -constexpr char kTestServiceInstance[] = "turtle"; -constexpr char kTestServiceName[] = "_foo"; -constexpr char kTestServiceProtocol[] = "_udp"; - -UdpSocket* const kDefaultSocket = - reinterpret_cast<UdpSocket*>(static_cast<uintptr_t>(8)); -UdpSocket* const kSecondSocket = - reinterpret_cast<UdpSocket*>(static_cast<uintptr_t>(32)); - -} // namespace - -TEST(FakeMdnsResponderAdapterTest, AQueries) { - FakeMdnsResponderAdapter mdns_responder; - - mdns_responder.Init(); - ASSERT_TRUE(mdns_responder.running()); - auto event = MakeAEvent("alpha", IPAddress{1, 2, 3, 4}, kDefaultSocket); - auto domain_name = event.domain_name; - mdns_responder.AddAEvent(std::move(event)); - - auto a_events = mdns_responder.TakeAResponses(); - EXPECT_TRUE(a_events.empty()); - - auto result = mdns_responder.StartAQuery(kDefaultSocket, domain_name); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - - a_events = mdns_responder.TakeAResponses(); - ASSERT_EQ(1u, a_events.size()); - EXPECT_EQ(domain_name, a_events[0].domain_name); - EXPECT_EQ((IPAddress{1, 2, 3, 4}), a_events[0].address); - - result = mdns_responder.StopAQuery(kDefaultSocket, domain_name); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - - mdns_responder.Close(); - ASSERT_FALSE(mdns_responder.running()); - - mdns_responder.AddAEvent( - MakeAEvent("alpha", IPAddress{1, 2, 3, 4}, kDefaultSocket)); - result = mdns_responder.StartAQuery(kDefaultSocket, domain_name); - EXPECT_NE(MdnsResponderErrorCode::kNoError, result); - a_events = mdns_responder.TakeAResponses(); - EXPECT_TRUE(a_events.empty()); -} - -TEST(FakeMdnsResponderAdapterTest, AaaaQueries) { - FakeMdnsResponderAdapter mdns_responder; - - mdns_responder.Init(); - ASSERT_TRUE(mdns_responder.running()); - auto event = MakeAaaaEvent("alpha", IPAddress{1, 2, 3, 4}, kDefaultSocket); - auto domain_name = event.domain_name; - mdns_responder.AddAaaaEvent(std::move(event)); - - auto aaaa_events = mdns_responder.TakeAaaaResponses(); - EXPECT_TRUE(aaaa_events.empty()); - - auto result = mdns_responder.StartAaaaQuery(kDefaultSocket, domain_name); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - - aaaa_events = mdns_responder.TakeAaaaResponses(); - ASSERT_EQ(1u, aaaa_events.size()); - EXPECT_EQ(domain_name, aaaa_events[0].domain_name); - EXPECT_EQ((IPAddress{1, 2, 3, 4}), aaaa_events[0].address); - - result = mdns_responder.StopAaaaQuery(kDefaultSocket, domain_name); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - - mdns_responder.Close(); - ASSERT_FALSE(mdns_responder.running()); - - mdns_responder.AddAaaaEvent( - MakeAaaaEvent("alpha", IPAddress{1, 2, 3, 4}, kDefaultSocket)); - result = mdns_responder.StartAaaaQuery(kDefaultSocket, domain_name); - EXPECT_NE(MdnsResponderErrorCode::kNoError, result); - aaaa_events = mdns_responder.TakeAaaaResponses(); - EXPECT_TRUE(aaaa_events.empty()); -} - -TEST(FakeMdnsResponderAdapterTest, PtrQueries) { - const DomainName kTestServiceType{ - {4, '_', 'f', 'o', 'o', 4, '_', 'u', 'd', 'p', 0}}; - const DomainName kTestServiceTypeCanon{{4, '_', 'f', 'o', 'o', 4, '_', 'u', - 'd', 'p', 5, 'l', 'o', 'c', 'a', 'l', - 0}}; - - FakeMdnsResponderAdapter mdns_responder; - - mdns_responder.Init(); - ASSERT_TRUE(mdns_responder.running()); - mdns_responder.AddPtrEvent( - MakePtrEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - kDefaultSocket)); - - auto ptr_events = mdns_responder.TakePtrResponses(); - EXPECT_TRUE(ptr_events.empty()); - - auto result = mdns_responder.StartPtrQuery(kDefaultSocket, kTestServiceType); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - - ptr_events = mdns_responder.TakePtrResponses(); - ASSERT_EQ(1u, ptr_events.size()); - auto labels = ptr_events[0].service_instance.GetLabels(); - EXPECT_EQ(kTestServiceInstance, labels[0]); - - // TODO(btolsch): qname if PtrEvent gets it. - ErrorOr<DomainName> st = - DomainName::FromLabels(labels.begin() + 1, labels.end()); - ASSERT_TRUE(st); - EXPECT_EQ(kTestServiceTypeCanon, st.value()); - - result = mdns_responder.StopPtrQuery(kDefaultSocket, kTestServiceType); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - - mdns_responder.Close(); - ASSERT_FALSE(mdns_responder.running()); - - mdns_responder.AddPtrEvent( - MakePtrEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - kDefaultSocket)); - result = mdns_responder.StartPtrQuery(kDefaultSocket, kTestServiceType); - EXPECT_NE(MdnsResponderErrorCode::kNoError, result); - ptr_events = mdns_responder.TakePtrResponses(); - EXPECT_TRUE(ptr_events.empty()); -} - -TEST(FakeMdnsResponderAdapterTest, SrvQueries) { - FakeMdnsResponderAdapter mdns_responder; - - mdns_responder.Init(); - ASSERT_TRUE(mdns_responder.running()); - - auto event = - MakeSrvEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - "alpha", 12345, kDefaultSocket); - auto service_instance = event.service_instance; - auto domain_name = event.domain_name; - mdns_responder.AddSrvEvent(std::move(event)); - - auto srv_events = mdns_responder.TakeSrvResponses(); - EXPECT_TRUE(srv_events.empty()); - - auto result = mdns_responder.StartSrvQuery(kDefaultSocket, service_instance); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - - srv_events = mdns_responder.TakeSrvResponses(); - ASSERT_EQ(1u, srv_events.size()); - EXPECT_EQ(service_instance, srv_events[0].service_instance); - EXPECT_EQ(domain_name, srv_events[0].domain_name); - EXPECT_EQ(12345, srv_events[0].port); - - result = mdns_responder.StopSrvQuery(kDefaultSocket, service_instance); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - - mdns_responder.Close(); - ASSERT_FALSE(mdns_responder.running()); - - mdns_responder.AddSrvEvent( - MakeSrvEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - "alpha", 12345, kDefaultSocket)); - result = mdns_responder.StartSrvQuery(kDefaultSocket, service_instance); - EXPECT_NE(MdnsResponderErrorCode::kNoError, result); - srv_events = mdns_responder.TakeSrvResponses(); - EXPECT_TRUE(srv_events.empty()); -} - -TEST(FakeMdnsResponderAdapterTest, TxtQueries) { - FakeMdnsResponderAdapter mdns_responder; - - mdns_responder.Init(); - ASSERT_TRUE(mdns_responder.running()); - - const auto txt_lines = std::vector<std::string>{"asdf", "jkl;", "j"}; - auto event = MakeTxtEvent(kTestServiceInstance, kTestServiceName, - kTestServiceProtocol, txt_lines, kDefaultSocket); - auto service_instance = event.service_instance; - mdns_responder.AddTxtEvent(std::move(event)); - - auto txt_events = mdns_responder.TakeTxtResponses(); - EXPECT_TRUE(txt_events.empty()); - - auto result = mdns_responder.StartTxtQuery(kDefaultSocket, service_instance); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - - txt_events = mdns_responder.TakeTxtResponses(); - ASSERT_EQ(1u, txt_events.size()); - EXPECT_EQ(service_instance, txt_events[0].service_instance); - EXPECT_EQ(txt_lines, txt_events[0].txt_info); - - result = mdns_responder.StopTxtQuery(kDefaultSocket, service_instance); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - - mdns_responder.Close(); - ASSERT_FALSE(mdns_responder.running()); - - mdns_responder.AddTxtEvent( - MakeTxtEvent(kTestServiceInstance, kTestServiceName, kTestServiceProtocol, - txt_lines, kDefaultSocket)); - result = mdns_responder.StartTxtQuery(kDefaultSocket, service_instance); - EXPECT_NE(MdnsResponderErrorCode::kNoError, result); - txt_events = mdns_responder.TakeTxtResponses(); - EXPECT_TRUE(txt_events.empty()); -} - -TEST(FakeMdnsResponderAdapterTest, RegisterServices) { - FakeMdnsResponderAdapter mdns_responder; - - mdns_responder.Init(); - ASSERT_TRUE(mdns_responder.running()); - - auto result = mdns_responder.RegisterService( - "instance", "name", "proto", - DomainName{{1, 'a', 5, 'l', 'o', 'c', 'a', 'l', 0}}, 12345, - {{"k1", "asdf"}, {"k2", "jkl"}}); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - EXPECT_EQ(1u, mdns_responder.registered_services().size()); - - result = mdns_responder.RegisterService( - "instance2", "name", "proto", - DomainName{{1, 'b', 5, 'l', 'o', 'c', 'a', 'l', 0}}, 12346, - {{"k1", "asdf"}, {"k2", "jkl"}}); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - EXPECT_EQ(2u, mdns_responder.registered_services().size()); - - result = mdns_responder.DeregisterService("instance", "name", "proto"); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - result = mdns_responder.DeregisterService("instance", "name", "proto"); - EXPECT_NE(MdnsResponderErrorCode::kNoError, result); - EXPECT_EQ(1u, mdns_responder.registered_services().size()); - - mdns_responder.Close(); - ASSERT_FALSE(mdns_responder.running()); - EXPECT_EQ(0u, mdns_responder.registered_services().size()); - - result = mdns_responder.RegisterService( - "instance2", "name", "proto", - DomainName{{1, 'b', 5, 'l', 'o', 'c', 'a', 'l', 0}}, 12346, - {{"k1", "asdf"}, {"k2", "jkl"}}); - EXPECT_NE(MdnsResponderErrorCode::kNoError, result); - EXPECT_EQ(0u, mdns_responder.registered_services().size()); -} - -TEST(FakeMdnsResponderAdapterTest, RegisterInterfaces) { - FakeMdnsResponderAdapter mdns_responder; - - mdns_responder.Init(); - ASSERT_TRUE(mdns_responder.running()); - EXPECT_EQ(0u, mdns_responder.registered_interfaces().size()); - - Error result = mdns_responder.RegisterInterface(InterfaceInfo{}, IPSubnet{}, - kDefaultSocket); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(1u, mdns_responder.registered_interfaces().size()); - - result = mdns_responder.RegisterInterface(InterfaceInfo{}, IPSubnet{}, - kDefaultSocket); - EXPECT_FALSE(result.ok()); - EXPECT_EQ(1u, mdns_responder.registered_interfaces().size()); - - result = mdns_responder.RegisterInterface(InterfaceInfo{}, IPSubnet{}, - kSecondSocket); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(2u, mdns_responder.registered_interfaces().size()); - - result = mdns_responder.DeregisterInterface(kSecondSocket); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(1u, mdns_responder.registered_interfaces().size()); - result = mdns_responder.DeregisterInterface(kSecondSocket); - EXPECT_FALSE(result.ok()); - EXPECT_EQ(1u, mdns_responder.registered_interfaces().size()); - - mdns_responder.Close(); - ASSERT_FALSE(mdns_responder.running()); - EXPECT_EQ(0u, mdns_responder.registered_interfaces().size()); - - result = mdns_responder.RegisterInterface(InterfaceInfo{}, IPSubnet{}, - kDefaultSocket); - EXPECT_FALSE(result.ok()); - EXPECT_EQ(0u, mdns_responder.registered_interfaces().size()); -} - -TEST(FakeMdnsResponderAdapterTest, UpdateTxtData) { - FakeMdnsResponderAdapter mdns_responder; - - mdns_responder.Init(); - ASSERT_TRUE(mdns_responder.running()); - - const std::map<std::string, std::string> txt_data1{{"k1", "asdf"}, - {"k2", "jkl"}}; - auto result = mdns_responder.RegisterService( - "instance", "name", "proto", - DomainName{{1, 'a', 5, 'l', 'o', 'c', 'a', 'l', 0}}, 12345, txt_data1); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - ASSERT_EQ(1u, mdns_responder.registered_services().size()); - EXPECT_EQ(txt_data1, mdns_responder.registered_services()[0].txt_data); - - const std::map<std::string, std::string> txt_data2{ - {"k1", "monkey"}, {"k2", "panda"}, {"k3", "turtle"}, {"k4", "rhino"}}; - result = mdns_responder.UpdateTxtData("instance", "name", "proto", txt_data2); - EXPECT_EQ(MdnsResponderErrorCode::kNoError, result); - ASSERT_EQ(1u, mdns_responder.registered_services().size()); - EXPECT_EQ(txt_data2, mdns_responder.registered_services()[0].txt_data); -} - -} // namespace osp -} // namespace openscreen diff --git a/osp/public/BUILD.gn b/osp/public/BUILD.gn index cc915c60..3606dcd5 100644 --- a/osp/public/BUILD.gn +++ b/osp/public/BUILD.gn @@ -11,7 +11,6 @@ source_set("public") { "endpoint_request_ids.cc", "endpoint_request_ids.h", "mdns_service_listener_factory.h", - "mdns_service_publisher_factory.h", "message_demuxer.h", "network_metrics.h", "network_service_manager.h", @@ -35,6 +34,7 @@ source_set("public") { "service_listener.h", "service_publisher.cc", "service_publisher.h", + "service_publisher_factory.h", "timestamp.h", ] diff --git a/osp/public/mdns_service_listener_factory.h b/osp/public/mdns_service_listener_factory.h index 663d060f..fa33f54f 100644 --- a/osp/public/mdns_service_listener_factory.h +++ b/osp/public/mdns_service_listener_factory.h @@ -8,6 +8,7 @@ #include <memory> #include "osp/public/service_listener.h" +#include "util/osp_logging.h" namespace openscreen { @@ -25,7 +26,9 @@ class MdnsServiceListenerFactory { static std::unique_ptr<ServiceListener> Create( const MdnsServiceListenerConfig& config, ServiceListener::Observer* observer, - TaskRunner* task_runner); + TaskRunner* task_runner) { + OSP_NOTREACHED(); + } }; } // namespace osp diff --git a/osp/public/network_service_manager.h b/osp/public/network_service_manager.h index 8289e178..b1a225c4 100644 --- a/osp/public/network_service_manager.h +++ b/osp/public/network_service_manager.h @@ -28,7 +28,7 @@ class NetworkServiceManager final { // be passed for services not provided by the embedder. static NetworkServiceManager* Create( std::unique_ptr<ServiceListener> mdns_listener, - std::unique_ptr<ServicePublisher> mdns_publisher, + std::unique_ptr<ServicePublisher> service_publisher, std::unique_ptr<ProtocolConnectionClient> connection_client, std::unique_ptr<ProtocolConnectionServer> connection_server); @@ -47,7 +47,7 @@ class NetworkServiceManager final { // Returns an instance of the mDNS receiver publisher, or nullptr if not // provided. - ServicePublisher* GetMdnsServicePublisher(); + ServicePublisher* GetServicePublisher(); // Returns an instance of the protocol connection client, or nullptr if not // provided. @@ -60,14 +60,14 @@ class NetworkServiceManager final { private: NetworkServiceManager( std::unique_ptr<ServiceListener> mdns_listener, - std::unique_ptr<ServicePublisher> mdns_publisher, + std::unique_ptr<ServicePublisher> service_publisher, std::unique_ptr<ProtocolConnectionClient> connection_client, std::unique_ptr<ProtocolConnectionServer> connection_server); ~NetworkServiceManager(); std::unique_ptr<ServiceListener> mdns_listener_; - std::unique_ptr<ServicePublisher> mdns_publisher_; + std::unique_ptr<ServicePublisher> service_publisher_; std::unique_ptr<ProtocolConnectionClient> connection_client_; std::unique_ptr<ProtocolConnectionServer> connection_server_; }; diff --git a/osp/public/service_info.h b/osp/public/service_info.h index e486052c..7c95ff2e 100644 --- a/osp/public/service_info.h +++ b/osp/public/service_info.h @@ -14,6 +14,8 @@ namespace openscreen { namespace osp { +constexpr char kOpenScreenServiceName[] = "_openscreen._udp"; + // This contains canonical information about a specific Open Screen service // found on the network via our discovery mechanism (mDNS). struct ServiceInfo { diff --git a/osp/public/service_publisher.cc b/osp/public/service_publisher.cc index 3a8e70b7..2268f2d1 100644 --- a/osp/public/service_publisher.cc +++ b/osp/public/service_publisher.cc @@ -7,26 +7,25 @@ namespace openscreen { namespace osp { -ServicePublisherError::ServicePublisherError() = default; -ServicePublisherError::ServicePublisherError(Code error, - const std::string& message) - : error(error), message(message) {} -ServicePublisherError::ServicePublisherError( - const ServicePublisherError& other) = default; -ServicePublisherError::~ServicePublisherError() = default; - -ServicePublisherError& ServicePublisherError::operator=( - const ServicePublisherError& other) = default; - ServicePublisher::Metrics::Metrics() = default; ServicePublisher::Metrics::~Metrics() = default; ServicePublisher::Config::Config() = default; ServicePublisher::Config::~Config() = default; +bool ServicePublisher::Config::IsValid() const { + return !friendly_name.empty() && !service_instance_name.empty() && + connection_server_port > 0 && !network_interfaces.empty(); +} + +ServicePublisher::~ServicePublisher() = default; + +void ServicePublisher::SetConfig(const Config& config) { + config_ = config; +} + ServicePublisher::ServicePublisher(Observer* observer) : state_(State::kStopped), observer_(observer) {} -ServicePublisher::~ServicePublisher() = default; } // namespace osp } // namespace openscreen diff --git a/osp/public/service_publisher.h b/osp/public/service_publisher.h index b31f59fc..190d22df 100644 --- a/osp/public/service_publisher.h +++ b/osp/public/service_publisher.h @@ -10,30 +10,13 @@ #include <vector> #include "osp/public/timestamp.h" -#include "platform/api/network_interface.h" +#include "platform/base/error.h" +#include "platform/base/interface_info.h" #include "platform/base/macros.h" namespace openscreen { namespace osp { -// Used to report an error from a ServiceListener implementation. -struct ServicePublisherError { - // TODO(mfoltz): Add additional error types, as implementations progress. - enum class Code { - kNone = 0, - }; - - ServicePublisherError(); - ServicePublisherError(Code error, const std::string& message); - ServicePublisherError(const ServicePublisherError& other); - ~ServicePublisherError(); - - ServicePublisherError& operator=(const ServicePublisherError& other); - - Code error; - std::string message; -}; - class ServicePublisher { public: enum class State { @@ -74,7 +57,7 @@ class ServicePublisher { virtual void OnSuspended() = 0; // Reports an error. - virtual void OnError(ServicePublisherError) = 0; + virtual void OnError(Error) = 0; // Reports metrics. virtual void OnMetrics(Metrics) = 0; @@ -103,15 +86,21 @@ class ServicePublisher { // configured in the ProtocolConnectionServer. uint16_t connection_server_port = 0; - // A list of network interface names that the publisher should use. + // A list of network interfaces that the publisher should use. // By default, all enabled Ethernet and WiFi interfaces are used. // This configuration must be identical to the interfaces configured // in the ScreenConnectionServer. - std::vector<NetworkInterfaceIndex> network_interface_indices; + std::vector<InterfaceInfo> network_interfaces; + + // Returns true if the config object is valid. + bool IsValid() const; }; virtual ~ServicePublisher(); + // Sets the service configuration for this publisher. + virtual void SetConfig(const Config& config); + // Starts publishing this service using the config object. // Returns true if state() == kStopped and the service will be started, false // otherwise. @@ -139,14 +128,15 @@ class ServicePublisher { State state() const { return state_; } // Returns the last error reported by this publisher. - ServicePublisherError last_error() const { return last_error_; } + Error last_error() const { return last_error_; } protected: explicit ServicePublisher(Observer* observer); State state_; - ServicePublisherError last_error_; + Error last_error_; Observer* observer_; + Config config_; OSP_DISALLOW_COPY_AND_ASSIGN(ServicePublisher); }; diff --git a/osp/public/mdns_service_publisher_factory.h b/osp/public/service_publisher_factory.h index 075137a7..93193d03 100644 --- a/osp/public/mdns_service_publisher_factory.h +++ b/osp/public/service_publisher_factory.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef OSP_PUBLIC_MDNS_SERVICE_PUBLISHER_FACTORY_H_ -#define OSP_PUBLIC_MDNS_SERVICE_PUBLISHER_FACTORY_H_ +#ifndef OSP_PUBLIC_SERVICE_PUBLISHER_FACTORY_H_ +#define OSP_PUBLIC_SERVICE_PUBLISHER_FACTORY_H_ #include <memory> @@ -15,7 +15,7 @@ class TaskRunner; namespace osp { -class MdnsServicePublisherFactory { +class ServicePublisherFactory { public: static std::unique_ptr<ServicePublisher> Create( const ServicePublisher::Config& config, @@ -26,4 +26,4 @@ class MdnsServicePublisherFactory { } // namespace osp } // namespace openscreen -#endif // OSP_PUBLIC_MDNS_SERVICE_PUBLISHER_FACTORY_H_ +#endif // OSP_PUBLIC_SERVICE_PUBLISHER_FACTORY_H_ diff --git a/third_party/mDNSResponder/BUILD.gn b/third_party/mDNSResponder/BUILD.gn deleted file mode 100644 index bb0047ee..00000000 --- a/third_party/mDNSResponder/BUILD.gn +++ /dev/null @@ -1,38 +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. - -config("mdnsresponder_config") { - cflags = [ "-w" ] # Disable all warnings. - - cflags_c = [ - # We need to rename some linked symbols in order to avoid multiple - # definitions. - "-DMD5_Update=MD5_Update_mDNS", - "-DMD5_Init=MD5_Init_mDNS", - "-DMD5_Final=MD5_Final_mDNS", - "-DMD5_Transform=MD5_Transform_mDNS", - ] -} - -source_set("core") { - sources = [ - "src/mDNSCore/DNSCommon.c", - "src/mDNSCore/DNSCommon.h", - "src/mDNSCore/DNSDigest.c", - "src/mDNSCore/mDNS.c", - "src/mDNSCore/mDNSDebug.h", - "src/mDNSCore/mDNSEmbeddedAPI.h", - "src/mDNSCore/uDNS.c", - "src/mDNSCore/uDNS.h", - "src/mDNSShared/mDNSDebug.c", - ] - - configs += [ ":mdnsresponder_config" ] - - if (is_debug) { - defines = [ "MDNS_DEBUGMSGS=2" ] - } - - include_dirs = [ "src/mDNSCore" ] -} diff --git a/third_party/mDNSResponder/README.chromium b/third_party/mDNSResponder/README.chromium deleted file mode 100644 index 77469188..00000000 --- a/third_party/mDNSResponder/README.chromium +++ /dev/null @@ -1,10 +0,0 @@ -Name: mDNSResponder -URL: https://github.com/jevinskie/mDNSResponder -License: Apache License, Version 2.0 -License File: src/LICENSE -Security Critical: no - -Description: - -Pull from Apple Bonjour's MDNS/DNS-SD implementation. Will eventually be -replaced with our custom implementation, currently only used in osp. |