1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be
3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file.
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/proxy/proxy_config_service_win.h"
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <windows.h>
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <winhttp.h>
9c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h"
11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_ptr.h"
12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_tokenizer.h"
13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_util.h"
143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/stl_util-inl.h"
153f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread_restrictions.h"
16731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/win/registry.h"
17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/net_errors.h"
18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/proxy/proxy_config.h"
19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#pragma comment(lib, "winhttp.lib")
21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace net {
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merricknamespace {
253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickconst int kPollIntervalSec = 10;
273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_config) {
29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (ie_config->lpszAutoConfigUrl)
30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    GlobalFree(ie_config->lpszAutoConfigUrl);
31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (ie_config->lpszProxy)
32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    GlobalFree(ie_config->lpszProxy);
33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (ie_config->lpszProxyBypass)
34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    GlobalFree(ie_config->lpszProxyBypass);
35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}  // namespace
383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// RegKey and ObjectWatcher pair.
403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickclass ProxyConfigServiceWin::KeyEntry {
413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick public:
423f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  bool StartWatching(base::win::ObjectWatcher::Delegate* delegate) {
433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // Try to create a watch event for the registry key (which watches the
443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // sibling tree as well).
4572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (key_.StartWatching() != ERROR_SUCCESS)
463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return false;
473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // Now setup an ObjectWatcher for this event, so we get OnObjectSignaled()
493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // invoked on this message loop once it is signalled.
503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (!watcher_.StartWatching(key_.watch_event(), delegate))
513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return false;
523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return true;
543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  bool CreateRegKey(HKEY rootkey, const wchar_t* subkey) {
5772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return key_.Create(rootkey, subkey, KEY_NOTIFY) == ERROR_SUCCESS;
583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  HANDLE watch_event() const {
613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return key_.watch_event();
623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick private:
65731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  base::win::RegKey key_;
663f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  base::win::ObjectWatcher watcher_;
673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick};
683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
693345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickProxyConfigServiceWin::ProxyConfigServiceWin()
703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    : PollingProxyConfigService(
713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          base::TimeDelta::FromSeconds(kPollIntervalSec),
723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          &ProxyConfigServiceWin::GetCurrentProxyConfig) {
733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
753345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickProxyConfigServiceWin::~ProxyConfigServiceWin() {
76513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // The registry functions below will end up going to disk.  Do this on another
77513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // thread to avoid slowing the IO thread.  http://crbug.com/61453
78513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::ScopedAllowIO allow_io;
793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  STLDeleteElements(&keys_to_watch_);
803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid ProxyConfigServiceWin::AddObserver(Observer* observer) {
833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Lazily-initialize our registry watcher.
843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  StartWatchingRegistryForChanges();
853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Let the super-class do its work now.
873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  PollingProxyConfigService::AddObserver(observer);
883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid ProxyConfigServiceWin::StartWatchingRegistryForChanges() {
913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!keys_to_watch_.empty())
923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return;  // Already initialized.
933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
94513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // The registry functions below will end up going to disk.  Do this on another
95513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // thread to avoid slowing the IO thread.  http://crbug.com/61453
96513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::ScopedAllowIO allow_io;
97513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // There are a number of different places where proxy settings can live
993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // in the registry. In some cases it appears in a binary value, in other
1003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // cases string values. Furthermore winhttp and wininet appear to have
1013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // separate stores, and proxy settings can be configured per-machine
1023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // or per-user.
1033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  //
1043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // This function is probably not exhaustive in the registry locations it
1053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // watches for changes, however it should catch the majority of the
1063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // cases. In case we have missed some less common triggers (likely), we
1073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // will catch them during the periodic (10 second) polling, so things
1083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // will recover.
1093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  AddKeyToWatchList(
1113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      HKEY_CURRENT_USER,
1123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
1133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  AddKeyToWatchList(
1153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      HKEY_LOCAL_MACHINE,
1163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
1173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  AddKeyToWatchList(
1193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      HKEY_LOCAL_MACHINE,
1203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      L"SOFTWARE\\Policies\\Microsoft\\Windows\\CurrentVersion\\"
1213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      L"Internet Settings");
1223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickbool ProxyConfigServiceWin::AddKeyToWatchList(HKEY rootkey,
1253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                              const wchar_t* subkey) {
1263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  scoped_ptr<KeyEntry> entry(new KeyEntry);
1273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!entry->CreateRegKey(rootkey, subkey))
1283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return false;
1293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!entry->StartWatching(this))
1313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return false;
1323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  keys_to_watch_.push_back(entry.release());
1343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return true;
1353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid ProxyConfigServiceWin::OnObjectSignaled(HANDLE object) {
1383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Figure out which registry key signalled this change.
1393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  KeyEntryList::iterator it;
1403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  for (it = keys_to_watch_.begin(); it != keys_to_watch_.end(); ++it) {
1413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if ((*it)->watch_event() == object)
1423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      break;
1433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK(it != keys_to_watch_.end());
1463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Keep watching the registry key.
1483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!(*it)->StartWatching(this))
1493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    keys_to_watch_.erase(it);
1503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Have the PollingProxyConfigService test for changes.
1523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  CheckForChangesNow();
1533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// static
1563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid ProxyConfigServiceWin::GetCurrentProxyConfig(ProxyConfig* config) {
157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_config = {0};
158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!WinHttpGetIEProxyConfigForCurrentUser(&ie_config)) {
159c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    LOG(ERROR) << "WinHttpGetIEProxyConfigForCurrentUser failed: " <<
160c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        GetLastError();
1613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    *config = ProxyConfig::CreateDirect();
1623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return;
163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
164c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  SetFromIEConfig(config, ie_config);
165c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  FreeIEConfig(&ie_config);
166c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
167c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
169c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid ProxyConfigServiceWin::SetFromIEConfig(
170c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ProxyConfig* config,
171c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG& ie_config) {
172c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (ie_config.fAutoDetect)
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    config->set_auto_detect(true);
174c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (ie_config.lpszProxy) {
175c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // lpszProxy may be a single proxy, or a proxy per scheme. The format
176c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // is compatible with ProxyConfig::ProxyRules's string format.
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    config->proxy_rules().ParseFromString(WideToASCII(ie_config.lpszProxy));
178c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (ie_config.lpszProxyBypass) {
180c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    std::string proxy_bypass = WideToASCII(ie_config.lpszProxyBypass);
181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    StringTokenizer proxy_server_bypass_list(proxy_bypass, ";, \t\n\r");
183c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    while (proxy_server_bypass_list.GetNext()) {
184c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      std::string bypass_url_domain = proxy_server_bypass_list.token();
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      config->proxy_rules().bypass_rules.AddRuleFromString(bypass_url_domain);
186c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
187c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
188c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (ie_config.lpszAutoConfigUrl)
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    config->set_pac_url(GURL(ie_config.lpszAutoConfigUrl));
190c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
191c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
192c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}  // namespace net
193