• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * ie_config)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:
StartWatching(base::win::ObjectWatcher::Delegate * delegate)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 
CreateRegKey(HKEY rootkey,const wchar_t * subkey)57   bool CreateRegKey(HKEY rootkey, const wchar_t* subkey) {
58     return key_.Create(rootkey, subkey, KEY_NOTIFY) == ERROR_SUCCESS;
59   }
60 
watch_event() const61   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 
ProxyConfigServiceWin()70 ProxyConfigServiceWin::ProxyConfigServiceWin()
71     : PollingProxyConfigService(
72           base::TimeDelta::FromSeconds(kPollIntervalSec),
73           &ProxyConfigServiceWin::GetCurrentProxyConfig) {
74 }
75 
~ProxyConfigServiceWin()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 
AddObserver(Observer * observer)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 
StartWatchingRegistryForChanges()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 
AddKeyToWatchList(HKEY rootkey,const wchar_t * subkey)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 
OnObjectSignaled(HANDLE object)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
GetCurrentProxyConfig(ProxyConfig * config)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
SetFromIEConfig(ProxyConfig * config,const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG & ie_config)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