Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / net / proxy / proxy_resolver_mac.cc
blob6d690806f2aa3115bd86e8ed28fdee6ff080af1f
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.
5 #include "net/proxy/proxy_resolver_mac.h"
7 #include <CoreFoundation/CoreFoundation.h>
9 #include "base/logging.h"
10 #include "base/mac/foundation_util.h"
11 #include "base/mac/scoped_cftyperef.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "net/base/net_errors.h"
15 #include "net/proxy/proxy_info.h"
16 #include "net/proxy/proxy_server.h"
18 #if defined(OS_IOS)
19 #include <CFNetwork/CFProxySupport.h>
20 #else
21 #include <CoreServices/CoreServices.h>
22 #endif
24 namespace net {
26 namespace {
28 // Utility function to map a CFProxyType to a ProxyServer::Scheme.
29 // If the type is unknown, returns ProxyServer::SCHEME_INVALID.
30 ProxyServer::Scheme GetProxyServerScheme(CFStringRef proxy_type) {
31 if (CFEqual(proxy_type, kCFProxyTypeNone))
32 return ProxyServer::SCHEME_DIRECT;
33 if (CFEqual(proxy_type, kCFProxyTypeHTTP))
34 return ProxyServer::SCHEME_HTTP;
35 if (CFEqual(proxy_type, kCFProxyTypeHTTPS)) {
36 // The "HTTPS" on the Mac side here means "proxy applies to https://" URLs;
37 // the proxy itself is still expected to be an HTTP proxy.
38 return ProxyServer::SCHEME_HTTP;
40 if (CFEqual(proxy_type, kCFProxyTypeSOCKS)) {
41 // We can't tell whether this was v4 or v5. We will assume it is
42 // v5 since that is the only version OS X supports.
43 return ProxyServer::SCHEME_SOCKS5;
45 return ProxyServer::SCHEME_INVALID;
48 // Callback for CFNetworkExecuteProxyAutoConfigurationURL. |client| is a pointer
49 // to a CFTypeRef. This stashes either |error| or |proxies| in that location.
50 void ResultCallback(void* client, CFArrayRef proxies, CFErrorRef error) {
51 DCHECK((proxies != NULL) == (error == NULL));
53 CFTypeRef* result_ptr = reinterpret_cast<CFTypeRef*>(client);
54 DCHECK(result_ptr != NULL);
55 DCHECK(*result_ptr == NULL);
57 if (error != NULL) {
58 *result_ptr = CFRetain(error);
59 } else {
60 *result_ptr = CFRetain(proxies);
62 CFRunLoopStop(CFRunLoopGetCurrent());
65 } // namespace
67 ProxyResolverMac::ProxyResolverMac()
68 : ProxyResolver(false /*expects_pac_bytes*/) {
71 ProxyResolverMac::~ProxyResolverMac() {}
73 // Gets the proxy information for a query URL from a PAC. Implementation
74 // inspired by http://developer.apple.com/samplecode/CFProxySupportTool/
75 int ProxyResolverMac::GetProxyForURL(const GURL& query_url,
76 ProxyInfo* results,
77 const CompletionCallback& /*callback*/,
78 RequestHandle* /*request*/,
79 const BoundNetLog& net_log) {
80 base::ScopedCFTypeRef<CFStringRef> query_ref(
81 base::SysUTF8ToCFStringRef(query_url.spec()));
82 base::ScopedCFTypeRef<CFURLRef> query_url_ref(
83 CFURLCreateWithString(kCFAllocatorDefault, query_ref.get(), NULL));
84 if (!query_url_ref.get())
85 return ERR_FAILED;
86 base::ScopedCFTypeRef<CFStringRef> pac_ref(base::SysUTF8ToCFStringRef(
87 script_data_->type() == ProxyResolverScriptData::TYPE_AUTO_DETECT
88 ? std::string()
89 : script_data_->url().spec()));
90 base::ScopedCFTypeRef<CFURLRef> pac_url_ref(
91 CFURLCreateWithString(kCFAllocatorDefault, pac_ref.get(), NULL));
92 if (!pac_url_ref.get())
93 return ERR_FAILED;
95 // Work around <rdar://problem/5530166>. This dummy call to
96 // CFNetworkCopyProxiesForURL initializes some state within CFNetwork that is
97 // required by CFNetworkExecuteProxyAutoConfigurationURL.
99 CFArrayRef dummy_result = CFNetworkCopyProxiesForURL(query_url_ref.get(),
100 NULL);
101 if (dummy_result)
102 CFRelease(dummy_result);
104 // We cheat here. We need to act as if we were synchronous, so we pump the
105 // runloop ourselves. Our caller moved us to a new thread anyway, so this is
106 // OK to do. (BTW, CFNetworkExecuteProxyAutoConfigurationURL returns a
107 // runloop source we need to release despite its name.)
109 CFTypeRef result = NULL;
110 CFStreamClientContext context = { 0, &result, NULL, NULL, NULL };
111 base::ScopedCFTypeRef<CFRunLoopSourceRef> runloop_source(
112 CFNetworkExecuteProxyAutoConfigurationURL(
113 pac_url_ref.get(), query_url_ref.get(), ResultCallback, &context));
114 if (!runloop_source)
115 return ERR_FAILED;
117 const CFStringRef private_runloop_mode =
118 CFSTR("org.chromium.ProxyResolverMac");
120 CFRunLoopAddSource(CFRunLoopGetCurrent(), runloop_source.get(),
121 private_runloop_mode);
122 CFRunLoopRunInMode(private_runloop_mode, DBL_MAX, false);
123 CFRunLoopSourceInvalidate(runloop_source.get());
124 DCHECK(result != NULL);
126 if (CFGetTypeID(result) == CFErrorGetTypeID()) {
127 // TODO(avi): do something better than this
128 CFRelease(result);
129 return ERR_FAILED;
131 base::ScopedCFTypeRef<CFArrayRef> proxy_array_ref(
132 base::mac::CFCastStrict<CFArrayRef>(result));
133 DCHECK(proxy_array_ref != NULL);
135 // This string will be an ordered list of <proxy-uri> entries, separated by
136 // semi-colons. It is the format that ProxyInfo::UseNamedProxy() expects.
137 // proxy-uri = [<proxy-scheme>"://"]<proxy-host>":"<proxy-port>
138 // (This also includes entries for direct connection, as "direct://").
139 std::string proxy_uri_list;
141 CFIndex proxy_array_count = CFArrayGetCount(proxy_array_ref.get());
142 for (CFIndex i = 0; i < proxy_array_count; ++i) {
143 CFDictionaryRef proxy_dictionary = base::mac::CFCastStrict<CFDictionaryRef>(
144 CFArrayGetValueAtIndex(proxy_array_ref.get(), i));
145 DCHECK(proxy_dictionary != NULL);
147 // The dictionary may have the following keys:
148 // - kCFProxyTypeKey : The type of the proxy
149 // - kCFProxyHostNameKey
150 // - kCFProxyPortNumberKey : The meat we're after.
151 // - kCFProxyUsernameKey
152 // - kCFProxyPasswordKey : Despite the existence of these keys in the
153 // documentation, they're never populated. Even if a
154 // username/password were to be set in the network
155 // proxy system preferences, we'd need to fetch it
156 // from the Keychain ourselves. CFProxy is such a
157 // tease.
158 // - kCFProxyAutoConfigurationURLKey : If the PAC file specifies another
159 // PAC file, I'm going home.
161 CFStringRef proxy_type = base::mac::GetValueFromDictionary<CFStringRef>(
162 proxy_dictionary, kCFProxyTypeKey);
163 ProxyServer proxy_server = ProxyServer::FromDictionary(
164 GetProxyServerScheme(proxy_type),
165 proxy_dictionary,
166 kCFProxyHostNameKey,
167 kCFProxyPortNumberKey);
168 if (!proxy_server.is_valid())
169 continue;
171 if (!proxy_uri_list.empty())
172 proxy_uri_list += ";";
173 proxy_uri_list += proxy_server.ToURI();
176 if (!proxy_uri_list.empty())
177 results->UseNamedProxy(proxy_uri_list);
178 // Else do nothing (results is already guaranteed to be in the default state).
180 return OK;
183 void ProxyResolverMac::CancelRequest(RequestHandle request) {
184 NOTREACHED();
187 LoadState ProxyResolverMac::GetLoadState(RequestHandle request) const {
188 NOTREACHED();
189 return LOAD_STATE_IDLE;
192 void ProxyResolverMac::CancelSetPacScript() {
193 NOTREACHED();
196 int ProxyResolverMac::SetPacScript(
197 const scoped_refptr<ProxyResolverScriptData>& script_data,
198 const CompletionCallback& /*callback*/) {
199 script_data_ = script_data;
200 return OK;
203 } // namespace net