1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qnetworkproxy.h"
41
42 #ifndef QT_NO_NETWORKPROXY
43
44 #include <qmutex.h>
45 #include <qstringlist.h>
46 #include <qregexp.h>
47 #include <qurl.h>
48 #include <private/qsystemlibrary_p.h>
49 #include <qnetworkinterface.h>
50 #include <qdebug.h>
51
52 #include <string.h>
53 #include <qt_windows.h>
54 #include <wininet.h>
55 #include <lmcons.h>
56
57 /*
58 * Information on the WinHTTP DLL:
59 * http://msdn.microsoft.com/en-us/library/aa384122(VS.85).aspx example for WPAD
60 *
61 * http://msdn.microsoft.com/en-us/library/aa384097(VS.85).aspx WinHttpGetProxyForUrl
62 * http://msdn.microsoft.com/en-us/library/aa384096(VS.85).aspx WinHttpGetIEProxyConfigForCurrentUs
63 * http://msdn.microsoft.com/en-us/library/aa384095(VS.85).aspx WinHttpGetDefaultProxyConfiguration
64 */
65
66 // We don't want to include winhttp.h because that's not
67 // present in some Windows SDKs (I don't know why)
68 // So, instead, copy the definitions here
69
70 typedef struct {
71 DWORD dwFlags;
72 DWORD dwAutoDetectFlags;
73 LPCWSTR lpszAutoConfigUrl;
74 LPVOID lpvReserved;
75 DWORD dwReserved;
76 BOOL fAutoLogonIfChallenged;
77 } WINHTTP_AUTOPROXY_OPTIONS;
78
79 typedef struct {
80 DWORD dwAccessType;
81 LPWSTR lpszProxy;
82 LPWSTR lpszProxyBypass;
83 } WINHTTP_PROXY_INFO;
84
85 typedef struct {
86 BOOL fAutoDetect;
87 LPWSTR lpszAutoConfigUrl;
88 LPWSTR lpszProxy;
89 LPWSTR lpszProxyBypass;
90 } WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
91
92 #define WINHTTP_AUTOPROXY_AUTO_DETECT 0x00000001
93 #define WINHTTP_AUTOPROXY_CONFIG_URL 0x00000002
94
95 #define WINHTTP_AUTO_DETECT_TYPE_DHCP 0x00000001
96 #define WINHTTP_AUTO_DETECT_TYPE_DNS_A 0x00000002
97
98 #define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0
99 #define WINHTTP_ACCESS_TYPE_NO_PROXY 1
100 #define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3
101
102 #define WINHTTP_NO_PROXY_NAME NULL
103 #define WINHTTP_NO_PROXY_BYPASS NULL
104
105 #define WINHTTP_ERROR_BASE 12000
106 #define ERROR_WINHTTP_LOGIN_FAILURE (WINHTTP_ERROR_BASE + 15)
107 #define ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT (WINHTTP_ERROR_BASE + 167)
108 #define ERROR_WINHTTP_AUTODETECTION_FAILED (WINHTTP_ERROR_BASE + 180)
109
110 QT_BEGIN_NAMESPACE
111
112 typedef BOOL (WINAPI * PtrWinHttpGetProxyForUrl)(HINTERNET, LPCWSTR, WINHTTP_AUTOPROXY_OPTIONS*, WINHTTP_PROXY_INFO*);
113 typedef HINTERNET (WINAPI * PtrWinHttpOpen)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR,DWORD);
114 typedef BOOL (WINAPI * PtrWinHttpGetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*);
115 typedef BOOL (WINAPI * PtrWinHttpGetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*);
116 typedef BOOL (WINAPI * PtrWinHttpCloseHandle)(HINTERNET);
117 typedef BOOL (WINAPI * PtrCloseServiceHandle)(SC_HANDLE hSCObject);
118 static PtrWinHttpGetProxyForUrl ptrWinHttpGetProxyForUrl = 0;
119 static PtrWinHttpOpen ptrWinHttpOpen = 0;
120 static PtrWinHttpGetDefaultProxyConfiguration ptrWinHttpGetDefaultProxyConfiguration = 0;
121 static PtrWinHttpGetIEProxyConfigForCurrentUser ptrWinHttpGetIEProxyConfigForCurrentUser = 0;
122 static PtrWinHttpCloseHandle ptrWinHttpCloseHandle = 0;
123
124
currentProcessIsService()125 static bool currentProcessIsService()
126 {
127 typedef BOOL (WINAPI *PtrGetUserName)(LPTSTR lpBuffer, LPDWORD lpnSize);
128 typedef BOOL (WINAPI *PtrLookupAccountName)(LPCTSTR lpSystemName, LPCTSTR lpAccountName, PSID Sid,
129 LPDWORD cbSid, LPTSTR ReferencedDomainName, LPDWORD cchReferencedDomainName, PSID_NAME_USE peUse);
130 static PtrGetUserName ptrGetUserName = (PtrGetUserName)QSystemLibrary::resolve(QLatin1String("Advapi32"), "GetUserNameW");
131 static PtrLookupAccountName ptrLookupAccountName = (PtrLookupAccountName)QSystemLibrary::resolve(QLatin1String("Advapi32"), "LookupAccountNameW");
132
133 if (ptrGetUserName && ptrLookupAccountName) {
134 wchar_t userName[UNLEN + 1] = L"";
135 DWORD size = UNLEN;
136 if (ptrGetUserName(userName, &size)) {
137 SID_NAME_USE type = SidTypeUser;
138 DWORD sidSize = 0;
139 DWORD domainSize = 0;
140 // first call is to get the correct size
141 bool bRet = ptrLookupAccountName(NULL, userName, NULL, &sidSize, NULL, &domainSize, &type);
142 if (bRet == FALSE && ERROR_INSUFFICIENT_BUFFER != GetLastError())
143 return false;
144 QVarLengthArray<BYTE, 68> buff(sidSize);
145 QVarLengthArray<wchar_t, MAX_PATH> domainName(domainSize);
146 // second call to LookupAccountNameW actually gets the SID
147 // both the pointer to the buffer and the pointer to the domain name should not be NULL
148 if (ptrLookupAccountName(NULL, userName, buff.data(), &sidSize, domainName.data(), &domainSize, &type))
149 return type != SidTypeUser; //returns true if the current user is not a user
150 }
151 }
152 return false;
153 }
154
splitSpaceSemicolon(const QString & source)155 static QStringList splitSpaceSemicolon(const QString &source)
156 {
157 QStringList list;
158 int start = 0;
159 int end;
160 while (true) {
161 int space = source.indexOf(QLatin1Char(' '), start);
162 int semicolon = source.indexOf(QLatin1Char(';'), start);
163 end = space;
164 if (semicolon != -1 && (end == -1 || semicolon < end))
165 end = semicolon;
166
167 if (end == -1) {
168 if (start != source.length())
169 list.append(source.mid(start));
170 return list;
171 }
172 if (start != end)
173 list.append(source.mid(start, end - start));
174 start = end + 1;
175 }
176 return list;
177 }
178
isBypassed(const QString & host,const QStringList & bypassList)179 static bool isBypassed(const QString &host, const QStringList &bypassList)
180 {
181 if (host.isEmpty())
182 return false;
183
184 bool isSimple = !host.contains(QLatin1Char('.')) && !host.contains(QLatin1Char(':'));
185
186 QHostAddress ipAddress;
187 bool isIpAddress = ipAddress.setAddress(host);
188
189 // always exclude loopback
190 if (isIpAddress && ipAddress.isLoopback())
191 return true;
192
193 // does it match the list of exclusions?
194 for (const QString &entry : bypassList) {
195 if (entry == QLatin1String("<local>")) {
196 if (isSimple)
197 return true;
198 if (isIpAddress) {
199 //exclude all local subnets
200 const auto ifaces = QNetworkInterface::allInterfaces();
201 for (const QNetworkInterface &iface : ifaces) {
202 const auto netaddrs = iface.addressEntries();
203 for (const QNetworkAddressEntry &netaddr : netaddrs) {
204 if (ipAddress.isInSubnet(netaddr.ip(), netaddr.prefixLength())) {
205 return true;
206 }
207 }
208 }
209 }
210 }
211 if (isIpAddress && ipAddress.isInSubnet(QHostAddress::parseSubnet(entry))) {
212 return true; // excluded
213 } else {
214 // do wildcard matching
215 QRegExp rx(entry, Qt::CaseInsensitive, QRegExp::Wildcard);
216 if (rx.exactMatch(host))
217 return true;
218 }
219 }
220
221 // host was not excluded
222 return false;
223 }
224
filterProxyListByCapabilities(const QList<QNetworkProxy> & proxyList,const QNetworkProxyQuery & query)225 static QList<QNetworkProxy> filterProxyListByCapabilities(const QList<QNetworkProxy> &proxyList, const QNetworkProxyQuery &query)
226 {
227 QNetworkProxy::Capabilities requiredCaps;
228 switch (query.queryType()) {
229 case QNetworkProxyQuery::TcpSocket:
230 requiredCaps = QNetworkProxy::TunnelingCapability;
231 break;
232 case QNetworkProxyQuery::UdpSocket:
233 requiredCaps = QNetworkProxy::UdpTunnelingCapability;
234 break;
235 case QNetworkProxyQuery::SctpSocket:
236 requiredCaps = QNetworkProxy::SctpTunnelingCapability;
237 break;
238 case QNetworkProxyQuery::TcpServer:
239 requiredCaps = QNetworkProxy::ListeningCapability;
240 break;
241 case QNetworkProxyQuery::SctpServer:
242 requiredCaps = QNetworkProxy::SctpListeningCapability;
243 break;
244 default:
245 return proxyList;
246 break;
247 }
248 QList<QNetworkProxy> result;
249 for (const QNetworkProxy &proxy : proxyList) {
250 if (proxy.capabilities() & requiredCaps)
251 result.append(proxy);
252 }
253 return result;
254 }
255
removeDuplicateProxies(const QList<QNetworkProxy> & proxyList)256 static QList<QNetworkProxy> removeDuplicateProxies(const QList<QNetworkProxy> &proxyList)
257 {
258 QList<QNetworkProxy> result;
259 for (const QNetworkProxy &proxy : proxyList) {
260 bool append = true;
261 for (int i=0; i < result.count(); i++) {
262 if (proxy.hostName() == result.at(i).hostName()
263 && proxy.port() == result.at(i).port()) {
264 append = false;
265 // HttpProxy trumps FtpCachingProxy or HttpCachingProxy on the same host/port
266 if (proxy.type() == QNetworkProxy::HttpProxy)
267 result[i] = proxy;
268 }
269 }
270 if (append)
271 result.append(proxy);
272 }
273 return result;
274 }
275
parseServerList(const QNetworkProxyQuery & query,const QStringList & proxyList)276 static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, const QStringList &proxyList)
277 {
278 // Reference documentation from Microsoft:
279 // http://msdn.microsoft.com/en-us/library/aa383912(VS.85).aspx
280 //
281 // According to the website, the proxy server list is
282 // one or more of the space- or semicolon-separated strings in the format:
283 // ([<scheme>=][<scheme>"://"]<server>[":"<port>])
284 // The first scheme relates to the protocol tag
285 // The second scheme, if present, overrides the proxy type
286
287 QList<QNetworkProxy> result;
288 QHash<QString, QNetworkProxy> taggedProxies;
289 const QString requiredTag = query.protocolTag();
290 // windows tags are only for clients
291 bool checkTags = !requiredTag.isEmpty()
292 && query.queryType() != QNetworkProxyQuery::TcpServer
293 && query.queryType() != QNetworkProxyQuery::SctpServer;
294 for (const QString &entry : proxyList) {
295 int server = 0;
296
297 QNetworkProxy::ProxyType proxyType = QNetworkProxy::HttpProxy;
298 quint16 port = 8080;
299
300 int pos = entry.indexOf(QLatin1Char('='));
301 QStringRef scheme;
302 QStringRef protocolTag;
303 if (pos != -1) {
304 scheme = protocolTag = entry.leftRef(pos);
305 server = pos + 1;
306 }
307 pos = entry.indexOf(QLatin1String("://"), server);
308 if (pos != -1) {
309 scheme = entry.midRef(server, pos - server);
310 server = pos + 3;
311 }
312
313 if (!scheme.isEmpty()) {
314 if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) {
315 // no-op
316 // defaults are above
317 } else if (scheme == QLatin1String("socks") || scheme == QLatin1String("socks5")) {
318 proxyType = QNetworkProxy::Socks5Proxy;
319 port = 1080;
320 } else if (scheme == QLatin1String("ftp")) {
321 proxyType = QNetworkProxy::FtpCachingProxy;
322 port = 2121;
323 } else {
324 // unknown proxy type
325 continue;
326 }
327 }
328
329 pos = entry.indexOf(QLatin1Char(':'), server);
330 if (pos != -1) {
331 bool ok;
332 uint value = entry.midRef(pos + 1).toUInt(&ok);
333 if (!ok || value > 65535)
334 continue; // invalid port number
335
336 port = value;
337 } else {
338 pos = entry.length();
339 }
340
341 result << QNetworkProxy(proxyType, entry.mid(server, pos - server), port);
342 if (!protocolTag.isEmpty())
343 taggedProxies.insert(protocolTag.toString(), result.constLast());
344 }
345
346 if (checkTags && taggedProxies.contains(requiredTag)) {
347 if (query.queryType() == QNetworkProxyQuery::UrlRequest) {
348 result.clear();
349 result.append(taggedProxies.value(requiredTag));
350 return result;
351 } else {
352 result.prepend(taggedProxies.value(requiredTag));
353 }
354 }
355 if (!checkTags || requiredTag != QLatin1String("http")) {
356 // if there are different http proxies for http and https, prefer the https one (more likely to be capable of CONNECT)
357 QNetworkProxy httpProxy = taggedProxies.value(QLatin1String("http"));
358 QNetworkProxy httpsProxy = taggedProxies.value(QLatin1String("http"));
359 if (httpProxy != httpsProxy && httpProxy.type() == QNetworkProxy::HttpProxy && httpsProxy.type() == QNetworkProxy::HttpProxy) {
360 for (int i = 0; i < result.count(); i++) {
361 if (httpProxy == result.at(i))
362 result[i].setType(QNetworkProxy::HttpCachingProxy);
363 }
364 }
365 }
366 result = filterProxyListByCapabilities(result, query);
367 return removeDuplicateProxies(result);
368 }
369
370 #if !defined(Q_OS_WINRT)
371 namespace {
372 class QRegistryWatcher {
373 Q_DISABLE_COPY_MOVE(QRegistryWatcher)
374 public:
375 QRegistryWatcher() = default;
376
addLocation(HKEY hive,const QString & path)377 void addLocation(HKEY hive, const QString& path)
378 {
379 HKEY openedKey;
380 if (RegOpenKeyEx(hive, reinterpret_cast<const wchar_t*>(path.utf16()), 0, KEY_READ, &openedKey) != ERROR_SUCCESS)
381 return;
382
383 const DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
384 REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY;
385
386 // Watch the registry key for a change of value.
387 HANDLE handle = CreateEvent(NULL, true, false, NULL);
388 if (RegNotifyChangeKeyValue(openedKey, true, filter, handle, true) != ERROR_SUCCESS) {
389 CloseHandle(handle);
390 return;
391 }
392 m_watchEvents.append(handle);
393 m_registryHandles.append(openedKey);
394 }
395
hasChanged() const396 bool hasChanged() const {
397 return !isEmpty() &&
398 WaitForMultipleObjects(m_watchEvents.size(), m_watchEvents.data(), false, 0) < WAIT_OBJECT_0 + m_watchEvents.size();
399 }
400
isEmpty() const401 bool isEmpty() const {
402 return m_watchEvents.isEmpty();
403 }
404
clear()405 void clear() {
406 for (HANDLE event : qAsConst(m_watchEvents))
407 CloseHandle(event);
408 for (HKEY key : qAsConst(m_registryHandles))
409 RegCloseKey(key);
410
411 m_watchEvents.clear();
412 m_registryHandles.clear();
413 }
414
~QRegistryWatcher()415 ~QRegistryWatcher() {
416 clear();
417 }
418
419 private:
420 QVector<HANDLE> m_watchEvents;
421 QVector<HKEY> m_registryHandles;
422 };
423 } // namespace
424 #endif // !defined(Q_OS_WINRT)
425
426 class QWindowsSystemProxy
427 {
428 Q_DISABLE_COPY_MOVE(QWindowsSystemProxy)
429 public:
430 QWindowsSystemProxy();
431 ~QWindowsSystemProxy();
432 void init();
433 void reset();
434
435 QMutex mutex;
436
437 HINTERNET hHttpSession;
438 WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions;
439
440 QString autoConfigUrl;
441 QStringList proxyServerList;
442 QStringList proxyBypass;
443 QList<QNetworkProxy> defaultResult;
444 #if !defined(Q_OS_WINRT)
445 QRegistryWatcher proxySettingsWatcher;
446 #endif
447 bool initialized;
448 bool functional;
449 bool isAutoConfig;
450 };
451
Q_GLOBAL_STATIC(QWindowsSystemProxy,systemProxy)452 Q_GLOBAL_STATIC(QWindowsSystemProxy, systemProxy)
453
454 QWindowsSystemProxy::QWindowsSystemProxy()
455 : hHttpSession(0), initialized(false), functional(false), isAutoConfig(false)
456 {
457 defaultResult << QNetworkProxy::NoProxy;
458 }
459
~QWindowsSystemProxy()460 QWindowsSystemProxy::~QWindowsSystemProxy()
461 {
462 if (hHttpSession)
463 ptrWinHttpCloseHandle(hHttpSession);
464 }
465
reset()466 void QWindowsSystemProxy::reset()
467 {
468 autoConfigUrl.clear();
469 proxyServerList.clear();
470 proxyBypass.clear();
471 defaultResult.clear();
472 defaultResult << QNetworkProxy::NoProxy;
473 functional = false;
474 isAutoConfig = false;
475 }
476
init()477 void QWindowsSystemProxy::init()
478 {
479 bool proxySettingsChanged = false;
480 #if !defined(Q_OS_WINRT)
481 proxySettingsChanged = proxySettingsWatcher.hasChanged();
482 #endif
483
484 if (initialized && !proxySettingsChanged)
485 return;
486 initialized = true;
487
488 reset();
489
490 #if !defined(Q_OS_WINRT)
491 proxySettingsWatcher.clear(); // needs reset to trigger a new detection
492 proxySettingsWatcher.addLocation(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
493 proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
494 proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral("Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
495 #endif
496
497 // load the winhttp.dll library
498 QSystemLibrary lib(L"winhttp");
499 if (!lib.load())
500 return; // failed to load
501
502 ptrWinHttpOpen = (PtrWinHttpOpen)lib.resolve("WinHttpOpen");
503 ptrWinHttpCloseHandle = (PtrWinHttpCloseHandle)lib.resolve("WinHttpCloseHandle");
504 ptrWinHttpGetProxyForUrl = (PtrWinHttpGetProxyForUrl)lib.resolve("WinHttpGetProxyForUrl");
505 ptrWinHttpGetDefaultProxyConfiguration = (PtrWinHttpGetDefaultProxyConfiguration)lib.resolve("WinHttpGetDefaultProxyConfiguration");
506 ptrWinHttpGetIEProxyConfigForCurrentUser = (PtrWinHttpGetIEProxyConfigForCurrentUser)lib.resolve("WinHttpGetIEProxyConfigForCurrentUser");
507
508 // Try to obtain the Internet Explorer configuration.
509 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyConfig;
510 const bool hasIEConfig = ptrWinHttpGetIEProxyConfigForCurrentUser(&ieProxyConfig);
511 if (hasIEConfig) {
512 if (ieProxyConfig.lpszAutoConfigUrl) {
513 autoConfigUrl = QString::fromWCharArray(ieProxyConfig.lpszAutoConfigUrl);
514 GlobalFree(ieProxyConfig.lpszAutoConfigUrl);
515 }
516 if (ieProxyConfig.lpszProxy) {
517 // http://msdn.microsoft.com/en-us/library/aa384250%28VS.85%29.aspx speaks only about a "proxy URL",
518 // not multiple URLs. However we tested this and it can return multiple URLs. So we use splitSpaceSemicolon
519 // on it.
520 proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(ieProxyConfig.lpszProxy));
521 GlobalFree(ieProxyConfig.lpszProxy);
522 }
523 if (ieProxyConfig.lpszProxyBypass) {
524 proxyBypass = splitSpaceSemicolon(QString::fromWCharArray(ieProxyConfig.lpszProxyBypass));
525 GlobalFree(ieProxyConfig.lpszProxyBypass);
526 }
527 }
528
529 if (!hasIEConfig ||
530 (currentProcessIsService() && proxyServerList.isEmpty() && proxyBypass.isEmpty())) {
531 // no user configuration
532 // attempt to get the default configuration instead
533 // that config will serve as default if WPAD fails
534 WINHTTP_PROXY_INFO proxyInfo;
535 if (ptrWinHttpGetDefaultProxyConfiguration(&proxyInfo) &&
536 proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
537 // we got information from the registry
538 // overwrite the IE configuration, if any
539
540 proxyBypass = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxyBypass));
541 proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxy));
542 }
543
544 if (proxyInfo.lpszProxy)
545 GlobalFree(proxyInfo.lpszProxy);
546 if (proxyInfo.lpszProxyBypass)
547 GlobalFree(proxyInfo.lpszProxyBypass);
548 }
549
550 hHttpSession = NULL;
551 if (ieProxyConfig.fAutoDetect || !autoConfigUrl.isEmpty()) {
552 // open the handle and obtain the options
553 hHttpSession = ptrWinHttpOpen(L"Qt System Proxy access/1.0",
554 WINHTTP_ACCESS_TYPE_NO_PROXY,
555 WINHTTP_NO_PROXY_NAME,
556 WINHTTP_NO_PROXY_BYPASS,
557 0);
558 if (!hHttpSession)
559 return;
560
561 isAutoConfig = true;
562 memset(&autoProxyOptions, 0, sizeof autoProxyOptions);
563 autoProxyOptions.fAutoLogonIfChallenged = false;
564 //Although it is possible to specify dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT | WINHTTP_AUTOPROXY_CONFIG_URL
565 //this has poor performance (WPAD is attempted for every url, taking 2.5 seconds per interface,
566 //before the configured pac file is used)
567 if (ieProxyConfig.fAutoDetect) {
568 autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
569 autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP |
570 WINHTTP_AUTO_DETECT_TYPE_DNS_A;
571 } else {
572 autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
573 autoProxyOptions.lpszAutoConfigUrl = reinterpret_cast<LPCWSTR>(autoConfigUrl.utf16());
574 }
575 }
576
577 functional = isAutoConfig || !proxyServerList.isEmpty();
578 }
579
systemProxyForQuery(const QNetworkProxyQuery & query)580 QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
581 {
582 QWindowsSystemProxy *sp = systemProxy();
583 if (!sp)
584 return QList<QNetworkProxy>() << QNetworkProxy();
585
586 QMutexLocker locker(&sp->mutex);
587 sp->init();
588 if (!sp->functional)
589 return sp->defaultResult;
590
591 if (sp->isAutoConfig) {
592 WINHTTP_PROXY_INFO proxyInfo;
593
594 // try to get the proxy config for the URL
595 QUrl url = query.url();
596 // url could be empty, e.g. from QNetworkProxy::applicationProxy(), that's fine,
597 // we'll still ask for the proxy.
598 // But for a file url, we know we don't need one.
599 if (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc"))
600 return sp->defaultResult;
601 if (query.queryType() != QNetworkProxyQuery::UrlRequest) {
602 // change the scheme to https, maybe it'll work
603 url.setScheme(QLatin1String("https"));
604 }
605
606 QString urlQueryString = url.toString();
607 if (urlQueryString.size() > 2083) {
608 // calls to WinHttpGetProxyForUrl with urls longer than 2083 characters
609 // fail with error code ERROR_INVALID_PARAMETER(87), so we truncate it
610 qWarning("Proxy query URL too long for windows API, try with truncated URL");
611 urlQueryString = url.toString().left(2083);
612 }
613
614 bool getProxySucceeded = ptrWinHttpGetProxyForUrl(sp->hHttpSession,
615 reinterpret_cast<LPCWSTR>(urlQueryString.utf16()),
616 &sp->autoProxyOptions,
617 &proxyInfo);
618 DWORD getProxyError = GetLastError();
619
620 if (!getProxySucceeded
621 && (ERROR_WINHTTP_AUTODETECTION_FAILED == getProxyError)) {
622 // WPAD failed
623 if (sp->autoConfigUrl.isEmpty()) {
624 //No config file could be retrieved on the network.
625 //Don't search for it next time again.
626 sp->isAutoConfig = false;
627 } else {
628 //pac file URL is specified as well, try using that
629 sp->autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
630 sp->autoProxyOptions.lpszAutoConfigUrl =
631 reinterpret_cast<LPCWSTR>(sp->autoConfigUrl.utf16());
632 getProxySucceeded = ptrWinHttpGetProxyForUrl(sp->hHttpSession,
633 reinterpret_cast<LPCWSTR>(urlQueryString.utf16()),
634 &sp->autoProxyOptions,
635 &proxyInfo);
636 getProxyError = GetLastError();
637 }
638 }
639
640 if (!getProxySucceeded
641 && (ERROR_WINHTTP_LOGIN_FAILURE == getProxyError)) {
642 // We first tried without AutoLogon, because this might prevent caching the result.
643 // But now we've to enable it (http://msdn.microsoft.com/en-us/library/aa383153%28v=VS.85%29.aspx)
644 sp->autoProxyOptions.fAutoLogonIfChallenged = TRUE;
645 getProxySucceeded = ptrWinHttpGetProxyForUrl(sp->hHttpSession,
646 reinterpret_cast<LPCWSTR>(urlQueryString.utf16()),
647 &sp->autoProxyOptions,
648 &proxyInfo);
649 getProxyError = GetLastError();
650 }
651
652 if (!getProxySucceeded
653 && (ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT == getProxyError)) {
654 // PAC file url is not connectable, or server returned error (e.g. http 404)
655 //Don't search for it next time again.
656 sp->isAutoConfig = false;
657 }
658
659 if (getProxySucceeded) {
660 // yes, we got a config for this URL
661 QString proxyBypass = QString::fromWCharArray(proxyInfo.lpszProxyBypass);
662 QStringList proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxy));
663 if (proxyInfo.lpszProxy)
664 GlobalFree(proxyInfo.lpszProxy);
665 if (proxyInfo.lpszProxyBypass)
666 GlobalFree(proxyInfo.lpszProxyBypass);
667
668 if (proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY)
669 return sp->defaultResult; //i.e. the PAC file result was "DIRECT"
670 if (isBypassed(query.peerHostName(), splitSpaceSemicolon(proxyBypass)))
671 return sp->defaultResult;
672 return parseServerList(query, proxyServerList);
673 }
674
675 // GetProxyForUrl failed, fall back to static configuration
676 }
677
678 // static configuration
679 if (isBypassed(query.peerHostName(), sp->proxyBypass))
680 return sp->defaultResult;
681
682 QList<QNetworkProxy> result = parseServerList(query, sp->proxyServerList);
683 // In some cases, this was empty. See SF task 00062670
684 if (result.isEmpty())
685 return sp->defaultResult;
686
687 return result;
688 }
689
690 QT_END_NAMESPACE
691
692 #endif
693