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 38 using base::StringPrintf; 39 40 namespace { 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. 44 enum 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. 51 const char kUMAProxyStartupStateHistogram[] = 52 "DataReductionProxy.StartupState"; 53 54 // Key of the UMA DataReductionProxy.ProbeURL histogram. 55 const char kUMAProxyProbeURL[] = "DataReductionProxy.ProbeURL"; 56 57 // Key of the UMA DataReductionProxy.ProbeURLNetError histogram. 58 const char kUMAProxyProbeURLNetError[] = "DataReductionProxy.ProbeURLNetError"; 59 60 // Record a network change event. 61 void RecordNetworkChangeEvent(DataReductionProxyNetworkChangeEvent event) { 62 UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.NetworkChangeEvents", 63 event, 64 CHANGE_EVENT_COUNT); 65 } 66 67 int64 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 79 bool IsEnabledOnCommandLine() { 80 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 81 return command_line.HasSwitch( 82 data_reduction_proxy::switches::kEnableDataReductionProxy); 83 } 84 85 } // namespace 86 87 namespace data_reduction_proxy { 88 89 DataReductionProxySettings::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 102 DataReductionProxySettings::~DataReductionProxySettings() { 103 if (params_->allowed()) 104 spdy_proxy_auth_enabled_.Destroy(); 105 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); 106 } 107 108 void 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 123 void 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 145 void 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 154 void DataReductionProxySettings::SetDataReductionProxyStatisticsPrefs( 155 DataReductionProxyStatisticsPrefs* statistics_prefs) { 156 statistics_prefs_ = statistics_prefs; 157 } 158 159 void 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 165 void DataReductionProxySettings::SetProxyConfigurator( 166 DataReductionProxyConfigurator* configurator) { 167 DCHECK(configurator); 168 configurator_ = configurator; 169 } 170 171 bool DataReductionProxySettings::IsDataReductionProxyEnabled() { 172 return spdy_proxy_auth_enabled_.GetValue() || IsEnabledOnCommandLine(); 173 } 174 175 bool 176 DataReductionProxySettings::IsDataReductionProxyAlternativeEnabled() const { 177 return data_reduction_proxy_alternative_enabled_.GetValue(); 178 } 179 180 bool DataReductionProxySettings::IsDataReductionProxyManaged() { 181 return spdy_proxy_auth_enabled_.IsManaged(); 182 } 183 184 void 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 196 void 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 208 int64 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 217 DataReductionProxySettings::ContentLengthList 218 DataReductionProxySettings::GetDailyOriginalContentLengths() { 219 DCHECK(thread_checker_.CalledOnValidThread()); 220 return GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength); 221 } 222 223 void DataReductionProxySettings::SetUnreachable(bool unreachable) { 224 unreachable_ = unreachable; 225 } 226 227 bool DataReductionProxySettings::IsDataReductionProxyUnreachable() { 228 DCHECK(thread_checker_.CalledOnValidThread()); 229 return unreachable_; 230 } 231 232 DataReductionProxySettings::ContentLengthList 233 DataReductionProxySettings::GetDailyReceivedContentLengths() { 234 DCHECK(thread_checker_.CalledOnValidThread()); 235 return GetDailyContentLengths(prefs::kDailyHttpReceivedContentLength); 236 } 237 238 void 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 297 PrefService* DataReductionProxySettings::GetOriginalProfilePrefs() { 298 DCHECK(thread_checker_.CalledOnValidThread()); 299 return prefs_; 300 } 301 302 void 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 317 void 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 335 void 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 350 void 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 359 void DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange() { 360 DCHECK(thread_checker_.CalledOnValidThread()); 361 if (!params_->alternative_allowed()) 362 return; 363 MaybeActivateDataReductionProxy(false); 364 } 365 366 void 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 381 void 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 407 void 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 437 void 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 450 void DataReductionProxySettings::RecordProbeURLFetchResult( 451 ProbeURLFetchResult result) { 452 UMA_HISTOGRAM_ENUMERATION(kUMAProxyProbeURL, 453 result, 454 PROBE_URL_FETCH_RESULT_COUNT); 455 } 456 457 void DataReductionProxySettings::RecordStartupState(ProxyStartupState state) { 458 UMA_HISTOGRAM_ENUMERATION(kUMAProxyStartupStateHistogram, 459 state, 460 PROXY_STARTUP_STATE_COUNT); 461 } 462 463 void DataReductionProxySettings::GetNetworkList( 464 net::NetworkInterfaceList* interfaces, 465 int policy) { 466 net::GetNetworkList(interfaces, policy); 467 } 468 469 void DataReductionProxySettings::ResetParamsForTest( 470 DataReductionProxyParams* params) { 471 params_.reset(params); 472 } 473 474 DataReductionProxySettings::ContentLengthList 475 DataReductionProxySettings::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 488 void 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 524 net::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 542 net::URLFetcher* 543 DataReductionProxySettings::GetURLFetcherForAvailabilityCheck() { 544 return GetBaseURLFetcher(params_->probe_url(), 545 net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY); 546 } 547 548 549 void DataReductionProxySettings::ProbeWhetherDataReductionProxyIsAvailable() { 550 net::URLFetcher* fetcher = GetURLFetcherForAvailabilityCheck(); 551 if (!fetcher) 552 return; 553 fetcher_.reset(fetcher); 554 fetcher_->Start(); 555 } 556 557 bool 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