summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2023-01-18 12:51:23 +0100
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2023-01-30 20:10:25 +0100
commitf932be58d56dd58d9a6c6f33054a32261ec5a932 (patch)
tree95173d98dd687e21e6a7df42e184495322479c04
parentade96ff6446d4b0977d4e5f03b96b77ff8223b8b (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.cmake1
-rw-r--r--src/network/CMakeLists.txt16
-rw-r--r--src/network/kernel/qnetworkproxy.cpp5
-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;
}