1// Copyright 2014 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 "components/data_reduction_proxy/browser/data_reduction_proxy_settings.h" 6 7#include "base/bind.h" 8#include "base/command_line.h" 9#include "base/metrics/field_trial.h" 10#include "base/metrics/histogram.h" 11#include "base/metrics/sparse_histogram.h" 12#include "base/prefs/pref_member.h" 13#include "base/prefs/pref_service.h" 14#include "base/prefs/scoped_user_pref_update.h" 15#include "base/strings/string_number_conversions.h" 16#include "base/strings/string_util.h" 17#include "base/strings/stringprintf.h" 18#include "base/strings/utf_string_conversions.h" 19#include "components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h" 20#include "components/data_reduction_proxy/browser/data_reduction_proxy_configurator.h" 21#include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h" 22#include "components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h" 23#include "components/data_reduction_proxy/common/data_reduction_proxy_pref_names.h" 24#include "components/data_reduction_proxy/common/data_reduction_proxy_switches.h" 25#include "net/base/host_port_pair.h" 26#include "net/base/load_flags.h" 27#include "net/base/net_errors.h" 28#include "net/base/net_util.h" 29#include "net/http/http_network_session.h" 30#include "net/http/http_response_headers.h" 31#include "net/url_request/url_fetcher.h" 32#include "net/url_request/url_fetcher_delegate.h" 33#include "net/url_request/url_request_context_getter.h" 34#include "net/url_request/url_request_status.h" 35#include "url/gurl.h" 36 37 38using base::StringPrintf; 39 40namespace { 41// Values of the UMA DataReductionProxy.NetworkChangeEvents histograms. 42// This enum must remain synchronized with the enum of the same 43// name in metrics/histograms/histograms.xml. 44enum DataReductionProxyNetworkChangeEvent { 45 IP_CHANGED = 0, // The client IP address changed. 46 DISABLED_ON_VPN = 1, // The proxy is disabled because a VPN is running. 47 CHANGE_EVENT_COUNT = 2 // This must always be last. 48}; 49 50// Key of the UMA DataReductionProxy.StartupState histogram. 51const char kUMAProxyStartupStateHistogram[] = 52 "DataReductionProxy.StartupState"; 53 54// Key of the UMA DataReductionProxy.ProbeURL histogram. 55const char kUMAProxyProbeURL[] = "DataReductionProxy.ProbeURL"; 56 57// Key of the UMA DataReductionProxy.ProbeURLNetError histogram. 58const char kUMAProxyProbeURLNetError[] = "DataReductionProxy.ProbeURLNetError"; 59 60// Record a network change event. 61void RecordNetworkChangeEvent(DataReductionProxyNetworkChangeEvent event) { 62 UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.NetworkChangeEvents", 63 event, 64 CHANGE_EVENT_COUNT); 65} 66 67int64 GetInt64PrefValue(const base::ListValue& list_value, size_t index) { 68 int64 val = 0; 69 std::string pref_value; 70 bool rv = list_value.GetString(index, &pref_value); 71 DCHECK(rv); 72 if (rv) { 73 rv = base::StringToInt64(pref_value, &val); 74 DCHECK(rv); 75 } 76 return val; 77} 78 79bool IsEnabledOnCommandLine() { 80 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 81 return command_line.HasSwitch( 82 data_reduction_proxy::switches::kEnableDataReductionProxy); 83} 84 85} // namespace 86 87namespace data_reduction_proxy { 88 89DataReductionProxySettings::DataReductionProxySettings( 90 DataReductionProxyParams* params) 91 : restricted_by_carrier_(false), 92 enabled_by_user_(false), 93 disabled_on_vpn_(false), 94 unreachable_(false), 95 prefs_(NULL), 96 url_request_context_getter_(NULL), 97 configurator_(NULL) { 98 DCHECK(params); 99 params_.reset(params); 100} 101 102DataReductionProxySettings::~DataReductionProxySettings() { 103 if (params_->allowed()) 104 spdy_proxy_auth_enabled_.Destroy(); 105 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); 106} 107 108void DataReductionProxySettings::InitPrefMembers() { 109 DCHECK(thread_checker_.CalledOnValidThread()); 110 spdy_proxy_auth_enabled_.Init( 111 prefs::kDataReductionProxyEnabled, 112 GetOriginalProfilePrefs(), 113 base::Bind(&DataReductionProxySettings::OnProxyEnabledPrefChange, 114 base::Unretained(this))); 115 data_reduction_proxy_alternative_enabled_.Init( 116 prefs::kDataReductionProxyAltEnabled, 117 GetOriginalProfilePrefs(), 118 base::Bind( 119 &DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange, 120 base::Unretained(this))); 121} 122 123void DataReductionProxySettings::InitDataReductionProxySettings( 124 PrefService* prefs, 125 net::URLRequestContextGetter* url_request_context_getter) { 126 DCHECK(thread_checker_.CalledOnValidThread()); 127 DCHECK(prefs); 128 DCHECK(url_request_context_getter); 129 prefs_ = prefs; 130 url_request_context_getter_ = url_request_context_getter; 131 InitPrefMembers(); 132 RecordDataReductionInit(); 133 134 // Disable the proxy if it is not allowed to be used. 135 if (!params_->allowed()) 136 return; 137 138 AddDefaultProxyBypassRules(); 139 net::NetworkChangeNotifier::AddIPAddressObserver(this); 140 141 // We set or reset the proxy pref at startup. 142 MaybeActivateDataReductionProxy(true); 143} 144 145void DataReductionProxySettings::InitDataReductionProxySettings( 146 PrefService* prefs, 147 net::URLRequestContextGetter* url_request_context_getter, 148 DataReductionProxyConfigurator* configurator) { 149 InitDataReductionProxySettings(prefs, 150 url_request_context_getter); 151 SetProxyConfigurator(configurator); 152} 153 154void DataReductionProxySettings::SetDataReductionProxyStatisticsPrefs( 155 DataReductionProxyStatisticsPrefs* statistics_prefs) { 156 statistics_prefs_ = statistics_prefs; 157} 158 159void DataReductionProxySettings::SetOnDataReductionEnabledCallback( 160 const base::Callback<void(bool)>& on_data_reduction_proxy_enabled) { 161 on_data_reduction_proxy_enabled_ = on_data_reduction_proxy_enabled; 162 on_data_reduction_proxy_enabled_.Run(IsDataReductionProxyEnabled()); 163} 164 165void DataReductionProxySettings::SetProxyConfigurator( 166 DataReductionProxyConfigurator* configurator) { 167 DCHECK(configurator); 168 configurator_ = configurator; 169} 170 171bool DataReductionProxySettings::IsDataReductionProxyEnabled() { 172 return spdy_proxy_auth_enabled_.GetValue() || IsEnabledOnCommandLine(); 173} 174 175bool 176DataReductionProxySettings::IsDataReductionProxyAlternativeEnabled() const { 177 return data_reduction_proxy_alternative_enabled_.GetValue(); 178} 179 180bool DataReductionProxySettings::IsDataReductionProxyManaged() { 181 return spdy_proxy_auth_enabled_.IsManaged(); 182} 183 184void DataReductionProxySettings::SetDataReductionProxyEnabled(bool enabled) { 185 DCHECK(thread_checker_.CalledOnValidThread()); 186 // Prevent configuring the proxy when it is not allowed to be used. 187 if (!params_->allowed()) 188 return; 189 190 if (spdy_proxy_auth_enabled_.GetValue() != enabled) { 191 spdy_proxy_auth_enabled_.SetValue(enabled); 192 OnProxyEnabledPrefChange(); 193 } 194} 195 196void DataReductionProxySettings::SetDataReductionProxyAlternativeEnabled( 197 bool enabled) { 198 DCHECK(thread_checker_.CalledOnValidThread()); 199 // Prevent configuring the proxy when it is not allowed to be used. 200 if (!params_->alternative_allowed()) 201 return; 202 if (data_reduction_proxy_alternative_enabled_.GetValue() != enabled) { 203 data_reduction_proxy_alternative_enabled_.SetValue(enabled); 204 OnProxyAlternativeEnabledPrefChange(); 205 } 206} 207 208int64 DataReductionProxySettings::GetDataReductionLastUpdateTime() { 209 DCHECK(thread_checker_.CalledOnValidThread()); 210 DCHECK(statistics_prefs_); 211 int64 last_update_internal = 212 statistics_prefs_->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate); 213 base::Time last_update = base::Time::FromInternalValue(last_update_internal); 214 return static_cast<int64>(last_update.ToJsTime()); 215} 216 217DataReductionProxySettings::ContentLengthList 218DataReductionProxySettings::GetDailyOriginalContentLengths() { 219 DCHECK(thread_checker_.CalledOnValidThread()); 220 return GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength); 221} 222 223void DataReductionProxySettings::SetUnreachable(bool unreachable) { 224 unreachable_ = unreachable; 225} 226 227bool DataReductionProxySettings::IsDataReductionProxyUnreachable() { 228 DCHECK(thread_checker_.CalledOnValidThread()); 229 return unreachable_; 230} 231 232DataReductionProxySettings::ContentLengthList 233DataReductionProxySettings::GetDailyReceivedContentLengths() { 234 DCHECK(thread_checker_.CalledOnValidThread()); 235 return GetDailyContentLengths(prefs::kDailyHttpReceivedContentLength); 236} 237 238void DataReductionProxySettings::OnURLFetchComplete( 239 const net::URLFetcher* source) { 240 DCHECK(thread_checker_.CalledOnValidThread()); 241 242 DCHECK(source == fetcher_.get()); 243 net::URLRequestStatus status = source->GetStatus(); 244 if (status.status() == net::URLRequestStatus::FAILED) { 245 if (status.error() == net::ERR_INTERNET_DISCONNECTED) { 246 RecordProbeURLFetchResult(INTERNET_DISCONNECTED); 247 return; 248 } 249 // TODO(bengr): Remove once we understand the reasons probes are failing. 250 // Probe errors are either due to fetcher-level errors or modified 251 // responses. This only tracks the former. 252 UMA_HISTOGRAM_SPARSE_SLOWLY( 253 kUMAProxyProbeURLNetError, std::abs(status.error())); 254 } 255 256 std::string response; 257 source->GetResponseAsString(&response); 258 259 if ("OK" == response.substr(0, 2)) { 260 DVLOG(1) << "The data reduction proxy is unrestricted."; 261 262 if (enabled_by_user_) { 263 if (restricted_by_carrier_) { 264 // The user enabled the proxy, but sometime previously in the session, 265 // the network operator had blocked the canary and restricted the user. 266 // The current network doesn't block the canary, so don't restrict the 267 // proxy configurations. 268 SetProxyConfigs(true /* enabled */, 269 IsDataReductionProxyAlternativeEnabled(), 270 false /* restricted */, 271 false /* at_startup */); 272 RecordProbeURLFetchResult(SUCCEEDED_PROXY_ENABLED); 273 } else { 274 RecordProbeURLFetchResult(SUCCEEDED_PROXY_ALREADY_ENABLED); 275 } 276 } 277 restricted_by_carrier_ = false; 278 return; 279 } 280 DVLOG(1) << "The data reduction proxy is restricted to the configured " 281 << "fallback proxy."; 282 if (enabled_by_user_) { 283 if (!restricted_by_carrier_) { 284 // Restrict the proxy. 285 SetProxyConfigs(true /* enabled */, 286 IsDataReductionProxyAlternativeEnabled(), 287 true /* restricted */, 288 false /* at_startup */); 289 RecordProbeURLFetchResult(FAILED_PROXY_DISABLED); 290 } else { 291 RecordProbeURLFetchResult(FAILED_PROXY_ALREADY_DISABLED); 292 } 293 } 294 restricted_by_carrier_ = true; 295} 296 297PrefService* DataReductionProxySettings::GetOriginalProfilePrefs() { 298 DCHECK(thread_checker_.CalledOnValidThread()); 299 return prefs_; 300} 301 302void DataReductionProxySettings::AddDefaultProxyBypassRules() { 303 // localhost 304 DCHECK(configurator_); 305 configurator_->AddHostPatternToBypass("<local>"); 306 // RFC1918 private addresses. 307 configurator_->AddHostPatternToBypass("10.0.0.0/8"); 308 configurator_->AddHostPatternToBypass("172.16.0.0/12"); 309 configurator_->AddHostPatternToBypass("192.168.0.0/16"); 310 // RFC4193 private addresses. 311 configurator_->AddHostPatternToBypass("fc00::/7"); 312 // IPV6 probe addresses. 313 configurator_->AddHostPatternToBypass("*-ds.metric.gstatic.com"); 314 configurator_->AddHostPatternToBypass("*-v4.metric.gstatic.com"); 315} 316 317void DataReductionProxySettings::LogProxyState( 318 bool enabled, bool restricted, bool at_startup) { 319 // This must stay a LOG(WARNING); the output is used in processing customer 320 // feedback. 321 const char kAtStartup[] = "at startup"; 322 const char kByUser[] = "by user action"; 323 const char kOn[] = "ON"; 324 const char kOff[] = "OFF"; 325 const char kRestricted[] = "(Restricted)"; 326 const char kUnrestricted[] = "(Unrestricted)"; 327 328 std::string annotated_on = 329 kOn + std::string(" ") + (restricted ? kRestricted : kUnrestricted); 330 331 LOG(WARNING) << "SPDY proxy " << (enabled ? annotated_on : kOff) 332 << " " << (at_startup ? kAtStartup : kByUser); 333} 334 335void DataReductionProxySettings::OnIPAddressChanged() { 336 DCHECK(thread_checker_.CalledOnValidThread()); 337 if (enabled_by_user_) { 338 DCHECK(params_->allowed()); 339 RecordNetworkChangeEvent(IP_CHANGED); 340 if (DisableIfVPN()) 341 return; 342 if (IsDataReductionProxyAlternativeEnabled() && 343 !params_->alternative_fallback_allowed()) { 344 return; 345 } 346 ProbeWhetherDataReductionProxyIsAvailable(); 347 } 348} 349 350void DataReductionProxySettings::OnProxyEnabledPrefChange() { 351 DCHECK(thread_checker_.CalledOnValidThread()); 352 if (!on_data_reduction_proxy_enabled_.is_null()) 353 on_data_reduction_proxy_enabled_.Run(IsDataReductionProxyEnabled()); 354 if (!params_->allowed()) 355 return; 356 MaybeActivateDataReductionProxy(false); 357} 358 359void DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange() { 360 DCHECK(thread_checker_.CalledOnValidThread()); 361 if (!params_->alternative_allowed()) 362 return; 363 MaybeActivateDataReductionProxy(false); 364} 365 366void DataReductionProxySettings::ResetDataReductionStatistics() { 367 DCHECK(thread_checker_.CalledOnValidThread()); 368 DCHECK(statistics_prefs_); 369 base::ListValue* original_update = 370 statistics_prefs_->GetList(prefs::kDailyHttpOriginalContentLength); 371 base::ListValue* received_update = 372 statistics_prefs_->GetList(prefs::kDailyHttpReceivedContentLength); 373 original_update->Clear(); 374 received_update->Clear(); 375 for (size_t i = 0; i < kNumDaysInHistory; ++i) { 376 original_update->AppendString(base::Int64ToString(0)); 377 received_update->AppendString(base::Int64ToString(0)); 378 } 379} 380 381void DataReductionProxySettings::MaybeActivateDataReductionProxy( 382 bool at_startup) { 383 DCHECK(thread_checker_.CalledOnValidThread()); 384 PrefService* prefs = GetOriginalProfilePrefs(); 385 // TODO(marq): Consider moving this so stats are wiped the first time the 386 // proxy settings are actually (not maybe) turned on. 387 if (spdy_proxy_auth_enabled_.GetValue() && 388 !prefs->GetBoolean(prefs::kDataReductionProxyWasEnabledBefore)) { 389 prefs->SetBoolean(prefs::kDataReductionProxyWasEnabledBefore, true); 390 ResetDataReductionStatistics(); 391 } 392 // Configure use of the data reduction proxy if it is enabled. 393 enabled_by_user_= IsDataReductionProxyEnabled(); 394 SetProxyConfigs(enabled_by_user_ && !disabled_on_vpn_, 395 IsDataReductionProxyAlternativeEnabled(), 396 restricted_by_carrier_, 397 at_startup); 398 399 // Check if the proxy has been restricted explicitly by the carrier. 400 if (enabled_by_user_ && !disabled_on_vpn_ && 401 !(IsDataReductionProxyAlternativeEnabled() && 402 !params_->alternative_fallback_allowed())) { 403 ProbeWhetherDataReductionProxyIsAvailable(); 404 } 405} 406 407void DataReductionProxySettings::SetProxyConfigs(bool enabled, 408 bool alternative_enabled, 409 bool restricted, 410 bool at_startup) { 411 DCHECK(thread_checker_.CalledOnValidThread()); 412 DCHECK(configurator_); 413 414 LogProxyState(enabled, restricted, at_startup); 415 // The alternative is only configured if the standard configuration is 416 // is enabled. 417 if (enabled & !params_->holdback()) { 418 if (alternative_enabled) { 419 configurator_->Enable(restricted, 420 !params_->alternative_fallback_allowed(), 421 params_->alt_origin().spec(), 422 std::string(), 423 params_->ssl_origin().spec()); 424 } else { 425 configurator_->Enable(restricted, 426 !params_->fallback_allowed(), 427 params_->origin().spec(), 428 params_->fallback_origin().spec(), 429 std::string()); 430 } 431 } else { 432 configurator_->Disable(); 433 } 434} 435 436// Metrics methods 437void DataReductionProxySettings::RecordDataReductionInit() { 438 DCHECK(thread_checker_.CalledOnValidThread()); 439 ProxyStartupState state = PROXY_NOT_AVAILABLE; 440 if (params_->allowed()) { 441 if (IsDataReductionProxyEnabled()) 442 state = PROXY_ENABLED; 443 else 444 state = PROXY_DISABLED; 445 } 446 447 RecordStartupState(state); 448} 449 450void DataReductionProxySettings::RecordProbeURLFetchResult( 451 ProbeURLFetchResult result) { 452 UMA_HISTOGRAM_ENUMERATION(kUMAProxyProbeURL, 453 result, 454 PROBE_URL_FETCH_RESULT_COUNT); 455} 456 457void DataReductionProxySettings::RecordStartupState(ProxyStartupState state) { 458 UMA_HISTOGRAM_ENUMERATION(kUMAProxyStartupStateHistogram, 459 state, 460 PROXY_STARTUP_STATE_COUNT); 461} 462 463void DataReductionProxySettings::GetNetworkList( 464 net::NetworkInterfaceList* interfaces, 465 int policy) { 466 net::GetNetworkList(interfaces, policy); 467} 468 469void DataReductionProxySettings::ResetParamsForTest( 470 DataReductionProxyParams* params) { 471 params_.reset(params); 472} 473 474DataReductionProxySettings::ContentLengthList 475DataReductionProxySettings::GetDailyContentLengths(const char* pref_name) { 476 DCHECK(thread_checker_.CalledOnValidThread()); 477 DataReductionProxySettings::ContentLengthList content_lengths; 478 DCHECK(statistics_prefs_); 479 const base::ListValue* list_value = statistics_prefs_->GetList(pref_name); 480 if (list_value->GetSize() == kNumDaysInHistory) { 481 for (size_t i = 0; i < kNumDaysInHistory; ++i) { 482 content_lengths.push_back(GetInt64PrefValue(*list_value, i)); 483 } 484 } 485 return content_lengths; 486} 487 488void DataReductionProxySettings::GetContentLengths( 489 unsigned int days, 490 int64* original_content_length, 491 int64* received_content_length, 492 int64* last_update_time) { 493 DCHECK(thread_checker_.CalledOnValidThread()); 494 DCHECK_LE(days, kNumDaysInHistory); 495 DCHECK(statistics_prefs_); 496 497 const base::ListValue* original_list = 498 statistics_prefs_->GetList(prefs::kDailyHttpOriginalContentLength); 499 const base::ListValue* received_list = 500 statistics_prefs_->GetList(prefs::kDailyHttpReceivedContentLength); 501 502 if (original_list->GetSize() != kNumDaysInHistory || 503 received_list->GetSize() != kNumDaysInHistory) { 504 *original_content_length = 0L; 505 *received_content_length = 0L; 506 *last_update_time = 0L; 507 return; 508 } 509 510 int64 orig = 0L; 511 int64 recv = 0L; 512 // Include days from the end of the list going backwards. 513 for (size_t i = kNumDaysInHistory - days; 514 i < kNumDaysInHistory; ++i) { 515 orig += GetInt64PrefValue(*original_list, i); 516 recv += GetInt64PrefValue(*received_list, i); 517 } 518 *original_content_length = orig; 519 *received_content_length = recv; 520 *last_update_time = 521 statistics_prefs_->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate); 522} 523 524net::URLFetcher* DataReductionProxySettings::GetBaseURLFetcher( 525 const GURL& gurl, 526 int load_flags) { 527 528 net::URLFetcher* fetcher = net::URLFetcher::Create(gurl, 529 net::URLFetcher::GET, 530 this); 531 fetcher->SetLoadFlags(load_flags); 532 DCHECK(url_request_context_getter_); 533 fetcher->SetRequestContext(url_request_context_getter_); 534 // Configure max retries to be at most kMaxRetries times for 5xx errors. 535 static const int kMaxRetries = 5; 536 fetcher->SetMaxRetriesOn5xx(kMaxRetries); 537 fetcher->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries); 538 return fetcher; 539} 540 541 542net::URLFetcher* 543DataReductionProxySettings::GetURLFetcherForAvailabilityCheck() { 544 return GetBaseURLFetcher(params_->probe_url(), 545 net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY); 546} 547 548 549void DataReductionProxySettings::ProbeWhetherDataReductionProxyIsAvailable() { 550 net::URLFetcher* fetcher = GetURLFetcherForAvailabilityCheck(); 551 if (!fetcher) 552 return; 553 fetcher_.reset(fetcher); 554 fetcher_->Start(); 555} 556 557bool DataReductionProxySettings::DisableIfVPN() { 558 net::NetworkInterfaceList network_interfaces; 559 GetNetworkList(&network_interfaces, 0); 560 // VPNs use a "tun" interface, so the presence of a "tun" interface indicates 561 // a VPN is in use. 562 // TODO(kundaji): Verify this works on Windows. 563 const std::string vpn_interface_name_prefix = "tun"; 564 for (size_t i = 0; i < network_interfaces.size(); ++i) { 565 std::string interface_name = network_interfaces[i].name; 566 if (LowerCaseEqualsASCII( 567 interface_name.begin(), 568 interface_name.begin() + vpn_interface_name_prefix.size(), 569 vpn_interface_name_prefix.c_str())) { 570 SetProxyConfigs(false, 571 IsDataReductionProxyAlternativeEnabled(), 572 false, 573 false); 574 disabled_on_vpn_ = true; 575 RecordNetworkChangeEvent(DISABLED_ON_VPN); 576 return true; 577 } 578 } 579 if (disabled_on_vpn_) { 580 SetProxyConfigs(enabled_by_user_, 581 IsDataReductionProxyAlternativeEnabled(), 582 restricted_by_carrier_, 583 false); 584 } 585 disabled_on_vpn_ = false; 586 return false; 587} 588 589} // namespace data_reduction_proxy 590