host_resolver_mdns_task.cc 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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. #include "net/dns/host_resolver_mdns_task.h"
  5. #include <algorithm>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/check_op.h"
  9. #include "base/location.h"
  10. #include "base/memory/raw_ptr.h"
  11. #include "base/notreached.h"
  12. #include "base/strings/string_util.h"
  13. #include "base/threading/sequenced_task_runner_handle.h"
  14. #include "net/base/ip_endpoint.h"
  15. #include "net/base/net_errors.h"
  16. #include "net/dns/dns_util.h"
  17. #include "net/dns/public/dns_protocol.h"
  18. #include "net/dns/public/dns_query_type.h"
  19. #include "net/dns/record_parsed.h"
  20. #include "net/dns/record_rdata.h"
  21. namespace net {
  22. namespace {
  23. HostCache::Entry ParseHostnameResult(const std::string& host, uint16_t port) {
  24. // Filter out root domain. Depending on the type, it either means no-result
  25. // or is simply not a result important to any expected Chrome usecases.
  26. if (host.empty()) {
  27. return HostCache::Entry(ERR_NAME_NOT_RESOLVED,
  28. HostCache::Entry::SOURCE_UNKNOWN);
  29. }
  30. return HostCache::Entry(OK,
  31. std::vector<HostPortPair>({HostPortPair(host, port)}),
  32. HostCache::Entry::SOURCE_UNKNOWN);
  33. }
  34. } // namespace
  35. class HostResolverMdnsTask::Transaction {
  36. public:
  37. Transaction(DnsQueryType query_type, HostResolverMdnsTask* task)
  38. : query_type_(query_type),
  39. results_(ERR_IO_PENDING, HostCache::Entry::SOURCE_UNKNOWN),
  40. task_(task) {}
  41. void Start() {
  42. DCHECK_CALLED_ON_VALID_SEQUENCE(task_->sequence_checker_);
  43. // Should not be completed or running yet.
  44. DCHECK_EQ(ERR_IO_PENDING, results_.error());
  45. DCHECK(!async_transaction_);
  46. // TODO(crbug.com/926300): Use |allow_cached_response| to set the
  47. // QUERY_CACHE flag or not.
  48. int flags = MDnsTransaction::SINGLE_RESULT | MDnsTransaction::QUERY_CACHE |
  49. MDnsTransaction::QUERY_NETWORK;
  50. // If |this| is destroyed, destruction of |internal_transaction_| should
  51. // cancel and prevent invocation of OnComplete.
  52. std::unique_ptr<MDnsTransaction> inner_transaction =
  53. task_->mdns_client_->CreateTransaction(
  54. DnsQueryTypeToQtype(query_type_), task_->hostname_, flags,
  55. base::BindRepeating(&HostResolverMdnsTask::Transaction::OnComplete,
  56. base::Unretained(this)));
  57. // Side effect warning: Start() may finish and invoke callbacks inline.
  58. bool start_result = inner_transaction->Start();
  59. if (!start_result)
  60. task_->Complete(true /* post_needed */);
  61. else if (results_.error() == ERR_IO_PENDING)
  62. async_transaction_ = std::move(inner_transaction);
  63. }
  64. bool IsDone() const { return results_.error() != ERR_IO_PENDING; }
  65. bool IsError() const {
  66. return IsDone() && results_.error() != OK &&
  67. results_.error() != ERR_NAME_NOT_RESOLVED;
  68. }
  69. const HostCache::Entry& results() const { return results_; }
  70. void Cancel() {
  71. DCHECK_CALLED_ON_VALID_SEQUENCE(task_->sequence_checker_);
  72. DCHECK_EQ(ERR_IO_PENDING, results_.error());
  73. results_ = HostCache::Entry(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
  74. async_transaction_ = nullptr;
  75. }
  76. private:
  77. void OnComplete(MDnsTransaction::Result result, const RecordParsed* parsed) {
  78. DCHECK_CALLED_ON_VALID_SEQUENCE(task_->sequence_checker_);
  79. DCHECK_EQ(ERR_IO_PENDING, results_.error());
  80. int error = ERR_UNEXPECTED;
  81. switch (result) {
  82. case MDnsTransaction::RESULT_RECORD:
  83. DCHECK(parsed);
  84. error = OK;
  85. break;
  86. case MDnsTransaction::RESULT_NO_RESULTS:
  87. case MDnsTransaction::RESULT_NSEC:
  88. error = ERR_NAME_NOT_RESOLVED;
  89. break;
  90. default:
  91. // No other results should be possible with the request flags used.
  92. NOTREACHED();
  93. }
  94. results_ = HostResolverMdnsTask::ParseResult(error, query_type_, parsed,
  95. task_->hostname_);
  96. // If we don't have a saved async_transaction, it means OnComplete was
  97. // invoked inline in MDnsTransaction::Start. Callbacks will need to be
  98. // invoked via post.
  99. task_->CheckCompletion(!async_transaction_);
  100. }
  101. const DnsQueryType query_type_;
  102. // ERR_IO_PENDING until transaction completes (or is cancelled).
  103. HostCache::Entry results_;
  104. // Not saved until MDnsTransaction::Start completes to differentiate inline
  105. // completion.
  106. std::unique_ptr<MDnsTransaction> async_transaction_;
  107. // Back pointer. Expected to destroy |this| before destroying itself.
  108. const raw_ptr<HostResolverMdnsTask> task_;
  109. };
  110. HostResolverMdnsTask::HostResolverMdnsTask(MDnsClient* mdns_client,
  111. std::string hostname,
  112. DnsQueryTypeSet query_types)
  113. : mdns_client_(mdns_client), hostname_(std::move(hostname)) {
  114. DCHECK(!query_types.Empty());
  115. DCHECK(!query_types.Has(DnsQueryType::UNSPECIFIED));
  116. static constexpr DnsQueryTypeSet kUnwantedQueries(
  117. DnsQueryType::HTTPS, DnsQueryType::INTEGRITY,
  118. DnsQueryType::HTTPS_EXPERIMENTAL);
  119. for (DnsQueryType query_type : Difference(query_types, kUnwantedQueries))
  120. transactions_.emplace_back(query_type, this);
  121. }
  122. HostResolverMdnsTask::~HostResolverMdnsTask() {
  123. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  124. transactions_.clear();
  125. }
  126. void HostResolverMdnsTask::Start(base::OnceClosure completion_closure) {
  127. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  128. DCHECK(!completion_closure_);
  129. DCHECK(mdns_client_);
  130. completion_closure_ = std::move(completion_closure);
  131. for (auto& transaction : transactions_) {
  132. // Only start transaction if it is not already marked done. A transaction
  133. // could be marked done before starting if it is preemptively canceled by
  134. // a previously started transaction finishing with an error.
  135. if (!transaction.IsDone())
  136. transaction.Start();
  137. }
  138. }
  139. HostCache::Entry HostResolverMdnsTask::GetResults() const {
  140. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  141. DCHECK(!transactions_.empty());
  142. DCHECK(!completion_closure_);
  143. DCHECK(std::all_of(transactions_.begin(), transactions_.end(),
  144. [](const Transaction& t) { return t.IsDone(); }));
  145. auto found_error =
  146. std::find_if(transactions_.begin(), transactions_.end(),
  147. [](const Transaction& t) { return t.IsError(); });
  148. if (found_error != transactions_.end()) {
  149. return found_error->results();
  150. }
  151. HostCache::Entry combined_results = transactions_.front().results();
  152. for (auto it = ++transactions_.begin(); it != transactions_.end(); ++it) {
  153. combined_results = HostCache::Entry::MergeEntries(
  154. std::move(combined_results), it->results());
  155. }
  156. return combined_results;
  157. }
  158. // static
  159. HostCache::Entry HostResolverMdnsTask::ParseResult(
  160. int error,
  161. DnsQueryType query_type,
  162. const RecordParsed* parsed,
  163. const std::string& expected_hostname) {
  164. if (error != OK) {
  165. return HostCache::Entry(error, HostCache::Entry::SOURCE_UNKNOWN);
  166. }
  167. DCHECK(parsed);
  168. // Expected to be validated by MDnsClient.
  169. DCHECK_EQ(DnsQueryTypeToQtype(query_type), parsed->type());
  170. DCHECK(base::EqualsCaseInsensitiveASCII(expected_hostname, parsed->name()));
  171. switch (query_type) {
  172. case DnsQueryType::UNSPECIFIED:
  173. // Should create two separate transactions with specified type.
  174. case DnsQueryType::HTTPS:
  175. case DnsQueryType::HTTPS_EXPERIMENTAL:
  176. // Not supported.
  177. // TODO(ericorth@chromium.org): Consider support for HTTPS in mDNS if it
  178. // is ever decided to support HTTPS via non-DoH.
  179. case DnsQueryType::INTEGRITY:
  180. // INTEGRITY queries are not expected to be useful in mDNS, so they're not
  181. // supported.
  182. NOTREACHED();
  183. return HostCache::Entry(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
  184. case DnsQueryType::A:
  185. return HostCache::Entry(
  186. OK, {IPEndPoint(parsed->rdata<net::ARecordRdata>()->address(), 0)},
  187. /*aliases=*/{}, HostCache::Entry::SOURCE_UNKNOWN);
  188. case DnsQueryType::AAAA:
  189. return HostCache::Entry(
  190. OK, {IPEndPoint(parsed->rdata<net::AAAARecordRdata>()->address(), 0)},
  191. /*aliases=*/{}, HostCache::Entry::SOURCE_UNKNOWN);
  192. case DnsQueryType::TXT:
  193. return HostCache::Entry(OK, parsed->rdata<net::TxtRecordRdata>()->texts(),
  194. HostCache::Entry::SOURCE_UNKNOWN);
  195. case DnsQueryType::PTR:
  196. return ParseHostnameResult(parsed->rdata<PtrRecordRdata>()->ptrdomain(),
  197. 0 /* port */);
  198. case DnsQueryType::SRV:
  199. return ParseHostnameResult(parsed->rdata<SrvRecordRdata>()->target(),
  200. parsed->rdata<SrvRecordRdata>()->port());
  201. }
  202. }
  203. void HostResolverMdnsTask::CheckCompletion(bool post_needed) {
  204. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  205. // Finish immediately if any transactions completed with an error.
  206. if (std::any_of(transactions_.begin(), transactions_.end(),
  207. [](const Transaction& t) { return t.IsError(); })) {
  208. Complete(post_needed);
  209. return;
  210. }
  211. if (std::all_of(transactions_.begin(), transactions_.end(),
  212. [](const Transaction& t) { return t.IsDone(); })) {
  213. Complete(post_needed);
  214. return;
  215. }
  216. }
  217. void HostResolverMdnsTask::Complete(bool post_needed) {
  218. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  219. // Cancel any incomplete async transactions.
  220. for (auto& transaction : transactions_) {
  221. if (!transaction.IsDone())
  222. transaction.Cancel();
  223. }
  224. if (post_needed) {
  225. base::SequencedTaskRunnerHandle::Get()->PostTask(
  226. FROM_HERE, base::BindOnce(
  227. [](base::WeakPtr<HostResolverMdnsTask> task) {
  228. if (task)
  229. std::move(task->completion_closure_).Run();
  230. },
  231. weak_ptr_factory_.GetWeakPtr()));
  232. } else {
  233. std::move(completion_closure_).Run();
  234. }
  235. }
  236. } // namespace net