• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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