| Index: net/proxy/dhcp_proxy_script_fetcher_win.cc
|
| diff --git a/net/proxy/dhcp_proxy_script_fetcher_win.cc b/net/proxy/dhcp_proxy_script_fetcher_win.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..35109f608647e7a53974c190a4416aa705efdd94
|
| --- /dev/null
|
| +++ b/net/proxy/dhcp_proxy_script_fetcher_win.cc
|
| @@ -0,0 +1,246 @@
|
| +// Copyright (c) 2011 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/dhcp_proxy_script_fetcher_win.h"
|
| +
|
| +#include "net/base/net_errors.h"
|
| +#include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
|
| +
|
| +#include <winsock2.h>
|
| +#include <iphlpapi.h>
|
| +#pragma comment(lib, "iphlpapi.lib")
|
| +
|
| +namespace {
|
| +
|
| +// 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.
|
| +const int kMaxWaitAfterFirstResultMs = 400;
|
| +
|
| +} // namespace
|
| +
|
| +namespace net {
|
| +
|
| +DhcpProxyScriptFetcherWin::DhcpProxyScriptFetcherWin(
|
| + URLRequestContext* url_request_context)
|
| + : state_(STATE_START),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_callback_(
|
| + this, &DhcpProxyScriptFetcherWin::OnFetcherDone)),
|
| + num_pending_fetchers_(0),
|
| + url_request_context_(url_request_context) {
|
| + DCHECK(url_request_context_);
|
| +}
|
| +
|
| +DhcpProxyScriptFetcherWin::~DhcpProxyScriptFetcherWin() {
|
| + Cancel();
|
| +}
|
| +
|
| +int DhcpProxyScriptFetcherWin::Fetch(string16* utf16_text,
|
| + CompletionCallback* callback) {
|
| + DCHECK(CalledOnValidThread());
|
| + if (state_ != STATE_START && state_ != STATE_DONE) {
|
| + NOTREACHED();
|
| + return ERR_UNEXPECTED;
|
| + }
|
| +
|
| + std::set<std::string> adapter_names;
|
| + if (!ImplGetCandidateAdapterNames(&adapter_names)) {
|
| + return ERR_UNEXPECTED;
|
| + }
|
| + if (adapter_names.empty()) {
|
| + return ERR_PAC_NOT_IN_DHCP;
|
| + }
|
| +
|
| + state_ = STATE_NO_RESULTS;
|
| +
|
| + client_callback_ = callback;
|
| + destination_string_ = utf16_text;
|
| +
|
| + for (std::set<std::string>::iterator it = adapter_names.begin();
|
| + it != adapter_names.end();
|
| + ++it) {
|
| + DhcpProxyScriptAdapterFetcher* fetcher(ImplCreateAdapterFetcher());
|
| + fetcher->Fetch(*it, &fetcher_callback_);
|
| + fetchers_.push_back(fetcher);
|
| + }
|
| + num_pending_fetchers_ = fetchers_.size();
|
| +
|
| + return ERR_IO_PENDING;
|
| +}
|
| +
|
| +void DhcpProxyScriptFetcherWin::Cancel() {
|
| + DCHECK(CalledOnValidThread());
|
| +
|
| + if (state_ != STATE_DONE) {
|
| + wait_timer_.Stop();
|
| + state_ = STATE_DONE;
|
| +
|
| + for (FetcherVector::iterator it = fetchers_.begin();
|
| + it != fetchers_.end();
|
| + ++it) {
|
| + (*it)->Cancel();
|
| + }
|
| +
|
| + fetchers_.reset();
|
| + }
|
| +}
|
| +
|
| +std::string DhcpProxyScriptFetcherWin::GetFetcherName() const {
|
| + DCHECK(CalledOnValidThread());
|
| + return "win";
|
| +}
|
| +
|
| +const GURL& DhcpProxyScriptFetcherWin::GetPacURL() const {
|
| + DCHECK(CalledOnValidThread());
|
| + DCHECK_EQ(state_, STATE_DONE);
|
| +
|
| + return pac_url_;
|
| +}
|
| +
|
| +void DhcpProxyScriptFetcherWin::OnFetcherDone(int result) {
|
| + DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
|
| +
|
| + 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;
|
| + wait_timer_.Start(
|
| + base::TimeDelta::FromMilliseconds(ImplGetMaxWaitMs()),
|
| + this, &DhcpProxyScriptFetcherWin::OnWaitTimer);
|
| + }
|
| +}
|
| +
|
| +void DhcpProxyScriptFetcherWin::OnWaitTimer() {
|
| + DCHECK_EQ(state_, STATE_SOME_RESULTS);
|
| + TransitionToDone();
|
| +}
|
| +
|
| +void DhcpProxyScriptFetcherWin::TransitionToDone() {
|
| + DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
|
| +
|
| + // Should have returned immediately at Fetch() if no adapters to check.
|
| + DCHECK(!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.
|
| + int result = ERR_ABORTED;
|
| + for (FetcherVector::iterator it = fetchers_.begin();
|
| + it != fetchers_.end();
|
| + ++it) {
|
| + if ((*it)->DidFinish() && (*it)->GetResult() == OK) {
|
| + result = OK;
|
| + *destination_string_ = (*it)->GetPacScript();
|
| + pac_url_ = (*it)->GetPacURL();
|
| + break;
|
| + }
|
| + }
|
| + if (result != OK) {
|
| + destination_string_->clear();
|
| + for (FetcherVector::iterator it = fetchers_.begin();
|
| + it != fetchers_.end();
|
| + ++it) {
|
| + if ((*it)->DidFinish()) {
|
| + result = (*it)->GetResult();
|
| + if (result != ERR_PAC_NOT_IN_DHCP) {
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + Cancel();
|
| + DCHECK_EQ(state_, STATE_DONE);
|
| + DCHECK(fetchers_.empty());
|
| +
|
| + client_callback_->Run(result);
|
| +}
|
| +
|
| +DhcpProxyScriptAdapterFetcher*
|
| + DhcpProxyScriptFetcherWin::ImplCreateAdapterFetcher() {
|
| + return new DhcpProxyScriptAdapterFetcher(url_request_context_);
|
| +}
|
| +
|
| +bool DhcpProxyScriptFetcherWin::ImplGetCandidateAdapterNames(
|
| + std::set<std::string>* adapter_names) {
|
| + return GetCandidateAdapterNames(adapter_names);
|
| +}
|
| +
|
| +int DhcpProxyScriptFetcherWin::ImplGetMaxWaitMs() {
|
| + return kMaxWaitAfterFirstResultMs;
|
| +}
|
| +
|
| +bool DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(
|
| + std::set<std::string>* adapter_names) {
|
| + DCHECK(adapter_names);
|
| + adapter_names->clear();
|
| +
|
| + // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to
|
| + // avoid reallocation.
|
| + ULONG adapters_size = 15000;
|
| + scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> adapters;
|
| + ULONG error = ERROR_SUCCESS;
|
| + int num_tries = 0;
|
| + do {
|
| + adapters.reset(
|
| + reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
|
| + // Return only unicast addresses, and skip information we do not need.
|
| + 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 (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 (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
|
| + continue;
|
| + if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0)
|
| + continue;
|
| +
|
| + DCHECK(adapter->AdapterName);
|
| + adapter_names->insert(adapter->AdapterName);
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // namespace net
|
|
|