xref: /aosp_15_r20/external/cronet/net/proxy_resolution/win/dhcp_pac_file_fetcher_win.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
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/proxy_resolution/win/dhcp_pac_file_fetcher_win.h"
6 
7 #include <winsock2.h>
8 
9 #include <iphlpapi.h>
10 
11 #include <memory>
12 #include <vector>
13 
14 #include "base/containers/queue.h"
15 #include "base/functional/bind.h"
16 #include "base/functional/callback_helpers.h"
17 #include "base/memory/free_deleter.h"
18 #include "base/synchronization/lock.h"
19 #include "base/task/task_runner.h"
20 #include "base/task/thread_pool.h"
21 #include "base/threading/scoped_blocking_call.h"
22 #include "base/values.h"
23 #include "net/base/net_errors.h"
24 #include "net/log/net_log.h"
25 #include "net/proxy_resolution/win/dhcp_pac_file_adapter_fetcher_win.h"
26 
27 namespace net {
28 
29 namespace {
30 
31 // Returns true if |adapter| should be considered when probing for WPAD via
32 // DHCP.
IsDhcpCapableAdapter(IP_ADAPTER_ADDRESSES * adapter)33 bool IsDhcpCapableAdapter(IP_ADAPTER_ADDRESSES* adapter) {
34   if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
35     return false;
36   if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0)
37     return false;
38 
39   // Don't probe interfaces which are not up and ready to pass packets.
40   //
41   // This is a speculative fix for https://crbug.com/770201, in case calling
42   // dhcpsvc!DhcpRequestParams on interfaces that aren't ready yet blocks for
43   // a long time.
44   //
45   // Since ConfiguredProxyResolutionService restarts WPAD probes in response to
46   // other network level changes, this will likely get called again once the
47   // interface is up.
48   if (adapter->OperStatus != IfOperStatusUp)
49     return false;
50 
51   return true;
52 }
53 
54 }  // namespace
55 
56 // This struct contains logging information describing how
57 // GetCandidateAdapterNames() performed, for output to NetLog.
58 struct DhcpAdapterNamesLoggingInfo {
59   DhcpAdapterNamesLoggingInfo() = default;
60 
61   DhcpAdapterNamesLoggingInfo(const DhcpAdapterNamesLoggingInfo&) = delete;
62   DhcpAdapterNamesLoggingInfo& operator=(const DhcpAdapterNamesLoggingInfo&) =
63       delete;
64 
65   ~DhcpAdapterNamesLoggingInfo() = default;
66 
67   // The error that iphlpapi!GetAdaptersAddresses returned.
68   ULONG error;
69 
70   // The adapters list that iphlpapi!GetAdaptersAddresses returned.
71   std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> adapters;
72 
73   // The time immediately before GetCandidateAdapterNames was posted to a worker
74   // thread from the origin thread.
75   base::TimeTicks origin_thread_start_time;
76 
77   // The time when GetCandidateAdapterNames began running on the worker thread.
78   base::TimeTicks worker_thread_start_time;
79 
80   // The time when GetCandidateAdapterNames completed running on the worker
81   // thread.
82   base::TimeTicks worker_thread_end_time;
83 
84   // The time when control returned to the origin thread
85   // (OnGetCandidateAdapterNamesDone)
86   base::TimeTicks origin_thread_end_time;
87 };
88 
89 namespace {
90 
91 // Maximum number of DHCP lookup tasks running concurrently. This is chosen
92 // based on the following UMA data:
93 // - When OnWaitTimer fires, ~99.8% of users have 6 or fewer network
94 //   adapters enabled for DHCP in total.
95 // - At the same measurement point, ~99.7% of users have 3 or fewer pending
96 //   DHCP adapter lookups.
97 // - There is however a very long and thin tail of users who have
98 //   systems reporting up to 100+ adapters (this must be some very weird
99 //   OS bug (?), probably the cause of http://crbug.com/240034).
100 //
101 // Th value is chosen such that DHCP lookup tasks don't prevent other tasks from
102 // running even on systems that report a huge number of network adapters, while
103 // giving a good chance of getting back results for any responsive adapters.
104 constexpr int kMaxConcurrentDhcpLookupTasks = 12;
105 
106 // How long to wait at maximum after we get results (a PAC file or
107 // knowledge that no PAC file is configured) from whichever network
108 // adapter finishes first.
109 constexpr base::TimeDelta kMaxWaitAfterFirstResult = base::Milliseconds(400);
110 
111 // A TaskRunner that never schedules more than |kMaxConcurrentDhcpLookupTasks|
112 // tasks concurrently.
113 class TaskRunnerWithCap : public base::TaskRunner {
114  public:
115   TaskRunnerWithCap() = default;
116 
117   TaskRunnerWithCap(const TaskRunnerWithCap&) = delete;
118   TaskRunnerWithCap& operator=(const TaskRunnerWithCap&) = delete;
119 
PostDelayedTask(const base::Location & from_here,base::OnceClosure task,base::TimeDelta delay)120   bool PostDelayedTask(const base::Location& from_here,
121                        base::OnceClosure task,
122                        base::TimeDelta delay) override {
123     // Delayed tasks are not supported.
124     DCHECK(delay.is_zero());
125 
126     // Wrap the task in a callback that runs |task|, then tries to schedule a
127     // task from |pending_tasks_|.
128     base::OnceClosure wrapped_task =
129         base::BindOnce(&TaskRunnerWithCap::RunTaskAndSchedulePendingTask, this,
130                        std::move(task));
131 
132     {
133       base::AutoLock auto_lock(lock_);
134 
135       // If |kMaxConcurrentDhcpLookupTasks| tasks are scheduled, move the task
136       // to |pending_tasks_|.
137       DCHECK_LE(num_scheduled_tasks_, kMaxConcurrentDhcpLookupTasks);
138       if (num_scheduled_tasks_ == kMaxConcurrentDhcpLookupTasks) {
139         pending_tasks_.emplace(from_here, std::move(wrapped_task));
140         return true;
141       }
142 
143       // If less than |kMaxConcurrentDhcpLookupTasks| tasks are scheduled,
144       // increment |num_scheduled_tasks_| and schedule the task.
145       ++num_scheduled_tasks_;
146     }
147 
148     task_runner_->PostTask(from_here, std::move(wrapped_task));
149     return true;
150   }
151 
152  private:
153   struct LocationAndTask {
154     LocationAndTask() = default;
LocationAndTasknet::__anon1e97fd2a0211::TaskRunnerWithCap::LocationAndTask155     LocationAndTask(const base::Location& from_here, base::OnceClosure task)
156         : from_here(from_here), task(std::move(task)) {}
157     base::Location from_here;
158     base::OnceClosure task;
159   };
160 
161   ~TaskRunnerWithCap() override = default;
162 
RunTaskAndSchedulePendingTask(base::OnceClosure task)163   void RunTaskAndSchedulePendingTask(base::OnceClosure task) {
164     // Run |task|.
165     std::move(task).Run();
166 
167     // If |pending_tasks_| is non-empty, schedule a task from it. Otherwise,
168     // decrement |num_scheduled_tasks_|.
169     LocationAndTask task_to_schedule;
170 
171     {
172       base::AutoLock auto_lock(lock_);
173 
174       DCHECK_GT(num_scheduled_tasks_, 0);
175       if (pending_tasks_.empty()) {
176         --num_scheduled_tasks_;
177         return;
178       }
179 
180       task_to_schedule = std::move(pending_tasks_.front());
181       pending_tasks_.pop();
182     }
183 
184     DCHECK(task_to_schedule.task);
185     task_runner_->PostTask(task_to_schedule.from_here,
186                            std::move(task_to_schedule.task));
187   }
188 
189   const scoped_refptr<base::TaskRunner> task_runner_ =
190       base::ThreadPool::CreateTaskRunner(
191           {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
192            base::TaskPriority::USER_VISIBLE});
193 
194   // Synchronizes access to members below.
195   base::Lock lock_;
196 
197   // Number of tasks that are currently scheduled.
198   int num_scheduled_tasks_ = 0;
199 
200   // Tasks that are waiting to be scheduled.
201   base::queue<LocationAndTask> pending_tasks_;
202 };
203 
NetLogGetAdaptersDoneParams(DhcpAdapterNamesLoggingInfo * info)204 base::Value::Dict NetLogGetAdaptersDoneParams(
205     DhcpAdapterNamesLoggingInfo* info) {
206   base::Value::Dict result;
207 
208   // Add information on each of the adapters enumerated (including those that
209   // were subsequently skipped).
210   base::Value::List adapters_list;
211   for (IP_ADAPTER_ADDRESSES* adapter = info->adapters.get(); adapter;
212        adapter = adapter->Next) {
213     base::Value::Dict adapter_value;
214 
215     adapter_value.Set("AdapterName", adapter->AdapterName);
216     adapter_value.Set("IfType", static_cast<int>(adapter->IfType));
217     adapter_value.Set("Flags", static_cast<int>(adapter->Flags));
218     adapter_value.Set("OperStatus", static_cast<int>(adapter->OperStatus));
219     adapter_value.Set("TunnelType", static_cast<int>(adapter->TunnelType));
220 
221     // "skipped" means the adapter was not ultimately chosen as a candidate for
222     // testing WPAD.
223     bool skipped = !IsDhcpCapableAdapter(adapter);
224     adapter_value.Set("skipped", base::Value(skipped));
225 
226     adapters_list.Append(std::move(adapter_value));
227   }
228   result.Set("adapters", std::move(adapters_list));
229 
230   result.Set("origin_to_worker_thread_hop_dt",
231              static_cast<int>((info->worker_thread_start_time -
232                                info->origin_thread_start_time)
233                                   .InMilliseconds()));
234   result.Set("worker_to_origin_thread_hop_dt",
235              static_cast<int>(
236                  (info->origin_thread_end_time - info->worker_thread_end_time)
237                      .InMilliseconds()));
238   result.Set("worker_dt", static_cast<int>((info->worker_thread_end_time -
239                                             info->worker_thread_start_time)
240                                                .InMilliseconds()));
241 
242   if (info->error != ERROR_SUCCESS)
243     result.Set("error", static_cast<int>(info->error));
244 
245   return result;
246 }
247 
NetLogFetcherDoneParams(int fetcher_index,int net_error)248 base::Value::Dict NetLogFetcherDoneParams(int fetcher_index, int net_error) {
249   base::Value::Dict result;
250 
251   result.Set("fetcher_index", fetcher_index);
252   result.Set("net_error", net_error);
253 
254   return result;
255 }
256 
257 }  // namespace
258 
DhcpPacFileFetcherWin(URLRequestContext * url_request_context)259 DhcpPacFileFetcherWin::DhcpPacFileFetcherWin(
260     URLRequestContext* url_request_context)
261     : url_request_context_(url_request_context),
262       task_runner_(base::MakeRefCounted<TaskRunnerWithCap>()) {
263   DCHECK(url_request_context_);
264 }
265 
~DhcpPacFileFetcherWin()266 DhcpPacFileFetcherWin::~DhcpPacFileFetcherWin() {
267   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
268   // Count as user-initiated if we are not yet in STATE_DONE.
269   Cancel();
270 }
271 
Fetch(std::u16string * utf16_text,CompletionOnceCallback callback,const NetLogWithSource & net_log,const NetworkTrafficAnnotationTag traffic_annotation)272 int DhcpPacFileFetcherWin::Fetch(
273     std::u16string* utf16_text,
274     CompletionOnceCallback callback,
275     const NetLogWithSource& net_log,
276     const NetworkTrafficAnnotationTag traffic_annotation) {
277   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
278   if (state_ != STATE_START && state_ != STATE_DONE) {
279     NOTREACHED();
280     return ERR_UNEXPECTED;
281   }
282 
283   net_log_ = net_log;
284 
285   if (!url_request_context_)
286     return ERR_CONTEXT_SHUT_DOWN;
287 
288   state_ = STATE_WAIT_ADAPTERS;
289   callback_ = std::move(callback);
290   destination_string_ = utf16_text;
291 
292   net_log.BeginEvent(NetLogEventType::WPAD_DHCP_WIN_FETCH);
293 
294   // TODO(eroman): This event is not ended in the case of cancellation.
295   net_log.BeginEvent(NetLogEventType::WPAD_DHCP_WIN_GET_ADAPTERS);
296 
297   last_query_ = ImplCreateAdapterQuery();
298   last_query_->logging_info()->origin_thread_start_time =
299       base::TimeTicks::Now();
300 
301   task_runner_->PostTaskAndReply(
302       FROM_HERE,
303       base::BindOnce(
304           &DhcpPacFileFetcherWin::AdapterQuery::GetCandidateAdapterNames,
305           last_query_.get()),
306       base::BindOnce(&DhcpPacFileFetcherWin::OnGetCandidateAdapterNamesDone,
307                      AsWeakPtr(), last_query_, traffic_annotation));
308 
309   return ERR_IO_PENDING;
310 }
311 
Cancel()312 void DhcpPacFileFetcherWin::Cancel() {
313   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
314 
315   CancelImpl();
316 }
317 
OnShutdown()318 void DhcpPacFileFetcherWin::OnShutdown() {
319   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
320 
321   // Cancel current request, if there is one.
322   CancelImpl();
323 
324   // Prevent future network requests.
325   url_request_context_ = nullptr;
326 }
327 
CancelImpl()328 void DhcpPacFileFetcherWin::CancelImpl() {
329   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
330 
331   if (state_ != STATE_DONE) {
332     callback_.Reset();
333     wait_timer_.Stop();
334     state_ = STATE_DONE;
335 
336     for (FetcherVector::iterator it = fetchers_.begin();
337          it != fetchers_.end();
338          ++it) {
339       (*it)->Cancel();
340     }
341 
342     fetchers_.clear();
343   }
344 }
345 
OnGetCandidateAdapterNamesDone(scoped_refptr<AdapterQuery> query,const NetworkTrafficAnnotationTag traffic_annotation)346 void DhcpPacFileFetcherWin::OnGetCandidateAdapterNamesDone(
347     scoped_refptr<AdapterQuery> query,
348     const NetworkTrafficAnnotationTag traffic_annotation) {
349   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
350 
351   // This can happen if this object is reused for multiple queries,
352   // and a previous query was cancelled before it completed.
353   if (query.get() != last_query_.get())
354     return;
355   last_query_ = nullptr;
356 
357   DhcpAdapterNamesLoggingInfo* logging_info = query->logging_info();
358   logging_info->origin_thread_end_time = base::TimeTicks::Now();
359 
360   net_log_.EndEvent(NetLogEventType::WPAD_DHCP_WIN_GET_ADAPTERS,
361                     [&] { return NetLogGetAdaptersDoneParams(logging_info); });
362 
363   // Enable unit tests to wait for this to happen; in production this function
364   // call is a no-op.
365   ImplOnGetCandidateAdapterNamesDone();
366 
367   // We may have been cancelled.
368   if (state_ != STATE_WAIT_ADAPTERS)
369     return;
370 
371   state_ = STATE_NO_RESULTS;
372 
373   const std::set<std::string>& adapter_names = query->adapter_names();
374 
375   if (adapter_names.empty()) {
376     TransitionToDone();
377     return;
378   }
379 
380   for (const std::string& adapter_name : adapter_names) {
381     std::unique_ptr<DhcpPacFileAdapterFetcher> fetcher(
382         ImplCreateAdapterFetcher());
383     size_t fetcher_index = fetchers_.size();
384     fetcher->Fetch(adapter_name,
385                    base::BindOnce(&DhcpPacFileFetcherWin::OnFetcherDone,
386                                   base::Unretained(this), fetcher_index),
387                    traffic_annotation);
388     fetchers_.push_back(std::move(fetcher));
389   }
390   num_pending_fetchers_ = fetchers_.size();
391 }
392 
GetFetcherName() const393 std::string DhcpPacFileFetcherWin::GetFetcherName() const {
394   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
395   return "win";
396 }
397 
GetPacURL() const398 const GURL& DhcpPacFileFetcherWin::GetPacURL() const {
399   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
400   DCHECK_EQ(state_, STATE_DONE);
401 
402   return pac_url_;
403 }
404 
OnFetcherDone(size_t fetcher_index,int result)405 void DhcpPacFileFetcherWin::OnFetcherDone(size_t fetcher_index,
406                                           int result) {
407   DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
408 
409   net_log_.AddEvent(NetLogEventType::WPAD_DHCP_WIN_ON_FETCHER_DONE, [&] {
410     return NetLogFetcherDoneParams(fetcher_index, result);
411   });
412 
413   if (--num_pending_fetchers_ == 0) {
414     TransitionToDone();
415     return;
416   }
417 
418   // If the only pending adapters are those less preferred than one
419   // with a valid PAC script, we do not need to wait any longer.
420   for (FetcherVector::iterator it = fetchers_.begin();
421        it != fetchers_.end();
422        ++it) {
423     bool did_finish = (*it)->DidFinish();
424     int fetch_result = (*it)->GetResult();
425     if (did_finish && fetch_result == OK) {
426       TransitionToDone();
427       return;
428     }
429     if (!did_finish || fetch_result != ERR_PAC_NOT_IN_DHCP) {
430       break;
431     }
432   }
433 
434   // Once we have a single result, we set a maximum on how long to wait
435   // for the rest of the results.
436   if (state_ == STATE_NO_RESULTS) {
437     state_ = STATE_SOME_RESULTS;
438     net_log_.AddEvent(NetLogEventType::WPAD_DHCP_WIN_START_WAIT_TIMER);
439     wait_timer_.Start(FROM_HERE,
440         ImplGetMaxWait(), this, &DhcpPacFileFetcherWin::OnWaitTimer);
441   }
442 }
443 
OnWaitTimer()444 void DhcpPacFileFetcherWin::OnWaitTimer() {
445   DCHECK_EQ(state_, STATE_SOME_RESULTS);
446 
447   net_log_.AddEvent(NetLogEventType::WPAD_DHCP_WIN_ON_WAIT_TIMER);
448   TransitionToDone();
449 }
450 
TransitionToDone()451 void DhcpPacFileFetcherWin::TransitionToDone() {
452   DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
453 
454   int used_fetcher_index = -1;
455   int result = ERR_PAC_NOT_IN_DHCP;  // Default if no fetchers.
456   if (!fetchers_.empty()) {
457     // Scan twice for the result; once through the whole list for success,
458     // then if no success, return result for most preferred network adapter,
459     // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error.
460     // Default to ERR_ABORTED if no fetcher completed.
461     result = ERR_ABORTED;
462     for (size_t i = 0; i < fetchers_.size(); ++i) {
463       const auto& fetcher = fetchers_[i];
464       if (fetcher->DidFinish() && fetcher->GetResult() == OK) {
465         result = OK;
466         *destination_string_ = fetcher->GetPacScript();
467         pac_url_ = fetcher->GetPacURL();
468         used_fetcher_index = i;
469         break;
470       }
471     }
472     if (result != OK) {
473       destination_string_->clear();
474       for (size_t i = 0; i < fetchers_.size(); ++i) {
475         const auto& fetcher = fetchers_[i];
476         if (fetcher->DidFinish()) {
477           result = fetcher->GetResult();
478           used_fetcher_index = i;
479           if (result != ERR_PAC_NOT_IN_DHCP) {
480             break;
481           }
482         }
483       }
484     }
485   }
486 
487   CompletionOnceCallback callback = std::move(callback_);
488   CancelImpl();
489   DCHECK_EQ(state_, STATE_DONE);
490   DCHECK(fetchers_.empty());
491 
492   net_log_.EndEvent(NetLogEventType::WPAD_DHCP_WIN_FETCH, [&] {
493     return NetLogFetcherDoneParams(used_fetcher_index, result);
494   });
495 
496   // We may be deleted re-entrantly within this outcall.
497   std::move(callback).Run(result);
498 }
499 
num_pending_fetchers() const500 int DhcpPacFileFetcherWin::num_pending_fetchers() const {
501   return num_pending_fetchers_;
502 }
503 
url_request_context() const504 URLRequestContext* DhcpPacFileFetcherWin::url_request_context() const {
505   return url_request_context_;
506 }
507 
GetTaskRunner()508 scoped_refptr<base::TaskRunner> DhcpPacFileFetcherWin::GetTaskRunner() {
509   return task_runner_;
510 }
511 
512 std::unique_ptr<DhcpPacFileAdapterFetcher>
ImplCreateAdapterFetcher()513 DhcpPacFileFetcherWin::ImplCreateAdapterFetcher() {
514   return std::make_unique<DhcpPacFileAdapterFetcher>(url_request_context_,
515                                                      task_runner_);
516 }
517 
518 scoped_refptr<DhcpPacFileFetcherWin::AdapterQuery>
ImplCreateAdapterQuery()519 DhcpPacFileFetcherWin::ImplCreateAdapterQuery() {
520   return base::MakeRefCounted<AdapterQuery>();
521 }
522 
ImplGetMaxWait()523 base::TimeDelta DhcpPacFileFetcherWin::ImplGetMaxWait() {
524   return kMaxWaitAfterFirstResult;
525 }
526 
GetCandidateAdapterNames(std::set<std::string> * adapter_names,DhcpAdapterNamesLoggingInfo * info)527 bool DhcpPacFileFetcherWin::GetCandidateAdapterNames(
528     std::set<std::string>* adapter_names,
529     DhcpAdapterNamesLoggingInfo* info) {
530   DCHECK(adapter_names);
531   adapter_names->clear();
532 
533   // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to
534   // avoid reallocation.
535   ULONG adapters_size = 15000;
536   std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> adapters;
537   ULONG error = ERROR_SUCCESS;
538   int num_tries = 0;
539 
540   do {
541     adapters.reset(static_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
542     // Return only unicast addresses, and skip information we do not need.
543     base::ScopedBlockingCall scoped_blocking_call(
544         FROM_HERE, base::BlockingType::MAY_BLOCK);
545     error = GetAdaptersAddresses(
546         AF_UNSPEC,
547         GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
548             GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
549         nullptr, adapters.get(), &adapters_size);
550     ++num_tries;
551   } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3);
552 
553   if (info)
554     info->error = error;
555 
556   if (error == ERROR_NO_DATA) {
557     // There are no adapters that we care about.
558     return true;
559   }
560 
561   if (error != ERROR_SUCCESS) {
562     LOG(WARNING) << "Unexpected error retrieving WPAD configuration from DHCP.";
563     return false;
564   }
565 
566   IP_ADAPTER_ADDRESSES* adapter = nullptr;
567   for (adapter = adapters.get(); adapter; adapter = adapter->Next) {
568     if (IsDhcpCapableAdapter(adapter)) {
569       DCHECK(adapter->AdapterName);
570       adapter_names->insert(adapter->AdapterName);
571     }
572   }
573 
574   // Transfer the buffer containing the adapters, so it can be used later for
575   // emitting NetLog parameters from the origin thread.
576   if (info)
577     info->adapters = std::move(adapters);
578   return true;
579 }
580 
AdapterQuery()581 DhcpPacFileFetcherWin::AdapterQuery::AdapterQuery()
582     : logging_info_(std::make_unique<DhcpAdapterNamesLoggingInfo>()) {}
583 
GetCandidateAdapterNames()584 void DhcpPacFileFetcherWin::AdapterQuery::GetCandidateAdapterNames() {
585   logging_info_->error = ERROR_NO_DATA;
586   logging_info_->adapters.reset();
587   logging_info_->worker_thread_start_time = base::TimeTicks::Now();
588 
589   ImplGetCandidateAdapterNames(&adapter_names_, logging_info_.get());
590 
591   logging_info_->worker_thread_end_time = base::TimeTicks::Now();
592 }
593 
594 const std::set<std::string>&
adapter_names() const595 DhcpPacFileFetcherWin::AdapterQuery::adapter_names() const {
596   return adapter_names_;
597 }
598 
ImplGetCandidateAdapterNames(std::set<std::string> * adapter_names,DhcpAdapterNamesLoggingInfo * info)599 bool DhcpPacFileFetcherWin::AdapterQuery::ImplGetCandidateAdapterNames(
600     std::set<std::string>* adapter_names,
601     DhcpAdapterNamesLoggingInfo* info) {
602   return DhcpPacFileFetcherWin::GetCandidateAdapterNames(adapter_names,
603                                                          info);
604 }
605 
606 DhcpPacFileFetcherWin::AdapterQuery::~AdapterQuery() = default;
607 
608 }  // namespace net
609