blob: 8870e94166aa81095a551b6ea8d60449fb3993a7 [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]ed671cf2013-08-27 12:23:469#include "base/threading/sequenced_worker_pool.h"
[email protected]7258def2011-05-17 19:53:0010#include "net/base/net_errors.h"
11#include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
12
13#include <winsock2.h>
14#include <iphlpapi.h>
15#pragma comment(lib, "iphlpapi.lib")
16
17namespace {
18
[email protected]ed671cf2013-08-27 12:23:4619// How many threads to use at maximum to do DHCP lookups. This is
20// chosen based on the following UMA data:
21// - When OnWaitTimer fires, ~99.8% of users have 6 or fewer network
22// adapters enabled for DHCP in total.
23// - At the same measurement point, ~99.7% of users have 3 or fewer pending
24// DHCP adapter lookups.
25// - There is however a very long and thin tail of users who have
26// systems reporting up to 100+ adapters (this must be some very weird
27// OS bug (?), probably the cause of http://crbug.com/240034).
28//
29// The maximum number of threads is chosen such that even systems that
30// report a huge number of network adapters should not run out of
31// memory from this number of threads, while giving a good chance of
32// getting back results for any responsive adapters.
33//
34// The ~99.8% of systems that have 6 or fewer network adapters will
35// not grow the thread pool to its maximum size (rather, they will
36// grow it to 6 or fewer threads) so setting the limit lower would not
37// improve performance or memory usage on those systems.
38const int kMaxDhcpLookupThreads = 12;
39
[email protected]7258def2011-05-17 19:53:0040// How long to wait at maximum after we get results (a PAC file or
41// knowledge that no PAC file is configured) from whichever network
42// adapter finishes first.
43const int kMaxWaitAfterFirstResultMs = 400;
44
[email protected]961fefb2011-05-24 13:59:5845const int kGetAdaptersAddressesErrors[] = {
46 ERROR_ADDRESS_NOT_ASSOCIATED,
47 ERROR_BUFFER_OVERFLOW,
48 ERROR_INVALID_PARAMETER,
49 ERROR_NOT_ENOUGH_MEMORY,
50 ERROR_NO_DATA,
51};
52
[email protected]7258def2011-05-17 19:53:0053} // namespace
54
55namespace net {
56
57DhcpProxyScriptFetcherWin::DhcpProxyScriptFetcherWin(
58 URLRequestContext* url_request_context)
59 : state_(STATE_START),
[email protected]7258def2011-05-17 19:53:0060 num_pending_fetchers_(0),
[email protected]7ec8793a2012-01-28 03:23:1161 destination_string_(NULL),
[email protected]7258def2011-05-17 19:53:0062 url_request_context_(url_request_context) {
63 DCHECK(url_request_context_);
[email protected]ed671cf2013-08-27 12:23:4664
65 worker_pool_ = new base::SequencedWorkerPool(kMaxDhcpLookupThreads,
66 "PacDhcpLookup");
[email protected]7258def2011-05-17 19:53:0067}
68
69DhcpProxyScriptFetcherWin::~DhcpProxyScriptFetcherWin() {
[email protected]961fefb2011-05-24 13:59:5870 // Count as user-initiated if we are not yet in STATE_DONE.
[email protected]7258def2011-05-17 19:53:0071 Cancel();
[email protected]ed671cf2013-08-27 12:23:4672
73 worker_pool_->Shutdown();
[email protected]7258def2011-05-17 19:53:0074}
75
[email protected]42cba2fb2013-03-29 19:58:5776int DhcpProxyScriptFetcherWin::Fetch(base::string16* utf16_text,
[email protected]235786812011-12-20 02:15:3177 const CompletionCallback& callback) {
[email protected]7258def2011-05-17 19:53:0078 DCHECK(CalledOnValidThread());
79 if (state_ != STATE_START && state_ != STATE_DONE) {
80 NOTREACHED();
81 return ERR_UNEXPECTED;
82 }
83
[email protected]c24ebc32011-06-23 20:34:5884 state_ = STATE_WAIT_ADAPTERS;
[email protected]235786812011-12-20 02:15:3185 callback_ = callback;
[email protected]7258def2011-05-17 19:53:0086 destination_string_ = utf16_text;
87
[email protected]28376542011-10-14 17:30:3788 last_query_ = ImplCreateAdapterQuery();
[email protected]ed671cf2013-08-27 12:23:4689 GetTaskRunner()->PostTaskAndReply(
[email protected]28376542011-10-14 17:30:3790 FROM_HERE,
91 base::Bind(
92 &DhcpProxyScriptFetcherWin::AdapterQuery::GetCandidateAdapterNames,
93 last_query_.get()),
94 base::Bind(
95 &DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone,
96 AsWeakPtr(),
[email protected]ed671cf2013-08-27 12:23:4697 last_query_));
[email protected]7258def2011-05-17 19:53:0098
99 return ERR_IO_PENDING;
100}
101
102void DhcpProxyScriptFetcherWin::Cancel() {
103 DCHECK(CalledOnValidThread());
104
[email protected]961fefb2011-05-24 13:59:58105 CancelImpl();
106}
107
108void DhcpProxyScriptFetcherWin::CancelImpl() {
109 DCHECK(CalledOnValidThread());
110
111 if (state_ != STATE_DONE) {
[email protected]235786812011-12-20 02:15:31112 callback_.Reset();
[email protected]7258def2011-05-17 19:53:00113 wait_timer_.Stop();
114 state_ = STATE_DONE;
115
116 for (FetcherVector::iterator it = fetchers_.begin();
117 it != fetchers_.end();
118 ++it) {
119 (*it)->Cancel();
120 }
121
[email protected]3ebea922012-07-19 00:38:58122 fetchers_.clear();
[email protected]7258def2011-05-17 19:53:00123 }
124}
125
[email protected]c24ebc32011-06-23 20:34:58126void DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone(
[email protected]28376542011-10-14 17:30:37127 scoped_refptr<AdapterQuery> query) {
[email protected]c24ebc32011-06-23 20:34:58128 DCHECK(CalledOnValidThread());
129
[email protected]28376542011-10-14 17:30:37130 // This can happen if this object is reused for multiple queries,
131 // and a previous query was cancelled before it completed.
132 if (query.get() != last_query_.get())
133 return;
134 last_query_ = NULL;
135
136 // Enable unit tests to wait for this to happen; in production this function
137 // call is a no-op.
138 ImplOnGetCandidateAdapterNamesDone();
139
[email protected]c24ebc32011-06-23 20:34:58140 // We may have been cancelled.
141 if (state_ != STATE_WAIT_ADAPTERS)
142 return;
143
144 state_ = STATE_NO_RESULTS;
145
[email protected]28376542011-10-14 17:30:37146 const std::set<std::string>& adapter_names = query->adapter_names();
147
[email protected]c24ebc32011-06-23 20:34:58148 if (adapter_names.empty()) {
149 TransitionToDone();
150 return;
151 }
152
153 for (std::set<std::string>::const_iterator it = adapter_names.begin();
154 it != adapter_names.end();
155 ++it) {
156 DhcpProxyScriptAdapterFetcher* fetcher(ImplCreateAdapterFetcher());
[email protected]235786812011-12-20 02:15:31157 fetcher->Fetch(
158 *it, base::Bind(&DhcpProxyScriptFetcherWin::OnFetcherDone,
159 base::Unretained(this)));
[email protected]c24ebc32011-06-23 20:34:58160 fetchers_.push_back(fetcher);
161 }
162 num_pending_fetchers_ = fetchers_.size();
163}
164
[email protected]7258def2011-05-17 19:53:00165std::string DhcpProxyScriptFetcherWin::GetFetcherName() const {
166 DCHECK(CalledOnValidThread());
167 return "win";
168}
169
170const GURL& DhcpProxyScriptFetcherWin::GetPacURL() const {
171 DCHECK(CalledOnValidThread());
172 DCHECK_EQ(state_, STATE_DONE);
173
174 return pac_url_;
175}
176
177void DhcpProxyScriptFetcherWin::OnFetcherDone(int result) {
178 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
179
180 if (--num_pending_fetchers_ == 0) {
181 TransitionToDone();
182 return;
183 }
184
185 // If the only pending adapters are those less preferred than one
186 // with a valid PAC script, we do not need to wait any longer.
187 for (FetcherVector::iterator it = fetchers_.begin();
188 it != fetchers_.end();
189 ++it) {
190 bool did_finish = (*it)->DidFinish();
191 int result = (*it)->GetResult();
192 if (did_finish && result == OK) {
193 TransitionToDone();
194 return;
195 }
196 if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) {
197 break;
198 }
199 }
200
201 // Once we have a single result, we set a maximum on how long to wait
202 // for the rest of the results.
203 if (state_ == STATE_NO_RESULTS) {
204 state_ = STATE_SOME_RESULTS;
[email protected]d323a172011-09-02 18:23:02205 wait_timer_.Start(FROM_HERE,
[email protected]0f2e45ec2012-07-11 15:41:43206 ImplGetMaxWait(), this, &DhcpProxyScriptFetcherWin::OnWaitTimer);
[email protected]7258def2011-05-17 19:53:00207 }
208}
209
210void DhcpProxyScriptFetcherWin::OnWaitTimer() {
211 DCHECK_EQ(state_, STATE_SOME_RESULTS);
[email protected]961fefb2011-05-24 13:59:58212
[email protected]7258def2011-05-17 19:53:00213 TransitionToDone();
214}
215
216void DhcpProxyScriptFetcherWin::TransitionToDone() {
217 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
218
[email protected]c24ebc32011-06-23 20:34:58219 int result = ERR_PAC_NOT_IN_DHCP; // Default if no fetchers.
220 if (!fetchers_.empty()) {
221 // Scan twice for the result; once through the whole list for success,
222 // then if no success, return result for most preferred network adapter,
223 // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error.
224 // Default to ERR_ABORTED if no fetcher completed.
225 result = ERR_ABORTED;
[email protected]7258def2011-05-17 19:53:00226 for (FetcherVector::iterator it = fetchers_.begin();
227 it != fetchers_.end();
228 ++it) {
[email protected]c24ebc32011-06-23 20:34:58229 if ((*it)->DidFinish() && (*it)->GetResult() == OK) {
230 result = OK;
231 *destination_string_ = (*it)->GetPacScript();
232 pac_url_ = (*it)->GetPacURL();
233 break;
234 }
235 }
236 if (result != OK) {
237 destination_string_->clear();
238 for (FetcherVector::iterator it = fetchers_.begin();
239 it != fetchers_.end();
240 ++it) {
241 if ((*it)->DidFinish()) {
242 result = (*it)->GetResult();
243 if (result != ERR_PAC_NOT_IN_DHCP) {
244 break;
245 }
[email protected]7258def2011-05-17 19:53:00246 }
247 }
248 }
249 }
250
[email protected]235786812011-12-20 02:15:31251 CompletionCallback callback = callback_;
[email protected]961fefb2011-05-24 13:59:58252 CancelImpl();
[email protected]7258def2011-05-17 19:53:00253 DCHECK_EQ(state_, STATE_DONE);
254 DCHECK(fetchers_.empty());
[email protected]235786812011-12-20 02:15:31255 DCHECK(callback_.is_null()); // Invariant of data.
[email protected]7258def2011-05-17 19:53:00256
[email protected]e2008d72011-06-13 17:08:20257 // We may be deleted re-entrantly within this outcall.
[email protected]235786812011-12-20 02:15:31258 callback.Run(result);
[email protected]7258def2011-05-17 19:53:00259}
260
[email protected]8d6bc142011-05-26 14:26:30261int DhcpProxyScriptFetcherWin::num_pending_fetchers() const {
262 return num_pending_fetchers_;
263}
264
265URLRequestContext* DhcpProxyScriptFetcherWin::url_request_context() const {
266 return url_request_context_;
267}
268
[email protected]ed671cf2013-08-27 12:23:46269scoped_refptr<base::TaskRunner> DhcpProxyScriptFetcherWin::GetTaskRunner() {
270 return worker_pool_->GetTaskRunnerWithShutdownBehavior(
271 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
272}
273
[email protected]7258def2011-05-17 19:53:00274DhcpProxyScriptAdapterFetcher*
275 DhcpProxyScriptFetcherWin::ImplCreateAdapterFetcher() {
[email protected]ed671cf2013-08-27 12:23:46276 return new DhcpProxyScriptAdapterFetcher(url_request_context_,
277 GetTaskRunner());
[email protected]7258def2011-05-17 19:53:00278}
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;
[email protected]dedde4ad2014-02-26 20:29:57297 scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> adapters;
[email protected]7258def2011-05-17 19:53:00298 ULONG error = ERROR_SUCCESS;
299 int num_tries = 0;
[email protected]961fefb2011-05-24 13:59:58300
[email protected]7258def2011-05-17 19:53:00301 do {
[email protected]dedde4ad2014-02-26 20:29:57302 adapters.reset(static_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
[email protected]7258def2011-05-17 19:53:00303 // Return only unicast addresses, and skip information we do not need.
304 error = GetAdaptersAddresses(AF_UNSPEC,
305 GAA_FLAG_SKIP_ANYCAST |
306 GAA_FLAG_SKIP_MULTICAST |
307 GAA_FLAG_SKIP_DNS_SERVER |
308 GAA_FLAG_SKIP_FRIENDLY_NAME,
309 NULL,
310 adapters.get(),
311 &adapters_size);
312 ++num_tries;
313 } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3);
314
[email protected]7258def2011-05-17 19:53:00315 if (error == ERROR_NO_DATA) {
316 // There are no adapters that we care about.
317 return true;
318 }
319
320 if (error != ERROR_SUCCESS) {
321 LOG(WARNING) << "Unexpected error retrieving WPAD configuration from DHCP.";
322 return false;
323 }
324
325 IP_ADAPTER_ADDRESSES* adapter = NULL;
326 for (adapter = adapters.get(); adapter; adapter = adapter->Next) {
327 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
328 continue;
329 if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0)
330 continue;
331
332 DCHECK(adapter->AdapterName);
333 adapter_names->insert(adapter->AdapterName);
334 }
335
336 return true;
337}
338
[email protected]28376542011-10-14 17:30:37339DhcpProxyScriptFetcherWin::AdapterQuery::AdapterQuery() {
[email protected]c24ebc32011-06-23 20:34:58340}
341
[email protected]28376542011-10-14 17:30:37342DhcpProxyScriptFetcherWin::AdapterQuery::~AdapterQuery() {
[email protected]c24ebc32011-06-23 20:34:58343}
344
[email protected]28376542011-10-14 17:30:37345void DhcpProxyScriptFetcherWin::AdapterQuery::GetCandidateAdapterNames() {
[email protected]c24ebc32011-06-23 20:34:58346 ImplGetCandidateAdapterNames(&adapter_names_);
[email protected]c24ebc32011-06-23 20:34:58347}
348
[email protected]28376542011-10-14 17:30:37349const std::set<std::string>&
350 DhcpProxyScriptFetcherWin::AdapterQuery::adapter_names() const {
351 return adapter_names_;
[email protected]c24ebc32011-06-23 20:34:58352}
353
[email protected]28376542011-10-14 17:30:37354bool DhcpProxyScriptFetcherWin::AdapterQuery::ImplGetCandidateAdapterNames(
[email protected]c24ebc32011-06-23 20:34:58355 std::set<std::string>* adapter_names) {
356 return DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(adapter_names);
357}
358
[email protected]7258def2011-05-17 19:53:00359} // namespace net