/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qset.h" #include "qnetworkinterface.h" #include "qnetworkinterface_p.h" #include "qalgorithms.h" #include "private/qnet_unix_p.h" #ifndef QT_NO_NETWORKINTERFACE #define IP_MULTICAST // make AIX happy and define IFF_MULTICAST #include #include #ifdef Q_OS_SOLARIS # include #endif #include #if defined(QT_LINUXBASE) # define QT_NO_GETIFADDRS #endif #ifdef Q_OS_ANDROID // android lacks if_nameindex # define QT_NO_IPV6IFNAME #endif #ifdef Q_OS_HAIKU # include # define IFF_RUNNING 0x0001 #endif #ifndef QT_NO_GETIFADDRS # include #endif #ifdef QT_LINUXBASE # include # ifndef SIOCGIFBRDADDR # define SIOCGIFBRDADDR 0x8919 # endif #endif // QT_LINUXBASE #include QT_BEGIN_NAMESPACE static QHostAddress addressFromSockaddr(sockaddr *sa, int ifindex = 0, const QString &ifname = QString()) { 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(((sockaddr_in6 *)sa)->sin6_addr.s6_addr); int scope = ((sockaddr_in6 *)sa)->sin6_scope_id; if (scope && scope == ifindex) { // this is the most likely scenario: // a scope ID in a socket is that of the interface this address came from address.setScopeId(ifname); } else if (scope) { address.setScopeId(QNetworkInterfaceManager::interfaceNameFromIndex(scope)); } } return address; } static QNetworkInterface::InterfaceFlags convertFlags(uint rawFlags) { QNetworkInterface::InterfaceFlags flags = 0; flags |= (rawFlags & IFF_UP) ? QNetworkInterface::IsUp : QNetworkInterface::InterfaceFlag(0); flags |= (rawFlags & IFF_RUNNING) ? QNetworkInterface::IsRunning : QNetworkInterface::InterfaceFlag(0); flags |= (rawFlags & IFF_BROADCAST) ? QNetworkInterface::CanBroadcast : QNetworkInterface::InterfaceFlag(0); flags |= (rawFlags & IFF_LOOPBACK) ? QNetworkInterface::IsLoopBack : QNetworkInterface::InterfaceFlag(0); #ifdef IFF_POINTOPOINT //cygwin doesn't define IFF_POINTOPOINT flags |= (rawFlags & IFF_POINTOPOINT) ? QNetworkInterface::IsPointToPoint : QNetworkInterface::InterfaceFlag(0); #endif #ifdef IFF_MULTICAST flags |= (rawFlags & IFF_MULTICAST) ? QNetworkInterface::CanMulticast : QNetworkInterface::InterfaceFlag(0); #endif return flags; } uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name) { #ifndef QT_NO_IPV6IFNAME return ::if_nametoindex(name.toLatin1()); #elif defined(SIOCGIFINDEX) struct ifreq req; int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0); if (socket < 0) return 0; QByteArray name8bit = name.toLatin1(); memset(&req, 0, sizeof(ifreq)); memcpy(req.ifr_name, name8bit, qMin(name8bit.length() + 1, sizeof(req.ifr_name) - 1)); uint id = 0; if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0) id = req.ifr_ifindex; qt_safe_close(socket); return id; #else return 0; #endif } QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index) { #ifndef QT_NO_IPV6IFNAME char buf[IF_NAMESIZE]; if (::if_indextoname(index, buf)) return QString::fromLatin1(buf); #elif defined(SIOCGIFNAME) struct ifreq req; int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0); if (socket >= 0) { memset(&req, 0, sizeof(ifreq)); req.ifr_ifindex = index; if (qt_safe_ioctl(socket, SIOCGIFNAME, &req) >= 0) { qt_safe_close(socket); return QString::fromLatin1(req.ifr_name); } qt_safe_close(socket); } #endif return QString::number(uint(index)); } #ifdef QT_NO_GETIFADDRS // getifaddrs not available static QSet interfaceNames(int socket) { QSet result; #ifdef QT_NO_IPV6IFNAME QByteArray storageBuffer; struct ifconf interfaceList; static const int STORAGEBUFFER_GROWTH = 256; forever { // grow the storage buffer storageBuffer.resize(storageBuffer.size() + STORAGEBUFFER_GROWTH); interfaceList.ifc_buf = storageBuffer.data(); interfaceList.ifc_len = storageBuffer.size(); // get the interface list if (qt_safe_ioctl(socket, SIOCGIFCONF, &interfaceList) >= 0) { if (int(interfaceList.ifc_len + sizeof(ifreq) + 64) < storageBuffer.size()) { // if the buffer was big enough, break storageBuffer.resize(interfaceList.ifc_len); break; } } else { // internal error return result; } if (storageBuffer.size() > 100000) { // out of space return result; } } int interfaceCount = interfaceList.ifc_len / sizeof(ifreq); for (int i = 0; i < interfaceCount; ++i) { QByteArray name = QByteArray(interfaceList.ifc_req[i].ifr_name); if (!name.isEmpty()) result << name; } return result; #else Q_UNUSED(socket); // use if_nameindex struct if_nameindex *interfaceList = ::if_nameindex(); for (struct if_nameindex *ptr = interfaceList; ptr && ptr->if_name; ++ptr) result << ptr->if_name; if_freenameindex(interfaceList); return result; #endif } static QNetworkInterfacePrivate *findInterface(int socket, QList &interfaces, struct ifreq &req) { QNetworkInterfacePrivate *iface = 0; int ifindex = 0; #if !defined(QT_NO_IPV6IFNAME) || defined(SIOCGIFINDEX) // Get the interface index # ifdef SIOCGIFINDEX if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0) # if defined(Q_OS_HAIKU) ifindex = req.ifr_index; # else ifindex = req.ifr_ifindex; # endif # else ifindex = if_nametoindex(req.ifr_name); # endif // find the interface data QList::Iterator if_it = interfaces.begin(); for ( ; if_it != interfaces.end(); ++if_it) if ((*if_it)->index == ifindex) { // existing interface iface = *if_it; break; } #else // Search by name QList::Iterator if_it = interfaces.begin(); for ( ; if_it != interfaces.end(); ++if_it) if ((*if_it)->name == QLatin1String(req.ifr_name)) { // existing interface iface = *if_it; break; } #endif if (!iface) { // new interface, create data: iface = new QNetworkInterfacePrivate; iface->index = ifindex; interfaces << iface; } return iface; } static QList interfaceListing() { QList interfaces; int socket; if ((socket = qt_safe_socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == -1) return interfaces; // error QSet names = interfaceNames(socket); QSet::ConstIterator it = names.constBegin(); for ( ; it != names.constEnd(); ++it) { ifreq req; memset(&req, 0, sizeof(ifreq)); memcpy(req.ifr_name, *it, qMin(it->length() + 1, sizeof(req.ifr_name) - 1)); QNetworkInterfacePrivate *iface = findInterface(socket, interfaces, req); #ifdef SIOCGIFNAME // Get the canonical name QByteArray oldName = req.ifr_name; if (qt_safe_ioctl(socket, SIOCGIFNAME, &req) >= 0) { iface->name = QString::fromLatin1(req.ifr_name); // reset the name: memcpy(req.ifr_name, oldName, qMin(oldName.length() + 1, sizeof(req.ifr_name) - 1)); } else #endif { // use this name anyways iface->name = QString::fromLatin1(req.ifr_name); } // Get interface flags if (qt_safe_ioctl(socket, SIOCGIFFLAGS, &req) >= 0) { iface->flags = convertFlags(req.ifr_flags); } #ifdef SIOCGIFHWADDR // Get the HW address if (qt_safe_ioctl(socket, SIOCGIFHWADDR, &req) >= 0) { uchar *addr = (uchar *)req.ifr_addr.sa_data; iface->hardwareAddress = iface->makeHwAddress(6, addr); } #endif // Get the address of the interface QNetworkAddressEntry entry; if (qt_safe_ioctl(socket, SIOCGIFADDR, &req) >= 0) { sockaddr *sa = &req.ifr_addr; entry.setIp(addressFromSockaddr(sa)); // Get the interface broadcast address if (iface->flags & QNetworkInterface::CanBroadcast) { if (qt_safe_ioctl(socket, SIOCGIFBRDADDR, &req) >= 0) { sockaddr *sa = &req.ifr_addr; if (sa->sa_family == AF_INET) entry.setBroadcast(addressFromSockaddr(sa)); } } // Get the interface netmask if (qt_safe_ioctl(socket, SIOCGIFNETMASK, &req) >= 0) { sockaddr *sa = &req.ifr_addr; entry.setNetmask(addressFromSockaddr(sa)); } iface->addressEntries << entry; } } ::close(socket); return interfaces; } #else // use getifaddrs // platform-specific defs: # ifdef Q_OS_LINUX QT_BEGIN_INCLUDE_NAMESPACE # include QT_END_INCLUDE_NAMESPACE # endif # if defined(Q_OS_LINUX) && __GLIBC__ - 0 >= 2 && __GLIBC_MINOR__ - 0 >= 1 && !defined(QT_LINUXBASE) # include static QList createInterfaces(ifaddrs *rawList) { QList interfaces; QSet seenInterfaces; QVarLengthArray seenIndexes; // faster than QSet // On Linux, glibc, uClibc and MUSL obtain the address listing via two // netlink calls: first an RTM_GETLINK to obtain the interface listing, // then one RTM_GETADDR to get all the addresses (uClibc implementation is // copied from glibc; Bionic currently doesn't support getifaddrs). They // synthesize AF_PACKET addresses from the RTM_GETLINK responses, which // means by construction they currently show up first in the interface // listing. for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) { if (ptr->ifa_addr && ptr->ifa_addr->sa_family == AF_PACKET) { sockaddr_ll *sll = (sockaddr_ll *)ptr->ifa_addr; QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate; interfaces << iface; iface->index = sll->sll_ifindex; iface->name = QString::fromLatin1(ptr->ifa_name); iface->flags = convertFlags(ptr->ifa_flags); iface->hardwareAddress = iface->makeHwAddress(sll->sll_halen, (uchar*)sll->sll_addr); Q_ASSERT(!seenIndexes.contains(iface->index)); seenIndexes.append(iface->index); seenInterfaces.insert(iface->name); } } // see if we missed anything: // - virtual interfaces with no HW address have no AF_PACKET // - interface labels have no AF_PACKET, but shouldn't be shown as a new interface for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) { if (!ptr->ifa_addr || ptr->ifa_addr->sa_family != AF_PACKET) { QString name = QString::fromLatin1(ptr->ifa_name); if (seenInterfaces.contains(name)) continue; int ifindex = if_nametoindex(ptr->ifa_name); if (seenIndexes.contains(ifindex)) continue; seenInterfaces.insert(name); seenIndexes.append(ifindex); QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate; interfaces << iface; iface->name = name; iface->flags = convertFlags(ptr->ifa_flags); iface->index = ifindex; } } return interfaces; } # elif defined(Q_OS_BSD4) QT_BEGIN_INCLUDE_NAMESPACE # include QT_END_INCLUDE_NAMESPACE static QList createInterfaces(ifaddrs *rawList) { QList interfaces; // on NetBSD we use AF_LINK and sockaddr_dl // scan the list for that family for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) if (ptr->ifa_addr && ptr->ifa_addr->sa_family == AF_LINK) { QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate; interfaces << iface; sockaddr_dl *sdl = (sockaddr_dl *)ptr->ifa_addr; iface->index = sdl->sdl_index; iface->name = QString::fromLatin1(ptr->ifa_name); iface->flags = convertFlags(ptr->ifa_flags); iface->hardwareAddress = iface->makeHwAddress(sdl->sdl_alen, (uchar*)LLADDR(sdl)); } return interfaces; } # else // Generic version static QList createInterfaces(ifaddrs *rawList) { QList interfaces; // make sure there's one entry for each interface for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) { // Get the interface index int ifindex = if_nametoindex(ptr->ifa_name); QList::Iterator if_it = interfaces.begin(); for ( ; if_it != interfaces.end(); ++if_it) if ((*if_it)->index == ifindex) // this one has been added already break; if (if_it == interfaces.end()) { // none found, create QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate; interfaces << iface; iface->index = ifindex; iface->name = QString::fromLatin1(ptr->ifa_name); iface->flags = convertFlags(ptr->ifa_flags); } } return interfaces; } # endif static QList interfaceListing() { QList interfaces; ifaddrs *interfaceListing; if (getifaddrs(&interfaceListing) == -1) { // error return interfaces; } interfaces = createInterfaces(interfaceListing); for (ifaddrs *ptr = interfaceListing; ptr; ptr = ptr->ifa_next) { // Find the interface QLatin1String name(ptr->ifa_name); QNetworkInterfacePrivate *iface = 0; QList::Iterator if_it = interfaces.begin(); for ( ; if_it != interfaces.end(); ++if_it) if ((*if_it)->name == name) { // found this interface already iface = *if_it; break; } if (!iface) { // it may be an interface label, search by interface index int ifindex = if_nametoindex(ptr->ifa_name); for (if_it = interfaces.begin(); if_it != interfaces.end(); ++if_it) if ((*if_it)->index == ifindex) { // found this interface already iface = *if_it; break; } } if (!iface) { // skip all non-IP interfaces continue; } QNetworkAddressEntry entry; entry.setIp(addressFromSockaddr(ptr->ifa_addr, iface->index, iface->name)); if (entry.ip().isNull()) // could not parse the address continue; entry.setNetmask(addressFromSockaddr(ptr->ifa_netmask, iface->index, iface->name)); if (iface->flags & QNetworkInterface::CanBroadcast) entry.setBroadcast(addressFromSockaddr(ptr->ifa_broadaddr, iface->index, iface->name)); iface->addressEntries << entry; } freeifaddrs(interfaceListing); return interfaces; } #endif QList QNetworkInterfaceManager::scan() { return interfaceListing(); } QT_END_NAMESPACE #endif // QT_NO_NETWORKINTERFACE