Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(641)

Side by Side Diff: net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc

Issue 6831025: Adds support for the DHCP portion of the WPAD (proxy auto-discovery) protocol. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix memory leaks in unit test. Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // 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_adapter_fetcher_win.h"
6
7 #include "base/message_loop_proxy.h"
8 #include "base/sys_string_conversions.h"
9 #include "base/task.h"
10 #include "base/threading/worker_pool.h"
11 #include "base/time.h"
12 #include "net/base/net_errors.h"
13 #include "net/proxy/dhcpcsvc_init_win.h"
14 #include "net/proxy/proxy_script_fetcher_impl.h"
15 #include "net/url_request/url_request_context.h"
16
17 #include <windows.h>
18 #include <winsock2.h>
19 #include <dhcpcsdk.h>
20 #pragma comment(lib, "dhcpcsvc.lib")
21
22 namespace {
23
24 // Maximum amount of time to wait for response from the Win32 DHCP API.
25 const int kTimeoutMs = 2000;
26
27 } // namespace
28
29 namespace net {
30
31 DhcpProxyScriptAdapterFetcher::DhcpProxyScriptAdapterFetcher(
32 URLRequestContext* url_request_context)
33 : state_(STATE_START),
34 result_(ERR_IO_PENDING),
35 callback_(NULL),
36 ALLOW_THIS_IN_INITIALIZER_LIST(
37 script_fetcher_callback_(
38 this, &DhcpProxyScriptAdapterFetcher::OnFetcherDone)),
39 url_request_context_(url_request_context) {
40 }
41
42 DhcpProxyScriptAdapterFetcher::~DhcpProxyScriptAdapterFetcher() {
43 Cancel();
44
45 // The WeakPtr we passed to the worker thread may be destroyed on the
46 // worker thread. This detaches any outstanding WeakPtr state from
47 // the current thread.
48 base::SupportsWeakPtr<DhcpProxyScriptAdapterFetcher>::DetachFromThread();
49 }
50
51 void DhcpProxyScriptAdapterFetcher::Fetch(
52 const std::string& adapter_name, CompletionCallback* callback) {
53 DCHECK(CalledOnValidThread());
54 DCHECK_EQ(state_, STATE_START);
55 result_ = ERR_IO_PENDING;
56 pac_script_ = string16();
57 state_ = STATE_WAIT_DHCP;
58 callback_ = callback;
59
60 wait_timer_.Start(ImplGetTimeout(),
61 this, &DhcpProxyScriptAdapterFetcher::OnTimeout);
62 worker_thread_ = ImplCreateWorkerThread(AsWeakPtr());
63 worker_thread_->Start(adapter_name);
64 }
65
66 void DhcpProxyScriptAdapterFetcher::Cancel() {
67 DCHECK(CalledOnValidThread());
68 callback_ = NULL;
69 wait_timer_.Stop();
70 script_fetcher_.reset();
71
72 switch (state_) {
73 case STATE_WAIT_DHCP:
74 // Nothing to do here, we let the worker thread run to completion,
75 // the task it posts back when it completes will check the state.
76 break;
77 case STATE_WAIT_URL:
78 break;
79 case STATE_START:
80 case STATE_FINISH:
81 case STATE_CANCEL:
82 break;
83 }
84
85 if (state_ != STATE_FINISH) {
86 result_ = ERR_ABORTED;
87 state_ = STATE_CANCEL;
88 }
89 }
90
91 bool DhcpProxyScriptAdapterFetcher::DidFinish() const {
92 DCHECK(CalledOnValidThread());
93 return state_ == STATE_FINISH;
94 }
95
96 int DhcpProxyScriptAdapterFetcher::GetResult() const {
97 DCHECK(CalledOnValidThread());
98 return result_;
99 }
100
101 string16 DhcpProxyScriptAdapterFetcher::GetPacScript() const {
102 DCHECK(CalledOnValidThread());
103 return pac_script_;
104 }
105
106 GURL DhcpProxyScriptAdapterFetcher::GetPacURL() const {
107 DCHECK(CalledOnValidThread());
108 return pac_url_;
109 }
110
111 DhcpProxyScriptAdapterFetcher::WorkerThread::WorkerThread(
112 const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner)
113 : owner_(owner),
114 origin_loop_(base::MessageLoopProxy::CreateForCurrentThread()) {
115 }
116
117 DhcpProxyScriptAdapterFetcher::WorkerThread::~WorkerThread() {
118 }
119
120 void DhcpProxyScriptAdapterFetcher::WorkerThread::Start(
121 const std::string& adapter_name) {
122 bool succeeded = base::WorkerPool::PostTask(
123 FROM_HERE,
124 NewRunnableMethod(
125 this,
126 &DhcpProxyScriptAdapterFetcher::WorkerThread::ThreadFunc,
127 adapter_name),
128 true);
129 DCHECK(succeeded);
130 }
131
132 void DhcpProxyScriptAdapterFetcher::WorkerThread::ThreadFunc(
133 const std::string& adapter_name) {
134 std::string url = ImplGetPacURLFromDhcp(adapter_name);
135
136 bool succeeded = origin_loop_->PostTask(
137 FROM_HERE,
138 NewRunnableMethod(
139 this,
140 &DhcpProxyScriptAdapterFetcher::WorkerThread::OnThreadDone,
141 url));
142 DCHECK(succeeded);
143 }
144
145 void DhcpProxyScriptAdapterFetcher::WorkerThread::OnThreadDone(
146 const std::string& url) {
147 DCHECK(thread_checker_.CalledOnValidThread());
148 if (owner_)
149 owner_->OnQueryDhcpDone(url);
150 }
151
152 std::string
153 DhcpProxyScriptAdapterFetcher::WorkerThread::ImplGetPacURLFromDhcp(
154 const std::string& adapter_name) {
155 return DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(adapter_name);
156 }
157
158 void DhcpProxyScriptAdapterFetcher::OnQueryDhcpDone(
159 const std::string& url) {
160 DCHECK(CalledOnValidThread());
161 // Because we can't cancel the call to the Win32 API, we can expect
162 // it to finish while we are in a few different states. The expected
163 // one is WAIT_DHCP, but it could be in CANCEL if Cancel() was called,
164 // or FINISH if timeout occurred.
165 DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_CANCEL ||
166 state_ == STATE_FINISH);
167 if (state_ != STATE_WAIT_DHCP)
168 return;
169
170 wait_timer_.Stop();
171
172 pac_url_ = GURL(url);
173 if (pac_url_.is_empty() || !pac_url_.is_valid()) {
174 result_ = ERR_PAC_NOT_IN_DHCP;
175 TransitionToFinish();
176 } else {
177 state_ = STATE_WAIT_URL;
178 script_fetcher_.reset(ImplCreateScriptFetcher());
179 script_fetcher_->Fetch(pac_url_, &pac_script_, &script_fetcher_callback_);
180 }
181 }
182
183 void DhcpProxyScriptAdapterFetcher::OnTimeout() {
184 DCHECK_EQ(state_, STATE_WAIT_DHCP);
185 result_ = ERR_TIMED_OUT;
186 TransitionToFinish();
187 }
188
189 void DhcpProxyScriptAdapterFetcher::OnFetcherDone(int result) {
190 DCHECK(CalledOnValidThread());
191 DCHECK(state_ == STATE_WAIT_URL || state_ == STATE_CANCEL);
192 if (state_ == STATE_CANCEL)
193 return;
194
195 // At this point, pac_script_ has already been written to.
196 script_fetcher_.reset();
197 result_ = result;
198 TransitionToFinish();
199 }
200
201 void DhcpProxyScriptAdapterFetcher::TransitionToFinish() {
202 DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_WAIT_URL);
203 state_ = STATE_FINISH;
204 callback_->Run(result_);
205 callback_ = NULL;
206 }
207
208 ProxyScriptFetcher* DhcpProxyScriptAdapterFetcher::ImplCreateScriptFetcher() {
209 return new ProxyScriptFetcherImpl(url_request_context_);
210 }
211
212 DhcpProxyScriptAdapterFetcher::WorkerThread*
213 DhcpProxyScriptAdapterFetcher::ImplCreateWorkerThread(
214 const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner) {
215 return new WorkerThread(owner);
216 }
217
218 base::TimeDelta DhcpProxyScriptAdapterFetcher::ImplGetTimeout() const {
219 return base::TimeDelta::FromMilliseconds(kTimeoutMs);
220 }
221
222 // static
223 std::string DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(
224 const std::string& adapter_name) {
225 EnsureDhcpcsvcInit();
226
227 std::wstring adapter_name_wide = base::SysMultiByteToWide(adapter_name,
228 CP_ACP);
229
230 DHCPCAPI_PARAMS_ARRAY send_params = { 0, NULL };
231
232 BYTE option_data[] = { 1, 252 };
233 DHCPCAPI_PARAMS wpad_params = { 0 };
234 wpad_params.OptionId = 252;
235 wpad_params.IsVendor = FALSE; // Surprising, but intentional.
236
237 DHCPCAPI_PARAMS_ARRAY request_params = { 0 };
238 request_params.nParams = 1;
239 request_params.Params = &wpad_params;
240
241 // The maximum message size is typically 4096 bytes on Windows per
242 // http://support.microsoft.com/kb/321592
243 DWORD result_buffer_size = 4096;
244 scoped_ptr_malloc<BYTE> result_buffer;
245 int retry_count = 0;
246 DWORD res = NO_ERROR;
247 do {
248 result_buffer.reset(reinterpret_cast<BYTE*>(malloc(result_buffer_size)));
249
250 // Note that while the DHCPCAPI_REQUEST_SYNCHRONOUS flag seems to indicate
251 // there might be an asynchronous mode, there seems to be (at least in
252 // terms of well-documented use of this API) only a synchronous mode, with
253 // an optional "async notifications later if the option changes" mode.
254 // Even IE9, which we hope to emulate as IE is the most widely deployed
255 // previous implementation of the DHCP aspect of WPAD and the only one
256 // on Windows (Konqueror is the other, on Linux), uses this API with the
257 // synchronous flag. There seem to be several Microsoft Knowledge Base
258 // articles about calls to this function failing when other flags are used
259 // (e.g. http://support.microsoft.com/kb/885270) so we won't take any
260 // chances on non-standard, poorly documented usage.
261 res = ::DhcpRequestParams(DHCPCAPI_REQUEST_SYNCHRONOUS,
262 NULL,
263 const_cast<LPWSTR>(adapter_name_wide.c_str()),
264 NULL,
265 send_params, request_params,
266 result_buffer.get(), &result_buffer_size,
267 NULL);
268 ++retry_count;
269 } while (res == ERROR_MORE_DATA && retry_count <= 3);
270
271 if (res != NO_ERROR) {
272 NOTREACHED();
273 } else if (wpad_params.nBytesData) {
274 // The result should be ASCII, not wide character.
275 DCHECK_EQ(strlen(reinterpret_cast<const char*>(wpad_params.Data)) + 1,
276 wpad_params.nBytesData);
277 // Return only up to the first null in case of embedded NULLs; if the
278 // server is giving us back a buffer with embedded NULLs, something is
279 // broken anyway.
280 return std::string(reinterpret_cast<const char *>(wpad_params.Data));
281 }
282
283 return "";
284 }
285
286 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698