1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
|
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/proxy_resolution/proxy_resolver_apple.h"
#include <CFNetwork/CFProxySupport.h>
#include <CoreFoundation/CoreFoundation.h>
#include <memory>
#include "base/apple/foundation_util.h"
#include "base/apple/scoped_cftyperef.h"
#include "base/check.h"
#include "base/lazy_instance.h"
#include "base/memory/raw_ref.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "build/build_config.h"
#include "net/base/net_errors.h"
#include "net/proxy_resolution/proxy_chain_util_apple.h"
#include "net/proxy_resolution/proxy_info.h"
#include "net/proxy_resolution/proxy_list.h"
#include "net/proxy_resolution/proxy_resolver.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_IOS)
#include <CFNetwork/CFProxySupport.h>
#else
#include <CoreServices/CoreServices.h>
#endif
#if LEAK_SANITIZER
#include <sanitizer/lsan_interface.h>
#endif
namespace net {
class NetworkAnonymizationKey;
namespace {
// A lock shared by all ProxyResolverApple instances. It is used to synchronize
// the events of multiple CFNetworkExecuteProxyAutoConfigurationURL run loop
// sources. These events are:
// 1. Adding the source to the run loop.
// 2. Handling the source result.
// 3. Removing the source from the run loop.
static base::LazyInstance<base::Lock>::Leaky g_cfnetwork_pac_runloop_lock =
LAZY_INSTANCE_INITIALIZER;
// Forward declaration of the callback function used by the
// SynchronizedRunLoopObserver class.
void RunLoopObserverCallBackFunc(CFRunLoopObserverRef observer,
CFRunLoopActivity activity,
void* info);
// 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 != nullptr) == (error == nullptr));
CFTypeRef* result_ptr = reinterpret_cast<CFTypeRef*>(client);
DCHECK(result_ptr != nullptr);
DCHECK(*result_ptr == nullptr);
if (error != nullptr) {
*result_ptr = CFRetain(error);
} else {
*result_ptr = CFRetain(proxies);
}
CFRunLoopStop(CFRunLoopGetCurrent());
}
#pragma mark - SynchronizedRunLoopObserver
// A run loop observer that guarantees that no two run loop sources protected
// by the same lock will be fired concurrently in different threads.
// The observer does not prevent the parallel execution of the sources but only
// synchronizes the run loop events associated with the sources. In the context
// of proxy resolver, the observer is used to synchronize the execution of the
// callbacks function that handles the result of
// CFNetworkExecuteProxyAutoConfigurationURL execution.
class SynchronizedRunLoopObserver final {
public:
// Creates the instance of an observer that will synchronize the sources
// using a given |lock|.
SynchronizedRunLoopObserver(base::Lock& lock);
SynchronizedRunLoopObserver(const SynchronizedRunLoopObserver&) = delete;
SynchronizedRunLoopObserver& operator=(const SynchronizedRunLoopObserver&) =
delete;
// Destructor.
~SynchronizedRunLoopObserver();
// Adds the observer to the current run loop for a given run loop mode.
// This method should always be paired with |RemoveFromCurrentRunLoop|.
void AddToCurrentRunLoop(const CFStringRef mode);
// Removes the observer from the current run loop for a given run loop mode.
// This method should always be paired with |AddToCurrentRunLoop|.
void RemoveFromCurrentRunLoop(const CFStringRef mode);
// Callback function that is called when an observable run loop event occurs.
void RunLoopObserverCallBack(CFRunLoopObserverRef observer,
CFRunLoopActivity activity);
private:
// Lock to use to synchronize the run loop sources.
const raw_ref<base::Lock> lock_;
// Indicates whether the current observer holds the lock. It is used to
// avoid double locking and releasing.
bool lock_acquired_ = false;
// The underlying CFRunLoopObserverRef structure wrapped by this instance.
base::apple::ScopedCFTypeRef<CFRunLoopObserverRef> observer_;
// Validates that all methods of this class are executed on the same thread.
base::ThreadChecker thread_checker_;
};
SynchronizedRunLoopObserver::SynchronizedRunLoopObserver(base::Lock& lock)
: lock_(lock) {
CFRunLoopObserverContext observer_context = {0, this, nullptr, nullptr,
nullptr};
observer_.reset(CFRunLoopObserverCreate(
kCFAllocatorDefault,
kCFRunLoopBeforeSources | kCFRunLoopBeforeWaiting | kCFRunLoopExit, true,
0, RunLoopObserverCallBackFunc, &observer_context));
}
SynchronizedRunLoopObserver::~SynchronizedRunLoopObserver() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!lock_acquired_);
}
void SynchronizedRunLoopObserver::AddToCurrentRunLoop(const CFStringRef mode) {
DCHECK(thread_checker_.CalledOnValidThread());
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer_.get(), mode);
}
void SynchronizedRunLoopObserver::RemoveFromCurrentRunLoop(
const CFStringRef mode) {
DCHECK(thread_checker_.CalledOnValidThread());
CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer_.get(), mode);
}
void SynchronizedRunLoopObserver::RunLoopObserverCallBack(
CFRunLoopObserverRef observer,
CFRunLoopActivity activity) NO_THREAD_SAFETY_ANALYSIS {
DCHECK(thread_checker_.CalledOnValidThread());
// Acquire the lock when a source has been signaled and going to be fired.
// In the context of the proxy resolver that happens when the proxy for a
// given URL has been resolved and the callback function that handles the
// result is going to be fired.
// Release the lock when all source events have been handled.
//
// NO_THREAD_SAFETY_ANALYSIS: Runtime dependent locking.
switch (activity) {
case kCFRunLoopBeforeSources:
if (!lock_acquired_) {
lock_->Acquire();
lock_acquired_ = true;
}
break;
case kCFRunLoopBeforeWaiting:
case kCFRunLoopExit:
if (lock_acquired_) {
lock_acquired_ = false;
lock_->Release();
}
break;
}
}
void RunLoopObserverCallBackFunc(CFRunLoopObserverRef observer,
CFRunLoopActivity activity,
void* info) {
// Forward the call to the instance of SynchronizedRunLoopObserver
// that is associated with the current CF run loop observer.
SynchronizedRunLoopObserver* observerInstance =
(SynchronizedRunLoopObserver*)info;
observerInstance->RunLoopObserverCallBack(observer, activity);
}
#pragma mark - ProxyResolverApple
class ProxyResolverApple : public ProxyResolver {
public:
explicit ProxyResolverApple(const scoped_refptr<PacFileData>& script_data);
~ProxyResolverApple() override;
// ProxyResolver methods:
int GetProxyForURL(const GURL& url,
const NetworkAnonymizationKey& network_anonymization_key,
ProxyInfo* results,
CompletionOnceCallback callback,
std::unique_ptr<Request>* request,
const NetLogWithSource& net_log) override;
private:
const scoped_refptr<PacFileData> script_data_;
};
ProxyResolverApple::ProxyResolverApple(
const scoped_refptr<PacFileData>& script_data)
: script_data_(script_data) {}
ProxyResolverApple::~ProxyResolverApple() = default;
// Gets the proxy information for a query URL from a PAC. Implementation
// inspired by http://developer.apple.com/samplecode/CFProxySupportTool/
int ProxyResolverApple::GetProxyForURL(
const GURL& query_url,
const NetworkAnonymizationKey& network_anonymization_key,
ProxyInfo* results,
CompletionOnceCallback /*callback*/,
std::unique_ptr<Request>* /*request*/,
const NetLogWithSource& net_log) {
// OS X's system resolver does not support WebSocket URLs in proxy.pac, as of
// version 10.13.5. See https://crbug.com/862121.
GURL mutable_query_url = query_url;
if (query_url.SchemeIsWSOrWSS()) {
GURL::Replacements replacements;
replacements.SetSchemeStr(query_url.SchemeIsCryptographic() ? "https"
: "http");
mutable_query_url = query_url.ReplaceComponents(replacements);
}
base::apple::ScopedCFTypeRef<CFStringRef> query_ref(
base::SysUTF8ToCFStringRef(mutable_query_url.spec()));
base::apple::ScopedCFTypeRef<CFURLRef> query_url_ref(
CFURLCreateWithString(kCFAllocatorDefault, query_ref.get(), nullptr));
if (!query_url_ref.get())
return ERR_FAILED;
base::apple::ScopedCFTypeRef<CFStringRef> pac_ref(base::SysUTF8ToCFStringRef(
script_data_->type() == PacFileData::TYPE_AUTO_DETECT
? std::string()
: script_data_->url().spec()));
base::apple::ScopedCFTypeRef<CFURLRef> pac_url_ref(
CFURLCreateWithString(kCFAllocatorDefault, pac_ref.get(), nullptr));
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.
base::apple::ScopedCFTypeRef<CFDictionaryRef> empty_dictionary(
CFDictionaryCreate(nullptr, nullptr, nullptr, 0, nullptr, nullptr));
base::apple::ScopedCFTypeRef<CFArrayRef> dummy_result(
CFNetworkCopyProxiesForURL(query_url_ref.get(), empty_dictionary.get()));
// 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 = nullptr;
CFStreamClientContext context = {0, &result, nullptr, nullptr, nullptr};
base::apple::ScopedCFTypeRef<CFRunLoopSourceRef> runloop_source(
CFNetworkExecuteProxyAutoConfigurationURL(
pac_url_ref.get(), query_url_ref.get(), ResultCallback, &context));
#if LEAK_SANITIZER
// CFNetworkExecuteProxyAutoConfigurationURL leaks the returned
// CFRunLoopSourceRef. Filed as FB12170226.
__lsan_ignore_object(runloop_source.get());
#endif
if (!runloop_source)
return ERR_FAILED;
const CFStringRef private_runloop_mode =
CFSTR("org.chromium.ProxyResolverApple");
// Add the run loop observer to synchronize events of
// CFNetworkExecuteProxyAutoConfigurationURL sources. See the definition of
// |g_cfnetwork_pac_runloop_lock|.
SynchronizedRunLoopObserver observer(g_cfnetwork_pac_runloop_lock.Get());
observer.AddToCurrentRunLoop(private_runloop_mode);
// Make sure that no CFNetworkExecuteProxyAutoConfigurationURL sources
// are added to the run loop concurrently.
{
base::AutoLock lock(g_cfnetwork_pac_runloop_lock.Get());
CFRunLoopAddSource(CFRunLoopGetCurrent(), runloop_source.get(),
private_runloop_mode);
}
CFRunLoopRunInMode(private_runloop_mode, DBL_MAX, false);
// Make sure that no CFNetworkExecuteProxyAutoConfigurationURL sources
// are removed from the run loop concurrently.
{
base::AutoLock lock(g_cfnetwork_pac_runloop_lock.Get());
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runloop_source.get(),
private_runloop_mode);
}
observer.RemoveFromCurrentRunLoop(private_runloop_mode);
DCHECK(result);
if (CFGetTypeID(result) == CFErrorGetTypeID()) {
// TODO(avi): do something better than this
CFRelease(result);
return ERR_FAILED;
}
base::apple::ScopedCFTypeRef<CFArrayRef> proxy_array_ref(
base::apple::CFCastStrict<CFArrayRef>(result));
DCHECK(proxy_array_ref);
ProxyList proxy_list;
CFIndex proxy_array_count = CFArrayGetCount(proxy_array_ref.get());
for (CFIndex i = 0; i < proxy_array_count; ++i) {
CFDictionaryRef proxy_dictionary =
base::apple::CFCastStrict<CFDictionaryRef>(
CFArrayGetValueAtIndex(proxy_array_ref.get(), i));
DCHECK(proxy_dictionary);
// 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::apple::GetValueFromDictionary<CFStringRef>(
proxy_dictionary, kCFProxyTypeKey);
ProxyChain proxy_chain =
ProxyDictionaryToProxyChain(proxy_type, proxy_dictionary,
kCFProxyHostNameKey, kCFProxyPortNumberKey);
if (!proxy_chain.IsValid()) {
continue;
}
proxy_list.AddProxyChain(proxy_chain);
}
if (!proxy_list.IsEmpty())
results->UseProxyList(proxy_list);
// Else do nothing (results is already guaranteed to be in the default state).
return OK;
}
} // namespace
ProxyResolverFactoryApple::ProxyResolverFactoryApple()
: ProxyResolverFactory(false /*expects_pac_bytes*/) {
}
int ProxyResolverFactoryApple::CreateProxyResolver(
const scoped_refptr<PacFileData>& pac_script,
std::unique_ptr<ProxyResolver>* resolver,
CompletionOnceCallback callback,
std::unique_ptr<Request>* request) {
*resolver = std::make_unique<ProxyResolverApple>(pac_script);
return OK;
}
} // namespace net
|