summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/network/kernel/qhostaddress.cpp197
-rw-r--r--src/network/kernel/qhostaddress.h5
-rw-r--r--src/network/kernel/qhostaddress_p.h16
-rw-r--r--tests/auto/network/kernel/qhostaddress/qhostaddress.pro3
-rw-r--r--tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp164
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()