• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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_resolution/win/proxy_config_service_win.h"
6 
7 #include <windows.h>
8 #include <winhttp.h>
9 
10 #include "base/functional/bind.h"
11 #include "base/functional/callback.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/logging.h"
14 #include "base/ranges/algorithm.h"
15 #include "base/strings/string_tokenizer.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "base/win/registry.h"
20 #include "base/win/scoped_handle.h"
21 #include "net/base/net_errors.h"
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 
ProxyConfigServiceWin(const NetworkTrafficAnnotationTag & traffic_annotation)40 ProxyConfigServiceWin::ProxyConfigServiceWin(
41     const NetworkTrafficAnnotationTag& traffic_annotation)
42     : PollingProxyConfigService(base::Seconds(kPollIntervalSec),
43                                 &ProxyConfigServiceWin::GetCurrentProxyConfig,
44                                 traffic_annotation) {
45   NetworkChangeNotifier::AddNetworkChangeObserver(this);
46 }
47 
~ProxyConfigServiceWin()48 ProxyConfigServiceWin::~ProxyConfigServiceWin() {
49   NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
50   // The registry functions below will end up going to disk.  TODO: Do this on
51   // another thread to avoid slowing the current thread.  http://crbug.com/61453
52   base::ScopedAllowBlocking scoped_allow_blocking;
53   keys_to_watch_.clear();
54 }
55 
AddObserver(Observer * observer)56 void ProxyConfigServiceWin::AddObserver(Observer* observer) {
57   // Lazily-initialize our registry watcher.
58   StartWatchingRegistryForChanges();
59 
60   // Let the super-class do its work now.
61   PollingProxyConfigService::AddObserver(observer);
62 }
63 
OnNetworkChanged(NetworkChangeNotifier::ConnectionType type)64 void ProxyConfigServiceWin::OnNetworkChanged(
65     NetworkChangeNotifier::ConnectionType type) {
66   // Proxy settings on Windows may change when the active connection changes.
67   // For instance, after connecting to a VPN, the proxy settings for the active
68   // connection will be that for the VPN. (And ProxyConfigService only reports
69   // proxy settings for the default connection).
70 
71   // This is conditioned on CONNECTION_NONE to avoid duplicating work, as
72   // NetworkChangeNotifier additionally sends it preceding completion.
73   // See https://crbug.com/1071901.
74   if (type == NetworkChangeNotifier::CONNECTION_NONE)
75     CheckForChangesNow();
76 }
77 
StartWatchingRegistryForChanges()78 void ProxyConfigServiceWin::StartWatchingRegistryForChanges() {
79   if (!keys_to_watch_.empty())
80     return;  // Already initialized.
81 
82   // The registry functions below will end up going to disk.  Do this on another
83   // thread to avoid slowing the current thread.  http://crbug.com/61453
84   base::ScopedAllowBlocking scoped_allow_blocking;
85 
86   // There are a number of different places where proxy settings can live
87   // in the registry. In some cases it appears in a binary value, in other
88   // cases string values. Furthermore winhttp and wininet appear to have
89   // separate stores, and proxy settings can be configured per-machine
90   // or per-user.
91   //
92   // This function is probably not exhaustive in the registry locations it
93   // watches for changes, however it should catch the majority of the
94   // cases. In case we have missed some less common triggers (likely), we
95   // will catch them during the periodic (10 second) polling, so things
96   // will recover.
97 
98   AddKeyToWatchList(
99       HKEY_CURRENT_USER,
100       L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
101 
102   AddKeyToWatchList(
103       HKEY_LOCAL_MACHINE,
104       L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
105 
106   AddKeyToWatchList(HKEY_LOCAL_MACHINE,
107                     L"SOFTWARE\\Policies\\Microsoft\\Windows\\CurrentVersion\\"
108                     L"Internet Settings");
109 }
110 
AddKeyToWatchList(HKEY rootkey,const wchar_t * subkey)111 bool ProxyConfigServiceWin::AddKeyToWatchList(HKEY rootkey,
112                                               const wchar_t* subkey) {
113   std::unique_ptr<base::win::RegKey> key =
114       std::make_unique<base::win::RegKey>();
115   if (key->Create(rootkey, subkey, KEY_NOTIFY) != ERROR_SUCCESS)
116     return false;
117 
118   if (!key->StartWatching(base::BindOnce(
119           &ProxyConfigServiceWin::OnObjectSignaled, base::Unretained(this),
120           base::Unretained(key.get())))) {
121     return false;
122   }
123 
124   keys_to_watch_.push_back(std::move(key));
125   return true;
126 }
127 
OnObjectSignaled(base::win::RegKey * key)128 void ProxyConfigServiceWin::OnObjectSignaled(base::win::RegKey* key) {
129   // Figure out which registry key signalled this change.
130   auto it = base::ranges::find(keys_to_watch_, key,
131                                &std::unique_ptr<base::win::RegKey>::get);
132   DCHECK(it != keys_to_watch_.end());
133 
134   // Keep watching the registry key.
135   if (!key->StartWatching(
136           base::BindOnce(&ProxyConfigServiceWin::OnObjectSignaled,
137                          base::Unretained(this), base::Unretained(key)))) {
138     keys_to_watch_.erase(it);
139   }
140 
141   // Have the PollingProxyConfigService test for changes.
142   CheckForChangesNow();
143 }
144 
145 // static
GetCurrentProxyConfig(const NetworkTrafficAnnotationTag traffic_annotation,ProxyConfigWithAnnotation * config)146 void ProxyConfigServiceWin::GetCurrentProxyConfig(
147     const NetworkTrafficAnnotationTag traffic_annotation,
148     ProxyConfigWithAnnotation* config) {
149   WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_config = {0};
150   if (!WinHttpGetIEProxyConfigForCurrentUser(&ie_config)) {
151     LOG(ERROR) << "WinHttpGetIEProxyConfigForCurrentUser failed: " <<
152         GetLastError();
153     *config = ProxyConfigWithAnnotation::CreateDirect();
154     return;
155   }
156   ProxyConfig proxy_config;
157   SetFromIEConfig(&proxy_config, ie_config);
158   FreeIEConfig(&ie_config);
159   proxy_config.set_from_system(true);
160   *config = ProxyConfigWithAnnotation(proxy_config, traffic_annotation);
161 }
162 
163 // static
SetFromIEConfig(ProxyConfig * config,const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG & ie_config)164 void ProxyConfigServiceWin::SetFromIEConfig(
165     ProxyConfig* config,
166     const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG& ie_config) {
167   if (ie_config.fAutoDetect)
168     config->set_auto_detect(true);
169   if (ie_config.lpszProxy) {
170     // lpszProxy may be a single proxy, or a proxy per scheme. The format
171     // is compatible with ProxyConfig::ProxyRules's string format.
172     config->proxy_rules().ParseFromString(
173         base::WideToUTF8(ie_config.lpszProxy));
174   }
175   if (ie_config.lpszProxyBypass) {
176     std::string proxy_bypass = base::WideToUTF8(ie_config.lpszProxyBypass);
177 
178     base::StringTokenizer proxy_server_bypass_list(proxy_bypass, ";, \t\n\r");
179     while (proxy_server_bypass_list.GetNext()) {
180       std::string bypass_url_domain = proxy_server_bypass_list.token();
181       config->proxy_rules().bypass_rules.AddRuleFromString(bypass_url_domain);
182     }
183   }
184   if (ie_config.lpszAutoConfigUrl)
185     config->set_pac_url(GURL(base::as_u16cstr(ie_config.lpszAutoConfigUrl)));
186 }
187 
188 }  // namespace net
189