| // Copyright (c) 2012 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/proxy_resolution/dhcp_pac_file_fetcher_win.h" | 
 |  | 
 | #include <memory> | 
 | #include <vector> | 
 |  | 
 | #include "starboard/types.h" | 
 |  | 
 | #include "starboard/memory.h" | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/bind_helpers.h" | 
 | #include "base/containers/queue.h" | 
 | #include "base/memory/free_deleter.h" | 
 | #include "base/synchronization/lock.h" | 
 | #include "base/task/post_task.h" | 
 | #include "base/task_runner.h" | 
 | #include "base/threading/scoped_blocking_call.h" | 
 | #include "base/values.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/log/net_log.h" | 
 | #include "net/proxy_resolution/dhcp_pac_file_adapter_fetcher_win.h" | 
 |  | 
 | #include <winsock2.h> | 
 | #include <iphlpapi.h> | 
 |  | 
 | namespace net { | 
 |  | 
 | namespace { | 
 |  | 
 | // Returns true if |adapter| should be considered when probing for WPAD via | 
 | // DHCP. | 
 | bool IsDhcpCapableAdapter(IP_ADAPTER_ADDRESSES* adapter) { | 
 |   if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) | 
 |     return false; | 
 |   if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0) | 
 |     return false; | 
 |  | 
 |   // Don't probe interfaces which are not up and ready to pass packets. | 
 |   // | 
 |   // This is a speculative fix for https://crbug.com/770201, in case calling | 
 |   // dhcpsvc!DhcpRequestParams on interfaces that aren't ready yet blocks for | 
 |   // a long time. | 
 |   // | 
 |   // Since ProxyResolutionService restarts WPAD probes in response to other | 
 |   // network level changes, this will likely get called again once the | 
 |   // interface is up. | 
 |   if (adapter->OperStatus != IfOperStatusUp) | 
 |     return false; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // This struct contains logging information describing how | 
 | // GetCandidateAdapterNames() performed, for output to NetLog. | 
 | struct DhcpAdapterNamesLoggingInfo { | 
 |   DhcpAdapterNamesLoggingInfo() = default; | 
 |   ~DhcpAdapterNamesLoggingInfo() = default; | 
 |  | 
 |   // The error that iphlpapi!GetAdaptersAddresses returned. | 
 |   ULONG error; | 
 |  | 
 |   // The adapters list that iphlpapi!GetAdaptersAddresses returned. | 
 |   std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> adapters; | 
 |  | 
 |   // The time immediately before GetCandidateAdapterNames was posted to a worker | 
 |   // thread from the origin thread. | 
 |   base::TimeTicks origin_thread_start_time; | 
 |  | 
 |   // The time when GetCandidateAdapterNames began running on the worker thread. | 
 |   base::TimeTicks worker_thread_start_time; | 
 |  | 
 |   // The time when GetCandidateAdapterNames completed running on the worker | 
 |   // thread. | 
 |   base::TimeTicks worker_thread_end_time; | 
 |  | 
 |   // The time when control returned to the origin thread | 
 |   // (OnGetCandidateAdapterNamesDone) | 
 |   base::TimeTicks origin_thread_end_time; | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(DhcpAdapterNamesLoggingInfo); | 
 | }; | 
 |  | 
 | namespace { | 
 |  | 
 | // Maximum number of DHCP lookup tasks running concurrently. This is chosen | 
 | // based on the following UMA data: | 
 | // - When OnWaitTimer fires, ~99.8% of users have 6 or fewer network | 
 | //   adapters enabled for DHCP in total. | 
 | // - At the same measurement point, ~99.7% of users have 3 or fewer pending | 
 | //   DHCP adapter lookups. | 
 | // - There is however a very long and thin tail of users who have | 
 | //   systems reporting up to 100+ adapters (this must be some very weird | 
 | //   OS bug (?), probably the cause of http://crbug.com/240034). | 
 | // | 
 | // Th value is chosen such that DHCP lookup tasks don't prevent other tasks from | 
 | // running even on systems that report a huge number of network adapters, while | 
 | // giving a good chance of getting back results for any responsive adapters. | 
 | constexpr int kMaxConcurrentDhcpLookupTasks = 12; | 
 |  | 
 | // How long to wait at maximum after we get results (a PAC file or | 
 | // knowledge that no PAC file is configured) from whichever network | 
 | // adapter finishes first. | 
 | constexpr base::TimeDelta kMaxWaitAfterFirstResult = | 
 |     base::TimeDelta::FromMilliseconds(400); | 
 |  | 
 | // A TaskRunner that never schedules more than |kMaxConcurrentDhcpLookupTasks| | 
 | // tasks concurrently. | 
 | class TaskRunnerWithCap : public base::TaskRunner { | 
 |  public: | 
 |   TaskRunnerWithCap() = default; | 
 |  | 
 |   bool PostDelayedTask(const base::Location& from_here, | 
 |                        base::OnceClosure task, | 
 |                        base::TimeDelta delay) override { | 
 |     // Delayed tasks are not supported. | 
 |     DCHECK(delay.is_zero()); | 
 |  | 
 |     // Wrap the task in a callback that runs |task|, then tries to schedule a | 
 |     // task from |pending_tasks_|. | 
 |     base::OnceClosure wrapped_task = | 
 |         base::BindOnce(&TaskRunnerWithCap::RunTaskAndSchedulePendingTask, this, | 
 |                        std::move(task)); | 
 |  | 
 |     { | 
 |       base::AutoLock auto_lock(lock_); | 
 |  | 
 |       // If |kMaxConcurrentDhcpLookupTasks| tasks are scheduled, move the task | 
 |       // to |pending_tasks_|. | 
 |       DCHECK_LE(num_scheduled_tasks_, kMaxConcurrentDhcpLookupTasks); | 
 |       if (num_scheduled_tasks_ == kMaxConcurrentDhcpLookupTasks) { | 
 |         pending_tasks_.emplace(from_here, std::move(wrapped_task)); | 
 |         return true; | 
 |       } | 
 |  | 
 |       // If less than |kMaxConcurrentDhcpLookupTasks| tasks are scheduled, | 
 |       // increment |num_scheduled_tasks_| and schedule the task. | 
 |       ++num_scheduled_tasks_; | 
 |     } | 
 |  | 
 |     task_runner_->PostTask(from_here, std::move(wrapped_task)); | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool RunsTasksInCurrentSequence() const override { | 
 |     return task_runner_->RunsTasksInCurrentSequence(); | 
 |   } | 
 |  | 
 |  private: | 
 |   struct LocationAndTask { | 
 |     LocationAndTask() = default; | 
 |     LocationAndTask(const base::Location& from_here, base::OnceClosure task) | 
 |         : from_here(from_here), task(std::move(task)) {} | 
 |     base::Location from_here; | 
 |     base::OnceClosure task; | 
 |   }; | 
 |  | 
 |   ~TaskRunnerWithCap() override = default; | 
 |  | 
 |   void RunTaskAndSchedulePendingTask(base::OnceClosure task) { | 
 |     // Run |task|. | 
 |     std::move(task).Run(); | 
 |  | 
 |     // If |pending_tasks_| is non-empty, schedule a task from it. Otherwise, | 
 |     // decrement |num_scheduled_tasks_|. | 
 |     LocationAndTask task_to_schedule; | 
 |  | 
 |     { | 
 |       base::AutoLock auto_lock(lock_); | 
 |  | 
 |       DCHECK_GT(num_scheduled_tasks_, 0); | 
 |       if (pending_tasks_.empty()) { | 
 |         --num_scheduled_tasks_; | 
 |         return; | 
 |       } | 
 |  | 
 |       task_to_schedule = std::move(pending_tasks_.front()); | 
 |       pending_tasks_.pop(); | 
 |     } | 
 |  | 
 |     DCHECK(task_to_schedule.task); | 
 |     task_runner_->PostTask(task_to_schedule.from_here, | 
 |                            std::move(task_to_schedule.task)); | 
 |   } | 
 |  | 
 |   const scoped_refptr<base::TaskRunner> task_runner_ = | 
 |       base::CreateTaskRunnerWithTraits( | 
 |           {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, | 
 |            base::TaskPriority::USER_VISIBLE}); | 
 |  | 
 |   // Synchronizes access to members below. | 
 |   base::Lock lock_; | 
 |  | 
 |   // Number of tasks that are currently scheduled. | 
 |   int num_scheduled_tasks_ = 0; | 
 |  | 
 |   // Tasks that are waiting to be scheduled. | 
 |   base::queue<LocationAndTask> pending_tasks_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(TaskRunnerWithCap); | 
 | }; | 
 |  | 
 | // Helper to set an integer value into a base::DictionaryValue. Because of | 
 | // C++'s implicit narrowing casts to |int|, this can be called with int64_t and | 
 | // ULONG too. | 
 | void SetInt(base::StringPiece key, int value, base::DictionaryValue* dict) { | 
 |   dict->SetKey(key, base::Value(value)); | 
 | } | 
 |  | 
 | std::unique_ptr<base::Value> NetLogGetAdaptersDoneCallback( | 
 |     DhcpAdapterNamesLoggingInfo* info, | 
 |     NetLogCaptureMode /* capture_mode */) { | 
 |   std::unique_ptr<base::DictionaryValue> result = | 
 |       std::make_unique<base::DictionaryValue>(); | 
 |  | 
 |   // Add information on each of the adapters enumerated (including those that | 
 |   // were subsequently skipped). | 
 |   base::ListValue adapters_value; | 
 |   for (IP_ADAPTER_ADDRESSES* adapter = info->adapters.get(); adapter; | 
 |        adapter = adapter->Next) { | 
 |     base::DictionaryValue adapter_value; | 
 |  | 
 |     adapter_value.SetKey("AdapterName", base::Value(adapter->AdapterName)); | 
 |     SetInt("IfType", adapter->IfType, &adapter_value); | 
 |     SetInt("Flags", adapter->Flags, &adapter_value); | 
 |     SetInt("OperStatus", adapter->OperStatus, &adapter_value); | 
 |     SetInt("TunnelType", adapter->TunnelType, &adapter_value); | 
 |  | 
 |     // "skipped" means the adapter was not ultimately chosen as a candidate for | 
 |     // testing WPAD. | 
 |     bool skipped = !IsDhcpCapableAdapter(adapter); | 
 |     adapter_value.SetKey("skipped", base::Value(skipped)); | 
 |  | 
 |     adapters_value.GetList().push_back(std::move(adapter_value)); | 
 |   } | 
 |   result->SetKey("adapters", std::move(adapters_value)); | 
 |  | 
 |   SetInt("origin_to_worker_thread_hop_dt", | 
 |          (info->worker_thread_start_time - info->origin_thread_start_time) | 
 |              .InMilliseconds(), | 
 |          result.get()); | 
 |   SetInt("worker_to_origin_thread_hop_dt", | 
 |          (info->origin_thread_end_time - info->worker_thread_end_time) | 
 |              .InMilliseconds(), | 
 |          result.get()); | 
 |   SetInt("worker_dt", | 
 |          (info->worker_thread_end_time - info->worker_thread_start_time) | 
 |              .InMilliseconds(), | 
 |          result.get()); | 
 |  | 
 |   if (info->error != ERROR_SUCCESS) | 
 |     SetInt("error", info->error, result.get()); | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | std::unique_ptr<base::Value> NetLogFetcherDoneCallback( | 
 |     int fetcher_index, | 
 |     int net_error, | 
 |     NetLogCaptureMode /* capture_mode */) { | 
 |   std::unique_ptr<base::DictionaryValue> result = | 
 |       std::make_unique<base::DictionaryValue>(); | 
 |  | 
 |   result->SetKey("fetcher_index", base::Value(fetcher_index)); | 
 |   result->SetKey("net_error", base::Value(net_error)); | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | DhcpPacFileFetcherWin::DhcpPacFileFetcherWin( | 
 |     URLRequestContext* url_request_context) | 
 |     : state_(STATE_START), | 
 |       num_pending_fetchers_(0), | 
 |       destination_string_(NULL), | 
 |       url_request_context_(url_request_context), | 
 |       task_runner_(base::MakeRefCounted<TaskRunnerWithCap>()) { | 
 |   DCHECK(url_request_context_); | 
 | } | 
 |  | 
 | DhcpPacFileFetcherWin::~DhcpPacFileFetcherWin() { | 
 |   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
 |   // Count as user-initiated if we are not yet in STATE_DONE. | 
 |   Cancel(); | 
 | } | 
 |  | 
 | int DhcpPacFileFetcherWin::Fetch( | 
 |     base::string16* utf16_text, | 
 |     CompletionOnceCallback callback, | 
 |     const NetLogWithSource& net_log, | 
 |     const NetworkTrafficAnnotationTag traffic_annotation) { | 
 |   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
 |   if (state_ != STATE_START && state_ != STATE_DONE) { | 
 |     NOTREACHED(); | 
 |     return ERR_UNEXPECTED; | 
 |   } | 
 |  | 
 |   net_log_ = net_log; | 
 |  | 
 |   if (!url_request_context_) | 
 |     return ERR_CONTEXT_SHUT_DOWN; | 
 |  | 
 |   state_ = STATE_WAIT_ADAPTERS; | 
 |   callback_ = std::move(callback); | 
 |   destination_string_ = utf16_text; | 
 |  | 
 |   net_log.BeginEvent(NetLogEventType::WPAD_DHCP_WIN_FETCH); | 
 |  | 
 |   // TODO(eroman): This event is not ended in the case of cancellation. | 
 |   net_log.BeginEvent(NetLogEventType::WPAD_DHCP_WIN_GET_ADAPTERS); | 
 |  | 
 |   last_query_ = ImplCreateAdapterQuery(); | 
 |   last_query_->logging_info()->origin_thread_start_time = | 
 |       base::TimeTicks::Now(); | 
 |  | 
 |   task_runner_->PostTaskAndReply( | 
 |       FROM_HERE, | 
 |       base::Bind(&DhcpPacFileFetcherWin::AdapterQuery::GetCandidateAdapterNames, | 
 |                  last_query_.get()), | 
 |       base::Bind(&DhcpPacFileFetcherWin::OnGetCandidateAdapterNamesDone, | 
 |                  AsWeakPtr(), last_query_, traffic_annotation)); | 
 |  | 
 |   return ERR_IO_PENDING; | 
 | } | 
 |  | 
 | void DhcpPacFileFetcherWin::Cancel() { | 
 |   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
 |  | 
 |   CancelImpl(); | 
 | } | 
 |  | 
 | void DhcpPacFileFetcherWin::OnShutdown() { | 
 |   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
 |  | 
 |   // Back up callback, if there is one, as CancelImpl() will destroy it. | 
 |   net::CompletionOnceCallback callback = std::move(callback_); | 
 |  | 
 |   // Cancel current request, if there is one. | 
 |   CancelImpl(); | 
 |  | 
 |   // Prevent future network requests. | 
 |   url_request_context_ = nullptr; | 
 |  | 
 |   // Invoke callback with error, if present. | 
 |   if (callback) | 
 |     std::move(callback).Run(ERR_CONTEXT_SHUT_DOWN); | 
 | } | 
 |  | 
 | void DhcpPacFileFetcherWin::CancelImpl() { | 
 |   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
 |  | 
 |   if (state_ != STATE_DONE) { | 
 |     callback_.Reset(); | 
 |     wait_timer_.Stop(); | 
 |     state_ = STATE_DONE; | 
 |  | 
 |     for (FetcherVector::iterator it = fetchers_.begin(); | 
 |          it != fetchers_.end(); | 
 |          ++it) { | 
 |       (*it)->Cancel(); | 
 |     } | 
 |  | 
 |     fetchers_.clear(); | 
 |   } | 
 | } | 
 |  | 
 | void DhcpPacFileFetcherWin::OnGetCandidateAdapterNamesDone( | 
 |     scoped_refptr<AdapterQuery> query, | 
 |     const NetworkTrafficAnnotationTag traffic_annotation) { | 
 |   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
 |  | 
 |   // This can happen if this object is reused for multiple queries, | 
 |   // and a previous query was cancelled before it completed. | 
 |   if (query.get() != last_query_.get()) | 
 |     return; | 
 |   last_query_ = NULL; | 
 |  | 
 |   DhcpAdapterNamesLoggingInfo* logging_info = query->logging_info(); | 
 |   logging_info->origin_thread_end_time = base::TimeTicks::Now(); | 
 |  | 
 |   net_log_.EndEvent(NetLogEventType::WPAD_DHCP_WIN_GET_ADAPTERS, | 
 |                     base::Bind(&NetLogGetAdaptersDoneCallback, | 
 |                                base::Unretained(logging_info))); | 
 |  | 
 |   // Enable unit tests to wait for this to happen; in production this function | 
 |   // call is a no-op. | 
 |   ImplOnGetCandidateAdapterNamesDone(); | 
 |  | 
 |   // We may have been cancelled. | 
 |   if (state_ != STATE_WAIT_ADAPTERS) | 
 |     return; | 
 |  | 
 |   state_ = STATE_NO_RESULTS; | 
 |  | 
 |   const std::set<std::string>& adapter_names = query->adapter_names(); | 
 |  | 
 |   if (adapter_names.empty()) { | 
 |     TransitionToDone(); | 
 |     return; | 
 |   } | 
 |  | 
 |   for (const std::string& adapter_name : adapter_names) { | 
 |     std::unique_ptr<DhcpPacFileAdapterFetcher> fetcher( | 
 |         ImplCreateAdapterFetcher()); | 
 |     size_t fetcher_index = fetchers_.size(); | 
 |     fetcher->Fetch(adapter_name, | 
 |                    base::Bind(&DhcpPacFileFetcherWin::OnFetcherDone, | 
 |                               base::Unretained(this), fetcher_index), | 
 |                    traffic_annotation); | 
 |     fetchers_.push_back(std::move(fetcher)); | 
 |   } | 
 |   num_pending_fetchers_ = fetchers_.size(); | 
 | } | 
 |  | 
 | std::string DhcpPacFileFetcherWin::GetFetcherName() const { | 
 |   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
 |   return "win"; | 
 | } | 
 |  | 
 | const GURL& DhcpPacFileFetcherWin::GetPacURL() const { | 
 |   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
 |   DCHECK_EQ(state_, STATE_DONE); | 
 |  | 
 |   return pac_url_; | 
 | } | 
 |  | 
 | void DhcpPacFileFetcherWin::OnFetcherDone(size_t fetcher_index, | 
 |                                           int result) { | 
 |   DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS); | 
 |  | 
 |   net_log_.AddEvent( | 
 |       NetLogEventType::WPAD_DHCP_WIN_ON_FETCHER_DONE, | 
 |       base::Bind(&NetLogFetcherDoneCallback, fetcher_index, result)); | 
 |  | 
 |   if (--num_pending_fetchers_ == 0) { | 
 |     TransitionToDone(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // If the only pending adapters are those less preferred than one | 
 |   // with a valid PAC script, we do not need to wait any longer. | 
 |   for (FetcherVector::iterator it = fetchers_.begin(); | 
 |        it != fetchers_.end(); | 
 |        ++it) { | 
 |     bool did_finish = (*it)->DidFinish(); | 
 |     int result = (*it)->GetResult(); | 
 |     if (did_finish && result == OK) { | 
 |       TransitionToDone(); | 
 |       return; | 
 |     } | 
 |     if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) { | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   // Once we have a single result, we set a maximum on how long to wait | 
 |   // for the rest of the results. | 
 |   if (state_ == STATE_NO_RESULTS) { | 
 |     state_ = STATE_SOME_RESULTS; | 
 |     net_log_.AddEvent(NetLogEventType::WPAD_DHCP_WIN_START_WAIT_TIMER); | 
 |     wait_timer_.Start(FROM_HERE, | 
 |         ImplGetMaxWait(), this, &DhcpPacFileFetcherWin::OnWaitTimer); | 
 |   } | 
 | } | 
 |  | 
 | void DhcpPacFileFetcherWin::OnWaitTimer() { | 
 |   DCHECK_EQ(state_, STATE_SOME_RESULTS); | 
 |  | 
 |   net_log_.AddEvent(NetLogEventType::WPAD_DHCP_WIN_ON_WAIT_TIMER); | 
 |   TransitionToDone(); | 
 | } | 
 |  | 
 | void DhcpPacFileFetcherWin::TransitionToDone() { | 
 |   DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS); | 
 |  | 
 |   int used_fetcher_index = -1; | 
 |   int result = ERR_PAC_NOT_IN_DHCP;  // Default if no fetchers. | 
 |   if (!fetchers_.empty()) { | 
 |     // Scan twice for the result; once through the whole list for success, | 
 |     // then if no success, return result for most preferred network adapter, | 
 |     // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error. | 
 |     // Default to ERR_ABORTED if no fetcher completed. | 
 |     result = ERR_ABORTED; | 
 |     for (size_t i = 0; i < fetchers_.size(); ++i) { | 
 |       const auto& fetcher = fetchers_[i]; | 
 |       if (fetcher->DidFinish() && fetcher->GetResult() == OK) { | 
 |         result = OK; | 
 |         *destination_string_ = fetcher->GetPacScript(); | 
 |         pac_url_ = fetcher->GetPacURL(); | 
 |         used_fetcher_index = i; | 
 |         break; | 
 |       } | 
 |     } | 
 |     if (result != OK) { | 
 |       destination_string_->clear(); | 
 |       for (size_t i = 0; i < fetchers_.size(); ++i) { | 
 |         const auto& fetcher = fetchers_[i]; | 
 |         if (fetcher->DidFinish()) { | 
 |           result = fetcher->GetResult(); | 
 |           used_fetcher_index = i; | 
 |           if (result != ERR_PAC_NOT_IN_DHCP) { | 
 |             break; | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   CompletionOnceCallback callback = std::move(callback_); | 
 |   CancelImpl(); | 
 |   DCHECK_EQ(state_, STATE_DONE); | 
 |   DCHECK(fetchers_.empty()); | 
 |  | 
 |   net_log_.EndEvent( | 
 |       NetLogEventType::WPAD_DHCP_WIN_FETCH, | 
 |       base::Bind(&NetLogFetcherDoneCallback, used_fetcher_index, result)); | 
 |  | 
 |   // We may be deleted re-entrantly within this outcall. | 
 |   std::move(callback).Run(result); | 
 | } | 
 |  | 
 | int DhcpPacFileFetcherWin::num_pending_fetchers() const { | 
 |   return num_pending_fetchers_; | 
 | } | 
 |  | 
 | URLRequestContext* DhcpPacFileFetcherWin::url_request_context() const { | 
 |   return url_request_context_; | 
 | } | 
 |  | 
 | scoped_refptr<base::TaskRunner> DhcpPacFileFetcherWin::GetTaskRunner() { | 
 |   return task_runner_; | 
 | } | 
 |  | 
 | DhcpPacFileAdapterFetcher* DhcpPacFileFetcherWin::ImplCreateAdapterFetcher() { | 
 |   return new DhcpPacFileAdapterFetcher(url_request_context_, task_runner_); | 
 | } | 
 |  | 
 | DhcpPacFileFetcherWin::AdapterQuery* | 
 | DhcpPacFileFetcherWin::ImplCreateAdapterQuery() { | 
 |   return new AdapterQuery(); | 
 | } | 
 |  | 
 | base::TimeDelta DhcpPacFileFetcherWin::ImplGetMaxWait() { | 
 |   return kMaxWaitAfterFirstResult; | 
 | } | 
 |  | 
 | bool DhcpPacFileFetcherWin::GetCandidateAdapterNames( | 
 |     std::set<std::string>* adapter_names, | 
 |     DhcpAdapterNamesLoggingInfo* info) { | 
 |   DCHECK(adapter_names); | 
 |   adapter_names->clear(); | 
 |  | 
 |   // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to | 
 |   // avoid reallocation. | 
 |   ULONG adapters_size = 15000; | 
 |   std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> adapters; | 
 |   ULONG error = ERROR_SUCCESS; | 
 |   int num_tries = 0; | 
 |  | 
 |   do { | 
 |     adapters.reset( | 
 |         static_cast<IP_ADAPTER_ADDRESSES*>(SbMemoryAllocate(adapters_size))); | 
 |     // Return only unicast addresses, and skip information we do not need. | 
 |     base::ScopedBlockingCall scoped_blocking_call( | 
 |         base::BlockingType::MAY_BLOCK); | 
 |     error = GetAdaptersAddresses(AF_UNSPEC, | 
 |                                  GAA_FLAG_SKIP_ANYCAST | | 
 |                                  GAA_FLAG_SKIP_MULTICAST | | 
 |                                  GAA_FLAG_SKIP_DNS_SERVER | | 
 |                                  GAA_FLAG_SKIP_FRIENDLY_NAME, | 
 |                                  NULL, | 
 |                                  adapters.get(), | 
 |                                  &adapters_size); | 
 |     ++num_tries; | 
 |   } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3); | 
 |  | 
 |   if (info) | 
 |     info->error = error; | 
 |  | 
 |   if (error == ERROR_NO_DATA) { | 
 |     // There are no adapters that we care about. | 
 |     return true; | 
 |   } | 
 |  | 
 |   if (error != ERROR_SUCCESS) { | 
 |     LOG(WARNING) << "Unexpected error retrieving WPAD configuration from DHCP."; | 
 |     return false; | 
 |   } | 
 |  | 
 |   IP_ADAPTER_ADDRESSES* adapter = NULL; | 
 |   for (adapter = adapters.get(); adapter; adapter = adapter->Next) { | 
 |     if (IsDhcpCapableAdapter(adapter)) { | 
 |       DCHECK(adapter->AdapterName); | 
 |       adapter_names->insert(adapter->AdapterName); | 
 |     } | 
 |   } | 
 |  | 
 |   // Transfer the buffer containing the adapters, so it can be used later for | 
 |   // emitting NetLog parameters from the origin thread. | 
 |   if (info) | 
 |     info->adapters = std::move(adapters); | 
 |   return true; | 
 | } | 
 |  | 
 | DhcpPacFileFetcherWin::AdapterQuery::AdapterQuery() | 
 |     : logging_info_(new DhcpAdapterNamesLoggingInfo()) {} | 
 |  | 
 | void DhcpPacFileFetcherWin::AdapterQuery::GetCandidateAdapterNames() { | 
 |   logging_info_->error = ERROR_NO_DATA; | 
 |   logging_info_->adapters.reset(); | 
 |   logging_info_->worker_thread_start_time = base::TimeTicks::Now(); | 
 |  | 
 |   ImplGetCandidateAdapterNames(&adapter_names_, logging_info_.get()); | 
 |  | 
 |   logging_info_->worker_thread_end_time = base::TimeTicks::Now(); | 
 | } | 
 |  | 
 | const std::set<std::string>& | 
 | DhcpPacFileFetcherWin::AdapterQuery::adapter_names() const { | 
 |   return adapter_names_; | 
 | } | 
 |  | 
 | bool DhcpPacFileFetcherWin::AdapterQuery::ImplGetCandidateAdapterNames( | 
 |     std::set<std::string>* adapter_names, | 
 |     DhcpAdapterNamesLoggingInfo* info) { | 
 |   return DhcpPacFileFetcherWin::GetCandidateAdapterNames(adapter_names, | 
 |                                                          info); | 
 | } | 
 |  | 
 | DhcpPacFileFetcherWin::AdapterQuery::~AdapterQuery() {} | 
 |  | 
 | }  // namespace net |