diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/network/kernel/qnetworkinterface_win.cpp | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/network/kernel/qnetworkinterface_win.cpp')
-rw-r--r-- | src/network/kernel/qnetworkinterface_win.cpp | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/src/network/kernel/qnetworkinterface_win.cpp b/src/network/kernel/qnetworkinterface_win.cpp new file mode 100644 index 0000000..9540c18 --- /dev/null +++ b/src/network/kernel/qnetworkinterface_win.cpp @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; + QT_WA({ + iphlpapiHnd = LoadLibraryW(L"iphlpapi"); + }, { + iphlpapiHnd = LoadLibraryA("iphlpapi"); + }); + if (iphlpapiHnd == NULL) + return; // failed to load, probably Windows 95 + +#if defined(Q_OS_WINCE) + ptrGetAdaptersInfo = (PtrGetAdaptersInfo)GetProcAddressW(iphlpapiHnd, L"GetAdaptersInfo"); + ptrGetAdaptersAddresses = (PtrGetAdaptersAddresses)GetProcAddressW(iphlpapiHnd, L"GetAdaptersAddresses"); + ptrGetNetworkParams = (PtrGetNetworkParams)GetProcAddressW(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); + // 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); + + // 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)) + 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); + + // 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); + + // 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 |