1/*
2 * Copyright (C) 2008 Collin Jackson  <collinj@webkit.org>
3 * Copyright (C) 2009 Apple Inc. All Rights Reserved.
4 * Copyright (C) 2012 Igalia S.L.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "DNS.h"
30#include "DNSResolveQueue.h"
31
32#include "URL.h"
33#include "Timer.h"
34#include <wtf/HashSet.h>
35#include <wtf/MainThread.h>
36#include <wtf/RetainPtr.h>
37#include <wtf/StdLibExtras.h>
38#include <wtf/text/StringHash.h>
39
40#if PLATFORM(IOS)
41#include <CFNetwork/CFNetwork.h>
42#endif
43
44#if PLATFORM(WIN)
45#include "LoaderRunLoopCF.h"
46#include <CFNetwork/CFNetwork.h>
47#endif
48
49namespace WebCore {
50
51bool DNSResolveQueue::platformProxyIsEnabledInSystemPreferences()
52{
53    // Don't do DNS prefetch if proxies are involved. For many proxy types, the user agent is never exposed
54    // to the IP address during normal operation. Querying an internal DNS server may not help performance,
55    // as it doesn't necessarily look up the actual external IP. Also, if DNS returns a fake internal address,
56    // local caches may keep it even after re-connecting to another network.
57
58    RetainPtr<CFDictionaryRef> proxySettings = adoptCF(CFNetworkCopySystemProxySettings());
59    if (!proxySettings)
60        return false;
61
62    RetainPtr<CFURLRef> httpCFURL = URL(ParsedURLString, "http://example.com/").createCFURL();
63    RetainPtr<CFURLRef> httpsCFURL = URL(ParsedURLString, "https://example.com/").createCFURL();
64
65    RetainPtr<CFArrayRef> httpProxyArray = adoptCF(CFNetworkCopyProxiesForURL(httpCFURL.get(), proxySettings.get()));
66    RetainPtr<CFArrayRef> httpsProxyArray = adoptCF(CFNetworkCopyProxiesForURL(httpsCFURL.get(), proxySettings.get()));
67
68    CFIndex httpProxyCount = CFArrayGetCount(httpProxyArray.get());
69    CFIndex httpsProxyCount = CFArrayGetCount(httpsProxyArray.get());
70    if (httpProxyCount == 1 && CFEqual(CFDictionaryGetValue(static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(httpProxyArray.get(), 0)), kCFProxyTypeKey), kCFProxyTypeNone))
71        httpProxyCount = 0;
72    if (httpsProxyCount == 1 && CFEqual(CFDictionaryGetValue(static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(httpsProxyArray.get(), 0)), kCFProxyTypeKey), kCFProxyTypeNone))
73        httpsProxyCount = 0;
74
75    return httpProxyCount || httpsProxyCount;
76}
77
78static void clientCallback(CFHostRef theHost, CFHostInfoType, const CFStreamError*, void*)
79{
80    DNSResolveQueue::shared().decrementRequestCount(); // It's ok to call shared() from a secondary thread, the static variable has already been initialized by now.
81    CFRelease(theHost);
82}
83
84void DNSResolveQueue::platformResolve(const String& hostname)
85{
86    ASSERT(isMainThread());
87
88    RetainPtr<CFHostRef> host = adoptCF(CFHostCreateWithName(0, hostname.createCFString().get()));
89    if (!host) {
90        decrementRequestCount();
91        return;
92    }
93
94    CFHostClientContext context = { 0, 0, 0, 0, 0 };
95    CFHostRef leakedHost = host.leakRef(); // The host will be released from clientCallback().
96    Boolean result = CFHostSetClient(leakedHost, clientCallback, &context);
97    ASSERT_UNUSED(result, result);
98#if !PLATFORM(WIN)
99    CFHostScheduleWithRunLoop(leakedHost, CFRunLoopGetMain(), kCFRunLoopCommonModes);
100#else
101    // On Windows, we run a separate thread with CFRunLoop, which is where clientCallback will be called.
102    CFHostScheduleWithRunLoop(leakedHost, loaderRunLoop(), kCFRunLoopDefaultMode);
103#endif
104    CFHostStartInfoResolution(leakedHost, kCFHostAddresses, 0);
105}
106
107void prefetchDNS(const String& hostname)
108{
109    ASSERT(isMainThread());
110    if (hostname.isEmpty())
111        return;
112    DNSResolveQueue::shared().add(hostname);
113}
114
115}
116