| /* |
| * Copyright (C) 2008 Apple Inc. All rights reserved. |
| * Copyright (C) 2009, 2012 Igalia S.L. |
| * |
| * 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 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 |
| * 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 "DNSResolveQueueSoup.h" |
| |
| #if USE(SOUP) |
| |
| #include <libsoup/soup.h> |
| #include <wtf/CompletionHandler.h> |
| #include <wtf/Function.h> |
| #include <wtf/MainThread.h> |
| #include <wtf/NeverDestroyed.h> |
| #include <wtf/glib/GUniquePtr.h> |
| #include <wtf/text/CString.h> |
| |
| namespace WebCore { |
| |
| // Initially true to ensure prefetch stays disabled until we have proxy settings. |
| static bool isUsingHttpProxy = true; |
| static bool isUsingHttpsProxy = true; |
| |
| static bool didResolveProxy(char** uris) |
| { |
| // We have a list of possible proxies to use for the URI. If the first item in the list is |
| // direct:// (the usual case), then the user prefers not to use a proxy. This is similar to |
| // resolving hostnames: there could be many possibilities returned in order of preference, and |
| // if we're trying to connect we should attempt each one in order, but here we are not trying |
| // to connect, merely to decide whether a proxy "should" be used. |
| return uris && *uris && strcmp(*uris, "direct://"); |
| } |
| |
| static void didResolveProxy(GProxyResolver* resolver, GAsyncResult* result, bool* isUsingProxyType, bool* isUsingProxy) |
| { |
| GUniqueOutPtr<GError> error; |
| GUniquePtr<char*> uris(g_proxy_resolver_lookup_finish(resolver, result, &error.outPtr())); |
| if (error) { |
| WTFLogAlways("Error determining system proxy settings: %s", error->message); |
| return; |
| } |
| |
| *isUsingProxyType = didResolveProxy(uris.get()); |
| *isUsingProxy = isUsingHttpProxy || isUsingHttpsProxy; |
| } |
| |
| static void proxyResolvedForHttpUriCallback(GObject* source, GAsyncResult* result, void* userData) |
| { |
| didResolveProxy(G_PROXY_RESOLVER(source), result, &isUsingHttpProxy, static_cast<bool*>(userData)); |
| } |
| |
| static void proxyResolvedForHttpsUriCallback(GObject* source, GAsyncResult* result, void* userData) |
| { |
| didResolveProxy(G_PROXY_RESOLVER(source), result, &isUsingHttpsProxy, static_cast<bool*>(userData)); |
| } |
| |
| Function<SoupSession*()>& globalDefaultSoupSessionAccessor() |
| { |
| static NeverDestroyed<Function<SoupSession*()>> accessor; |
| return accessor.get(); |
| } |
| |
| void DNSResolveQueueSoup::setGlobalDefaultSoupSessionAccessor(Function<SoupSession*()>&& accessor) |
| { |
| globalDefaultSoupSessionAccessor() = WTFMove(accessor); |
| } |
| |
| void DNSResolveQueueSoup::updateIsUsingProxy() |
| { |
| GRefPtr<GProxyResolver> resolver; |
| g_object_get(globalDefaultSoupSessionAccessor()(), "proxy-resolver", &resolver.outPtr(), nullptr); |
| ASSERT(resolver); |
| |
| g_proxy_resolver_lookup_async(resolver.get(), "http://example.com/", nullptr, proxyResolvedForHttpUriCallback, &m_isUsingProxy); |
| g_proxy_resolver_lookup_async(resolver.get(), "https://example.com/", nullptr, proxyResolvedForHttpsUriCallback, &m_isUsingProxy); |
| } |
| |
| static void resolvedCallback(SoupAddress*, guint, void*) |
| { |
| DNSResolveQueue::singleton().decrementRequestCount(); |
| } |
| |
| static void resolvedWithObserverCallback(SoupAddress* address, guint status, void* data) |
| { |
| ASSERT(data); |
| auto* resolveQueue = static_cast<DNSResolveQueueSoup*>(data); |
| |
| uint64_t identifier = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(address), "identifier")); |
| |
| auto completionAndCancelHandlers = resolveQueue->takeCompletionAndCancelHandlers(identifier); |
| |
| if (!completionAndCancelHandlers) |
| return; |
| |
| auto completionHandler = WTFMove(completionAndCancelHandlers.get()->first); |
| |
| if (status != SOUP_STATUS_OK) { |
| DNSError error = DNSError::Unknown; |
| |
| switch (status) { |
| case SOUP_STATUS_CANT_RESOLVE: |
| error = DNSError::CannotResolve; |
| break; |
| case SOUP_STATUS_CANCELLED: |
| error = DNSError::Cancelled; |
| break; |
| case SOUP_STATUS_OK: |
| default: |
| ASSERT_NOT_REACHED(); |
| }; |
| |
| completionHandler(makeUnexpected(error)); |
| return; |
| } |
| |
| if (!soup_address_is_resolved(address)) { |
| completionHandler(makeUnexpected(DNSError::Unknown)); |
| return; |
| } |
| |
| Vector<WebCore::IPAddress> addresses; |
| addresses.reserveInitialCapacity(1); |
| int len; |
| // FIXME: Support multiple addresses, IPv6 and IPv4. |
| auto* ipAddress = soup_address_get_sockaddr(address, &len); |
| if (ipAddress) { |
| if (auto address = IPAddress::fromSockAddrIn6(reinterpret_cast<const struct sockaddr_in6&>(*ipAddress))) |
| addresses.uncheckedAppend(*address); |
| } |
| if (addresses.isEmpty()) { |
| completionHandler(makeUnexpected(WebCore::DNSError::CannotResolve)); |
| return; |
| } |
| |
| completionHandler(addresses); |
| } |
| |
| std::unique_ptr<DNSResolveQueueSoup::CompletionAndCancelHandlers> DNSResolveQueueSoup::takeCompletionAndCancelHandlers(uint64_t identifier) |
| { |
| ASSERT(isMainThread()); |
| |
| auto completionAndCancelHandlers = m_completionAndCancelHandlers.take(identifier); |
| |
| if (!completionAndCancelHandlers) |
| return nullptr; |
| |
| return completionAndCancelHandlers; |
| } |
| |
| void DNSResolveQueueSoup::removeCancelAndCompletionHandler(uint64_t identifier) |
| { |
| ASSERT(isMainThread()); |
| |
| m_completionAndCancelHandlers.remove(identifier); |
| } |
| |
| void DNSResolveQueueSoup::platformResolve(const String& hostname) |
| { |
| ASSERT(isMainThread()); |
| |
| soup_session_prefetch_dns(globalDefaultSoupSessionAccessor()(), hostname.utf8().data(), nullptr, resolvedCallback, nullptr); |
| } |
| |
| void DNSResolveQueueSoup::resolve(const String& hostname, uint64_t identifier, DNSCompletionHandler&& completionHandler) |
| { |
| ASSERT(isMainThread()); |
| |
| auto address = adoptGRef(soup_address_new(hostname.utf8().data(), 0)); |
| auto cancellable = adoptGRef(g_cancellable_new()); |
| soup_address_resolve_async(address.get(), soup_session_get_async_context(globalDefaultSoupSessionAccessor()()), cancellable.get(), resolvedWithObserverCallback, this); |
| |
| g_object_set_data(G_OBJECT(address.get()), "identifier", GUINT_TO_POINTER(identifier)); |
| |
| m_completionAndCancelHandlers.add(identifier, makeUniqueWithoutFastMallocCheck<DNSResolveQueueSoup::CompletionAndCancelHandlers>(WTFMove(completionHandler), WTFMove(cancellable))); |
| } |
| |
| void DNSResolveQueueSoup::stopResolve(uint64_t identifier) |
| { |
| ASSERT(isMainThread()); |
| |
| if (auto completionAndCancelHandler = m_completionAndCancelHandlers.take(identifier)) { |
| g_cancellable_cancel(completionAndCancelHandler.get()->second.get()); |
| completionAndCancelHandler.get()->first(makeUnexpected(DNSError::Cancelled)); |
| } |
| } |
| |
| } |
| |
| #endif |