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