Implement MULTICAST_DNS source for new HostResolver API.
Only used when explicitly requested. The MDnsClient is created and
starts listening for MDNS messages at first use in HostResolverImpl.
For now supports only a basic feature set, eg we'll DCHECK if canonname
is requested (in contrast to the async resolver behavior that was
unnoticed until recently due to the lack of a DCHECK).
Bug: 846423
Cq-Include-Trybots: luci.chromium.try:linux_mojo
Change-Id: Ib296ac432d392baee2454587693b2537513f461c
Reviewed-on: https://chromium-review.googlesource.com/1211542
Reviewed-by: Matt Menke <[email protected]>
Reviewed-by: Tom Sepez <[email protected]>
Commit-Queue: Eric Orth <[email protected]>
Cr-Commit-Position: refs/heads/master@{#595956}
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 3d29b9d2..cdad914 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -64,7 +64,9 @@
#include "net/dns/dns_response.h"
#include "net/dns/dns_transaction.h"
#include "net/dns/dns_util.h"
+#include "net/dns/host_resolver_mdns_task.h"
#include "net/dns/host_resolver_proc.h"
+#include "net/dns/mdns_client.h"
#include "net/log/net_log.h"
#include "net/log/net_log_capture_mode.h"
#include "net/log/net_log_event_type.h"
@@ -76,6 +78,10 @@
#include "net/socket/datagram_client_socket.h"
#include "url/url_canon_ip.h"
+#if BUILDFLAG(ENABLE_MDNS)
+#include "net/dns/mdns_client_impl.h"
+#endif
+
#if defined(OS_WIN)
#include "net/base/winsock_init.h"
#endif
@@ -1512,7 +1518,7 @@
// This will destroy the Job.
CompleteRequests(
MakeCacheEntry(OK, addr_list, HostCache::Entry::SOURCE_HOSTS),
- base::TimeDelta());
+ base::TimeDelta(), true /* allow_cache */);
return true;
}
return false;
@@ -1525,7 +1531,7 @@
}
bool is_running() const {
- return is_dns_running() || is_proc_running();
+ return is_dns_running() || is_mdns_running() || is_proc_running();
}
private:
@@ -1617,7 +1623,8 @@
switch (key_.host_resolver_source) {
case HostResolverSource::ANY:
if (resolver_->HaveDnsConfig() &&
- !ResemblesMulticastDNSName(key_.hostname)) {
+ !ResemblesMulticastDNSName(key_.hostname) &&
+ !(key_.host_resolver_flags & HOST_RESOLVER_CANONNAME)) {
StartDnsTask();
} else {
StartProcTask();
@@ -1633,6 +1640,9 @@
StartDnsTask();
break;
+ case HostResolverSource::MULTICAST_DNS:
+ StartMdnsTask();
+ break;
}
// Caution: Job::Start must not complete synchronously.
@@ -1643,7 +1653,7 @@
// TaskScheduler threads low, we will need to use an "inner"
// PrioritizedDispatcher with tighter limits.
void StartProcTask() {
- DCHECK(!is_dns_running());
+ DCHECK(!is_running());
proc_task_ = std::make_unique<ProcTask>(
key_, resolver_->proc_params_,
base::BindOnce(&Job::OnProcTaskComplete, base::Unretained(this),
@@ -1693,7 +1703,7 @@
// Don't store the |ttl| in cache since it's not obtained from the server.
CompleteRequests(
MakeCacheEntry(net_error, addr_list, HostCache::Entry::SOURCE_UNKNOWN),
- ttl);
+ ttl, true /* allow_cache */);
}
void StartDnsTask() {
@@ -1751,7 +1761,7 @@
CompleteRequests(
HostCache::Entry(net_error, AddressList(),
HostCache::Entry::Source::SOURCE_UNKNOWN, ttl),
- ttl);
+ ttl, true /* allow_cache */);
}
}
@@ -1784,7 +1794,7 @@
} else {
CompleteRequests(MakeCacheEntryWithTTL(net_error, addr_list,
HostCache::Entry::SOURCE_DNS, ttl),
- bounded_ttl);
+ bounded_ttl, true /* allow_cache */);
}
}
@@ -1801,6 +1811,50 @@
dns_task_->StartSecondTransaction();
}
+ void StartMdnsTask() {
+ DCHECK(!is_running());
+
+ // No flags are supported for MDNS except
+ // HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6 (which is not actually an
+ // input flag).
+ DCHECK_EQ(0, key_.host_resolver_flags &
+ ~HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6);
+
+ std::vector<HostResolver::DnsQueryType> query_types;
+ switch (key_.address_family) {
+ case ADDRESS_FAMILY_UNSPECIFIED:
+ query_types.push_back(HostResolver::DnsQueryType::A);
+ query_types.push_back(HostResolver::DnsQueryType::AAAA);
+ break;
+ case ADDRESS_FAMILY_IPV4:
+ query_types.push_back(HostResolver::DnsQueryType::A);
+ break;
+ case ADDRESS_FAMILY_IPV6:
+ query_types.push_back(HostResolver::DnsQueryType::AAAA);
+ break;
+ }
+
+ mdns_task_ = std::make_unique<HostResolverMdnsTask>(
+ resolver_->GetOrCreateMdnsClient(), key_.hostname, query_types);
+ mdns_task_->Start(
+ base::BindOnce(&Job::OnMdnsTaskComplete, base::Unretained(this)));
+ }
+
+ void OnMdnsTaskComplete(int error) {
+ DCHECK(is_mdns_running());
+ // TODO(crbug.com/846423): Consider adding MDNS-specific logging.
+
+ if (error != OK) {
+ CompleteRequestsWithError(error);
+ } else if (ContainsIcannNameCollisionIp(mdns_task_->result_addresses())) {
+ CompleteRequestsWithError(ERR_ICANN_NAME_COLLISION);
+ } else {
+ // MDNS uses a separate cache, so skip saving result to cache.
+ // TODO(crbug.com/846423): Consider merging caches.
+ CompleteRequestsWithoutCache(error, mdns_task_->result_addresses());
+ }
+ }
+
URLRequestContext* url_request_context() override {
return resolver_->url_request_context_;
}
@@ -1880,8 +1934,12 @@
}
// Performs Job's last rites. Completes all Requests. Deletes this.
+ //
+ // If not |allow_cache|, result will not be stored in the host cache, even if
+ // result would otherwise allow doing so.
void CompleteRequests(const HostCache::Entry& entry,
- base::TimeDelta ttl) {
+ base::TimeDelta ttl,
+ bool allow_cache) {
CHECK(resolver_.get());
// This job must be removed from resolver's |jobs_| now to make room for a
@@ -1893,6 +1951,7 @@
if (is_running()) {
proc_task_ = nullptr;
KillDnsTask();
+ mdns_task_ = nullptr;
// Signal dispatcher that a slot has opened.
resolver_->dispatcher_->OnJobFinished();
@@ -1922,7 +1981,7 @@
bool did_complete = (entry.error() != ERR_NETWORK_CHANGED) &&
(entry.error() != ERR_HOST_RESOLVER_QUEUE_TOO_LARGE);
- if (did_complete)
+ if (did_complete && allow_cache)
resolver_->CacheResult(key_, entry, ttl);
RecordJobHistograms(entry.error());
@@ -1954,11 +2013,17 @@
}
}
+ void CompleteRequestsWithoutCache(int error, const AddressList& addresses) {
+ CompleteRequests(
+ MakeCacheEntry(error, addresses, HostCache::Entry::SOURCE_UNKNOWN),
+ base::TimeDelta(), false /* allow_cache */);
+ }
+
// Convenience wrapper for CompleteRequests in case of failure.
void CompleteRequestsWithError(int net_error) {
CompleteRequests(HostCache::Entry(net_error, AddressList(),
HostCache::Entry::SOURCE_UNKNOWN),
- base::TimeDelta());
+ base::TimeDelta(), true /* allow_cache */);
}
RequestPriority priority() const override {
@@ -1972,6 +2037,8 @@
bool is_dns_running() const { return !!dns_task_; }
+ bool is_mdns_running() const { return !!mdns_task_; }
+
bool is_proc_running() const { return !!proc_task_; }
base::WeakPtr<HostResolverImpl> resolver_;
@@ -2005,6 +2072,9 @@
// Resolves the host using a DnsTransaction.
std::unique_ptr<DnsTask> dns_task_;
+ // Resolves the host using MDnsClient.
+ std::unique_ptr<HostResolverMdnsTask> mdns_task_;
+
// All Requests waiting for the result of this Job. Some can be canceled.
base::LinkedList<RequestImpl> requests_;
@@ -2311,6 +2381,17 @@
}
}
+void HostResolverImpl::SetMdnsSocketFactoryForTesting(
+ std::unique_ptr<MDnsSocketFactory> socket_factory) {
+ DCHECK(!mdns_client_);
+ mdns_socket_factory_ = std::move(socket_factory);
+}
+
+void HostResolverImpl::SetMdnsClientForTesting(
+ std::unique_ptr<MDnsClient> client) {
+ mdns_client_ = std::move(client);
+}
+
void HostResolverImpl::SetTaskRunnerForTesting(
scoped_refptr<base::TaskRunner> task_runner) {
proc_task_runner_ = std::move(task_runner);
@@ -2321,6 +2402,11 @@
DCHECK(!request->job());
// Request may only be resolved once.
DCHECK(!request->complete());
+ // MDNS requests do not support skipping cache.
+ // TODO(crbug.com/846423): Either add support for skipping the MDNS cache, or
+ // merge to use the normal host cache for MDNS requests.
+ DCHECK(request->parameters().source != HostResolverSource::MULTICAST_DNS ||
+ request->parameters().allow_cached_response);
request->set_request_time(tick_clock_->NowTicks());
@@ -2866,6 +2952,25 @@
std::abs(net_error));
}
+MDnsClient* HostResolverImpl::GetOrCreateMdnsClient() {
+#if BUILDFLAG(ENABLE_MDNS)
+ if (!mdns_client_) {
+ if (!mdns_socket_factory_)
+ mdns_socket_factory_ = std::make_unique<MDnsSocketFactoryImpl>(net_log_);
+
+ mdns_client_ = MDnsClient::CreateDefault();
+ mdns_client_->StartListening(mdns_socket_factory_.get());
+ }
+
+ DCHECK(mdns_client_->IsListening());
+ return mdns_client_.get();
+#else
+ // Should not request MDNS resoltuion unless MDNS is enabled.
+ NOTREACHED();
+ return nullptr;
+#endif
+}
+
HostResolverImpl::RequestImpl::~RequestImpl() {
if (job_)
job_->CancelRequest(this);