| // Copyright 2010-2015, Google 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: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * 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. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT |
| // OWNER OR 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 "net/proxy_manager.h" |
| |
| #include <string> |
| |
| #ifdef OS_MACOSX |
| #include <CoreServices/CoreServices.h> |
| #include <SystemConfiguration/SystemConfiguration.h> |
| #include "base/scoped_cftyperef.h" |
| #include "base/mac_util.h" |
| #endif |
| |
| #include "base/port.h" |
| #include "base/logging.h" |
| #include "base/singleton.h" |
| |
| namespace mozc { |
| |
| #ifdef OS_MACOSX |
| namespace { |
| // Helper functions for proxy configuration for Mac. |
| |
| // Callback for CFNetworkExecuteProxyAutoConfigurationURL. client is a |
| // pointer to a CFTypeRef. This stashes either error or proxies in that |
| // location. |
| // The implementation is inspired by |
| // http://developer.apple.com/samplecode/CFProxySupportTool/ |
| static void PACResultCallback( |
| void * client, CFArrayRef proxies, CFErrorRef error) { |
| DCHECK((proxies == NULL && error != NULL) || |
| (proxies != NULL && error == NULL)); |
| CFTypeRef *result = static_cast<CFTypeRef *>(client); |
| |
| if (result != NULL && *result == NULL) { |
| if (error != NULL) { |
| *result = CFRetain(error); |
| } else { |
| *result = CFRetain(proxies); |
| } |
| } |
| |
| CFRunLoopStop(CFRunLoopGetCurrent()); |
| } |
| |
| // If the specified proxy is PAC, it fetches the PAC file using |
| // CFNetworkExecuteProxyAutoConfigurationURL() and apply it to cfurl, |
| // and then returns the actual proxy configuration dictionary. Otherwise, |
| // it just retains the specified proxy and then returns it. |
| // The implementation is inspired by |
| // http://developer.apple.com/samplecode/CFProxySupportTool/ |
| CFDictionaryRef RetainOrExpandPacFile(CFURLRef cfurl, CFDictionaryRef proxy) { |
| CFDictionaryRef final_proxy = NULL; |
| CFStringRef proxy_type = reinterpret_cast<CFStringRef>( |
| CFDictionaryGetValue(proxy, kCFProxyTypeKey)); |
| |
| // PAC file case. Currently it always call |
| // CFNetworkExecuteProxyAutoConfigurationURL() so it always ask to |
| // the pac URL server. However, it seems that the function seems to |
| // take care of caching in memory. So there are no additional |
| // latency problems here. |
| if (proxy_type != NULL && CFGetTypeID(proxy_type) == CFStringGetTypeID() && |
| CFEqual(proxy_type, kCFProxyTypeAutoConfigurationURL)) { |
| CFURLRef script_url = reinterpret_cast<CFURLRef>( |
| CFDictionaryGetValue(proxy, kCFProxyAutoConfigurationURLKey)); |
| if (script_url != NULL && CFGetTypeID(script_url) == CFURLGetTypeID()) { |
| CFTypeRef result = NULL; |
| CFStreamClientContext context = {0, &result, NULL, NULL, NULL}; |
| scoped_cftyperef<CFRunLoopSourceRef> runloop_source( |
| CFNetworkExecuteProxyAutoConfigurationURL( |
| script_url, cfurl, PACResultCallback, &context)); |
| const string label = MacUtil::GetLabelForSuffix("ProxyResolverMac"); |
| scoped_cftyperef<CFStringRef> private_runloop_mode( |
| CFStringCreateWithBytes( |
| NULL, reinterpret_cast<const UInt8 *>(label.data()), |
| label.size(), kCFStringEncodingUTF8, NULL)); |
| CFRunLoopAddSource( |
| CFRunLoopGetCurrent(), runloop_source.get(), |
| private_runloop_mode.get()); |
| CFRunLoopRunInMode(private_runloop_mode.get(), 1.0e10, false); |
| CFRunLoopRemoveSource( |
| CFRunLoopGetCurrent(), runloop_source.get(), |
| private_runloop_mode.get()); |
| |
| // resolving PAC succeeds |
| if (result != NULL && CFGetTypeID(result) == CFArrayGetTypeID() && |
| CFArrayGetCount(reinterpret_cast<CFArrayRef>(result)) > 0) { |
| final_proxy = reinterpret_cast<CFDictionaryRef>( |
| CFArrayGetValueAtIndex(reinterpret_cast<CFArrayRef>(result), 0)); |
| CFRetain(final_proxy); |
| } else { |
| LOG(WARNING) << "Failed to resolve PAC file. " |
| << "Possibly wrong PAC file is specified."; |
| } |
| if (result != NULL) { |
| CFRelease(result); |
| } |
| } |
| } |
| |
| // If configuration isn't PAC or resolving PAC fails, just returns |
| // the retained proxy. |
| if (final_proxy == NULL) { |
| final_proxy = reinterpret_cast<CFDictionaryRef>(CFRetain(proxy)); |
| } |
| |
| return final_proxy; |
| } |
| } // anonymous namespace |
| |
| // MacProxyManager is a proxy manager for Mac OSX. It uses |
| // CoreService API and SystemConfiguration API to obtain the current |
| // network configuration. |
| class MacProxyManager : public ProxyManagerInterface { |
| public: |
| bool GetProxyData(const string &url, string *hostdata, string *authdata) { |
| DCHECK(hostdata); |
| DCHECK(authdata); |
| scoped_cftyperef<CFDictionaryRef> proxy_settings( |
| SCDynamicStoreCopyProxies(NULL)); |
| if (!proxy_settings.Verify(CFDictionaryGetTypeID())) { |
| LOG(ERROR) << "Failed to create proxy setting"; |
| return false; |
| } |
| |
| scoped_cftyperef<CFURLRef> cfurl(CFURLCreateWithBytes( |
| NULL, reinterpret_cast<const UInt8 *>(url.data()), url.size(), |
| kCFStringEncodingUTF8, NULL)); |
| if (!cfurl.Verify(CFURLGetTypeID())) { |
| LOG(ERROR) << "Failed to create URL object from the specified URL"; |
| return false; |
| } |
| |
| scoped_cftyperef<CFArrayRef> proxies( |
| CFNetworkCopyProxiesForURL(cfurl.get(), proxy_settings.get())); |
| if (!proxies.Verify(CFArrayGetTypeID())) { |
| LOG(ERROR) << "Failed to get the proxies from the URL / proxy settings"; |
| return false; |
| } |
| |
| bool proxy_available = false; |
| if (CFArrayGetCount(proxies.get()) > 0) { |
| scoped_cftyperef<CFDictionaryRef> proxy( |
| RetainOrExpandPacFile(cfurl.get(), reinterpret_cast<CFDictionaryRef>( |
| CFArrayGetValueAtIndex(proxies.get(), 0)))); |
| |
| CFStringRef proxy_type = static_cast<CFStringRef>( |
| CFDictionaryGetValue(proxy.get(), kCFProxyTypeKey)); |
| if (proxy.Verify(CFDictionaryGetTypeID()) && |
| CFEqual(proxy_type, kCFProxyTypeHTTP)) { |
| scoped_cftyperef<CFTypeRef> host( |
| CFDictionaryGetValue(proxy.get(), kCFProxyHostNameKey), true); |
| scoped_cftyperef<CFTypeRef> port( |
| CFDictionaryGetValue(proxy.get(), kCFProxyPortNumberKey), true); |
| scoped_cftyperef<CFTypeRef> username( |
| CFDictionaryGetValue(proxy.get(), kCFProxyUsernameKey), true); |
| scoped_cftyperef<CFTypeRef> password( |
| CFDictionaryGetValue(proxy.get(), kCFProxyPasswordKey), true); |
| if (host.Verify(CFStringGetTypeID())) { |
| scoped_cftyperef<CFStringRef> host_desc; |
| if (port.Verify(CFNumberGetTypeID())) { |
| host_desc.reset(CFStringCreateWithFormat( |
| NULL, NULL, CFSTR("%@:%@"), host.get(), port.get())); |
| } else { |
| host_desc.reset(reinterpret_cast<CFStringRef>(host.get())); |
| CFRetain(host.get()); |
| } |
| if (host_desc.Verify(CFStringGetTypeID())) { |
| const char *hostdata_ptr = |
| CFStringGetCStringPtr(host_desc.get(), kCFStringEncodingUTF8); |
| if (hostdata_ptr != NULL) { |
| hostdata->assign(hostdata_ptr); |
| proxy_available = true; |
| } else { |
| proxy_available = false; |
| } |
| } else { |
| LOG(ERROR) << "Invalid proxy spec: no host is specified"; |
| } |
| } |
| if (proxy_available && |
| username.Verify(CFStringGetTypeID()) && |
| password.Verify(CFStringGetTypeID())) { |
| scoped_cftyperef<CFStringRef> auth_desc(CFStringCreateWithFormat( |
| NULL, NULL, CFSTR("%@:%@"), username.get(), password.get())); |
| if (auth_desc.Verify(CFStringGetTypeID())) { |
| const char *authdata_ptr = |
| CFStringGetCStringPtr(auth_desc.get(), kCFStringEncodingUTF8); |
| if (authdata_ptr != NULL) { |
| authdata->assign(authdata_ptr); |
| } |
| } |
| } |
| } |
| } |
| |
| return proxy_available; |
| } |
| }; |
| #endif // OS_MACOSX |
| |
| bool DummyProxyManager::GetProxyData( |
| const string &url, string *hostdata, string *authdata) { |
| return false; |
| } |
| |
| namespace { |
| |
| // This is only used for dependency injection |
| ProxyManagerInterface *g_proxy_manager = NULL; |
| |
| ProxyManagerInterface *GetProxyManager() { |
| if (g_proxy_manager == NULL) { |
| #ifdef OS_MACOSX |
| return Singleton<MacProxyManager>::get(); |
| #else |
| return Singleton<DummyProxyManager>::get(); |
| #endif |
| } else { |
| return g_proxy_manager; |
| } |
| } |
| } // anonymous namespace |
| |
| void ProxyManager::SetProxyManager( |
| ProxyManagerInterface *proxy_manager) { |
| g_proxy_manager = proxy_manager; |
| } |
| |
| bool ProxyManager::GetProxyData( |
| const string &url, string *hostdata, string *authdata) { |
| return GetProxyManager()->GetProxyData(url, hostdata, authdata); |
| } |
| |
| ProxyManagerInterface::ProxyManagerInterface() { |
| } |
| |
| ProxyManagerInterface::~ProxyManagerInterface() { |
| } |
| } // namespace mozc |