QHostAddress: comparator with flexible conversion rules
Add a named comparison method that allows the caller to specify in how far a translation between IPv4 and IPv6 should be performed before comparing. Change-Id: I9aeffcd4539a6c0537c083c4553357b22167df3f Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>bb10
parent
c0979fe9d6
commit
cedc554321
|
|
@ -152,20 +152,37 @@ void QHostAddressPrivate::setAddress(quint32 a_)
|
|||
|
||||
/// parses v4-mapped addresses or the AnyIPv6 address and stores in \a a;
|
||||
/// returns true if the address was one of those
|
||||
static bool convertToIpv4(quint32& a, const Q_IPV6ADDR &a6)
|
||||
static bool convertToIpv4(quint32& a, const Q_IPV6ADDR &a6, const QHostAddress::ConversionMode mode)
|
||||
{
|
||||
if (mode == QHostAddress::StrictConversion)
|
||||
return false;
|
||||
|
||||
const uchar *ptr = a6.c;
|
||||
if (qFromUnaligned<quint64>(ptr) != 0)
|
||||
return false;
|
||||
if (qFromBigEndian<quint32>(ptr + 8) == 0) {
|
||||
// is it AnyIPv6?
|
||||
a = 0;
|
||||
return qFromBigEndian<quint32>(ptr + 12) == 0;
|
||||
|
||||
const quint32 mid = qFromBigEndian<quint32>(ptr + 8);
|
||||
if ((mid == 0xffff) && (mode & QHostAddress::ConvertV4MappedToIPv4)) {
|
||||
a = qFromBigEndian<quint32>(ptr + 12);
|
||||
return true;
|
||||
}
|
||||
if (qFromBigEndian<quint32>(ptr + 8) != 0xFFFF)
|
||||
if (mid != 0)
|
||||
return false;
|
||||
a = qFromBigEndian<quint32>(ptr + 12);
|
||||
return true;
|
||||
|
||||
const quint32 low = qFromBigEndian<quint32>(ptr + 12);
|
||||
if ((low == 0) && (mode & QHostAddress::ConvertUnspecifiedAddress)) {
|
||||
a = 0;
|
||||
return true;
|
||||
}
|
||||
if ((low == 1) && (mode & QHostAddress::ConvertLocalHost)) {
|
||||
a = INADDR_LOOPBACK;
|
||||
return true;
|
||||
}
|
||||
if ((low != 1) && (mode & QHostAddress::ConvertV4CompatToIPv4)) {
|
||||
a = low;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void QHostAddressPrivate::setAddress(const quint8 *a_)
|
||||
|
|
@ -174,7 +191,8 @@ void QHostAddressPrivate::setAddress(const quint8 *a_)
|
|||
isParsed = true;
|
||||
memcpy(a6.c, a_, sizeof(a6));
|
||||
a = 0;
|
||||
convertToIpv4(a, a6);
|
||||
convertToIpv4(a, a6, (QHostAddress::ConvertV4MappedToIPv4
|
||||
| QHostAddress::ConvertUnspecifiedAddress));
|
||||
}
|
||||
|
||||
void QHostAddressPrivate::setAddress(const Q_IPV6ADDR &a_)
|
||||
|
|
@ -386,6 +404,20 @@ void QNetmaskAddress::setPrefixLength(QAbstractSocket::NetworkLayerProtocol prot
|
|||
\value Any The dual stack any-address. A socket bound with this address will listen on both IPv4 and IPv6 interfaces.
|
||||
*/
|
||||
|
||||
/*! \enum QHostAddress::ConversionModeFlag
|
||||
|
||||
\since 5.8
|
||||
|
||||
\value StrictConversion Don't convert IPv6 addresses to IPv4 when comparing two QHostAddress objects of different protocols, so they will always be considered different.
|
||||
\value ConvertV4MappedToIPv4 Convert IPv4-mapped IPv6 addresses (RFC 4291 sect. 2.5.5.2) when comparing. Therefore QHostAddress("::ffff:192.168.1.1") will compare equal to QHostAddress("192.168.1.1").
|
||||
\value ConvertV4CompatToIPv4 Convert IPv4-compatible IPv6 addresses (RFC 4291 sect. 2.5.5.1) when comparing. Therefore QHostAddress("::192.168.1.1") will compare equal to QHostAddress("192.168.1.1").
|
||||
\value ConvertLocalHost Convert the IPv6 loopback addresses to its IPv4 equivalent when comparing. Therefore e.g. QHostAddress("::1") will compare equal to QHostAddress("127.0.0.1").
|
||||
\value ConvertUnspecifiedAddress All unspecified addresses will compare equal, namely AnyIPv4, AnyIPv6 and Any.
|
||||
\value TolerantConversion Sets all three preceding flags.
|
||||
|
||||
\sa isEqual()
|
||||
*/
|
||||
|
||||
/*! Constructs a null host address object, i.e. an address which is not valid for any host or interface.
|
||||
|
||||
\sa clear()
|
||||
|
|
@ -691,7 +723,9 @@ quint32 QHostAddress::toIPv4Address(bool *ok) const
|
|||
quint32 dummy;
|
||||
if (ok)
|
||||
*ok = d->protocol == QAbstractSocket::IPv4Protocol || d->protocol == QAbstractSocket::AnyIPProtocol
|
||||
|| (d->protocol == QAbstractSocket::IPv6Protocol && convertToIpv4(dummy, d->a6));
|
||||
|| (d->protocol == QAbstractSocket::IPv6Protocol
|
||||
&& convertToIpv4(dummy, d->a6, ConversionMode(QHostAddress::ConvertV4MappedToIPv4
|
||||
| QHostAddress::ConvertUnspecifiedAddress)));
|
||||
return d->a;
|
||||
}
|
||||
|
||||
|
|
@ -813,19 +847,73 @@ void QHostAddress::setScopeId(const QString &id)
|
|||
|
||||
/*!
|
||||
Returns \c true if this host address is the same as the \a other address
|
||||
given; otherwise returns \c false.
|
||||
given; otherwise returns \c false. This operator just calls isEqual(other, StrictConversion).
|
||||
|
||||
\sa isEqual()
|
||||
*/
|
||||
bool QHostAddress::operator==(const QHostAddress &other) const
|
||||
{
|
||||
return isEqual(other, StrictConversion);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.8
|
||||
|
||||
Returns \c true if this host address is the same as the \a other address
|
||||
given; otherwise returns \c false.
|
||||
|
||||
The parameter \a mode controls which conversions are preformed between addresses
|
||||
of differing protocols. If no \a mode is given, \c TolerantConversion is performed
|
||||
by default.
|
||||
|
||||
\sa ConversionMode, operator==()
|
||||
*/
|
||||
bool QHostAddress::isEqual(const QHostAddress &other, ConversionMode mode) const
|
||||
{
|
||||
QT_ENSURE_PARSED(this);
|
||||
QT_ENSURE_PARSED(&other);
|
||||
|
||||
if (d->protocol == QAbstractSocket::IPv4Protocol)
|
||||
return other.d->protocol == QAbstractSocket::IPv4Protocol && d->a == other.d->a;
|
||||
if (d->protocol == QAbstractSocket::IPv6Protocol) {
|
||||
return other.d->protocol == QAbstractSocket::IPv6Protocol
|
||||
&& memcmp(&d->a6, &other.d->a6, sizeof(Q_IPV6ADDR)) == 0;
|
||||
if (d->protocol == QAbstractSocket::IPv4Protocol) {
|
||||
switch (other.d->protocol) {
|
||||
case QAbstractSocket::IPv4Protocol:
|
||||
return d->a == other.d->a;
|
||||
case QAbstractSocket::IPv6Protocol:
|
||||
quint32 a4;
|
||||
return convertToIpv4(a4, other.d->a6, mode) && (a4 == d->a);
|
||||
case QAbstractSocket::AnyIPProtocol:
|
||||
return (mode & QHostAddress::ConvertUnspecifiedAddress) && d->a == 0;
|
||||
case QAbstractSocket::UnknownNetworkLayerProtocol:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (d->protocol == QAbstractSocket::IPv6Protocol) {
|
||||
switch (other.d->protocol) {
|
||||
case QAbstractSocket::IPv4Protocol:
|
||||
quint32 a4;
|
||||
return convertToIpv4(a4, d->a6, mode) && (a4 == other.d->a);
|
||||
case QAbstractSocket::IPv6Protocol:
|
||||
return memcmp(&d->a6, &other.d->a6, sizeof(Q_IPV6ADDR)) == 0;
|
||||
case QAbstractSocket::AnyIPProtocol:
|
||||
return (mode & QHostAddress::ConvertUnspecifiedAddress)
|
||||
&& (other.d->a6_64.c[0] == 0) && (other.d->a6_64.c[1] == 0);
|
||||
case QAbstractSocket::UnknownNetworkLayerProtocol:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((d->protocol == QAbstractSocket::AnyIPProtocol)
|
||||
&& (mode & QHostAddress::ConvertUnspecifiedAddress)) {
|
||||
switch (other.d->protocol) {
|
||||
case QAbstractSocket::IPv4Protocol:
|
||||
return other.d->a == 0;
|
||||
case QAbstractSocket::IPv6Protocol:
|
||||
return (other.d->a6_64.c[0] == 0) && (other.d->a6_64.c[1] == 0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return d->protocol == other.d->protocol;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,16 @@ public:
|
|||
AnyIPv6,
|
||||
AnyIPv4
|
||||
};
|
||||
enum ConversionModeFlag {
|
||||
ConvertV4MappedToIPv4 = 1,
|
||||
ConvertV4CompatToIPv4 = 2,
|
||||
ConvertUnspecifiedAddress = 4,
|
||||
ConvertLocalHost = 8,
|
||||
TolerantConversion = 0xff,
|
||||
|
||||
StrictConversion = 0
|
||||
};
|
||||
Q_DECLARE_FLAGS(ConversionMode, ConversionModeFlag)
|
||||
|
||||
QHostAddress();
|
||||
explicit QHostAddress(quint32 ip4Addr);
|
||||
|
|
@ -118,6 +128,7 @@ public:
|
|||
QString scopeId() const;
|
||||
void setScopeId(const QString &id);
|
||||
|
||||
bool isEqual(const QHostAddress &address, ConversionMode mode = TolerantConversion) const;
|
||||
bool operator ==(const QHostAddress &address) const;
|
||||
bool operator ==(SpecialAddress address) const;
|
||||
inline bool operator !=(const QHostAddress &address) const
|
||||
|
|
@ -139,6 +150,7 @@ public:
|
|||
protected:
|
||||
QScopedPointer<QHostAddressPrivate> d;
|
||||
};
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QHostAddress::ConversionMode)
|
||||
Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QHostAddress)
|
||||
|
||||
inline bool operator ==(QHostAddress::SpecialAddress address1, const QHostAddress &address2)
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ private slots:
|
|||
void specialAddresses();
|
||||
void compare_data();
|
||||
void compare();
|
||||
void isEqual_data();
|
||||
void isEqual();
|
||||
void assignment();
|
||||
void scopeId();
|
||||
void hashKey();
|
||||
|
|
@ -282,6 +284,7 @@ void tst_QHostAddress::compare_data()
|
|||
QTest::newRow("5") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::Broadcast) << false;
|
||||
QTest::newRow("6") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHostIPv6) << false;
|
||||
QTest::newRow("7") << QHostAddress() << QHostAddress(QHostAddress::LocalHostIPv6) << false;
|
||||
QTest::newRow("any4-any6") << QHostAddress(QHostAddress::AnyIPv4) << QHostAddress(QHostAddress::AnyIPv6) << false;
|
||||
|
||||
Q_IPV6ADDR localhostv4mapped = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1 } };
|
||||
QTest::newRow("v4-v4mapped") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("::ffff:127.0.0.1") << false;
|
||||
|
|
@ -300,6 +303,53 @@ void tst_QHostAddress::compare()
|
|||
QCOMPARE(qHash(first), qHash(second));
|
||||
}
|
||||
|
||||
void tst_QHostAddress::isEqual_data()
|
||||
{
|
||||
QTest::addColumn<QHostAddress>("first");
|
||||
QTest::addColumn<QHostAddress>("second");
|
||||
QTest::addColumn<int>("flags");
|
||||
QTest::addColumn<bool>("result");
|
||||
|
||||
// QHostAddress::StrictConversion is already tested in compare()
|
||||
QTest::newRow("localhost4to6-local") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertLocalHost << true;
|
||||
QTest::newRow("localhost4to6-compat") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertV4CompatToIPv4 << false;
|
||||
QTest::newRow("localhost4to6-mapped") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertV4MappedToIPv4 << false;
|
||||
QTest::newRow("localhost4to6-unspec") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertUnspecifiedAddress << false;
|
||||
QTest::newRow("0.0.0.1-::1-local") << QHostAddress("0.0.0.1") << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertLocalHost << false;
|
||||
QTest::newRow("v4-v4compat-local") << QHostAddress("192.168.1.1") << QHostAddress("::192.168.1.1") << (int)QHostAddress::ConvertLocalHost << false;
|
||||
QTest::newRow("v4-v4mapped-local") << QHostAddress("192.168.1.1") << QHostAddress("::ffff:192.168.1.1") << (int)QHostAddress::ConvertLocalHost << false;
|
||||
QTest::newRow("0.0.0.1-::1-unspec") << QHostAddress("0.0.0.1") << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertUnspecifiedAddress << false;
|
||||
QTest::newRow("v4-v4compat-unspec") << QHostAddress("192.168.1.1") << QHostAddress("::192.168.1.1") << (int)QHostAddress::ConvertUnspecifiedAddress << false;
|
||||
QTest::newRow("v4-v4mapped-unspec") << QHostAddress("192.168.1.1") << QHostAddress("::ffff:192.168.1.1") << (int)QHostAddress::ConvertUnspecifiedAddress << false;
|
||||
QTest::newRow("0.0.0.1-::1-compat") << QHostAddress("0.0.0.1") << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertV4CompatToIPv4 << false;
|
||||
QTest::newRow("v4-v4compat-compat") << QHostAddress("192.168.1.1") << QHostAddress("::192.168.1.1") << (int)QHostAddress::ConvertV4CompatToIPv4 << true;
|
||||
QTest::newRow("v4-v4mapped-compat") << QHostAddress("192.168.1.1") << QHostAddress("::ffff:192.168.1.1") << (int)QHostAddress::ConvertV4CompatToIPv4 << false;
|
||||
QTest::newRow("0.0.0.1-::1-mapped") << QHostAddress("0.0.0.1") << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertV4MappedToIPv4 << false;
|
||||
QTest::newRow("v4-v4compat-mapped") << QHostAddress("192.168.1.1") << QHostAddress("::192.168.1.1") << (int)QHostAddress::ConvertV4MappedToIPv4 << false;
|
||||
QTest::newRow("v4-v4mapped-mapped") << QHostAddress("192.168.1.1") << QHostAddress("::FFFF:192.168.1.1") << (int)QHostAddress::ConvertV4MappedToIPv4 << true;
|
||||
QTest::newRow("undef-any-local") << QHostAddress() << QHostAddress(QHostAddress::Any) << (int)QHostAddress::ConvertLocalHost << false;
|
||||
QTest::newRow("undef-any-unspec") << QHostAddress() << QHostAddress(QHostAddress::Any) << (int)QHostAddress::ConvertUnspecifiedAddress << false;
|
||||
QTest::newRow("anyv6-anyv4-compat") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertV4CompatToIPv4 << true;
|
||||
QTest::newRow("anyv6-anyv4-mapped") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertV4MappedToIPv4 << false;
|
||||
QTest::newRow("anyv6-anyv4-unspec") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertUnspecifiedAddress << true;
|
||||
QTest::newRow("any-anyv4-unspec") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertUnspecifiedAddress << true;
|
||||
QTest::newRow("any-anyv6-unspec") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv6) << (int)QHostAddress::ConvertUnspecifiedAddress << true;
|
||||
QTest::newRow("anyv6-anyv4-local") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertLocalHost << false;
|
||||
QTest::newRow("any-anyv4-local") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertLocalHost << false;
|
||||
QTest::newRow("any-anyv6-local") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv6) << (int)QHostAddress::ConvertLocalHost << false;
|
||||
}
|
||||
|
||||
void tst_QHostAddress::isEqual()
|
||||
{
|
||||
QFETCH(QHostAddress, first);
|
||||
QFETCH(QHostAddress, second);
|
||||
QFETCH(int, flags);
|
||||
QFETCH(bool, result);
|
||||
|
||||
QCOMPARE(first.isEqual(second, QHostAddress::ConversionModeFlag(flags)), result);
|
||||
QCOMPARE(second.isEqual(first, QHostAddress::ConversionModeFlag(flags)), result);
|
||||
}
|
||||
|
||||
void tst_QHostAddress::assignment()
|
||||
{
|
||||
QHostAddress address;
|
||||
|
|
|
|||
Loading…
Reference in New Issue