summaryrefslogtreecommitdiff
path: root/chromium/net/proxy/proxy_resolver_mac.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/proxy/proxy_resolver_mac.cc')
-rw-r--r--chromium/net/proxy/proxy_resolver_mac.cc204
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