| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/proxy/proxy_config_service_win.h" |
| |
| #include <windows.h> |
| #include <winhttp.h> |
| |
| #include "base/logging.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/profiler/scoped_profile.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/string_tokenizer.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/win/registry.h" |
| #include "net/base/net_errors.h" |
| #include "net/proxy/proxy_config.h" |
| |
| #pragma comment(lib, "winhttp.lib") |
| |
| namespace net { |
| |
| namespace { |
| |
| const int kPollIntervalSec = 10; |
| |
| void FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_config) { |
| if (ie_config->lpszAutoConfigUrl) |
| GlobalFree(ie_config->lpszAutoConfigUrl); |
| if (ie_config->lpszProxy) |
| GlobalFree(ie_config->lpszProxy); |
| if (ie_config->lpszProxyBypass) |
| GlobalFree(ie_config->lpszProxyBypass); |
| } |
| |
| } // namespace |
| |
| // RegKey and ObjectWatcher pair. |
| class ProxyConfigServiceWin::KeyEntry { |
| public: |
| bool StartWatching(base::win::ObjectWatcher::Delegate* delegate) { |
| // Try to create a watch event for the registry key (which watches the |
| // sibling tree as well). |
| if (key_.StartWatching() != ERROR_SUCCESS) |
| return false; |
| |
| // Now setup an ObjectWatcher for this event, so we get OnObjectSignaled() |
| // invoked on this message loop once it is signalled. |
| if (!watcher_.StartWatching(key_.watch_event(), delegate)) |
| return false; |
| |
| return true; |
| } |
| |
| bool CreateRegKey(HKEY rootkey, const wchar_t* subkey) { |
| return key_.Create(rootkey, subkey, KEY_NOTIFY) == ERROR_SUCCESS; |
| } |
| |
| HANDLE watch_event() const { |
| return key_.watch_event(); |
| } |
| |
| private: |
| base::win::RegKey key_; |
| base::win::ObjectWatcher watcher_; |
| }; |
| |
| ProxyConfigServiceWin::ProxyConfigServiceWin() |
| : PollingProxyConfigService( |
| base::TimeDelta::FromSeconds(kPollIntervalSec), |
| &ProxyConfigServiceWin::GetCurrentProxyConfig) { |
| } |
| |
| ProxyConfigServiceWin::~ProxyConfigServiceWin() { |
| // The registry functions below will end up going to disk. Do this on another |
| // thread to avoid slowing the IO thread. http://crbug.com/61453 |
| base::ThreadRestrictions::ScopedAllowIO allow_io; |
| STLDeleteElements(&keys_to_watch_); |
| } |
| |
| void ProxyConfigServiceWin::AddObserver(Observer* observer) { |
| // Lazily-initialize our registry watcher. |
| StartWatchingRegistryForChanges(); |
| |
| // Let the super-class do its work now. |
| PollingProxyConfigService::AddObserver(observer); |
| } |
| |
| void ProxyConfigServiceWin::StartWatchingRegistryForChanges() { |
| if (!keys_to_watch_.empty()) |
| return; // Already initialized. |
| |
| // The registry functions below will end up going to disk. Do this on another |
| // thread to avoid slowing the IO thread. http://crbug.com/61453 |
| base::ThreadRestrictions::ScopedAllowIO allow_io; |
| |
| // There are a number of different places where proxy settings can live |
| // in the registry. In some cases it appears in a binary value, in other |
| // cases string values. Furthermore winhttp and wininet appear to have |
| // separate stores, and proxy settings can be configured per-machine |
| // or per-user. |
| // |
| // This function is probably not exhaustive in the registry locations it |
| // watches for changes, however it should catch the majority of the |
| // cases. In case we have missed some less common triggers (likely), we |
| // will catch them during the periodic (10 second) polling, so things |
| // will recover. |
| |
| AddKeyToWatchList( |
| HKEY_CURRENT_USER, |
| L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"); |
| |
| AddKeyToWatchList( |
| HKEY_LOCAL_MACHINE, |
| L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"); |
| |
| AddKeyToWatchList( |
| HKEY_LOCAL_MACHINE, |
| L"SOFTWARE\\Policies\\Microsoft\\Windows\\CurrentVersion\\" |
| L"Internet Settings"); |
| } |
| |
| bool ProxyConfigServiceWin::AddKeyToWatchList(HKEY rootkey, |
| const wchar_t* subkey) { |
| scoped_ptr<KeyEntry> entry(new KeyEntry); |
| if (!entry->CreateRegKey(rootkey, subkey)) |
| return false; |
| |
| if (!entry->StartWatching(this)) |
| return false; |
| |
| keys_to_watch_.push_back(entry.release()); |
| return true; |
| } |
| |
| void ProxyConfigServiceWin::OnObjectSignaled(HANDLE object) { |
| // TODO(vadimt): Remove ScopedProfile below once crbug.com/418183 is fixed. |
| tracked_objects::ScopedProfile tracking_profile( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "ProxyConfigServiceWin_OnObjectSignaled")); |
| |
| // Figure out which registry key signalled this change. |
| KeyEntryList::iterator it; |
| for (it = keys_to_watch_.begin(); it != keys_to_watch_.end(); ++it) { |
| if ((*it)->watch_event() == object) |
| break; |
| } |
| |
| DCHECK(it != keys_to_watch_.end()); |
| |
| // Keep watching the registry key. |
| if (!(*it)->StartWatching(this)) |
| keys_to_watch_.erase(it); |
| |
| // Have the PollingProxyConfigService test for changes. |
| CheckForChangesNow(); |
| } |
| |
| // static |
| void ProxyConfigServiceWin::GetCurrentProxyConfig(ProxyConfig* config) { |
| WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_config = {0}; |
| if (!WinHttpGetIEProxyConfigForCurrentUser(&ie_config)) { |
| LOG(ERROR) << "WinHttpGetIEProxyConfigForCurrentUser failed: " << |
| GetLastError(); |
| *config = ProxyConfig::CreateDirect(); |
| config->set_source(PROXY_CONFIG_SOURCE_SYSTEM_FAILED); |
| return; |
| } |
| SetFromIEConfig(config, ie_config); |
| FreeIEConfig(&ie_config); |
| } |
| |
| // static |
| void ProxyConfigServiceWin::SetFromIEConfig( |
| ProxyConfig* config, |
| const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG& ie_config) { |
| if (ie_config.fAutoDetect) |
| config->set_auto_detect(true); |
| if (ie_config.lpszProxy) { |
| // lpszProxy may be a single proxy, or a proxy per scheme. The format |
| // is compatible with ProxyConfig::ProxyRules's string format. |
| config->proxy_rules().ParseFromString( |
| base::UTF16ToASCII(ie_config.lpszProxy)); |
| } |
| if (ie_config.lpszProxyBypass) { |
| std::string proxy_bypass = base::UTF16ToASCII(ie_config.lpszProxyBypass); |
| |
| base::StringTokenizer proxy_server_bypass_list(proxy_bypass, ";, \t\n\r"); |
| while (proxy_server_bypass_list.GetNext()) { |
| std::string bypass_url_domain = proxy_server_bypass_list.token(); |
| config->proxy_rules().bypass_rules.AddRuleFromString(bypass_url_domain); |
| } |
| } |
| if (ie_config.lpszAutoConfigUrl) |
| config->set_pac_url(GURL(ie_config.lpszAutoConfigUrl)); |
| config->set_source(PROXY_CONFIG_SOURCE_SYSTEM); |
| } |
| |
| } // namespace net |