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 "chrome/browser/net/pref_proxy_config_tracker_impl.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/metrics/histogram_macros.h" | |
9 #include "base/prefs/pref_registry_simple.h" | |
10 #include "base/prefs/pref_service.h" | |
11 #include "base/strings/string_util.h" | |
12 #include "base/values.h" | |
13 #include "chrome/browser/chrome_notification_types.h" | |
14 #include "chrome/common/pref_names.h" | |
15 #include "components/pref_registry/pref_registry_syncable.h" | |
16 #include "components/proxy_config/proxy_config_dictionary.h" | |
17 #include "content/public/browser/browser_thread.h" | |
18 #include "content/public/browser/notification_details.h" | |
19 #include "content/public/browser/notification_source.h" | |
20 #include "net/proxy/proxy_list.h" | |
21 #include "net/proxy/proxy_server.h" | |
22 | |
23 using content::BrowserThread; | |
24 | |
25 namespace { | |
26 | |
27 // Determine if |proxy| is of the form "*.googlezip.net". | |
28 bool IsGooglezipDataReductionProxy(const net::ProxyServer& proxy) { | |
29 return proxy.is_valid() && !proxy.is_direct() && | |
30 base::EndsWith(proxy.host_port_pair().host(), ".googlezip.net", | |
31 base::CompareCase::SENSITIVE); | |
32 } | |
33 | |
34 // Removes any Data Reduction Proxies like *.googlezip.net from |proxy_list|. | |
35 // Returns the number of proxies that were removed from |proxy_list|. | |
36 size_t RemoveGooglezipDataReductionProxiesFromList(net::ProxyList* proxy_list) { | |
37 bool found_googlezip_proxy = false; | |
38 for (const net::ProxyServer& proxy : proxy_list->GetAll()) { | |
39 if (IsGooglezipDataReductionProxy(proxy)) { | |
40 found_googlezip_proxy = true; | |
41 break; | |
42 } | |
43 } | |
44 if (!found_googlezip_proxy) | |
45 return 0; | |
46 | |
47 size_t num_removed_proxies = 0; | |
48 net::ProxyList replacement_list; | |
49 for (const net::ProxyServer& proxy : proxy_list->GetAll()) { | |
50 if (!IsGooglezipDataReductionProxy(proxy)) | |
51 replacement_list.AddProxyServer(proxy); | |
52 else | |
53 ++num_removed_proxies; | |
54 } | |
55 | |
56 if (replacement_list.IsEmpty()) | |
57 replacement_list.AddProxyServer(net::ProxyServer::Direct()); | |
58 *proxy_list = replacement_list; | |
59 return num_removed_proxies; | |
60 } | |
61 | |
62 // Remove any Data Reduction Proxies like *.googlezip.net from |proxy_rules|. | |
63 // This is to prevent a Data Reduction Proxy from being activated in an | |
64 // unsupported way, such as from a proxy pref, which could cause Chrome to use | |
65 // the Data Reduction Proxy without adding any of the necessary authentication | |
66 // headers or applying the Data Reduction Proxy bypass logic. See | |
67 // http://crbug.com/476610. | |
68 // TODO(sclittle): This method should be removed once the UMA indicates that | |
69 // *.googlezip.net proxies are no longer present in the |proxy_rules|. | |
70 void RemoveGooglezipDataReductionProxies( | |
71 net::ProxyConfig::ProxyRules* proxy_rules) { | |
72 size_t num_removed_proxies = | |
73 RemoveGooglezipDataReductionProxiesFromList( | |
74 &proxy_rules->fallback_proxies) + | |
75 RemoveGooglezipDataReductionProxiesFromList( | |
76 &proxy_rules->proxies_for_ftp) + | |
77 RemoveGooglezipDataReductionProxiesFromList( | |
78 &proxy_rules->proxies_for_http) + | |
79 RemoveGooglezipDataReductionProxiesFromList( | |
80 &proxy_rules->proxies_for_https) + | |
81 RemoveGooglezipDataReductionProxiesFromList(&proxy_rules->single_proxies); | |
82 | |
83 UMA_HISTOGRAM_COUNTS_100("Net.PrefProxyConfig.GooglezipProxyRemovalCount", | |
84 num_removed_proxies); | |
85 } | |
86 | |
87 } // namespace | |
88 | |
89 //============================= ChromeProxyConfigService ======================= | |
90 | |
91 ChromeProxyConfigService::ChromeProxyConfigService( | |
92 net::ProxyConfigService* base_service) | |
93 : base_service_(base_service), | |
94 pref_config_state_(ProxyPrefs::CONFIG_UNSET), | |
95 pref_config_read_pending_(true), | |
96 registered_observer_(false) { | |
97 } | |
98 | |
99 ChromeProxyConfigService::~ChromeProxyConfigService() { | |
100 if (registered_observer_ && base_service_.get()) | |
101 base_service_->RemoveObserver(this); | |
102 } | |
103 | |
104 void ChromeProxyConfigService::AddObserver( | |
105 net::ProxyConfigService::Observer* observer) { | |
106 RegisterObserver(); | |
107 observers_.AddObserver(observer); | |
108 } | |
109 | |
110 void ChromeProxyConfigService::RemoveObserver( | |
111 net::ProxyConfigService::Observer* observer) { | |
112 observers_.RemoveObserver(observer); | |
113 } | |
114 | |
115 net::ProxyConfigService::ConfigAvailability | |
116 ChromeProxyConfigService::GetLatestProxyConfig(net::ProxyConfig* config) { | |
117 RegisterObserver(); | |
118 | |
119 if (pref_config_read_pending_) | |
120 return net::ProxyConfigService::CONFIG_PENDING; | |
121 | |
122 // Ask the base service if available. | |
123 net::ProxyConfig system_config; | |
124 ConfigAvailability system_availability = | |
125 net::ProxyConfigService::CONFIG_UNSET; | |
126 if (base_service_.get()) | |
127 system_availability = base_service_->GetLatestProxyConfig(&system_config); | |
128 | |
129 ProxyPrefs::ConfigState config_state; | |
130 return PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig( | |
131 pref_config_state_, pref_config_, | |
132 system_availability, system_config, false, | |
133 &config_state, config); | |
134 } | |
135 | |
136 void ChromeProxyConfigService::OnLazyPoll() { | |
137 if (base_service_.get()) | |
138 base_service_->OnLazyPoll(); | |
139 } | |
140 | |
141 void ChromeProxyConfigService::UpdateProxyConfig( | |
142 ProxyPrefs::ConfigState config_state, | |
143 const net::ProxyConfig& config) { | |
144 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
145 | |
146 pref_config_read_pending_ = false; | |
147 pref_config_state_ = config_state; | |
148 pref_config_ = config; | |
149 | |
150 if (!observers_.might_have_observers()) | |
151 return; | |
152 | |
153 // Evaluate the proxy configuration. If GetLatestProxyConfig returns | |
154 // CONFIG_PENDING, we are using the system proxy service, but it doesn't have | |
155 // a valid configuration yet. Once it is ready, OnProxyConfigChanged() will be | |
156 // called and broadcast the proxy configuration. | |
157 // Note: If a switch between a preference proxy configuration and the system | |
158 // proxy configuration occurs an unnecessary notification might get send if | |
159 // the two configurations agree. This case should be rare however, so we don't | |
160 // handle that case specially. | |
161 net::ProxyConfig new_config; | |
162 ConfigAvailability availability = GetLatestProxyConfig(&new_config); | |
163 if (availability != CONFIG_PENDING) { | |
164 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_, | |
165 OnProxyConfigChanged(new_config, availability)); | |
166 } | |
167 } | |
168 | |
169 void ChromeProxyConfigService::OnProxyConfigChanged( | |
170 const net::ProxyConfig& config, | |
171 ConfigAvailability availability) { | |
172 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
173 | |
174 // Check whether there is a proxy configuration defined by preferences. In | |
175 // this case that proxy configuration takes precedence and the change event | |
176 // from the delegate proxy service can be disregarded. | |
177 if (!PrefProxyConfigTrackerImpl::PrefPrecedes(pref_config_state_)) { | |
178 net::ProxyConfig actual_config; | |
179 availability = GetLatestProxyConfig(&actual_config); | |
180 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_, | |
181 OnProxyConfigChanged(actual_config, availability)); | |
182 } | |
183 } | |
184 | |
185 void ChromeProxyConfigService::RegisterObserver() { | |
186 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
187 if (!registered_observer_ && base_service_.get()) { | |
188 base_service_->AddObserver(this); | |
189 registered_observer_ = true; | |
190 } | |
191 } | |
192 | |
193 //========================= PrefProxyConfigTrackerImpl ========================= | |
194 | |
195 PrefProxyConfigTrackerImpl::PrefProxyConfigTrackerImpl( | |
196 PrefService* pref_service) | |
197 : pref_service_(pref_service), | |
198 chrome_proxy_config_service_(NULL), | |
199 update_pending_(true) { | |
200 config_state_ = ReadPrefConfig(pref_service_, &pref_config_); | |
201 proxy_prefs_.Init(pref_service); | |
202 proxy_prefs_.Add(prefs::kProxy, | |
203 base::Bind(&PrefProxyConfigTrackerImpl::OnProxyPrefChanged, | |
204 base::Unretained(this))); | |
205 } | |
206 | |
207 PrefProxyConfigTrackerImpl::~PrefProxyConfigTrackerImpl() { | |
208 DCHECK(pref_service_ == NULL); | |
209 } | |
210 | |
211 scoped_ptr<net::ProxyConfigService> | |
212 PrefProxyConfigTrackerImpl::CreateTrackingProxyConfigService( | |
213 scoped_ptr<net::ProxyConfigService> base_service) { | |
214 chrome_proxy_config_service_ = | |
215 new ChromeProxyConfigService(base_service.release()); | |
216 VLOG(1) << this << ": set chrome proxy config service to " | |
217 << chrome_proxy_config_service_; | |
218 if (chrome_proxy_config_service_ && update_pending_) | |
219 OnProxyConfigChanged(config_state_, pref_config_); | |
220 | |
221 return scoped_ptr<net::ProxyConfigService>(chrome_proxy_config_service_); | |
222 } | |
223 | |
224 void PrefProxyConfigTrackerImpl::DetachFromPrefService() { | |
225 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
226 // Stop notifications. | |
227 proxy_prefs_.RemoveAll(); | |
228 pref_service_ = NULL; | |
229 chrome_proxy_config_service_ = NULL; | |
230 } | |
231 | |
232 // static | |
233 bool PrefProxyConfigTrackerImpl::PrefPrecedes( | |
234 ProxyPrefs::ConfigState config_state) { | |
235 return config_state == ProxyPrefs::CONFIG_POLICY || | |
236 config_state == ProxyPrefs::CONFIG_EXTENSION || | |
237 config_state == ProxyPrefs::CONFIG_OTHER_PRECEDE; | |
238 } | |
239 | |
240 // static | |
241 net::ProxyConfigService::ConfigAvailability | |
242 PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig( | |
243 ProxyPrefs::ConfigState pref_state, | |
244 const net::ProxyConfig& pref_config, | |
245 net::ProxyConfigService::ConfigAvailability system_availability, | |
246 const net::ProxyConfig& system_config, | |
247 bool ignore_fallback_config, | |
248 ProxyPrefs::ConfigState* effective_config_state, | |
249 net::ProxyConfig* effective_config) { | |
250 net::ProxyConfigService::ConfigAvailability rv; | |
251 *effective_config_state = pref_state; | |
252 | |
253 if (PrefPrecedes(pref_state)) { | |
254 *effective_config = pref_config; | |
255 rv = net::ProxyConfigService::CONFIG_VALID; | |
256 } else if (system_availability == net::ProxyConfigService::CONFIG_UNSET) { | |
257 // If there's no system proxy config, fall back to prefs or default. | |
258 if (pref_state == ProxyPrefs::CONFIG_FALLBACK && !ignore_fallback_config) | |
259 *effective_config = pref_config; | |
260 else | |
261 *effective_config = net::ProxyConfig::CreateDirect(); | |
262 rv = net::ProxyConfigService::CONFIG_VALID; | |
263 } else { | |
264 *effective_config_state = ProxyPrefs::CONFIG_SYSTEM; | |
265 *effective_config = system_config; | |
266 rv = system_availability; | |
267 } | |
268 | |
269 // Remove any Data Reduction Proxies like *.googlezip.net from the proxy | |
270 // config rules, since specifying a DRP in the proxy rules is not a supported | |
271 // means of activating the DRP, and could cause requests to be sent to the DRP | |
272 // without the appropriate authentication headers and without using any of the | |
273 // DRP bypass logic. This prevents the Data Reduction Proxy from being | |
274 // improperly activated via the proxy pref. | |
275 // TODO(sclittle): This is a temporary fix for http://crbug.com/476610, and | |
276 // should be removed once that bug is fixed and verified. | |
277 if (rv == net::ProxyConfigService::CONFIG_VALID) | |
278 RemoveGooglezipDataReductionProxies(&effective_config->proxy_rules()); | |
279 | |
280 return rv; | |
281 } | |
282 | |
283 // static | |
284 void PrefProxyConfigTrackerImpl::RegisterPrefs(PrefRegistrySimple* registry) { | |
285 base::DictionaryValue* default_settings = | |
286 ProxyConfigDictionary::CreateSystem(); | |
287 registry->RegisterDictionaryPref(prefs::kProxy, default_settings); | |
288 } | |
289 | |
290 // static | |
291 void PrefProxyConfigTrackerImpl::RegisterProfilePrefs( | |
292 user_prefs::PrefRegistrySyncable* pref_service) { | |
293 base::DictionaryValue* default_settings = | |
294 ProxyConfigDictionary::CreateSystem(); | |
295 pref_service->RegisterDictionaryPref(prefs::kProxy, default_settings); | |
296 } | |
297 | |
298 // static | |
299 ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::ReadPrefConfig( | |
300 const PrefService* pref_service, | |
301 net::ProxyConfig* config) { | |
302 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
303 | |
304 // Clear the configuration and source. | |
305 *config = net::ProxyConfig(); | |
306 ProxyPrefs::ConfigState config_state = ProxyPrefs::CONFIG_UNSET; | |
307 | |
308 const PrefService::Preference* pref = | |
309 pref_service->FindPreference(prefs::kProxy); | |
310 DCHECK(pref); | |
311 | |
312 const base::DictionaryValue* dict = | |
313 pref_service->GetDictionary(prefs::kProxy); | |
314 DCHECK(dict); | |
315 ProxyConfigDictionary proxy_dict(dict); | |
316 | |
317 if (PrefConfigToNetConfig(proxy_dict, config)) { | |
318 if (!pref->IsUserModifiable() || pref->HasUserSetting()) { | |
319 if (pref->IsManaged()) | |
320 config_state = ProxyPrefs::CONFIG_POLICY; | |
321 else if (pref->IsExtensionControlled()) | |
322 config_state = ProxyPrefs::CONFIG_EXTENSION; | |
323 else | |
324 config_state = ProxyPrefs::CONFIG_OTHER_PRECEDE; | |
325 } else { | |
326 config_state = ProxyPrefs::CONFIG_FALLBACK; | |
327 } | |
328 } | |
329 | |
330 return config_state; | |
331 } | |
332 | |
333 ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::GetProxyConfig( | |
334 net::ProxyConfig* config) { | |
335 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
336 if (config_state_ != ProxyPrefs::CONFIG_UNSET) | |
337 *config = pref_config_; | |
338 return config_state_; | |
339 } | |
340 | |
341 void PrefProxyConfigTrackerImpl::OnProxyConfigChanged( | |
342 ProxyPrefs::ConfigState config_state, | |
343 const net::ProxyConfig& config) { | |
344 if (!chrome_proxy_config_service_) { | |
345 VLOG(1) << "No chrome proxy config service to push to UpdateProxyConfig"; | |
346 update_pending_ = true; | |
347 return; | |
348 } | |
349 update_pending_ = !BrowserThread::PostTask( | |
350 BrowserThread::IO, FROM_HERE, | |
351 base::Bind(&ChromeProxyConfigService::UpdateProxyConfig, | |
352 base::Unretained(chrome_proxy_config_service_), | |
353 config_state, config)); | |
354 VLOG(1) << this << (update_pending_ ? ": Error" : ": Done") | |
355 << " pushing proxy to UpdateProxyConfig"; | |
356 } | |
357 | |
358 bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig( | |
359 const ProxyConfigDictionary& proxy_dict, | |
360 net::ProxyConfig* config) { | |
361 ProxyPrefs::ProxyMode mode; | |
362 if (!proxy_dict.GetMode(&mode)) { | |
363 // Fall back to system settings if the mode preference is invalid. | |
364 return false; | |
365 } | |
366 | |
367 switch (mode) { | |
368 case ProxyPrefs::MODE_SYSTEM: | |
369 // Use system settings. | |
370 return false; | |
371 case ProxyPrefs::MODE_DIRECT: | |
372 // Ignore all the other proxy config preferences if the use of a proxy | |
373 // has been explicitly disabled. | |
374 return true; | |
375 case ProxyPrefs::MODE_AUTO_DETECT: | |
376 config->set_auto_detect(true); | |
377 return true; | |
378 case ProxyPrefs::MODE_PAC_SCRIPT: { | |
379 std::string proxy_pac; | |
380 if (!proxy_dict.GetPacUrl(&proxy_pac)) { | |
381 LOG(ERROR) << "Proxy settings request PAC script but do not specify " | |
382 << "its URL. Falling back to direct connection."; | |
383 return true; | |
384 } | |
385 GURL proxy_pac_url(proxy_pac); | |
386 if (!proxy_pac_url.is_valid()) { | |
387 LOG(ERROR) << "Invalid proxy PAC url: " << proxy_pac; | |
388 return true; | |
389 } | |
390 config->set_pac_url(proxy_pac_url); | |
391 bool pac_mandatory = false; | |
392 proxy_dict.GetPacMandatory(&pac_mandatory); | |
393 config->set_pac_mandatory(pac_mandatory); | |
394 return true; | |
395 } | |
396 case ProxyPrefs::MODE_FIXED_SERVERS: { | |
397 std::string proxy_server; | |
398 if (!proxy_dict.GetProxyServer(&proxy_server)) { | |
399 LOG(ERROR) << "Proxy settings request fixed proxy servers but do not " | |
400 << "specify their URLs. Falling back to direct connection."; | |
401 return true; | |
402 } | |
403 config->proxy_rules().ParseFromString(proxy_server); | |
404 | |
405 std::string proxy_bypass; | |
406 if (proxy_dict.GetBypassList(&proxy_bypass)) { | |
407 config->proxy_rules().bypass_rules.ParseFromString(proxy_bypass); | |
408 } | |
409 return true; | |
410 } | |
411 case ProxyPrefs::kModeCount: { | |
412 // Fall through to NOTREACHED(). | |
413 } | |
414 } | |
415 NOTREACHED() << "Unknown proxy mode, falling back to system settings."; | |
416 return false; | |
417 } | |
418 | |
419 void PrefProxyConfigTrackerImpl::OnProxyPrefChanged() { | |
420 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
421 net::ProxyConfig new_config; | |
422 ProxyPrefs::ConfigState config_state = ReadPrefConfig(pref_service_, | |
423 &new_config); | |
424 if (config_state_ != config_state || | |
425 (config_state_ != ProxyPrefs::CONFIG_UNSET && | |
426 !pref_config_.Equals(new_config))) { | |
427 config_state_ = config_state; | |
428 if (config_state_ != ProxyPrefs::CONFIG_UNSET) | |
429 pref_config_ = new_config; | |
430 update_pending_ = true; | |
431 } | |
432 if (update_pending_) | |
433 OnProxyConfigChanged(config_state, new_config); | |
434 } | |
OLD | NEW |