From 18ec85a80c06d770c3943d98f3981ac829fd75ca Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 9 Aug 2017 09:38:55 -0700 Subject: [PATCH] QNetworkInterface: Add the DNS eligibility [ChangeLog][QtNetwork][QNetworkInterface] Added dnsEligibility() to QNetworkAddressEntry to indicate whether the address is eligible or not for publication in DNS or similar mechanisms. Change-Id: Id3ae5f853d964358ac1ab19b525334a426e0e052 Reviewed-by: Timur Pocheptsov --- src/network/kernel/qhostaddress.cpp | 27 -------- src/network/kernel/qhostaddress.h | 1 + src/network/kernel/qhostaddress_p.h | 29 ++++++++ src/network/kernel/qnetworkinterface.cpp | 68 ++++++++++++++++++- src/network/kernel/qnetworkinterface.h | 9 +++ .../kernel/qnetworkinterface_linux.cpp | 5 +- src/network/kernel/qnetworkinterface_p.h | 15 ++++ src/network/kernel/qnetworkinterface_unix.cpp | 11 +++ src/network/kernel/qnetworkinterface_win.cpp | 3 + .../tst_qnetworkinterface.cpp | 4 ++ 10 files changed, 143 insertions(+), 29 deletions(-) diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp index 72b6d2dc70..bf00c6efce 100644 --- a/src/network/kernel/qhostaddress.cpp +++ b/src/network/kernel/qhostaddress.cpp @@ -64,33 +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; - - AddressClassification classify() const; - - friend class QHostAddress; -}; - QHostAddressPrivate::QHostAddressPrivate() : a(0), protocol(QAbstractSocket::UnknownNetworkLayerProtocol) { diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h index bb1424826d..00555f3d8e 100644 --- a/src/network/kernel/qhostaddress.h +++ b/src/network/kernel/qhostaddress.h @@ -159,6 +159,7 @@ public: friend Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed) Q_DECL_NOTHROW; protected: + friend class QHostAddressPrivate; QExplicitlySharedDataPointer 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 f06feb0559..4dc2989011 100644 --- a/src/network/kernel/qhostaddress_p.h +++ b/src/network/kernel/qhostaddress_p.h @@ -101,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/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp index e546c8d949..ec58fa65c0 100644 --- a/src/network/kernel/qnetworkinterface.cpp +++ b/src/network/kernel/qnetworkinterface.cpp @@ -119,8 +119,15 @@ QList > QNetworkInterfaceManager::a QList > 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(ptr); + } return result; } @@ -158,6 +165,32 @@ QString QNetworkInterfacePrivate::makeHwAddress(int len, uchar *data) This class represents one such group. */ +/*! + \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. */ @@ -212,6 +245,39 @@ bool QNetworkAddressEntry::operator==(const QNetworkAddressEntry &other) const d->broadcast == other.d->broadcast; } +/*! + \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 diff --git a/src/network/kernel/qnetworkinterface.h b/src/network/kernel/qnetworkinterface.h index d39615e430..32b358eb8f 100644 --- a/src/network/kernel/qnetworkinterface.h +++ b/src/network/kernel/qnetworkinterface.h @@ -56,6 +56,12 @@ 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); diff --git a/src/network/kernel/qnetworkinterface_linux.cpp b/src/network/kernel/qnetworkinterface_linux.cpp index cc66c310cb..5940f80dfa 100644 --- a/src/network/kernel/qnetworkinterface_linux.cpp +++ b/src/network/kernel/qnetworkinterface_linux.cpp @@ -406,7 +406,10 @@ static void getAddresses(int sock, char *buf, QList } // now handle flags - Q_UNUSED(flags); + QNetworkInterfacePrivate::calculateDnsEligibility(&entry, + flags & IFA_F_TEMPORARY, + flags & IFA_F_DEPRECATED); + if (!entry.ip().isNull()) { entry.setPrefixLength(ifa->ifa_prefixlen); diff --git a/src/network/kernel/qnetworkinterface_p.h b/src/network/kernel/qnetworkinterface_p.h index 305a8fe020..b16149e12f 100644 --- a/src/network/kernel/qnetworkinterface_p.h +++ b/src/network/kernel/qnetworkinterface_p.h @@ -76,6 +76,7 @@ public: QNetmask netmask; bool lifetimeKnown = false; + QNetworkAddressEntry::DnsEligibilityStatus dnsEligibility = QNetworkAddressEntry::DnsEligibilityUnknown; }; class QNetworkInterfacePrivate: public QSharedData @@ -97,6 +98,20 @@ public: QList 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_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp index 7733f073d0..1c2f5d335a 100644 --- a/src/network/kernel/qnetworkinterface_unix.cpp +++ b/src/network/kernel/qnetworkinterface_unix.cpp @@ -499,6 +499,17 @@ static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + // get flags + ifr.ifr_addr = *reinterpret_cast(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(sa); if (qt_safe_ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr) < 0) { diff --git a/src/network/kernel/qnetworkinterface_win.cpp b/src/network/kernel/qnetworkinterface_win.cpp index 34d253a6d2..705bc24c32 100644 --- a/src/network/kernel/qnetworkinterface_win.cpp +++ b/src/network/kernel/qnetworkinterface_win.cpp @@ -229,6 +229,9 @@ static QList interfaceListing() 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/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp b/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp index 95dbfc749a..2f2937fcdb 100644 --- a/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp +++ b/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp @@ -152,6 +152,10 @@ void tst_QNetworkInterface::dump() << " (" << qPrintable(e.netmask().toString()) << ')'; if (!e.broadcast().isNull()) s.nospace() << " broadcast " << qPrintable(e.broadcast().toString()); + if (e.dnsEligibility() == QNetworkAddressEntry::DnsEligible) + s.nospace() << " dns-eligible"; + else if (e.dnsEligibility() == QNetworkAddressEntry::DnsIneligible) + s.nospace() << " dns-ineligible"; if (e.isLifetimeKnown()) { #define printable(l) qPrintable(l.isForever() ? "forever" : QString::fromLatin1("%1ms").arg(l.remainingTime())) s.nospace() << " preferred:" << printable(e.preferredLifetime())