7#ifndef QT_NO_NETWORKPROXY
10#include <qstringlist.h>
11#include <qregularexpression.h>
13#include <qnetworkinterface.h>
15#include <qvarlengtharray.h>
19#include <qt_windows.h>
25using namespace Qt::StringLiterals;
29 wchar_t userName[UNLEN + 1] = L"";
31 if (GetUserNameW(userName, &size)) {
32 SID_NAME_USE type = SidTypeUser;
36 bool bRet = LookupAccountNameW(NULL, userName, NULL, &sidSize, NULL, &domainSize, &type);
37 if (bRet == FALSE && ERROR_INSUFFICIENT_BUFFER != GetLastError())
39 QVarLengthArray<BYTE, 68> buff(sidSize);
40 QVarLengthArray<
wchar_t, MAX_PATH> domainName(domainSize);
43 if (LookupAccountNameW(NULL, userName, buff.data(), &sidSize, domainName.data(), &domainSize, &type))
44 return type != SidTypeUser;
55 qsizetype space = source.indexOf(u' ', start);
56 qsizetype semicolon = source.indexOf(u';', start);
58 if (semicolon != -1 && (end == -1 || semicolon < end))
62 if (start != source.length())
63 list.append(source.mid(start));
67 list.append(source.mid(start, end - start));
73static bool isBypassed(
const QString &host,
const QStringList &bypassList)
78 bool isSimple = !host.contains(u'.') && !host.contains(u':');
80 QHostAddress ipAddress;
81 bool isIpAddress = ipAddress.setAddress(host);
84 if (isIpAddress && ipAddress.isLoopback())
88 for (
const QString &entry : bypassList) {
89 if (entry ==
"<local>"_L1) {
94 const auto ifaces = QNetworkInterface::allInterfaces();
95 for (
const QNetworkInterface &iface : ifaces) {
96 const auto netaddrs = iface.addressEntries();
97 for (
const QNetworkAddressEntry &netaddr : netaddrs) {
98 if (ipAddress.isInSubnet(netaddr.ip(), netaddr.prefixLength())) {
105 if (isIpAddress && ipAddress.isInSubnet(QHostAddress::parseSubnet(entry))) {
109 auto rx = QRegularExpression::fromWildcard(entry, Qt::CaseInsensitive);
110 if (rx.match(host).hasMatch())
121 QNetworkProxy::Capabilities requiredCaps;
122 switch (query.queryType()) {
123 case QNetworkProxyQuery::TcpSocket:
124 requiredCaps = QNetworkProxy::TunnelingCapability;
126 case QNetworkProxyQuery::UdpSocket:
127 requiredCaps = QNetworkProxy::UdpTunnelingCapability;
129 case QNetworkProxyQuery::SctpSocket:
130 requiredCaps = QNetworkProxy::SctpTunnelingCapability;
132 case QNetworkProxyQuery::TcpServer:
133 requiredCaps = QNetworkProxy::ListeningCapability;
135 case QNetworkProxyQuery::SctpServer:
136 requiredCaps = QNetworkProxy::SctpListeningCapability;
142 QList<QNetworkProxy> result;
143 for (
const QNetworkProxy &proxy : proxyList) {
144 if (proxy.capabilities() & requiredCaps)
145 result.append(proxy);
152 QList<QNetworkProxy> result;
153 for (
const QNetworkProxy &proxy : proxyList) {
155 for (
int i=0; i < result.count(); i++) {
156 if (proxy.hostName() == result.at(i).hostName()
157 && proxy.port() == result.at(i).port()) {
160 if (proxy.type() == QNetworkProxy::HttpProxy)
165 result.append(proxy);
181 QList<QNetworkProxy> result;
182 QHash<QString, QNetworkProxy> taggedProxies;
183 const QString requiredTag = query.protocolTag();
185 bool checkTags = !requiredTag.isEmpty()
186 && query.queryType() != QNetworkProxyQuery::TcpServer
187 && query.queryType() != QNetworkProxyQuery::SctpServer;
188 for (
const QString &entry : proxyList) {
189 qsizetype server = 0;
191 QNetworkProxy::ProxyType proxyType = QNetworkProxy::HttpProxy;
194 qsizetype pos = entry.indexOf(u'=');
196 QStringView protocolTag;
198 scheme = protocolTag = QStringView{entry}.left(pos);
201 pos = entry.indexOf(
"://"_L1, server);
203 scheme = QStringView{entry}.mid(server, pos - server);
207 if (!scheme.isEmpty()) {
208 if (scheme ==
"http"_L1 || scheme ==
"https"_L1) {
211 }
else if (scheme ==
"socks"_L1 || scheme ==
"socks5"_L1) {
212 proxyType = QNetworkProxy::Socks5Proxy;
214 }
else if (scheme ==
"ftp"_L1) {
215 proxyType = QNetworkProxy::FtpCachingProxy;
223 pos = entry.indexOf(u':', server);
226 uint value = QStringView{entry}.mid(pos + 1).toUInt(&ok);
227 if (!ok || value > 65535)
232 pos = entry.length();
235 result << QNetworkProxy(proxyType, entry.mid(server, pos - server), port);
236 if (!protocolTag.isEmpty())
237 taggedProxies.insert(protocolTag.toString(), result.constLast());
240 if (checkTags && taggedProxies.contains(requiredTag)) {
241 if (query.queryType() == QNetworkProxyQuery::UrlRequest) {
243 result.append(taggedProxies.value(requiredTag));
246 result.prepend(taggedProxies.value(requiredTag));
249 if (!checkTags || requiredTag !=
"http"_L1) {
253 if (httpProxy != httpsProxy && httpProxy.type() == QNetworkProxy::HttpProxy && httpsProxy.type() == QNetworkProxy::HttpProxy) {
254 for (
int i = 0; i < result.count(); i++) {
255 if (httpProxy == result.at(i))
256 result[i].setType(QNetworkProxy::HttpCachingProxy);
260 result = filterProxyListByCapabilities(result, query);
261 return removeDuplicateProxies(result);
265class QRegistryWatcher {
266 Q_DISABLE_COPY_MOVE(QRegistryWatcher)
268 QRegistryWatcher() =
default;
270 void addLocation(HKEY hive,
const QString& path)
273 if (RegOpenKeyEx(hive,
reinterpret_cast<
const wchar_t*>(path.utf16()), 0, KEY_READ, &openedKey) != ERROR_SUCCESS)
276 const DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
277 REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY;
280 HANDLE handle = CreateEvent(NULL,
true,
false, NULL);
281 if (RegNotifyChangeKeyValue(openedKey,
true, filter, handle,
true) != ERROR_SUCCESS) {
285 m_watchEvents.append(handle);
286 m_registryHandles.append(openedKey);
289 bool hasChanged()
const {
291 WaitForMultipleObjects(m_watchEvents.size(), m_watchEvents.data(),
false, 0) < WAIT_OBJECT_0 + m_watchEvents.size();
294 bool isEmpty()
const {
295 return m_watchEvents.isEmpty();
299 for (HANDLE event : std::as_const(m_watchEvents))
301 for (HKEY key : std::as_const(m_registryHandles))
304 m_watchEvents.clear();
305 m_registryHandles.clear();
308 ~QRegistryWatcher() {
313 QList<HANDLE> m_watchEvents;
314 QList<HKEY> m_registryHandles;
344QWindowsSystemProxy::QWindowsSystemProxy()
345 : hHttpSession(0), initialized(
false), functional(
false), isAutoConfig(
false)
347 defaultResult << QNetworkProxy::NoProxy;
353 WinHttpCloseHandle(hHttpSession);
358 autoConfigUrl.clear();
359 proxyServerList.clear();
361 defaultResult.clear();
362 defaultResult << QNetworkProxy::NoProxy;
369 bool proxySettingsChanged =
false;
370 proxySettingsChanged = proxySettingsWatcher.hasChanged();
378 proxySettingsWatcher.clear();
379 proxySettingsWatcher.addLocation(HKEY_CURRENT_USER, QStringLiteral(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
380 proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
381 proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral(
"Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
384 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyConfig;
385 const bool hasIEConfig = WinHttpGetIEProxyConfigForCurrentUser(&ieProxyConfig);
387 if (ieProxyConfig.lpszAutoConfigUrl) {
388 autoConfigUrl = QString::fromWCharArray(ieProxyConfig.lpszAutoConfigUrl);
389 GlobalFree(ieProxyConfig.lpszAutoConfigUrl);
391 if (ieProxyConfig.lpszProxy) {
395 proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(ieProxyConfig.lpszProxy));
396 GlobalFree(ieProxyConfig.lpszProxy);
398 if (ieProxyConfig.lpszProxyBypass) {
399 proxyBypass = splitSpaceSemicolon(QString::fromWCharArray(ieProxyConfig.lpszProxyBypass));
400 GlobalFree(ieProxyConfig.lpszProxyBypass);
405 (currentProcessIsService() && proxyServerList.isEmpty() && proxyBypass.isEmpty())) {
409 WINHTTP_PROXY_INFO proxyInfo;
410 if (WinHttpGetDefaultProxyConfiguration(&proxyInfo) &&
411 proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
415 proxyBypass = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxyBypass));
416 proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxy));
419 if (proxyInfo.lpszProxy)
420 GlobalFree(proxyInfo.lpszProxy);
421 if (proxyInfo.lpszProxyBypass)
422 GlobalFree(proxyInfo.lpszProxyBypass);
426 if (ieProxyConfig.fAutoDetect || !autoConfigUrl.isEmpty()) {
428 hHttpSession = WinHttpOpen(L"Qt System Proxy access/1.0",
429 WINHTTP_ACCESS_TYPE_NO_PROXY,
430 WINHTTP_NO_PROXY_NAME,
431 WINHTTP_NO_PROXY_BYPASS,
437 memset(&autoProxyOptions, 0,
sizeof autoProxyOptions);
438 autoProxyOptions.fAutoLogonIfChallenged =
false;
442 if (ieProxyConfig.fAutoDetect) {
443 autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
444 autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP |
445 WINHTTP_AUTO_DETECT_TYPE_DNS_A;
447 autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
448 autoProxyOptions.lpszAutoConfigUrl =
reinterpret_cast<LPCWSTR>(autoConfigUrl.utf16());
452 functional = isAutoConfig || !proxyServerList.isEmpty();
455QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(
const QNetworkProxyQuery &query)
457 QWindowsSystemProxy *sp = systemProxy();
459 return QList<QNetworkProxy>() << QNetworkProxy();
461 QMutexLocker locker(&sp->mutex);
464 return sp->defaultResult;
466 if (sp->isAutoConfig) {
467 WINHTTP_PROXY_INFO proxyInfo;
470 QUrl url = query.url();
474 if (url.scheme() ==
"file"_L1 || url.scheme() ==
"qrc"_L1)
475 return sp->defaultResult;
476 if (query.queryType() != QNetworkProxyQuery::UrlRequest) {
478 url.setScheme(
"https"_L1);
481 QString urlQueryString = url.toString();
482 if (urlQueryString.size() > 2083) {
485 qWarning(
"Proxy query URL too long for windows API, try with truncated URL");
486 urlQueryString = url.toString().left(2083);
489 bool getProxySucceeded = WinHttpGetProxyForUrl(sp->hHttpSession,
490 reinterpret_cast<LPCWSTR>(urlQueryString.utf16()),
491 &sp->autoProxyOptions,
493 DWORD getProxyError = GetLastError();
495 if (!getProxySucceeded
496 && (ERROR_WINHTTP_AUTODETECTION_FAILED == getProxyError)) {
498 if (sp->autoConfigUrl.isEmpty()) {
501 sp->isAutoConfig =
false;
504 sp->autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
505 sp->autoProxyOptions.lpszAutoConfigUrl =
506 reinterpret_cast<LPCWSTR>(sp->autoConfigUrl.utf16());
507 getProxySucceeded = WinHttpGetProxyForUrl(sp->hHttpSession,
508 reinterpret_cast<LPCWSTR>(urlQueryString.utf16()),
509 &sp->autoProxyOptions,
511 getProxyError = GetLastError();
515 if (!getProxySucceeded
516 && (ERROR_WINHTTP_LOGIN_FAILURE == getProxyError)) {
519 sp->autoProxyOptions.fAutoLogonIfChallenged = TRUE;
520 getProxySucceeded = WinHttpGetProxyForUrl(sp->hHttpSession,
521 reinterpret_cast<LPCWSTR>(urlQueryString.utf16()),
522 &sp->autoProxyOptions,
524 getProxyError = GetLastError();
527 if (!getProxySucceeded
528 && (ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT == getProxyError)) {
531 sp->isAutoConfig =
false;
534 if (getProxySucceeded) {
536 QString proxyBypass = QString::fromWCharArray(proxyInfo.lpszProxyBypass);
537 QStringList proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxy));
538 if (proxyInfo.lpszProxy)
539 GlobalFree(proxyInfo.lpszProxy);
540 if (proxyInfo.lpszProxyBypass)
541 GlobalFree(proxyInfo.lpszProxyBypass);
543 if (proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY)
544 return sp->defaultResult;
545 if (isBypassed(query.peerHostName(), splitSpaceSemicolon(proxyBypass)))
546 return sp->defaultResult;
547 return parseServerList(query, proxyServerList);
554 if (isBypassed(query.peerHostName(), sp->proxyBypass))
555 return sp->defaultResult;
557 QList<QNetworkProxy> result = parseServerList(query, sp->proxyServerList);
559 if (result.isEmpty())
560 return sp->defaultResult;
The QNetworkProxy class provides a network layer proxy.
QStringList proxyServerList
QList< QNetworkProxy > defaultResult
WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions
QRegistryWatcher proxySettingsWatcher
static QStringList splitSpaceSemicolon(const QString &source)
static QList< QNetworkProxy > removeDuplicateProxies(const QList< QNetworkProxy > &proxyList)
static bool isBypassed(const QString &host, const QStringList &bypassList)
static QList< QNetworkProxy > filterProxyListByCapabilities(const QList< QNetworkProxy > &proxyList, const QNetworkProxyQuery &query)
static QList< QNetworkProxy > parseServerList(const QNetworkProxyQuery &query, const QStringList &proxyList)
static bool currentProcessIsService()