blob: a197313aa697a9815abe2bcbffbefe5dcbc738fb [file] [log] [blame]
[email protected]119655002010-07-23 06:02:401// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]a9944f02009-12-10 10:37:272// 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_mac.h"
6
7#include <CoreFoundation/CoreFoundation.h>
8#include <SystemConfiguration/SystemConfiguration.h>
9
10#include "base/logging.h"
11#include "base/mac_util.h"
12#include "base/scoped_cftyperef.h"
13#include "base/sys_string_conversions.h"
14#include "net/base/net_errors.h"
15#include "net/proxy/proxy_config.h"
16#include "net/proxy/proxy_info.h"
17#include "net/proxy/proxy_server.h"
18
[email protected]119655002010-07-23 06:02:4019namespace net {
20
[email protected]a9944f02009-12-10 10:37:2721namespace {
22
[email protected]119655002010-07-23 06:02:4023const int kPollIntervalSec = 5;
24
[email protected]a9944f02009-12-10 10:37:2725// Utility function to pull out a boolean value from a dictionary and return it,
26// returning a default value if the key is not present.
27bool GetBoolFromDictionary(CFDictionaryRef dict,
28 CFStringRef key,
29 bool default_value) {
30 CFNumberRef number = (CFNumberRef)mac_util::GetValueFromDictionary(
31 dict, key, CFNumberGetTypeID());
32 if (!number)
33 return default_value;
34
35 int int_value;
36 if (CFNumberGetValue(number, kCFNumberIntType, &int_value))
37 return int_value;
38 else
39 return default_value;
40}
41
[email protected]119655002010-07-23 06:02:4042void GetCurrentProxyConfig(ProxyConfig* config) {
[email protected]a9944f02009-12-10 10:37:2743 scoped_cftyperef<CFDictionaryRef> config_dict(
44 SCDynamicStoreCopyProxies(NULL));
45 DCHECK(config_dict);
46
47 // auto-detect
48
49 // There appears to be no UI for this configuration option, and we're not sure
50 // if Apple's proxy code even takes it into account. But the constant is in
51 // the header file so we'll use it.
[email protected]ed4ed0f2010-02-24 00:20:4852 config->set_auto_detect(
[email protected]a9944f02009-12-10 10:37:2753 GetBoolFromDictionary(config_dict.get(),
54 kSCPropNetProxiesProxyAutoDiscoveryEnable,
[email protected]ed4ed0f2010-02-24 00:20:4855 false));
[email protected]a9944f02009-12-10 10:37:2756
57 // PAC file
58
59 if (GetBoolFromDictionary(config_dict.get(),
60 kSCPropNetProxiesProxyAutoConfigEnable,
61 false)) {
62 CFStringRef pac_url_ref = (CFStringRef)mac_util::GetValueFromDictionary(
63 config_dict.get(),
64 kSCPropNetProxiesProxyAutoConfigURLString,
65 CFStringGetTypeID());
66 if (pac_url_ref)
[email protected]ed4ed0f2010-02-24 00:20:4867 config->set_pac_url(GURL(base::SysCFStringRefToUTF8(pac_url_ref)));
[email protected]a9944f02009-12-10 10:37:2768 }
69
70 // proxies (for now ftp, http, https, and SOCKS)
71
72 if (GetBoolFromDictionary(config_dict.get(),
73 kSCPropNetProxiesFTPEnable,
74 false)) {
75 ProxyServer proxy_server =
76 ProxyServer::FromDictionary(ProxyServer::SCHEME_HTTP,
77 config_dict.get(),
78 kSCPropNetProxiesFTPProxy,
79 kSCPropNetProxiesFTPPort);
80 if (proxy_server.is_valid()) {
[email protected]ed4ed0f2010-02-24 00:20:4881 config->proxy_rules().type =
82 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
83 config->proxy_rules().proxy_for_ftp = proxy_server;
[email protected]a9944f02009-12-10 10:37:2784 }
85 }
86 if (GetBoolFromDictionary(config_dict.get(),
87 kSCPropNetProxiesHTTPEnable,
88 false)) {
89 ProxyServer proxy_server =
90 ProxyServer::FromDictionary(ProxyServer::SCHEME_HTTP,
91 config_dict.get(),
92 kSCPropNetProxiesHTTPProxy,
93 kSCPropNetProxiesHTTPPort);
94 if (proxy_server.is_valid()) {
[email protected]ed4ed0f2010-02-24 00:20:4895 config->proxy_rules().type =
96 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
97 config->proxy_rules().proxy_for_http = proxy_server;
[email protected]a9944f02009-12-10 10:37:2798 }
99 }
100 if (GetBoolFromDictionary(config_dict.get(),
101 kSCPropNetProxiesHTTPSEnable,
102 false)) {
103 ProxyServer proxy_server =
104 ProxyServer::FromDictionary(ProxyServer::SCHEME_HTTP,
105 config_dict.get(),
106 kSCPropNetProxiesHTTPSProxy,
107 kSCPropNetProxiesHTTPSPort);
108 if (proxy_server.is_valid()) {
[email protected]ed4ed0f2010-02-24 00:20:48109 config->proxy_rules().type =
110 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
111 config->proxy_rules().proxy_for_https = proxy_server;
[email protected]a9944f02009-12-10 10:37:27112 }
113 }
114 if (GetBoolFromDictionary(config_dict.get(),
115 kSCPropNetProxiesSOCKSEnable,
116 false)) {
117 ProxyServer proxy_server =
118 ProxyServer::FromDictionary(ProxyServer::SCHEME_SOCKS5,
119 config_dict.get(),
120 kSCPropNetProxiesSOCKSProxy,
121 kSCPropNetProxiesSOCKSPort);
122 if (proxy_server.is_valid()) {
[email protected]ed4ed0f2010-02-24 00:20:48123 config->proxy_rules().type =
124 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
[email protected]2b6ed3dc2010-08-25 06:03:48125 config->proxy_rules().fallback_proxy = proxy_server;
[email protected]a9944f02009-12-10 10:37:27126 }
127 }
128
129 // proxy bypass list
130
131 CFArrayRef bypass_array_ref =
132 (CFArrayRef)mac_util::GetValueFromDictionary(
133 config_dict.get(),
134 kSCPropNetProxiesExceptionsList,
135 CFArrayGetTypeID());
136 if (bypass_array_ref) {
137 CFIndex bypass_array_count = CFArrayGetCount(bypass_array_ref);
138 for (CFIndex i = 0; i < bypass_array_count; ++i) {
139 CFStringRef bypass_item_ref =
140 (CFStringRef)CFArrayGetValueAtIndex(bypass_array_ref, i);
141 if (CFGetTypeID(bypass_item_ref) != CFStringGetTypeID()) {
142 LOG(WARNING) << "Expected value for item " << i
143 << " in the kSCPropNetProxiesExceptionsList"
144 " to be a CFStringRef but it was not";
145
146 } else {
[email protected]ed4ed0f2010-02-24 00:20:48147 config->proxy_rules().bypass_rules.AddRuleFromString(
[email protected]a9944f02009-12-10 10:37:27148 base::SysCFStringRefToUTF8(bypass_item_ref));
149 }
150 }
151 }
152
153 // proxy bypass boolean
154
[email protected]7541206c2010-02-19 20:24:06155 if (GetBoolFromDictionary(config_dict.get(),
[email protected]a9944f02009-12-10 10:37:27156 kSCPropNetProxiesExcludeSimpleHostnames,
[email protected]7541206c2010-02-19 20:24:06157 false)) {
[email protected]ed4ed0f2010-02-24 00:20:48158 config->proxy_rules().bypass_rules.AddRuleToBypassLocal();
[email protected]7541206c2010-02-19 20:24:06159 }
[email protected]119655002010-07-23 06:02:40160}
[email protected]a9944f02009-12-10 10:37:27161
[email protected]119655002010-07-23 06:02:40162} // namespace
163
[email protected]9328443b2010-07-30 06:09:40164// Reference-counted helper for posting a task to
165// ProxyConfigServiceMac::OnProxyConfigChanged between the notifier and IO
166// thread. This helper object may outlive the ProxyConfigServiceMac.
167class ProxyConfigServiceMac::Helper
168 : public base::RefCountedThreadSafe<ProxyConfigServiceMac::Helper> {
169 public:
170 explicit Helper(ProxyConfigServiceMac* parent) : parent_(parent) {
171 DCHECK(parent);
172 }
173
174 // Called when the parent is destroyed.
175 void Orphan() {
176 parent_ = NULL;
177 }
178
179 void OnProxyConfigChanged(const ProxyConfig& new_config) {
180 if (parent_)
181 parent_->OnProxyConfigChanged(new_config);
182 }
183
184 private:
185 ProxyConfigServiceMac* parent_;
186};
187
188ProxyConfigServiceMac::ProxyConfigServiceMac(MessageLoop* io_loop)
[email protected]6688a4962010-09-07 19:41:36189 : forwarder_(this),
190 config_watcher_(&forwarder_),
191 has_fetched_config_(false),
[email protected]9328443b2010-07-30 06:09:40192 helper_(new Helper(this)),
193 io_loop_(io_loop) {
194 DCHECK(io_loop);
195}
196
197ProxyConfigServiceMac::~ProxyConfigServiceMac() {
198 DCHECK_EQ(io_loop_, MessageLoop::current());
199 helper_->Orphan();
200 io_loop_ = NULL;
201}
202
203void ProxyConfigServiceMac::AddObserver(Observer* observer) {
204 DCHECK_EQ(io_loop_, MessageLoop::current());
205 observers_.AddObserver(observer);
206}
207
208void ProxyConfigServiceMac::RemoveObserver(Observer* observer) {
209 DCHECK_EQ(io_loop_, MessageLoop::current());
210 observers_.RemoveObserver(observer);
211}
212
213bool ProxyConfigServiceMac::GetLatestProxyConfig(ProxyConfig* config) {
214 DCHECK_EQ(io_loop_, MessageLoop::current());
215
216 // Lazy-initialize by fetching the proxy setting from this thread.
217 if (!has_fetched_config_) {
218 GetCurrentProxyConfig(&last_config_fetched_);
219 has_fetched_config_ = true;
220 }
221
222 *config = last_config_fetched_;
223 return has_fetched_config_;
224}
225
226void ProxyConfigServiceMac::SetDynamicStoreNotificationKeys(
227 SCDynamicStoreRef store) {
[email protected]6688a4962010-09-07 19:41:36228 // Called on notifier thread.
229
[email protected]9328443b2010-07-30 06:09:40230 CFStringRef proxies_key = SCDynamicStoreKeyCreateProxies(NULL);
231 CFArrayRef key_array = CFArrayCreate(
232 NULL, (const void **)(&proxies_key), 1, &kCFTypeArrayCallBacks);
233
234 bool ret = SCDynamicStoreSetNotificationKeys(store, key_array, NULL);
235 // TODO(willchan): Figure out a proper way to handle this rather than crash.
236 CHECK(ret);
237
238 CFRelease(key_array);
239 CFRelease(proxies_key);
240}
241
242void ProxyConfigServiceMac::OnNetworkConfigChange(CFArrayRef changed_keys) {
243 // Called on notifier thread.
244
245 // Fetch the new system proxy configuration.
246 ProxyConfig new_config;
247 GetCurrentProxyConfig(&new_config);
248
249 // Call OnProxyConfigChanged() on the IO thread to notify our observers.
250 io_loop_->PostTask(
251 FROM_HERE,
252 NewRunnableMethod(
253 helper_.get(), &Helper::OnProxyConfigChanged, new_config));
254}
255
256void ProxyConfigServiceMac::OnProxyConfigChanged(
257 const ProxyConfig& new_config) {
258 DCHECK_EQ(io_loop_, MessageLoop::current());
259
260 // Keep track of the last value we have seen.
261 has_fetched_config_ = true;
262 last_config_fetched_ = new_config;
263
264 // Notify all the observers.
265 FOR_EACH_OBSERVER(Observer, observers_, OnProxyConfigChanged(new_config));
[email protected]a9944f02009-12-10 10:37:27266}
267
268} // namespace net