blob: dbe4df2b300f3e1a4a77500e7e0f0043e3d0877f [file] [log] [blame]
[email protected]7258def2011-05-17 19:53:001// Copyright (c) 2011 The Chromium Authors. All rights reserved.
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/dhcp_proxy_script_fetcher_win.h"
6
[email protected]961fefb2011-05-24 13:59:587#include "base/metrics/histogram.h"
8#include "base/perftimer.h"
[email protected]7258def2011-05-17 19:53:009#include "net/base/net_errors.h"
10#include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
11
12#include <winsock2.h>
13#include <iphlpapi.h>
14#pragma comment(lib, "iphlpapi.lib")
15
16namespace {
17
18// How long to wait at maximum after we get results (a PAC file or
19// knowledge that no PAC file is configured) from whichever network
20// adapter finishes first.
21const int kMaxWaitAfterFirstResultMs = 400;
22
[email protected]961fefb2011-05-24 13:59:5823const int kGetAdaptersAddressesErrors[] = {
24 ERROR_ADDRESS_NOT_ASSOCIATED,
25 ERROR_BUFFER_OVERFLOW,
26 ERROR_INVALID_PARAMETER,
27 ERROR_NOT_ENOUGH_MEMORY,
28 ERROR_NO_DATA,
29};
30
[email protected]7258def2011-05-17 19:53:0031} // namespace
32
33namespace net {
34
35DhcpProxyScriptFetcherWin::DhcpProxyScriptFetcherWin(
36 URLRequestContext* url_request_context)
37 : state_(STATE_START),
38 ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_callback_(
39 this, &DhcpProxyScriptFetcherWin::OnFetcherDone)),
40 num_pending_fetchers_(0),
41 url_request_context_(url_request_context) {
42 DCHECK(url_request_context_);
43}
44
45DhcpProxyScriptFetcherWin::~DhcpProxyScriptFetcherWin() {
[email protected]961fefb2011-05-24 13:59:5846 // Count as user-initiated if we are not yet in STATE_DONE.
[email protected]7258def2011-05-17 19:53:0047 Cancel();
48}
49
50int DhcpProxyScriptFetcherWin::Fetch(string16* utf16_text,
51 CompletionCallback* callback) {
52 DCHECK(CalledOnValidThread());
53 if (state_ != STATE_START && state_ != STATE_DONE) {
54 NOTREACHED();
55 return ERR_UNEXPECTED;
56 }
57
[email protected]961fefb2011-05-24 13:59:5858 fetch_start_time_ = base::TimeTicks::Now();
59
[email protected]7258def2011-05-17 19:53:0060 std::set<std::string> adapter_names;
61 if (!ImplGetCandidateAdapterNames(&adapter_names)) {
62 return ERR_UNEXPECTED;
63 }
64 if (adapter_names.empty()) {
65 return ERR_PAC_NOT_IN_DHCP;
66 }
67
68 state_ = STATE_NO_RESULTS;
69
70 client_callback_ = callback;
71 destination_string_ = utf16_text;
72
73 for (std::set<std::string>::iterator it = adapter_names.begin();
74 it != adapter_names.end();
75 ++it) {
76 DhcpProxyScriptAdapterFetcher* fetcher(ImplCreateAdapterFetcher());
77 fetcher->Fetch(*it, &fetcher_callback_);
78 fetchers_.push_back(fetcher);
79 }
80 num_pending_fetchers_ = fetchers_.size();
81
82 return ERR_IO_PENDING;
83}
84
85void DhcpProxyScriptFetcherWin::Cancel() {
86 DCHECK(CalledOnValidThread());
87
88 if (state_ != STATE_DONE) {
[email protected]961fefb2011-05-24 13:59:5889 // We only count this stat if the cancel was explicitly initiated by
90 // our client, and if we weren't already in STATE_DONE.
91 UMA_HISTOGRAM_TIMES("Net.DhcpWpadCancelTime",
92 base::TimeTicks::Now() - fetch_start_time_);
93 }
94
95 CancelImpl();
96}
97
98void DhcpProxyScriptFetcherWin::CancelImpl() {
99 DCHECK(CalledOnValidThread());
100
101 if (state_ != STATE_DONE) {
[email protected]e2008d72011-06-13 17:08:20102 client_callback_ = NULL;
[email protected]7258def2011-05-17 19:53:00103 wait_timer_.Stop();
104 state_ = STATE_DONE;
105
106 for (FetcherVector::iterator it = fetchers_.begin();
107 it != fetchers_.end();
108 ++it) {
109 (*it)->Cancel();
110 }
111
112 fetchers_.reset();
113 }
114}
115
116std::string DhcpProxyScriptFetcherWin::GetFetcherName() const {
117 DCHECK(CalledOnValidThread());
118 return "win";
119}
120
121const GURL& DhcpProxyScriptFetcherWin::GetPacURL() const {
122 DCHECK(CalledOnValidThread());
123 DCHECK_EQ(state_, STATE_DONE);
124
125 return pac_url_;
126}
127
128void DhcpProxyScriptFetcherWin::OnFetcherDone(int result) {
129 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
130
131 if (--num_pending_fetchers_ == 0) {
132 TransitionToDone();
133 return;
134 }
135
136 // If the only pending adapters are those less preferred than one
137 // with a valid PAC script, we do not need to wait any longer.
138 for (FetcherVector::iterator it = fetchers_.begin();
139 it != fetchers_.end();
140 ++it) {
141 bool did_finish = (*it)->DidFinish();
142 int result = (*it)->GetResult();
143 if (did_finish && result == OK) {
144 TransitionToDone();
145 return;
146 }
147 if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) {
148 break;
149 }
150 }
151
152 // Once we have a single result, we set a maximum on how long to wait
153 // for the rest of the results.
154 if (state_ == STATE_NO_RESULTS) {
155 state_ = STATE_SOME_RESULTS;
156 wait_timer_.Start(
157 base::TimeDelta::FromMilliseconds(ImplGetMaxWaitMs()),
158 this, &DhcpProxyScriptFetcherWin::OnWaitTimer);
159 }
160}
161
162void DhcpProxyScriptFetcherWin::OnWaitTimer() {
163 DCHECK_EQ(state_, STATE_SOME_RESULTS);
[email protected]961fefb2011-05-24 13:59:58164
165 // These are intended to help us understand whether our timeout may
166 // be too aggressive or not aggressive enough.
167 UMA_HISTOGRAM_COUNTS_100("Net.DhcpWpadNumAdaptersAtWaitTimer",
168 fetchers_.size());
169 UMA_HISTOGRAM_COUNTS_100("Net.DhcpWpadNumPendingAdaptersAtWaitTimer",
170 num_pending_fetchers_);
171
[email protected]7258def2011-05-17 19:53:00172 TransitionToDone();
173}
174
175void DhcpProxyScriptFetcherWin::TransitionToDone() {
176 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
177
178 // Should have returned immediately at Fetch() if no adapters to check.
179 DCHECK(!fetchers_.empty());
180
181 // Scan twice for the result; once through the whole list for success,
182 // then if no success, return result for most preferred network adapter,
183 // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error.
184 // Default to ERR_ABORTED if no fetcher completed.
185 int result = ERR_ABORTED;
186 for (FetcherVector::iterator it = fetchers_.begin();
187 it != fetchers_.end();
188 ++it) {
189 if ((*it)->DidFinish() && (*it)->GetResult() == OK) {
190 result = OK;
191 *destination_string_ = (*it)->GetPacScript();
192 pac_url_ = (*it)->GetPacURL();
193 break;
194 }
195 }
196 if (result != OK) {
197 destination_string_->clear();
198 for (FetcherVector::iterator it = fetchers_.begin();
199 it != fetchers_.end();
200 ++it) {
201 if ((*it)->DidFinish()) {
202 result = (*it)->GetResult();
203 if (result != ERR_PAC_NOT_IN_DHCP) {
204 break;
205 }
206 }
207 }
208 }
209
[email protected]e2008d72011-06-13 17:08:20210 CompletionCallback* callback = client_callback_;
[email protected]961fefb2011-05-24 13:59:58211 CancelImpl();
[email protected]7258def2011-05-17 19:53:00212 DCHECK_EQ(state_, STATE_DONE);
213 DCHECK(fetchers_.empty());
[email protected]e2008d72011-06-13 17:08:20214 DCHECK(!client_callback_); // Invariant of data.
[email protected]7258def2011-05-17 19:53:00215
[email protected]961fefb2011-05-24 13:59:58216 UMA_HISTOGRAM_TIMES("Net.DhcpWpadCompletionTime",
217 base::TimeTicks::Now() - fetch_start_time_);
218
219 if (result != OK) {
220 UMA_HISTOGRAM_CUSTOM_ENUMERATION(
221 "Net.DhcpWpadFetchError", std::abs(result), GetAllErrorCodesForUma());
222 }
223
[email protected]e2008d72011-06-13 17:08:20224 // We may be deleted re-entrantly within this outcall.
225 callback->Run(result);
[email protected]7258def2011-05-17 19:53:00226}
227
[email protected]8d6bc142011-05-26 14:26:30228int DhcpProxyScriptFetcherWin::num_pending_fetchers() const {
229 return num_pending_fetchers_;
230}
231
232URLRequestContext* DhcpProxyScriptFetcherWin::url_request_context() const {
233 return url_request_context_;
234}
235
[email protected]7258def2011-05-17 19:53:00236DhcpProxyScriptAdapterFetcher*
237 DhcpProxyScriptFetcherWin::ImplCreateAdapterFetcher() {
238 return new DhcpProxyScriptAdapterFetcher(url_request_context_);
239}
240
241bool DhcpProxyScriptFetcherWin::ImplGetCandidateAdapterNames(
242 std::set<std::string>* adapter_names) {
243 return GetCandidateAdapterNames(adapter_names);
244}
245
246int DhcpProxyScriptFetcherWin::ImplGetMaxWaitMs() {
247 return kMaxWaitAfterFirstResultMs;
248}
249
250bool DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(
251 std::set<std::string>* adapter_names) {
252 DCHECK(adapter_names);
253 adapter_names->clear();
254
255 // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to
256 // avoid reallocation.
257 ULONG adapters_size = 15000;
258 scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> adapters;
259 ULONG error = ERROR_SUCCESS;
260 int num_tries = 0;
[email protected]961fefb2011-05-24 13:59:58261
262 PerfTimer time_api_access;
[email protected]7258def2011-05-17 19:53:00263 do {
264 adapters.reset(
265 reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
266 // Return only unicast addresses, and skip information we do not need.
267 error = GetAdaptersAddresses(AF_UNSPEC,
268 GAA_FLAG_SKIP_ANYCAST |
269 GAA_FLAG_SKIP_MULTICAST |
270 GAA_FLAG_SKIP_DNS_SERVER |
271 GAA_FLAG_SKIP_FRIENDLY_NAME,
272 NULL,
273 adapters.get(),
274 &adapters_size);
275 ++num_tries;
276 } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3);
277
[email protected]961fefb2011-05-24 13:59:58278 // This is primarily to validate our belief that the GetAdaptersAddresses API
279 // function is fast enough to call synchronously from the network thread.
280 UMA_HISTOGRAM_TIMES("Net.DhcpWpadGetAdaptersAddressesTime",
281 time_api_access.Elapsed());
282
283 if (error != ERROR_SUCCESS) {
284 UMA_HISTOGRAM_CUSTOM_ENUMERATION(
285 "Net.DhcpWpadGetAdaptersAddressesError",
286 error,
287 base::CustomHistogram::ArrayToCustomRanges(
288 kGetAdaptersAddressesErrors,
289 arraysize(kGetAdaptersAddressesErrors)));
290 }
291
[email protected]7258def2011-05-17 19:53:00292 if (error == ERROR_NO_DATA) {
293 // There are no adapters that we care about.
294 return true;
295 }
296
297 if (error != ERROR_SUCCESS) {
298 LOG(WARNING) << "Unexpected error retrieving WPAD configuration from DHCP.";
299 return false;
300 }
301
302 IP_ADAPTER_ADDRESSES* adapter = NULL;
303 for (adapter = adapters.get(); adapter; adapter = adapter->Next) {
304 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
305 continue;
306 if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0)
307 continue;
308
309 DCHECK(adapter->AdapterName);
310 adapter_names->insert(adapter->AdapterName);
311 }
312
313 return true;
314}
315
316} // namespace net