From e6e2f280ff2ba35f417b03286dc09fd3861e844b Mon Sep 17 00:00:00 2001 From: Harald Alvestrand Date: Wed, 24 Mar 2021 12:13:28 +0000 Subject: [PATCH] Add a new API to DNS resolution This API should allow existing factories to be used unmodified, but offers a new API that documents ownership better and does not use sigslot. Bug: webrtc:12598 Change-Id: I0f68371059cd4a18ab07b87fc0e7526dcc0ac669 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/212609 Reviewed-by: Tommi Commit-Queue: Harald Alvestrand Cr-Commit-Position: refs/heads/master@{#33553} --- api/BUILD.gn | 2 + api/DEPS | 4 + api/async_dns_resolver.h | 85 +++++++++++++++++ p2p/base/basic_async_resolver_factory.cc | 94 +++++++++++++++++++ p2p/base/basic_async_resolver_factory.h | 26 ++++- .../basic_async_resolver_factory_unittest.cc | 14 +++ 6 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 api/async_dns_resolver.h diff --git a/api/BUILD.gn b/api/BUILD.gn index 1e4bac22ff..4274014e96 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn @@ -249,12 +249,14 @@ rtc_library("rtc_error") { rtc_source_set("packet_socket_factory") { visibility = [ "*" ] sources = [ + "async_dns_resolver.h", "async_resolver_factory.h", "packet_socket_factory.h", ] deps = [ "../rtc_base:async_resolver_interface", "../rtc_base:rtc_base", + "../rtc_base:socket_address", "../rtc_base/system:rtc_export", ] } diff --git a/api/DEPS b/api/DEPS index bf814e82d3..9665938a5d 100644 --- a/api/DEPS +++ b/api/DEPS @@ -63,6 +63,10 @@ specific_include_rules = { "+rtc_base/async_resolver_interface.h", ], + "async_dns_resolver\.h": [ + "+rtc_base/socket_address.h", + ], + "candidate\.h": [ "+rtc_base/network_constants.h", "+rtc_base/socket_address.h", diff --git a/api/async_dns_resolver.h b/api/async_dns_resolver.h new file mode 100644 index 0000000000..800ffa613d --- /dev/null +++ b/api/async_dns_resolver.h @@ -0,0 +1,85 @@ +/* + * Copyright 2021 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_ASYNC_DNS_RESOLVER_H_ +#define API_ASYNC_DNS_RESOLVER_H_ + +#include + +#include "rtc_base/socket_address.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// This interface defines the methods to resolve a hostname asynchronously. +// The AsyncDnsResolverInterface class encapsulates a single name query. +// +// Usage: +// std::unique_ptr resolver = +// factory->Create(address-to-be-resolved, [r = resolver.get()]() { +// if (r->result.GetResolvedAddress(AF_INET, &addr) { +// // success +// } else { +// // failure +// error = r->result().GetError(); +// } +// // Release resolver. +// resolver_list.erase(std::remove_if(resolver_list.begin(), +// resolver_list.end(), +// [](refptr) { refptr.get() == r; }); +// }); +// resolver_list.push_back(std::move(resolver)); + +class AsyncDnsResolverResult { + public: + virtual ~AsyncDnsResolverResult() = default; + // Returns true iff the address from |Start| was successfully resolved. + // If the address was successfully resolved, sets |addr| to a copy of the + // address from |Start| with the IP address set to the top most resolved + // address of |family| (|addr| will have both hostname and the resolved ip). + virtual bool GetResolvedAddress(int family, + rtc::SocketAddress* addr) const = 0; + // Returns error from resolver. + virtual int GetError() const = 0; +}; + +class RTC_EXPORT AsyncDnsResolverInterface { + public: + virtual ~AsyncDnsResolverInterface() = default; + + // Start address resolution of the hostname in |addr|. + virtual void Start(const rtc::SocketAddress& addr, + std::function callback) = 0; + virtual const AsyncDnsResolverResult& result() const = 0; +}; + +// An abstract factory for creating AsyncDnsResolverInterfaces. This allows +// client applications to provide WebRTC with their own mechanism for +// performing DNS resolution. +class AsyncDnsResolverFactoryInterface { + public: + virtual ~AsyncDnsResolverFactoryInterface() = default; + + // Creates an AsyncDnsResolver and starts resolving the name. The callback + // will be called when resolution is finished. + // The callback will be called on the thread that the caller runs on. + virtual std::unique_ptr CreateAndResolve( + const rtc::SocketAddress& addr, + std::function callback) = 0; + // Creates an AsyncDnsResolver and does not start it. + // For backwards compatibility, will be deprecated and removed. + // One has to do a separate Start() call on the + // resolver to start name resolution. + virtual std::unique_ptr Create() = 0; +}; + +} // namespace webrtc + +#endif // API_ASYNC_DNS_RESOLVER_H_ diff --git a/p2p/base/basic_async_resolver_factory.cc b/p2p/base/basic_async_resolver_factory.cc index 027358b515..67dd18f5f2 100644 --- a/p2p/base/basic_async_resolver_factory.cc +++ b/p2p/base/basic_async_resolver_factory.cc @@ -10,6 +10,11 @@ #include "p2p/base/basic_async_resolver_factory.h" +#include +#include + +#include "absl/memory/memory.h" +#include "api/async_dns_resolver.h" #include "rtc_base/async_resolver.h" namespace webrtc { @@ -18,4 +23,93 @@ rtc::AsyncResolverInterface* BasicAsyncResolverFactory::Create() { return new rtc::AsyncResolver(); } +class WrappingAsyncDnsResolver; + +class WrappingAsyncDnsResolverResult : public AsyncDnsResolverResult { + public: + explicit WrappingAsyncDnsResolverResult(WrappingAsyncDnsResolver* owner) + : owner_(owner) {} + ~WrappingAsyncDnsResolverResult() {} + + // Note: Inline declaration not possible, since it refers to + // WrappingAsyncDnsResolver. + bool GetResolvedAddress(int family, rtc::SocketAddress* addr) const override; + int GetError() const override; + + private: + WrappingAsyncDnsResolver* const owner_; +}; + +class WrappingAsyncDnsResolver : public AsyncDnsResolverInterface, + public sigslot::has_slots<> { + public: + explicit WrappingAsyncDnsResolver(rtc::AsyncResolverInterface* wrapped) + : wrapped_(absl::WrapUnique(wrapped)), result_(this) {} + + ~WrappingAsyncDnsResolver() override { wrapped_.release()->Destroy(false); } + + void Start(const rtc::SocketAddress& addr, + std::function callback) override { + RTC_DCHECK(state_ == State::kNotStarted); + state_ = State::kStarted; + callback_ = callback; + wrapped_->SignalDone.connect(this, + &WrappingAsyncDnsResolver::OnResolveResult); + wrapped_->Start(addr); + } + + const AsyncDnsResolverResult& result() const override { + RTC_DCHECK(state_ == State::kResolved); + return result_; + } + + // For use by WrappingAsyncDnsResolverResult + rtc::AsyncResolverInterface* wrapped() const { return wrapped_.get(); } + + private: + enum class State { kNotStarted, kStarted, kResolved }; + + void OnResolveResult(rtc::AsyncResolverInterface* ref) { + RTC_DCHECK(state_ == State::kStarted); + RTC_DCHECK_EQ(ref, wrapped_.get()); + state_ = State::kResolved; + callback_(); + } + + std::function callback_; + std::unique_ptr wrapped_; + State state_ = State::kNotStarted; + WrappingAsyncDnsResolverResult result_; +}; + +bool WrappingAsyncDnsResolverResult::GetResolvedAddress( + int family, + rtc::SocketAddress* addr) const { + if (!owner_->wrapped()) { + return false; + } + return owner_->wrapped()->GetResolvedAddress(family, addr); +} + +int WrappingAsyncDnsResolverResult::GetError() const { + if (!owner_->wrapped()) { + return -1; // FIXME: Find a code that makes sense. + } + return owner_->wrapped()->GetError(); +} + +std::unique_ptr +WrappingAsyncDnsResolverFactory::Create() { + return std::make_unique(wrapped_factory_->Create()); +} + +std::unique_ptr +WrappingAsyncDnsResolverFactory::CreateAndResolve( + const rtc::SocketAddress& addr, + std::function callback) { + std::unique_ptr resolver = Create(); + resolver->Start(addr, callback); + return resolver; +} + } // namespace webrtc diff --git a/p2p/base/basic_async_resolver_factory.h b/p2p/base/basic_async_resolver_factory.h index c4661b448b..90427444be 100644 --- a/p2p/base/basic_async_resolver_factory.h +++ b/p2p/base/basic_async_resolver_factory.h @@ -11,16 +11,40 @@ #ifndef P2P_BASE_BASIC_ASYNC_RESOLVER_FACTORY_H_ #define P2P_BASE_BASIC_ASYNC_RESOLVER_FACTORY_H_ +#include +#include +#include + +#include "api/async_dns_resolver.h" #include "api/async_resolver_factory.h" #include "rtc_base/async_resolver_interface.h" namespace webrtc { -class BasicAsyncResolverFactory : public AsyncResolverFactory { +class BasicAsyncResolverFactory final : public AsyncResolverFactory { public: rtc::AsyncResolverInterface* Create() override; }; +// This class wraps a factory using the older webrtc::AsyncResolverFactory API, +// and produces webrtc::AsyncDnsResolver objects that contain an +// rtc::AsyncResolver object. +class WrappingAsyncDnsResolverFactory final + : public AsyncDnsResolverFactoryInterface { + public: + WrappingAsyncDnsResolverFactory( + std::unique_ptr wrapped_factory) + : wrapped_factory_(std::move(wrapped_factory)) {} + std::unique_ptr CreateAndResolve( + const rtc::SocketAddress& addr, + std::function callback) override; + + std::unique_ptr Create() override; + + private: + const std::unique_ptr wrapped_factory_; +}; + } // namespace webrtc #endif // P2P_BASE_BASIC_ASYNC_RESOLVER_FACTORY_H_ diff --git a/p2p/base/basic_async_resolver_factory_unittest.cc b/p2p/base/basic_async_resolver_factory_unittest.cc index 8242146bae..03fb2df8b0 100644 --- a/p2p/base/basic_async_resolver_factory_unittest.cc +++ b/p2p/base/basic_async_resolver_factory_unittest.cc @@ -47,4 +47,18 @@ TEST_F(BasicAsyncResolverFactoryTest, TestCreate) { TestCreate(); } +TEST(WrappingAsyncDnsResolverFactoryTest, TestCreate) { + WrappingAsyncDnsResolverFactory factory( + std::make_unique()); + + std::unique_ptr resolver(factory.Create()); + ASSERT_TRUE(resolver); + + bool address_resolved = false; + rtc::SocketAddress address("", 0); + resolver->Start(address, [&address_resolved]() { address_resolved = true; }); + ASSERT_TRUE_WAIT(address_resolved, 10000 /*ms*/); + resolver.reset(); +} + } // namespace webrtc