mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2025-02-26 20:03:26 +03:00
274 lines
9.4 KiB
C++
274 lines
9.4 KiB
C++
// Copyright 2018 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/dns/host_resolver_mdns_task.h"
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
#include "base/bind.h"
|
|
#include "base/check_op.h"
|
|
#include "base/notreached.h"
|
|
#include "base/strings/string_util.h"
|
|
#include "net/base/ip_endpoint.h"
|
|
#include "net/base/net_errors.h"
|
|
#include "net/dns/public/dns_protocol.h"
|
|
#include "net/dns/record_parsed.h"
|
|
#include "net/dns/record_rdata.h"
|
|
|
|
namespace net {
|
|
|
|
namespace {
|
|
HostCache::Entry ParseHostnameResult(const std::string& host, uint16_t port) {
|
|
// Filter out root domain. Depending on the type, it either means no-result
|
|
// or is simply not a result important to any expected Chrome usecases.
|
|
if (host.empty()) {
|
|
return HostCache::Entry(ERR_NAME_NOT_RESOLVED,
|
|
HostCache::Entry::SOURCE_UNKNOWN);
|
|
}
|
|
return HostCache::Entry(OK,
|
|
std::vector<HostPortPair>({HostPortPair(host, port)}),
|
|
HostCache::Entry::SOURCE_UNKNOWN);
|
|
}
|
|
} // namespace
|
|
|
|
class HostResolverMdnsTask::Transaction {
|
|
public:
|
|
Transaction(DnsQueryType query_type, HostResolverMdnsTask* task)
|
|
: query_type_(query_type),
|
|
results_(ERR_IO_PENDING, HostCache::Entry::SOURCE_UNKNOWN),
|
|
task_(task) {}
|
|
|
|
void Start() {
|
|
DCHECK_CALLED_ON_VALID_SEQUENCE(task_->sequence_checker_);
|
|
|
|
// Should not be completed or running yet.
|
|
DCHECK_EQ(ERR_IO_PENDING, results_.error());
|
|
DCHECK(!async_transaction_);
|
|
|
|
// TODO(crbug.com/926300): Use |allow_cached_response| to set the
|
|
// QUERY_CACHE flag or not.
|
|
int flags = MDnsTransaction::SINGLE_RESULT | MDnsTransaction::QUERY_CACHE |
|
|
MDnsTransaction::QUERY_NETWORK;
|
|
// If |this| is destroyed, destruction of |internal_transaction_| should
|
|
// cancel and prevent invocation of OnComplete.
|
|
std::unique_ptr<MDnsTransaction> inner_transaction =
|
|
task_->mdns_client_->CreateTransaction(
|
|
DnsQueryTypeToQtype(query_type_), task_->hostname_, flags,
|
|
base::BindRepeating(&HostResolverMdnsTask::Transaction::OnComplete,
|
|
base::Unretained(this)));
|
|
|
|
// Side effect warning: Start() may finish and invoke callbacks inline.
|
|
bool start_result = inner_transaction->Start();
|
|
|
|
if (!start_result)
|
|
task_->Complete(true /* post_needed */);
|
|
else if (results_.error() == ERR_IO_PENDING)
|
|
async_transaction_ = std::move(inner_transaction);
|
|
}
|
|
|
|
bool IsDone() const { return results_.error() != ERR_IO_PENDING; }
|
|
bool IsError() const {
|
|
return IsDone() && results_.error() != OK &&
|
|
results_.error() != ERR_NAME_NOT_RESOLVED;
|
|
}
|
|
const HostCache::Entry& results() const { return results_; }
|
|
|
|
void Cancel() {
|
|
DCHECK_CALLED_ON_VALID_SEQUENCE(task_->sequence_checker_);
|
|
DCHECK_EQ(ERR_IO_PENDING, results_.error());
|
|
|
|
results_ = HostCache::Entry(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
|
|
async_transaction_ = nullptr;
|
|
}
|
|
|
|
private:
|
|
void OnComplete(MDnsTransaction::Result result, const RecordParsed* parsed) {
|
|
DCHECK_CALLED_ON_VALID_SEQUENCE(task_->sequence_checker_);
|
|
DCHECK_EQ(ERR_IO_PENDING, results_.error());
|
|
|
|
int error = ERR_UNEXPECTED;
|
|
switch (result) {
|
|
case MDnsTransaction::RESULT_RECORD:
|
|
DCHECK(parsed);
|
|
error = OK;
|
|
break;
|
|
case MDnsTransaction::RESULT_NO_RESULTS:
|
|
case MDnsTransaction::RESULT_NSEC:
|
|
error = ERR_NAME_NOT_RESOLVED;
|
|
break;
|
|
default:
|
|
// No other results should be possible with the request flags used.
|
|
NOTREACHED();
|
|
}
|
|
|
|
results_ = HostResolverMdnsTask::ParseResult(error, query_type_, parsed,
|
|
task_->hostname_);
|
|
|
|
// If we don't have a saved async_transaction, it means OnComplete was
|
|
// invoked inline in MDnsTransaction::Start. Callbacks will need to be
|
|
// invoked via post.
|
|
task_->CheckCompletion(!async_transaction_);
|
|
}
|
|
|
|
const DnsQueryType query_type_;
|
|
|
|
// ERR_IO_PENDING until transaction completes (or is cancelled).
|
|
HostCache::Entry results_;
|
|
|
|
// Not saved until MDnsTransaction::Start completes to differentiate inline
|
|
// completion.
|
|
std::unique_ptr<MDnsTransaction> async_transaction_;
|
|
|
|
// Back pointer. Expected to destroy |this| before destroying itself.
|
|
HostResolverMdnsTask* const task_;
|
|
};
|
|
|
|
HostResolverMdnsTask::HostResolverMdnsTask(
|
|
MDnsClient* mdns_client,
|
|
const std::string& hostname,
|
|
const std::vector<DnsQueryType>& query_types)
|
|
: mdns_client_(mdns_client), hostname_(hostname) {
|
|
DCHECK(!query_types.empty());
|
|
for (DnsQueryType query_type : query_types) {
|
|
transactions_.emplace_back(query_type, this);
|
|
}
|
|
}
|
|
|
|
HostResolverMdnsTask::~HostResolverMdnsTask() {
|
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
|
transactions_.clear();
|
|
}
|
|
|
|
void HostResolverMdnsTask::Start(base::OnceClosure completion_closure) {
|
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
|
DCHECK(!completion_closure_);
|
|
DCHECK(mdns_client_);
|
|
|
|
completion_closure_ = std::move(completion_closure);
|
|
|
|
for (auto& transaction : transactions_) {
|
|
// Only start transaction if it is not already marked done. A transaction
|
|
// could be marked done before starting if it is preemptively canceled by
|
|
// a previously started transaction finishing with an error.
|
|
if (!transaction.IsDone())
|
|
transaction.Start();
|
|
}
|
|
}
|
|
|
|
HostCache::Entry HostResolverMdnsTask::GetResults() const {
|
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
|
DCHECK(!transactions_.empty());
|
|
DCHECK(!completion_closure_);
|
|
DCHECK(std::all_of(transactions_.begin(), transactions_.end(),
|
|
[](const Transaction& t) { return t.IsDone(); }));
|
|
|
|
auto found_error =
|
|
std::find_if(transactions_.begin(), transactions_.end(),
|
|
[](const Transaction& t) { return t.IsError(); });
|
|
if (found_error != transactions_.end()) {
|
|
return found_error->results();
|
|
}
|
|
|
|
HostCache::Entry combined_results = transactions_.front().results();
|
|
for (auto it = ++transactions_.begin(); it != transactions_.end(); ++it) {
|
|
combined_results = HostCache::Entry::MergeEntries(
|
|
std::move(combined_results), it->results());
|
|
}
|
|
|
|
return combined_results;
|
|
}
|
|
|
|
// static
|
|
HostCache::Entry HostResolverMdnsTask::ParseResult(
|
|
int error,
|
|
DnsQueryType query_type,
|
|
const RecordParsed* parsed,
|
|
const std::string& expected_hostname) {
|
|
if (error != OK) {
|
|
return HostCache::Entry(error, HostCache::Entry::SOURCE_UNKNOWN);
|
|
}
|
|
DCHECK(parsed);
|
|
|
|
// Expected to be validated by MDnsClient.
|
|
DCHECK_EQ(DnsQueryTypeToQtype(query_type), parsed->type());
|
|
DCHECK(base::EqualsCaseInsensitiveASCII(expected_hostname, parsed->name()));
|
|
|
|
switch (query_type) {
|
|
case DnsQueryType::UNSPECIFIED:
|
|
// Should create two separate transactions with specified type.
|
|
case DnsQueryType::HTTPS:
|
|
// Not supported.
|
|
// TODO(ericorth@chromium.org): Consider support for HTTPS in mDNS if it
|
|
// is ever decided to support HTTPS via non-DoH.
|
|
case DnsQueryType::INTEGRITY:
|
|
// INTEGRITY queries are not expected to be useful in mDNS, so they're not
|
|
// supported.
|
|
NOTREACHED();
|
|
return HostCache::Entry(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
|
|
case DnsQueryType::A:
|
|
return HostCache::Entry(
|
|
OK,
|
|
AddressList(
|
|
IPEndPoint(parsed->rdata<net::ARecordRdata>()->address(), 0)),
|
|
HostCache::Entry::SOURCE_UNKNOWN);
|
|
case DnsQueryType::AAAA:
|
|
return HostCache::Entry(
|
|
OK,
|
|
AddressList(
|
|
IPEndPoint(parsed->rdata<net::AAAARecordRdata>()->address(), 0)),
|
|
HostCache::Entry::SOURCE_UNKNOWN);
|
|
case DnsQueryType::TXT:
|
|
return HostCache::Entry(OK, parsed->rdata<net::TxtRecordRdata>()->texts(),
|
|
HostCache::Entry::SOURCE_UNKNOWN);
|
|
case DnsQueryType::PTR:
|
|
return ParseHostnameResult(parsed->rdata<PtrRecordRdata>()->ptrdomain(),
|
|
0 /* port */);
|
|
case DnsQueryType::SRV:
|
|
return ParseHostnameResult(parsed->rdata<SrvRecordRdata>()->target(),
|
|
parsed->rdata<SrvRecordRdata>()->port());
|
|
}
|
|
}
|
|
|
|
void HostResolverMdnsTask::CheckCompletion(bool post_needed) {
|
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
|
|
|
// Finish immediately if any transactions completed with an error.
|
|
if (std::any_of(transactions_.begin(), transactions_.end(),
|
|
[](const Transaction& t) { return t.IsError(); })) {
|
|
Complete(post_needed);
|
|
return;
|
|
}
|
|
|
|
if (std::all_of(transactions_.begin(), transactions_.end(),
|
|
[](const Transaction& t) { return t.IsDone(); })) {
|
|
Complete(post_needed);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void HostResolverMdnsTask::Complete(bool post_needed) {
|
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
|
|
|
// Cancel any incomplete async transactions.
|
|
for (auto& transaction : transactions_) {
|
|
if (!transaction.IsDone())
|
|
transaction.Cancel();
|
|
}
|
|
|
|
if (post_needed) {
|
|
base::SequencedTaskRunnerHandle::Get()->PostTask(
|
|
FROM_HERE, base::BindOnce(
|
|
[](base::WeakPtr<HostResolverMdnsTask> task) {
|
|
if (task)
|
|
std::move(task->completion_closure_).Run();
|
|
},
|
|
weak_ptr_factory_.GetWeakPtr()));
|
|
} else {
|
|
std::move(completion_closure_).Run();
|
|
}
|
|
}
|
|
|
|
} // namespace net
|