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