1 // Copyright (c) 2011 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/string_tokenizer.h"
13 #include "base/string_util.h"
14 #include "base/stl_util-inl.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "base/win/registry.h"
17 #include "net/base/net_errors.h"
18 #include "net/proxy/proxy_config.h"
19
20 #pragma comment(lib, "winhttp.lib")
21
22 namespace net {
23
24 namespace {
25
26 const int kPollIntervalSec = 10;
27
FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * ie_config)28 void FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_config) {
29 if (ie_config->lpszAutoConfigUrl)
30 GlobalFree(ie_config->lpszAutoConfigUrl);
31 if (ie_config->lpszProxy)
32 GlobalFree(ie_config->lpszProxy);
33 if (ie_config->lpszProxyBypass)
34 GlobalFree(ie_config->lpszProxyBypass);
35 }
36
37 } // namespace
38
39 // RegKey and ObjectWatcher pair.
40 class ProxyConfigServiceWin::KeyEntry {
41 public:
StartWatching(base::win::ObjectWatcher::Delegate * delegate)42 bool StartWatching(base::win::ObjectWatcher::Delegate* delegate) {
43 // Try to create a watch event for the registry key (which watches the
44 // sibling tree as well).
45 if (key_.StartWatching() != ERROR_SUCCESS)
46 return false;
47
48 // Now setup an ObjectWatcher for this event, so we get OnObjectSignaled()
49 // invoked on this message loop once it is signalled.
50 if (!watcher_.StartWatching(key_.watch_event(), delegate))
51 return false;
52
53 return true;
54 }
55
CreateRegKey(HKEY rootkey,const wchar_t * subkey)56 bool CreateRegKey(HKEY rootkey, const wchar_t* subkey) {
57 return key_.Create(rootkey, subkey, KEY_NOTIFY) == ERROR_SUCCESS;
58 }
59
watch_event() const60 HANDLE watch_event() const {
61 return key_.watch_event();
62 }
63
64 private:
65 base::win::RegKey key_;
66 base::win::ObjectWatcher watcher_;
67 };
68
ProxyConfigServiceWin()69 ProxyConfigServiceWin::ProxyConfigServiceWin()
70 : PollingProxyConfigService(
71 base::TimeDelta::FromSeconds(kPollIntervalSec),
72 &ProxyConfigServiceWin::GetCurrentProxyConfig) {
73 }
74
~ProxyConfigServiceWin()75 ProxyConfigServiceWin::~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
AddObserver(Observer * observer)82 void 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
StartWatchingRegistryForChanges()90 void 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
AddKeyToWatchList(HKEY rootkey,const wchar_t * subkey)124 bool 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
OnObjectSignaled(HANDLE object)137 void 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
GetCurrentProxyConfig(ProxyConfig * config)156 void 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 return;
163 }
164 SetFromIEConfig(config, ie_config);
165 FreeIEConfig(&ie_config);
166 }
167
168 // static
SetFromIEConfig(ProxyConfig * config,const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG & ie_config)169 void ProxyConfigServiceWin::SetFromIEConfig(
170 ProxyConfig* config,
171 const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG& ie_config) {
172 if (ie_config.fAutoDetect)
173 config->set_auto_detect(true);
174 if (ie_config.lpszProxy) {
175 // lpszProxy may be a single proxy, or a proxy per scheme. The format
176 // is compatible with ProxyConfig::ProxyRules's string format.
177 config->proxy_rules().ParseFromString(WideToASCII(ie_config.lpszProxy));
178 }
179 if (ie_config.lpszProxyBypass) {
180 std::string proxy_bypass = WideToASCII(ie_config.lpszProxyBypass);
181
182 StringTokenizer proxy_server_bypass_list(proxy_bypass, ";, \t\n\r");
183 while (proxy_server_bypass_list.GetNext()) {
184 std::string bypass_url_domain = proxy_server_bypass_list.token();
185 config->proxy_rules().bypass_rules.AddRuleFromString(bypass_url_domain);
186 }
187 }
188 if (ie_config.lpszAutoConfigUrl)
189 config->set_pac_url(GURL(ie_config.lpszAutoConfigUrl));
190 }
191
192 } // namespace net
193