diff options
author | Timur Pocheptsov <timur.pocheptsov@qt.io> | 2023-01-18 12:51:23 +0100 |
---|---|---|
committer | Timur Pocheptsov <timur.pocheptsov@qt.io> | 2023-01-30 20:10:25 +0100 |
commit | f932be58d56dd58d9a6c6f33054a32261ec5a932 (patch) | |
tree | 95173d98dd687e21e6a7df42e184495322479c04 | |
parent | ade96ff6446d4b0977d4e5f03b96b77ff8223b8b (diff) |
qnetworkproxy_mac: use API available both on iOS and macOS
To extract system proxies, the one we used previously, was not available
on iOS and thus we could not obtain system proxies there. Support is
limited - no such things, as SOCKS/FTP/etc. proxies, only PAC (auto
configuration), and HTTP/HTTPS. There are no keys to extract info
about HTTPS, so instead we'll use CFNetworkCopyProxiesForURL (
looks like this enables exclusion lists (which are hidden)
functionality and apparently from the system point of view HTTP/HTTPS
are the same.
Fixes: QTBUG-39869
Change-Id: I73af719a2e2b5cded706e6b3faa4b8eaa879352b
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
-rw-r--r-- | cmake/QtFrameworkHelpers.cmake | 1 | ||||
-rw-r--r-- | src/network/CMakeLists.txt | 16 | ||||
-rw-r--r-- | src/network/kernel/qnetworkproxy.cpp | 5 | ||||
-rw-r--r-- | src/network/kernel/qnetworkproxy_darwin.cpp (renamed from src/network/kernel/qnetworkproxy_mac.cpp) | 129 |
4 files changed, 110 insertions, 41 deletions
diff --git a/cmake/QtFrameworkHelpers.cmake b/cmake/QtFrameworkHelpers.cmake index f49423a6ce..1eed9df379 100644 --- a/cmake/QtFrameworkHelpers.cmake +++ b/cmake/QtFrameworkHelpers.cmake @@ -4,6 +4,7 @@ macro(qt_find_apple_system_frameworks) if(APPLE) qt_internal_find_apple_system_framework(FWAppKit AppKit) + qt_internal_find_apple_system_framework(FWCFNetwork CFNetwork) qt_internal_find_apple_system_framework(FWAssetsLibrary AssetsLibrary) qt_internal_find_apple_system_framework(FWAudioToolbox AudioToolbox) qt_internal_find_apple_system_framework(FWApplicationServices ApplicationServices) diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 20a8280c6d..61dc7784af 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -226,6 +226,18 @@ qt_internal_extend_target(Network CONDITION APPLE AND NOT UIKIT ${FWSystemConfiguration} ) +qt_internal_extend_target(Network CONDITION APPLE AND NOT UIKIT + LIBRARIES + ${FWCoreServices} + ${FWSystemConfiguration} +) + +qt_internal_extend_target(Network CONDITION APPLE + LIBRARIES + ${FWCFNetwork} +) + + qt_internal_extend_target(Network CONDITION IOS OR MACOS SOURCES kernel/qnetconmonitor_darwin.mm @@ -253,9 +265,9 @@ qt_internal_extend_target(Network CONDITION UIKIT kernel/qnetworkinterface_uikit_p.h ) -qt_internal_extend_target(Network CONDITION MACOS +qt_internal_extend_target(Network CONDITION APPLE SOURCES - kernel/qnetworkproxy_mac.cpp + kernel/qnetworkproxy_darwin.cpp ) qt_internal_extend_target(Network CONDITION QT_FEATURE_libproxy AND UNIX AND NOT MACOS diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp index a00b0031ac..62bba24bce 100644 --- a/src/network/kernel/qnetworkproxy.cpp +++ b/src/network/kernel/qnetworkproxy.cpp @@ -1466,7 +1466,7 @@ void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *fact Internet Explorer's settings and use them. On \macos, this function will obtain the proxy settings using the - SystemConfiguration framework from Apple. It will apply the FTP, + CFNetwork framework from Apple. It will apply the FTP, HTTP and HTTPS proxy configurations for queries that contain the protocol tag "ftp", "http" and "https", respectively. If the SOCKS proxy is enabled in that configuration, this function will use the @@ -1489,9 +1489,6 @@ void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *fact listed here. \list - \li On \macos, this function will ignore the Proxy Auto Configuration - settings, since it cannot execute the associated ECMAScript code. - \li On Windows platforms, this function may take several seconds to execute depending on the configuration of the user's system. \endlist diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_darwin.cpp index aa691090cc..431228c934 100644 --- a/src/network/kernel/qnetworkproxy_mac.cpp +++ b/src/network/kernel/qnetworkproxy_darwin.cpp @@ -14,6 +14,7 @@ #include <QtCore/QUrl> #include <QtCore/qendian.h> #include <QtCore/qstringlist.h> +#include <QtCore/qsystemdetection.h> #include "private/qcore_mac_p.h" /* @@ -32,11 +33,11 @@ * \li Bypass list (by default: *.local, 169.254/16) * \endlist * - * The matching configuration can be obtained by calling SCDynamicStoreCopyProxies - * (from <SystemConfiguration/SCDynamicStoreCopySpecific.h>). See + * The matching configuration can be obtained by calling CFNetworkCopySystemProxySettings() + * (from <CFNetwork/CFProxySupport.h>). See * Apple's documentation: * - * http://developer.apple.com/DOCUMENTATION/Networking/Reference/SysConfig/SCDynamicStoreCopySpecific/CompositePage.html#//apple_ref/c/func/SCDynamicStoreCopyProxies + * https://developer.apple.com/documentation/cfnetwork/1426754-cfnetworkcopysystemproxysettings?language=objc * */ @@ -46,13 +47,19 @@ using namespace Qt::StringLiterals; static bool isHostExcluded(CFDictionaryRef dict, const QString &host) { + Q_ASSERT(dict); + if (host.isEmpty()) return true; +#ifndef Q_OS_IOS + // On iOS all those keys are not available, and worse so - entries + // for HTTPS are not in the dictionary, but instead in some nested dictionary + // with undocumented keys/object types. bool isSimple = !host.contains(u'.') && !host.contains(u':'); CFNumberRef excludeSimples; if (isSimple && - (excludeSimples = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExcludeSimpleHostnames))) { + (excludeSimples = (CFNumberRef)CFDictionaryGetValue(dict, kCFNetworkProxiesExcludeSimpleHostnames))) { int enabled; if (CFNumberGetValue(excludeSimples, kCFNumberIntType, &enabled) && enabled) return true; @@ -63,7 +70,7 @@ static bool isHostExcluded(CFDictionaryRef dict, const QString &host) // not a simple host name // does it match the list of exclusions? - CFArrayRef exclusionList = (CFArrayRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExceptionsList); + CFArrayRef exclusionList = (CFArrayRef)CFDictionaryGetValue(dict, kCFNetworkProxiesExceptionsList); if (!exclusionList) return false; @@ -81,7 +88,9 @@ static bool isHostExcluded(CFDictionaryRef dict, const QString &host) return true; } } - +#else + Q_UNUSED(dict); +#endif // Q_OS_IOS // host was not excluded return false; } @@ -112,7 +121,6 @@ static QNetworkProxy proxyFromDictionary(CFDictionaryRef dict, QNetworkProxy::Pr return QNetworkProxy(); } - static QNetworkProxy proxyFromDictionary(CFDictionaryRef dict) { QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy; @@ -178,16 +186,43 @@ QCFType<CFStringRef> stringByAddingPercentEscapes(CFStringRef originalPath) return escaped.toCFString(); } -} // anon namespace +#ifdef Q_OS_IOS +QList<QNetworkProxy> proxiesForQueryUrl(CFDictionaryRef dict, const QUrl &url) +{ + Q_ASSERT(dict); + + const QCFType<CFURLRef> cfUrl = url.toCFURL(); + const QCFType<CFArrayRef> proxies = CFNetworkCopyProxiesForURL(cfUrl, dict); + Q_ASSERT(proxies); + + QList<QNetworkProxy> result; + const auto count = CFArrayGetCount(proxies); + if (!count) // Could be no proper proxy or host excluded. + return result; + + for (CFIndex i = 0; i < count; ++i) { + const void *obj = CFArrayGetValueAtIndex(proxies, i); + if (CFGetTypeID(obj) != CFDictionaryGetTypeID()) + continue; + const QNetworkProxy proxy = proxyFromDictionary(static_cast<CFDictionaryRef>(obj)); + if (proxy.type() == QNetworkProxy::NoProxy || proxy.type() == QNetworkProxy::DefaultProxy) + continue; + result << proxy; + } + + return result; +} +#endif // Q_OS_IOS +} // unnamed namespace. QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query) { QList<QNetworkProxy> result; // obtain a dictionary to the proxy settings: - const QCFType<CFDictionaryRef> dict = SCDynamicStoreCopyProxies(NULL); + const QCFType<CFDictionaryRef> dict = CFNetworkCopySystemProxySettings(); if (!dict) { - qWarning("QNetworkProxyFactory::systemProxyForQuery: SCDynamicStoreCopyProxies returned NULL"); + qWarning("QNetworkProxyFactory::systemProxyForQuery: CFNetworkCopySystemProxySettings returned nullptr"); return result; // failed } @@ -196,13 +231,13 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query) // is there a PAC enabled? If so, use it first. CFNumberRef pacEnabled; - if ((pacEnabled = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigEnable))) { + if ((pacEnabled = (CFNumberRef)CFDictionaryGetValue(dict, kCFNetworkProxiesProxyAutoConfigEnable))) { int enabled; if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) { // PAC is enabled // kSCPropNetProxiesProxyAutoConfigURLString returns the URL string // as entered in the system proxy configuration dialog - CFStringRef pacLocationSetting = (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString); + CFStringRef pacLocationSetting = (CFStringRef)CFDictionaryGetValue(dict, kCFNetworkProxiesProxyAutoConfigURLString); auto cfPacLocation = stringByAddingPercentEscapes(pacLocationSetting); QCFType<CFDataRef> pacData; QCFType<CFURLRef> pacUrl = CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, NULL); @@ -252,53 +287,77 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query) } } - // no PAC, decide which proxy we're looking for based on the query - bool isHttps = false; - QString protocol = query.protocolTag().toLower(); - + // No PAC, decide which proxy we're looking for based on the query // try the protocol-specific proxy + QString protocol = query.protocolTag().toLower(); QNetworkProxy protocolSpecificProxy; + if (protocol == "http"_L1) { + protocolSpecificProxy = + proxyFromDictionary(dict, QNetworkProxy::HttpProxy, + kCFNetworkProxiesHTTPEnable, + kCFNetworkProxiesHTTPProxy, + kCFNetworkProxiesHTTPPort); + } + + +#ifdef Q_OS_IOS + if (protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy + && protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy) { + // HTTP proxy is enabled (on iOS there is no separate HTTPS, though + // 'dict' contains deeply buried entries which are the same as HTTP. + result << protocolSpecificProxy; + } + + // TODO: check query.queryType()? It's possible, the exclude list + // did exclude it but above we added a proxy because HTTP proxy + // is found. We'll deal with such a situation later, since now NMI. + const auto proxiesForUrl = proxiesForQueryUrl(dict, query.url()); + for (const auto &proxy : proxiesForUrl) { + if (!result.contains(proxy)) + result << proxy; + } +#else + bool isHttps = false; if (protocol == "ftp"_L1) { protocolSpecificProxy = proxyFromDictionary(dict, QNetworkProxy::FtpCachingProxy, - kSCPropNetProxiesFTPEnable, - kSCPropNetProxiesFTPProxy, - kSCPropNetProxiesFTPPort); - } else if (protocol == "http"_L1) { - protocolSpecificProxy = - proxyFromDictionary(dict, QNetworkProxy::HttpProxy, - kSCPropNetProxiesHTTPEnable, - kSCPropNetProxiesHTTPProxy, - kSCPropNetProxiesHTTPPort); + kCFNetworkProxiesFTPEnable, + kCFNetworkProxiesFTPProxy, + kCFNetworkProxiesFTPPort); } else if (protocol == "https"_L1) { isHttps = true; protocolSpecificProxy = proxyFromDictionary(dict, QNetworkProxy::HttpProxy, - kSCPropNetProxiesHTTPSEnable, - kSCPropNetProxiesHTTPSProxy, - kSCPropNetProxiesHTTPSPort); + kCFNetworkProxiesHTTPSEnable, + kCFNetworkProxiesHTTPSProxy, + kCFNetworkProxiesHTTPSPort); } + if (protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy) result << protocolSpecificProxy; // let's add SOCKSv5 if present too QNetworkProxy socks5 = proxyFromDictionary(dict, QNetworkProxy::Socks5Proxy, - kSCPropNetProxiesSOCKSEnable, - kSCPropNetProxiesSOCKSProxy, - kSCPropNetProxiesSOCKSPort); + kCFNetworkProxiesSOCKSEnable, + kCFNetworkProxiesSOCKSProxy, + kCFNetworkProxiesSOCKSPort); if (socks5.type() != QNetworkProxy::DefaultProxy) result << socks5; // let's add the HTTPS proxy if present (and if we haven't added // yet) if (!isHttps) { - QNetworkProxy https = proxyFromDictionary(dict, QNetworkProxy::HttpProxy, - kSCPropNetProxiesHTTPSEnable, - kSCPropNetProxiesHTTPSProxy, - kSCPropNetProxiesHTTPSPort); + QNetworkProxy https; + https = proxyFromDictionary(dict, QNetworkProxy::HttpProxy, + kCFNetworkProxiesHTTPSEnable, + kCFNetworkProxiesHTTPSProxy, + kCFNetworkProxiesHTTPSPort); + + if (https.type() != QNetworkProxy::DefaultProxy && https != protocolSpecificProxy) result << https; } +#endif // !Q_OS_IOS return result; } |