src/network/kernel/qhostinfo_win.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/network/kernel/qhostinfo_win.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,286 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if defined Q_CC_MSVC && _MSC_VER <=1300
+//VC.net 2002 support for templates doesn't match some PSDK requirements
+#define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0]))
+#endif
+
+#include <winsock2.h>
+
+#include "qhostinfo_p.h"
+#include "private/qnativesocketengine_p.h"
+#include <ws2tcpip.h>
+#include <qlibrary.h>
+#include <qtimer.h>
+#include <qmutex.h>
+#include <qurl.h>
+#include <private/qmutexpool_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define QHOSTINFO_DEBUG
+
+// Older SDKs do not include the addrinfo struct declaration, so we
+// include a copy of it here.
+struct qt_addrinfo
+{
+    int ai_flags;
+    int ai_family;
+    int ai_socktype;
+    int ai_protocol;
+    size_t ai_addrlen;
+    char *ai_canonname;
+    sockaddr *ai_addr;
+    qt_addrinfo *ai_next;
+};
+
+//###
+#define QT_SOCKLEN_T int
+#ifndef NI_MAXHOST // already defined to 1025 in ws2tcpip.h?
+#define NI_MAXHOST 1024
+#endif
+
+typedef int (__stdcall *getnameinfoProto)(const sockaddr *, QT_SOCKLEN_T, const char *, DWORD, const char *, DWORD, int);
+typedef int (__stdcall *getaddrinfoProto)(const char *, const char *, const qt_addrinfo *, qt_addrinfo **);
+typedef int (__stdcall *freeaddrinfoProto)(qt_addrinfo *);
+static getnameinfoProto local_getnameinfo = 0;
+static getaddrinfoProto local_getaddrinfo = 0;
+static freeaddrinfoProto local_freeaddrinfo = 0;
+
+static void resolveLibrary()
+{
+    // Attempt to resolve getaddrinfo(); without it we'll have to fall
+    // back to gethostbyname(), which has no IPv6 support.
+#if !defined(Q_OS_WINCE)
+    local_getaddrinfo = (getaddrinfoProto) QLibrary::resolve(QLatin1String("ws2_32.dll"), "getaddrinfo");
+    local_freeaddrinfo = (freeaddrinfoProto) QLibrary::resolve(QLatin1String("ws2_32.dll"), "freeaddrinfo");
+    local_getnameinfo = (getnameinfoProto) QLibrary::resolve(QLatin1String("ws2_32.dll"), "getnameinfo");
+#else
+    local_getaddrinfo = (getaddrinfoProto) QLibrary::resolve(QLatin1String("ws2.dll"), "getaddrinfo");
+    local_freeaddrinfo = (freeaddrinfoProto) QLibrary::resolve(QLatin1String("ws2.dll"), "freeaddrinfo");
+    local_getnameinfo = (getnameinfoProto) QLibrary::resolve(QLatin1String("ws2.dll"), "getnameinfo");
+#endif
+}
+
+#if defined(Q_OS_WINCE)
+#include <qmutex.h>
+QMutex qPrivCEMutex;
+#endif
+/*
+    Performs a blocking call to gethostbyname or getaddrinfo, stores
+    the results in a QHostInfo structure and emits the
+    resultsReady() signal.
+*/
+QHostInfo QHostInfoAgent::fromName(const QString &hostName)
+{
+#if defined(Q_OS_WINCE)
+    QMutexLocker locker(&qPrivCEMutex);
+#endif
+    QWindowsSockInit winSock;
+
+    // Load res_init on demand.
+    static volatile bool triedResolve = false;
+    if (!triedResolve) {
+#ifndef QT_NO_THREAD
+        QMutexLocker locker(QMutexPool::globalInstanceGet(&local_getaddrinfo));
+#endif
+        if (!triedResolve) {
+            resolveLibrary();
+            triedResolve = true;
+        }
+    }
+
+    QHostInfo results;
+
+#if defined(QHOSTINFO_DEBUG)
+    qDebug("QHostInfoAgent::fromName(%p): looking up \"%s\" (IPv6 support is %s)",
+           this, hostName.toLatin1().constData(),
+           (local_getaddrinfo && local_freeaddrinfo) ? "enabled" : "disabled");
+#endif
+
+    QHostAddress address;
+    if (address.setAddress(hostName)) {
+        // Reverse lookup
+        if (local_getnameinfo) {
+            sockaddr_in sa4;
+            qt_sockaddr_in6 sa6;
+            sockaddr *sa;
+            QT_SOCKLEN_T saSize;
+            if (address.protocol() == QAbstractSocket::IPv4Protocol) {
+                sa = (sockaddr *)&sa4;
+                saSize = sizeof(sa4);
+                memset(&sa4, 0, sizeof(sa4));
+                sa4.sin_family = AF_INET;
+                sa4.sin_addr.s_addr = htonl(address.toIPv4Address());
+            } else {
+                sa = (sockaddr *)&sa6;
+                saSize = sizeof(sa6);
+                memset(&sa6, 0, sizeof(sa6));
+                sa6.sin6_family = AF_INET6;
+                memcpy(sa6.sin6_addr.qt_s6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr.qt_s6_addr));
+            }
+
+            char hbuf[NI_MAXHOST];
+            if (local_getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) == 0)
+                results.setHostName(QString::fromLatin1(hbuf));
+        } else {
+            unsigned long addr = inet_addr(hostName.toLatin1().constData());
+            struct hostent *ent = gethostbyaddr((const char*)&addr, sizeof(addr), AF_INET);
+            if (ent)
+                results.setHostName(QString::fromLatin1(ent->h_name));
+        }
+
+        if (results.hostName().isEmpty())
+            results.setHostName(address.toString());
+        results.setAddresses(QList<QHostAddress>() << address);
+        return results;
+    }
+
+    // IDN support
+    QByteArray aceHostname = QUrl::toAce(hostName);
+    results.setHostName(hostName);
+    if (aceHostname.isEmpty()) {
+        results.setError(QHostInfo::HostNotFound);
+        results.setErrorString(hostName.isEmpty() ? tr("No host name given") : tr("Invalid hostname"));
+        return results;
+    }
+
+    if (local_getaddrinfo && local_freeaddrinfo) {
+        // Call getaddrinfo, and place all IPv4 addresses at the start
+        // and the IPv6 addresses at the end of the address list in
+        // results.
+        qt_addrinfo *res;
+        int err = local_getaddrinfo(aceHostname.constData(), 0, 0, &res);
+        if (err == 0) {
+            QList<QHostAddress> addresses;
+            for (qt_addrinfo *p = res; p != 0; p = p->ai_next) {
+                switch (p->ai_family) {
+                case AF_INET: {
+                    QHostAddress addr;
+                    addr.setAddress(ntohl(((sockaddr_in *) p->ai_addr)->sin_addr.s_addr));
+                    if (!addresses.contains(addr))
+                        addresses.append(addr);
+                }
+                    break;
+                case AF_INET6: {
+                    QHostAddress addr;
+                    addr.setAddress(((qt_sockaddr_in6 *) p->ai_addr)->sin6_addr.qt_s6_addr);
+                    if (!addresses.contains(addr))
+                        addresses.append(addr);
+                }
+                    break;
+                default:
+                    results.setError(QHostInfo::UnknownError);
+                    results.setErrorString(tr("Unknown address type"));
+                }
+            }
+            results.setAddresses(addresses);
+            local_freeaddrinfo(res);
+        } else if (WSAGetLastError() == WSAHOST_NOT_FOUND || WSAGetLastError() == WSANO_DATA) {
+            results.setError(QHostInfo::HostNotFound);
+            results.setErrorString(tr("Host not found"));
+        } else {
+            results.setError(QHostInfo::UnknownError);
+            results.setErrorString(tr("Unknown error"));
+        }
+    } else {
+        // Fall back to gethostbyname, which only supports IPv4.
+        hostent *ent = gethostbyname(aceHostname.constData());
+        if (ent) {
+            char **p;
+            QList<QHostAddress> addresses;
+            switch (ent->h_addrtype) {
+            case AF_INET:
+                for (p = ent->h_addr_list; *p != 0; p++) {
+                    long *ip4Addr = (long *) *p;
+                    QHostAddress temp;
+                    temp.setAddress(ntohl(*ip4Addr));
+                    addresses << temp;
+                }
+                break;
+            default:
+                results.setError(QHostInfo::UnknownError);
+                results.setErrorString(tr("Unknown address type"));
+                break;
+            }
+            results.setAddresses(addresses);
+        } else if (WSAGetLastError() == 11001) {
+            results.setErrorString(tr("Host not found"));
+            results.setError(QHostInfo::HostNotFound);
+        } else {
+            results.setErrorString(tr("Unknown error"));
+            results.setError(QHostInfo::UnknownError);
+        }
+    }
+
+#if defined(QHOSTINFO_DEBUG)
+    if (results.error() != QHostInfo::NoError) {
+        qDebug("QHostInfoAgent::run(%p): error (%s)",
+               this, results.errorString().toLatin1().constData());
+    } else {
+        QString tmp;
+        QList<QHostAddress> addresses = results.addresses();
+        for (int i = 0; i < addresses.count(); ++i) {
+            if (i != 0) tmp += ", ";
+            tmp += addresses.at(i).toString();
+        }
+        qDebug("QHostInfoAgent::run(%p): found %i entries: {%s}",
+               this, addresses.count(), tmp.toLatin1().constData());
+    }
+#endif
+    return results;
+}
+
+QString QHostInfo::localHostName()
+{
+    QWindowsSockInit winSock;
+
+    char hostName[512];
+    if (gethostname(hostName, sizeof(hostName)) == -1)
+        return QString();
+    hostName[sizeof(hostName) - 1] = '\0';
+    return QString::fromLocal8Bit(hostName);
+}
+
+// QString QHostInfo::localDomainName() defined in qnetworkinterface_win.cpp
+
+QT_END_NAMESPACE