13447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// Use of this source code is governed by a BSD-style license that can be 3324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// found in the LICENSE file. 43447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein 53447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein#include "net/proxy/proxy_config_service_win.h" 63447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein 73447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein#include <windows.h> 83447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein#include <winhttp.h> 93447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein 10324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include "base/logging.h" 113447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein#include "base/memory/scoped_ptr.h" 12324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include "base/stl_util.h" 13324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include "base/strings/string_util.h" 143447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein#include "base/strings/string_tokenizer.h" 15324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include "base/threading/thread_restrictions.h" 163447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein#include "base/win/registry.h" 173447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein#include "net/base/net_errors.h" 183447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein#include "net/proxy/proxy_config.h" 193447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein 203447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein#pragma comment(lib, "winhttp.lib") 213447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein 223447a5916aa62f44de24cc441fc9987116ddff52Andrew Sappersteinnamespace net { 233447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein 243447a5916aa62f44de24cc441fc9987116ddff52Andrew Sappersteinnamespace { 253447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein 263447a5916aa62f44de24cc441fc9987116ddff52Andrew Sappersteinconst int kPollIntervalSec = 10; 27324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver 283447a5916aa62f44de24cc441fc9987116ddff52Andrew Sappersteinvoid FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_config) { 29324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver if (ie_config->lpszAutoConfigUrl) 303447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein GlobalFree(ie_config->lpszAutoConfigUrl); 313447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein if (ie_config->lpszProxy) 32324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver GlobalFree(ie_config->lpszProxy); 33324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver if (ie_config->lpszProxyBypass) 343447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein GlobalFree(ie_config->lpszProxyBypass); 35324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver} 36324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver 37324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver} // namespace 38324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver 39324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// RegKey and ObjectWatcher pair. 40324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverclass ProxyConfigServiceWin::KeyEntry { 41324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver public: 42324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver bool StartWatching(base::win::ObjectWatcher::Delegate* delegate) { 43324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver // Try to create a watch event for the registry key (which watches the 44324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver // sibling tree as well). 45324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver if (key_.StartWatching() != ERROR_SUCCESS) 46324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver return false; 47324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver 48324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver // Now setup an ObjectWatcher for this event, so we get OnObjectSignaled() 49324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver // invoked on this message loop once it is signalled. 50324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver if (!watcher_.StartWatching(key_.watch_event(), delegate)) 51324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver return false; 52324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver 53324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver return true; 543447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein } 55324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver 56324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver bool CreateRegKey(HKEY rootkey, const wchar_t* subkey) { 57324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver return key_.Create(rootkey, subkey, KEY_NOTIFY) == ERROR_SUCCESS; 583447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein } 59324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver 60324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver HANDLE watch_event() const { 61324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver return key_.watch_event(); 623447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein } 633447a5916aa62f44de24cc441fc9987116ddff52Andrew Sapperstein 64 private: 65 base::win::RegKey key_; 66 base::win::ObjectWatcher watcher_; 67}; 68 69ProxyConfigServiceWin::ProxyConfigServiceWin() 70 : PollingProxyConfigService( 71 base::TimeDelta::FromSeconds(kPollIntervalSec), 72 &ProxyConfigServiceWin::GetCurrentProxyConfig) { 73} 74 75ProxyConfigServiceWin::~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 82void 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 90void 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 124bool 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 137void 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 156void 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 config->set_source(PROXY_CONFIG_SOURCE_SYSTEM_FAILED); 163 return; 164 } 165 SetFromIEConfig(config, ie_config); 166 FreeIEConfig(&ie_config); 167} 168 169// static 170void ProxyConfigServiceWin::SetFromIEConfig( 171 ProxyConfig* config, 172 const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG& ie_config) { 173 if (ie_config.fAutoDetect) 174 config->set_auto_detect(true); 175 if (ie_config.lpszProxy) { 176 // lpszProxy may be a single proxy, or a proxy per scheme. The format 177 // is compatible with ProxyConfig::ProxyRules's string format. 178 config->proxy_rules().ParseFromString(WideToASCII(ie_config.lpszProxy)); 179 } 180 if (ie_config.lpszProxyBypass) { 181 std::string proxy_bypass = WideToASCII(ie_config.lpszProxyBypass); 182 183 base::StringTokenizer proxy_server_bypass_list(proxy_bypass, ";, \t\n\r"); 184 while (proxy_server_bypass_list.GetNext()) { 185 std::string bypass_url_domain = proxy_server_bypass_list.token(); 186 config->proxy_rules().bypass_rules.AddRuleFromString(bypass_url_domain); 187 } 188 } 189 if (ie_config.lpszAutoConfigUrl) 190 config->set_pac_url(GURL(ie_config.lpszAutoConfigUrl)); 191 config->set_source(PROXY_CONFIG_SOURCE_SYSTEM); 192} 193 194} // namespace net 195