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/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/profiler/scoped_profile.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/string_tokenizer.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/thread_restrictions.h"
20 #include "base/win/registry.h"
21 #include "base/win/scoped_handle.h"
22 #include "net/base/net_errors.h"
23 #include "net/proxy/proxy_config.h"
25 #pragma comment(lib, "winhttp.lib")
31 const int kPollIntervalSec
= 10;
33 void FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
* ie_config
) {
34 if (ie_config
->lpszAutoConfigUrl
)
35 GlobalFree(ie_config
->lpszAutoConfigUrl
);
36 if (ie_config
->lpszProxy
)
37 GlobalFree(ie_config
->lpszProxy
);
38 if (ie_config
->lpszProxyBypass
)
39 GlobalFree(ie_config
->lpszProxyBypass
);
44 ProxyConfigServiceWin::ProxyConfigServiceWin()
45 : PollingProxyConfigService(
46 base::TimeDelta::FromSeconds(kPollIntervalSec
),
47 &ProxyConfigServiceWin::GetCurrentProxyConfig
) {
50 ProxyConfigServiceWin::~ProxyConfigServiceWin() {
51 // The registry functions below will end up going to disk. Do this on another
52 // thread to avoid slowing the IO thread. http://crbug.com/61453
53 base::ThreadRestrictions::ScopedAllowIO allow_io
;
54 STLDeleteElements(&keys_to_watch_
);
57 void ProxyConfigServiceWin::AddObserver(Observer
* observer
) {
58 // Lazily-initialize our registry watcher.
59 StartWatchingRegistryForChanges();
61 // Let the super-class do its work now.
62 PollingProxyConfigService::AddObserver(observer
);
65 void ProxyConfigServiceWin::StartWatchingRegistryForChanges() {
66 if (!keys_to_watch_
.empty())
67 return; // Already initialized.
69 // The registry functions below will end up going to disk. Do this on another
70 // thread to avoid slowing the IO thread. http://crbug.com/61453
71 base::ThreadRestrictions::ScopedAllowIO allow_io
;
73 // There are a number of different places where proxy settings can live
74 // in the registry. In some cases it appears in a binary value, in other
75 // cases string values. Furthermore winhttp and wininet appear to have
76 // separate stores, and proxy settings can be configured per-machine
79 // This function is probably not exhaustive in the registry locations it
80 // watches for changes, however it should catch the majority of the
81 // cases. In case we have missed some less common triggers (likely), we
82 // will catch them during the periodic (10 second) polling, so things
87 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
91 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
95 L
"SOFTWARE\\Policies\\Microsoft\\Windows\\CurrentVersion\\"
96 L
"Internet Settings");
99 bool ProxyConfigServiceWin::AddKeyToWatchList(HKEY rootkey
,
100 const wchar_t* subkey
) {
101 scoped_ptr
<base::win::RegKey
> key(new base::win::RegKey
);
102 if (key
->Create(rootkey
, subkey
, KEY_NOTIFY
) != ERROR_SUCCESS
)
105 if (!key
->StartWatching(base::Bind(&ProxyConfigServiceWin::OnObjectSignaled
,
106 base::Unretained(this),
107 base::Unretained(key
.get())))) {
111 keys_to_watch_
.push_back(key
.release());
115 void ProxyConfigServiceWin::OnObjectSignaled(base::win::RegKey
* key
) {
116 // TODO(vadimt): Remove ScopedProfile below once crbug.com/418183 is fixed.
117 tracked_objects::ScopedProfile
tracking_profile(
118 FROM_HERE_WITH_EXPLICIT_FUNCTION(
119 "ProxyConfigServiceWin_OnObjectSignaled"));
121 // Figure out which registry key signalled this change.
122 RegKeyList::iterator it
=
123 std::find(keys_to_watch_
.begin(), keys_to_watch_
.end(), key
);
124 DCHECK(it
!= keys_to_watch_
.end());
126 // Keep watching the registry key.
127 if (!key
->StartWatching(base::Bind(&ProxyConfigServiceWin::OnObjectSignaled
,
128 base::Unretained(this),
129 base::Unretained(key
)))) {
131 keys_to_watch_
.erase(it
);
134 // Have the PollingProxyConfigService test for changes.
135 CheckForChangesNow();
139 void ProxyConfigServiceWin::GetCurrentProxyConfig(ProxyConfig
* config
) {
140 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_config
= {0};
141 if (!WinHttpGetIEProxyConfigForCurrentUser(&ie_config
)) {
142 LOG(ERROR
) << "WinHttpGetIEProxyConfigForCurrentUser failed: " <<
144 *config
= ProxyConfig::CreateDirect();
145 config
->set_source(PROXY_CONFIG_SOURCE_SYSTEM_FAILED
);
148 SetFromIEConfig(config
, ie_config
);
149 FreeIEConfig(&ie_config
);
153 void ProxyConfigServiceWin::SetFromIEConfig(
155 const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
& ie_config
) {
156 if (ie_config
.fAutoDetect
)
157 config
->set_auto_detect(true);
158 if (ie_config
.lpszProxy
) {
159 // lpszProxy may be a single proxy, or a proxy per scheme. The format
160 // is compatible with ProxyConfig::ProxyRules's string format.
161 config
->proxy_rules().ParseFromString(
162 base::UTF16ToASCII(ie_config
.lpszProxy
));
164 if (ie_config
.lpszProxyBypass
) {
165 std::string proxy_bypass
= base::UTF16ToASCII(ie_config
.lpszProxyBypass
);
167 base::StringTokenizer
proxy_server_bypass_list(proxy_bypass
, ";, \t\n\r");
168 while (proxy_server_bypass_list
.GetNext()) {
169 std::string bypass_url_domain
= proxy_server_bypass_list
.token();
170 config
->proxy_rules().bypass_rules
.AddRuleFromString(bypass_url_domain
);
173 if (ie_config
.lpszAutoConfigUrl
)
174 config
->set_pac_url(GURL(ie_config
.lpszAutoConfigUrl
));
175 config
->set_source(PROXY_CONFIG_SOURCE_SYSTEM
);