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.
5 #include "net/proxy/proxy_config_service_win.h"
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"
21 #pragma comment(lib, "winhttp.lib")
27 const int kPollIntervalSec
= 10;
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
);
40 // RegKey and ObjectWatcher pair.
41 class ProxyConfigServiceWin::KeyEntry
{
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
)
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
))
57 bool CreateRegKey(HKEY rootkey
, const wchar_t* subkey
) {
58 return key_
.Create(rootkey
, subkey
, KEY_NOTIFY
) == ERROR_SUCCESS
;
61 HANDLE
watch_event() const {
62 return key_
.watch_event();
66 base::win::RegKey key_
;
67 base::win::ObjectWatcher watcher_
;
70 ProxyConfigServiceWin::ProxyConfigServiceWin()
71 : PollingProxyConfigService(
72 base::TimeDelta::FromSeconds(kPollIntervalSec
),
73 &ProxyConfigServiceWin::GetCurrentProxyConfig
) {
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_
);
83 void ProxyConfigServiceWin::AddObserver(Observer
* observer
) {
84 // Lazily-initialize our registry watcher.
85 StartWatchingRegistryForChanges();
87 // Let the super-class do its work now.
88 PollingProxyConfigService::AddObserver(observer
);
91 void ProxyConfigServiceWin::StartWatchingRegistryForChanges() {
92 if (!keys_to_watch_
.empty())
93 return; // Already initialized.
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
;
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
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
113 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
117 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
121 L
"SOFTWARE\\Policies\\Microsoft\\Windows\\CurrentVersion\\"
122 L
"Internet Settings");
125 bool ProxyConfigServiceWin::AddKeyToWatchList(HKEY rootkey
,
126 const wchar_t* subkey
) {
127 scoped_ptr
<KeyEntry
> entry(new KeyEntry
);
128 if (!entry
->CreateRegKey(rootkey
, subkey
))
131 if (!entry
->StartWatching(this))
134 keys_to_watch_
.push_back(entry
.release());
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
)
146 DCHECK(it
!= keys_to_watch_
.end());
148 // Keep watching the registry key.
149 if (!(*it
)->StartWatching(this))
150 keys_to_watch_
.erase(it
);
152 // Have the PollingProxyConfigService test for changes.
153 CheckForChangesNow();
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: " <<
162 *config
= ProxyConfig::CreateDirect();
163 config
->set_source(PROXY_CONFIG_SOURCE_SYSTEM_FAILED
);
166 SetFromIEConfig(config
, ie_config
);
167 FreeIEConfig(&ie_config
);
171 void ProxyConfigServiceWin::SetFromIEConfig(
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
));
182 if (ie_config
.lpszProxyBypass
) {
183 std::string proxy_bypass
= base::UTF16ToASCII(ie_config
.lpszProxyBypass
);
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
);
191 if (ie_config
.lpszAutoConfigUrl
)
192 config
->set_pac_url(GURL(ie_config
.lpszAutoConfigUrl
));
193 config
->set_source(PROXY_CONFIG_SOURCE_SYSTEM
);