diff options
Diffstat (limited to 'chromium/net/proxy/proxy_resolver_mac.cc')
-rw-r--r-- | chromium/net/proxy/proxy_resolver_mac.cc | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/chromium/net/proxy/proxy_resolver_mac.cc b/chromium/net/proxy/proxy_resolver_mac.cc new file mode 100644 index 00000000000..8ddf81fffe4 --- /dev/null +++ b/chromium/net/proxy/proxy_resolver_mac.cc @@ -0,0 +1,204 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/proxy/proxy_resolver_mac.h" + +#include <CoreFoundation/CoreFoundation.h> + +#include "base/logging.h" +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/strings/string_util.h" +#include "base/strings/sys_string_conversions.h" +#include "net/base/net_errors.h" +#include "net/proxy/proxy_info.h" +#include "net/proxy/proxy_server.h" + +#if defined(OS_IOS) +#include <CFNetwork/CFProxySupport.h> +#else +#include <CoreServices/CoreServices.h> +#endif + +namespace { + +// Utility function to map a CFProxyType to a ProxyServer::Scheme. +// If the type is unknown, returns ProxyServer::SCHEME_INVALID. +net::ProxyServer::Scheme GetProxyServerScheme(CFStringRef proxy_type) { + if (CFEqual(proxy_type, kCFProxyTypeNone)) + return net::ProxyServer::SCHEME_DIRECT; + if (CFEqual(proxy_type, kCFProxyTypeHTTP)) + return net::ProxyServer::SCHEME_HTTP; + if (CFEqual(proxy_type, kCFProxyTypeHTTPS)) { + // The "HTTPS" on the Mac side here means "proxy applies to https://" URLs; + // the proxy itself is still expected to be an HTTP proxy. + return net::ProxyServer::SCHEME_HTTP; + } + if (CFEqual(proxy_type, kCFProxyTypeSOCKS)) { + // We can't tell whether this was v4 or v5. We will assume it is + // v5 since that is the only version OS X supports. + return net::ProxyServer::SCHEME_SOCKS5; + } + return net::ProxyServer::SCHEME_INVALID; +} + +// Callback for CFNetworkExecuteProxyAutoConfigurationURL. |client| is a pointer +// to a CFTypeRef. This stashes either |error| or |proxies| in that location. +void ResultCallback(void* client, CFArrayRef proxies, CFErrorRef error) { + DCHECK((proxies != NULL) == (error == NULL)); + + CFTypeRef* result_ptr = reinterpret_cast<CFTypeRef*>(client); + DCHECK(result_ptr != NULL); + DCHECK(*result_ptr == NULL); + + if (error != NULL) { + *result_ptr = CFRetain(error); + } else { + *result_ptr = CFRetain(proxies); + } + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +} // namespace + +namespace net { + +ProxyResolverMac::ProxyResolverMac() + : ProxyResolver(false /*expects_pac_bytes*/) { +} + +ProxyResolverMac::~ProxyResolverMac() {} + +// Gets the proxy information for a query URL from a PAC. Implementation +// inspired by http://developer.apple.com/samplecode/CFProxySupportTool/ +int ProxyResolverMac::GetProxyForURL(const GURL& query_url, + ProxyInfo* results, + const CompletionCallback& /*callback*/, + RequestHandle* /*request*/, + const BoundNetLog& net_log) { + base::ScopedCFTypeRef<CFStringRef> query_ref( + base::SysUTF8ToCFStringRef(query_url.spec())); + base::ScopedCFTypeRef<CFURLRef> query_url_ref( + CFURLCreateWithString(kCFAllocatorDefault, query_ref.get(), NULL)); + if (!query_url_ref.get()) + return ERR_FAILED; + base::ScopedCFTypeRef<CFStringRef> pac_ref(base::SysUTF8ToCFStringRef( + script_data_->type() == ProxyResolverScriptData::TYPE_AUTO_DETECT + ? std::string() + : script_data_->url().spec())); + base::ScopedCFTypeRef<CFURLRef> pac_url_ref( + CFURLCreateWithString(kCFAllocatorDefault, pac_ref.get(), NULL)); + if (!pac_url_ref.get()) + return ERR_FAILED; + + // Work around <rdar://problem/5530166>. This dummy call to + // CFNetworkCopyProxiesForURL initializes some state within CFNetwork that is + // required by CFNetworkExecuteProxyAutoConfigurationURL. + + CFArrayRef dummy_result = CFNetworkCopyProxiesForURL(query_url_ref.get(), + NULL); + if (dummy_result) + CFRelease(dummy_result); + + // We cheat here. We need to act as if we were synchronous, so we pump the + // runloop ourselves. Our caller moved us to a new thread anyway, so this is + // OK to do. (BTW, CFNetworkExecuteProxyAutoConfigurationURL returns a + // runloop source we need to release despite its name.) + + CFTypeRef result = NULL; + CFStreamClientContext context = { 0, &result, NULL, NULL, NULL }; + base::ScopedCFTypeRef<CFRunLoopSourceRef> runloop_source( + CFNetworkExecuteProxyAutoConfigurationURL( + pac_url_ref.get(), query_url_ref.get(), ResultCallback, &context)); + if (!runloop_source) + return ERR_FAILED; + + const CFStringRef private_runloop_mode = + CFSTR("org.chromium.ProxyResolverMac"); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), runloop_source.get(), + private_runloop_mode); + CFRunLoopRunInMode(private_runloop_mode, DBL_MAX, false); + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runloop_source.get(), + private_runloop_mode); + DCHECK(result != NULL); + + if (CFGetTypeID(result) == CFErrorGetTypeID()) { + // TODO(avi): do something better than this + CFRelease(result); + return ERR_FAILED; + } + base::ScopedCFTypeRef<CFArrayRef> proxy_array_ref( + base::mac::CFCastStrict<CFArrayRef>(result)); + DCHECK(proxy_array_ref != NULL); + + // This string will be an ordered list of <proxy-uri> entries, separated by + // semi-colons. It is the format that ProxyInfo::UseNamedProxy() expects. + // proxy-uri = [<proxy-scheme>"://"]<proxy-host>":"<proxy-port> + // (This also includes entries for direct connection, as "direct://"). + std::string proxy_uri_list; + + CFIndex proxy_array_count = CFArrayGetCount(proxy_array_ref.get()); + for (CFIndex i = 0; i < proxy_array_count; ++i) { + CFDictionaryRef proxy_dictionary = base::mac::CFCastStrict<CFDictionaryRef>( + CFArrayGetValueAtIndex(proxy_array_ref.get(), i)); + DCHECK(proxy_dictionary != NULL); + + // The dictionary may have the following keys: + // - kCFProxyTypeKey : The type of the proxy + // - kCFProxyHostNameKey + // - kCFProxyPortNumberKey : The meat we're after. + // - kCFProxyUsernameKey + // - kCFProxyPasswordKey : Despite the existence of these keys in the + // documentation, they're never populated. Even if a + // username/password were to be set in the network + // proxy system preferences, we'd need to fetch it + // from the Keychain ourselves. CFProxy is such a + // tease. + // - kCFProxyAutoConfigurationURLKey : If the PAC file specifies another + // PAC file, I'm going home. + + CFStringRef proxy_type = base::mac::GetValueFromDictionary<CFStringRef>( + proxy_dictionary, kCFProxyTypeKey); + ProxyServer proxy_server = ProxyServer::FromDictionary( + GetProxyServerScheme(proxy_type), + proxy_dictionary, + kCFProxyHostNameKey, + kCFProxyPortNumberKey); + if (!proxy_server.is_valid()) + continue; + + if (!proxy_uri_list.empty()) + proxy_uri_list += ";"; + proxy_uri_list += proxy_server.ToURI(); + } + + if (!proxy_uri_list.empty()) + results->UseNamedProxy(proxy_uri_list); + // Else do nothing (results is already guaranteed to be in the default state). + + return OK; +} + +void ProxyResolverMac::CancelRequest(RequestHandle request) { + NOTREACHED(); +} + +LoadState ProxyResolverMac::GetLoadState(RequestHandle request) const { + NOTREACHED(); + return LOAD_STATE_IDLE; +} + +void ProxyResolverMac::CancelSetPacScript() { + NOTREACHED(); +} + +int ProxyResolverMac::SetPacScript( + const scoped_refptr<ProxyResolverScriptData>& script_data, + const CompletionCallback& /*callback*/) { + script_data_ = script_data; + return OK; +} + +} // namespace net |