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. 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. 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 ProxyResolverMac::ProxyResolverMac() 57 : ProxyResolver(false /*expects_pac_bytes*/) { 58 } 59 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/ 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 180 void ProxyResolverMac::CancelRequest(RequestHandle request) { 181 NOTREACHED(); 182 } 183 184 void ProxyResolverMac::CancelSetPacScript() { 185 NOTREACHED(); 186 } 187 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