blob: 35109f608647e7a53974c190a4416aa705efdd94 [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
7#include "net/base/net_errors.h"
8#include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
9
10#include <winsock2.h>
11#include <iphlpapi.h>
12#pragma comment(lib, "iphlpapi.lib")
13
14namespace {
15
16// How long to wait at maximum after we get results (a PAC file or
17// knowledge that no PAC file is configured) from whichever network
18// adapter finishes first.
19const int kMaxWaitAfterFirstResultMs = 400;
20
21} // namespace
22
23namespace net {
24
25DhcpProxyScriptFetcherWin::DhcpProxyScriptFetcherWin(
26 URLRequestContext* url_request_context)
27 : state_(STATE_START),
28 ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_callback_(
29 this, &DhcpProxyScriptFetcherWin::OnFetcherDone)),
30 num_pending_fetchers_(0),
31 url_request_context_(url_request_context) {
32 DCHECK(url_request_context_);
33}
34
35DhcpProxyScriptFetcherWin::~DhcpProxyScriptFetcherWin() {
36 Cancel();
37}
38
39int DhcpProxyScriptFetcherWin::Fetch(string16* utf16_text,
40 CompletionCallback* callback) {
41 DCHECK(CalledOnValidThread());
42 if (state_ != STATE_START && state_ != STATE_DONE) {
43 NOTREACHED();
44 return ERR_UNEXPECTED;
45 }
46
47 std::set<std::string> adapter_names;
48 if (!ImplGetCandidateAdapterNames(&adapter_names)) {
49 return ERR_UNEXPECTED;
50 }
51 if (adapter_names.empty()) {
52 return ERR_PAC_NOT_IN_DHCP;
53 }
54
55 state_ = STATE_NO_RESULTS;
56
57 client_callback_ = callback;
58 destination_string_ = utf16_text;
59
60 for (std::set<std::string>::iterator it = adapter_names.begin();
61 it != adapter_names.end();
62 ++it) {
63 DhcpProxyScriptAdapterFetcher* fetcher(ImplCreateAdapterFetcher());
64 fetcher->Fetch(*it, &fetcher_callback_);
65 fetchers_.push_back(fetcher);
66 }
67 num_pending_fetchers_ = fetchers_.size();
68
69 return ERR_IO_PENDING;
70}
71
72void DhcpProxyScriptFetcherWin::Cancel() {
73 DCHECK(CalledOnValidThread());
74
75 if (state_ != STATE_DONE) {
76 wait_timer_.Stop();
77 state_ = STATE_DONE;
78
79 for (FetcherVector::iterator it = fetchers_.begin();
80 it != fetchers_.end();
81 ++it) {
82 (*it)->Cancel();
83 }
84
85 fetchers_.reset();
86 }
87}
88
89std::string DhcpProxyScriptFetcherWin::GetFetcherName() const {
90 DCHECK(CalledOnValidThread());
91 return "win";
92}
93
94const GURL& DhcpProxyScriptFetcherWin::GetPacURL() const {
95 DCHECK(CalledOnValidThread());
96 DCHECK_EQ(state_, STATE_DONE);
97
98 return pac_url_;
99}
100
101void DhcpProxyScriptFetcherWin::OnFetcherDone(int result) {
102 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
103
104 if (--num_pending_fetchers_ == 0) {
105 TransitionToDone();
106 return;
107 }
108
109 // If the only pending adapters are those less preferred than one
110 // with a valid PAC script, we do not need to wait any longer.
111 for (FetcherVector::iterator it = fetchers_.begin();
112 it != fetchers_.end();
113 ++it) {
114 bool did_finish = (*it)->DidFinish();
115 int result = (*it)->GetResult();
116 if (did_finish && result == OK) {
117 TransitionToDone();
118 return;
119 }
120 if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) {
121 break;
122 }
123 }
124
125 // Once we have a single result, we set a maximum on how long to wait
126 // for the rest of the results.
127 if (state_ == STATE_NO_RESULTS) {
128 state_ = STATE_SOME_RESULTS;
129 wait_timer_.Start(
130 base::TimeDelta::FromMilliseconds(ImplGetMaxWaitMs()),
131 this, &DhcpProxyScriptFetcherWin::OnWaitTimer);
132 }
133}
134
135void DhcpProxyScriptFetcherWin::OnWaitTimer() {
136 DCHECK_EQ(state_, STATE_SOME_RESULTS);
137 TransitionToDone();
138}
139
140void DhcpProxyScriptFetcherWin::TransitionToDone() {
141 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
142
143 // Should have returned immediately at Fetch() if no adapters to check.
144 DCHECK(!fetchers_.empty());
145
146 // Scan twice for the result; once through the whole list for success,
147 // then if no success, return result for most preferred network adapter,
148 // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error.
149 // Default to ERR_ABORTED if no fetcher completed.
150 int result = ERR_ABORTED;
151 for (FetcherVector::iterator it = fetchers_.begin();
152 it != fetchers_.end();
153 ++it) {
154 if ((*it)->DidFinish() && (*it)->GetResult() == OK) {
155 result = OK;
156 *destination_string_ = (*it)->GetPacScript();
157 pac_url_ = (*it)->GetPacURL();
158 break;
159 }
160 }
161 if (result != OK) {
162 destination_string_->clear();
163 for (FetcherVector::iterator it = fetchers_.begin();
164 it != fetchers_.end();
165 ++it) {
166 if ((*it)->DidFinish()) {
167 result = (*it)->GetResult();
168 if (result != ERR_PAC_NOT_IN_DHCP) {
169 break;
170 }
171 }
172 }
173 }
174
175 Cancel();
176 DCHECK_EQ(state_, STATE_DONE);
177 DCHECK(fetchers_.empty());
178
179 client_callback_->Run(result);
180}
181
182DhcpProxyScriptAdapterFetcher*
183 DhcpProxyScriptFetcherWin::ImplCreateAdapterFetcher() {
184 return new DhcpProxyScriptAdapterFetcher(url_request_context_);
185}
186
187bool DhcpProxyScriptFetcherWin::ImplGetCandidateAdapterNames(
188 std::set<std::string>* adapter_names) {
189 return GetCandidateAdapterNames(adapter_names);
190}
191
192int DhcpProxyScriptFetcherWin::ImplGetMaxWaitMs() {
193 return kMaxWaitAfterFirstResultMs;
194}
195
196bool DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(
197 std::set<std::string>* adapter_names) {
198 DCHECK(adapter_names);
199 adapter_names->clear();
200
201 // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to
202 // avoid reallocation.
203 ULONG adapters_size = 15000;
204 scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> adapters;
205 ULONG error = ERROR_SUCCESS;
206 int num_tries = 0;
207 do {
208 adapters.reset(
209 reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
210 // Return only unicast addresses, and skip information we do not need.
211 error = GetAdaptersAddresses(AF_UNSPEC,
212 GAA_FLAG_SKIP_ANYCAST |
213 GAA_FLAG_SKIP_MULTICAST |
214 GAA_FLAG_SKIP_DNS_SERVER |
215 GAA_FLAG_SKIP_FRIENDLY_NAME,
216 NULL,
217 adapters.get(),
218 &adapters_size);
219 ++num_tries;
220 } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3);
221
222 if (error == ERROR_NO_DATA) {
223 // There are no adapters that we care about.
224 return true;
225 }
226
227 if (error != ERROR_SUCCESS) {
228 LOG(WARNING) << "Unexpected error retrieving WPAD configuration from DHCP.";
229 return false;
230 }
231
232 IP_ADAPTER_ADDRESSES* adapter = NULL;
233 for (adapter = adapters.get(); adapter; adapter = adapter->Next) {
234 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
235 continue;
236 if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0)
237 continue;
238
239 DCHECK(adapter->AdapterName);
240 adapter_names->insert(adapter->AdapterName);
241 }
242
243 return true;
244}
245
246} // namespace net