| 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 |