• 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_util.h"
11 #include "base/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 
56 // Gets the proxy information for a query URL from a PAC. Implementation
57 // inspired by http://developer.apple.com/samplecode/CFProxySupportTool/
GetProxyForURL(const GURL & query_url,ProxyInfo * results,CompletionCallback *,RequestHandle *,LoadLog * load_log)58 int ProxyResolverMac::GetProxyForURL(const GURL& query_url,
59                                      ProxyInfo* results,
60                                      CompletionCallback* /*callback*/,
61                                      RequestHandle* /*request*/,
62                                      LoadLog* load_log) {
63   scoped_cftyperef<CFStringRef> query_ref(
64       base::SysUTF8ToCFStringRef(query_url.spec()));
65   scoped_cftyperef<CFURLRef> query_url_ref(
66       CFURLCreateWithString(kCFAllocatorDefault,
67                             query_ref.get(),
68                             NULL));
69   if (!query_url_ref.get())
70     return ERR_FAILED;
71   scoped_cftyperef<CFStringRef> pac_ref(
72       base::SysUTF8ToCFStringRef(pac_url_.spec()));
73   scoped_cftyperef<CFURLRef> pac_url_ref(
74       CFURLCreateWithString(kCFAllocatorDefault,
75                             pac_ref.get(),
76                             NULL));
77   if (!pac_url_ref.get())
78     return ERR_FAILED;
79 
80   // Work around <rdar://problem/5530166>. This dummy call to
81   // CFNetworkCopyProxiesForURL initializes some state within CFNetwork that is
82   // required by CFNetworkExecuteProxyAutoConfigurationURL.
83 
84   CFArrayRef dummy_result = CFNetworkCopyProxiesForURL(query_url_ref.get(),
85                                                        NULL);
86   if (dummy_result)
87     CFRelease(dummy_result);
88 
89   // We cheat here. We need to act as if we were synchronous, so we pump the
90   // runloop ourselves. Our caller moved us to a new thread anyway, so this is
91   // OK to do. (BTW, CFNetworkExecuteProxyAutoConfigurationURL returns a
92   // runloop source we need to release despite its name.)
93 
94   CFTypeRef result = NULL;
95   CFStreamClientContext context = { 0, &result, NULL, NULL, NULL };
96   scoped_cftyperef<CFRunLoopSourceRef> runloop_source(
97       CFNetworkExecuteProxyAutoConfigurationURL(pac_url_ref.get(),
98                                                 query_url_ref.get(),
99                                                 ResultCallback,
100                                                 &context));
101   if (!runloop_source)
102     return ERR_FAILED;
103 
104   const CFStringRef private_runloop_mode =
105       CFSTR("org.chromium.ProxyResolverMac");
106 
107   CFRunLoopAddSource(CFRunLoopGetCurrent(), runloop_source.get(),
108                      private_runloop_mode);
109   CFRunLoopRunInMode(private_runloop_mode, DBL_MAX, false);
110   CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runloop_source.get(),
111                         private_runloop_mode);
112   DCHECK(result != NULL);
113 
114   if (CFGetTypeID(result) == CFErrorGetTypeID()) {
115     // TODO(avi): do something better than this
116     CFRelease(result);
117     return ERR_FAILED;
118   }
119   DCHECK(CFGetTypeID(result) == CFArrayGetTypeID());
120   scoped_cftyperef<CFArrayRef> proxy_array_ref((CFArrayRef)result);
121 
122   // This string will be an ordered list of <proxy-uri> entries, separated by
123   // semi-colons. It is the format that ProxyInfo::UseNamedProxy() expects.
124   //    proxy-uri = [<proxy-scheme>"://"]<proxy-host>":"<proxy-port>
125   // (This also includes entries for direct connection, as "direct://").
126   std::string proxy_uri_list;
127 
128   CFIndex proxy_array_count = CFArrayGetCount(proxy_array_ref.get());
129   for (CFIndex i = 0; i < proxy_array_count; ++i) {
130     CFDictionaryRef proxy_dictionary =
131         (CFDictionaryRef)CFArrayGetValueAtIndex(proxy_array_ref.get(), i);
132     DCHECK(CFGetTypeID(proxy_dictionary) == CFDictionaryGetTypeID());
133 
134     // The dictionary may have the following keys:
135     // - kCFProxyTypeKey : The type of the proxy
136     // - kCFProxyHostNameKey
137     // - kCFProxyPortNumberKey : The meat we're after.
138     // - kCFProxyUsernameKey
139     // - kCFProxyPasswordKey : Despite the existence of these keys in the
140     //                         documentation, they're never populated. Even if a
141     //                         username/password were to be set in the network
142     //                         proxy system preferences, we'd need to fetch it
143     //                         from the Keychain ourselves. CFProxy is such a
144     //                         tease.
145     // - kCFProxyAutoConfigurationURLKey : If the PAC file specifies another
146     //                                     PAC file, I'm going home.
147 
148     CFStringRef proxy_type =
149         (CFStringRef)mac_util::GetValueFromDictionary(proxy_dictionary,
150                                                       kCFProxyTypeKey,
151                                                       CFStringGetTypeID());
152     ProxyServer proxy_server = ProxyServer::FromDictionary(
153         GetProxyServerScheme(proxy_type),
154         proxy_dictionary,
155         kCFProxyHostNameKey,
156         kCFProxyPortNumberKey);
157     if (!proxy_server.is_valid())
158       continue;
159 
160     if (!proxy_uri_list.empty())
161       proxy_uri_list += ";";
162     proxy_uri_list += proxy_server.ToURI();
163   }
164 
165   if (!proxy_uri_list.empty())
166     results->UseNamedProxy(proxy_uri_list);
167   // Else do nothing (results is already guaranteed to be in the default state).
168 
169   return OK;
170 }
171 
172 }  // namespace net
173