diff options
Diffstat (limited to 'src/network/kernel')
21 files changed, 1715 insertions, 154 deletions
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri index b822a70e88..11b80d59d5 100644 --- a/src/network/kernel/kernel.pri +++ b/src/network/kernel/kernel.pri @@ -7,8 +7,6 @@ HEADERS += kernel/qtnetworkglobal.h \ kernel/qtnetworkglobal_p.h \ kernel/qauthenticator.h \ kernel/qauthenticator_p.h \ - kernel/qdnslookup.h \ - kernel/qdnslookup_p.h \ kernel/qhostaddress.h \ kernel/qhostaddress_p.h \ kernel/qhostinfo.h \ @@ -17,10 +15,10 @@ HEADERS += kernel/qtnetworkglobal.h \ kernel/qnetworkdatagram_p.h \ kernel/qnetworkinterface.h \ kernel/qnetworkinterface_p.h \ + kernel/qnetworkinterface_unix_p.h \ kernel/qnetworkproxy.h SOURCES += kernel/qauthenticator.cpp \ - kernel/qdnslookup.cpp \ kernel/qhostaddress.cpp \ kernel/qhostinfo.cpp \ kernel/qnetworkdatagram.cpp \ @@ -32,12 +30,22 @@ qtConfig(ftp) { SOURCES += kernel/qurlinfo.cpp } +qtConfig(dnslookup) { + HEADERS += kernel/qdnslookup.h \ + kernel/qdnslookup_p.h + + SOURCES += kernel/qdnslookup.cpp +} + unix { - !integrity: SOURCES += kernel/qdnslookup_unix.cpp - SOURCES += kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp + !integrity:qtConfig(dnslookup): SOURCES += kernel/qdnslookup_unix.cpp + SOURCES += kernel/qhostinfo_unix.cpp + + qtConfig(linux-netlink): SOURCES += kernel/qnetworkinterface_linux.cpp + else: SOURCES += kernel/qnetworkinterface_unix.cpp } -android { +android:qtConfig(dnslookup) { SOURCES -= kernel/qdnslookup_unix.cpp SOURCES += kernel/qdnslookup_android.cpp } @@ -46,12 +54,12 @@ win32: { SOURCES += kernel/qhostinfo_win.cpp !winrt { - SOURCES += kernel/qdnslookup_win.cpp \ - kernel/qnetworkinterface_win.cpp + SOURCES += kernel/qnetworkinterface_win.cpp + qtConfig(dnslookup): SOURCES += kernel/qdnslookup_win.cpp LIBS_PRIVATE += -ldnsapi -liphlpapi } else { - SOURCES += kernel/qdnslookup_winrt.cpp \ - kernel/qnetworkinterface_winrt.cpp + SOURCES += kernel/qnetworkinterface_winrt.cpp + qtConfig(dnslookup): SOURCES += kernel/qdnslookup_winrt.cpp } } @@ -60,6 +68,7 @@ mac { !uikit: LIBS_PRIVATE += -framework CoreServices -framework SystemConfiguration } +uikit:HEADERS += kernel/qnetworkinterface_uikit_p.h osx:SOURCES += kernel/qnetworkproxy_mac.cpp else:win32:!winrt: SOURCES += kernel/qnetworkproxy_win.cpp else: qtConfig(libproxy) { diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h index ead5e650f5..eebd0abe66 100644 --- a/src/network/kernel/qdnslookup.h +++ b/src/network/kernel/qdnslookup.h @@ -47,6 +47,8 @@ #include <QtCore/qsharedpointer.h> #include <QtCore/qstring.h> +QT_REQUIRE_CONFIG(dnslookup); + QT_BEGIN_NAMESPACE class QHostAddress; @@ -218,9 +220,9 @@ public: }; Q_ENUM(Type) - explicit QDnsLookup(QObject *parent = Q_NULLPTR); - QDnsLookup(Type type, const QString &name, QObject *parent = Q_NULLPTR); - QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent = Q_NULLPTR); + explicit QDnsLookup(QObject *parent = nullptr); + QDnsLookup(Type type, const QString &name, QObject *parent = nullptr); + QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent = nullptr); ~QDnsLookup(); Error error() const; diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h index d070286383..4584396efe 100644 --- a/src/network/kernel/qdnslookup_p.h +++ b/src/network/kernel/qdnslookup_p.h @@ -60,6 +60,8 @@ #include "QtNetwork/qhostaddress.h" #include "private/qobject_p.h" +QT_REQUIRE_CONFIG(dnslookup); + QT_BEGIN_NAMESPACE //#define QDNSLOOKUP_DEBUG @@ -118,7 +120,7 @@ public: , requestName(name) , nameserver(nameserver) { } - void run() Q_DECL_OVERRIDE; + void run() override; signals: void finished(const QDnsLookupReply &reply); diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp index a1adc61c4c..bf00c6efce 100644 --- a/src/network/kernel/qhostaddress.cpp +++ b/src/network/kernel/qhostaddress.cpp @@ -64,32 +64,6 @@ QT_BEGIN_NAMESPACE - -class QHostAddressPrivate : public QSharedData -{ -public: - QHostAddressPrivate(); - - void setAddress(quint32 a_ = 0); - void setAddress(const quint8 *a_); - void setAddress(const Q_IPV6ADDR &a_); - - bool parse(const QString &ipString); - void clear(); - - QString scopeId; - - union { - Q_IPV6ADDR a6; // IPv6 address - struct { quint64 c[2]; } a6_64; - struct { quint32 c[4]; } a6_32; - }; - quint32 a; // IPv4 address - qint8 protocol; - - friend class QHostAddress; -}; - QHostAddressPrivate::QHostAddressPrivate() : a(0), protocol(QAbstractSocket::UnknownNetworkLayerProtocol) { @@ -205,6 +179,75 @@ void QHostAddressPrivate::clear() memset(&a6, 0, sizeof(a6)); } +AddressClassification QHostAddressPrivate::classify() const +{ + if (a) { + // This is an IPv4 address or an IPv6 v4-mapped address includes all + // IPv6 v4-compat addresses, except for ::ffff:0.0.0.0 (because `a' is + // zero). See setAddress(quint8*) below, which calls convertToIpv4(), + // for details. + // Source: RFC 5735 + if ((a & 0xff000000U) == 0x7f000000U) // 127.0.0.0/8 + return LoopbackAddress; + if ((a & 0xf0000000U) == 0xe0000000U) // 224.0.0.0/4 + return MulticastAddress; + if ((a & 0xffff0000U) == 0xa9fe0000U) // 169.254.0.0/16 + return LinkLocalAddress; + if ((a & 0xff000000U) == 0) // 0.0.0.0/8 except 0.0.0.0 (handled below) + return LocalNetAddress; + if ((a & 0xf0000000U) == 0xf0000000U) { // 240.0.0.0/4 + if (a == 0xffffffffU) // 255.255.255.255 + return BroadcastAddress; + return UnknownAddress; + } + + // Not testing for PrivateNetworkAddress and TestNetworkAddress + // since we don't need them yet. + return GlobalAddress; + } + + // As `a' is zero, this address is either ::ffff:0.0.0.0 or a non-v4-mapped IPv6 address. + // Source: https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml + if (a6_64.c[0]) { + quint32 high16 = qFromBigEndian(a6_32.c[0]) >> 16; + switch (high16 >> 8) { + case 0xff: // ff00::/8 + return MulticastAddress; + case 0xfe: + switch (high16 & 0xffc0) { + case 0xfec0: // fec0::/10 + return SiteLocalAddress; + + case 0xfe80: // fe80::/10 + return LinkLocalAddress; + + default: // fe00::/9 + return UnknownAddress; + } + case 0xfd: // fc00::/7 + case 0xfc: + return UniqueLocalAddress; + default: + return GlobalAddress; + } + } + + quint64 low64 = qFromBigEndian(a6_64.c[1]); + if (low64 == 1) // ::1 + return LoopbackAddress; + if (low64 >> 32 == 0xffff) { // ::ffff:0.0.0.0/96 + Q_ASSERT(quint32(low64) == 0); + return LocalNetAddress; + } + if (low64) // not :: + return GlobalAddress; + + if (protocol == QAbstractSocket::UnknownNetworkLayerProtocol) + return UnknownAddress; + + // only :: and 0.0.0.0 remain now + return LocalNetAddress; +} bool QNetmask::setAddress(const QHostAddress &address) { @@ -685,7 +728,7 @@ void QHostAddress::setAddress(SpecialAddress address) */ quint32 QHostAddress::toIPv4Address() const { - return toIPv4Address(Q_NULLPTR); + return toIPv4Address(nullptr); } /*! @@ -1154,21 +1197,91 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet) */ bool QHostAddress::isLoopback() const { - if ((d->a & 0xFF000000) == 0x7F000000) - return true; // v4 range (including IPv6 wrapped IPv4 addresses) - if (d->protocol == QAbstractSocket::IPv6Protocol) { -#ifdef __SSE2__ - const __m128i loopback = _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); - __m128i ipv6 = _mm_loadu_si128((const __m128i *)d->a6.c); - __m128i cmp = _mm_cmpeq_epi8(ipv6, loopback); - return _mm_movemask_epi8(cmp) == 0xffff; -#else - if (d->a6_64.c[0] != 0 || qFromBigEndian(d->a6_64.c[1]) != 1) - return false; -#endif - return true; - } - return false; + return d->classify() == LoopbackAddress; +} + +/*! + \since 5.11 + + Returns \c true if the address is an IPv4 or IPv6 global address, \c false + otherwise. A global address is an address that is not reserved for + special purposes (like loopback or multicast) or future purposes. + + Note that IPv6 unique local unicast addresses are considered global + addresses (see isUniqueLocalUnicast()), as are IPv4 addresses reserved for + local networks by \l {https://tools.ietf.org/html/rfc1918}{RFC 1918}. + + Also note that IPv6 site-local addresses are deprecated and should be + considered as global in new applications. This function returns true for + site-local addresses too. + + \sa isLoopback(), isSiteLocal(), isUniqueLocalUnicast() +*/ +bool QHostAddress::isGlobal() const +{ + return d->classify() & GlobalAddress; // GlobalAddress is a bit +} + +/*! + \since 5.11 + + Returns \c true if the address is an IPv4 or IPv6 link-local address, \c + false otherwise. + + An IPv4 link-local address is an address in the network 169.254.0.0/16. An + IPv6 link-local address is one in the network fe80::/10. See the + \l{https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml}{IANA + IPv6 Address Space} registry for more information. + + \sa isLoopback(), isGlobal(). isMulticast(), isSiteLocal(), isUniqueLocalUnicast() +*/ +bool QHostAddress::isLinkLocal() const +{ + return d->classify() == LinkLocalAddress; +} + +/*! + \since 5.11 + + Returns \c true if the address is an IPv6 site-local address, \c + false otherwise. + + An IPv6 site-local address is one in the network fec0::/10. See the + \l{https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml}{IANA + IPv6 Address Space} registry for more information. + + IPv6 site-local addresses are deprecated and should not be depended upon in + new applications. New applications should not depend on this function and + should consider site-local addresses the same as global (which is why + isGlobal() also returns true). Site-local addresses were replaced by Unique + Local Addresses (ULA). + + \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast() +*/ +bool QHostAddress::isSiteLocal() const +{ + return d->classify() == SiteLocalAddress; +} + +/*! + \since 5.11 + + Returns \c true if the address is an IPv6 unique local unicast address, \c + false otherwise. + + An IPv6 unique local unicast address is one in the network fc00::/7. See the + \l{https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml} + {IANA IPv6 Address Space} registry for more information. + + Note that Unique local unicast addresses count as global addresses too. RFC + 4193 says that, in practice, "applications may treat these addresses like + global scoped addresses." Only routers need care about the distinction. + + \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast() +*/ +bool QHostAddress::isUniqueLocalUnicast() const +{ + return d->classify() == UniqueLocalAddress; } /*! @@ -1176,14 +1289,29 @@ bool QHostAddress::isLoopback() const Returns \c true if the address is an IPv4 or IPv6 multicast address, \c false otherwise. + + \sa isLoopback(), isGlobal(), isLinkLocal(), isSiteLocal(), isUniqueLocalUnicast() */ bool QHostAddress::isMulticast() const { - if ((d->a & 0xF0000000) == 0xE0000000) - return true; // 224.0.0.0-239.255.255.255 (including v4-mapped IPv6 addresses) - if (d->protocol == QAbstractSocket::IPv6Protocol) - return d->a6.c[0] == 0xff; - return false; + return d->classify() == MulticastAddress; +} + +/*! + \since 5.11 + + Returns \c true if the address is the IPv4 broadcast address, \c false + otherwise. The IPv4 broadcast address is 255.255.255.255. + + Note that this function does not return true for an IPv4 network's local + broadcast address. For that, please use \l QNetworkInterface to obtain the + broadcast addresses of the local machine. + + \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast() +*/ +bool QHostAddress::isBroadcast() const +{ + return d->classify() == BroadcastAddress; } #ifndef QT_NO_DEBUG_STREAM diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h index fdbdbfc72c..00555f3d8e 100644 --- a/src/network/kernel/qhostaddress.h +++ b/src/network/kernel/qhostaddress.h @@ -148,12 +148,18 @@ public: bool isInSubnet(const QPair<QHostAddress, int> &subnet) const; bool isLoopback() const; + bool isGlobal() const; + bool isLinkLocal() const; + bool isSiteLocal() const; + bool isUniqueLocalUnicast() const; bool isMulticast() const; + bool isBroadcast() const; static QPair<QHostAddress, int> parseSubnet(const QString &subnet); friend Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed) Q_DECL_NOTHROW; protected: + friend class QHostAddressPrivate; QExplicitlySharedDataPointer<QHostAddressPrivate> d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QHostAddress::ConversionMode) diff --git a/src/network/kernel/qhostaddress_p.h b/src/network/kernel/qhostaddress_p.h index 5106760ed9..4dc2989011 100644 --- a/src/network/kernel/qhostaddress_p.h +++ b/src/network/kernel/qhostaddress_p.h @@ -57,6 +57,22 @@ QT_BEGIN_NAMESPACE +enum AddressClassification { + LoopbackAddress = 1, + LocalNetAddress, // RFC 1122 + LinkLocalAddress, // RFC 4291 (v6), RFC 3927 (v4) + MulticastAddress, // RFC 4291 (v6), RFC 3171 (v4) + BroadcastAddress, // RFC 919, 922 + + GlobalAddress = 16, + TestNetworkAddress, // RFC 3849 (v6), RFC 5737 (v4), + PrivateNetworkAddress, // RFC 1918 + UniqueLocalAddress, // RFC 4193 + SiteLocalAddress, // RFC 4291 (deprecated by RFC 3879, should be treated as global) + + UnknownAddress = 0 // unclassified or reserved +}; + class QNetmask { // stores 0-32 for IPv4, 0-128 for IPv6, or 255 for invalid @@ -85,6 +101,35 @@ public: { return n1.length == n2.length; } }; +class QHostAddressPrivate : public QSharedData +{ +public: + QHostAddressPrivate(); + + void setAddress(quint32 a_ = 0); + void setAddress(const quint8 *a_); + void setAddress(const Q_IPV6ADDR &a_); + + bool parse(const QString &ipString); + void clear(); + + QString scopeId; + + union { + Q_IPV6ADDR a6; // IPv6 address + struct { quint64 c[2]; } a6_64; + struct { quint32 c[4]; } a6_32; + }; + quint32 a; // IPv4 address + qint8 protocol; + + AddressClassification classify() const; + static AddressClassification classify(const QHostAddress &address) + { return address.d->classify(); } + + friend class QHostAddress; +}; + QT_END_NAMESPACE #endif diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index 0156e22d41..4682070712 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -300,7 +300,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, */ /*! - \fn int QHostInfo::lookupHost(const QString &name, const QObject *receiver, PointerToMemberFunction function) + \fn template<typename PointerToMemberFunction> int QHostInfo::lookupHost(const QString &name, const QObject *receiver, PointerToMemberFunction function) \since 5.9 @@ -319,7 +319,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, */ /*! - \fn int QHostInfo::lookupHost(const QString &name, Functor functor) + \fn template<typename Functor> int QHostInfo::lookupHost(const QString &name, Functor functor) \since 5.9 @@ -337,7 +337,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, */ /*! - \fn int QHostInfo::lookupHost(const QString &name, const QObject *context, Functor functor) + \fn template<typename Functor> int QHostInfo::lookupHost(const QString &name, const QObject *context, Functor functor) \since 5.9 diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h index c5727bb6eb..75917a02a3 100644 --- a/src/network/kernel/qhostinfo.h +++ b/src/network/kernel/qhostinfo.h @@ -90,7 +90,7 @@ public: static QString localHostName(); static QString localDomainName(); -#ifdef Q_QDOC +#ifdef Q_CLANG_QDOC template<typename PointerToMemberFunction> static int QHostInfo::lookupHost(const QString &name, const QObject *receiver, PointerToMemberFunction function); diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h index b4af938353..82cf34719f 100644 --- a/src/network/kernel/qhostinfo_p.h +++ b/src/network/kernel/qhostinfo_p.h @@ -194,7 +194,7 @@ public: QHostInfoRunnable(const QString &hn, int i); QHostInfoRunnable(const QString &hn, int i, const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj); - void run() Q_DECL_OVERRIDE; + void run() override; QString toBeLookedUp; int id; @@ -225,7 +225,7 @@ public: QHostInfoLookupManager(); ~QHostInfoLookupManager(); - void clear() Q_DECL_OVERRIDE; + void clear() override; void work(); // called from QHostInfo diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 8d2cffc304..d22608e22f 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -79,6 +79,11 @@ QT_BEGIN_NAMESPACE # define Q_ADDRCONFIG AI_ADDRCONFIG #endif +enum LibResolvFeature { + NeedResInit, + NeedResNInit +}; + typedef struct __res_state *res_state_ptr; typedef int (*res_init_proto)(void); @@ -89,9 +94,29 @@ typedef void (*res_nclose_proto)(res_state_ptr); static res_nclose_proto local_res_nclose = 0; static res_state_ptr local_res = 0; -static bool resolveLibraryInternal() -{ #if QT_CONFIG(library) && !defined(Q_OS_QNX) +namespace { +struct LibResolv +{ + enum { +#ifdef RES_NORELOAD + // If RES_NORELOAD is defined, then the libc is capable of watching + // /etc/resolv.conf for changes and reloading as necessary. So accept + // whatever is configured. + ReinitNecessary = false +#else + ReinitNecessary = true +#endif + }; + + QLibrary lib; + LibResolv(); + ~LibResolv() { lib.unload(); } +}; +} + +LibResolv::LibResolv() +{ QLibrary lib; #ifdef LIBRESOLV_SO lib.setFileName(QStringLiteral(LIBRESOLV_SO)); @@ -100,32 +125,45 @@ static bool resolveLibraryInternal() { lib.setFileName(QLatin1String("resolv")); if (!lib.load()) - return false; + return; } - local_res_init = res_init_proto(lib.resolve("__res_init")); - if (!local_res_init) - local_res_init = res_init_proto(lib.resolve("res_init")); - + // res_ninit is required for localDomainName() local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit")); if (!local_res_ninit) local_res_ninit = res_ninit_proto(lib.resolve("res_ninit")); - - if (!local_res_ninit) { - // if we can't get a thread-safe context, we have to use the global _res state - local_res = res_state_ptr(lib.resolve("_res")); - } else { + if (local_res_ninit) { + // we must now find res_nclose local_res_nclose = res_nclose_proto(lib.resolve("res_nclose")); if (!local_res_nclose) local_res_nclose = res_nclose_proto(lib.resolve("__res_nclose")); if (!local_res_nclose) - local_res_ninit = 0; + local_res_ninit = nullptr; } -#endif - return true; + if (ReinitNecessary || !local_res_ninit) { + local_res_init = res_init_proto(lib.resolve("__res_init")); + if (!local_res_init) + local_res_init = res_init_proto(lib.resolve("res_init")); + + if (local_res_init && !local_res_ninit) { + // if we can't get a thread-safe context, we have to use the global _res state + local_res = res_state_ptr(lib.resolve("_res")); + } + } +} +Q_GLOBAL_STATIC(LibResolv, libResolv) + +static void resolveLibrary(LibResolvFeature f) +{ + if (LibResolv::ReinitNecessary || f == NeedResNInit) + libResolv(); +} +#else // QT_CONFIG(library) || Q_OS_QNX +static void resolveLibrary(LibResolvFeature) +{ } -Q_GLOBAL_STATIC_WITH_ARGS(bool, resolveLibrary, (resolveLibraryInternal())) +#endif // QT_CONFIG(library) || Q_OS_QNX QHostInfo QHostInfoAgent::fromName(const QString &hostName) { @@ -137,7 +175,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) #endif // Load res_init on demand. - resolveLibrary(); + resolveLibrary(NeedResInit); // If res_init is available, poll it. if (local_res_init) @@ -275,7 +313,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) QString QHostInfo::localDomainName() { #if !defined(Q_OS_VXWORKS) && !defined(Q_OS_ANDROID) - resolveLibrary(); + resolveLibrary(NeedResNInit); if (local_res_ninit) { // using thread-safe version res_state_ptr state = res_state_ptr(malloc(sizeof(*state))); diff --git a/src/network/kernel/qnetworkdatagram.cpp b/src/network/kernel/qnetworkdatagram.cpp index d167190684..50421fa7f5 100644 --- a/src/network/kernel/qnetworkdatagram.cpp +++ b/src/network/kernel/qnetworkdatagram.cpp @@ -442,10 +442,11 @@ void QNetworkDatagram::setData(const QByteArray &data) } /*! - \fn QNetworkDatagram QNetworkDatagram::makeReply(const QByteArray &data) const + \fn QNetworkDatagram QNetworkDatagram::makeReply(const QByteArray &payload) const & + \fn QNetworkDatagram QNetworkDatagram::makeReply(const QByteArray &payload) && Creates a new QNetworkDatagram representing a reply to this incoming datagram - and sets the payload data to \a data. This function is a very convenient + and sets the payload data to \a payload. This function is a very convenient way of responding to a datagram back to the original sender. Example: @@ -495,6 +496,7 @@ void QNetworkDatagram::setData(const QByteArray &data) \endcode */ + static bool isNonMulticast(const QHostAddress &addr) { // is it a multicast address? @@ -528,6 +530,11 @@ void QNetworkDatagram::destroy(QNetworkDatagramPrivate *d) delete d; } +/*! \fn void QNetworkDatagram::swap(QNetworkDatagram &other) + Swaps this instance with \a other. +*/ + + QT_END_NAMESPACE #endif // QT_NO_UDPSOCKET diff --git a/src/network/kernel/qnetworkdatagram.h b/src/network/kernel/qnetworkdatagram.h index fa994d6170..1acb44a1e0 100644 --- a/src/network/kernel/qnetworkdatagram.h +++ b/src/network/kernel/qnetworkdatagram.h @@ -63,7 +63,7 @@ public: QNetworkDatagram(QNetworkDatagram &&other) Q_DECL_NOTHROW : d(other.d) - { other.d = Q_NULLPTR; } + { other.d = nullptr; } QNetworkDatagram &operator=(QNetworkDatagram &&other) Q_DECL_NOTHROW { swap(other); return *this; } @@ -91,7 +91,7 @@ public: QByteArray data() const; void setData(const QByteArray &data); -#ifdef Q_COMPILER_REF_QUALIFIERS +#if defined(Q_COMPILER_REF_QUALIFIERS) || defined(Q_CLANG_QDOC) QNetworkDatagram makeReply(const QByteArray &payload) const & { return makeReply_helper(payload); } QNetworkDatagram makeReply(const QByteArray &payload) && diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp index 3857ff87b9..cab008d2ef 100644 --- a/src/network/kernel/qnetworkinterface.cpp +++ b/src/network/kernel/qnetworkinterface.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2017 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -59,19 +59,15 @@ static QList<QNetworkInterfacePrivate *> postProcess(QList<QNetworkInterfacePriv // The math is: // broadcast = IP | ~netmask - QList<QNetworkInterfacePrivate *>::Iterator it = list.begin(); - const QList<QNetworkInterfacePrivate *>::Iterator end = list.end(); - for ( ; it != end; ++it) { - QList<QNetworkAddressEntry>::Iterator addr_it = (*it)->addressEntries.begin(); - const QList<QNetworkAddressEntry>::Iterator addr_end = (*it)->addressEntries.end(); - for ( ; addr_it != addr_end; ++addr_it) { - if (addr_it->ip().protocol() != QAbstractSocket::IPv4Protocol) + for (QNetworkInterfacePrivate *interface : list) { + for (QNetworkAddressEntry &address : interface->addressEntries) { + if (address.ip().protocol() != QAbstractSocket::IPv4Protocol) continue; - if (!addr_it->netmask().isNull() && addr_it->broadcast().isNull()) { - QHostAddress bcast = addr_it->ip(); - bcast = QHostAddress(bcast.toIPv4Address() | ~addr_it->netmask().toIPv4Address()); - addr_it->setBroadcast(bcast); + if (!address.netmask().isNull() && address.broadcast().isNull()) { + QHostAddress bcast = address.ip(); + bcast = QHostAddress(bcast.toIPv4Address() | ~address.netmask().toIPv4Address()); + address.setBroadcast(bcast); } } } @@ -91,17 +87,16 @@ QNetworkInterfaceManager::~QNetworkInterfaceManager() QSharedDataPointer<QNetworkInterfacePrivate> QNetworkInterfaceManager::interfaceFromName(const QString &name) { - QList<QSharedDataPointer<QNetworkInterfacePrivate> > interfaceList = allInterfaces(); - QList<QSharedDataPointer<QNetworkInterfacePrivate> >::ConstIterator it = interfaceList.constBegin(); + const auto interfaceList = allInterfaces(); bool ok; uint index = name.toUInt(&ok); - for ( ; it != interfaceList.constEnd(); ++it) { - if (ok && (*it)->index == int(index)) - return *it; - else if ((*it)->name == name) - return *it; + for (const auto &interface : interfaceList) { + if (ok && interface->index == int(index)) + return interface; + else if (interface->name == name) + return interface; } return empty; @@ -109,11 +104,11 @@ QSharedDataPointer<QNetworkInterfacePrivate> QNetworkInterfaceManager::interface QSharedDataPointer<QNetworkInterfacePrivate> QNetworkInterfaceManager::interfaceFromIndex(int index) { - QList<QSharedDataPointer<QNetworkInterfacePrivate> > interfaceList = allInterfaces(); - QList<QSharedDataPointer<QNetworkInterfacePrivate> >::ConstIterator it = interfaceList.constBegin(); - for ( ; it != interfaceList.constEnd(); ++it) - if ((*it)->index == index) - return *it; + const auto interfaceList = allInterfaces(); + for (const auto &interface : interfaceList) { + if (interface->index == index) + return interface; + } return empty; } @@ -124,8 +119,15 @@ QList<QSharedDataPointer<QNetworkInterfacePrivate> > QNetworkInterfaceManager::a QList<QSharedDataPointer<QNetworkInterfacePrivate> > result; result.reserve(list.size()); - for (QNetworkInterfacePrivate *ptr : list) + for (QNetworkInterfacePrivate *ptr : list) { + if ((ptr->flags & QNetworkInterface::IsUp) == 0) { + // if the network interface isn't UP, the addresses are ineligible for DNS + for (auto &addr : ptr->addressEntries) + addr.setDnsEligibility(QNetworkAddressEntry::DnsIneligible); + } + result << QSharedDataPointer<QNetworkInterfacePrivate>(ptr); + } return result; } @@ -164,6 +166,32 @@ QString QNetworkInterfacePrivate::makeHwAddress(int len, uchar *data) */ /*! + \enum QNetworkAddressEntry::DnsEligilibilityStatus + \since 5.11 + + This enum indicates whether a given host address is eligible to be + published in the Domain Name System (DNS) or other similar name resolution + mechanisms. In general, an address is suitable for publication if it is an + address this machine will be reached at for an indeterminate amount of + time, though it need not be permanent. For example, addresses obtained via + DHCP are often eligible, but cryptographically-generated temporary IPv6 + addresses are not. + + \value DnsEligibilityUnknown Qt and the operating system could not determine + whether this address should be published or not. + The application may need to apply further + heuristics if it cannot find any eligible + addresses. + \value DnsEligible This address is eligible for publication in DNS. + \value DnsIneligible This address should not be published in DNS and + should not be transmitted to other parties, + except maybe as the source address of an outgoing + packet. + + \sa dnsEligibility(), setDnsEligibility() +*/ + +/*! Constructs an empty QNetworkAddressEntry object. */ QNetworkAddressEntry::QNetworkAddressEntry() @@ -218,6 +246,39 @@ bool QNetworkAddressEntry::operator==(const QNetworkAddressEntry &other) const } /*! + \since 5.11 + + Returns whether this address is eligible for publication in the Domain Name + System (DNS) or similar name resolution mechanisms. + + In general, an address is suitable for publication if it is an address this + machine will be reached at for an indeterminate amount of time, though it + need not be permanent. For example, addresses obtained via DHCP are often + eligible, but cryptographically-generated temporary IPv6 addresses are not. + + On some systems, QNetworkInterface will need to heuristically determine + which addresses are eligible. + + \sa isLifetimeKnown(), isPermanent(), setDnsEligibility() +*/ +QNetworkAddressEntry::DnsEligibilityStatus QNetworkAddressEntry::dnsEligibility() const +{ + return d->dnsEligibility; +} + +/*! + \since 5.11 + + Sets the DNS eligibility flag for this address to \a status. + + \sa dnsEligibility() +*/ +void QNetworkAddressEntry::setDnsEligibility(DnsEligibilityStatus status) +{ + d->dnsEligibility = status; +} + +/*! \fn bool QNetworkAddressEntry::operator!=(const QNetworkAddressEntry &other) const Returns \c true if this network address entry is different from \a @@ -340,6 +401,122 @@ void QNetworkAddressEntry::setBroadcast(const QHostAddress &newBroadcast) } /*! + \since 5.11 + + Returns \c true if the address lifetime is known, \c false if not. If the + lifetime is not known, both preferredLifetime() and validityLifetime() will + return QDeadlineTimer::Forever. + + \sa preferredLifetime(), validityLifetime(), setAddressLifetime(), clearAddressLifetime() +*/ +bool QNetworkAddressEntry::isLifetimeKnown() const +{ + return d->lifetimeKnown; +} + +/*! + \since 5.11 + + Returns the deadline when this address becomes deprecated (no longer + preferred), if known. If the address lifetime is not known (see + isLifetimeKnown()), this function always returns QDeadlineTimer::Forever. + + While an address is preferred, it may be used by the operating system as + the source address for new, outgoing packets. After it becomes deprecated, + it will remain valid for incoming packets for a while longer until finally + removed (see validityLifetime()). + + \sa validityLifetime(), isLifetimeKnown(), setAddressLifetime(), clearAddressLifetime() +*/ +QDeadlineTimer QNetworkAddressEntry::preferredLifetime() const +{ + return d->preferredLifetime; +} + +/*! + \since 5.11 + + Returns the deadline when this address becomes invalid and will be removed + from the networking stack, if known. If the address lifetime is not known + (see isLifetimeKnown()), this function always returns + QDeadlineTimer::Forever. + + While an address is valid, it will be accepted by the operating system as a + valid destination address for this machine. Whether it is used as a source + address for new, outgoing packets is controlled by, among other rules, the + preferred lifetime (see preferredLifetime()). + + \sa preferredLifetime(), isLifetimeKnown(), setAddressLifetime(), clearAddressLifetime() +*/ +QDeadlineTimer QNetworkAddressEntry::validityLifetime() const +{ + return d->validityLifetime; +} + +/*! + \since 5.11 + + Sets both the preferred and valid lifetimes for this address to the \a + preferred and \a validity deadlines, respectively. After this call, + isLifetimeKnown() will return \c true, even if both parameters are + QDeadlineTimer::Forever. + + \sa preferredLifetime(), validityLifetime(), isLifetimeKnown(), clearAddressLifetime() +*/ +void QNetworkAddressEntry::setAddressLifetime(QDeadlineTimer preferred, QDeadlineTimer validity) +{ + d->preferredLifetime = preferred; + d->validityLifetime = validity; + d->lifetimeKnown = true; +} + +/*! + \since 5.11 + + Resets both the preferred and valid lifetimes for this address. After this + call, isLifetimeKnown() will return \c false. + + \sa preferredLifetime(), validityLifetime(), isLifetimeKnown(), setAddressLifetime() +*/ +void QNetworkAddressEntry::clearAddressLifetime() +{ + d->preferredLifetime = QDeadlineTimer::Forever; + d->validityLifetime = QDeadlineTimer::Forever; + d->lifetimeKnown = false; +} + +/*! + \since 5.11 + + Returns \c true if this address is permanent on this interface, \c false if + it's temporary. A permenant address is one which has no expiration time and + is often static (manually configured). + + If this information could not be determined, this function returns \c true. + + \note Depending on the operating system and the networking configuration + tool, it is possible for a temporary address to be interpreted as + permanent, if the tool did not inform the details correctly to the + operating system. + + \sa isLifetimeKnown(), validityLifetime(), isTemporary() +*/ +bool QNetworkAddressEntry::isPermanent() const +{ + return d->validityLifetime.isForever(); +} + +/*! + \fn bool QNetworkAddressEntry::isTemporary() const + \since 5.11 + + Returns \c true if this address is temporary on this interface, \c false if + it's permanent. + + \sa isLifetimeKnown(), validityLifetime(), isPermanent() +*/ + +/*! \class QNetworkInterface \brief The QNetworkInterface class provides a listing of the host's IP addresses and network interfaces. @@ -396,6 +573,57 @@ void QNetworkAddressEntry::setBroadcast(const QHostAddress &newBroadcast) */ /*! + \enum QNetworkInterface::InterfaceType + + Specifies the type of hardware (PHY layer, OSI level 1) this interface is, + if it could be determined. Interface types that are not among those listed + below will generally be listed as Unknown, though future versions of Qt may + add new enumeration values. + + The possible values are: + + \value Unknown The interface type could not be determined or is not + one of the other listed types. + \value Loopback The virtual loopback interface, which is assigned + the loopback IP addresses (127.0.0.1, ::1). + \value Virtual A type of interface determined to be virtual, but + not any of the other possible types. For example, + tunnel interfaces are (currently) detected as + virtual ones. + \value Ethernet IEEE 802.3 Ethernet interfaces, though on many + systems other types of IEEE 802 interfaces may also + be detected as Ethernet (especially Wi-Fi). + \value Wifi IEEE 802.11 Wi-Fi interfaces. Note that on some + systems, QNetworkInterface may be unable to + distinguish regular Ethernet from Wi-Fi and will + not return this enum value. + \value Ieee80211 An alias for WiFi. + \value CanBus ISO 11898 Controller Area Network bus interfaces, + usually found on automotive systems. + \value Fddi ANSI X3T12 Fiber Distributed Data Interface, a local area + network over optical fibers. + \value Ppp Point-to-Point Protocol interfaces, establishing a + direct connection between two nodes over a lower + transport layer (often serial over radio or physical + line). + \value Slip Serial Line Internet Protocol interfaces. + \value Phonet Interfaces using the Linux Phonet socket family, for + communication with cellular modems. See the + \l {https://www.kernel.org/doc/Documentation/networking/phonet.txt}{Linux kernel documentation} + for more information. + \value Ieee802154 IEEE 802.15.4 Personal Area Network interfaces, other + than 6LoWPAN (see below). + \value SixLoWPAN 6LoWPAN (IPv6 over Low-power Wireless Personal Area + Networks) interfaces, which operate on IEEE 802.15.4 + PHY, but have specific header compression schemes + for IPv6 and UDP. This type of interface is often + used for mesh networking. + \value Ieee80216 IEEE 802.16 Wireless Metropolitan Area Network, also + known under the commercial name "WiMAX". + \value Ieee1394 IEEE 1394 interfaces (a.k.a. "FireWire"). +*/ + +/*! Constructs an empty network interface object. */ QNetworkInterface::QNetworkInterface() @@ -461,6 +689,29 @@ int QNetworkInterface::index() const } /*! + \since 5.11 + + Returns the maximum transmission unit on this interface, if known, or 0 + otherwise. + + The maximum transmission unit is the largest packet that may be sent on + this interface without incurring link-level fragmentation. Applications may + use this value to calculate the size of the payload that will fit an + unfragmented UDP datagram. Remember to subtract the sizes of headers used + in your communication over the interface, e.g. TCP (20 bytes) or UDP (12), + IPv4 (20) or IPv6 (40, absent some form of header compression), when + computing how big a payload you can transmit. Also note that the MTU along + the full path (the Path MTU) to the destination may be smaller than the + interface's MTU. + + \sa QUdpSocket +*/ +int QNetworkInterface::maxTransmissionUnit() const +{ + return d ? d->mtu : 0; +} + +/*! Returns the name of this network interface. On Unix systems, this is a string containing the type of the interface and optionally a sequence number, such as "eth0", "lo" or "pcn0". On Windows, it's @@ -499,6 +750,19 @@ QNetworkInterface::InterfaceFlags QNetworkInterface::flags() const } /*! + \since 5.11 + + Returns the type of this interface, if it could be determined. If it could + not be determined, this function returns QNetworkInterface::Unknown. + + \sa hardwareAddress() +*/ +QNetworkInterface::InterfaceType QNetworkInterface::type() const +{ + return d ? d->type : Unknown; +} + +/*! Returns the low-level hardware address for this interface. On Ethernet interfaces, this will be a MAC address in string representation, separated by colons. @@ -506,6 +770,8 @@ QNetworkInterface::InterfaceFlags QNetworkInterface::flags() const Other interface types may have other types of hardware addresses. Implementations should not depend on this function returning a valid MAC address. + + \sa type() */ QString QNetworkInterface::hardwareAddress() const { @@ -691,4 +957,6 @@ QDebug operator<<(QDebug debug, const QNetworkInterface &networkInterface) QT_END_NAMESPACE +#include "moc_qnetworkinterface.cpp" + #endif // QT_NO_NETWORKINTERFACE diff --git a/src/network/kernel/qnetworkinterface.h b/src/network/kernel/qnetworkinterface.h index c31621c3cb..f7ef192dc0 100644 --- a/src/network/kernel/qnetworkinterface.h +++ b/src/network/kernel/qnetworkinterface.h @@ -49,13 +49,19 @@ QT_BEGIN_NAMESPACE - +class QDeadlineTimer; template<typename T> class QList; class QNetworkAddressEntryPrivate; class Q_NETWORK_EXPORT QNetworkAddressEntry { public: + enum DnsEligibilityStatus : qint8 { + DnsEligibilityUnknown = -1, + DnsIneligible = 0, + DnsEligible = 1 + }; + QNetworkAddressEntry(); QNetworkAddressEntry(const QNetworkAddressEntry &other); #ifdef Q_COMPILER_RVALUE_REFS @@ -70,6 +76,9 @@ public: inline bool operator!=(const QNetworkAddressEntry &other) const { return !(*this == other); } + DnsEligibilityStatus dnsEligibility() const; + void setDnsEligibility(DnsEligibilityStatus status); + QHostAddress ip() const; void setIp(const QHostAddress &newIp); @@ -81,6 +90,14 @@ public: QHostAddress broadcast() const; void setBroadcast(const QHostAddress &newBroadcast); + bool isLifetimeKnown() const; + QDeadlineTimer preferredLifetime() const; + QDeadlineTimer validityLifetime() const; + void setAddressLifetime(QDeadlineTimer preferred, QDeadlineTimer validity); + void clearAddressLifetime(); + bool isPermanent() const; + bool isTemporary() const { return !isPermanent(); } + private: QScopedPointer<QNetworkAddressEntryPrivate> d; }; @@ -90,6 +107,7 @@ Q_DECLARE_SHARED(QNetworkAddressEntry) class QNetworkInterfacePrivate; class Q_NETWORK_EXPORT QNetworkInterface { + Q_GADGET public: enum InterfaceFlag { IsUp = 0x1, @@ -100,6 +118,27 @@ public: CanMulticast = 0x20 }; Q_DECLARE_FLAGS(InterfaceFlags, InterfaceFlag) + Q_FLAG(InterfaceFlags) + + enum InterfaceType { + Loopback = 1, + Virtual, + Ethernet, + Slip, + CanBus, + Ppp, + Fddi, + Wifi, + Ieee80211 = Wifi, // alias + Phonet, + Ieee802154, + SixLoWPAN, // 6LoWPAN, but we can't start with a digit + Ieee80216, + Ieee1394, + + Unknown = 0 + }; + Q_ENUM(InterfaceType) QNetworkInterface(); QNetworkInterface(const QNetworkInterface &other); @@ -114,9 +153,11 @@ public: bool isValid() const; int index() const; + int maxTransmissionUnit() const; QString name() const; QString humanReadableName() const; InterfaceFlags flags() const; + InterfaceType type() const; QString hardwareAddress() const; QList<QNetworkAddressEntry> addressEntries() const; diff --git a/src/network/kernel/qnetworkinterface_linux.cpp b/src/network/kernel/qnetworkinterface_linux.cpp new file mode 100644 index 0000000000..01b2811070 --- /dev/null +++ b/src/network/kernel/qnetworkinterface_linux.cpp @@ -0,0 +1,443 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qnetworkinterface.h" +#include "qnetworkinterface_p.h" +#include "qnetworkinterface_unix_p.h" + +#include <qendian.h> +#include <qobjectdefs.h> +#include <qvarlengtharray.h> + +// accordding to rtnetlink(7) +#include <asm/types.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/wireless.h> +#include <sys/socket.h> + +/* in case these aren't defined in linux/if_arp.h (added since 2.6.28) */ +#define ARPHRD_PHONET 820 /* v2.6.29: PhoNet media type */ +#define ARPHRD_PHONET_PIPE 821 /* v2.6.29: PhoNet pipe header */ +#define ARPHRD_IEEE802154 804 /* v2.6.31 */ +#define ARPHRD_6LOWPAN 825 /* v3.14: IPv6 over LoWPAN */ + +QT_BEGIN_NAMESPACE + +enum { + BufferSize = 8192 +}; + +static QNetworkInterface::InterfaceType probeIfType(int socket, struct ifreq *req, short arptype) +{ + switch (ushort(arptype)) { + case ARPHRD_LOOPBACK: + return QNetworkInterface::Loopback; + + case ARPHRD_ETHER: + // check if it's a WiFi interface + if (qt_safe_ioctl(socket, SIOCGIWMODE, req) >= 0) + return QNetworkInterface::Wifi; + return QNetworkInterface::Ethernet; + + case ARPHRD_SLIP: + case ARPHRD_CSLIP: + case ARPHRD_SLIP6: + case ARPHRD_CSLIP6: + return QNetworkInterface::Slip; + + case ARPHRD_CAN: + return QNetworkInterface::CanBus; + + case ARPHRD_PPP: + return QNetworkInterface::Ppp; + + case ARPHRD_FDDI: + return QNetworkInterface::Fddi; + + case ARPHRD_IEEE80211: + case ARPHRD_IEEE80211_PRISM: + case ARPHRD_IEEE80211_RADIOTAP: + return QNetworkInterface::Ieee80211; + + case ARPHRD_IEEE802154: + return QNetworkInterface::Ieee802154; + + case ARPHRD_PHONET: + case ARPHRD_PHONET_PIPE: + return QNetworkInterface::Phonet; + + case ARPHRD_6LOWPAN: + return QNetworkInterface::SixLoWPAN; + + case ARPHRD_TUNNEL: + case ARPHRD_TUNNEL6: + case ARPHRD_NONE: + case ARPHRD_VOID: + return QNetworkInterface::Virtual; + } + return QNetworkInterface::Unknown; +} + + +namespace { +struct NetlinkSocket +{ + int sock; + NetlinkSocket(int bufferSize) + { + sock = qt_safe_socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (Q_UNLIKELY(sock == -1)) + qErrnoWarning("Could not create AF_NETLINK socket"); + + // set buffer length + socklen_t len = sizeof(bufferSize); + setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &bufferSize, len); + } + + ~NetlinkSocket() + { + if (sock != -1) + qt_safe_close(sock); + } + + operator int() const { return sock; } +}; + +template <typename Lambda> struct ProcessNetlinkRequest +{ + using FunctionTraits = QtPrivate::FunctionPointer<decltype(&Lambda::operator())>; + using FirstArgument = typename FunctionTraits::Arguments::Car; + + static int expectedTypeForRequest(int rtype) + { + Q_STATIC_ASSERT(RTM_NEWADDR == RTM_GETADDR - 2); + Q_STATIC_ASSERT(RTM_NEWLINK == RTM_GETLINK - 2); + Q_ASSERT(rtype == RTM_GETADDR || rtype == RTM_GETLINK); + return rtype - 2; + } + + void operator()(int sock, nlmsghdr *hdr, char *buf, size_t bufsize, Lambda &&func) + { + // send the request + if (send(sock, hdr, hdr->nlmsg_len, 0) != ssize_t(hdr->nlmsg_len)) + return; + + // receive and parse the request + int expectedType = expectedTypeForRequest(hdr->nlmsg_type); + const bool isDump = hdr->nlmsg_flags & NLM_F_DUMP; + forever { + qsizetype len = recv(sock, buf, bufsize, 0); + hdr = reinterpret_cast<struct nlmsghdr *>(buf); + if (!NLMSG_OK(hdr, len)) + return; + + auto arg = reinterpret_cast<FirstArgument>(NLMSG_DATA(hdr)); + size_t payloadLen = NLMSG_PAYLOAD(hdr, 0); + + // is this a multipart message? + Q_ASSERT(isDump == !!(hdr->nlmsg_flags & NLM_F_MULTI)); + if (!isDump) { + // no, single message + if (hdr->nlmsg_type == expectedType && payloadLen >= sizeof(FirstArgument)) + return void(func(arg, payloadLen)); + } else { + // multipart, parse until done + do { + if (hdr->nlmsg_type == NLMSG_DONE) + return; + if (hdr->nlmsg_type != expectedType || payloadLen < sizeof(FirstArgument)) + break; + func(arg, payloadLen); + + // NLMSG_NEXT also updates the len variable + hdr = NLMSG_NEXT(hdr, len); + arg = reinterpret_cast<FirstArgument>(NLMSG_DATA(hdr)); + payloadLen = NLMSG_PAYLOAD(hdr, 0); + } while (NLMSG_OK(hdr, len)); + + if (len == 0) + continue; // get new datagram + } + +#ifndef QT_NO_DEBUG + if (NLMSG_OK(hdr, len)) + qWarning("QNetworkInterface/AF_NETLINK: received unknown packet type (%d) or too short (%u)", + hdr->nlmsg_type, hdr->nlmsg_len); + else + qWarning("QNetworkInterface/AF_NETLINK: received invalid packet with size %d", int(len)); +#endif + return; + } + } +}; + +template <typename Lambda> +void processNetlinkRequest(int sock, struct nlmsghdr *hdr, char *buf, size_t bufsize, Lambda &&l) +{ + ProcessNetlinkRequest<Lambda>()(sock, hdr, buf, bufsize, std::forward<Lambda>(l)); +} +} + +uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name) +{ + uint index = 0; + if (name.length() >= IFNAMSIZ) + return index; + + int socket = qt_safe_socket(AF_INET, SOCK_DGRAM, 0); + if (socket >= 0) { + struct ifreq req; + req.ifr_ifindex = 0; + strcpy(req.ifr_name, name.toLatin1().constData()); + + if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0) + index = req.ifr_ifindex; + qt_safe_close(socket); + } + return index; +} + +QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index) +{ + int socket = qt_safe_socket(AF_INET, SOCK_DGRAM, 0); + if (socket >= 0) { + struct ifreq req; + 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); + } + return QString(); +} + +static QList<QNetworkInterfacePrivate *> getInterfaces(int sock, char *buf) +{ + QList<QNetworkInterfacePrivate *> result; + struct ifreq req; + + // request all links + struct { + struct nlmsghdr req; + struct ifinfomsg ifi; + } ifi_req; + memset(&ifi_req, 0, sizeof(ifi_req)); + + ifi_req.req.nlmsg_len = sizeof(ifi_req); + ifi_req.req.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + ifi_req.req.nlmsg_type = RTM_GETLINK; + + // parse the interfaces + processNetlinkRequest(sock, &ifi_req.req, buf, BufferSize, [&](ifinfomsg *ifi, size_t len) { + auto iface = new QNetworkInterfacePrivate; + iface->index = ifi->ifi_index; + iface->flags = convertFlags(ifi->ifi_flags); + + // read attributes + auto rta = reinterpret_cast<struct rtattr *>(ifi + 1); + len -= sizeof(*ifi); + for ( ; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + int payloadLen = RTA_PAYLOAD(rta); + auto payloadPtr = reinterpret_cast<char *>(RTA_DATA(rta)); + + switch (rta->rta_type) { + case IFLA_ADDRESS: // link-level address + iface->hardwareAddress = + iface->makeHwAddress(payloadLen, reinterpret_cast<uchar *>(payloadPtr)); + break; + + case IFLA_IFNAME: // interface name + Q_ASSERT(payloadLen <= int(sizeof(req.ifr_name))); + memcpy(req.ifr_name, payloadPtr, payloadLen); // including terminating NUL + iface->name = QString::fromLatin1(payloadPtr, payloadLen - 1); + break; + + case IFLA_MTU: + Q_ASSERT(payloadLen == sizeof(int)); + iface->mtu = *reinterpret_cast<int *>(payloadPtr); + break; + + case IFLA_OPERSTATE: // operational state + if (*payloadPtr != IF_OPER_UNKNOWN) { + // override the flag + iface->flags &= ~QNetworkInterface::IsUp; + if (*payloadPtr == IF_OPER_UP) + iface->flags |= QNetworkInterface::IsUp; + } + break; + } + } + + if (Q_UNLIKELY(iface->name.isEmpty())) { + qWarning("QNetworkInterface: found interface %d with no name", iface->index); + delete iface; + } else { + iface->type = probeIfType(sock, &req, ifi->ifi_type); + result.append(iface); + } + }); + return result; +} + +static void getAddresses(int sock, char *buf, QList<QNetworkInterfacePrivate *> &result) +{ + // request all addresses + struct { + struct nlmsghdr req; + struct ifaddrmsg ifa; + } ifa_req; + memset(&ifa_req, 0, sizeof(ifa_req)); + + ifa_req.req.nlmsg_len = sizeof(ifa_req); + ifa_req.req.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + ifa_req.req.nlmsg_type = RTM_GETADDR; + ifa_req.req.nlmsg_seq = 1; + + // parse the addresses + processNetlinkRequest(sock, &ifa_req.req, buf, BufferSize, [&](ifaddrmsg *ifa, size_t len) { + if (Q_UNLIKELY(ifa->ifa_family != AF_INET && ifa->ifa_family != AF_INET6)) { + // unknown address types + return; + } + + // find the interface this is relevant to + QNetworkInterfacePrivate *iface = nullptr; + for (auto candidate : qAsConst(result)) { + if (candidate->index != int(ifa->ifa_index)) + continue; + iface = candidate; + break; + } + + if (Q_UNLIKELY(!iface)) { + qWarning("QNetworkInterface/AF_NETLINK: found unknown interface with index %d", ifa->ifa_index); + return; + } + + QNetworkAddressEntry entry; + quint32 flags = ifa->ifa_flags; // may be overwritten by IFA_FLAGS + + auto makeAddress = [=](uchar *ptr, int len) { + QHostAddress addr; + if (ifa->ifa_family == AF_INET) { + Q_ASSERT(len == 4); + addr.setAddress(qFromBigEndian<quint32>(ptr)); + } else { + Q_ASSERT(len == 16); + addr.setAddress(ptr); + + // do we need a scope ID? + if (addr.isLinkLocal()) + addr.setScopeId(iface->name); + } + return addr; + }; + + // read attributes + auto rta = reinterpret_cast<struct rtattr *>(ifa + 1); + len -= sizeof(*ifa); + for ( ; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + int payloadLen = RTA_PAYLOAD(rta); + auto payloadPtr = reinterpret_cast<uchar *>(RTA_DATA(rta)); + + switch (rta->rta_type) { + case IFA_ADDRESS: // address + entry.setIp(makeAddress(payloadPtr, payloadLen)); + break; + + case IFA_BROADCAST: + Q_ASSERT(ifa->ifa_family == AF_INET); + entry.setBroadcast(makeAddress(payloadPtr, payloadLen)); + break; + + case IFA_CACHEINFO: + if (size_t(payloadLen) >= sizeof(ifa_cacheinfo)) { + auto cacheinfo = reinterpret_cast<ifa_cacheinfo *>(payloadPtr); + auto toDeadline = [](quint32 lifetime) -> QDeadlineTimer { + if (lifetime == quint32(-1)) + return QDeadlineTimer::Forever; + return QDeadlineTimer(lifetime * 1000); + }; + entry.setAddressLifetime(toDeadline(cacheinfo->ifa_prefered), toDeadline(cacheinfo->ifa_valid)); + } + break; + + case IFA_FLAGS: + Q_ASSERT(payloadLen == 4); + flags = qFromUnaligned<quint32>(payloadPtr); + break; + } + } + + // now handle flags + QNetworkInterfacePrivate::calculateDnsEligibility(&entry, + flags & IFA_F_TEMPORARY, + flags & IFA_F_DEPRECATED); + + + if (!entry.ip().isNull()) { + entry.setPrefixLength(ifa->ifa_prefixlen); + iface->addressEntries.append(entry); + } + }); +} + +QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan() +{ + // open netlink socket + QList<QNetworkInterfacePrivate *> result; + NetlinkSocket sock(BufferSize); + if (Q_UNLIKELY(sock == -1)) + return result; + + QByteArray buffer(BufferSize, Qt::Uninitialized); + char *buf = buffer.data(); + + result = getInterfaces(sock, buf); + getAddresses(sock, buf, result); + + return result; +} + +QT_END_NAMESPACE diff --git a/src/network/kernel/qnetworkinterface_p.h b/src/network/kernel/qnetworkinterface_p.h index 51901eeda8..87a46b75fa 100644 --- a/src/network/kernel/qnetworkinterface_p.h +++ b/src/network/kernel/qnetworkinterface_p.h @@ -52,7 +52,9 @@ // #include <QtNetwork/private/qtnetworkglobal_p.h> +#include <QtNetwork/qnetworkinterface.h> #include <QtCore/qatomic.h> +#include <QtCore/qdeadlinetimer.h> #include <QtCore/qlist.h> #include <QtCore/qreadwritelock.h> #include <QtCore/qstring.h> @@ -69,7 +71,12 @@ class QNetworkAddressEntryPrivate public: QHostAddress address; QHostAddress broadcast; + QDeadlineTimer preferredLifetime = QDeadlineTimer::Forever; + QDeadlineTimer validityLifetime = QDeadlineTimer::Forever; + QNetmask netmask; + bool lifetimeKnown = false; + QNetworkAddressEntry::DnsEligibilityStatus dnsEligibility = QNetworkAddressEntry::DnsEligibilityUnknown; }; class QNetworkInterfacePrivate: public QSharedData @@ -81,7 +88,9 @@ public: { } int index; // interface index, if know + int mtu = 0; QNetworkInterface::InterfaceFlags flags; + QNetworkInterface::InterfaceType type = QNetworkInterface::Unknown; QString name; QString friendlyName; @@ -90,6 +99,20 @@ public: QList<QNetworkAddressEntry> addressEntries; static QString makeHwAddress(int len, uchar *data); + static void calculateDnsEligibility(QNetworkAddressEntry *entry, bool isTemporary, + bool isDeprecated) + { + // this implements an algorithm that yields the same results as Windows + // produces, for the same input (as far as I can test) + if (isTemporary || isDeprecated) + entry->setDnsEligibility(QNetworkAddressEntry::DnsIneligible); + + AddressClassification cl = QHostAddressPrivate::classify(entry->ip()); + if (cl == LoopbackAddress || cl == LinkLocalAddress) + entry->setDnsEligibility(QNetworkAddressEntry::DnsIneligible); + else + entry->setDnsEligibility(QNetworkAddressEntry::DnsEligible); + } private: // disallow copying -- avoid detaching diff --git a/src/network/kernel/qnetworkinterface_uikit_p.h b/src/network/kernel/qnetworkinterface_uikit_p.h new file mode 100644 index 0000000000..ea40e74f5c --- /dev/null +++ b/src/network/kernel/qnetworkinterface_uikit_p.h @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#ifndef QNETWORKINTERFACE_UIKIT_P_H +#define QNETWORKINTERFACE_UIKIT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// Platform SDK for iOS, tvOS and watchOS is missing those headers: +// net/if_media.h, netinet/in_var.h, netinet6/in6_var.h This header is +// a workaround, it provides missing macros and structs. + +// <net/if_media.h>: + +/* + * Ethernet + */ +#define IFM_ETHER 0x00000020 +/* + * FDDI + */ +#define IFM_FDDI 0x00000060 +/* + * IEEE 802.11 Wireless + */ +#define IFM_IEEE80211 0x00000080 +/* + * Masks + */ +#define IFM_NMASK 0x000000e0 /* Network type */ +/* + * Macros to extract various bits of information from the media word. + */ +#define IFM_TYPE(x) ((x) & IFM_NMASK) + +// <netinet6/in6_var.h>: + +struct in6_addrlifetime { + time_t ia6t_expire; /* valid lifetime expiration time */ + time_t ia6t_preferred; /* preferred lifetime expiration time */ + u_int32_t ia6t_vltime; /* valid lifetime */ + u_int32_t ia6t_pltime; /* prefix lifetime */ +}; + +/* + * IPv6 interface statistics, as defined in RFC2465 Ipv6IfStatsEntry (p12). + */ +struct in6_ifstat { + u_quad_t ifs6_in_receive; /* # of total input datagram */ + u_quad_t ifs6_in_hdrerr; /* # of datagrams with invalid hdr */ + u_quad_t ifs6_in_toobig; /* # of datagrams exceeded MTU */ + u_quad_t ifs6_in_noroute; /* # of datagrams with no route */ + u_quad_t ifs6_in_addrerr; /* # of datagrams with invalid dst */ + u_quad_t ifs6_in_protounknown; /* # of datagrams with unknown proto */ + /* NOTE: increment on final dst if */ + u_quad_t ifs6_in_truncated; /* # of truncated datagrams */ + u_quad_t ifs6_in_discard; /* # of discarded datagrams */ + /* NOTE: fragment timeout is not here */ + u_quad_t ifs6_in_deliver; /* # of datagrams delivered to ULP */ + /* NOTE: increment on final dst if */ + u_quad_t ifs6_out_forward; /* # of datagrams forwarded */ + /* NOTE: increment on outgoing if */ + u_quad_t ifs6_out_request; /* # of outgoing datagrams from ULP */ + /* NOTE: does not include forwrads */ + u_quad_t ifs6_out_discard; /* # of discarded datagrams */ + u_quad_t ifs6_out_fragok; /* # of datagrams fragmented */ + u_quad_t ifs6_out_fragfail; /* # of datagrams failed on fragment */ + u_quad_t ifs6_out_fragcreat; /* # of fragment datagrams */ + /* NOTE: this is # after fragment */ + u_quad_t ifs6_reass_reqd; /* # of incoming fragmented packets */ + /* NOTE: increment on final dst if */ + u_quad_t ifs6_reass_ok; /* # of reassembled packets */ + /* NOTE: this is # after reass */ + /* NOTE: increment on final dst if */ + u_quad_t ifs6_atmfrag_rcvd; /* # of atomic fragments received */ + u_quad_t ifs6_reass_fail; /* # of reass failures */ + /* NOTE: may not be packet count */ + /* NOTE: increment on final dst if */ + u_quad_t ifs6_in_mcast; /* # of inbound multicast datagrams */ + u_quad_t ifs6_out_mcast; /* # of outbound multicast datagrams */ + + u_quad_t ifs6_cantfoward_icmp6; /* # of ICMPv6 packets received for unreachable dest */ + u_quad_t ifs6_addr_expiry_cnt; /* # of address expiry events (excluding privacy addresses) */ + u_quad_t ifs6_pfx_expiry_cnt; /* # of prefix expiry events */ + u_quad_t ifs6_defrtr_expiry_cnt; /* # of default router expiry events */ +}; + +/* + * ICMPv6 interface statistics, as defined in RFC2466 Ipv6IfIcmpEntry. + * XXX: I'm not sure if this file is the right place for this structure... + */ +struct icmp6_ifstat { + /* + * Input statistics + */ + /* ipv6IfIcmpInMsgs, total # of input messages */ + u_quad_t ifs6_in_msg; + /* ipv6IfIcmpInErrors, # of input error messages */ + u_quad_t ifs6_in_error; + /* ipv6IfIcmpInDestUnreachs, # of input dest unreach errors */ + u_quad_t ifs6_in_dstunreach; + /* ipv6IfIcmpInAdminProhibs, # of input admin. prohibited errs */ + u_quad_t ifs6_in_adminprohib; + /* ipv6IfIcmpInTimeExcds, # of input time exceeded errors */ + u_quad_t ifs6_in_timeexceed; + /* ipv6IfIcmpInParmProblems, # of input parameter problem errors */ + u_quad_t ifs6_in_paramprob; + /* ipv6IfIcmpInPktTooBigs, # of input packet too big errors */ + u_quad_t ifs6_in_pkttoobig; + /* ipv6IfIcmpInEchos, # of input echo requests */ + u_quad_t ifs6_in_echo; + /* ipv6IfIcmpInEchoReplies, # of input echo replies */ + u_quad_t ifs6_in_echoreply; + /* ipv6IfIcmpInRouterSolicits, # of input router solicitations */ + u_quad_t ifs6_in_routersolicit; + /* ipv6IfIcmpInRouterAdvertisements, # of input router advertisements */ + u_quad_t ifs6_in_routeradvert; + /* ipv6IfIcmpInNeighborSolicits, # of input neighbor solicitations */ + u_quad_t ifs6_in_neighborsolicit; + /* ipv6IfIcmpInNeighborAdvertisements, # of input neighbor advs. */ + u_quad_t ifs6_in_neighboradvert; + /* ipv6IfIcmpInRedirects, # of input redirects */ + u_quad_t ifs6_in_redirect; + /* ipv6IfIcmpInGroupMembQueries, # of input MLD queries */ + u_quad_t ifs6_in_mldquery; + /* ipv6IfIcmpInGroupMembResponses, # of input MLD reports */ + u_quad_t ifs6_in_mldreport; + /* ipv6IfIcmpInGroupMembReductions, # of input MLD done */ + u_quad_t ifs6_in_mlddone; + + /* + * Output statistics. We should solve unresolved routing problem... + */ + /* ipv6IfIcmpOutMsgs, total # of output messages */ + u_quad_t ifs6_out_msg; + /* ipv6IfIcmpOutErrors, # of output error messages */ + u_quad_t ifs6_out_error; + /* ipv6IfIcmpOutDestUnreachs, # of output dest unreach errors */ + u_quad_t ifs6_out_dstunreach; + /* ipv6IfIcmpOutAdminProhibs, # of output admin. prohibited errs */ + u_quad_t ifs6_out_adminprohib; + /* ipv6IfIcmpOutTimeExcds, # of output time exceeded errors */ + u_quad_t ifs6_out_timeexceed; + /* ipv6IfIcmpOutParmProblems, # of output parameter problem errors */ + u_quad_t ifs6_out_paramprob; + /* ipv6IfIcmpOutPktTooBigs, # of output packet too big errors */ + u_quad_t ifs6_out_pkttoobig; + /* ipv6IfIcmpOutEchos, # of output echo requests */ + u_quad_t ifs6_out_echo; + /* ipv6IfIcmpOutEchoReplies, # of output echo replies */ + u_quad_t ifs6_out_echoreply; + /* ipv6IfIcmpOutRouterSolicits, # of output router solicitations */ + u_quad_t ifs6_out_routersolicit; + /* ipv6IfIcmpOutRouterAdvertisements, # of output router advs. */ + u_quad_t ifs6_out_routeradvert; + /* ipv6IfIcmpOutNeighborSolicits, # of output neighbor solicitations */ + u_quad_t ifs6_out_neighborsolicit; + /* ipv6IfIcmpOutNeighborAdvertisements, # of output neighbor advs. */ + u_quad_t ifs6_out_neighboradvert; + /* ipv6IfIcmpOutRedirects, # of output redirects */ + u_quad_t ifs6_out_redirect; + /* ipv6IfIcmpOutGroupMembQueries, # of output MLD queries */ + u_quad_t ifs6_out_mldquery; + /* ipv6IfIcmpOutGroupMembResponses, # of output MLD reports */ + u_quad_t ifs6_out_mldreport; + /* ipv6IfIcmpOutGroupMembReductions, # of output MLD done */ + u_quad_t ifs6_out_mlddone; +}; + +#define SCOPE6_ID_MAX 16 + +struct in6_ifreq { + char ifr_name[IFNAMSIZ]; + union { + struct sockaddr_in6 ifru_addr; + struct sockaddr_in6 ifru_dstaddr; + int ifru_flags; + int ifru_flags6; + int ifru_metric; + int ifru_intval; + caddr_t ifru_data; + struct in6_addrlifetime ifru_lifetime; + struct in6_ifstat ifru_stat; + struct icmp6_ifstat ifru_icmp6stat; + u_int32_t ifru_scope_id[SCOPE6_ID_MAX]; + } ifr_ifru; +}; + +#define IN6_IFF_TEMPORARY 0x0080 /* temporary (anonymous) address. */ +#define IN6_IFF_DEPRECATED 0x0010 /* deprecated address */ + +#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq) +#define SIOCGIFALIFETIME_IN6 _IOWR('i', 81, struct in6_ifreq) + +// The definition below is ONLY a temporary workaround to unblock +// integrations on CI. MUST be removed ASAP, as soon as SDK is +// updated. Currently, we have WatchOS SDK 3.2 and it's missing +// net/if_types.h (unlike SDK 4.0, which has it). Alas, we have to +// work this around. We only define constants that we use in code. + +#if !QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_NA, __IPHONE_NA, __TVOS_NA, __WATCHOS_4_0) + +#define QT_WATCHOS_OUTDATED_SDK_WORKAROUND + +#define IFT_PPP 0x17 /* RFC 1331 */ +#define IFT_LOOP 0x18 /* loopback */ +#define IFT_SLIP 0x1c /* IP over generic TTY */ + +#define IFT_GIF 0x37 /*0xf0*/ +#define IFT_STF 0x39 /*0xf3*/ + +#define IFT_IEEE1394 0x90 /* IEEE1394 High Performance SerialBus*/ + +#endif // WatchOS SDK below 4.0 + +#endif // QNETWORKINTERFACE_UIKIT_P_H + diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp index afa6b4296e..d69fc47667 100644 --- a/src/network/kernel/qnetworkinterface_unix.cpp +++ b/src/network/kernel/qnetworkinterface_unix.cpp @@ -41,30 +41,19 @@ #include "qset.h" #include "qnetworkinterface.h" #include "qnetworkinterface_p.h" +#include "qnetworkinterface_unix_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 <sys/types.h> -#include <sys/socket.h> - -#ifdef Q_OS_SOLARIS -# include <sys/sockio.h> +#if defined(QT_NO_CLOCK_MONOTONIC) +# include "qdatetime.h" #endif -#include <net/if.h> #if defined(QT_LINUXBASE) # define QT_NO_GETIFADDRS #endif -#ifdef Q_OS_HAIKU -# include <sys/sockio.h> -# define IFF_RUNNING 0x0001 -#endif - #ifndef QT_NO_GETIFADDRS # include <ifaddrs.h> #endif @@ -103,23 +92,6 @@ static QHostAddress addressFromSockaddr(sockaddr *sa, int ifindex = 0, const QSt } -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 @@ -167,6 +139,15 @@ QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index) return QString::number(uint(index)); } +static int getMtu(int socket, struct ifreq *req) +{ +#ifdef SIOCGIFMTU + if (qt_safe_ioctl(socket, SIOCGIFMTU, req) == 0) + return req->ifr_mtu; +#endif + return 0; +} + #ifdef QT_NO_GETIFADDRS // getifaddrs not available @@ -306,6 +287,7 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() if (qt_safe_ioctl(socket, SIOCGIFFLAGS, &req) >= 0) { iface->flags = convertFlags(req.ifr_flags); } + iface->mtu = getMtu(socket, &req); #ifdef SIOCGIFHWADDR // Get the HW address @@ -359,6 +341,7 @@ QT_END_INCLUDE_NAMESPACE static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList) { + Q_UNUSED(getMtu) QList<QNetworkInterfacePrivate *> interfaces; QSet<QString> seenInterfaces; QVarLengthArray<int, 16> seenIndexes; // faster than QSet<int> @@ -413,14 +396,96 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList) return interfaces; } +static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa, const char *ifname) +{ + Q_UNUSED(entry); + Q_UNUSED(sa); + Q_UNUSED(ifname) +} + # elif defined(Q_OS_BSD4) QT_BEGIN_INCLUDE_NAMESPACE # include <net/if_dl.h> +#if defined(QT_PLATFORM_UIKIT) +# include "qnetworkinterface_uikit_p.h" +#if !defined(QT_WATCHOS_OUTDATED_SDK_WORKAROUND) +// TODO: remove it as soon as SDK is updated on CI!!! +# include <net/if_types.h> +#endif +#else +# include <net/if_media.h> +# include <net/if_types.h> +# include <netinet/in_var.h> +#endif // QT_PLATFORM_UIKIT QT_END_INCLUDE_NAMESPACE +static int openSocket(int &socket) +{ + if (socket == -1) + socket = qt_safe_socket(AF_INET, SOCK_DGRAM, 0); + return socket; +} + +static QNetworkInterface::InterfaceType probeIfType(int socket, int iftype, struct ifmediareq *req) +{ + // Determine the interface type. + + // On Darwin, these are #defines, but on FreeBSD they're just an + // enum, so we can't #ifdef them. Use the authoritative list from + // https://www.iana.org/assignments/smi-numbers/smi-numbers.xhtml#smi-numbers-5 + switch (iftype) { + case IFT_PPP: + return QNetworkInterface::Ppp; + + case IFT_LOOP: + return QNetworkInterface::Loopback; + + case IFT_SLIP: + return QNetworkInterface::Slip; + + case 0x47: // IFT_IEEE80211 + return QNetworkInterface::Ieee80211; + + case IFT_IEEE1394: + return QNetworkInterface::Ieee1394; + + case IFT_GIF: + case IFT_STF: + return QNetworkInterface::Virtual; + } + + // For the remainder (including Ethernet), let's try SIOGIFMEDIA + req->ifm_count = 0; + if (qt_safe_ioctl(socket, SIOCGIFMEDIA, req) == 0) { + // see https://man.openbsd.org/ifmedia.4 + + switch (IFM_TYPE(req->ifm_current)) { + case IFM_ETHER: + return QNetworkInterface::Ethernet; + + case IFM_FDDI: + return QNetworkInterface::Fddi; + + case IFM_IEEE80211: + return QNetworkInterface::Ieee80211; + } + } + + return QNetworkInterface::Unknown; +} + static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList) { QList<QNetworkInterfacePrivate *> interfaces; + union { + struct ifmediareq mediareq; + struct ifreq req; + }; + int socket = -1; + + // ensure both structs start with the name field, of size IFNAMESIZ + Q_STATIC_ASSERT(sizeof(mediareq.ifm_name) == sizeof(req.ifr_name)); + Q_ASSERT(&mediareq.ifm_name == &req.ifr_name); // on NetBSD we use AF_LINK and sockaddr_dl // scan the list for that family @@ -434,15 +499,73 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList) iface->name = QString::fromLatin1(ptr->ifa_name); iface->flags = convertFlags(ptr->ifa_flags); iface->hardwareAddress = iface->makeHwAddress(sdl->sdl_alen, (uchar*)LLADDR(sdl)); + + strlcpy(mediareq.ifm_name, ptr->ifa_name, sizeof(mediareq.ifm_name)); + iface->type = probeIfType(openSocket(socket), sdl->sdl_type, &mediareq); + iface->mtu = getMtu(socket, &req); } + if (socket != -1) + qt_safe_close(socket); return interfaces; } +static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa, const char *ifname) +{ + // get IPv6 address lifetimes + if (sa->sa_family != AF_INET6) + return; + + struct in6_ifreq ifr; + + int s6 = qt_safe_socket(AF_INET6, SOCK_DGRAM, 0); + if (Q_UNLIKELY(s6 < 0)) { + qErrnoWarning("QNetworkInterface: could not create IPv6 socket"); + return; + } + + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + + // get flags + ifr.ifr_addr = *reinterpret_cast<struct sockaddr_in6 *>(sa); + if (qt_safe_ioctl(s6, SIOCGIFAFLAG_IN6, &ifr) < 0) { + qt_safe_close(s6); + return; + } + int flags = ifr.ifr_ifru.ifru_flags6; + QNetworkInterfacePrivate::calculateDnsEligibility(entry, + flags & IN6_IFF_TEMPORARY, + flags & IN6_IFF_DEPRECATED); + + // get lifetimes + ifr.ifr_addr = *reinterpret_cast<struct sockaddr_in6 *>(sa); + if (qt_safe_ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr) < 0) { + qt_safe_close(s6); + return; + } + qt_safe_close(s6); + + auto toDeadline = [](time_t when) { + QDeadlineTimer deadline = QDeadlineTimer::Forever; + if (when) { +#if defined(QT_NO_CLOCK_MONOTONIC) + // no monotonic clock + deadline.setPreciseRemainingTime(when - QDateTime::currentSecsSinceEpoch()); +#else + deadline.setPreciseDeadline(when); +#endif + } + return deadline; + }; + entry->setAddressLifetime(toDeadline(ifr.ifr_ifru.ifru_lifetime.ia6t_preferred), + toDeadline(ifr.ifr_ifru.ifru_lifetime.ia6t_expire)); +} + # else // Generic version static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList) { + Q_UNUSED(getMtu) QList<QNetworkInterfacePrivate *> interfaces; // make sure there's one entry for each interface @@ -470,9 +593,14 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList) return interfaces; } +static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa, const char *ifname) +{ + Q_UNUSED(entry); + Q_UNUSED(sa); + Q_UNUSED(ifname) +} # endif - static QList<QNetworkInterfacePrivate *> interfaceListing() { QList<QNetworkInterfacePrivate *> interfaces; @@ -521,6 +649,7 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() 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)); + getAddressExtraInfo(&entry, ptr->ifa_addr, name.latin1()); iface->addressEntries << entry; } diff --git a/src/network/kernel/qnetworkinterface_unix_p.h b/src/network/kernel/qnetworkinterface_unix_p.h new file mode 100644 index 0000000000..c085194e3c --- /dev/null +++ b/src/network/kernel/qnetworkinterface_unix_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +#ifndef QNETWORKINTERFACE_UNIX_P_H +#define QNETWORKINTERFACE_UNIX_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qnetworkinterface_p.h" +#include "private/qnet_unix_p.h" + +#ifndef QT_NO_NETWORKINTERFACE + +#define IP_MULTICAST // make AIX happy and define IFF_MULTICAST + +#include <sys/types.h> +#include <sys/socket.h> +#ifdef Q_OS_SOLARIS +# include <sys/sockio.h> +#endif +#ifdef Q_OS_HAIKU +# include <sys/sockio.h> +# define IFF_RUNNING 0x0001 +#endif +#if QT_CONFIG(linux_netlink) +// Same as net/if.h but contains other things we need in +// qnetworkinterface_linux.cpp. +# include <linux/if.h> +#else +# include <net/if.h> +#endif + +QT_BEGIN_NAMESPACE + +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; +} + +QT_END_NAMESPACE + +#endif // QT_NO_NETWORKINTERFACE + +#endif // QNETWORKINTERFACE_UNIX_P_H diff --git a/src/network/kernel/qnetworkinterface_win.cpp b/src/network/kernel/qnetworkinterface_win.cpp index 64c3fa6f83..150553f673 100644 --- a/src/network/kernel/qnetworkinterface_win.cpp +++ b/src/network/kernel/qnetworkinterface_win.cpp @@ -62,6 +62,10 @@ #include <qt_windows.h> +// In case these aren't defined +#define IF_TYPE_IEEE80216_WMAN 237 +#define IF_TYPE_IEEE802154 259 + QT_BEGIN_NAMESPACE static QHostAddress addressFromSockaddr(sockaddr *sa) @@ -147,6 +151,7 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() else if (ptr->IfIndex != 0) iface->index = ptr->IfIndex; + iface->mtu = qMin<qint64>(ptr->Mtu, INT_MAX); iface->flags = QNetworkInterface::CanBroadcast; if (ptr->OperStatus == IfOperStatusUp) iface->flags |= QNetworkInterface::IsUp | QNetworkInterface::IsRunning; @@ -155,6 +160,45 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() if (ptr->IfType == IF_TYPE_PPP) iface->flags |= QNetworkInterface::IsPointToPoint; + switch (ptr->IfType) { + case IF_TYPE_ETHERNET_CSMACD: + iface->type = QNetworkInterface::Ethernet; + break; + + case IF_TYPE_FDDI: + iface->type = QNetworkInterface::Fddi; + break; + + case IF_TYPE_PPP: + iface->type = QNetworkInterface::Ppp; + break; + + case IF_TYPE_SLIP: + iface->type = QNetworkInterface::Slip; + break; + + case IF_TYPE_SOFTWARE_LOOPBACK: + iface->type = QNetworkInterface::Loopback; + iface->flags |= QNetworkInterface::IsLoopBack; + break; + + case IF_TYPE_IEEE80211: + iface->type = QNetworkInterface::Ieee80211; + break; + + case IF_TYPE_IEEE1394: + iface->type = QNetworkInterface::Ieee1394; + break; + + case IF_TYPE_IEEE80216_WMAN: + iface->type = QNetworkInterface::Ieee80216; + break; + + case IF_TYPE_IEEE802154: + iface->type = QNetworkInterface::Ieee802154; + break; + } + // use ConvertInterfaceLuidToNameW because that returns a friendlier name, though not // as "friendly" as FriendlyName below WCHAR buf[IF_MAX_STRING_SIZE + 1]; @@ -167,9 +211,6 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() if (ptr->PhysicalAddressLength) iface->hardwareAddress = iface->makeHwAddress(ptr->PhysicalAddressLength, ptr->PhysicalAddress); - else - // loopback if it has no address - iface->flags |= QNetworkInterface::IsLoopBack; // parse the IP (unicast) addresses for (PIP_ADAPTER_UNICAST_ADDRESS addr = ptr->FirstUnicastAddress; addr; addr = addr->Next) { @@ -182,6 +223,17 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() QNetworkAddressEntry entry; entry.setIp(addressFromSockaddr(addr->Address.lpSockaddr)); entry.setPrefixLength(addr->OnLinkPrefixLength); + + auto toDeadline = [](ULONG lifetime) -> QDeadlineTimer { + if (lifetime == 0xffffffffUL) + return QDeadlineTimer::Forever; + return QDeadlineTimer(lifetime * 1000); + }; + entry.setAddressLifetime(toDeadline(addr->ValidLifetime), toDeadline(addr->PreferredLifetime)); + entry.setDnsEligibility(addr->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE ? + QNetworkAddressEntry::DnsEligible : + QNetworkAddressEntry::DnsIneligible); + iface->addressEntries << entry; } } diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp index 6b53b4b58e..3646a9526a 100644 --- a/src/network/kernel/qnetworkproxy.cpp +++ b/src/network/kernel/qnetworkproxy.cpp @@ -230,7 +230,11 @@ #if QT_CONFIG(socks5) #include "private/qsocks5socketengine_p.h" #endif + +#if QT_CONFIG(http) #include "private/qhttpsocketengine_p.h" +#endif + #include "qauthenticator.h" #include "qdebug.h" #include "qmutex.h" @@ -256,7 +260,7 @@ public: #if QT_CONFIG(socks5) , socks5SocketEngineHandler(0) #endif -#ifndef QT_NO_HTTP +#if QT_CONFIG(http) , httpSocketEngineHandler(0) #endif #ifdef QT_USE_SYSTEM_PROXIES @@ -268,7 +272,7 @@ public: #if QT_CONFIG(socks5) socks5SocketEngineHandler = new QSocks5SocketEngineHandler(); #endif -#ifndef QT_NO_HTTP +#if QT_CONFIG(http) httpSocketEngineHandler = new QHttpSocketEngineHandler(); #endif } @@ -280,7 +284,7 @@ public: #if QT_CONFIG(socks5) delete socks5SocketEngineHandler; #endif -#ifndef QT_NO_HTTP +#if QT_CONFIG(http) delete httpSocketEngineHandler; #endif } @@ -340,7 +344,7 @@ private: #if QT_CONFIG(socks5) QSocks5SocketEngineHandler *socks5SocketEngineHandler; #endif -#ifndef QT_NO_HTTP +#if QT_CONFIG(http) QHttpSocketEngineHandler *httpSocketEngineHandler; #endif bool useSystemProxies; |