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