src/network/kernel/qnetworkinterface_win.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/network/kernel/qnetworkinterface_win.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,327 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qnetworkinterface.h"
+#include "qnetworkinterface_p.h"
+
+#ifndef QT_NO_NETWORKINTERFACE
+
+#include "qnetworkinterface_win_p.h"
+#include <qhostinfo.h>
+#include <qhash.h>
+#include <qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef DWORD (WINAPI *PtrGetAdaptersInfo)(PIP_ADAPTER_INFO, PULONG);
+static PtrGetAdaptersInfo ptrGetAdaptersInfo = 0;
+typedef ULONG (WINAPI *PtrGetAdaptersAddresses)(ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG);
+static PtrGetAdaptersAddresses ptrGetAdaptersAddresses = 0;
+typedef DWORD (WINAPI *PtrGetNetworkParams)(PFIXED_INFO, PULONG);
+static PtrGetNetworkParams ptrGetNetworkParams = 0;
+
+static void resolveLibs()
+{
+    // try to find the functions we need from Iphlpapi.dll
+    static bool done = false;
+
+    if (!done) {
+        done = true;
+
+        HINSTANCE iphlpapiHnd = LoadLibrary(L"iphlpapi");
+        if (iphlpapiHnd == NULL)
+            return;
+
+#if defined(Q_OS_WINCE)
+        ptrGetAdaptersInfo = (PtrGetAdaptersInfo)GetProcAddress(iphlpapiHnd, L"GetAdaptersInfo");
+        ptrGetAdaptersAddresses = (PtrGetAdaptersAddresses)GetProcAddress(iphlpapiHnd, L"GetAdaptersAddresses");
+        ptrGetNetworkParams = (PtrGetNetworkParams)GetProcAddress(iphlpapiHnd, L"GetNetworkParams");
+#else
+        ptrGetAdaptersInfo = (PtrGetAdaptersInfo)GetProcAddress(iphlpapiHnd, "GetAdaptersInfo");
+        ptrGetAdaptersAddresses = (PtrGetAdaptersAddresses)GetProcAddress(iphlpapiHnd, "GetAdaptersAddresses");
+        ptrGetNetworkParams = (PtrGetNetworkParams)GetProcAddress(iphlpapiHnd, "GetNetworkParams");
+#endif
+    }
+}
+
+static QHostAddress addressFromSockaddr(sockaddr *sa)
+{
+    QHostAddress address;
+    if (!sa)
+        return address;
+
+    if (sa->sa_family == AF_INET)
+        address.setAddress(htonl(((sockaddr_in *)sa)->sin_addr.s_addr));
+    else if (sa->sa_family == AF_INET6)
+        address.setAddress(((qt_sockaddr_in6 *)sa)->sin6_addr.qt_s6_addr);
+    else
+        qWarning("Got unknown socket family %d", sa->sa_family);
+    return address;
+
+}
+
+static QHash<QHostAddress, QHostAddress> ipv4Netmasks()
+{
+    //Retrieve all the IPV4 addresses & netmasks
+    IP_ADAPTER_INFO staticBuf[2]; // 2 is arbitrary
+    PIP_ADAPTER_INFO pAdapter = staticBuf;
+    ULONG bufSize = sizeof staticBuf;
+    QHash<QHostAddress, QHostAddress> ipv4netmasks; 
+
+    DWORD retval = ptrGetAdaptersInfo(pAdapter, &bufSize);
+    if (retval == ERROR_BUFFER_OVERFLOW) {
+        // need more memory
+        pAdapter = (IP_ADAPTER_INFO *)qMalloc(bufSize);
+        if (!pAdapter)
+            return ipv4netmasks;
+        // try again
+        if (ptrGetAdaptersInfo(pAdapter, &bufSize) != ERROR_SUCCESS) {
+            qFree(pAdapter);
+            return ipv4netmasks;
+        }
+    } else if (retval != ERROR_SUCCESS) {
+        // error
+        return ipv4netmasks;
+    }
+
+    // iterate over the list and add the entries to our listing
+    for (PIP_ADAPTER_INFO ptr = pAdapter; ptr; ptr = ptr->Next) {
+        for (PIP_ADDR_STRING addr = &ptr->IpAddressList; addr; addr = addr->Next) {
+            QHostAddress address(QLatin1String(addr->IpAddress.String));
+            QHostAddress mask(QLatin1String(addr->IpMask.String));
+            ipv4netmasks[address] = mask;
+        }
+    }
+    if (pAdapter != staticBuf)
+        qFree(pAdapter);
+
+    return ipv4netmasks;
+
+}
+
+static QList<QNetworkInterfacePrivate *> interfaceListingWinXP()
+{
+    QList<QNetworkInterfacePrivate *> interfaces;
+    IP_ADAPTER_ADDRESSES staticBuf[2]; // 2 is arbitrary
+    PIP_ADAPTER_ADDRESSES pAdapter = staticBuf;
+    ULONG bufSize = sizeof staticBuf;
+
+    const QHash<QHostAddress, QHostAddress> &ipv4netmasks = ipv4Netmasks();
+    ULONG flags = GAA_FLAG_INCLUDE_ALL_INTERFACES |
+                  GAA_FLAG_INCLUDE_PREFIX |
+                  GAA_FLAG_SKIP_DNS_SERVER |
+                  GAA_FLAG_SKIP_MULTICAST;
+    ULONG retval = ptrGetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize);
+    if (retval == ERROR_BUFFER_OVERFLOW) {
+        // need more memory
+        pAdapter = (IP_ADAPTER_ADDRESSES *)qMalloc(bufSize);
+        if (!pAdapter)
+            return interfaces;
+        // try again
+        if (ptrGetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize) != ERROR_SUCCESS) {
+            qFree(pAdapter);
+            return interfaces;
+        }
+    } else if (retval != ERROR_SUCCESS) {
+        // error
+        return interfaces;
+    }
+
+    // iterate over the list and add the entries to our listing
+    for (PIP_ADAPTER_ADDRESSES ptr = pAdapter; ptr; ptr = ptr->Next) {
+        QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
+        interfaces << iface;
+
+        iface->index = 0;
+        if (ptr->Length >= offsetof(IP_ADAPTER_ADDRESSES, Ipv6IfIndex) && ptr->Ipv6IfIndex != 0)
+            iface->index = ptr->Ipv6IfIndex;
+        else if (ptr->IfIndex != 0)
+            iface->index = ptr->IfIndex;
+
+        iface->flags = QNetworkInterface::CanBroadcast;
+        if (ptr->OperStatus == IfOperStatusUp)
+            iface->flags |= QNetworkInterface::IsUp | QNetworkInterface::IsRunning;
+        if ((ptr->Flags & IP_ADAPTER_NO_MULTICAST) == 0)
+            iface->flags |= QNetworkInterface::CanMulticast;
+
+        iface->name = QString::fromLocal8Bit(ptr->AdapterName);
+        iface->friendlyName = QString::fromWCharArray(ptr->FriendlyName);
+        if (ptr->PhysicalAddressLength)
+            iface->hardwareAddress = iface->makeHwAddress(ptr->PhysicalAddressLength,
+                                                          ptr->PhysicalAddress);
+        else
+            // loopback if it has no address
+            iface->flags |= QNetworkInterface::IsLoopBack;
+
+        // The GetAdaptersAddresses call has an interesting semantic:
+        // It can return a number N of addresses and a number M of prefixes.
+        // But if you have IPv6 addresses, generally N > M.
+        // I cannot find a way to relate the Address to the Prefix, aside from stopping
+        // the iteration at the last Prefix entry and assume that it applies to all addresses
+        // from that point on.
+        PIP_ADAPTER_PREFIX pprefix = 0;
+        if (ptr->Length >= offsetof(IP_ADAPTER_ADDRESSES, FirstPrefix))
+            pprefix = ptr->FirstPrefix;
+        for (PIP_ADAPTER_UNICAST_ADDRESS addr = ptr->FirstUnicastAddress; addr; addr = addr->Next) {
+            QNetworkAddressEntry entry;
+            entry.setIp(addressFromSockaddr(addr->Address.lpSockaddr));
+            if (pprefix) {
+                if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
+                    entry.setNetmask(ipv4netmasks[entry.ip()]);
+
+                    // broadcast address is set on postProcess()
+                } else { //IPV6
+                    entry.setPrefixLength(pprefix->PrefixLength);
+                }
+                pprefix = pprefix->Next ? pprefix->Next : pprefix;
+            }
+            iface->addressEntries << entry;
+        }
+    }
+
+    if (pAdapter != staticBuf)
+        qFree(pAdapter);
+
+    return interfaces;
+}
+
+static QList<QNetworkInterfacePrivate *> interfaceListingWin2k()
+{
+    QList<QNetworkInterfacePrivate *> interfaces;
+    IP_ADAPTER_INFO staticBuf[2]; // 2 is arbitrary
+    PIP_ADAPTER_INFO pAdapter = staticBuf;
+    ULONG bufSize = sizeof staticBuf;
+
+    DWORD retval = ptrGetAdaptersInfo(pAdapter, &bufSize);
+    if (retval == ERROR_BUFFER_OVERFLOW) {
+        // need more memory
+        pAdapter = (IP_ADAPTER_INFO *)qMalloc(bufSize);
+        if (!pAdapter)
+            return interfaces;
+        // try again
+        if (ptrGetAdaptersInfo(pAdapter, &bufSize) != ERROR_SUCCESS) {
+            qFree(pAdapter);
+            return interfaces;
+        }
+    } else if (retval != ERROR_SUCCESS) {
+        // error
+        return interfaces;
+    }
+
+    // iterate over the list and add the entries to our listing
+    for (PIP_ADAPTER_INFO ptr = pAdapter; ptr; ptr = ptr->Next) {
+        QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
+        interfaces << iface;
+
+        iface->index = ptr->Index;
+        iface->flags = QNetworkInterface::IsUp | QNetworkInterface::IsRunning;
+        if (ptr->Type == MIB_IF_TYPE_PPP)
+            iface->flags |= QNetworkInterface::IsPointToPoint;
+        else
+            iface->flags |= QNetworkInterface::CanBroadcast;
+        iface->name = QString::fromLocal8Bit(ptr->AdapterName);
+        iface->hardwareAddress = QNetworkInterfacePrivate::makeHwAddress(ptr->AddressLength,
+                                                                         ptr->Address);
+
+        for (PIP_ADDR_STRING addr = &ptr->IpAddressList; addr; addr = addr->Next) {
+            QNetworkAddressEntry entry;
+            entry.setIp(QHostAddress(QLatin1String(addr->IpAddress.String)));
+            entry.setNetmask(QHostAddress(QLatin1String(addr->IpMask.String)));
+            // broadcast address is set on postProcess()
+
+            iface->addressEntries << entry;
+        }
+    }
+
+    if (pAdapter != staticBuf)
+        qFree(pAdapter);
+
+    return interfaces;
+}
+
+static QList<QNetworkInterfacePrivate *> interfaceListing()
+{
+    resolveLibs();
+    if (ptrGetAdaptersAddresses != NULL)
+        return interfaceListingWinXP();
+    else if (ptrGetAdaptersInfo != NULL)
+        return interfaceListingWin2k();
+
+    // failed
+    return QList<QNetworkInterfacePrivate *>();
+}
+
+QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan()
+{
+    return interfaceListing();
+}
+
+QString QHostInfo::localDomainName()
+{
+    resolveLibs();
+    if (ptrGetNetworkParams == NULL)
+        return QString();       // couldn't resolve
+
+    FIXED_INFO info, *pinfo;
+    ULONG bufSize = sizeof info;
+    pinfo = &info;
+    if (ptrGetNetworkParams(pinfo, &bufSize) == ERROR_BUFFER_OVERFLOW) {
+        pinfo = (FIXED_INFO *)qMalloc(bufSize);
+        if (!pinfo)
+            return QString();
+        // try again
+        if (ptrGetNetworkParams(pinfo, &bufSize) != ERROR_SUCCESS) {
+            qFree(pinfo);
+            return QString();   // error
+        }
+    }
+
+    QString domainName = QUrl::fromAce(pinfo->DomainName);
+
+    if (pinfo != &info)
+        qFree(pinfo);
+
+    return domainName;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKINTERFACE