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 <timur.pocheptsov@qt.io>
This commit is contained in:
Thiago Macieira 2017-08-09 09:38:55 -07:00
parent a93437342b
commit 18ec85a80c
10 changed files with 143 additions and 29 deletions

View File

@ -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)
{

View File

@ -159,6 +159,7 @@ public:
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)

View File

@ -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

View File

@ -119,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;
}
@ -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

View File

@ -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);

View File

@ -406,7 +406,10 @@ static void getAddresses(int sock, char *buf, QList<QNetworkInterfacePrivate *>
}
// 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);

View File

@ -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<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

View File

@ -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<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) {

View File

@ -229,6 +229,9 @@ static QList<QNetworkInterfacePrivate *> 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;
}

View File

@ -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())