| // 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 <deque> |
| #include <memory> |
| #include <optional> |
| #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" |
| #include "third_party/abseil-cpp/absl/types/variant.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); |
| |
| // 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; |
| size_t counts_[NUM_PRIORITIES] = {}; |
| }; |
| |
| 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, |
| HostCache::Entry results, |
| bool secure) override; |
| void OnIntermediateTransactionsComplete( |
| std::optional<HostResolverDnsTask::SingleTransactionResults> |
| single_transaction_results) 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_ |