| /* |
| * Copyright (C) 2010 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "ProxyServer.h" |
| |
| #include "URL.h" |
| #include "Logging.h" |
| #include <wtf/RetainPtr.h> |
| #include <wtf/text/CString.h> |
| |
| #if PLATFORM(IOS) || PLATFORM(WIN) |
| #include <CFNetwork/CFNetwork.h> |
| #endif |
| |
| namespace WebCore { |
| |
| static void processProxyServers(Vector<ProxyServer>& proxyServers, CFArrayRef proxies, CFURLRef url); |
| |
| static void proxyAutoConfigurationResultCallback(void *context, CFArrayRef proxies, CFErrorRef error) |
| { |
| // We only expect a single result callback per invocation. Stop our runloop to unblock our caller. |
| CFRunLoopStop(CFRunLoopGetCurrent()); |
| |
| Vector<ProxyServer>* proxyServers = (Vector<ProxyServer>*)context; |
| if (!proxies) { |
| ASSERT(error); |
| RetainPtr<CFStringRef> errorDescriptionCF = adoptCF(CFErrorCopyDescription(error)); |
| String errorDescription(errorDescriptionCF.get()); |
| LOG(Network, "Failed to process proxy auto-configuration file with error: %s", errorDescription.utf8().data()); |
| return; |
| } |
| |
| processProxyServers(*proxyServers, proxies, 0); |
| } |
| |
| static void processProxyServers(Vector<ProxyServer>& proxyServers, CFArrayRef proxies, CFURLRef url) |
| { |
| CFIndex numProxies = CFArrayGetCount(proxies); |
| for (CFIndex i = 0; i < numProxies; ++i) { |
| CFDictionaryRef proxyDictionary = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(proxies, i)); |
| |
| ProxyServer::Type type = ProxyServer::Direct; |
| CFStringRef typeString = static_cast<CFStringRef>(CFDictionaryGetValue(proxyDictionary, kCFProxyTypeKey)); |
| |
| if (!url) { |
| // If we have no URL then we're processing an auto-configuration response. |
| // It isn't sensible to receive another auto-configured proxy in such a response. |
| ASSERT(!CFEqual(typeString, kCFProxyTypeAutoConfigurationURL)); |
| } |
| |
| if (CFEqual(typeString, kCFProxyTypeAutoConfigurationURL)) { |
| if (!url) |
| continue; |
| |
| // FIXME: We should restructure to allow this to happen asynchronously. |
| CFURLRef scriptURL = static_cast<CFURLRef>(CFDictionaryGetValue(proxyDictionary, kCFProxyAutoConfigurationURLKey)); |
| if (!scriptURL || CFGetTypeID(scriptURL) != CFURLGetTypeID()) |
| continue; |
| |
| CFStreamClientContext context = { 0, (void*)&proxyServers, 0, 0, 0 }; |
| RetainPtr<CFRunLoopSourceRef> runLoopSource = adoptCF(CFNetworkExecuteProxyAutoConfigurationURL(scriptURL, url, proxyAutoConfigurationResultCallback, &context)); |
| |
| CFStringRef privateRunLoopMode = CFSTR("com.apple.WebKit.ProxyAutoConfiguration"); |
| CFTimeInterval timeout = 5; |
| CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource.get(), privateRunLoopMode); |
| CFRunLoopRunInMode(privateRunLoopMode, timeout, 0); |
| CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource.get(), privateRunLoopMode); |
| CFRunLoopSourceInvalidate(runLoopSource.get()); |
| |
| // The proxyAutoConfigurationResultCallback has added any relevant ProxyServers to proxyServers. |
| continue; |
| } |
| |
| if (CFEqual(typeString, kCFProxyTypeNone)) { |
| proxyServers.append(ProxyServer(ProxyServer::Direct, String(), -1)); |
| continue; |
| } |
| |
| if (CFEqual(typeString, kCFProxyTypeHTTP)) |
| type = ProxyServer::HTTP; |
| else if (CFEqual(typeString, kCFProxyTypeHTTPS)) |
| type = ProxyServer::HTTPS; |
| else if (CFEqual(typeString, kCFProxyTypeSOCKS)) |
| type = ProxyServer::SOCKS; |
| else { |
| // We don't know how to handle this type. |
| continue; |
| } |
| |
| CFStringRef host = static_cast<CFStringRef>(CFDictionaryGetValue(proxyDictionary, kCFProxyHostNameKey)); |
| CFNumberRef port = static_cast<CFNumberRef>(CFDictionaryGetValue(proxyDictionary, kCFProxyPortNumberKey)); |
| SInt32 portValue; |
| CFNumberGetValue(port, kCFNumberSInt32Type, &portValue); |
| |
| proxyServers.append(ProxyServer(type, host, portValue)); |
| } |
| } |
| |
| static void addProxyServersForURL(Vector<ProxyServer>& proxyServers, const URL& url) |
| { |
| RetainPtr<CFDictionaryRef> proxySettings = adoptCF(CFNetworkCopySystemProxySettings()); |
| if (!proxySettings) |
| return; |
| |
| RetainPtr<CFURLRef> cfURL = url.createCFURL(); |
| RetainPtr<CFArrayRef> proxiesForURL = adoptCF(CFNetworkCopyProxiesForURL(cfURL.get(), proxySettings.get())); |
| if (!proxiesForURL) |
| return; |
| |
| processProxyServers(proxyServers, proxiesForURL.get(), cfURL.get()); |
| } |
| |
| Vector<ProxyServer> proxyServersForURL(const URL& url) |
| { |
| Vector<ProxyServer> proxyServers; |
| |
| addProxyServersForURL(proxyServers, url); |
| return proxyServers; |
| |
| } |
| |
| } // namespace WebCore |