Home | History | Annotate | Download | only in browser
      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