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