OLD | NEW |
| (Empty) |
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_resolver_mac.h" | |
6 | |
7 #include <CoreFoundation/CoreFoundation.h> | |
8 | |
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" | |
17 | |
18 #if defined(OS_IOS) | |
19 #include <CFNetwork/CFProxySupport.h> | |
20 #else | |
21 #include <CoreServices/CoreServices.h> | |
22 #endif | |
23 | |
24 namespace { | |
25 | |
26 // Utility function to map a CFProxyType to a ProxyServer::Scheme. | |
27 // If the type is unknown, returns ProxyServer::SCHEME_INVALID. | |
28 net::ProxyServer::Scheme GetProxyServerScheme(CFStringRef proxy_type) { | |
29 if (CFEqual(proxy_type, kCFProxyTypeNone)) | |
30 return net::ProxyServer::SCHEME_DIRECT; | |
31 if (CFEqual(proxy_type, kCFProxyTypeHTTP)) | |
32 return net::ProxyServer::SCHEME_HTTP; | |
33 if (CFEqual(proxy_type, kCFProxyTypeHTTPS)) { | |
34 // The "HTTPS" on the Mac side here means "proxy applies to https://" URLs; | |
35 // the proxy itself is still expected to be an HTTP proxy. | |
36 return net::ProxyServer::SCHEME_HTTP; | |
37 } | |
38 if (CFEqual(proxy_type, kCFProxyTypeSOCKS)) { | |
39 // We can't tell whether this was v4 or v5. We will assume it is | |
40 // v5 since that is the only version OS X supports. | |
41 return net::ProxyServer::SCHEME_SOCKS5; | |
42 } | |
43 return net::ProxyServer::SCHEME_INVALID; | |
44 } | |
45 | |
46 // Callback for CFNetworkExecuteProxyAutoConfigurationURL. |client| is a pointer | |
47 // to a CFTypeRef. This stashes either |error| or |proxies| in that location. | |
48 void ResultCallback(void* client, CFArrayRef proxies, CFErrorRef error) { | |
49 DCHECK((proxies != NULL) == (error == NULL)); | |
50 | |
51 CFTypeRef* result_ptr = reinterpret_cast<CFTypeRef*>(client); | |
52 DCHECK(result_ptr != NULL); | |
53 DCHECK(*result_ptr == NULL); | |
54 | |
55 if (error != NULL) { | |
56 *result_ptr = CFRetain(error); | |
57 } else { | |
58 *result_ptr = CFRetain(proxies); | |
59 } | |
60 CFRunLoopStop(CFRunLoopGetCurrent()); | |
61 } | |
62 | |
63 } // namespace | |
64 | |
65 namespace net { | |
66 | |
67 ProxyResolverMac::ProxyResolverMac() | |
68 : ProxyResolver(false /*expects_pac_bytes*/) { | |
69 } | |
70 | |
71 ProxyResolverMac::~ProxyResolverMac() {} | |
72 | |
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; | |
94 | |
95 // Work around <rdar://problem/5530166>. This dummy call to | |
96 // CFNetworkCopyProxiesForURL initializes some state within CFNetwork that is | |
97 // required by CFNetworkExecuteProxyAutoConfigurationURL. | |
98 | |
99 CFArrayRef dummy_result = CFNetworkCopyProxiesForURL(query_url_ref.get(), | |
100 NULL); | |
101 if (dummy_result) | |
102 CFRelease(dummy_result); | |
103 | |
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.) | |
108 | |
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; | |
116 | |
117 const CFStringRef private_runloop_mode = | |
118 CFSTR("org.chromium.ProxyResolverMac"); | |
119 | |
120 CFRunLoopAddSource(CFRunLoopGetCurrent(), runloop_source.get(), | |
121 private_runloop_mode); | |
122 CFRunLoopRunInMode(private_runloop_mode, DBL_MAX, false); | |
123 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runloop_source.get(), | |
124 private_runloop_mode); | |
125 DCHECK(result != NULL); | |
126 | |
127 if (CFGetTypeID(result) == CFErrorGetTypeID()) { | |
128 // TODO(avi): do something better than this | |
129 CFRelease(result); | |
130 return ERR_FAILED; | |
131 } | |
132 base::ScopedCFTypeRef<CFArrayRef> proxy_array_ref( | |
133 base::mac::CFCastStrict<CFArrayRef>(result)); | |
134 DCHECK(proxy_array_ref != NULL); | |
135 | |
136 // This string will be an ordered list of <proxy-uri> entries, separated by | |
137 // semi-colons. It is the format that ProxyInfo::UseNamedProxy() expects. | |
138 // proxy-uri = [<proxy-scheme>"://"]<proxy-host>":"<proxy-port> | |
139 // (This also includes entries for direct connection, as "direct://"). | |
140 std::string proxy_uri_list; | |
141 | |
142 CFIndex proxy_array_count = CFArrayGetCount(proxy_array_ref.get()); | |
143 for (CFIndex i = 0; i < proxy_array_count; ++i) { | |
144 CFDictionaryRef proxy_dictionary = base::mac::CFCastStrict<CFDictionaryRef>( | |
145 CFArrayGetValueAtIndex(proxy_array_ref.get(), i)); | |
146 DCHECK(proxy_dictionary != NULL); | |
147 | |
148 // The dictionary may have the following keys: | |
149 // - kCFProxyTypeKey : The type of the proxy | |
150 // - kCFProxyHostNameKey | |
151 // - kCFProxyPortNumberKey : The meat we're after. | |
152 // - kCFProxyUsernameKey | |
153 // - kCFProxyPasswordKey : Despite the existence of these keys in the | |
154 // documentation, they're never populated. Even if a | |
155 // username/password were to be set in the network | |
156 // proxy system preferences, we'd need to fetch it | |
157 // from the Keychain ourselves. CFProxy is such a | |
158 // tease. | |
159 // - kCFProxyAutoConfigurationURLKey : If the PAC file specifies another | |
160 // PAC file, I'm going home. | |
161 | |
162 CFStringRef proxy_type = base::mac::GetValueFromDictionary<CFStringRef>( | |
163 proxy_dictionary, kCFProxyTypeKey); | |
164 ProxyServer proxy_server = ProxyServer::FromDictionary( | |
165 GetProxyServerScheme(proxy_type), | |
166 proxy_dictionary, | |
167 kCFProxyHostNameKey, | |
168 kCFProxyPortNumberKey); | |
169 if (!proxy_server.is_valid()) | |
170 continue; | |
171 | |
172 if (!proxy_uri_list.empty()) | |
173 proxy_uri_list += ";"; | |
174 proxy_uri_list += proxy_server.ToURI(); | |
175 } | |
176 | |
177 if (!proxy_uri_list.empty()) | |
178 results->UseNamedProxy(proxy_uri_list); | |
179 // Else do nothing (results is already guaranteed to be in the default state). | |
180 | |
181 return OK; | |
182 } | |
183 | |
184 void ProxyResolverMac::CancelRequest(RequestHandle request) { | |
185 NOTREACHED(); | |
186 } | |
187 | |
188 LoadState ProxyResolverMac::GetLoadState(RequestHandle request) const { | |
189 NOTREACHED(); | |
190 return LOAD_STATE_IDLE; | |
191 } | |
192 | |
193 void ProxyResolverMac::CancelSetPacScript() { | |
194 NOTREACHED(); | |
195 } | |
196 | |
197 int ProxyResolverMac::SetPacScript( | |
198 const scoped_refptr<ProxyResolverScriptData>& script_data, | |
199 const CompletionCallback& /*callback*/) { | |
200 script_data_ = script_data; | |
201 return OK; | |
202 } | |
203 | |
204 } // namespace net | |
OLD | NEW |