- add sources.
[platform/framework/web/crosswalk.git] / src / net / proxy / dhcp_proxy_script_adapter_fetcher_win.cc
1 // Copyright (c) 2012 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/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "base/task_runner.h"
15 #include "base/time/time.h"
16 #include "net/base/net_errors.h"
17 #include "net/proxy/dhcpcsvc_init_win.h"
18 #include "net/proxy/proxy_script_fetcher_impl.h"
19 #include "net/url_request/url_request_context.h"
20
21 #include <windows.h>
22 #include <winsock2.h>
23 #include <dhcpcsdk.h>
24 #pragma comment(lib, "dhcpcsvc.lib")
25
26 namespace {
27
28 // Maximum amount of time to wait for response from the Win32 DHCP API.
29 const int kTimeoutMs = 2000;
30
31 }  // namespace
32
33 namespace net {
34
35 DhcpProxyScriptAdapterFetcher::DhcpProxyScriptAdapterFetcher(
36     URLRequestContext* url_request_context,
37     scoped_refptr<base::TaskRunner> task_runner)
38     : task_runner_(task_runner),
39       state_(STATE_START),
40       result_(ERR_IO_PENDING),
41       url_request_context_(url_request_context) {
42   DCHECK(url_request_context_);
43 }
44
45 DhcpProxyScriptAdapterFetcher::~DhcpProxyScriptAdapterFetcher() {
46   Cancel();
47 }
48
49 void DhcpProxyScriptAdapterFetcher::Fetch(
50     const std::string& adapter_name, const CompletionCallback& callback) {
51   DCHECK(CalledOnValidThread());
52   DCHECK_EQ(state_, STATE_START);
53   result_ = ERR_IO_PENDING;
54   pac_script_ = base::string16();
55   state_ = STATE_WAIT_DHCP;
56   callback_ = callback;
57
58   wait_timer_.Start(FROM_HERE, ImplGetTimeout(),
59                     this, &DhcpProxyScriptAdapterFetcher::OnTimeout);
60   scoped_refptr<DhcpQuery> dhcp_query(ImplCreateDhcpQuery());
61   task_runner_->PostTaskAndReply(
62       FROM_HERE,
63       base::Bind(
64           &DhcpProxyScriptAdapterFetcher::DhcpQuery::GetPacURLForAdapter,
65           dhcp_query.get(),
66           adapter_name),
67       base::Bind(
68           &DhcpProxyScriptAdapterFetcher::OnDhcpQueryDone,
69           AsWeakPtr(),
70           dhcp_query));
71 }
72
73 void DhcpProxyScriptAdapterFetcher::Cancel() {
74   DCHECK(CalledOnValidThread());
75   callback_.Reset();
76   wait_timer_.Stop();
77   script_fetcher_.reset();
78
79   switch (state_) {
80     case STATE_WAIT_DHCP:
81       // Nothing to do here, we let the worker thread run to completion,
82       // the task it posts back when it completes will check the state.
83       break;
84     case STATE_WAIT_URL:
85       break;
86     case STATE_START:
87     case STATE_FINISH:
88     case STATE_CANCEL:
89       break;
90   }
91
92   if (state_ != STATE_FINISH) {
93     result_ = ERR_ABORTED;
94     state_ = STATE_CANCEL;
95   }
96 }
97
98 bool DhcpProxyScriptAdapterFetcher::DidFinish() const {
99   DCHECK(CalledOnValidThread());
100   return state_ == STATE_FINISH;
101 }
102
103 int DhcpProxyScriptAdapterFetcher::GetResult() const {
104   DCHECK(CalledOnValidThread());
105   return result_;
106 }
107
108 base::string16 DhcpProxyScriptAdapterFetcher::GetPacScript() const {
109   DCHECK(CalledOnValidThread());
110   return pac_script_;
111 }
112
113 GURL DhcpProxyScriptAdapterFetcher::GetPacURL() const {
114   DCHECK(CalledOnValidThread());
115   return pac_url_;
116 }
117
118 DhcpProxyScriptAdapterFetcher::DhcpQuery::DhcpQuery() {
119 }
120
121 DhcpProxyScriptAdapterFetcher::DhcpQuery::~DhcpQuery() {
122 }
123
124 void DhcpProxyScriptAdapterFetcher::DhcpQuery::GetPacURLForAdapter(
125     const std::string& adapter_name) {
126   url_ = ImplGetPacURLFromDhcp(adapter_name);
127 }
128
129 const std::string& DhcpProxyScriptAdapterFetcher::DhcpQuery::url() const {
130   return url_;
131 }
132
133 std::string
134     DhcpProxyScriptAdapterFetcher::DhcpQuery::ImplGetPacURLFromDhcp(
135         const std::string& adapter_name) {
136   return DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(adapter_name);
137 }
138
139 void DhcpProxyScriptAdapterFetcher::OnDhcpQueryDone(
140     scoped_refptr<DhcpQuery> dhcp_query) {
141   DCHECK(CalledOnValidThread());
142   // Because we can't cancel the call to the Win32 API, we can expect
143   // it to finish while we are in a few different states.  The expected
144   // one is WAIT_DHCP, but it could be in CANCEL if Cancel() was called,
145   // or FINISH if timeout occurred.
146   DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_CANCEL ||
147          state_ == STATE_FINISH);
148   if (state_ != STATE_WAIT_DHCP)
149     return;
150
151   wait_timer_.Stop();
152
153   pac_url_ = GURL(dhcp_query->url());
154   if (pac_url_.is_empty() || !pac_url_.is_valid()) {
155     result_ = ERR_PAC_NOT_IN_DHCP;
156     TransitionToFinish();
157   } else {
158     state_ = STATE_WAIT_URL;
159     script_fetcher_.reset(ImplCreateScriptFetcher());
160     script_fetcher_->Fetch(
161         pac_url_, &pac_script_,
162         base::Bind(&DhcpProxyScriptAdapterFetcher::OnFetcherDone,
163                    base::Unretained(this)));
164   }
165 }
166
167 void DhcpProxyScriptAdapterFetcher::OnTimeout() {
168   DCHECK_EQ(state_, STATE_WAIT_DHCP);
169   result_ = ERR_TIMED_OUT;
170   TransitionToFinish();
171 }
172
173 void DhcpProxyScriptAdapterFetcher::OnFetcherDone(int result) {
174   DCHECK(CalledOnValidThread());
175   DCHECK(state_ == STATE_WAIT_URL || state_ == STATE_CANCEL);
176   if (state_ == STATE_CANCEL)
177     return;
178
179   // At this point, pac_script_ has already been written to.
180   script_fetcher_.reset();
181   result_ = result;
182   TransitionToFinish();
183 }
184
185 void DhcpProxyScriptAdapterFetcher::TransitionToFinish() {
186   DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_WAIT_URL);
187   state_ = STATE_FINISH;
188   CompletionCallback callback = callback_;
189   callback_.Reset();
190
191   // Be careful not to touch any member state after this, as the client
192   // may delete us during this callback.
193   callback.Run(result_);
194 }
195
196 DhcpProxyScriptAdapterFetcher::State
197     DhcpProxyScriptAdapterFetcher::state() const {
198   return state_;
199 }
200
201 ProxyScriptFetcher* DhcpProxyScriptAdapterFetcher::ImplCreateScriptFetcher() {
202   return new ProxyScriptFetcherImpl(url_request_context_);
203 }
204
205 DhcpProxyScriptAdapterFetcher::DhcpQuery*
206     DhcpProxyScriptAdapterFetcher::ImplCreateDhcpQuery() {
207   return new DhcpQuery();
208 }
209
210 base::TimeDelta DhcpProxyScriptAdapterFetcher::ImplGetTimeout() const {
211   return base::TimeDelta::FromMilliseconds(kTimeoutMs);
212 }
213
214 // static
215 std::string DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(
216     const std::string& adapter_name) {
217   EnsureDhcpcsvcInit();
218
219   std::wstring adapter_name_wide = base::SysMultiByteToWide(adapter_name,
220                                                             CP_ACP);
221
222   DHCPCAPI_PARAMS_ARRAY send_params = { 0, NULL };
223
224   BYTE option_data[] = { 1, 252 };
225   DHCPCAPI_PARAMS wpad_params = { 0 };
226   wpad_params.OptionId = 252;
227   wpad_params.IsVendor = FALSE;  // Surprising, but intentional.
228
229   DHCPCAPI_PARAMS_ARRAY request_params = { 0 };
230   request_params.nParams = 1;
231   request_params.Params = &wpad_params;
232
233   // The maximum message size is typically 4096 bytes on Windows per
234   // http://support.microsoft.com/kb/321592
235   DWORD result_buffer_size = 4096;
236   scoped_ptr_malloc<BYTE> result_buffer;
237   int retry_count = 0;
238   DWORD res = NO_ERROR;
239   do {
240     result_buffer.reset(reinterpret_cast<BYTE*>(malloc(result_buffer_size)));
241
242     // Note that while the DHCPCAPI_REQUEST_SYNCHRONOUS flag seems to indicate
243     // there might be an asynchronous mode, there seems to be (at least in
244     // terms of well-documented use of this API) only a synchronous mode, with
245     // an optional "async notifications later if the option changes" mode.
246     // Even IE9, which we hope to emulate as IE is the most widely deployed
247     // previous implementation of the DHCP aspect of WPAD and the only one
248     // on Windows (Konqueror is the other, on Linux), uses this API with the
249     // synchronous flag.  There seem to be several Microsoft Knowledge Base
250     // articles about calls to this function failing when other flags are used
251     // (e.g. http://support.microsoft.com/kb/885270) so we won't take any
252     // chances on non-standard, poorly documented usage.
253     res = ::DhcpRequestParams(DHCPCAPI_REQUEST_SYNCHRONOUS,
254                               NULL,
255                               const_cast<LPWSTR>(adapter_name_wide.c_str()),
256                               NULL,
257                               send_params, request_params,
258                               result_buffer.get(), &result_buffer_size,
259                               NULL);
260     ++retry_count;
261   } while (res == ERROR_MORE_DATA && retry_count <= 3);
262
263   if (res != NO_ERROR) {
264     LOG(INFO) << "Error fetching PAC URL from DHCP: " << res;
265     UMA_HISTOGRAM_COUNTS("Net.DhcpWpadUnhandledDhcpError", 1);
266   } else if (wpad_params.nBytesData) {
267     return SanitizeDhcpApiString(
268         reinterpret_cast<const char*>(wpad_params.Data),
269         wpad_params.nBytesData);
270   }
271
272   return "";
273 }
274
275 // static
276 std::string DhcpProxyScriptAdapterFetcher::SanitizeDhcpApiString(
277     const char* data, size_t count_bytes) {
278   // The result should be ASCII, not wide character.  Some DHCP
279   // servers appear to count the trailing NULL in nBytesData, others
280   // do not.  A few (we've had one report, http://crbug.com/297810)
281   // do not NULL-terminate but may \n-terminate.
282   //
283   // Belt and suspenders and elastic waistband: First, ensure we
284   // NULL-terminate after nBytesData; this is the inner constructor
285   // with nBytesData as a parameter.  Then, return only up to the
286   // first null in case of embedded NULLs; this is the outer
287   // constructor that takes the result of c_str() on the inner.  If
288   // the server is giving us back a buffer with embedded NULLs,
289   // something is broken anyway.  Finally, trim trailing whitespace.
290   std::string result(std::string(data, count_bytes).c_str());
291   TrimWhitespaceASCII(result, TRIM_TRAILING, &result);
292   return result;
293 }
294
295 }  // namespace net