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