diff options
-rw-r--r-- | src/network/kernel/qhostaddress.cpp | 197 | ||||
-rw-r--r-- | src/network/kernel/qhostaddress.h | 5 | ||||
-rw-r--r-- | src/network/kernel/qhostaddress_p.h | 16 | ||||
-rw-r--r-- | tests/auto/network/kernel/qhostaddress/qhostaddress.pro | 3 | ||||
-rw-r--r-- | tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp | 164 |
5 files changed, 279 insertions, 106 deletions
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp index 4f4c674d20..8b380960e6 100644 --- a/src/network/kernel/qhostaddress.cpp +++ b/src/network/kernel/qhostaddress.cpp @@ -64,7 +64,6 @@ QT_BEGIN_NAMESPACE - class QHostAddressPrivate : public QSharedData { public: @@ -87,6 +86,8 @@ public: quint32 a; // IPv4 address qint8 protocol; + AddressClassification classify() const; + friend class QHostAddress; }; @@ -205,6 +206,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) { @@ -1154,21 +1224,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 +1316,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..bb1424826d 100644 --- a/src/network/kernel/qhostaddress.h +++ b/src/network/kernel/qhostaddress.h @@ -148,7 +148,12 @@ 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); diff --git a/src/network/kernel/qhostaddress_p.h b/src/network/kernel/qhostaddress_p.h index 5106760ed9..f06feb0559 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 diff --git a/tests/auto/network/kernel/qhostaddress/qhostaddress.pro b/tests/auto/network/kernel/qhostaddress/qhostaddress.pro index a79fa2f59d..b5d6ea6459 100644 --- a/tests/auto/network/kernel/qhostaddress/qhostaddress.pro +++ b/tests/auto/network/kernel/qhostaddress/qhostaddress.pro @@ -2,7 +2,6 @@ CONFIG += testcase TARGET = tst_qhostaddress SOURCES += tst_qhostaddress.cpp - -QT = core network testlib +QT = core network-private testlib win32:LIBS += -lws2_32 diff --git a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp index bc3f5650ba..224e4d61a9 100644 --- a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp +++ b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp @@ -27,9 +27,10 @@ ** ****************************************************************************/ +#include <qhostaddress.h> +#include <private/qhostaddress_p.h> #include <qcoreapplication.h> #include <QtTest/QtTest> -#include <qhostaddress.h> #include <qplatformdefs.h> #include <qdebug.h> #include <qhash.h> @@ -46,6 +47,7 @@ # include <netinet/in.h> #endif +Q_DECLARE_METATYPE(AddressClassification) Q_DECLARE_METATYPE(QHostAddress::SpecialAddress) class tst_QHostAddress : public QObject @@ -75,10 +77,8 @@ private slots: void parseSubnet(); void isInSubnet_data(); void isInSubnet(); - void isLoopback_data(); - void isLoopback(); - void isMulticast_data(); - void isMulticast(); + void classification_data(); + void classification(); void convertv4v6_data(); void convertv4v6(); }; @@ -667,90 +667,88 @@ void tst_QHostAddress::isInSubnet() QTEST(address.isInSubnet(prefix, prefixLength), "result"); } -void tst_QHostAddress::isLoopback_data() -{ - QTest::addColumn<QHostAddress>("address"); - QTest::addColumn<bool>("result"); - - QTest::newRow("default") << QHostAddress() << false; - QTest::newRow("invalid") << QHostAddress("&&&") << false; - - QTest::newRow("ipv6_loop") << QHostAddress(QHostAddress::LocalHostIPv6) << true; - QTest::newRow("::1") << QHostAddress("::1") << true; - - QTest::newRow("ipv4_loop") << QHostAddress(QHostAddress::LocalHost) << true; - QTest::newRow("127.0.0.1") << QHostAddress("127.0.0.1") << true; - QTest::newRow("127.0.0.2") << QHostAddress("127.0.0.2") << true; - QTest::newRow("127.3.2.1") << QHostAddress("127.3.2.1") << true; - - QTest::newRow("1.2.3.4") << QHostAddress("1.2.3.4") << false; - QTest::newRow("10.0.0.4") << QHostAddress("10.0.0.4") << false; - QTest::newRow("192.168.3.4") << QHostAddress("192.168.3.4") << false; - - QTest::newRow("::") << QHostAddress("::") << false; - QTest::newRow("Any") << QHostAddress(QHostAddress::Any) << false; - QTest::newRow("AnyIPv4") << QHostAddress(QHostAddress::AnyIPv4) << false; - QTest::newRow("AnyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << false; - QTest::newRow("Broadcast") << QHostAddress(QHostAddress::Broadcast) << false; - QTest::newRow("Null") << QHostAddress(QHostAddress::Null) << false; - QTest::newRow("ipv6-all-ffff") << QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") << false; - - QTest::newRow("::ffff:127.0.0.1") << QHostAddress("::ffff:127.0.0.1") << true; - QTest::newRow("::ffff:127.0.0.2") << QHostAddress("::ffff:127.0.0.2") << true; - QTest::newRow("::ffff:127.3.2.1") << QHostAddress("::ffff:127.3.2.1") << true; - -} - -void tst_QHostAddress::isLoopback() -{ - QFETCH(QHostAddress, address); - QFETCH(bool, result); - - QCOMPARE(address.isLoopback(), result); -} - -void tst_QHostAddress::isMulticast_data() +void tst_QHostAddress::classification_data() { QTest::addColumn<QHostAddress>("address"); - QTest::addColumn<bool>("result"); - - QTest::newRow("default") << QHostAddress() << false; - QTest::newRow("invalid") << QHostAddress("&&&") << false; - - QTest::newRow("ipv6_loop") << QHostAddress(QHostAddress::LocalHostIPv6) << false; - QTest::newRow("::1") << QHostAddress("::1") << false; - QTest::newRow("ipv4_loop") << QHostAddress(QHostAddress::LocalHost) << false; - QTest::newRow("127.0.0.1") << QHostAddress("127.0.0.1") << false; - QTest::newRow("::") << QHostAddress("::") << false; - QTest::newRow("Any") << QHostAddress(QHostAddress::Any) << false; - QTest::newRow("AnyIPv4") << QHostAddress(QHostAddress::AnyIPv4) << false; - QTest::newRow("AnyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << false; - QTest::newRow("Broadcast") << QHostAddress(QHostAddress::Broadcast) << false; - QTest::newRow("Null") << QHostAddress(QHostAddress::Null) << false; - - QTest::newRow("223.255.255.255") << QHostAddress("223.255.255.255") << false; - QTest::newRow("224.0.0.0") << QHostAddress("224.0.0.0") << true; - QTest::newRow("239.255.255.255") << QHostAddress("239.255.255.255") << true; - QTest::newRow("240.0.0.0") << QHostAddress("240.0.0.0") << false; - - QTest::newRow("::ffff:223.255.255.255") << QHostAddress("::ffff:223.255.255.255") << false; - QTest::newRow("::ffff:224.0.0.0") << QHostAddress("::ffff:224.0.0.0") << true; - QTest::newRow("::ffff:239.255.255.255") << QHostAddress("::ffff:239.255.255.255") << true; - QTest::newRow("::ffff:240.0.0.0") << QHostAddress("::ffff:240.0.0.0") << false; - - QTest::newRow("fc00::") << QHostAddress("fc00::") << false; - QTest::newRow("fe80::") << QHostAddress("fe80::") << false; - QTest::newRow("fec0::") << QHostAddress("fec0::") << false; - QTest::newRow("ff00::") << QHostAddress("ff00::") << true; - QTest::newRow("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") << QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") << true; + QTest::addColumn<AddressClassification>("result"); + + QTest::newRow("default") << QHostAddress() << UnknownAddress; + QTest::newRow("invalid") << QHostAddress("&&&") << UnknownAddress; + + QTest::newRow("Any") << QHostAddress(QHostAddress::Any) << LocalNetAddress; + QTest::newRow("Null") << QHostAddress(QHostAddress::Null) << UnknownAddress; + + // IPv6 address space + auto addV6 = [](const char *str, AddressClassification cl) { + QTest::newRow(str) << QHostAddress(str) << cl; + }; + QTest::newRow("AnyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << LocalNetAddress; + QTest::newRow("ipv6_loop") << QHostAddress(QHostAddress::LocalHostIPv6) << LoopbackAddress; + addV6("::", LocalNetAddress); + addV6("::1", LoopbackAddress); + addV6("::2", GlobalAddress); + addV6("2000::", GlobalAddress); + addV6("3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", GlobalAddress); + addV6("fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", GlobalAddress); + addV6("fc00::", UniqueLocalAddress); + addV6("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", UniqueLocalAddress); + addV6("fe00::", UnknownAddress); + addV6("fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff", UnknownAddress); + addV6("fe80::", LinkLocalAddress); + addV6("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", LinkLocalAddress); + addV6("fec0::", SiteLocalAddress); + addV6("feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", SiteLocalAddress); + addV6("ff00::", MulticastAddress); + addV6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", MulticastAddress); + + // IPv4 address space + auto addV4 = [](const char *str, AddressClassification cl) { + QTest::newRow(str) << QHostAddress(str) << cl; + QByteArray v6 = "::ffff:"; + v6 += str; + QTest::newRow(v6.constData()) << QHostAddress(QString::fromLatin1(v6)) << cl; + }; + QTest::newRow("AnyIPv4") << QHostAddress(QHostAddress::AnyIPv4) << LocalNetAddress; + QTest::newRow("ipv4_loop") << QHostAddress(QHostAddress::LocalHost) << LoopbackAddress; + QTest::newRow("Broadcast") << QHostAddress(QHostAddress::Broadcast) << BroadcastAddress; + addV4("0.0.0.0", LocalNetAddress); + addV4("0.0.0.1", LocalNetAddress); + addV4("0.255.255.255", LocalNetAddress); + addV4("1.0.0.0", GlobalAddress); + addV4("1.2.3.4", GlobalAddress); + addV4("10.0.0.4", PrivateNetworkAddress); + addV4("127.0.0.1", LoopbackAddress); + addV4("127.0.0.2", LoopbackAddress); + addV4("127.255.255.255", LoopbackAddress); + addV4("192.168.3.4", PrivateNetworkAddress); + addV4("223.255.255.255", GlobalAddress); + addV4("224.0.0.0", MulticastAddress); + addV4("239.255.255.255", MulticastAddress); + addV4("240.0.0.0", UnknownAddress); + addV4("255.255.255.254", UnknownAddress); + addV4("255.255.255.255", BroadcastAddress); } -void tst_QHostAddress::isMulticast() +void tst_QHostAddress::classification() { QFETCH(QHostAddress, address); - QFETCH(bool, result); - - QCOMPARE(address.isMulticast(), result); + QFETCH(AddressClassification, result); + + bool isLoopback = (result == LoopbackAddress); + bool isGlobal = (result & GlobalAddress); // GlobalAddress is a bit + bool isLinkLocal = (result == LinkLocalAddress); + bool isSiteLocal = (result == SiteLocalAddress); + bool isUniqueLocalAddress = (result == UniqueLocalAddress); + bool isMulticast = (result == MulticastAddress); + bool isBroadcast = (result == BroadcastAddress); + + QCOMPARE(address.isLoopback(), isLoopback); + QCOMPARE(address.isGlobal(), isGlobal); + QCOMPARE(address.isLinkLocal(), isLinkLocal); + QCOMPARE(address.isSiteLocal(), isSiteLocal); + QCOMPARE(address.isUniqueLocalUnicast(), isUniqueLocalAddress); + QCOMPARE(address.isMulticast(), isMulticast); + QCOMPARE(address.isBroadcast(), isBroadcast); } void tst_QHostAddress::convertv4v6_data() |