blob: d7392fefbdbd95e9cc3ce0177cc0f682be75f771 [file] [log] [blame]
[email protected]7ec8793a2012-01-28 03:23:111// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]7258def2011-05-17 19:53:002// 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]28376542011-10-14 17:30:377#include "base/bind.h"
[email protected]235786812011-12-20 02:15:318#include "base/bind_helpers.h"
[email protected]961fefb2011-05-24 13:59:589#include "base/metrics/histogram.h"
10#include "base/perftimer.h"
[email protected]c24ebc32011-06-23 20:34:5811#include "base/threading/worker_pool.h"
[email protected]7258def2011-05-17 19:53:0012#include "net/base/net_errors.h"
13#include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
14
15#include <winsock2.h>
16#include <iphlpapi.h>
17#pragma comment(lib, "iphlpapi.lib")
18
19namespace {
20
21// How long to wait at maximum after we get results (a PAC file or
22// knowledge that no PAC file is configured) from whichever network
23// adapter finishes first.
24const int kMaxWaitAfterFirstResultMs = 400;
25
[email protected]961fefb2011-05-24 13:59:5826const int kGetAdaptersAddressesErrors[] = {
27 ERROR_ADDRESS_NOT_ASSOCIATED,
28 ERROR_BUFFER_OVERFLOW,
29 ERROR_INVALID_PARAMETER,
30 ERROR_NOT_ENOUGH_MEMORY,
31 ERROR_NO_DATA,
32};
33
[email protected]7258def2011-05-17 19:53:0034} // namespace
35
36namespace net {
37
38DhcpProxyScriptFetcherWin::DhcpProxyScriptFetcherWin(
39 URLRequestContext* url_request_context)
40 : state_(STATE_START),
[email protected]7258def2011-05-17 19:53:0041 num_pending_fetchers_(0),
[email protected]7ec8793a2012-01-28 03:23:1142 destination_string_(NULL),
[email protected]7258def2011-05-17 19:53:0043 url_request_context_(url_request_context) {
44 DCHECK(url_request_context_);
45}
46
47DhcpProxyScriptFetcherWin::~DhcpProxyScriptFetcherWin() {
[email protected]961fefb2011-05-24 13:59:5848 // Count as user-initiated if we are not yet in STATE_DONE.
[email protected]7258def2011-05-17 19:53:0049 Cancel();
[email protected]c24ebc32011-06-23 20:34:5850
51 // The WeakPtr we passed to the worker thread may be destroyed on the
52 // worker thread. This detaches any outstanding WeakPtr state from
53 // the current thread.
54 base::SupportsWeakPtr<DhcpProxyScriptFetcherWin>::DetachFromThread();
[email protected]7258def2011-05-17 19:53:0055}
56
57int DhcpProxyScriptFetcherWin::Fetch(string16* utf16_text,
[email protected]235786812011-12-20 02:15:3158 const CompletionCallback& callback) {
[email protected]7258def2011-05-17 19:53:0059 DCHECK(CalledOnValidThread());
60 if (state_ != STATE_START && state_ != STATE_DONE) {
61 NOTREACHED();
62 return ERR_UNEXPECTED;
63 }
64
[email protected]961fefb2011-05-24 13:59:5865 fetch_start_time_ = base::TimeTicks::Now();
66
[email protected]c24ebc32011-06-23 20:34:5867 state_ = STATE_WAIT_ADAPTERS;
[email protected]235786812011-12-20 02:15:3168 callback_ = callback;
[email protected]7258def2011-05-17 19:53:0069 destination_string_ = utf16_text;
70
[email protected]28376542011-10-14 17:30:3771 last_query_ = ImplCreateAdapterQuery();
72 base::WorkerPool::PostTaskAndReply(
73 FROM_HERE,
74 base::Bind(
75 &DhcpProxyScriptFetcherWin::AdapterQuery::GetCandidateAdapterNames,
76 last_query_.get()),
77 base::Bind(
78 &DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone,
79 AsWeakPtr(),
80 last_query_),
81 true);
[email protected]7258def2011-05-17 19:53:0082
83 return ERR_IO_PENDING;
84}
85
86void DhcpProxyScriptFetcherWin::Cancel() {
87 DCHECK(CalledOnValidThread());
88
89 if (state_ != STATE_DONE) {
[email protected]961fefb2011-05-24 13:59:5890 // We only count this stat if the cancel was explicitly initiated by
91 // our client, and if we weren't already in STATE_DONE.
92 UMA_HISTOGRAM_TIMES("Net.DhcpWpadCancelTime",
93 base::TimeTicks::Now() - fetch_start_time_);
94 }
95
96 CancelImpl();
97}
98
99void DhcpProxyScriptFetcherWin::CancelImpl() {
100 DCHECK(CalledOnValidThread());
101
102 if (state_ != STATE_DONE) {
[email protected]235786812011-12-20 02:15:31103 callback_.Reset();
[email protected]7258def2011-05-17 19:53:00104 wait_timer_.Stop();
105 state_ = STATE_DONE;
106
107 for (FetcherVector::iterator it = fetchers_.begin();
108 it != fetchers_.end();
109 ++it) {
110 (*it)->Cancel();
111 }
112
113 fetchers_.reset();
114 }
115}
116
[email protected]c24ebc32011-06-23 20:34:58117void DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone(
[email protected]28376542011-10-14 17:30:37118 scoped_refptr<AdapterQuery> query) {
[email protected]c24ebc32011-06-23 20:34:58119 DCHECK(CalledOnValidThread());
120
[email protected]28376542011-10-14 17:30:37121 // This can happen if this object is reused for multiple queries,
122 // and a previous query was cancelled before it completed.
123 if (query.get() != last_query_.get())
124 return;
125 last_query_ = NULL;
126
127 // Enable unit tests to wait for this to happen; in production this function
128 // call is a no-op.
129 ImplOnGetCandidateAdapterNamesDone();
130
[email protected]c24ebc32011-06-23 20:34:58131 // We may have been cancelled.
132 if (state_ != STATE_WAIT_ADAPTERS)
133 return;
134
135 state_ = STATE_NO_RESULTS;
136
[email protected]28376542011-10-14 17:30:37137 const std::set<std::string>& adapter_names = query->adapter_names();
138
[email protected]c24ebc32011-06-23 20:34:58139 if (adapter_names.empty()) {
140 TransitionToDone();
141 return;
142 }
143
144 for (std::set<std::string>::const_iterator it = adapter_names.begin();
145 it != adapter_names.end();
146 ++it) {
147 DhcpProxyScriptAdapterFetcher* fetcher(ImplCreateAdapterFetcher());
[email protected]235786812011-12-20 02:15:31148 fetcher->Fetch(
149 *it, base::Bind(&DhcpProxyScriptFetcherWin::OnFetcherDone,
150 base::Unretained(this)));
[email protected]c24ebc32011-06-23 20:34:58151 fetchers_.push_back(fetcher);
152 }
153 num_pending_fetchers_ = fetchers_.size();
154}
155
[email protected]7258def2011-05-17 19:53:00156std::string DhcpProxyScriptFetcherWin::GetFetcherName() const {
157 DCHECK(CalledOnValidThread());
158 return "win";
159}
160
161const GURL& DhcpProxyScriptFetcherWin::GetPacURL() const {
162 DCHECK(CalledOnValidThread());
163 DCHECK_EQ(state_, STATE_DONE);
164
165 return pac_url_;
166}
167
168void DhcpProxyScriptFetcherWin::OnFetcherDone(int result) {
169 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
170
171 if (--num_pending_fetchers_ == 0) {
172 TransitionToDone();
173 return;
174 }
175
176 // If the only pending adapters are those less preferred than one
177 // with a valid PAC script, we do not need to wait any longer.
178 for (FetcherVector::iterator it = fetchers_.begin();
179 it != fetchers_.end();
180 ++it) {
181 bool did_finish = (*it)->DidFinish();
182 int result = (*it)->GetResult();
183 if (did_finish && result == OK) {
184 TransitionToDone();
185 return;
186 }
187 if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) {
188 break;
189 }
190 }
191
192 // Once we have a single result, we set a maximum on how long to wait
193 // for the rest of the results.
194 if (state_ == STATE_NO_RESULTS) {
195 state_ = STATE_SOME_RESULTS;
[email protected]d323a172011-09-02 18:23:02196 wait_timer_.Start(FROM_HERE,
[email protected]0f2e45ec2012-07-11 15:41:43197 ImplGetMaxWait(), this, &DhcpProxyScriptFetcherWin::OnWaitTimer);
[email protected]7258def2011-05-17 19:53:00198 }
199}
200
201void DhcpProxyScriptFetcherWin::OnWaitTimer() {
202 DCHECK_EQ(state_, STATE_SOME_RESULTS);
[email protected]961fefb2011-05-24 13:59:58203
204 // These are intended to help us understand whether our timeout may
205 // be too aggressive or not aggressive enough.
206 UMA_HISTOGRAM_COUNTS_100("Net.DhcpWpadNumAdaptersAtWaitTimer",
207 fetchers_.size());
208 UMA_HISTOGRAM_COUNTS_100("Net.DhcpWpadNumPendingAdaptersAtWaitTimer",
209 num_pending_fetchers_);
210
[email protected]7258def2011-05-17 19:53:00211 TransitionToDone();
212}
213
214void DhcpProxyScriptFetcherWin::TransitionToDone() {
215 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
216
[email protected]c24ebc32011-06-23 20:34:58217 int result = ERR_PAC_NOT_IN_DHCP; // Default if no fetchers.
218 if (!fetchers_.empty()) {
219 // Scan twice for the result; once through the whole list for success,
220 // then if no success, return result for most preferred network adapter,
221 // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error.
222 // Default to ERR_ABORTED if no fetcher completed.
223 result = ERR_ABORTED;
[email protected]7258def2011-05-17 19:53:00224 for (FetcherVector::iterator it = fetchers_.begin();
225 it != fetchers_.end();
226 ++it) {
[email protected]c24ebc32011-06-23 20:34:58227 if ((*it)->DidFinish() && (*it)->GetResult() == OK) {
228 result = OK;
229 *destination_string_ = (*it)->GetPacScript();
230 pac_url_ = (*it)->GetPacURL();
231 break;
232 }
233 }
234 if (result != OK) {
235 destination_string_->clear();
236 for (FetcherVector::iterator it = fetchers_.begin();
237 it != fetchers_.end();
238 ++it) {
239 if ((*it)->DidFinish()) {
240 result = (*it)->GetResult();
241 if (result != ERR_PAC_NOT_IN_DHCP) {
242 break;
243 }
[email protected]7258def2011-05-17 19:53:00244 }
245 }
246 }
247 }
248
[email protected]235786812011-12-20 02:15:31249 CompletionCallback callback = callback_;
[email protected]961fefb2011-05-24 13:59:58250 CancelImpl();
[email protected]7258def2011-05-17 19:53:00251 DCHECK_EQ(state_, STATE_DONE);
252 DCHECK(fetchers_.empty());
[email protected]235786812011-12-20 02:15:31253 DCHECK(callback_.is_null()); // Invariant of data.
[email protected]7258def2011-05-17 19:53:00254
[email protected]961fefb2011-05-24 13:59:58255 UMA_HISTOGRAM_TIMES("Net.DhcpWpadCompletionTime",
256 base::TimeTicks::Now() - fetch_start_time_);
257
258 if (result != OK) {
259 UMA_HISTOGRAM_CUSTOM_ENUMERATION(
260 "Net.DhcpWpadFetchError", std::abs(result), GetAllErrorCodesForUma());
261 }
262
[email protected]e2008d72011-06-13 17:08:20263 // We may be deleted re-entrantly within this outcall.
[email protected]235786812011-12-20 02:15:31264 callback.Run(result);
[email protected]7258def2011-05-17 19:53:00265}
266
[email protected]8d6bc142011-05-26 14:26:30267int DhcpProxyScriptFetcherWin::num_pending_fetchers() const {
268 return num_pending_fetchers_;
269}
270
271URLRequestContext* DhcpProxyScriptFetcherWin::url_request_context() const {
272 return url_request_context_;
273}
274
[email protected]7258def2011-05-17 19:53:00275DhcpProxyScriptAdapterFetcher*
276 DhcpProxyScriptFetcherWin::ImplCreateAdapterFetcher() {
277 return new DhcpProxyScriptAdapterFetcher(url_request_context_);
278}
279
[email protected]28376542011-10-14 17:30:37280DhcpProxyScriptFetcherWin::AdapterQuery*
281 DhcpProxyScriptFetcherWin::ImplCreateAdapterQuery() {
282 return new AdapterQuery();
[email protected]7258def2011-05-17 19:53:00283}
284
[email protected]0f2e45ec2012-07-11 15:41:43285base::TimeDelta DhcpProxyScriptFetcherWin::ImplGetMaxWait() {
286 return base::TimeDelta::FromMilliseconds(kMaxWaitAfterFirstResultMs);
[email protected]7258def2011-05-17 19:53:00287}
288
289bool DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(
290 std::set<std::string>* adapter_names) {
291 DCHECK(adapter_names);
292 adapter_names->clear();
293
294 // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to
295 // avoid reallocation.
296 ULONG adapters_size = 15000;
297 scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> adapters;
298 ULONG error = ERROR_SUCCESS;
299 int num_tries = 0;
[email protected]961fefb2011-05-24 13:59:58300
301 PerfTimer time_api_access;
[email protected]7258def2011-05-17 19:53:00302 do {
303 adapters.reset(
304 reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
305 // Return only unicast addresses, and skip information we do not need.
306 error = GetAdaptersAddresses(AF_UNSPEC,
307 GAA_FLAG_SKIP_ANYCAST |
308 GAA_FLAG_SKIP_MULTICAST |
309 GAA_FLAG_SKIP_DNS_SERVER |
310 GAA_FLAG_SKIP_FRIENDLY_NAME,
311 NULL,
312 adapters.get(),
313 &adapters_size);
314 ++num_tries;
315 } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3);
316
[email protected]961fefb2011-05-24 13:59:58317 // This is primarily to validate our belief that the GetAdaptersAddresses API
318 // function is fast enough to call synchronously from the network thread.
319 UMA_HISTOGRAM_TIMES("Net.DhcpWpadGetAdaptersAddressesTime",
320 time_api_access.Elapsed());
321
322 if (error != ERROR_SUCCESS) {
323 UMA_HISTOGRAM_CUSTOM_ENUMERATION(
324 "Net.DhcpWpadGetAdaptersAddressesError",
325 error,
326 base::CustomHistogram::ArrayToCustomRanges(
327 kGetAdaptersAddressesErrors,
328 arraysize(kGetAdaptersAddressesErrors)));
329 }
330
[email protected]7258def2011-05-17 19:53:00331 if (error == ERROR_NO_DATA) {
332 // There are no adapters that we care about.
333 return true;
334 }
335
336 if (error != ERROR_SUCCESS) {
337 LOG(WARNING) << "Unexpected error retrieving WPAD configuration from DHCP.";
338 return false;
339 }
340
341 IP_ADAPTER_ADDRESSES* adapter = NULL;
342 for (adapter = adapters.get(); adapter; adapter = adapter->Next) {
343 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
344 continue;
345 if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0)
346 continue;
347
348 DCHECK(adapter->AdapterName);
349 adapter_names->insert(adapter->AdapterName);
350 }
351
352 return true;
353}
354
[email protected]28376542011-10-14 17:30:37355DhcpProxyScriptFetcherWin::AdapterQuery::AdapterQuery() {
[email protected]c24ebc32011-06-23 20:34:58356}
357
[email protected]28376542011-10-14 17:30:37358DhcpProxyScriptFetcherWin::AdapterQuery::~AdapterQuery() {
[email protected]c24ebc32011-06-23 20:34:58359}
360
[email protected]28376542011-10-14 17:30:37361void DhcpProxyScriptFetcherWin::AdapterQuery::GetCandidateAdapterNames() {
[email protected]c24ebc32011-06-23 20:34:58362 ImplGetCandidateAdapterNames(&adapter_names_);
[email protected]c24ebc32011-06-23 20:34:58363}
364
[email protected]28376542011-10-14 17:30:37365const std::set<std::string>&
366 DhcpProxyScriptFetcherWin::AdapterQuery::adapter_names() const {
367 return adapter_names_;
[email protected]c24ebc32011-06-23 20:34:58368}
369
[email protected]28376542011-10-14 17:30:37370bool DhcpProxyScriptFetcherWin::AdapterQuery::ImplGetCandidateAdapterNames(
[email protected]c24ebc32011-06-23 20:34:58371 std::set<std::string>* adapter_names) {
372 return DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(adapter_names);
373}
374
[email protected]7258def2011-05-17 19:53:00375} // namespace net