Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| 15 #include <windows.h> | |
| 16 #include <winsock2.h> | |
| 17 #include <dhcpcsdk.h> | |
| 18 #pragma comment(lib, "dhcpcsvc.lib") | |
| 19 | |
|
cpu_(ooo_6.6-7.5)
2011/04/22 20:54:59
can this dhcp dll be delay-loaded?
Jói
2011/05/03 21:20:59
Done, please take a look to make sure I did it cor
| |
| 20 namespace { | |
| 21 | |
| 22 // Maximum amount of time to wait for response from the Win32 DHCP API. | |
| 23 const int kTimeoutMs = 2000; | |
| 24 | |
| 25 } // namespace | |
| 26 | |
| 27 namespace net { | |
| 28 | |
| 29 DhcpProxyScriptAdapterFetcher::DhcpProxyScriptAdapterFetcher( | |
| 30 URLRequestContext* url_request_context) | |
| 31 : state_(STATE_START), | |
| 32 result_(ERR_IO_PENDING), | |
| 33 callback_(NULL), | |
| 34 ALLOW_THIS_IN_INITIALIZER_LIST( | |
| 35 script_fetcher_callback_( | |
| 36 this, &DhcpProxyScriptAdapterFetcher::OnFetcherDone)), | |
| 37 origin_loop_(base::MessageLoopProxy::CreateForCurrentThread()), | |
| 38 url_request_context_(url_request_context) { | |
| 39 } | |
| 40 | |
| 41 DhcpProxyScriptAdapterFetcher::~DhcpProxyScriptAdapterFetcher() { | |
| 42 Cancel(); | |
| 43 } | |
| 44 | |
| 45 void DhcpProxyScriptAdapterFetcher::Fetch( | |
| 46 const std::string& adapter_name, CompletionCallback* callback) { | |
| 47 DCHECK(CalledOnValidThread()); | |
| 48 DCHECK(state_ == STATE_START); | |
| 49 result_ = ERR_IO_PENDING; | |
| 50 pac_script_ = string16(); | |
| 51 state_ = STATE_WAIT_DHCP; | |
| 52 callback_ = callback; | |
| 53 | |
| 54 wait_timer_.Start(base::TimeDelta::FromMilliseconds(ImplGetTimeoutMs()), | |
| 55 this, &DhcpProxyScriptAdapterFetcher::OnTimeout); | |
| 56 bool succeeded = base::WorkerPool::PostTask( | |
| 57 FROM_HERE, | |
| 58 NewRunnableMethod( | |
| 59 this, | |
| 60 &DhcpProxyScriptAdapterFetcher::QueryDhcpOnWorkerThread, | |
| 61 adapter_name), | |
| 62 true); | |
| 63 DCHECK(succeeded); | |
| 64 } | |
| 65 | |
| 66 void DhcpProxyScriptAdapterFetcher::Cancel() { | |
| 67 DCHECK(CalledOnValidThread()); | |
| 68 callback_ = NULL; | |
| 69 wait_timer_.Stop(); | |
| 70 if (script_fetcher_.get()) { | |
|
eroman
2011/04/21 05:22:48
nit: don't need this if, can just reset unconditio
Jói
2011/05/03 21:20:59
Done.
| |
| 71 script_fetcher_.reset(); | |
| 72 } | |
| 73 | |
| 74 switch (state_) { | |
| 75 case STATE_WAIT_DHCP: | |
| 76 // Nothing to do here, we let the worker thread run to completion, | |
| 77 // the task it posts back when it completes will check the state. | |
| 78 break; | |
| 79 case STATE_WAIT_URL: | |
| 80 break; | |
| 81 case STATE_START: | |
| 82 case STATE_FINISH: | |
| 83 case STATE_CANCEL: | |
| 84 break; | |
| 85 } | |
| 86 | |
| 87 if (state_ != STATE_FINISH) { | |
| 88 result_ = ERR_ABORTED; | |
| 89 state_ = STATE_CANCEL; | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 bool DhcpProxyScriptAdapterFetcher::DidFinish() const { | |
| 94 DCHECK(CalledOnValidThread()); | |
| 95 return state_ == STATE_FINISH; | |
| 96 } | |
| 97 | |
| 98 int DhcpProxyScriptAdapterFetcher::result() const { | |
| 99 DCHECK(CalledOnValidThread()); | |
| 100 return result_; | |
| 101 } | |
| 102 | |
| 103 string16 DhcpProxyScriptAdapterFetcher::pac_script() const { | |
| 104 DCHECK(CalledOnValidThread()); | |
| 105 return pac_script_; | |
| 106 } | |
| 107 | |
| 108 GURL DhcpProxyScriptAdapterFetcher::pac_url() const { | |
| 109 DCHECK(CalledOnValidThread()); | |
| 110 return pac_url_; | |
| 111 } | |
| 112 | |
| 113 void DhcpProxyScriptAdapterFetcher::QueryDhcpOnWorkerThread( | |
| 114 const std::string& adapter_name) { | |
| 115 std::string url = ImplGetPacURLFromDhcp(adapter_name); | |
| 116 | |
| 117 bool succeeded = origin_loop_->PostTask( | |
| 118 FROM_HERE, | |
| 119 NewRunnableMethod(this, | |
| 120 &DhcpProxyScriptAdapterFetcher::OnQueryDhcpDone, | |
| 121 url)); | |
| 122 DCHECK(succeeded); | |
| 123 } | |
| 124 | |
| 125 void DhcpProxyScriptAdapterFetcher::OnQueryDhcpDone( | |
| 126 std::string url) { | |
| 127 DCHECK(CalledOnValidThread()); | |
| 128 // Because we can't cancel the call to the Win32 API, we can expect | |
| 129 // it to finish while we are in a few different states. The expected | |
| 130 // one is WAIT_DHCP, but it could be in CANCEL if Cancel() was called, | |
| 131 // or FINISH if timeout occurred. | |
| 132 DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_CANCEL || | |
| 133 state_ == STATE_FINISH); | |
| 134 if (state_ != STATE_WAIT_DHCP) | |
| 135 return; | |
| 136 | |
| 137 wait_timer_.Stop(); | |
| 138 | |
| 139 if (url.empty()) { | |
| 140 result_ = ERR_PAC_NOT_IN_DHCP; | |
| 141 TransitionToFinish(); | |
| 142 } else { | |
| 143 state_ = STATE_WAIT_URL; | |
| 144 pac_url_ = GURL(url); | |
| 145 script_fetcher_.reset(ImplCreateScriptFetcher(pac_url_)); | |
| 146 script_fetcher_->Fetch(&pac_script_, &script_fetcher_callback_); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 void DhcpProxyScriptAdapterFetcher::OnTimeout() { | |
| 151 DCHECK(state_ == STATE_WAIT_DHCP); | |
| 152 result_ = ERR_TIMED_OUT; | |
| 153 TransitionToFinish(); | |
| 154 } | |
| 155 | |
| 156 void DhcpProxyScriptAdapterFetcher::OnFetcherDone(int result) { | |
| 157 DCHECK(CalledOnValidThread()); | |
| 158 DCHECK(state_ == STATE_WAIT_URL || state_ == STATE_CANCEL); | |
| 159 if (state_ == STATE_CANCEL) | |
| 160 return; | |
| 161 | |
| 162 // At this point, pac_script_ has already been written to. | |
| 163 script_fetcher_.reset(); | |
| 164 result_ = result; | |
| 165 TransitionToFinish(); | |
| 166 } | |
| 167 | |
| 168 void DhcpProxyScriptAdapterFetcher::TransitionToFinish() { | |
| 169 DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_WAIT_URL); | |
| 170 state_ = STATE_FINISH; | |
| 171 callback_->Run(result_); | |
| 172 callback_ = NULL; | |
| 173 } | |
| 174 | |
| 175 std::string DhcpProxyScriptAdapterFetcher::ImplGetPacURLFromDhcp( | |
| 176 const std::string& adapter_name) { | |
| 177 return GetPacURLFromDhcp(adapter_name); | |
| 178 } | |
| 179 | |
| 180 ProxyScriptFetcher* DhcpProxyScriptAdapterFetcher::ImplCreateScriptFetcher( | |
| 181 const GURL& url) { | |
| 182 URLProxyScriptFetcher* fetcher = | |
| 183 new ProxyScriptFetcherImpl(url_request_context_); | |
| 184 fetcher->SetURL(url); | |
| 185 return fetcher; | |
| 186 } | |
| 187 | |
| 188 int DhcpProxyScriptAdapterFetcher::ImplGetTimeoutMs() const { | |
| 189 return kTimeoutMs; | |
| 190 } | |
| 191 | |
| 192 std::string DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp( | |
| 193 const std::string& adapter_name) { | |
| 194 EnsureDhcpcsvcInit(); | |
| 195 | |
| 196 std::wstring adapter_name_wide = base::SysMultiByteToWide(adapter_name, | |
| 197 CP_ACP); | |
| 198 | |
| 199 DHCPCAPI_PARAMS_ARRAY send_params = { 0, NULL }; | |
| 200 | |
| 201 BYTE option_data[] = { 1, 252 }; | |
| 202 DHCPCAPI_PARAMS wpad_params = { 0 }; | |
| 203 wpad_params.OptionId = 252; | |
| 204 wpad_params.IsVendor = FALSE; // Surprising, but intentional. | |
| 205 | |
| 206 DHCPCAPI_PARAMS_ARRAY request_params = { 0 }; | |
| 207 request_params.nParams = 1; | |
| 208 request_params.Params = &wpad_params; | |
| 209 | |
| 210 // The maximum message size is typically 4096 bytes on Windows per | |
| 211 // http://support.microsoft.com/kb/321592 | |
| 212 DWORD result_buffer_size = 4096; | |
| 213 scoped_ptr_malloc<BYTE> result_buffer; | |
| 214 int retry_count = 0; | |
| 215 DWORD res = NO_ERROR; | |
| 216 do { | |
| 217 result_buffer.reset(reinterpret_cast<BYTE*>(malloc(result_buffer_size))); | |
| 218 | |
| 219 // Note that while the DHCPCAPI_REQUEST_SYNCHRONOUS flag seems to indicate | |
| 220 // there might be an asynchronous mode, there seems to be (at least in | |
| 221 // terms of well-documented use of this API) only a synchronous mode, with | |
| 222 // an optional "async notifications later if the option changes" mode. | |
| 223 // Even IE9, which we hope to emulate as IE is the most widely deployed | |
| 224 // previous implementation of the DHCP aspect of WPAD and the only one | |
| 225 // on Windows (Konqueror is the other, on Linux), uses this API with the | |
| 226 // synchronous flag. There seem to be several Microsoft Knowledge Base | |
| 227 // articles about calls to this function failing when other flags are used | |
| 228 // (e.g. http://support.microsoft.com/kb/885270) so we won't take any | |
| 229 // chances on non-standard, poorly documented usage. | |
| 230 res = ::DhcpRequestParams(DHCPCAPI_REQUEST_SYNCHRONOUS, | |
| 231 NULL, | |
| 232 const_cast<LPWSTR>(adapter_name_wide.c_str()), | |
| 233 NULL, | |
| 234 send_params, request_params, | |
| 235 result_buffer.get(), &result_buffer_size, | |
| 236 NULL); | |
| 237 ++retry_count; | |
| 238 } while (res == ERROR_MORE_DATA && retry_count <= 3); | |
| 239 | |
| 240 if (res != NO_ERROR) { | |
| 241 NOTREACHED(); | |
| 242 } else if (wpad_params.nBytesData) { | |
| 243 // The result should be ASCII, not wide character. | |
| 244 DCHECK(strlen(reinterpret_cast<const char*>(wpad_params.Data)) + 1 == | |
| 245 wpad_params.nBytesData); | |
| 246 return std::string(reinterpret_cast<const char *>(wpad_params.Data)); | |
| 247 } | |
| 248 | |
| 249 return ""; | |
| 250 } | |
| 251 | |
| 252 } // namespace net | |
| OLD | NEW |