Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 1 | // 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 Marchand | 6d0558fd | 2019-01-25 16:49:37 | [diff] [blame] | 10 | #include "base/bind.h" |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 11 | #include "base/logging.h" |
Eric Orth | 828bd3ae | 2018-12-12 17:30:36 | [diff] [blame] | 12 | #include "base/strings/string_util.h" |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 13 | #include "net/base/ip_endpoint.h" |
| 14 | #include "net/base/net_errors.h" |
Eric Orth | 8afaf15 | 2018-11-07 21:01:26 | [diff] [blame] | 15 | #include "net/dns/public/dns_protocol.h" |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 16 | #include "net/dns/record_parsed.h" |
| 17 | #include "net/dns/record_rdata.h" |
| 18 | |
| 19 | namespace net { |
| 20 | |
Eric Orth | 026776a | 2019-01-18 00:13:28 | [diff] [blame] | 21 | namespace { |
| 22 | HostCache::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 Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 35 | class HostResolverMdnsTask::Transaction { |
| 36 | public: |
Eric Orth | 192e3bb | 2018-11-14 19:30:32 | [diff] [blame] | 37 | Transaction(DnsQueryType query_type, HostResolverMdnsTask* task) |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 38 | : query_type_(query_type), |
| 39 | results_(ERR_IO_PENDING, HostCache::Entry::SOURCE_UNKNOWN), |
| 40 | task_(task) {} |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 41 | |
| 42 | void Start() { |
| 43 | DCHECK_CALLED_ON_VALID_SEQUENCE(task_->sequence_checker_); |
| 44 | |
| 45 | // Should not be completed or running yet. |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 46 | DCHECK_EQ(ERR_IO_PENDING, results_.error()); |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 47 | DCHECK(!async_transaction_); |
| 48 | |
Eric Orth | 3d63870 | 2019-01-29 22:08:02 | [diff] [blame] | 49 | // TODO(crbug.com/926300): Use |allow_cached_response| to set the |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 50 | // 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 Orth | 192e3bb | 2018-11-14 19:30:32 | [diff] [blame] | 57 | DnsQueryTypeToQtype(query_type_), task_->hostname_, flags, |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 58 | 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 Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 65 | task_->Complete(true /* post_needed */); |
| 66 | else if (results_.error() == ERR_IO_PENDING) |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 67 | async_transaction_ = std::move(inner_transaction); |
| 68 | } |
| 69 | |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 70 | bool IsDone() const { return results_.error() != ERR_IO_PENDING; } |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 71 | bool IsError() const { |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 72 | return IsDone() && results_.error() != OK && |
| 73 | results_.error() != ERR_NAME_NOT_RESOLVED; |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 74 | } |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 75 | const HostCache::Entry& results() const { return results_; } |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 76 | |
| 77 | void Cancel() { |
| 78 | DCHECK_CALLED_ON_VALID_SEQUENCE(task_->sequence_checker_); |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 79 | DCHECK_EQ(ERR_IO_PENDING, results_.error()); |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 80 | |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 81 | results_ = HostCache::Entry(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 82 | 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 Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 88 | DCHECK_EQ(ERR_IO_PENDING, results_.error()); |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 89 | |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 90 | int error = ERR_UNEXPECTED; |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 91 | switch (result) { |
| 92 | case MDnsTransaction::RESULT_RECORD: |
Eric Orth | 026776a | 2019-01-18 00:13:28 | [diff] [blame] | 93 | DCHECK(parsed); |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 94 | error = OK; |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 95 | break; |
| 96 | case MDnsTransaction::RESULT_NO_RESULTS: |
| 97 | case MDnsTransaction::RESULT_NSEC: |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 98 | error = ERR_NAME_NOT_RESOLVED; |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 99 | break; |
| 100 | default: |
| 101 | // No other results should be possible with the request flags used. |
| 102 | NOTREACHED(); |
| 103 | } |
| 104 | |
Eric Orth | 026776a | 2019-01-18 00:13:28 | [diff] [blame] | 105 | results_ = HostResolverMdnsTask::ParseResult(error, query_type_, parsed, |
| 106 | task_->hostname_); |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 107 | |
| 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 Orth | 192e3bb | 2018-11-14 19:30:32 | [diff] [blame] | 114 | const DnsQueryType query_type_; |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 115 | |
| 116 | // ERR_IO_PENDING until transaction completes (or is cancelled). |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 117 | HostCache::Entry results_; |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 118 | |
| 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 | |
| 127 | HostResolverMdnsTask::HostResolverMdnsTask( |
| 128 | MDnsClient* mdns_client, |
| 129 | const std::string& hostname, |
Eric Orth | 192e3bb | 2018-11-14 19:30:32 | [diff] [blame] | 130 | const std::vector<DnsQueryType>& query_types) |
Jeremy Roman | d54000b2 | 2019-07-08 18:40:16 | [diff] [blame] | 131 | : mdns_client_(mdns_client), hostname_(hostname) { |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 132 | DCHECK(!query_types.empty()); |
Eric Orth | 192e3bb | 2018-11-14 19:30:32 | [diff] [blame] | 133 | for (DnsQueryType query_type : query_types) { |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 134 | transactions_.emplace_back(query_type, this); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | HostResolverMdnsTask::~HostResolverMdnsTask() { |
| 139 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 140 | transactions_.clear(); |
| 141 | } |
| 142 | |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 143 | void HostResolverMdnsTask::Start(base::OnceClosure completion_closure) { |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 144 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 145 | DCHECK(!completion_closure_); |
Eric Orth | e857ebb | 2019-03-13 23:02:07 | [diff] [blame] | 146 | DCHECK(mdns_client_); |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 147 | |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 148 | completion_closure_ = std::move(completion_closure); |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 149 | |
| 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 Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 159 | HostCache::Entry HostResolverMdnsTask::GetResults() const { |
Eric Orth | 07ee5f0 | 2018-11-29 00:42:03 | [diff] [blame] | 160 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 161 | DCHECK(!transactions_.empty()); |
| 162 | DCHECK(!completion_closure_); |
| 163 | DCHECK(std::all_of(transactions_.begin(), transactions_.end(), |
| 164 | [](const Transaction& t) { return t.IsDone(); })); |
Eric Orth | 07ee5f0 | 2018-11-29 00:42:03 | [diff] [blame] | 165 | |
Giovanni Ortuño Urquidi | e369ab3 | 2018-11-29 05:23:26 | [diff] [blame] | 166 | 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 Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 170 | 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 Orth | 026776a | 2019-01-18 00:13:28 | [diff] [blame] | 182 | // static |
| 183 | HostCache::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 Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 226 | void 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 Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 233 | return; |
| 234 | } |
| 235 | |
| 236 | if (std::all_of(transactions_.begin(), transactions_.end(), |
| 237 | [](const Transaction& t) { return t.IsDone(); })) { |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 238 | Complete(post_needed); |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 239 | return; |
| 240 | } |
| 241 | } |
| 242 | |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 243 | void HostResolverMdnsTask::Complete(bool post_needed) { |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 244 | 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 Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 254 | 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 Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 260 | } else { |
Matt Menke | c35d163 | 2018-11-29 12:43:49 | [diff] [blame] | 261 | std::move(completion_closure_).Run(); |
Eric Orth | 9871aafa | 2018-10-02 19:59:18 | [diff] [blame] | 262 | } |
| 263 | } |
| 264 | |
| 265 | } // namespace net |