1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_DNS_HOST_RESOLVER_MANAGER_JOB_H_
#define NET_DNS_HOST_RESOLVER_MANAGER_JOB_H_
#include <array>
#include <deque>
#include <memory>
#include <optional>
#include <variant>
#include <vector>
#include "base/containers/linked_list.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/safe_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "net/base/address_family.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/network_handle.h"
#include "net/base/prioritized_dispatcher.h"
#include "net/dns/dns_task_results_manager.h"
#include "net/dns/host_cache.h"
#include "net/dns/host_resolver.h"
#include "net/dns/host_resolver_dns_task.h"
#include "net/dns/host_resolver_manager.h"
#include "net/dns/public/dns_query_type.h"
#include "net/dns/public/secure_dns_mode.h"
#include "net/log/net_log_with_source.h"
namespace net {
class ResolveContext;
class HostResolverMdnsTask;
class HostResolverNat64Task;
// Key used to identify a HostResolverManager::Job.
struct HostResolverManager::JobKey {
JobKey(HostResolver::Host host, ResolveContext* resolve_context);
~JobKey();
JobKey(const JobKey& other);
JobKey& operator=(const JobKey& other);
bool operator<(const JobKey& other) const;
bool operator==(const JobKey& other) const;
HostResolver::Host host;
NetworkAnonymizationKey network_anonymization_key;
DnsQueryTypeSet query_types;
HostResolverFlags flags;
HostResolverSource source;
SecureDnsMode secure_dns_mode;
base::WeakPtr<ResolveContext> resolve_context;
HostCache::Key ToCacheKey(bool secure) const;
handles::NetworkHandle GetTargetNetwork() const;
};
// Aggregates all Requests for the same Key. Dispatched via
// PrioritizedDispatcher.
class HostResolverManager::Job : public PrioritizedDispatcher::Job,
public HostResolverDnsTask::Delegate,
public DnsTaskResultsManager::Delegate {
public:
// Creates new job for |key| where |request_net_log| is bound to the
// request that spawned it.
Job(const base::WeakPtr<HostResolverManager>& resolver,
JobKey key,
ResolveHostParameters::CacheUsage cache_usage,
HostCache* host_cache,
std::deque<TaskType> tasks,
RequestPriority priority,
const NetLogWithSource& source_net_log,
const base::TickClock* tick_clock,
const HostResolver::HttpsSvcbOptions& https_svcb_options);
~Job() override;
// Add this job to the dispatcher. If "at_head" is true, adds at the front
// of the queue.
void Schedule(bool at_head);
void AddRequest(RequestImpl* request);
void ChangeRequestPriority(RequestImpl* req, RequestPriority priority);
// Detach cancelled request. If it was the last active Request, also finishes
// this Job.
void CancelRequest(RequestImpl* request);
void AddServiceEndpointRequest(ServiceEndpointRequestImpl* request);
// Similar to CancelRequest(), if `request` was the last active one, finishes
// this job.
void CancelServiceEndpointRequest(ServiceEndpointRequestImpl* request);
// Similar to ChangeRequestPriority(), but for a ServiceEndpointRequest.
void ChangeServiceEndpointRequestPriority(ServiceEndpointRequestImpl* request,
RequestPriority priority);
// Called from AbortJobsWithoutTargetNetwork(). Completes all requests and
// destroys the job. This currently assumes the abort is due to a network
// change.
// TODO This should not delete |this|.
void Abort();
// Gets a closure that will abort an insecure DnsTask (see
// AbortInsecureDnsTask()) iff |this| is still valid. Useful if aborting a
// list of Jobs as some may be cancelled while aborting others.
base::OnceClosure GetAbortInsecureDnsTaskClosure(int error,
bool fallback_only);
// Aborts or removes any current/future insecure DnsTasks if a
// HostResolverSystemTask is available for fallback. If no fallback is
// available and |fallback_only| is false, a job that is currently running an
// insecure DnsTask will be completed with |error|.
void AbortInsecureDnsTask(int error, bool fallback_only);
// Called by HostResolverManager when this job is evicted due to queue
// overflow. Completes all requests and destroys the job. The job could have
// waiting requests that will receive completion callbacks, so cleanup
// asynchronously to avoid reentrancy.
void OnEvicted();
// Attempts to serve the job from HOSTS. Returns true if succeeded and
// this Job was destroyed.
bool ServeFromHosts();
void OnAddedToJobMap(JobMap::iterator iterator);
void OnRemovedFromJobMap();
void RunNextTask();
const JobKey& key() const { return key_; }
bool is_queued() const { return !handle_.is_null(); }
bool is_running() const { return job_running_; }
bool HasTargetNetwork() const {
return key_.GetTargetNetwork() != handles::kInvalidNetworkHandle;
}
DnsTaskResultsManager* dns_task_results_manager() const {
return dns_task_results_manager_.get();
}
private:
// Keeps track of the highest priority.
class PriorityTracker {
public:
explicit PriorityTracker(RequestPriority initial_priority)
: highest_priority_(initial_priority) {}
RequestPriority highest_priority() const { return highest_priority_; }
size_t total_count() const { return total_count_; }
void Add(RequestPriority req_priority) {
++total_count_;
++counts_[req_priority];
if (highest_priority_ < req_priority) {
highest_priority_ = req_priority;
}
}
void Remove(RequestPriority req_priority) {
DCHECK_GT(total_count_, 0u);
DCHECK_GT(counts_[req_priority], 0u);
--total_count_;
--counts_[req_priority];
size_t i;
for (i = highest_priority_; i > MINIMUM_PRIORITY && !counts_[i]; --i) {
}
highest_priority_ = static_cast<RequestPriority>(i);
// In absence of requests, default to MINIMUM_PRIORITY.
if (total_count_ == 0) {
DCHECK_EQ(MINIMUM_PRIORITY, highest_priority_);
}
}
private:
RequestPriority highest_priority_;
size_t total_count_ = 0;
std::array<size_t, NUM_PRIORITIES> counts_ = {};
};
base::Value::Dict NetLogJobCreationParams(const NetLogSource& source);
void Finish();
void KillDnsTask();
// Reduce the number of job slots occupied and queued in the dispatcher by
// one. If the next Job slot is queued in the dispatcher, cancels the queued
// job. Otherwise, the next Job has been started by the PrioritizedDispatcher,
// so signals it is complete.
void ReduceByOneJobSlot();
// Common helper methods for adding and canceling a request.
void AddRequestCommon(RequestPriority request_priority,
const NetLogWithSource& request_net_log,
bool is_speculative);
void CancelRequestCommon(RequestPriority request_priority,
const NetLogWithSource& request_net_log);
void UpdatePriority();
// PrioritizedDispatcher::Job:
void Start() override;
// TODO(szym): Since DnsTransaction does not consume threads, we can increase
// the limits on |dispatcher_|. But in order to keep the number of
// ThreadPool threads low, we will need to use an "inner"
// PrioritizedDispatcher with tighter limits.
void StartSystemTask();
// Called by HostResolverSystemTask when it completes.
void OnSystemTaskComplete(base::TimeTicks start_time,
const AddressList& addr_list,
int /*os_error*/,
int net_error);
void InsecureCacheLookup();
void StartDnsTask(bool secure);
void StartNextDnsTransaction();
// Called if DnsTask fails. It is posted from StartDnsTask, so Job may be
// deleted before this callback. In this case dns_task is deleted as well,
// so we use it as indicator whether Job is still valid.
void OnDnsTaskFailure(const base::WeakPtr<HostResolverDnsTask>& dns_task,
base::TimeDelta duration,
bool allow_fallback,
const HostCache::Entry& failure_results,
bool secure);
// HostResolverDnsTask::Delegate implementation:
void OnDnsTaskComplete(base::TimeTicks start_time,
bool allow_fallback,
HostResolverDnsTask::Results results,
bool secure) override;
void OnIntermediateTransactionsComplete(
std::optional<HostResolverDnsTask::SingleTransactionResults>
single_transaction_results) override;
bool IsHappyEyeballsV3Enabled() const override;
void AddTransactionTimeQueued(base::TimeDelta time_queued) override;
// DnsTaskResultsManager::Delegate implementation:
void OnServiceEndpointsUpdated() override;
void StartMdnsTask();
void OnMdnsTaskComplete();
void OnMdnsImmediateFailure(int rv);
void StartNat64Task();
void OnNat64TaskComplete();
void RecordJobHistograms(const HostCache::Entry& results,
std::optional<TaskType> task_type);
void MaybeCacheResult(const HostCache::Entry& results,
base::TimeDelta ttl,
bool secure);
// 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. Update the key to reflect |secure|,
// which indicates whether or not the result was obtained securely.
void CompleteRequests(const HostCache::Entry& results,
base::TimeDelta ttl,
bool allow_cache,
bool secure,
std::optional<TaskType> task_type);
void CompleteRequestsWithoutCache(
const HostCache::Entry& results,
std::optional<HostCache::EntryStaleness> stale_info,
TaskType task_type);
// Convenience wrapper for CompleteRequests in case of failure.
void CompleteRequestsWithError(int net_error,
std::optional<TaskType> task_type);
RequestPriority priority() const override;
// Number of non-canceled requests in |requests_|.
size_t num_active_requests() const { return priority_tracker_.total_count(); }
base::WeakPtr<HostResolverManager> resolver_;
const JobKey key_;
const ResolveHostParameters::CacheUsage cache_usage_;
// TODO(crbug.com/41462480): Consider allowing requests within a single Job to
// have different HostCaches.
const raw_ptr<HostCache> host_cache_;
struct CompletionResult {
const HostCache::Entry entry;
base::TimeDelta ttl;
bool secure;
};
// Results to use in last-ditch attempt to complete request.
std::vector<CompletionResult> completion_results_;
// The sequence of tasks to run in this Job. Tasks may be aborted and removed
// from the sequence, but otherwise the tasks will run in order until a
// successful result is found.
std::deque<TaskType> tasks_;
// Whether the job is running.
bool job_running_ = false;
// Tracks the highest priority across |requests_|.
PriorityTracker priority_tracker_;
bool had_non_speculative_request_ = false;
// Number of slots occupied by this Job in |dispatcher_|. Should be 0 when
// the job is not registered with any dispatcher.
int num_occupied_job_slots_ = 0;
// True once this Job has been sent to `resolver_->dispatcher_`.
bool dispatched_ = false;
// Result of DnsTask.
int dns_task_error_ = OK;
raw_ptr<const base::TickClock> tick_clock_;
base::TimeTicks start_time_;
HostResolver::HttpsSvcbOptions https_svcb_options_;
NetLogWithSource net_log_;
// Resolves the host using the system DNS resolver, which can be overridden
// for tests.
std::unique_ptr<HostResolverSystemTask> system_task_;
// Resolves the host using a DnsTransaction.
std::unique_ptr<HostResolverDnsTask> dns_task_;
// Resolves the host using MDnsClient.
std::unique_ptr<HostResolverMdnsTask> mdns_task_;
// Perform NAT64 address synthesis to a given IPv4 literal.
std::unique_ptr<HostResolverNat64Task> nat64_task_;
// All Requests waiting for the result of this Job. Some can be canceled.
base::LinkedList<RequestImpl> requests_;
// All ServiceEndpointRequests waiting for the result of this Job. Some can
// be canceled.
base::LinkedList<ServiceEndpointRequestImpl> service_endpoint_requests_;
// Builds and updates intermediate service endpoints while executing
// a DnsTransaction.
std::unique_ptr<DnsTaskResultsManager> dns_task_results_manager_;
// A handle used for |dispatcher_|.
PrioritizedDispatcher::Handle handle_;
// Iterator to |this| in the JobMap. |nullopt| if not owned by the JobMap.
std::optional<JobMap::iterator> self_iterator_;
base::TimeDelta total_transaction_time_queued_;
base::WeakPtrFactory<Job> weak_ptr_factory_{this};
};
} // namespace net
#endif // NET_DNS_HOST_RESOLVER_MANAGER_JOB_H_
|