• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2009 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/init_proxy_resolver.h"
6 
7 #include "base/compiler_specific.h"
8 #include "base/format_macros.h"
9 #include "base/logging.h"
10 #include "base/string_util.h"
11 #include "net/base/net_log.h"
12 #include "net/base/net_errors.h"
13 #include "net/proxy/proxy_config.h"
14 #include "net/proxy/proxy_resolver.h"
15 #include "net/proxy/proxy_script_fetcher.h"
16 
17 namespace net {
18 
19 // This is the hard-coded location used by the DNS portion of web proxy
20 // auto-discovery.
21 //
22 // Note that we not use DNS devolution to find the WPAD host, since that could
23 // be dangerous should our top level domain registry  become out of date.
24 //
25 // Instead we directly resolve "wpad", and let the operating system apply the
26 // DNS suffix search paths. This is the same approach taken by Firefox, and
27 // compatibility hasn't been an issue.
28 //
29 // For more details, also check out this comment:
30 // http://code.google.com/p/chromium/issues/detail?id=18575#c20
31 static const char kWpadUrl[] = "http://wpad/wpad.dat";
32 
InitProxyResolver(ProxyResolver * resolver,ProxyScriptFetcher * proxy_script_fetcher,NetLog * net_log)33 InitProxyResolver::InitProxyResolver(ProxyResolver* resolver,
34                                      ProxyScriptFetcher* proxy_script_fetcher,
35                                      NetLog* net_log)
36     : resolver_(resolver),
37       proxy_script_fetcher_(proxy_script_fetcher),
38       ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(
39           this, &InitProxyResolver::OnIOCompletion)),
40       user_callback_(NULL),
41       current_pac_url_index_(0u),
42       next_state_(STATE_NONE),
43       net_log_(BoundNetLog::Make(
44           net_log, NetLog::SOURCE_INIT_PROXY_RESOLVER)),
45       effective_config_(NULL) {
46 }
47 
~InitProxyResolver()48 InitProxyResolver::~InitProxyResolver() {
49   if (next_state_ != STATE_NONE)
50     Cancel();
51 }
52 
Init(const ProxyConfig & config,const base::TimeDelta wait_delay,ProxyConfig * effective_config,CompletionCallback * callback)53 int InitProxyResolver::Init(const ProxyConfig& config,
54                             const base::TimeDelta wait_delay,
55                             ProxyConfig* effective_config,
56                             CompletionCallback* callback) {
57   DCHECK_EQ(STATE_NONE, next_state_);
58   DCHECK(callback);
59   DCHECK(config.HasAutomaticSettings());
60 
61   net_log_.BeginEvent(NetLog::TYPE_INIT_PROXY_RESOLVER, NULL);
62 
63   // Save the |wait_delay| as a non-negative value.
64   wait_delay_ = wait_delay;
65   if (wait_delay_ < base::TimeDelta())
66     wait_delay_ = base::TimeDelta();
67 
68   effective_config_ = effective_config;
69 
70   pac_urls_ = BuildPacUrlsFallbackList(config);
71   DCHECK(!pac_urls_.empty());
72 
73   next_state_ = STATE_WAIT;
74 
75   int rv = DoLoop(OK);
76   if (rv == ERR_IO_PENDING)
77     user_callback_ = callback;
78   else
79     DidCompleteInit();
80 
81   return rv;
82 }
83 
84 // Initialize the fallback rules.
85 // (1) WPAD (DNS).
86 // (2) Custom PAC URL.
BuildPacUrlsFallbackList(const ProxyConfig & config) const87 InitProxyResolver::UrlList InitProxyResolver::BuildPacUrlsFallbackList(
88     const ProxyConfig& config) const {
89   UrlList pac_urls;
90   if (config.auto_detect())
91     pac_urls.push_back(PacURL(true, GURL()));
92   if (config.has_pac_url())
93     pac_urls.push_back(PacURL(false, config.pac_url()));
94   return pac_urls;
95 }
96 
OnIOCompletion(int result)97 void InitProxyResolver::OnIOCompletion(int result) {
98   DCHECK_NE(STATE_NONE, next_state_);
99   int rv = DoLoop(result);
100   if (rv != ERR_IO_PENDING) {
101     DidCompleteInit();
102     DoCallback(rv);
103   }
104 }
105 
DoLoop(int result)106 int InitProxyResolver::DoLoop(int result) {
107   DCHECK_NE(next_state_, STATE_NONE);
108   int rv = result;
109   do {
110     State state = next_state_;
111     next_state_ = STATE_NONE;
112     switch (state) {
113       case STATE_WAIT:
114         DCHECK_EQ(OK, rv);
115         rv = DoWait();
116         break;
117       case STATE_WAIT_COMPLETE:
118         rv = DoWaitComplete(rv);
119         break;
120       case STATE_FETCH_PAC_SCRIPT:
121         DCHECK_EQ(OK, rv);
122         rv = DoFetchPacScript();
123         break;
124       case STATE_FETCH_PAC_SCRIPT_COMPLETE:
125         rv = DoFetchPacScriptComplete(rv);
126         break;
127       case STATE_SET_PAC_SCRIPT:
128         DCHECK_EQ(OK, rv);
129         rv = DoSetPacScript();
130         break;
131       case STATE_SET_PAC_SCRIPT_COMPLETE:
132         rv = DoSetPacScriptComplete(rv);
133         break;
134       default:
135         NOTREACHED() << "bad state";
136         rv = ERR_UNEXPECTED;
137         break;
138     }
139   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
140   return rv;
141 }
142 
DoCallback(int result)143 void InitProxyResolver::DoCallback(int result) {
144   DCHECK_NE(ERR_IO_PENDING, result);
145   DCHECK(user_callback_);
146   user_callback_->Run(result);
147 }
148 
DoWait()149 int InitProxyResolver::DoWait() {
150   next_state_ = STATE_WAIT_COMPLETE;
151 
152   // If no waiting is required, continue on to the next state.
153   if (wait_delay_.ToInternalValue() == 0)
154     return OK;
155 
156   // Otherwise wait the specified amount of time.
157   wait_timer_.Start(wait_delay_, this, &InitProxyResolver::OnWaitTimerFired);
158   net_log_.BeginEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT, NULL);
159   return ERR_IO_PENDING;
160 }
161 
DoWaitComplete(int result)162 int InitProxyResolver::DoWaitComplete(int result) {
163   DCHECK_EQ(OK, result);
164   if (wait_delay_.ToInternalValue() != 0) {
165     net_log_.EndEventWithNetErrorCode(NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT,
166                                       result);
167   }
168   next_state_ = GetStartState();
169   return OK;
170 }
171 
DoFetchPacScript()172 int InitProxyResolver::DoFetchPacScript() {
173   DCHECK(resolver_->expects_pac_bytes());
174 
175   next_state_ = STATE_FETCH_PAC_SCRIPT_COMPLETE;
176 
177   const PacURL& pac_url = current_pac_url();
178 
179   const GURL effective_pac_url =
180       pac_url.auto_detect ? GURL(kWpadUrl) : pac_url.url;
181 
182   net_log_.BeginEvent(
183       NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT,
184       make_scoped_refptr(new NetLogStringParameter(
185           "url", effective_pac_url.possibly_invalid_spec())));
186 
187   if (!proxy_script_fetcher_) {
188     net_log_.AddEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_HAS_NO_FETCHER, NULL);
189     return ERR_UNEXPECTED;
190   }
191 
192   return proxy_script_fetcher_->Fetch(effective_pac_url,
193                                       &pac_script_,
194                                       &io_callback_);
195 }
196 
DoFetchPacScriptComplete(int result)197 int InitProxyResolver::DoFetchPacScriptComplete(int result) {
198   DCHECK(resolver_->expects_pac_bytes());
199 
200   net_log_.EndEventWithNetErrorCode(
201       NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT, result);
202   if (result != OK)
203     return TryToFallbackPacUrl(result);
204 
205   next_state_ = STATE_SET_PAC_SCRIPT;
206   return result;
207 }
208 
DoSetPacScript()209 int InitProxyResolver::DoSetPacScript() {
210   net_log_.BeginEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT, NULL);
211 
212   const PacURL& pac_url = current_pac_url();
213 
214   next_state_ = STATE_SET_PAC_SCRIPT_COMPLETE;
215 
216   scoped_refptr<ProxyResolverScriptData> script_data;
217 
218   if (resolver_->expects_pac_bytes()) {
219     script_data = ProxyResolverScriptData::FromUTF16(pac_script_);
220   } else {
221     script_data = pac_url.auto_detect ?
222         ProxyResolverScriptData::ForAutoDetect() :
223         ProxyResolverScriptData::FromURL(pac_url.url);
224   }
225 
226   return resolver_->SetPacScript(script_data, &io_callback_);
227 }
228 
DoSetPacScriptComplete(int result)229 int InitProxyResolver::DoSetPacScriptComplete(int result) {
230   net_log_.EndEventWithNetErrorCode(
231       NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT, result);
232   if (result != OK)
233     return TryToFallbackPacUrl(result);
234 
235   // Let the caller know which automatic setting we ended up initializing the
236   // resolver for (there may have been multiple fallbacks to choose from.)
237   if (effective_config_) {
238     if (current_pac_url().auto_detect && resolver_->expects_pac_bytes()) {
239       *effective_config_ =
240           ProxyConfig::CreateFromCustomPacURL(GURL(kWpadUrl));
241     } else if (current_pac_url().auto_detect) {
242       *effective_config_ = ProxyConfig::CreateAutoDetect();
243     } else {
244       *effective_config_ =
245           ProxyConfig::CreateFromCustomPacURL(current_pac_url().url);
246     }
247   }
248 
249   return result;
250 }
251 
TryToFallbackPacUrl(int error)252 int InitProxyResolver::TryToFallbackPacUrl(int error) {
253   DCHECK_LT(error, 0);
254 
255   if (current_pac_url_index_ + 1 >= pac_urls_.size()) {
256     // Nothing left to fall back to.
257     return error;
258   }
259 
260   // Advance to next URL in our list.
261   ++current_pac_url_index_;
262 
263   net_log_.AddEvent(
264       NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL, NULL);
265 
266   next_state_ = GetStartState();
267 
268   return OK;
269 }
270 
GetStartState() const271 InitProxyResolver::State InitProxyResolver::GetStartState() const {
272   return resolver_->expects_pac_bytes() ?
273       STATE_FETCH_PAC_SCRIPT : STATE_SET_PAC_SCRIPT;
274 }
275 
current_pac_url() const276 const InitProxyResolver::PacURL& InitProxyResolver::current_pac_url() const {
277   DCHECK_LT(current_pac_url_index_, pac_urls_.size());
278   return pac_urls_[current_pac_url_index_];
279 }
280 
OnWaitTimerFired()281 void InitProxyResolver::OnWaitTimerFired() {
282   OnIOCompletion(OK);
283 }
284 
DidCompleteInit()285 void InitProxyResolver::DidCompleteInit() {
286   net_log_.EndEvent(NetLog::TYPE_INIT_PROXY_RESOLVER, NULL);
287 }
288 
Cancel()289 void InitProxyResolver::Cancel() {
290   DCHECK_NE(STATE_NONE, next_state_);
291 
292   net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL);
293 
294   switch (next_state_) {
295     case STATE_WAIT_COMPLETE:
296       wait_timer_.Stop();
297       break;
298     case STATE_FETCH_PAC_SCRIPT_COMPLETE:
299       proxy_script_fetcher_->Cancel();
300       break;
301     case STATE_SET_PAC_SCRIPT_COMPLETE:
302       resolver_->CancelSetPacScript();
303       break;
304     default:
305       NOTREACHED();
306       break;
307   }
308 
309   DidCompleteInit();
310 }
311 
312 }  // namespace net
313