source: trunk/src/network/kernel/qnetworkproxy_win.cpp@ 352

Last change on this file since 352 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 14.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qnetworkproxy.h"
43
44#ifndef QT_NO_NETWORKPROXY
45
46#if defined(UNICODE)
47
48#include <qmutex.h>
49#include <qstringlist.h>
50#include <qregexp.h>
51#include <qurl.h>
52
53#include <string.h>
54#include <windows.h>
55#include <wininet.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
70typedef struct {
71 DWORD dwFlags;
72 DWORD dwAutoDetectFlags;
73 LPCWSTR lpszAutoConfigUrl;
74 LPVOID lpvReserved;
75 DWORD dwReserved;
76 BOOL fAutoLogonIfChallenged;
77} WINHTTP_AUTOPROXY_OPTIONS;
78
79typedef struct {
80 DWORD dwAccessType;
81 LPWSTR lpszProxy;
82 LPWSTR lpszProxyBypass;
83} WINHTTP_PROXY_INFO;
84
85typedef 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
105QT_BEGIN_NAMESPACE
106
107typedef BOOL (WINAPI * PtrWinHttpGetProxyForUrl)(HINTERNET, LPCWSTR, WINHTTP_AUTOPROXY_OPTIONS*, WINHTTP_PROXY_INFO*);
108typedef HINTERNET (WINAPI * PtrWinHttpOpen)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR,DWORD);
109typedef BOOL (WINAPI * PtrWinHttpGetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*);
110typedef BOOL (WINAPI * PtrWinHttpGetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*);
111typedef BOOL (WINAPI * PtrWinHttpCloseHandle)(HINTERNET);
112static PtrWinHttpGetProxyForUrl ptrWinHttpGetProxyForUrl = 0;
113static PtrWinHttpOpen ptrWinHttpOpen = 0;
114static PtrWinHttpGetDefaultProxyConfiguration ptrWinHttpGetDefaultProxyConfiguration = 0;
115static PtrWinHttpGetIEProxyConfigForCurrentUser ptrWinHttpGetIEProxyConfigForCurrentUser = 0;
116static PtrWinHttpCloseHandle ptrWinHttpCloseHandle = 0;
117
118
119static QStringList splitSpaceSemicolon(const QString &source)
120{
121 QStringList list;
122 int start = 0;
123 int end;
124 while (true) {
125 int space = source.indexOf(QLatin1Char(' '), start);
126 int semicolon = source.indexOf(QLatin1Char(';'), start);
127 end = space;
128 if (semicolon != -1 && (end == -1 || semicolon < end))
129 end = semicolon;
130
131 if (end == -1) {
132 if (start != source.length())
133 list.append(source.mid(start));
134 return list;
135 }
136 if (start != end)
137 list.append(source.mid(start, end - start));
138 start = end + 1;
139 }
140 return list;
141}
142
143static bool isBypassed(const QString &host, const QStringList &bypassList)
144{
145 if (host.isEmpty())
146 return true;
147
148 bool isSimple = !host.contains(QLatin1Char('.')) && !host.contains(QLatin1Char(':'));
149
150 QHostAddress ipAddress;
151 bool isIpAddress = ipAddress.setAddress(host);
152
153 // does it match the list of exclusions?
154 foreach (const QString &entry, bypassList) {
155 if (isSimple && entry == QLatin1String("<local>"))
156 return true;
157 if (isIpAddress && ipAddress.isInSubnet(QHostAddress::parseSubnet(entry))) {
158 return true; // excluded
159 } else {
160 // do wildcard matching
161 QRegExp rx(entry, Qt::CaseInsensitive, QRegExp::Wildcard);
162 if (rx.exactMatch(host))
163 return true;
164 }
165 }
166
167 // host was not excluded
168 return false;
169}
170
171static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, const QStringList &proxyList)
172{
173 // Reference documentation from Microsoft:
174 // http://msdn.microsoft.com/en-us/library/aa383912(VS.85).aspx
175 //
176 // According to the website, the proxy server list is
177 // one or more of the space- or semicolon-separated strings in the format:
178 // ([<scheme>=][<scheme>"://"]<server>[":"<port>])
179
180 QList<QNetworkProxy> result;
181 foreach (const QString &entry, proxyList) {
182 int server = 0;
183
184 int pos = entry.indexOf(QLatin1Char('='));
185 if (pos != -1) {
186 QStringRef scheme = entry.leftRef(pos);
187 if (scheme != query.protocolTag())
188 continue;
189
190 server = pos + 1;
191 }
192
193 QNetworkProxy::ProxyType proxyType = QNetworkProxy::HttpProxy;
194 quint16 port = 8080;
195
196 pos = entry.indexOf(QLatin1String("://"), server);
197 if (pos != -1) {
198 QStringRef scheme = entry.midRef(server, pos - server);
199 if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) {
200 // no-op
201 // defaults are above
202 } else if (scheme == QLatin1String("socks") || scheme == QLatin1String("socks5")) {
203 proxyType = QNetworkProxy::Socks5Proxy;
204 port = 1080;
205 } else {
206 // unknown proxy type
207 continue;
208 }
209
210 server = pos + 3;
211 }
212
213 pos = entry.indexOf(QLatin1Char(':'), server);
214 if (pos != -1) {
215 bool ok;
216 uint value = entry.mid(pos + 1).toUInt(&ok);
217 if (!ok || value > 65535)
218 continue; // invalid port number
219
220 port = value;
221 } else {
222 pos = entry.length();
223 }
224
225 result << QNetworkProxy(proxyType, entry.mid(server, pos - server), port);
226 }
227
228 return result;
229}
230
231class QWindowsSystemProxy
232{
233public:
234 QWindowsSystemProxy();
235 ~QWindowsSystemProxy();
236 void init();
237
238 QMutex mutex;
239
240 HINTERNET hHttpSession;
241 WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions;
242
243 QString autoConfigUrl;
244 QStringList proxyServerList;
245 QStringList proxyBypass;
246 QList<QNetworkProxy> defaultResult;
247
248 bool initialized;
249 bool functional;
250 bool isAutoConfig;
251};
252
253Q_GLOBAL_STATIC(QWindowsSystemProxy, systemProxy)
254
255QWindowsSystemProxy::QWindowsSystemProxy()
256 : initialized(false), functional(false), isAutoConfig(false)
257{
258 defaultResult << QNetworkProxy::NoProxy;
259}
260
261QWindowsSystemProxy::~QWindowsSystemProxy()
262{
263 if (hHttpSession)
264 ptrWinHttpCloseHandle(hHttpSession);
265}
266
267void QWindowsSystemProxy::init()
268{
269 if (initialized)
270 return;
271 initialized = true;
272 if (QSysInfo::windowsVersion() & QSysInfo::WV_DOS_based)
273 return; // no point, this library is only available on 2k, XP and up
274
275#ifdef Q_OS_WINCE
276 // Windows CE does not have any of the following API
277 return;
278#else
279 // load the winhttp.dll library
280 HINSTANCE winhttpHnd = LoadLibraryW(L"winhttp");
281 if (!winhttpHnd)
282 return; // failed to load
283
284 ptrWinHttpOpen = (PtrWinHttpOpen)GetProcAddress(winhttpHnd, "WinHttpOpen");
285 ptrWinHttpCloseHandle = (PtrWinHttpCloseHandle)GetProcAddress(winhttpHnd, "WinHttpCloseHandle");
286 ptrWinHttpGetProxyForUrl = (PtrWinHttpGetProxyForUrl)GetProcAddress(winhttpHnd, "WinHttpGetProxyForUrl");
287 ptrWinHttpGetDefaultProxyConfiguration = (PtrWinHttpGetDefaultProxyConfiguration)GetProcAddress(winhttpHnd, "WinHttpGetDefaultProxyConfiguration");
288 ptrWinHttpGetIEProxyConfigForCurrentUser = (PtrWinHttpGetIEProxyConfigForCurrentUser)GetProcAddress(winhttpHnd, "WinHttpGetIEProxyConfigForCurrentUser");
289
290 // Try to obtain the Internet Explorer configuration.
291 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyConfig;
292 if (ptrWinHttpGetIEProxyConfigForCurrentUser(&ieProxyConfig)) {
293 if (ieProxyConfig.lpszAutoConfigUrl) {
294 autoConfigUrl = QString::fromWCharArray(ieProxyConfig.lpszAutoConfigUrl);
295 GlobalFree(ieProxyConfig.lpszAutoConfigUrl);
296 }
297 if (ieProxyConfig.lpszProxy) {
298 proxyServerList << QString::fromWCharArray(ieProxyConfig.lpszProxy);
299 GlobalFree(ieProxyConfig.lpszProxy);
300 }
301 if (ieProxyConfig.lpszProxyBypass) {
302 proxyBypass = splitSpaceSemicolon(QString::fromWCharArray(ieProxyConfig.lpszProxyBypass));
303 GlobalFree(ieProxyConfig.lpszProxyBypass);
304 }
305 }
306
307 hHttpSession = NULL;
308 if (ieProxyConfig.fAutoDetect || !autoConfigUrl.isEmpty()) {
309 // using proxy autoconfiguration
310 proxyServerList.clear();
311 proxyBypass.clear();
312
313 // open the handle and obtain the options
314 hHttpSession = ptrWinHttpOpen(L"Qt System Proxy access/1.0",
315 WINHTTP_ACCESS_TYPE_NO_PROXY,
316 WINHTTP_NO_PROXY_NAME,
317 WINHTTP_NO_PROXY_BYPASS,
318 0);
319 if (!hHttpSession)
320 return;
321
322 isAutoConfig = true;
323 memset(&autoProxyOptions, 0, sizeof autoProxyOptions);
324 autoProxyOptions.fAutoLogonIfChallenged = true;
325 if (ieProxyConfig.fAutoDetect) {
326 autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
327 autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP |
328 WINHTTP_AUTO_DETECT_TYPE_DNS_A;
329 } else {
330 autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
331 autoProxyOptions.lpszAutoConfigUrl = (LPCWSTR)autoConfigUrl.utf16();
332 }
333 } else {
334 // not auto-detected
335 // attempt to get the static configuration instead
336 WINHTTP_PROXY_INFO proxyInfo;
337 if (ptrWinHttpGetDefaultProxyConfiguration(&proxyInfo) &&
338 proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
339 // we got information from the registry
340 // overwrite the IE configuration, if any
341
342 proxyBypass = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxyBypass));
343 proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxy));
344 }
345
346 if (proxyInfo.lpszProxy)
347 GlobalFree(proxyInfo.lpszProxy);
348 if (proxyInfo.lpszProxyBypass)
349 GlobalFree(proxyInfo.lpszProxyBypass);
350 }
351
352 functional = isAutoConfig || !proxyServerList.isEmpty();
353#endif
354}
355
356QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
357{
358 QWindowsSystemProxy *sp = systemProxy();
359 if (!sp)
360 return QList<QNetworkProxy>() << QNetworkProxy();
361
362 QMutexLocker locker(&sp->mutex);
363 sp->init();
364 if (!sp->functional)
365 return sp->defaultResult;
366
367 if (sp->isAutoConfig) {
368 WINHTTP_PROXY_INFO proxyInfo;
369
370 // try to get the proxy config for the URL, if we have a URL
371 QUrl url = query.url();
372 if (query.queryType() != QNetworkProxyQuery::UrlRequest) {
373 // change the scheme to https, maybe it'll work
374 url.setScheme(QLatin1String("https"));
375 }
376 if (ptrWinHttpGetProxyForUrl(sp->hHttpSession,
377 (LPCWSTR)url.toString().utf16(),
378 &sp->autoProxyOptions,
379 &proxyInfo)) {
380 // yes, we got a config for this URL
381 QString proxyBypass = QString::fromWCharArray(proxyInfo.lpszProxyBypass);
382 QStringList proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxy));
383 if (proxyInfo.lpszProxy)
384 GlobalFree(proxyInfo.lpszProxy);
385 if (proxyInfo.lpszProxyBypass)
386 GlobalFree(proxyInfo.lpszProxyBypass);
387
388 if (isBypassed(query.peerHostName(), splitSpaceSemicolon(proxyBypass)))
389 return sp->defaultResult;
390 return parseServerList(query, proxyServerList);
391 }
392
393 // GetProxyForUrl failed
394 return sp->defaultResult;
395 }
396
397 // static configuration
398 if (isBypassed(query.peerHostName(), sp->proxyBypass))
399 return sp->defaultResult;
400
401 return parseServerList(query, sp->proxyServerList);
402}
403
404#else // !UNICODE
405
406QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &)
407{
408 return QList<QNetworkProxy>() << QNetworkProxy::NoProxy;
409}
410
411#endif
412
413QT_END_NAMESPACE
414
415#endif
Note: See TracBrowser for help on using the repository browser.