blob: 356278b5721c9616df26da90e022e1d2a9785125 [file] [log] [blame]
Eric Orth9871aafa2018-10-02 19:59:181// Copyright 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/dns/host_resolver_mdns_task.h"
6
7#include <algorithm>
8#include <utility>
9
Sebastien Marchand6d0558fd2019-01-25 16:49:3710#include "base/bind.h"
Eric Orth9871aafa2018-10-02 19:59:1811#include "base/logging.h"
Eric Orth828bd3ae2018-12-12 17:30:3612#include "base/strings/string_util.h"
Eric Orth9871aafa2018-10-02 19:59:1813#include "net/base/ip_endpoint.h"
14#include "net/base/net_errors.h"
Eric Orth8afaf152018-11-07 21:01:2615#include "net/dns/public/dns_protocol.h"
Eric Orth9871aafa2018-10-02 19:59:1816#include "net/dns/record_parsed.h"
17#include "net/dns/record_rdata.h"
18
19namespace net {
20
Eric Orth026776a2019-01-18 00:13:2821namespace {
22HostCache::Entry ParseHostnameResult(const std::string& host, uint16_t port) {
23 // Filter out root domain. Depending on the type, it either means no-result
24 // or is simply not a result important to any expected Chrome usecases.
25 if (host.empty()) {
26 return HostCache::Entry(ERR_NAME_NOT_RESOLVED,
27 HostCache::Entry::SOURCE_UNKNOWN);
28 }
29 return HostCache::Entry(OK,
30 std::vector<HostPortPair>({HostPortPair(host, port)}),
31 HostCache::Entry::SOURCE_UNKNOWN);
32}
33} // namespace
34
Eric Orth9871aafa2018-10-02 19:59:1835class HostResolverMdnsTask::Transaction {
36 public:
Eric Orth192e3bb2018-11-14 19:30:3237 Transaction(DnsQueryType query_type, HostResolverMdnsTask* task)
Matt Menkec35d1632018-11-29 12:43:4938 : query_type_(query_type),
39 results_(ERR_IO_PENDING, HostCache::Entry::SOURCE_UNKNOWN),
40 task_(task) {}
Eric Orth9871aafa2018-10-02 19:59:1841
42 void Start() {
43 DCHECK_CALLED_ON_VALID_SEQUENCE(task_->sequence_checker_);
44
45 // Should not be completed or running yet.
Matt Menkec35d1632018-11-29 12:43:4946 DCHECK_EQ(ERR_IO_PENDING, results_.error());
Eric Orth9871aafa2018-10-02 19:59:1847 DCHECK(!async_transaction_);
48
Eric Orth3d638702019-01-29 22:08:0249 // TODO(crbug.com/926300): Use |allow_cached_response| to set the
Eric Orth9871aafa2018-10-02 19:59:1850 // QUERY_CACHE flag or not.
51 int flags = MDnsTransaction::SINGLE_RESULT | MDnsTransaction::QUERY_CACHE |
52 MDnsTransaction::QUERY_NETWORK;
53 // If |this| is destroyed, destruction of |internal_transaction_| should
54 // cancel and prevent invocation of OnComplete.
55 std::unique_ptr<MDnsTransaction> inner_transaction =
56 task_->mdns_client_->CreateTransaction(
Eric Orth192e3bb2018-11-14 19:30:3257 DnsQueryTypeToQtype(query_type_), task_->hostname_, flags,
Eric Orth9871aafa2018-10-02 19:59:1858 base::BindRepeating(&HostResolverMdnsTask::Transaction::OnComplete,
59 base::Unretained(this)));
60
61 // Side effect warning: Start() may finish and invoke callbacks inline.
62 bool start_result = inner_transaction->Start();
63
64 if (!start_result)
Matt Menkec35d1632018-11-29 12:43:4965 task_->Complete(true /* post_needed */);
66 else if (results_.error() == ERR_IO_PENDING)
Eric Orth9871aafa2018-10-02 19:59:1867 async_transaction_ = std::move(inner_transaction);
68 }
69
Matt Menkec35d1632018-11-29 12:43:4970 bool IsDone() const { return results_.error() != ERR_IO_PENDING; }
Eric Orth9871aafa2018-10-02 19:59:1871 bool IsError() const {
Matt Menkec35d1632018-11-29 12:43:4972 return IsDone() && results_.error() != OK &&
73 results_.error() != ERR_NAME_NOT_RESOLVED;
Eric Orth9871aafa2018-10-02 19:59:1874 }
Matt Menkec35d1632018-11-29 12:43:4975 const HostCache::Entry& results() const { return results_; }
Eric Orth9871aafa2018-10-02 19:59:1876
77 void Cancel() {
78 DCHECK_CALLED_ON_VALID_SEQUENCE(task_->sequence_checker_);
Matt Menkec35d1632018-11-29 12:43:4979 DCHECK_EQ(ERR_IO_PENDING, results_.error());
Eric Orth9871aafa2018-10-02 19:59:1880
Matt Menkec35d1632018-11-29 12:43:4981 results_ = HostCache::Entry(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
Eric Orth9871aafa2018-10-02 19:59:1882 async_transaction_ = nullptr;
83 }
84
85 private:
86 void OnComplete(MDnsTransaction::Result result, const RecordParsed* parsed) {
87 DCHECK_CALLED_ON_VALID_SEQUENCE(task_->sequence_checker_);
Matt Menkec35d1632018-11-29 12:43:4988 DCHECK_EQ(ERR_IO_PENDING, results_.error());
Eric Orth9871aafa2018-10-02 19:59:1889
Matt Menkec35d1632018-11-29 12:43:4990 int error = ERR_UNEXPECTED;
Eric Orth9871aafa2018-10-02 19:59:1891 switch (result) {
92 case MDnsTransaction::RESULT_RECORD:
Eric Orth026776a2019-01-18 00:13:2893 DCHECK(parsed);
Matt Menkec35d1632018-11-29 12:43:4994 error = OK;
Eric Orth9871aafa2018-10-02 19:59:1895 break;
96 case MDnsTransaction::RESULT_NO_RESULTS:
97 case MDnsTransaction::RESULT_NSEC:
Matt Menkec35d1632018-11-29 12:43:4998 error = ERR_NAME_NOT_RESOLVED;
Eric Orth9871aafa2018-10-02 19:59:1899 break;
100 default:
101 // No other results should be possible with the request flags used.
102 NOTREACHED();
103 }
104
Eric Orth026776a2019-01-18 00:13:28105 results_ = HostResolverMdnsTask::ParseResult(error, query_type_, parsed,
106 task_->hostname_);
Eric Orth9871aafa2018-10-02 19:59:18107
108 // If we don't have a saved async_transaction, it means OnComplete was
109 // invoked inline in MDnsTransaction::Start. Callbacks will need to be
110 // invoked via post.
111 task_->CheckCompletion(!async_transaction_);
112 }
113
Eric Orth192e3bb2018-11-14 19:30:32114 const DnsQueryType query_type_;
Eric Orth9871aafa2018-10-02 19:59:18115
116 // ERR_IO_PENDING until transaction completes (or is cancelled).
Matt Menkec35d1632018-11-29 12:43:49117 HostCache::Entry results_;
Eric Orth9871aafa2018-10-02 19:59:18118
119 // Not saved until MDnsTransaction::Start completes to differentiate inline
120 // completion.
121 std::unique_ptr<MDnsTransaction> async_transaction_;
122
123 // Back pointer. Expected to destroy |this| before destroying itself.
124 HostResolverMdnsTask* const task_;
125};
126
127HostResolverMdnsTask::HostResolverMdnsTask(
128 MDnsClient* mdns_client,
129 const std::string& hostname,
Eric Orth192e3bb2018-11-14 19:30:32130 const std::vector<DnsQueryType>& query_types)
Jeremy Romand54000b22019-07-08 18:40:16131 : mdns_client_(mdns_client), hostname_(hostname) {
Eric Orth9871aafa2018-10-02 19:59:18132 DCHECK(!query_types.empty());
Eric Orth192e3bb2018-11-14 19:30:32133 for (DnsQueryType query_type : query_types) {
Eric Orth9871aafa2018-10-02 19:59:18134 transactions_.emplace_back(query_type, this);
135 }
136}
137
138HostResolverMdnsTask::~HostResolverMdnsTask() {
139 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
140 transactions_.clear();
141}
142
Matt Menkec35d1632018-11-29 12:43:49143void HostResolverMdnsTask::Start(base::OnceClosure completion_closure) {
Eric Orth9871aafa2018-10-02 19:59:18144 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Matt Menkec35d1632018-11-29 12:43:49145 DCHECK(!completion_closure_);
Eric Orthe857ebb2019-03-13 23:02:07146 DCHECK(mdns_client_);
Eric Orth9871aafa2018-10-02 19:59:18147
Matt Menkec35d1632018-11-29 12:43:49148 completion_closure_ = std::move(completion_closure);
Eric Orth9871aafa2018-10-02 19:59:18149
150 for (auto& transaction : transactions_) {
151 // Only start transaction if it is not already marked done. A transaction
152 // could be marked done before starting if it is preemptively canceled by
153 // a previously started transaction finishing with an error.
154 if (!transaction.IsDone())
155 transaction.Start();
156 }
157}
158
Matt Menkec35d1632018-11-29 12:43:49159HostCache::Entry HostResolverMdnsTask::GetResults() const {
Eric Orth07ee5f02018-11-29 00:42:03160 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Matt Menkec35d1632018-11-29 12:43:49161 DCHECK(!transactions_.empty());
162 DCHECK(!completion_closure_);
163 DCHECK(std::all_of(transactions_.begin(), transactions_.end(),
164 [](const Transaction& t) { return t.IsDone(); }));
Eric Orth07ee5f02018-11-29 00:42:03165
Giovanni Ortuño Urquidie369ab32018-11-29 05:23:26166 auto found_error =
167 std::find_if(transactions_.begin(), transactions_.end(),
168 [](const Transaction& t) { return t.IsError(); });
169 if (found_error != transactions_.end()) {
Matt Menkec35d1632018-11-29 12:43:49170 return found_error->results();
171 }
172
173 HostCache::Entry combined_results = transactions_.front().results();
174 for (auto it = ++transactions_.begin(); it != transactions_.end(); ++it) {
175 combined_results = HostCache::Entry::MergeEntries(
176 std::move(combined_results), it->results());
177 }
178
179 return combined_results;
180}
181
Eric Orth026776a2019-01-18 00:13:28182// static
183HostCache::Entry HostResolverMdnsTask::ParseResult(
184 int error,
185 DnsQueryType query_type,
186 const RecordParsed* parsed,
187 const std::string& expected_hostname) {
188 if (error != OK) {
189 return HostCache::Entry(error, HostCache::Entry::SOURCE_UNKNOWN);
190 }
191 DCHECK(parsed);
192
193 // Expected to be validated by MDnsClient.
194 DCHECK_EQ(DnsQueryTypeToQtype(query_type), parsed->type());
195 DCHECK(base::EqualsCaseInsensitiveASCII(expected_hostname, parsed->name()));
196
197 switch (query_type) {
198 case DnsQueryType::UNSPECIFIED:
199 // Should create two separate transactions with specified type.
200 NOTREACHED();
201 return HostCache::Entry(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
202 case DnsQueryType::A:
203 return HostCache::Entry(
204 OK,
205 AddressList(
206 IPEndPoint(parsed->rdata<net::ARecordRdata>()->address(), 0)),
207 HostCache::Entry::SOURCE_UNKNOWN);
208 case DnsQueryType::AAAA:
209 return HostCache::Entry(
210 OK,
211 AddressList(
212 IPEndPoint(parsed->rdata<net::AAAARecordRdata>()->address(), 0)),
213 HostCache::Entry::SOURCE_UNKNOWN);
214 case DnsQueryType::TXT:
215 return HostCache::Entry(OK, parsed->rdata<net::TxtRecordRdata>()->texts(),
216 HostCache::Entry::SOURCE_UNKNOWN);
217 case DnsQueryType::PTR:
218 return ParseHostnameResult(parsed->rdata<PtrRecordRdata>()->ptrdomain(),
219 0 /* port */);
220 case DnsQueryType::SRV:
221 return ParseHostnameResult(parsed->rdata<SrvRecordRdata>()->target(),
222 parsed->rdata<SrvRecordRdata>()->port());
223 }
224}
225
Matt Menkec35d1632018-11-29 12:43:49226void HostResolverMdnsTask::CheckCompletion(bool post_needed) {
227 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
228
229 // Finish immediately if any transactions completed with an error.
230 if (std::any_of(transactions_.begin(), transactions_.end(),
231 [](const Transaction& t) { return t.IsError(); })) {
232 Complete(post_needed);
Eric Orth9871aafa2018-10-02 19:59:18233 return;
234 }
235
236 if (std::all_of(transactions_.begin(), transactions_.end(),
237 [](const Transaction& t) { return t.IsDone(); })) {
Matt Menkec35d1632018-11-29 12:43:49238 Complete(post_needed);
Eric Orth9871aafa2018-10-02 19:59:18239 return;
240 }
241}
242
Matt Menkec35d1632018-11-29 12:43:49243void HostResolverMdnsTask::Complete(bool post_needed) {
Eric Orth9871aafa2018-10-02 19:59:18244 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
245
246 // Cancel any incomplete async transactions.
247 for (auto& transaction : transactions_) {
248 if (!transaction.IsDone())
249 transaction.Cancel();
250 }
251
252 if (post_needed) {
253 base::SequencedTaskRunnerHandle::Get()->PostTask(
Matt Menkec35d1632018-11-29 12:43:49254 FROM_HERE, base::BindOnce(
255 [](base::WeakPtr<HostResolverMdnsTask> task) {
256 if (task)
257 std::move(task->completion_closure_).Run();
258 },
259 weak_ptr_factory_.GetWeakPtr()));
Eric Orth9871aafa2018-10-02 19:59:18260 } else {
Matt Menkec35d1632018-11-29 12:43:49261 std::move(completion_closure_).Run();
Eric Orth9871aafa2018-10-02 19:59:18262 }
263}
264
265} // namespace net