Home | History | Annotate | Download | only in proxy
      1 // Copyright (c) 2012 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 "net/proxy/proxy_config_service_win.h"
      6 
      7 #include <windows.h>
      8 #include <winhttp.h>
      9 
     10 #include "base/logging.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/stl_util.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/string_tokenizer.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "base/threading/thread_restrictions.h"
     17 #include "base/win/registry.h"
     18 #include "net/base/net_errors.h"
     19 #include "net/proxy/proxy_config.h"
     20 
     21 #pragma comment(lib, "winhttp.lib")
     22 
     23 namespace net {
     24 
     25 namespace {
     26 
     27 const int kPollIntervalSec = 10;
     28 
     29 void FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_config) {
     30   if (ie_config->lpszAutoConfigUrl)
     31     GlobalFree(ie_config->lpszAutoConfigUrl);
     32   if (ie_config->lpszProxy)
     33     GlobalFree(ie_config->lpszProxy);
     34   if (ie_config->lpszProxyBypass)
     35     GlobalFree(ie_config->lpszProxyBypass);
     36 }
     37 
     38 }  // namespace
     39 
     40 // RegKey and ObjectWatcher pair.
     41 class ProxyConfigServiceWin::KeyEntry {
     42  public:
     43   bool StartWatching(base::win::ObjectWatcher::Delegate* delegate) {
     44     // Try to create a watch event for the registry key (which watches the
     45     // sibling tree as well).
     46     if (key_.StartWatching() != ERROR_SUCCESS)
     47       return false;
     48 
     49     // Now setup an ObjectWatcher for this event, so we get OnObjectSignaled()
     50     // invoked on this message loop once it is signalled.
     51     if (!watcher_.StartWatching(key_.watch_event(), delegate))
     52       return false;
     53 
     54     return true;
     55   }
     56 
     57   bool CreateRegKey(HKEY rootkey, const wchar_t* subkey) {
     58     return key_.Create(rootkey, subkey, KEY_NOTIFY) == ERROR_SUCCESS;
     59   }
     60 
     61   HANDLE watch_event() const {
     62     return key_.watch_event();
     63   }
     64 
     65  private:
     66   base::win::RegKey key_;
     67   base::win::ObjectWatcher watcher_;
     68 };
     69 
     70 ProxyConfigServiceWin::ProxyConfigServiceWin()
     71     : PollingProxyConfigService(
     72           base::TimeDelta::FromSeconds(kPollIntervalSec),
     73           &ProxyConfigServiceWin::GetCurrentProxyConfig) {
     74 }
     75 
     76 ProxyConfigServiceWin::~ProxyConfigServiceWin() {
     77   // The registry functions below will end up going to disk.  Do this on another
     78   // thread to avoid slowing the IO thread.  http://crbug.com/61453
     79   base::ThreadRestrictions::ScopedAllowIO allow_io;
     80   STLDeleteElements(&keys_to_watch_);
     81 }
     82 
     83 void ProxyConfigServiceWin::AddObserver(Observer* observer) {
     84   // Lazily-initialize our registry watcher.
     85   StartWatchingRegistryForChanges();
     86 
     87   // Let the super-class do its work now.
     88   PollingProxyConfigService::AddObserver(observer);
     89 }
     90 
     91 void ProxyConfigServiceWin::StartWatchingRegistryForChanges() {
     92   if (!keys_to_watch_.empty())
     93     return;  // Already initialized.
     94 
     95   // The registry functions below will end up going to disk.  Do this on another
     96   // thread to avoid slowing the IO thread.  http://crbug.com/61453
     97   base::ThreadRestrictions::ScopedAllowIO allow_io;
     98 
     99   // There are a number of different places where proxy settings can live
    100   // in the registry. In some cases it appears in a binary value, in other
    101   // cases string values. Furthermore winhttp and wininet appear to have
    102   // separate stores, and proxy settings can be configured per-machine
    103   // or per-user.
    104   //
    105   // This function is probably not exhaustive in the registry locations it
    106   // watches for changes, however it should catch the majority of the
    107   // cases. In case we have missed some less common triggers (likely), we
    108   // will catch them during the periodic (10 second) polling, so things
    109   // will recover.
    110 
    111   AddKeyToWatchList(
    112       HKEY_CURRENT_USER,
    113       L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
    114 
    115   AddKeyToWatchList(
    116       HKEY_LOCAL_MACHINE,
    117       L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
    118 
    119   AddKeyToWatchList(
    120       HKEY_LOCAL_MACHINE,
    121       L"SOFTWARE\\Policies\\Microsoft\\Windows\\CurrentVersion\\"
    122       L"Internet Settings");
    123 }
    124 
    125 bool ProxyConfigServiceWin::AddKeyToWatchList(HKEY rootkey,
    126                                               const wchar_t* subkey) {
    127   scoped_ptr<KeyEntry> entry(new KeyEntry);
    128   if (!entry->CreateRegKey(rootkey, subkey))
    129     return false;
    130 
    131   if (!entry->StartWatching(this))
    132     return false;
    133 
    134   keys_to_watch_.push_back(entry.release());
    135   return true;
    136 }
    137 
    138 void ProxyConfigServiceWin::OnObjectSignaled(HANDLE object) {
    139   // Figure out which registry key signalled this change.
    140   KeyEntryList::iterator it;
    141   for (it = keys_to_watch_.begin(); it != keys_to_watch_.end(); ++it) {
    142     if ((*it)->watch_event() == object)
    143       break;
    144   }
    145 
    146   DCHECK(it != keys_to_watch_.end());
    147 
    148   // Keep watching the registry key.
    149   if (!(*it)->StartWatching(this))
    150     keys_to_watch_.erase(it);
    151 
    152   // Have the PollingProxyConfigService test for changes.
    153   CheckForChangesNow();
    154 }
    155 
    156 // static
    157 void ProxyConfigServiceWin::GetCurrentProxyConfig(ProxyConfig* config) {
    158   WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_config = {0};
    159   if (!WinHttpGetIEProxyConfigForCurrentUser(&ie_config)) {
    160     LOG(ERROR) << "WinHttpGetIEProxyConfigForCurrentUser failed: " <<
    161         GetLastError();
    162     *config = ProxyConfig::CreateDirect();
    163     config->set_source(PROXY_CONFIG_SOURCE_SYSTEM_FAILED);
    164     return;
    165   }
    166   SetFromIEConfig(config, ie_config);
    167   FreeIEConfig(&ie_config);
    168 }
    169 
    170 // static
    171 void ProxyConfigServiceWin::SetFromIEConfig(
    172     ProxyConfig* config,
    173     const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG& ie_config) {
    174   if (ie_config.fAutoDetect)
    175     config->set_auto_detect(true);
    176   if (ie_config.lpszProxy) {
    177     // lpszProxy may be a single proxy, or a proxy per scheme. The format
    178     // is compatible with ProxyConfig::ProxyRules's string format.
    179     config->proxy_rules().ParseFromString(
    180         base::UTF16ToASCII(ie_config.lpszProxy));
    181   }
    182   if (ie_config.lpszProxyBypass) {
    183     std::string proxy_bypass = base::UTF16ToASCII(ie_config.lpszProxyBypass);
    184 
    185     base::StringTokenizer proxy_server_bypass_list(proxy_bypass, ";, \t\n\r");
    186     while (proxy_server_bypass_list.GetNext()) {
    187       std::string bypass_url_domain = proxy_server_bypass_list.token();
    188       config->proxy_rules().bypass_rules.AddRuleFromString(bypass_url_domain);
    189     }
    190   }
    191   if (ie_config.lpszAutoConfigUrl)
    192     config->set_pac_url(GURL(ie_config.lpszAutoConfigUrl));
    193   config->set_source(PROXY_CONFIG_SOURCE_SYSTEM);
    194 }
    195 
    196 }  // namespace net
    197