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