summaryrefslogtreecommitdiffstats
path: root/src/network/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/kernel')
-rw-r--r--src/network/kernel/qauthenticator.cpp63
-rw-r--r--src/network/kernel/qauthenticator.h1
-rw-r--r--src/network/kernel/qauthenticator_p.h6
-rw-r--r--src/network/kernel/qdnslookup.cpp764
-rw-r--r--src/network/kernel/qdnslookup.h124
-rw-r--r--src/network/kernel/qdnslookup_android.cpp20
-rw-r--r--src/network/kernel/qdnslookup_dummy.cpp15
-rw-r--r--src/network/kernel/qdnslookup_p.h199
-rw-r--r--src/network/kernel/qdnslookup_unix.cpp597
-rw-r--r--src/network/kernel/qdnslookup_win.cpp234
-rw-r--r--src/network/kernel/qhostaddress.cpp38
-rw-r--r--src/network/kernel/qhostaddress.h3
-rw-r--r--src/network/kernel/qhostinfo.cpp120
-rw-r--r--src/network/kernel/qhostinfo.h65
-rw-r--r--src/network/kernel/qhostinfo_p.h32
-rw-r--r--src/network/kernel/qhostinfo_unix.cpp228
-rw-r--r--src/network/kernel/qnetconmonitor_darwin.mm2
-rw-r--r--src/network/kernel/qnetconmonitor_win.cpp31
-rw-r--r--src/network/kernel/qnetworkdatagram.cpp2
-rw-r--r--src/network/kernel/qnetworkdatagram.h2
-rw-r--r--src/network/kernel/qnetworkinformation.cpp90
-rw-r--r--src/network/kernel/qnetworkinformation.h2
-rw-r--r--src/network/kernel/qnetworkinformation_p.h37
-rw-r--r--src/network/kernel/qnetworkinterface.cpp2
-rw-r--r--src/network/kernel/qnetworkinterface_linux.cpp11
-rw-r--r--src/network/kernel/qnetworkinterface_unix.cpp62
-rw-r--r--src/network/kernel/qnetworkproxy.cpp60
-rw-r--r--src/network/kernel/qnetworkproxy.h5
-rw-r--r--src/network/kernel/qnetworkproxy_android.cpp2
-rw-r--r--src/network/kernel/qnetworkproxy_darwin.cpp (renamed from src/network/kernel/qnetworkproxy_mac.cpp)133
-rw-r--r--src/network/kernel/qnetworkproxy_libproxy.cpp9
-rw-r--r--src/network/kernel/qnetworkproxy_win.cpp4
-rw-r--r--src/network/kernel/qtldurl.cpp4
-rw-r--r--src/network/kernel/qtldurl_p.h3
-rw-r--r--src/network/kernel/qtnetworkglobal_p.h1
35 files changed, 2063 insertions, 908 deletions
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index 9e5f48c456..e42450d7e5 100644
--- a/src/network/kernel/qauthenticator.cpp
+++ b/src/network/kernel/qauthenticator.cpp
@@ -14,6 +14,7 @@
#include <qstring.h>
#include <qdatetime.h>
#include <qrandom.h>
+#include <QtNetwork/qhttpheaders.h>
#ifdef Q_OS_WIN
#include <qmutex.h>
@@ -444,13 +445,14 @@ static bool verifyDigestMD5(QByteArrayView value)
return true; // assume it's ok if algorithm is not specified
}
-void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray>> &values,
+void QAuthenticatorPrivate::parseHttpResponse(const QHttpHeaders &headers,
bool isProxy, QStringView host)
{
#if !QT_CONFIG(gssapi)
Q_UNUSED(host);
#endif
- const char *search = isProxy ? "proxy-authenticate" : "www-authenticate";
+ const auto search = isProxy ? QHttpHeaders::WellKnownHeader::ProxyAuthenticate
+ : QHttpHeaders::WellKnownHeader::WWWAuthenticate;
method = None;
/*
@@ -463,26 +465,23 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
authentication parameters.
*/
- QByteArray headerVal;
- for (int i = 0; i < values.size(); ++i) {
- const QPair<QByteArray, QByteArray> &current = values.at(i);
- if (current.first.compare(search, Qt::CaseInsensitive) != 0)
- continue;
- QByteArray str = current.second.toLower();
- if (method < Basic && str.startsWith("basic")) {
+ QByteArrayView headerVal;
+ for (const auto &current : headers.values(search)) {
+ const QLatin1StringView str(current);
+ if (method < Basic && str.startsWith("basic"_L1, Qt::CaseInsensitive)) {
method = Basic;
- headerVal = current.second.mid(6);
- } else if (method < Ntlm && str.startsWith("ntlm")) {
+ headerVal = QByteArrayView(current).mid(6);
+ } else if (method < Ntlm && str.startsWith("ntlm"_L1, Qt::CaseInsensitive)) {
method = Ntlm;
- headerVal = current.second.mid(5);
- } else if (method < DigestMd5 && str.startsWith("digest")) {
+ headerVal = QByteArrayView(current).mid(5);
+ } else if (method < DigestMd5 && str.startsWith("digest"_L1, Qt::CaseInsensitive)) {
// Make sure the algorithm is actually MD5 before committing to it:
- if (!verifyDigestMD5(QByteArrayView(current.second).sliced(7)))
+ if (!verifyDigestMD5(QByteArrayView(current).sliced(7)))
continue;
method = DigestMd5;
- headerVal = current.second.mid(7);
- } else if (method < Negotiate && str.startsWith("negotiate")) {
+ headerVal = QByteArrayView(current).mid(7);
+ } else if (method < Negotiate && str.startsWith("negotiate"_L1, Qt::CaseInsensitive)) {
#if QT_CONFIG(sspi) || QT_CONFIG(gssapi) // if it's not supported then we shouldn't try to use it
#if QT_CONFIG(gssapi)
// For GSSAPI there needs to be a KDC set up for the host (afaict).
@@ -492,14 +491,14 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
continue;
#endif
method = Negotiate;
- headerVal = current.second.mid(10);
+ headerVal = QByteArrayView(current).mid(10);
#endif
}
}
// Reparse credentials since we know the method now
updateCredentials();
- challenge = headerVal.trimmed();
+ challenge = headerVal.trimmed().toByteArray();
QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge);
// Sets phase to Start if this updates our realm and sets the two locations where we store
@@ -547,16 +546,14 @@ QByteArray QAuthenticatorPrivate::calculateResponse(QByteArrayView requestMethod
Q_UNUSED(host);
#endif
QByteArray response;
- const char* methodString = nullptr;
+ QByteArrayView methodString;
switch(method) {
case QAuthenticatorPrivate::None:
- methodString = "";
phase = Done;
break;
case QAuthenticatorPrivate::Basic:
methodString = "Basic";
- response = user.toLatin1() + ':' + password.toLatin1();
- response = response.toBase64();
+ response = (user + ':'_L1 + password).toLatin1().toBase64();
phase = Done;
break;
case QAuthenticatorPrivate::DigestMd5:
@@ -645,26 +642,35 @@ QByteArray QAuthenticatorPrivate::calculateResponse(QByteArrayView requestMethod
break;
}
- return QByteArray::fromRawData(methodString, qstrlen(methodString)) + ' ' + response;
+ return methodString + ' ' + response;
}
// ---------------------------- Digest Md5 code ----------------------------------------
+static bool containsAuth(QByteArrayView data)
+{
+ for (auto element : QLatin1StringView(data).tokenize(','_L1)) {
+ if (element == "auth"_L1)
+ return true;
+ }
+ return false;
+}
+
QHash<QByteArray, QByteArray>
QAuthenticatorPrivate::parseDigestAuthenticationChallenge(QByteArrayView challenge)
{
QHash<QByteArray, QByteArray> options;
// parse the challenge
const char *d = challenge.data();
- const char *end = d + challenge.length();
+ const char *end = d + challenge.size();
while (d < end) {
while (d < end && (*d == ' ' || *d == '\n' || *d == '\r'))
++d;
const char *start = d;
while (d < end && *d != '=')
++d;
- QByteArray key = QByteArray(start, d - start);
+ QByteArrayView key = QByteArrayView(start, d - start);
++d;
if (d >= end)
break;
@@ -695,13 +701,12 @@ QAuthenticatorPrivate::parseDigestAuthenticationChallenge(QByteArrayView challen
while (d < end && *d != ',')
++d;
++d;
- options[key] = value;
+ options[key.toByteArray()] = std::move(value);
}
QByteArray qop = options.value("qop");
if (!qop.isEmpty()) {
- QList<QByteArray> qopoptions = qop.split(',');
- if (!qopoptions.contains("auth"))
+ if (!containsAuth(qop))
return QHash<QByteArray, QByteArray>();
// #### can't do auth-int currently
// if (qop.contains("auth-int"))
@@ -1759,7 +1764,7 @@ static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, QByteArrayView cha
if (!challenge.isEmpty()) {
inBuf.value = const_cast<char*>(challenge.data());
- inBuf.length = challenge.length();
+ inBuf.length = challenge.size();
}
majStat = gss_init_sec_context(&minStat,
diff --git a/src/network/kernel/qauthenticator.h b/src/network/kernel/qauthenticator.h
index 4d8e85c000..a05d359e93 100644
--- a/src/network/kernel/qauthenticator.h
+++ b/src/network/kernel/qauthenticator.h
@@ -16,6 +16,7 @@ class QUrl;
class Q_NETWORK_EXPORT QAuthenticator
{
+ Q_GADGET
public:
QAuthenticator();
~QAuthenticator();
diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h
index f8579a46c0..bc16139941 100644
--- a/src/network/kernel/qauthenticator_p.h
+++ b/src/network/kernel/qauthenticator_p.h
@@ -26,13 +26,14 @@
QT_BEGIN_NAMESPACE
class QHttpResponseHeader;
+class QHttpHeaders;
#if QT_CONFIG(sspi) // SSPI
class QSSPIWindowsHandles;
#elif QT_CONFIG(gssapi) // GSSAPI
class QGssApiHandles;
#endif
-class Q_AUTOTEST_EXPORT QAuthenticatorPrivate
+class Q_NETWORK_EXPORT QAuthenticatorPrivate
{
public:
enum Method { None, Basic, Negotiate, Ntlm, DigestMd5, };
@@ -80,8 +81,7 @@ public:
static QHash<QByteArray, QByteArray>
parseDigestAuthenticationChallenge(QByteArrayView challenge);
- void parseHttpResponse(const QList<QPair<QByteArray, QByteArray>> &, bool isProxy,
- QStringView host);
+ void parseHttpResponse(const QHttpHeaders &headers, bool isProxy, QStringView host);
void updateCredentials();
static bool isMethodSupported(QByteArrayView method);
diff --git a/src/network/kernel/qdnslookup.cpp b/src/network/kernel/qdnslookup.cpp
index fb8a96a049..1b4db7130b 100644
--- a/src/network/kernel/qdnslookup.cpp
+++ b/src/network/kernel/qdnslookup.cpp
@@ -1,23 +1,43 @@
// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdnslookup.h"
#include "qdnslookup_p.h"
+#include <qapplicationstatic.h>
#include <qcoreapplication.h>
#include <qdatetime.h>
+#include <qendian.h>
+#include <qloggingcategory.h>
#include <qrandom.h>
+#include <qspan.h>
#include <qurl.h>
+#if QT_CONFIG(ssl)
+# include <qsslsocket.h>
+#endif
+
#include <algorithm>
QT_BEGIN_NAMESPACE
-QT_IMPL_METATYPE_EXTERN(QDnsLookupReply)
+using namespace Qt::StringLiterals;
-#if QT_CONFIG(thread)
-Q_GLOBAL_STATIC(QDnsLookupThreadPool, theDnsLookupThreadPool);
-#endif
+static Q_LOGGING_CATEGORY(lcDnsLookup, "qt.network.dnslookup", QtCriticalMsg)
+
+namespace {
+struct QDnsLookupThreadPool : QThreadPool
+{
+ QDnsLookupThreadPool()
+ {
+ // Run up to 5 lookups in parallel.
+ setMaxThreadCount(5);
+ }
+};
+}
+
+Q_APPLICATION_STATIC(QDnsLookupThreadPool, theDnsLookupThreadPool);
static bool qt_qdnsmailexchangerecord_less_than(const QDnsMailExchangeRecord &r1, const QDnsMailExchangeRecord &r2)
{
@@ -121,9 +141,6 @@ static void qt_qdnsservicerecord_sort(QList<QDnsServiceRecord> &records)
}
}
-const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
- QT_TRANSLATE_NOOP("QDnsLookupRunnable", "IPv6 addresses for nameservers are currently not supported");
-
/*!
\class QDnsLookup
\brief The QDnsLookup class represents a DNS lookup.
@@ -150,6 +167,42 @@ const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
\note If you simply want to find the IP address(es) associated with a host
name, or the host name associated with an IP address you should use
QHostInfo instead.
+
+ \section1 DNS-over-TLS and Authentic Data
+
+ QDnsLookup supports DNS-over-TLS (DoT, as specified by \l{RFC 7858}) on
+ some platforms. That currently includes all Unix platforms where regular
+ queries are supported, if \l QSslSocket support is present in Qt. To query
+ if support is present at runtime, use isProtocolSupported().
+
+ When using DNS-over-TLS, QDnsLookup only implements the "Opportunistic
+ Privacy Profile" method of authentication, as described in \l{RFC 7858}
+ section 4.1. In this mode, QDnsLookup (through \l QSslSocket) only
+ validates that the server presents a certificate that is valid for the
+ server being connected to. Clients may use setSslConfiguration() to impose
+ additional restrictions and sslConfiguration() to obtain information after
+ the query is complete.
+
+ QDnsLookup will request DNS servers queried over TLS to perform
+ authentication on the data they return. If they confirm the data is valid,
+ the \l authenticData property will be set to true. QDnsLookup does not
+ verify the integrity of the data by itself, so applications should only
+ trust this property on servers they have confirmed through other means to
+ be trustworthy.
+
+ \section2 Authentic Data without TLS
+
+ QDnsLookup request Authentic Data for any server set with setNameserver(),
+ even if TLS encryption is not required. This is useful when querying a
+ caching nameserver on the same host as the application or on a trusted
+ network. Though similar to the TLS case, the application is responsible for
+ determining if the server it chose to use is trustworthy, and if the
+ unencrypted connection cannot be tampered with.
+
+ QDnsLookup obeys the system configuration to request Authentic Data on the
+ default nameserver (that is, if setNameserver() is not called). This is
+ currently only supported on Linux systems using glibc 2.31 or later. On any
+ other systems, QDnsLookup will ignore the AD bit in the query header.
*/
/*!
@@ -178,6 +231,9 @@ const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
\value NotFoundError the requested domain name does not exist
(NXDOMAIN).
+
+ \value TimeoutError the server was not reached or did not reply
+ in time (since 6.6).
*/
/*!
@@ -201,10 +257,72 @@ const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
\value SRV service records.
+ \value[since 6.8] TLSA TLS association records.
+
\value TXT text records.
*/
/*!
+ \enum QDnsLookup::Protocol
+
+ Indicates the type of DNS server that is being queried.
+
+ \value Standard
+ Regular, unencrypted DNS, using UDP and falling back to TCP as necessary
+ (default port: 53)
+
+ \value DnsOverTls
+ Encrypted DNS over TLS (DoT, as specified by \l{RFC 7858}), over TCP
+ (default port: 853)
+
+ \sa isProtocolSupported(), nameserverProtocol, setNameserver()
+*/
+
+/*!
+ \since 6.8
+
+ Returns true if DNS queries using \a protocol are supported with QDnsLookup.
+
+ \sa nameserverProtocol
+*/
+bool QDnsLookup::isProtocolSupported(Protocol protocol)
+{
+#if QT_CONFIG(libresolv) || defined(Q_OS_WIN)
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ return true;
+ case QDnsLookup::DnsOverTls:
+# if QT_CONFIG(ssl)
+ if (QSslSocket::supportsSsl())
+ return true;
+# endif
+ return false;
+ }
+#else
+ Q_UNUSED(protocol)
+#endif
+ return false;
+}
+
+/*!
+ \since 6.8
+
+ Returns the standard (default) port number for the protocol \a protocol.
+
+ \sa isProtocolSupported()
+*/
+quint16 QDnsLookup::defaultPortForProtocol(Protocol protocol) noexcept
+{
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ return DnsPort;
+ case QDnsLookup::DnsOverTls:
+ return DnsOverTlsPort;
+ }
+ return 0; // will probably fail somewhere
+}
+
+/*!
\fn void QDnsLookup::finished()
This signal is emitted when the reply has finished processing.
@@ -233,8 +351,8 @@ const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
QDnsLookup::QDnsLookup(QObject *parent)
: QObject(*new QDnsLookupPrivate, parent)
{
- qRegisterMetaType<QDnsLookupReply>();
}
+
/*!
Constructs a QDnsLookup object for the given \a type and \a name and sets
\a parent as the parent object.
@@ -244,7 +362,6 @@ QDnsLookup::QDnsLookup(Type type, const QString &name, QObject *parent)
: QObject(*new QDnsLookupPrivate, parent)
{
Q_D(QDnsLookup);
- qRegisterMetaType<QDnsLookupReply>();
d->name = name;
d->type = type;
}
@@ -252,18 +369,65 @@ QDnsLookup::QDnsLookup(Type type, const QString &name, QObject *parent)
/*!
\fn QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent)
\since 5.4
- Constructs a QDnsLookup object for the given \a type, \a name and
- \a nameserver and sets \a parent as the parent object.
+
+ Constructs a QDnsLookup object to issue a query for \a name of record type
+ \a type, using the DNS server \a nameserver running on the default DNS port,
+ and sets \a parent as the parent object.
*/
QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent)
+ : QDnsLookup(type, name, nameserver, 0, parent)
+{
+}
+
+/*!
+ \fn QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port, QObject *parent)
+ \since 6.6
+
+ Constructs a QDnsLookup object to issue a query for \a name of record type
+ \a type, using the DNS server \a nameserver running on port \a port, and
+ sets \a parent as the parent object.
+
+//! [nameserver-port]
+ \note Setting the port number to any value other than the default (53) can
+ cause the name resolution to fail, depending on the operating system
+ limitations and firewalls, if the nameserverProtocol() to be used
+ QDnsLookup::Standard. Notably, the Windows API used by QDnsLookup is unable
+ to handle alternate port numbers.
+//! [nameserver-port]
+*/
+QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port, QObject *parent)
+ : QObject(*new QDnsLookupPrivate, parent)
+{
+ Q_D(QDnsLookup);
+ d->name = name;
+ d->type = type;
+ d->port = port;
+ d->nameserver = nameserver;
+}
+
+/*!
+ \since 6.8
+
+ Constructs a QDnsLookup object to issue a query for \a name of record type
+ \a type, using the DNS server \a nameserver running on port \a port, and
+ sets \a parent as the parent object.
+
+ The query will be sent using \a protocol, if supported. Use
+ isProtocolSupported() to check if it is supported.
+
+ \include qdnslookup.cpp nameserver-port
+*/
+QDnsLookup::QDnsLookup(Type type, const QString &name, Protocol protocol,
+ const QHostAddress &nameserver, quint16 port, QObject *parent)
: QObject(*new QDnsLookupPrivate, parent)
{
Q_D(QDnsLookup);
- qRegisterMetaType<QDnsLookupReply>();
d->name = name;
d->type = type;
d->nameserver = nameserver;
+ d->port = port;
+ d->protocol = protocol;
}
/*!
@@ -278,6 +442,28 @@ QDnsLookup::~QDnsLookup()
}
/*!
+ \since 6.8
+ \property QDnsLookup::authenticData
+ \brief whether the reply was authenticated by the resolver.
+
+ QDnsLookup does not perform the authentication itself. Instead, it trusts
+ the name server that was queried to perform the authentication and report
+ it. The application is responsible for determining if any servers it
+ configured with setNameserver() are trustworthy; if no server was set,
+ QDnsLookup obeys system configuration on whether responses should be
+ trusted.
+
+ This property may be set even if error() indicates a resolver error
+ occurred.
+
+ \sa setNameserver(), nameserverProtocol()
+*/
+bool QDnsLookup::isAuthenticData() const
+{
+ return d_func()->reply.authenticData;
+}
+
+/*!
\property QDnsLookup::error
\brief the type of error that occurred if the DNS lookup failed, or NoError.
*/
@@ -310,6 +496,10 @@ bool QDnsLookup::isFinished() const
\property QDnsLookup::name
\brief the name to lookup.
+ If the name to look up is empty, QDnsLookup will attempt to resolve the
+ root domain of DNS. That query is usually performed with QDnsLookup::type
+ set to \l{QDnsLookup::Type}{NS}.
+
\note The name will be encoded using IDNA, which means it's unsuitable for
querying SRV records compatible with the DNS-SD specification.
*/
@@ -376,6 +566,76 @@ QBindable<QHostAddress> QDnsLookup::bindableNameserver()
}
/*!
+ \property QDnsLookup::nameserverPort
+ \since 6.6
+ \brief the port number of nameserver to use for DNS lookup.
+
+ The value of 0 indicates that QDnsLookup should use the default port for
+ the nameserverProtocol().
+
+ \include qdnslookup.cpp nameserver-port
+*/
+
+quint16 QDnsLookup::nameserverPort() const
+{
+ return d_func()->port;
+}
+
+void QDnsLookup::setNameserverPort(quint16 nameserverPort)
+{
+ Q_D(QDnsLookup);
+ d->port = nameserverPort;
+}
+
+QBindable<quint16> QDnsLookup::bindableNameserverPort()
+{
+ Q_D(QDnsLookup);
+ return &d->port;
+}
+
+/*!
+ \property QDnsLookup::nameserverProtocol
+ \since 6.8
+ \brief the protocol to use when sending the DNS query
+
+ \sa isProtocolSupported()
+*/
+QDnsLookup::Protocol QDnsLookup::nameserverProtocol() const
+{
+ return d_func()->protocol;
+}
+
+void QDnsLookup::setNameserverProtocol(Protocol protocol)
+{
+ d_func()->protocol = protocol;
+}
+
+QBindable<QDnsLookup::Protocol> QDnsLookup::bindableNameserverProtocol()
+{
+ return &d_func()->protocol;
+}
+
+/*!
+ \fn void QDnsLookup::setNameserver(const QHostAddress &nameserver, quint16 port)
+ \since 6.6
+
+ Sets the nameserver to \a nameserver and the port to \a port.
+
+ \include qdnslookup.cpp nameserver-port
+
+ \sa QDnsLookup::nameserver, QDnsLookup::nameserverPort
+*/
+
+void QDnsLookup::setNameserver(Protocol protocol, const QHostAddress &nameserver, quint16 port)
+{
+ Qt::beginPropertyUpdateGroup();
+ setNameserver(nameserver);
+ setNameserverPort(port);
+ setNameserverProtocol(protocol);
+ Qt::endPropertyUpdateGroup();
+}
+
+/*!
Returns the list of canonical name records associated with this lookup.
*/
@@ -447,6 +707,46 @@ QList<QDnsTextRecord> QDnsLookup::textRecords() const
}
/*!
+ \since 6.8
+ Returns the list of TLS association records associated with this lookup.
+
+ According to the standards relating to DNS-based Authentication of Named
+ Entities (DANE), this field should be ignored and must not be used for
+ verifying the authentity of a given server if the authenticity of the DNS
+ reply cannot itself be confirmed. See isAuthenticData() for more
+ information.
+ */
+QList<QDnsTlsAssociationRecord> QDnsLookup::tlsAssociationRecords() const
+{
+ return d_func()->reply.tlsAssociationRecords;
+}
+
+#if QT_CONFIG(ssl)
+/*!
+ \since 6.8
+ Sets the \a sslConfiguration to use for outgoing DNS-over-TLS connections.
+
+ \sa sslConfiguration(), QSslSocket::setSslConfiguration()
+*/
+void QDnsLookup::setSslConfiguration(const QSslConfiguration &sslConfiguration)
+{
+ Q_D(QDnsLookup);
+ d->sslConfiguration.emplace(sslConfiguration);
+}
+
+/*!
+ Returns the current SSL configuration.
+
+ \sa setSslConfiguration()
+*/
+QSslConfiguration QDnsLookup::sslConfiguration() const
+{
+ const Q_D(QDnsLookup);
+ return d->sslConfiguration.value_or(QSslConfiguration::defaultConfiguration());
+}
+#endif
+
+/*!
Aborts the DNS lookup operation.
If the lookup is already finished, does nothing.
@@ -476,13 +776,32 @@ void QDnsLookup::lookup()
Q_D(QDnsLookup);
d->isFinished = false;
d->reply = QDnsLookupReply();
- d->runnable = new QDnsLookupRunnable(d->type, QUrl::toAce(d->name), d->nameserver);
- connect(d->runnable, SIGNAL(finished(QDnsLookupReply)),
- this, SLOT(_q_lookupFinished(QDnsLookupReply)),
- Qt::BlockingQueuedConnection);
-#if QT_CONFIG(thread)
- theDnsLookupThreadPool()->start(d->runnable);
+ if (!QCoreApplication::instance()) {
+ // NOT qCWarning because this isn't a result of the lookup
+ qWarning("QDnsLookup requires a QCoreApplication");
+ return;
+ }
+
+ auto l = [this](const QDnsLookupReply &reply) {
+ Q_D(QDnsLookup);
+ if (d->runnable == sender()) {
+#ifdef QDNSLOOKUP_DEBUG
+ qDebug("DNS reply for %s: %i (%s)", qPrintable(d->name), reply.error, qPrintable(reply.errorString));
#endif
+#if QT_CONFIG(ssl)
+ d->sslConfiguration = std::move(reply.sslConfiguration);
+#endif
+ d->reply = reply;
+ d->runnable = nullptr;
+ d->isFinished = true;
+ emit finished();
+ }
+ };
+
+ d->runnable = new QDnsLookupRunnable(d);
+ connect(d->runnable, &QDnsLookupRunnable::finished, this, l,
+ Qt::BlockingQueuedConnection);
+ theDnsLookupThreadPool->start(d->runnable);
}
/*!
@@ -959,18 +1278,249 @@ QDnsTextRecord &QDnsTextRecord::operator=(const QDnsTextRecord &other)
very fast and never fails.
*/
-void QDnsLookupPrivate::_q_lookupFinished(const QDnsLookupReply &_reply)
+/*!
+ \class QDnsTlsAssociationRecord
+ \since 6.8
+ \brief The QDnsTlsAssociationRecord class stores information about a DNS TLSA record.
+
+ \inmodule QtNetwork
+ \ingroup network
+ \ingroup shared
+
+ When performing a text lookup, zero or more records will be returned. Each
+ record is represented by a QDnsTlsAssociationRecord instance.
+
+ The meaning of the fields is defined in \l{RFC 6698}.
+
+ \sa QDnsLookup
+*/
+
+QT_DEFINE_QSDP_SPECIALIZATION_DTOR(QDnsTlsAssociationRecordPrivate)
+
+/*!
+ \enum QDnsTlsAssociationRecord::CertificateUsage
+
+ This enumeration contains valid values for the certificate usage field of
+ TLS Association queries. The following list is up-to-date with \l{RFC 6698}
+ section 2.1.1 and RFC 7218 section 2.1. Please refer to those documents for
+ authoritative instructions on interpreting this enumeration.
+
+ \value CertificateAuthorityConstrait
+ Indicates the record includes an association to a specific Certificate
+ Authority that must be found in the TLS server's certificate chain and
+ must pass PKIX validation.
+
+ \value ServiceCertificateConstraint
+ Indicates the record includes an association to a certificate that must
+ match the end entity certificate provided by the TLS server and must
+ pass PKIX validation.
+
+ \value TrustAnchorAssertion
+ Indicates the record includes an association to a certificate that MUST
+ be used as the ultimate trust anchor to validate the TLS server's
+ certificate and must pass PKIX validation.
+
+ \value DomainIssuedCertificate
+ Indicates the record includes an association to a certificate that must
+ match the end entity certificate provided by the TLS server. PKIX
+ validation is not tested.
+
+ \value PrivateUse
+ No standard meaning applied.
+
+ \value PKIX_TA
+ Alias; mnemonic for Public Key Infrastructure Trust Anchor
+
+ \value PKIX_EE
+ Alias; mnemonic for Public Key Infrastructure End Entity
+
+ \value DANE_TA
+ Alias; mnemonic for DNS-based Authentication of Named Entities Trust Anchor
+
+ \value DANE_EE
+ Alias; mnemonic for DNS-based Authentication of Named Entities End Entity
+
+ \value PrivCert
+ Alias
+
+ Other values are currently reserved, but may be unreserved by future
+ standards. This enumeration can be used for those values even if no
+ enumerator is provided.
+
+ \sa certificateUsage()
+*/
+
+/*!
+ \enum QDnsTlsAssociationRecord::Selector
+
+ This enumeration contains valid values for the selector field of TLS
+ Association queries. The following list is up-to-date with \l{RFC 6698}
+ section 2.1.2 and RFC 7218 section 2.2. Please refer to those documents for
+ authoritative instructions on interpreting this enumeration.
+
+ \value FullCertificate
+ Indicates this record refers to the full certificate in its binary
+ structure form.
+
+ \value SubjectPublicKeyInfo
+ Indicates the record refers to the certificate's subject and public
+ key information, in DER-encoded binary structure form.
+
+ \value PrivateUse
+ No standard meaning applied.
+
+ \value Cert
+ Alias
+
+ \value SPKI
+ Alias
+
+ \value PrivSel
+ Alias
+
+ Other values are currently reserved, but may be unreserved by future
+ standards. This enumeration can be used for those values even if no
+ enumerator is provided.
+
+ \sa selector()
+*/
+
+/*!
+ \enum QDnsTlsAssociationRecord::MatchingType
+
+ This enumeration contains valid values for the matching type field of TLS
+ Association queries. The following list is up-to-date with \l{RFC 6698}
+ section 2.1.3 and RFC 7218 section 2.3. Please refer to those documents for
+ authoritative instructions on interpreting this enumeration.
+
+ \value Exact
+ Indicates this the certificate or SPKI data is stored verbatim in this
+ record.
+
+ \value Sha256
+ Indicates this a SHA-256 checksum of the the certificate or SPKI data
+ present in this record.
+
+ \value Sha512
+ Indicates this a SHA-512 checksum of the the certificate or SPKI data
+ present in this record.
+
+ \value PrivateUse
+ No standard meaning applied.
+
+ \value PrivMatch
+ Alias
+
+ Other values are currently reserved, but may be unreserved by future
+ standards. This enumeration can be used for those values even if no
+ enumerator is provided.
+
+ \sa matchingType()
+*/
+
+/*!
+ Constructs an empty TLS Association record.
+ */
+QDnsTlsAssociationRecord::QDnsTlsAssociationRecord()
+ : d(new QDnsTlsAssociationRecordPrivate)
{
- Q_Q(QDnsLookup);
- if (runnable == q->sender()) {
-#ifdef QDNSLOOKUP_DEBUG
- qDebug("DNS reply for %s: %i (%s)", qPrintable(name), _reply.error, qPrintable(_reply.errorString));
+}
+
+/*!
+ Constructs a copy of \a other.
+ */
+QDnsTlsAssociationRecord::QDnsTlsAssociationRecord(const QDnsTlsAssociationRecord &other) = default;
+
+/*!
+ Moves the content of \a other into this object.
+ */
+QDnsTlsAssociationRecord &
+QDnsTlsAssociationRecord::operator=(const QDnsTlsAssociationRecord &other) = default;
+
+/*!
+ Destroys this TLS Association record object.
+ */
+QDnsTlsAssociationRecord::~QDnsTlsAssociationRecord() = default;
+
+/*!
+ Returns the name of this record.
+*/
+QString QDnsTlsAssociationRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+quint32 QDnsTlsAssociationRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Returns the certificate usage field for this record.
+ */
+QDnsTlsAssociationRecord::CertificateUsage QDnsTlsAssociationRecord::usage() const
+{
+ return d->usage;
+}
+
+/*!
+ Returns the selector field for this record.
+ */
+QDnsTlsAssociationRecord::Selector QDnsTlsAssociationRecord::selector() const
+{
+ return d->selector;
+}
+
+/*!
+ Returns the match type field for this record.
+ */
+QDnsTlsAssociationRecord::MatchingType QDnsTlsAssociationRecord::matchType() const
+{
+ return d->matchType;
+}
+
+/*!
+ Returns the binary data field for this record. The interpretation of this
+ binary data depends on the three numeric fields provided by
+ certificateUsage(), selector(), and matchType().
+
+ Do note this is a binary field, even for the checksums, similar to what
+ QCyrptographicHash::result() returns.
+ */
+QByteArray QDnsTlsAssociationRecord::value() const
+{
+ return d->value;
+}
+
+static QDnsLookupRunnable::EncodedLabel encodeLabel(const QString &label)
+{
+ QDnsLookupRunnable::EncodedLabel::value_type rootDomain = u'.';
+ if (label.isEmpty())
+ return QDnsLookupRunnable::EncodedLabel(1, rootDomain);
+
+ QString encodedLabel = qt_ACE_do(label, ToAceOnly, ForbidLeadingDot);
+#ifdef Q_OS_WIN
+ return encodedLabel;
+#else
+ return std::move(encodedLabel).toLatin1();
+#endif
+}
+
+inline QDnsLookupRunnable::QDnsLookupRunnable(const QDnsLookupPrivate *d)
+ : requestName(encodeLabel(d->name)),
+ nameserver(d->nameserver),
+ requestType(d->type),
+ port(d->port),
+ protocol(d->protocol)
+{
+ if (port == 0)
+ port = QDnsLookup::defaultPortForProtocol(protocol);
+#if QT_CONFIG(ssl)
+ sslConfiguration = d->sslConfiguration;
#endif
- reply = _reply;
- runnable = nullptr;
- isFinished = true;
- emit q->finished();
- }
}
void QDnsLookupRunnable::run()
@@ -978,60 +1528,144 @@ void QDnsLookupRunnable::run()
QDnsLookupReply reply;
// Validate input.
- if (requestName.isEmpty()) {
+ if (qsizetype n = requestName.size(); n > MaxDomainNameLength || n == 0) {
reply.error = QDnsLookup::InvalidRequestError;
- reply.errorString = tr("Invalid domain name");
- emit finished(reply);
- return;
+ reply.errorString = QDnsLookup::tr("Invalid domain name");
+ } else {
+ // Perform request.
+ query(&reply);
+
+ // Sort results.
+ qt_qdnsmailexchangerecord_sort(reply.mailExchangeRecords);
+ qt_qdnsservicerecord_sort(reply.serviceRecords);
}
- // Perform request.
- query(requestType, requestName, nameserver, &reply);
+ emit finished(reply);
- // Sort results.
- qt_qdnsmailexchangerecord_sort(reply.mailExchangeRecords);
- qt_qdnsservicerecord_sort(reply.serviceRecords);
+ // maybe print the lookup error as warning
+ switch (reply.error) {
+ case QDnsLookup::NoError:
+ case QDnsLookup::OperationCancelledError:
+ case QDnsLookup::NotFoundError:
+ case QDnsLookup::ServerFailureError:
+ case QDnsLookup::ServerRefusedError:
+ case QDnsLookup::TimeoutError:
+ break; // no warning for these
+
+ case QDnsLookup::ResolverError:
+ case QDnsLookup::InvalidRequestError:
+ case QDnsLookup::InvalidReplyError:
+ qCWarning(lcDnsLookup()).nospace()
+ << "DNS lookup failed (" << reply.error << "): "
+ << qUtf16Printable(reply.errorString)
+ << "; request was " << this; // continues below
+ }
+}
- emit finished(reply);
+inline QDebug operator<<(QDebug &d, QDnsLookupRunnable *r)
+{
+ // continued: print the information about the request
+ d << r->requestName.left(MaxDomainNameLength);
+ if (r->requestName.size() > MaxDomainNameLength)
+ d << "... (truncated)";
+ d << " type " << r->requestType;
+ if (!r->nameserver.isNull()) {
+ d << " to nameserver " << qUtf16Printable(r->nameserver.toString())
+ << " port " << (r->port ? r->port : QDnsLookup::defaultPortForProtocol(r->protocol));
+ switch (r->protocol) {
+ case QDnsLookup::Standard:
+ break;
+ case QDnsLookup::DnsOverTls:
+ d << " (TLS)";
+ }
+ }
+ return d;
}
-#if QT_CONFIG(thread)
-QDnsLookupThreadPool::QDnsLookupThreadPool()
- : signalsConnected(false)
+#if QT_CONFIG(ssl)
+static constexpr std::chrono::milliseconds DnsOverTlsConnectTimeout(15'000);
+static constexpr std::chrono::milliseconds DnsOverTlsTimeout(120'000);
+static constexpr quint8 DnsAuthenticDataBit = 0x20;
+
+static int makeReplyErrorFromSocket(QDnsLookupReply *reply, const QAbstractSocket *socket)
{
- // Run up to 5 lookups in parallel.
- setMaxThreadCount(5);
+ QDnsLookup::Error error = [&] {
+ switch (socket->error()) {
+ case QAbstractSocket::SocketTimeoutError:
+ case QAbstractSocket::ProxyConnectionTimeoutError:
+ return QDnsLookup::TimeoutError;
+ default:
+ return QDnsLookup::ResolverError;
+ }
+ }();
+ reply->setError(error, socket->errorString());
+ return false;
}
-void QDnsLookupThreadPool::start(QRunnable *runnable)
+bool QDnsLookupRunnable::sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query,
+ ReplyBuffer &response)
{
- // Ensure threads complete at application destruction.
- if (!signalsConnected) {
- QMutexLocker signalsLocker(&signalsMutex);
- if (!signalsConnected) {
- QCoreApplication *app = QCoreApplication::instance();
- if (!app) {
- qWarning("QDnsLookup requires a QCoreApplication");
- delete runnable;
- return;
- }
+ QSslSocket socket;
+ socket.setSslConfiguration(sslConfiguration.value_or(QSslConfiguration::defaultConfiguration()));
+
+# if QT_CONFIG(networkproxy)
+ socket.setProtocolTag("domain-s"_L1);
+# endif
+
+ // Request the name server attempt to authenticate the reply.
+ query[3] |= DnsAuthenticDataBit;
- moveToThread(app->thread());
- connect(app, SIGNAL(destroyed()),
- SLOT(_q_applicationDestroyed()), Qt::DirectConnection);
- signalsConnected = true;
+ do {
+ quint16 size = qToBigEndian<quint16>(query.size());
+ QDeadlineTimer timeout(DnsOverTlsTimeout);
+
+ socket.connectToHostEncrypted(nameserver.toString(), port);
+ socket.write(reinterpret_cast<const char *>(&size), sizeof(size));
+ socket.write(reinterpret_cast<const char *>(query.data()), query.size());
+ if (!socket.waitForEncrypted(DnsOverTlsConnectTimeout.count()))
+ break;
+
+ reply->sslConfiguration = socket.sslConfiguration();
+
+ // accumulate reply
+ auto waitForBytes = [&](void *buffer, int count) {
+ int remaining = timeout.remainingTime();
+ while (remaining >= 0 && socket.bytesAvailable() < count) {
+ if (!socket.waitForReadyRead(remaining))
+ return false;
+ }
+ return socket.read(static_cast<char *>(buffer), count) == count;
+ };
+ if (!waitForBytes(&size, sizeof(size)))
+ break;
+
+ // note: strictly speaking, we're allocating memory based on untrusted data
+ // but in practice, due to limited range of the data type (16 bits),
+ // the maximum allocation is small.
+ size = qFromBigEndian(size);
+ response.resize(size);
+ if (waitForBytes(response.data(), size)) {
+ // check if the AD bit is set; we'll trust it over TLS requests
+ if (size >= 4)
+ reply->authenticData = response[3] & DnsAuthenticDataBit;
+ return true;
}
- }
+ } while (false);
- QThreadPool::start(runnable);
+ // handle errors
+ return makeReplyErrorFromSocket(reply, &socket);
}
-
-void QDnsLookupThreadPool::_q_applicationDestroyed()
+#else
+bool QDnsLookupRunnable::sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query,
+ ReplyBuffer &response)
{
- waitForDone();
- signalsConnected = false;
+ Q_UNUSED(query)
+ Q_UNUSED(response)
+ reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr("SSL/TLS support not present"));
+ return false;
}
-#endif // QT_CONFIG(thread)
+#endif
+
QT_END_NAMESPACE
#include "moc_qdnslookup.cpp"
diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h
index b0bb34ee75..8d21e99c84 100644
--- a/src/network/kernel/qdnslookup.h
+++ b/src/network/kernel/qdnslookup.h
@@ -8,7 +8,6 @@
#include <QtCore/qlist.h>
#include <QtCore/qobject.h>
#include <QtCore/qshareddata.h>
-#include <QtCore/qsharedpointer.h>
#include <QtCore/qstring.h>
#include <QtCore/qproperty.h>
@@ -23,6 +22,10 @@ class QDnsHostAddressRecordPrivate;
class QDnsMailExchangeRecordPrivate;
class QDnsServiceRecordPrivate;
class QDnsTextRecordPrivate;
+class QDnsTlsAssociationRecordPrivate;
+class QSslConfiguration;
+
+QT_DECLARE_QSDP_SPECIALIZATION_DTOR(QDnsTlsAssociationRecordPrivate)
class Q_NETWORK_EXPORT QDnsDomainNameRecord
{
@@ -138,15 +141,92 @@ private:
Q_DECLARE_SHARED(QDnsTextRecord)
+class Q_NETWORK_EXPORT QDnsTlsAssociationRecord
+{
+ Q_GADGET
+public:
+ enum class CertificateUsage : quint8 {
+ // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#certificate-usages
+ // RFC 6698
+ CertificateAuthorityConstrait = 0,
+ ServiceCertificateConstraint = 1,
+ TrustAnchorAssertion = 2,
+ DomainIssuedCertificate = 3,
+ PrivateUse = 255,
+
+ // Aliases by RFC 7218
+ PKIX_TA = 0,
+ PKIX_EE = 1,
+ DANE_TA = 2,
+ DANE_EE = 3,
+ PrivCert = 255,
+ };
+ Q_ENUM(CertificateUsage)
+
+ enum class Selector : quint8 {
+ // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#selectors
+ // RFC 6698
+ FullCertificate = 0,
+ SubjectPublicKeyInfo = 1,
+ PrivateUse = 255,
+
+ // Aliases by RFC 7218
+ Cert = FullCertificate,
+ SPKI = SubjectPublicKeyInfo,
+ PrivSel = PrivateUse,
+ };
+ Q_ENUM(Selector)
+
+ enum class MatchingType : quint8 {
+ // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#matching-types
+ // RFC 6698
+ Exact = 0,
+ Sha256 = 1,
+ Sha512 = 2,
+ PrivateUse = 255,
+ PrivMatch = PrivateUse,
+ };
+ Q_ENUM(MatchingType)
+
+ QDnsTlsAssociationRecord();
+ QDnsTlsAssociationRecord(const QDnsTlsAssociationRecord &other);
+ QDnsTlsAssociationRecord(QDnsTlsAssociationRecord &&other)
+ : d(std::move(other.d))
+ {}
+ QDnsTlsAssociationRecord &operator=(QDnsTlsAssociationRecord &&other) noexcept { swap(other); return *this; }
+ QDnsTlsAssociationRecord &operator=(const QDnsTlsAssociationRecord &other);
+ ~QDnsTlsAssociationRecord();
+
+ void swap(QDnsTlsAssociationRecord &other) noexcept { d.swap(other.d); }
+
+ QString name() const;
+ quint32 timeToLive() const;
+ CertificateUsage usage() const;
+ Selector selector() const;
+ MatchingType matchType() const;
+ QByteArray value() const;
+
+private:
+ QSharedDataPointer<QDnsTlsAssociationRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+Q_DECLARE_SHARED(QDnsTlsAssociationRecord)
+
class Q_NETWORK_EXPORT QDnsLookup : public QObject
{
Q_OBJECT
Q_PROPERTY(Error error READ error NOTIFY finished)
+ Q_PROPERTY(bool authenticData READ isAuthenticData NOTIFY finished)
Q_PROPERTY(QString errorString READ errorString NOTIFY finished)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged BINDABLE bindableName)
Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged BINDABLE bindableType)
Q_PROPERTY(QHostAddress nameserver READ nameserver WRITE setNameserver NOTIFY nameserverChanged
BINDABLE bindableNameserver)
+ Q_PROPERTY(quint16 nameserverPort READ nameserverPort WRITE setNameserverPort
+ NOTIFY nameserverPortChanged BINDABLE bindableNameserverPort)
+ Q_PROPERTY(Protocol nameserverProtocol READ nameserverProtocol WRITE setNameserverProtocol
+ NOTIFY nameserverProtocolChanged BINDABLE bindableNameserverProtocol)
public:
enum Error
@@ -158,7 +238,8 @@ public:
InvalidReplyError,
ServerFailureError,
ServerRefusedError,
- NotFoundError
+ NotFoundError,
+ TimeoutError,
};
Q_ENUM(Error)
@@ -172,15 +253,27 @@ public:
NS = 2,
PTR = 12,
SRV = 33,
+ TLSA = 52,
TXT = 16
};
Q_ENUM(Type)
+ enum Protocol : quint8 {
+ Standard = 0,
+ DnsOverTls,
+ };
+ Q_ENUM(Protocol)
+
explicit QDnsLookup(QObject *parent = nullptr);
QDnsLookup(Type type, const QString &name, QObject *parent = nullptr);
QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent = nullptr);
+ QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port,
+ QObject *parent = nullptr);
+ QDnsLookup(Type type, const QString &name, Protocol protocol, const QHostAddress &nameserver,
+ quint16 port = 0, QObject *parent = nullptr);
~QDnsLookup();
+ bool isAuthenticData() const;
Error error() const;
QString errorString() const;
bool isFinished() const;
@@ -196,6 +289,15 @@ public:
QHostAddress nameserver() const;
void setNameserver(const QHostAddress &nameserver);
QBindable<QHostAddress> bindableNameserver();
+ quint16 nameserverPort() const;
+ void setNameserverPort(quint16 port);
+ QBindable<quint16> bindableNameserverPort();
+ Protocol nameserverProtocol() const;
+ void setNameserverProtocol(Protocol protocol);
+ QBindable<Protocol> bindableNameserverProtocol();
+ void setNameserver(Protocol protocol, const QHostAddress &nameserver, quint16 port = 0);
+ QT_NETWORK_INLINE_SINCE(6, 8)
+ void setNameserver(const QHostAddress &nameserver, quint16 port);
QList<QDnsDomainNameRecord> canonicalNameRecords() const;
QList<QDnsHostAddressRecord> hostAddressRecords() const;
@@ -204,7 +306,15 @@ public:
QList<QDnsDomainNameRecord> pointerRecords() const;
QList<QDnsServiceRecord> serviceRecords() const;
QList<QDnsTextRecord> textRecords() const;
+ QList<QDnsTlsAssociationRecord> tlsAssociationRecords() const;
+
+#if QT_CONFIG(ssl)
+ void setSslConfiguration(const QSslConfiguration &sslConfiguration);
+ QSslConfiguration sslConfiguration() const;
+#endif
+ static bool isProtocolSupported(Protocol protocol);
+ static quint16 defaultPortForProtocol(Protocol protocol) noexcept Q_DECL_CONST_FUNCTION;
public Q_SLOTS:
void abort();
@@ -215,12 +325,20 @@ Q_SIGNALS:
void nameChanged(const QString &name);
void typeChanged(Type type);
void nameserverChanged(const QHostAddress &nameserver);
+ void nameserverPortChanged(quint16 port);
+ void nameserverProtocolChanged(Protocol protocol);
private:
Q_DECLARE_PRIVATE(QDnsLookup)
- Q_PRIVATE_SLOT(d_func(), void _q_lookupFinished(const QDnsLookupReply &reply))
};
+#if QT_NETWORK_INLINE_IMPL_SINCE(6, 8)
+void QDnsLookup::setNameserver(const QHostAddress &nameserver, quint16 port)
+{
+ setNameserver(Standard, nameserver, port);
+}
+#endif
+
QT_END_NAMESPACE
#endif // QDNSLOOKUP_H
diff --git a/src/network/kernel/qdnslookup_android.cpp b/src/network/kernel/qdnslookup_android.cpp
deleted file mode 100644
index 8fc1265e80..0000000000
--- a/src/network/kernel/qdnslookup_android.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (C) 2012 Collabora Ltd, author <robin.burchell@collabora.co.uk>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qdnslookup_p.h"
-
-QT_BEGIN_NAMESPACE
-
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
-{
- Q_UNUSED(requestType);
- Q_UNUSED(requestName);
- Q_UNUSED(nameserver);
- Q_UNUSED(reply);
- qWarning("Not yet supported on Android");
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Not yet supported on Android");
- return;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_dummy.cpp b/src/network/kernel/qdnslookup_dummy.cpp
new file mode 100644
index 0000000000..6cc6ed92c5
--- /dev/null
+++ b/src/network/kernel/qdnslookup_dummy.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2012 Collabora Ltd, author <robin.burchell@collabora.co.uk>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qdnslookup_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QDnsLookupRunnable::query(QDnsLookupReply *reply)
+{
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = tr("Not yet supported on this OS");
+ return;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h
index 21262346b5..1f32b4ee4f 100644
--- a/src/network/kernel/qdnslookup_p.h
+++ b/src/network/kernel/qdnslookup_p.h
@@ -1,4 +1,5 @@
// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QDNSLOOKUP_P_H
@@ -18,13 +19,17 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qmutex.h"
#include "QtCore/qrunnable.h"
-#include "QtCore/qsharedpointer.h"
#if QT_CONFIG(thread)
#include "QtCore/qthreadpool.h"
#endif
#include "QtNetwork/qdnslookup.h"
#include "QtNetwork/qhostaddress.h"
#include "private/qobject_p.h"
+#include "private/qurl_p.h"
+
+#if QT_CONFIG(ssl)
+# include "qsslconfiguration.h"
+#endif
QT_REQUIRE_CONFIG(dnslookup);
@@ -32,16 +37,18 @@ QT_BEGIN_NAMESPACE
//#define QDNSLOOKUP_DEBUG
+constexpr qsizetype MaxDomainNameLength = 255;
+constexpr quint16 DnsPort = 53;
+constexpr quint16 DnsOverTlsPort = 853;
+
class QDnsLookupRunnable;
+QDebug operator<<(QDebug &, QDnsLookupRunnable *);
class QDnsLookupReply
{
public:
- QDnsLookupReply()
- : error(QDnsLookup::NoError)
- { }
-
- QDnsLookup::Error error;
+ QDnsLookup::Error error = QDnsLookup::NoError;
+ bool authenticData = false;
QString errorString;
QList<QDnsDomainNameRecord> canonicalNameRecords;
@@ -50,24 +57,94 @@ public:
QList<QDnsDomainNameRecord> nameServerRecords;
QList<QDnsDomainNameRecord> pointerRecords;
QList<QDnsServiceRecord> serviceRecords;
+ QList<QDnsTlsAssociationRecord> tlsAssociationRecords;
QList<QDnsTextRecord> textRecords;
+
+#if QT_CONFIG(ssl)
+ std::optional<QSslConfiguration> sslConfiguration;
+#endif
+
+ // helper methods
+ void setError(QDnsLookup::Error err, QString &&msg)
+ {
+ error = err;
+ errorString = std::move(msg);
+ }
+
+ void makeResolverSystemError(int code = -1)
+ {
+ Q_ASSERT(allAreEmpty());
+ setError(QDnsLookup::ResolverError, qt_error_string(code));
+ }
+
+ void makeTimeoutError()
+ {
+ Q_ASSERT(allAreEmpty());
+ setError(QDnsLookup::TimeoutError, QDnsLookup::tr("Request timed out"));
+ }
+
+ void makeDnsRcodeError(quint8 rcode)
+ {
+ Q_ASSERT(allAreEmpty());
+ switch (rcode) {
+ case 1: // FORMERR
+ error = QDnsLookup::InvalidRequestError;
+ errorString = QDnsLookup::tr("Server could not process query");
+ return;
+ case 2: // SERVFAIL
+ case 4: // NOTIMP
+ error = QDnsLookup::ServerFailureError;
+ errorString = QDnsLookup::tr("Server failure");
+ return;
+ case 3: // NXDOMAIN
+ error = QDnsLookup::NotFoundError;
+ errorString = QDnsLookup::tr("Non existent domain");
+ return;
+ case 5: // REFUSED
+ error = QDnsLookup::ServerRefusedError;
+ errorString = QDnsLookup::tr("Server refused to answer");
+ return;
+ default:
+ error = QDnsLookup::InvalidReplyError;
+ errorString = QDnsLookup::tr("Invalid reply received (rcode %1)")
+ .arg(rcode);
+ return;
+ }
+ }
+
+ void makeInvalidReplyError(QString &&msg = QString())
+ {
+ if (msg.isEmpty())
+ msg = QDnsLookup::tr("Invalid reply received");
+ else
+ msg = QDnsLookup::tr("Invalid reply received (%1)").arg(std::move(msg));
+ *this = QDnsLookupReply(); // empty our lists
+ setError(QDnsLookup::InvalidReplyError, std::move(msg));
+ }
+
+private:
+ bool allAreEmpty() const
+ {
+ return canonicalNameRecords.isEmpty()
+ && hostAddressRecords.isEmpty()
+ && mailExchangeRecords.isEmpty()
+ && nameServerRecords.isEmpty()
+ && pointerRecords.isEmpty()
+ && serviceRecords.isEmpty()
+ && tlsAssociationRecords.isEmpty()
+ && textRecords.isEmpty();
+ }
};
class QDnsLookupPrivate : public QObjectPrivate
{
public:
QDnsLookupPrivate()
- : isFinished(false)
- , type(QDnsLookup::A)
- , runnable(nullptr)
+ : type(QDnsLookup::A)
+ , port(0)
+ , protocol(QDnsLookup::Standard)
{ }
- void _q_lookupFinished(const QDnsLookupReply &reply);
-
- static const char *msgNoIpV6NameServerAdresses;
-
- bool isFinished;
-
void nameChanged()
{
emit q_func()->nameChanged(name);
@@ -75,6 +152,13 @@ public:
Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QString, name,
&QDnsLookupPrivate::nameChanged);
+ void nameserverChanged()
+ {
+ emit q_func()->nameserverChanged(nameserver);
+ }
+ Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QHostAddress, nameserver,
+ &QDnsLookupPrivate::nameserverChanged);
+
void typeChanged()
{
emit q_func()->typeChanged(type);
@@ -83,15 +167,29 @@ public:
Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QDnsLookup::Type,
type, &QDnsLookupPrivate::typeChanged);
- void nameserverChanged()
+ void nameserverPortChanged()
{
- emit q_func()->nameserverChanged(nameserver);
+ emit q_func()->nameserverPortChanged(port);
}
- Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QHostAddress, nameserver,
- &QDnsLookupPrivate::nameserverChanged);
+
+ Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, quint16,
+ port, &QDnsLookupPrivate::nameserverPortChanged);
+
+ void nameserverProtocolChanged()
+ {
+ emit q_func()->nameserverProtocolChanged(protocol);
+ }
+
+ Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QDnsLookup::Protocol,
+ protocol, &QDnsLookupPrivate::nameserverProtocolChanged);
QDnsLookupReply reply;
- QDnsLookupRunnable *runnable;
+ QDnsLookupRunnable *runnable = nullptr;
+ bool isFinished = false;
+
+#if QT_CONFIG(ssl)
+ std::optional<QSslConfiguration> sslConfiguration;
+#endif
Q_DECLARE_PUBLIC(QDnsLookup)
};
@@ -101,40 +199,40 @@ class QDnsLookupRunnable : public QObject, public QRunnable
Q_OBJECT
public:
- QDnsLookupRunnable(QDnsLookup::Type type, const QByteArray &name, const QHostAddress &nameserver)
- : requestType(type)
- , requestName(name)
- , nameserver(nameserver)
- { }
+#ifdef Q_OS_WIN
+ using EncodedLabel = QString;
+#else
+ using EncodedLabel = QByteArray;
+#endif
+ // minimum IPv6 MTU (1280) minus the IPv6 (40) and UDP headers (8)
+ static constexpr qsizetype ReplyBufferSize = 1280 - 40 - 8;
+ using ReplyBuffer = QVarLengthArray<unsigned char, ReplyBufferSize>;
+
+ QDnsLookupRunnable(const QDnsLookupPrivate *d);
void run() override;
+ bool sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query, ReplyBuffer &response);
signals:
void finished(const QDnsLookupReply &reply);
private:
- static void query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply);
- QDnsLookup::Type requestType;
- QByteArray requestName;
- QHostAddress nameserver;
-};
-
-#if QT_CONFIG(thread)
-class QDnsLookupThreadPool : public QThreadPool
-{
- Q_OBJECT
-
-public:
- QDnsLookupThreadPool();
- void start(QRunnable *runnable);
+ template <typename T> static QString decodeLabel(T encodedLabel)
+ {
+ return qt_ACE_do(encodedLabel.toString(), NormalizeAce, ForbidLeadingDot);
+ }
+ void query(QDnsLookupReply *reply);
-private slots:
- void _q_applicationDestroyed();
+ EncodedLabel requestName;
+ QHostAddress nameserver;
+ QDnsLookup::Type requestType;
+ quint16 port;
+ QDnsLookup::Protocol protocol;
-private:
- QMutex signalsMutex;
- bool signalsConnected;
+#if QT_CONFIG(ssl)
+ std::optional<QSslConfiguration> sslConfiguration;
+#endif
+ friend QDebug operator<<(QDebug &, QDnsLookupRunnable *);
};
-#endif // QT_CONFIG(thread)
class QDnsRecordPrivate : public QSharedData
{
@@ -200,8 +298,15 @@ public:
QList<QByteArray> values;
};
-QT_END_NAMESPACE
+class QDnsTlsAssociationRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsTlsAssociationRecord::CertificateUsage usage;
+ QDnsTlsAssociationRecord::Selector selector;
+ QDnsTlsAssociationRecord::MatchingType matchType;
+ QByteArray value;
+};
-QT_DECL_METATYPE_EXTERN(QDnsLookupReply, Q_NETWORK_EXPORT)
+QT_END_NAMESPACE
#endif // QDNSLOOKUP_P_H
diff --git a/src/network/kernel/qdnslookup_unix.cpp b/src/network/kernel/qdnslookup_unix.cpp
index 75f7c6c440..9de073b781 100644
--- a/src/network/kernel/qdnslookup_unix.cpp
+++ b/src/network/kernel/qdnslookup_unix.cpp
@@ -1,387 +1,448 @@
// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdnslookup_p.h"
-#if QT_CONFIG(library)
-#include <qlibrary.h>
-#endif
-#include <qvarlengtharray.h>
+#include <qendian.h>
#include <qscopedpointer.h>
+#include <qspan.h>
#include <qurl.h>
-#include <private/qnativesocketengine_p.h>
+#include <qvarlengtharray.h>
+#include <private/qnativesocketengine_p.h> // for setSockAddr
+#include <private/qtnetwork-config_p.h>
+
+QT_REQUIRE_CONFIG(libresolv);
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
-#if !defined(Q_OS_OPENBSD)
+#if __has_include(<arpa/nameser_compat.h>)
# include <arpa/nameser_compat.h>
#endif
+#include <errno.h>
#include <resolv.h>
-#if defined(__GNU_LIBRARY__) && !defined(__UCLIBC__)
-# include <gnu/lib-names.h>
-#endif
+#include <array>
-#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)
-# include <dlfcn.h>
+#ifndef T_OPT
+// the older arpa/nameser_compat.h wasn't updated between 1999 and 2016 in glibc
+# define T_OPT ns_t_opt
#endif
-#include <cstring>
-
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+using ReplyBuffer = QDnsLookupRunnable::ReplyBuffer;
+
+// https://www.rfc-editor.org/rfc/rfc6891
+static constexpr unsigned char Edns0Record[] = {
+ 0x00, // root label
+ T_OPT >> 8, T_OPT & 0xff, // type OPT
+ ReplyBuffer::PreallocatedSize >> 8, ReplyBuffer::PreallocatedSize & 0xff, // payload size
+ NOERROR, // extended rcode
+ 0, // version
+ 0x00, 0x00, // flags
+ 0x00, 0x00, // option length
+};
-#if QT_CONFIG(library)
+// maximum length of a EDNS0 query with a 255-character domain (rounded up to 16)
+static constexpr qsizetype QueryBufferSize =
+ HFIXEDSZ + QFIXEDSZ + MAXCDNAME + 1 + sizeof(Edns0Record);
+using QueryBuffer = std::array<unsigned char, (QueryBufferSize + 15) / 16 * 16>;
-#if defined(Q_OS_OPENBSD)
-typedef struct __res_state* res_state;
-#endif
-typedef int (*dn_expand_proto)(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
-static dn_expand_proto local_dn_expand = nullptr;
-typedef void (*res_nclose_proto)(res_state);
-static res_nclose_proto local_res_nclose = nullptr;
-typedef int (*res_ninit_proto)(res_state);
-static res_ninit_proto local_res_ninit = nullptr;
-typedef int (*res_nquery_proto)(res_state, const char *, int, int, unsigned char *, int);
-static res_nquery_proto local_res_nquery = nullptr;
-
-// Custom deleter to close resolver state.
-
-struct QDnsLookupStateDeleter
+namespace {
+struct QDnsCachedName
{
- static inline void cleanup(struct __res_state *pointer)
- {
- local_res_nclose(pointer);
- }
+ QString name;
+ int code = 0;
+ QDnsCachedName(const QString &name, int code) : name(name), code(code) {}
};
+}
+Q_DECLARE_TYPEINFO(QDnsCachedName, Q_RELOCATABLE_TYPE);
+using Cache = QList<QDnsCachedName>; // QHash or QMap are overkill
-static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym)
+#if QT_CONFIG(res_setservers)
+// https://www.ibm.com/docs/en/i/7.3?topic=ssw_ibm_i_73/apis/ressetservers.html
+// https://docs.oracle.com/cd/E86824_01/html/E54774/res-setservers-3resolv.html
+static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
{
- if (lib.isLoaded())
- return lib.resolve(sym);
-
-#if defined(RTLD_DEFAULT) && (defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen))
- return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_DEFAULT, sym));
+ union res_sockaddr_union u;
+ setSockaddr(reinterpret_cast<sockaddr *>(&u.sin), nameserver, port);
+ res_setservers(state, &u, 1);
+ return true;
+}
#else
- return nullptr;
-#endif
+template <typename T> void setNsMap(T &ext, std::enable_if_t<sizeof(T::nsmap) != 0, uint16_t> v)
+{
+ // Set nsmap[] to indicate that nsaddrs[0] is an IPv6 address
+ // See: https://sourceware.org/ml/libc-hacker/2002-05/msg00035.html
+ // Unneeded since glibc 2.22 (2015), but doesn't hurt to set it
+ // See: https://sourceware.org/git/?p=glibc.git;a=commit;h=2212c1420c92a33b0e0bd9a34938c9814a56c0f7
+ ext.nsmap[0] = v;
}
-
-static bool resolveLibraryInternal()
+template <typename T> void setNsMap(T &, ...)
{
- QLibrary lib;
-#ifdef LIBRESOLV_SO
- lib.setFileName(QStringLiteral(LIBRESOLV_SO));
- if (!lib.load())
-#endif
- {
- lib.setFileName("resolv"_L1);
- lib.load();
- }
+ // fallback
+}
- local_dn_expand = dn_expand_proto(resolveSymbol(lib, "__dn_expand"));
- if (!local_dn_expand)
- local_dn_expand = dn_expand_proto(resolveSymbol(lib, "dn_expand"));
+template <bool Condition>
+using EnableIfIPv6 = std::enable_if_t<Condition, const QHostAddress *>;
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "__res_nclose"));
- if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_9_nclose"));
- if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_nclose"));
+template <typename State>
+bool setIpv6NameServer(State *state,
+ EnableIfIPv6<sizeof(std::declval<State>()._u._ext.nsaddrs) != 0> addr,
+ quint16 port)
+{
+ // glibc-like API to set IPv6 name servers
+ struct sockaddr_in6 *ns = state->_u._ext.nsaddrs[0];
+
+ // nsaddrs will be NULL if no nameserver is set in /etc/resolv.conf
+ if (!ns) {
+ // Memory allocated here will be free()'d in res_close() as we
+ // have done res_init() above.
+ ns = static_cast<struct sockaddr_in6*>(calloc(1, sizeof(struct sockaddr_in6)));
+ Q_CHECK_PTR(ns);
+ state->_u._ext.nsaddrs[0] = ns;
+ }
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "__res_ninit"));
- if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_9_ninit"));
- if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_ninit"));
+ setNsMap(state->_u._ext, MAXNS + 1);
+ state->_u._ext.nscount6 = 1;
+ setSockaddr(ns, *addr, port);
+ return true;
+}
- local_res_nquery = res_nquery_proto(resolveSymbol(lib, "__res_nquery"));
- if (!local_res_nquery)
- local_res_nquery = res_nquery_proto(resolveSymbol(lib, "res_9_nquery"));
- if (!local_res_nquery)
- local_res_nquery = res_nquery_proto(resolveSymbol(lib, "res_nquery"));
+template <typename State> bool setIpv6NameServer(State *, const void *, quint16)
+{
+ // fallback
+ return false;
+}
+static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
+{
+ state->nscount = 1;
+ state->nsaddr_list[0].sin_family = AF_UNSPEC;
+ if (nameserver.protocol() == QAbstractSocket::IPv6Protocol)
+ return setIpv6NameServer(state, &nameserver, port);
+ setSockaddr(&state->nsaddr_list[0], nameserver, port);
return true;
}
-Q_GLOBAL_STATIC_WITH_ARGS(bool, resolveLibrary, (resolveLibraryInternal()))
+#endif // !QT_CONFIG(res_setservers)
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
+static int
+prepareQueryBuffer(res_state state, QueryBuffer &buffer, const char *label, ns_rcode type)
{
- // Load dn_expand, res_ninit and res_nquery on demand.
- resolveLibrary();
+ // Create header and our query
+ int queryLength = res_nmkquery(state, QUERY, label, C_IN, type, nullptr, 0, nullptr,
+ buffer.data(), buffer.size());
+ Q_ASSERT(queryLength < int(buffer.size()));
+ if (Q_UNLIKELY(queryLength < 0))
+ return queryLength;
+
+ // Append EDNS0 record and set the number of additional RRs to 1
+ Q_ASSERT(queryLength + sizeof(Edns0Record) < buffer.size());
+ std::copy_n(std::begin(Edns0Record), sizeof(Edns0Record), buffer.begin() + queryLength);
+ reinterpret_cast<HEADER *>(buffer.data())->arcount = qToBigEndian<quint16>(1);
+
+ return queryLength + sizeof(Edns0Record);
+}
- // If dn_expand, res_ninit or res_nquery is missing, fail.
- if (!local_dn_expand || !local_res_nclose || !local_res_ninit || !local_res_nquery) {
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Resolver functions not found");
- return;
+static int sendStandardDns(QDnsLookupReply *reply, res_state state, QSpan<unsigned char> qbuffer,
+ ReplyBuffer &buffer, const QHostAddress &nameserver, quint16 port)
+{
+ // Check if a nameserver was set. If so, use it.
+ if (!nameserver.isNull()) {
+ if (!applyNameServer(state, nameserver, port)) {
+ reply->setError(QDnsLookup::ResolverError,
+ QDnsLookup::tr("IPv6 nameservers are currently not supported on this OS"));
+ return -1;
+ }
+
+ // Request the name server attempt to authenticate the reply.
+ reinterpret_cast<HEADER *>(buffer.data())->ad = true;
+
+#ifdef RES_TRUSTAD
+ // Need to set this option even though we set the AD bit, otherwise
+ // glibc turns it off.
+ state->options |= RES_TRUSTAD;
+#endif
}
- // Initialize state.
- struct __res_state state;
- std::memset(&state, 0, sizeof(state));
- if (local_res_ninit(&state) < 0) {
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Resolver initialization failed");
- return;
+ auto attemptToSend = [&]() {
+ std::memset(buffer.data(), 0, HFIXEDSZ); // the header is enough
+ int responseLength = res_nsend(state, qbuffer.data(), qbuffer.size(), buffer.data(), buffer.size());
+ if (responseLength >= 0)
+ return responseLength; // success
+
+ // libresolv uses ETIMEDOUT for resolver errors ("no answer")
+ if (errno == ECONNREFUSED)
+ reply->setError(QDnsLookup::ServerRefusedError, qt_error_string());
+ else if (errno != ETIMEDOUT)
+ reply->makeResolverSystemError(); // some other error
+
+ auto query = reinterpret_cast<HEADER *>(qbuffer.data());
+ auto header = reinterpret_cast<HEADER *>(buffer.data());
+ if (query->id == header->id && header->qr)
+ reply->makeDnsRcodeError(header->rcode);
+ else
+ reply->makeTimeoutError(); // must really be a timeout
+ return -1;
+ };
+
+ // strictly use UDP, we'll deal with truncated replies ourselves
+ state->options |= RES_IGNTC;
+ int responseLength = attemptToSend();
+ if (responseLength < 0)
+ return responseLength;
+
+ // check if we need to use the virtual circuit (TCP)
+ auto header = reinterpret_cast<HEADER *>(buffer.data());
+ if (header->rcode == NOERROR && header->tc) {
+ // yes, increase our buffer size
+ buffer.resize(std::numeric_limits<quint16>::max());
+ header = reinterpret_cast<HEADER *>(buffer.data());
+
+ // remove the EDNS record in the query
+ reinterpret_cast<HEADER *>(qbuffer.data())->arcount = 0;
+ qbuffer = qbuffer.first(qbuffer.size() - sizeof(Edns0Record));
+
+ // send using the virtual circuit
+ state->options |= RES_USEVC;
+ responseLength = attemptToSend();
+ if (Q_UNLIKELY(responseLength > buffer.size())) {
+ // Ok, we give up.
+ reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr("Reply was too large"));
+ return -1;
+ }
}
- //Check if a nameserver was set. If so, use it
- if (!nameserver.isNull()) {
- if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) {
- state.nsaddr_list[0].sin_addr.s_addr = htonl(nameserver.toIPv4Address());
- state.nscount = 1;
- } else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) {
-#if defined(Q_OS_LINUX)
- struct sockaddr_in6 *ns;
- ns = state._u._ext.nsaddrs[0];
- // nsaddrs will be NULL if no nameserver is set in /etc/resolv.conf
- if (!ns) {
- // Memory allocated here will be free'd in res_close() as we
- // have done res_init() above.
- ns = (struct sockaddr_in6*) calloc(1, sizeof(struct sockaddr_in6));
- Q_CHECK_PTR(ns);
- state._u._ext.nsaddrs[0] = ns;
- }
-#ifndef __UCLIBC__
- // Set nsmap[] to indicate that nsaddrs[0] is an IPv6 address
- // See: https://sourceware.org/ml/libc-hacker/2002-05/msg00035.html
- state._u._ext.nsmap[0] = MAXNS + 1;
-#endif
- state._u._ext.nscount6 = 1;
- ns->sin6_family = AF_INET6;
- ns->sin6_port = htons(53);
- SetSALen::set(ns, sizeof(*ns));
-
- Q_IPV6ADDR ipv6Address = nameserver.toIPv6Address();
- for (int i=0; i<16; i++) {
- ns->sin6_addr.s6_addr[i] = ipv6Address[i];
- }
-#else
- qWarning("%s", QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr(QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- return;
+ // We only trust the AD bit in the reply if we're querying a custom name
+ // server or if we can tell the system administrator configured the resolver
+ // to trust replies.
+#ifndef RES_TRUSTAD
+ if (nameserver.isNull())
+ header->ad = false;
#endif
- }
+ reply->authenticData = header->ad;
+
+ return responseLength;
+}
+
+void QDnsLookupRunnable::query(QDnsLookupReply *reply)
+{
+ // Initialize state.
+ std::remove_pointer_t<res_state> state = {};
+ if (res_ninit(&state) < 0) {
+ int error = errno;
+ qErrnoWarning(error, "QDnsLookup: Resolver initialization failed");
+ return reply->makeResolverSystemError(error);
}
+ auto guard = qScopeGuard([&] { res_nclose(&state); });
+
#ifdef QDNSLOOKUP_DEBUG
state.options |= RES_DEBUG;
#endif
- QScopedPointer<struct __res_state, QDnsLookupStateDeleter> state_ptr(&state);
+
+ // Prepare the DNS query.
+ QueryBuffer qbuffer;
+ int queryLength = prepareQueryBuffer(&state, qbuffer, requestName.constData(), ns_rcode(requestType));
+ if (Q_UNLIKELY(queryLength < 0))
+ return reply->makeResolverSystemError();
// Perform DNS query.
- QVarLengthArray<unsigned char, PACKETSZ> buffer(PACKETSZ);
- std::memset(buffer.data(), 0, buffer.size());
- int responseLength = local_res_nquery(&state, requestName, C_IN, requestType, buffer.data(), buffer.size());
- if (Q_UNLIKELY(responseLength > PACKETSZ)) {
- buffer.resize(responseLength);
- std::memset(buffer.data(), 0, buffer.size());
- responseLength = local_res_nquery(&state, requestName, C_IN, requestType, buffer.data(), buffer.size());
- if (Q_UNLIKELY(responseLength > buffer.size())) {
- // Ok, we give up.
- reply->error = QDnsLookup::ResolverError;
- reply->errorString.clear(); // We cannot be more specific, alas.
+ ReplyBuffer buffer(ReplyBufferSize);
+ int responseLength = -1;
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ responseLength = sendStandardDns(reply, &state, qbuffer, buffer, nameserver, port);
+ break;
+ case QDnsLookup::DnsOverTls:
+ if (!sendDnsOverTls(reply, qbuffer, buffer))
return;
- }
+ responseLength = buffer.size();
+ break;
}
- unsigned char *response = buffer.data();
- // Check the response header. Though res_nquery returns -1 as a
- // responseLength in case of error, we still can extract the
- // exact error code from the response.
- HEADER *header = (HEADER*)response;
- const int answerCount = ntohs(header->ancount);
- switch (header->rcode) {
- case NOERROR:
- break;
- case FORMERR:
- reply->error = QDnsLookup::InvalidRequestError;
- reply->errorString = tr("Server could not process query");
+ if (responseLength < 0)
return;
- case SERVFAIL:
- reply->error = QDnsLookup::ServerFailureError;
- reply->errorString = tr("Server failure");
- return;
- case NXDOMAIN:
- reply->error = QDnsLookup::NotFoundError;
- reply->errorString = tr("Non existent domain");
- return;
- case REFUSED:
- reply->error = QDnsLookup::ServerRefusedError;
- reply->errorString = tr("Server refused to answer");
- return;
- default:
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid reply received");
- return;
- }
// Check the reply is valid.
- if (responseLength < int(sizeof(HEADER))) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid reply received");
- return;
- }
+ if (responseLength < int(sizeof(HEADER)))
+ return reply->makeInvalidReplyError();
- // Skip the query host, type (2 bytes) and class (2 bytes).
- char host[PACKETSZ], answer[PACKETSZ];
- unsigned char *p = response + sizeof(HEADER);
- int status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Could not expand domain name");
- return;
+ // Parse the reply.
+ auto header = reinterpret_cast<HEADER *>(buffer.data());
+ if (header->rcode)
+ return reply->makeDnsRcodeError(header->rcode);
+
+ qptrdiff offset = sizeof(HEADER);
+ unsigned char *response = buffer.data();
+ int status;
+
+ auto expandHost = [&, cache = Cache{}](qptrdiff offset) mutable {
+ if (uchar n = response[offset]; n & NS_CMPRSFLGS) {
+ // compressed name, see if we already have it cached
+ if (offset + 1 < responseLength) {
+ int id = ((n & ~NS_CMPRSFLGS) << 8) | response[offset + 1];
+ auto it = std::find_if(cache.constBegin(), cache.constEnd(),
+ [id](const QDnsCachedName &n) { return n.code == id; });
+ if (it != cache.constEnd()) {
+ status = 2;
+ return it->name;
+ }
+ }
+ }
+
+ // uncached, expand it
+ char host[MAXCDNAME + 1];
+ status = dn_expand(response, response + responseLength, response + offset,
+ host, sizeof(host));
+ if (status >= 0)
+ return cache.emplaceBack(decodeLabel(QLatin1StringView(host)), offset).name;
+
+ // failed
+ reply->makeInvalidReplyError(QDnsLookup::tr("Could not expand domain name"));
+ return QString();
+ };
+
+ if (ntohs(header->qdcount) == 1) {
+ // Skip the query host, type (2 bytes) and class (2 bytes).
+ expandHost(offset);
+ if (status < 0)
+ return;
+ if (offset + status + 4 > responseLength)
+ header->qdcount = 0xffff; // invalid reply below
+ else
+ offset += status + 4;
}
- p += status + 4;
+ if (ntohs(header->qdcount) > 1)
+ return reply->makeInvalidReplyError();
// Extract results.
+ const int answerCount = ntohs(header->ancount);
int answerIndex = 0;
- while ((p < response + responseLength) && (answerIndex < answerCount)) {
- status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Could not expand domain name");
+ while ((offset < responseLength) && (answerIndex < answerCount)) {
+ const QString name = expandHost(offset);
+ if (status < 0)
return;
- }
- const QString name = QUrl::fromAce(host);
- p += status;
- const quint16 type = (p[0] << 8) | p[1];
- p += 2; // RR type
- p += 2; // RR class
- const quint32 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
- p += 4;
- const quint16 size = (p[0] << 8) | p[1];
- p += 2;
+ offset += status;
+ if (offset + RRFIXEDSZ > responseLength) {
+ // probably just a truncated reply, return what we have
+ return;
+ }
+ const quint16 type = qFromBigEndian<quint16>(response + offset);
+ const qint16 rrclass = qFromBigEndian<quint16>(response + offset + 2);
+ const quint32 ttl = qFromBigEndian<quint32>(response + offset + 4);
+ const quint16 size = qFromBigEndian<quint16>(response + offset + 8);
+ offset += RRFIXEDSZ;
+ if (offset + size > responseLength)
+ return; // truncated
+ if (rrclass != C_IN)
+ continue;
if (type == QDnsLookup::A) {
- if (size != 4) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid IPv4 address record");
- return;
- }
- const quint32 addr = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ if (size != 4)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid IPv4 address record"));
+ const quint32 addr = qFromBigEndian<quint32>(response + offset);
QDnsHostAddressRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
record.d->value = QHostAddress(addr);
reply->hostAddressRecords.append(record);
} else if (type == QDnsLookup::AAAA) {
- if (size != 16) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid IPv6 address record");
- return;
- }
+ if (size != 16)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid IPv6 address record"));
QDnsHostAddressRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QHostAddress(p);
+ record.d->value = QHostAddress(response + offset);
reply->hostAddressRecords.append(record);
} else if (type == QDnsLookup::CNAME) {
- status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid canonical name record");
- return;
- }
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QUrl::fromAce(answer);
+ record.d->value = expandHost(offset);
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid canonical name record"));
reply->canonicalNameRecords.append(record);
} else if (type == QDnsLookup::NS) {
- status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid name server record");
- return;
- }
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QUrl::fromAce(answer);
+ record.d->value = expandHost(offset);
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid name server record"));
reply->nameServerRecords.append(record);
} else if (type == QDnsLookup::PTR) {
- status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid pointer record");
- return;
- }
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QUrl::fromAce(answer);
+ record.d->value = expandHost(offset);
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid pointer record"));
reply->pointerRecords.append(record);
} else if (type == QDnsLookup::MX) {
- const quint16 preference = (p[0] << 8) | p[1];
- status = local_dn_expand(response, response + responseLength, p + 2, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid mail exchange record");
- return;
- }
+ const quint16 preference = qFromBigEndian<quint16>(response + offset);
QDnsMailExchangeRecord record;
- record.d->exchange = QUrl::fromAce(answer);
+ record.d->exchange = expandHost(offset + 2);
record.d->name = name;
record.d->preference = preference;
record.d->timeToLive = ttl;
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid mail exchange record"));
reply->mailExchangeRecords.append(record);
} else if (type == QDnsLookup::SRV) {
- const quint16 priority = (p[0] << 8) | p[1];
- const quint16 weight = (p[2] << 8) | p[3];
- const quint16 port = (p[4] << 8) | p[5];
- status = local_dn_expand(response, response + responseLength, p + 6, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid service record");
- return;
- }
+ if (size < 7)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
+ const quint16 priority = qFromBigEndian<quint16>(response + offset);
+ const quint16 weight = qFromBigEndian<quint16>(response + offset + 2);
+ const quint16 port = qFromBigEndian<quint16>(response + offset + 4);
QDnsServiceRecord record;
record.d->name = name;
- record.d->target = QUrl::fromAce(answer);
+ record.d->target = expandHost(offset + 6);
record.d->port = port;
record.d->priority = priority;
record.d->timeToLive = ttl;
record.d->weight = weight;
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
reply->serviceRecords.append(record);
+ } else if (type == QDnsLookup::TLSA) {
+ // https://datatracker.ietf.org/doc/html/rfc6698#section-2.1
+ if (size < 3)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid TLS association record"));
+
+ const quint8 usage = response[offset];
+ const quint8 selector = response[offset + 1];
+ const quint8 matchType = response[offset + 2];
+
+ QDnsTlsAssociationRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->usage = QDnsTlsAssociationRecord::CertificateUsage(usage);
+ record.d->selector = QDnsTlsAssociationRecord::Selector(selector);
+ record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType);
+ record.d->value.assign(response + offset + 3, response + offset + size);
+ reply->tlsAssociationRecords.append(std::move(record));
} else if (type == QDnsLookup::TXT) {
- unsigned char *txt = p;
QDnsTextRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- while (txt < p + size) {
- const unsigned char length = *txt;
+ qptrdiff txt = offset;
+ while (txt < offset + size) {
+ const unsigned char length = response[txt];
txt++;
- if (txt + length > p + size) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid text record");
- return;
- }
- record.d->values << QByteArray((char*)txt, length);
+ if (txt + length > offset + size)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid text record"));
+ record.d->values << QByteArrayView(response + txt, length).toByteArray();
txt += length;
}
reply->textRecords.append(record);
}
- p += size;
+ offset += size;
answerIndex++;
}
}
-#else
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
-{
- Q_UNUSED(requestType);
- Q_UNUSED(requestName);
- Q_UNUSED(nameserver);
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Resolver library can't be loaded: No runtime library loading support");
- return;
-}
-
-#endif /* QT_CONFIG(library) */
-
QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp
index 564966e395..1b07776db9 100644
--- a/src/network/kernel/qdnslookup_win.cpp
+++ b/src/network/kernel/qdnslookup_win.cpp
@@ -1,69 +1,180 @@
// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <winsock2.h>
#include "qdnslookup_p.h"
-#include <qurl.h>
+#include <qendian.h>
+#include <private/qnativesocketengine_p.h>
#include <private/qsystemerror_p.h>
+#include <qurl.h>
+#include <qspan.h>
#include <qt_windows.h>
#include <windns.h>
#include <memory.h>
+#ifndef DNS_ADDR_MAX_SOCKADDR_LENGTH
+// MinGW headers are missing almost all of this
+typedef struct Qt_DnsAddr {
+ CHAR MaxSa[32];
+ DWORD DnsAddrUserDword[8];
+} DNS_ADDR, *PDNS_ADDR;
+typedef struct Qt_DnsAddrArray {
+ DWORD MaxCount;
+ DWORD AddrCount;
+ DWORD Tag;
+ WORD Family;
+ WORD WordReserved;
+ DWORD Flags;
+ DWORD MatchFlag;
+ DWORD Reserved1;
+ DWORD Reserved2;
+ DNS_ADDR AddrArray[];
+} DNS_ADDR_ARRAY, *PDNS_ADDR_ARRAY;
+# ifndef DNS_QUERY_RESULTS_VERSION1
+typedef struct Qt_DNS_QUERY_RESULT {
+ ULONG Version;
+ DNS_STATUS QueryStatus;
+ ULONG64 QueryOptions;
+ PDNS_RECORD pQueryRecords;
+ PVOID Reserved;
+} DNS_QUERY_RESULT, *PDNS_QUERY_RESULT;
+typedef VOID WINAPI DNS_QUERY_COMPLETION_ROUTINE(PVOID pQueryContext,PDNS_QUERY_RESULT pQueryResults);
+typedef DNS_QUERY_COMPLETION_ROUTINE *PDNS_QUERY_COMPLETION_ROUTINE;
+# endif
+typedef struct Qt_DNS_QUERY_REQUEST {
+ ULONG Version;
+ PCWSTR QueryName;
+ WORD QueryType;
+ ULONG64 QueryOptions;
+ PDNS_ADDR_ARRAY pDnsServerList;
+ ULONG InterfaceIndex;
+ PDNS_QUERY_COMPLETION_ROUTINE pQueryCompletionCallback;
+ PVOID pQueryContext;
+} DNS_QUERY_REQUEST, *PDNS_QUERY_REQUEST;
+
+typedef void *PDNS_QUERY_CANCEL; // not really, but we don't need it
+extern "C" {
+DNS_STATUS WINAPI DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest,
+ PDNS_QUERY_RESULT pQueryResults,
+ PDNS_QUERY_CANCEL pCancelHandle);
+}
+#endif
+
QT_BEGIN_NAMESPACE
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
+static DNS_STATUS sendAlternate(QDnsLookupRunnable *self, QDnsLookupReply *reply,
+ PDNS_QUERY_REQUEST request, PDNS_QUERY_RESULT results)
{
- // Perform DNS query.
- PDNS_RECORD dns_records = 0;
- const QString requestNameUtf16 = QString::fromUtf8(requestName.data(), requestName.size());
- IP4_ARRAY srvList;
- memset(&srvList, 0, sizeof(IP4_ARRAY));
- if (!nameserver.isNull()) {
- if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) {
- // The below code is referenced from: http://support.microsoft.com/kb/831226
- srvList.AddrCount = 1;
- srvList.AddrArray[0] = htonl(nameserver.toIPv4Address());
- } else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) {
- // For supoprting IPv6 nameserver addresses, we'll need to switch
- // from DnsQuey() to DnsQueryEx() as it supports passing an IPv6
- // address in the nameserver list
- qWarning("%s", QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr(QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- return;
+ // WinDNS wants MTU - IP Header - UDP header for some reason, in spite
+ // of never needing that much
+ QVarLengthArray<unsigned char, 1472> query(1472);
+
+ auto dnsBuffer = new (query.data()) DNS_MESSAGE_BUFFER;
+ DWORD dnsBufferSize = query.size();
+ WORD xid = 0;
+ bool recursionDesired = true;
+
+ SetLastError(ERROR_SUCCESS);
+
+ // MinGW winheaders incorrectly declare the third parameter as LPWSTR
+ if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
+ const_cast<LPWSTR>(request->QueryName), request->QueryType,
+ xid, recursionDesired)) {
+ // let's try reallocating
+ query.resize(dnsBufferSize);
+ if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
+ const_cast<LPWSTR>(request->QueryName), request->QueryType,
+ xid, recursionDesired)) {
+ return GetLastError();
}
}
- const DNS_STATUS status = DnsQuery_W(reinterpret_cast<const wchar_t*>(requestNameUtf16.utf16()), requestType, DNS_QUERY_STANDARD, &srvList, &dns_records, NULL);
- switch (status) {
- case ERROR_SUCCESS:
+
+ // set AD bit: we want to trust this server
+ dnsBuffer->MessageHead.AuthenticatedData = true;
+
+ QDnsLookupRunnable::ReplyBuffer replyBuffer;
+ if (!self->sendDnsOverTls(reply, { query.data(), qsizetype(dnsBufferSize) }, replyBuffer))
+ return DNS_STATUS(-1); // error set in reply
+
+ // interpret the RCODE in the reply
+ auto response = reinterpret_cast<PDNS_MESSAGE_BUFFER>(replyBuffer.data());
+ DNS_HEADER *header = &response->MessageHead;
+ if (!header->IsResponse)
+ return DNS_ERROR_BAD_PACKET; // not a reply
+
+ // Convert the byte order for the 16-bit quantities in the header, so
+ // DnsExtractRecordsFromMessage can parse the contents.
+ //header->Xid = qFromBigEndian(header->Xid);
+ header->QuestionCount = qFromBigEndian(header->QuestionCount);
+ header->AnswerCount = qFromBigEndian(header->AnswerCount);
+ header->NameServerCount = qFromBigEndian(header->NameServerCount);
+ header->AdditionalCount = qFromBigEndian(header->AdditionalCount);
+
+ results->QueryOptions = request->QueryOptions;
+ return DnsExtractRecordsFromMessage_W(response, replyBuffer.size(), &results->pQueryRecords);
+}
+
+void QDnsLookupRunnable::query(QDnsLookupReply *reply)
+{
+ // Perform DNS query.
+ alignas(DNS_ADDR_ARRAY) uchar dnsAddresses[sizeof(DNS_ADDR_ARRAY) + sizeof(DNS_ADDR)];
+ DNS_QUERY_REQUEST request = {};
+ request.Version = 1;
+ request.QueryName = reinterpret_cast<const wchar_t *>(requestName.constData());
+ request.QueryType = requestType;
+ request.QueryOptions = DNS_QUERY_STANDARD | DNS_QUERY_TREAT_AS_FQDN;
+
+ if (protocol == QDnsLookup::Standard && !nameserver.isNull()) {
+ memset(dnsAddresses, 0, sizeof(dnsAddresses));
+ request.pDnsServerList = new (dnsAddresses) DNS_ADDR_ARRAY;
+ auto addr = new (request.pDnsServerList->AddrArray) DNS_ADDR[1];
+ auto sa = new (addr[0].MaxSa) sockaddr;
+ request.pDnsServerList->MaxCount = sizeof(dnsAddresses);
+ request.pDnsServerList->AddrCount = 1;
+ // ### setting port 53 seems to cause some systems to fail
+ setSockaddr(sa, nameserver, port == DnsPort ? 0 : port);
+ request.pDnsServerList->Family = sa->sa_family;
+ }
+
+ DNS_QUERY_RESULT results = {};
+ results.Version = 1;
+ DNS_STATUS status = ERROR_INVALID_PARAMETER;
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ status = DnsQueryEx(&request, &results, nullptr);
+ break;
+ case QDnsLookup::DnsOverTls:
+ status = sendAlternate(this, reply, &request, &results);
break;
- case DNS_ERROR_RCODE_FORMAT_ERROR:
- reply->error = QDnsLookup::InvalidRequestError;
- reply->errorString = tr("Server could not process query");
- return;
- case DNS_ERROR_RCODE_SERVER_FAILURE:
- reply->error = QDnsLookup::ServerFailureError;
- reply->errorString = tr("Server failure");
- return;
- case DNS_ERROR_RCODE_NAME_ERROR:
- reply->error = QDnsLookup::NotFoundError;
- reply->errorString = tr("Non existent domain");
- return;
- case DNS_ERROR_RCODE_REFUSED:
- reply->error = QDnsLookup::ServerRefusedError;
- reply->errorString = tr("Server refused to answer");
- return;
- default:
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = QSystemError(status, QSystemError::NativeError).toString();
- return;
}
+ if (status == DNS_STATUS(-1))
+ return; // error already set in reply
+ if (status >= DNS_ERROR_RCODE_FORMAT_ERROR && status <= DNS_ERROR_RCODE_LAST)
+ return reply->makeDnsRcodeError(status - DNS_ERROR_RCODE_FORMAT_ERROR + 1);
+ else if (status == ERROR_TIMEOUT)
+ return reply->makeTimeoutError();
+ else if (status != ERROR_SUCCESS)
+ return reply->makeResolverSystemError(status);
+
+ QStringView lastEncodedName;
+ QString cachedDecodedName;
+ auto extractAndCacheHost = [&](QStringView name) -> const QString & {
+ lastEncodedName = name;
+ cachedDecodedName = decodeLabel(name);
+ return cachedDecodedName;
+ };
+ auto extractMaybeCachedHost = [&](QStringView name) -> const QString & {
+ return lastEncodedName == name ? cachedDecodedName : extractAndCacheHost(name);
+ };
+
// Extract results.
- for (PDNS_RECORD ptr = dns_records; ptr != NULL; ptr = ptr->pNext) {
- const QString name = QUrl::fromAce( QString::fromWCharArray( ptr->pName ).toLatin1() );
+ for (PDNS_RECORD ptr = results.pQueryRecords; ptr != NULL; ptr = ptr->pNext) {
+ // warning: always assign name to the record before calling extractXxxHost() again
+ const QString &name = extractMaybeCachedHost(ptr->pName);
if (ptr->wType == QDnsLookup::A) {
QDnsHostAddressRecord record;
record.d->name = name;
@@ -83,12 +194,12 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
- record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Cname.pNameHost).toLatin1());
+ record.d->value = extractAndCacheHost(ptr->Data.Cname.pNameHost);
reply->canonicalNameRecords.append(record);
} else if (ptr->wType == QDnsLookup::MX) {
QDnsMailExchangeRecord record;
record.d->name = name;
- record.d->exchange = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Mx.pNameExchange).toLatin1());
+ record.d->exchange = decodeLabel(QStringView(ptr->Data.Mx.pNameExchange));
record.d->preference = ptr->Data.Mx.wPreference;
record.d->timeToLive = ptr->dwTtl;
reply->mailExchangeRecords.append(record);
@@ -96,35 +207,54 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
- record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ns.pNameHost).toLatin1());
+ record.d->value = decodeLabel(QStringView(ptr->Data.Ns.pNameHost));
reply->nameServerRecords.append(record);
} else if (ptr->wType == QDnsLookup::PTR) {
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
- record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ptr.pNameHost).toLatin1());
+ record.d->value = decodeLabel(QStringView(ptr->Data.Ptr.pNameHost));
reply->pointerRecords.append(record);
} else if (ptr->wType == QDnsLookup::SRV) {
QDnsServiceRecord record;
record.d->name = name;
- record.d->target = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Srv.pNameTarget).toLatin1());
+ record.d->target = decodeLabel(QStringView(ptr->Data.Srv.pNameTarget));
record.d->port = ptr->Data.Srv.wPort;
record.d->priority = ptr->Data.Srv.wPriority;
record.d->timeToLive = ptr->dwTtl;
record.d->weight = ptr->Data.Srv.wWeight;
reply->serviceRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::TLSA) {
+ // Note: untested, because the DNS_RECORD reply appears to contain
+ // no records relating to TLSA. Maybe WinDNS filters them out of
+ // zones without DNSSEC.
+ QDnsTlsAssociationRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ptr->dwTtl;
+
+ const auto &tlsa = ptr->Data.Tlsa;
+ const quint8 usage = tlsa.bCertUsage;
+ const quint8 selector = tlsa.bSelector;
+ const quint8 matchType = tlsa.bMatchingType;
+
+ record.d->usage = QDnsTlsAssociationRecord::CertificateUsage(usage);
+ record.d->selector = QDnsTlsAssociationRecord::Selector(selector);
+ record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType);
+ record.d->value.assign(tlsa.bCertificateAssociationData,
+ tlsa.bCertificateAssociationData + tlsa.bCertificateAssociationDataLength);
+ reply->tlsAssociationRecords.append(std::move(record));
} else if (ptr->wType == QDnsLookup::TXT) {
QDnsTextRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
for (unsigned int i = 0; i < ptr->Data.Txt.dwStringCount; ++i) {
- record.d->values << QString::fromWCharArray((ptr->Data.Txt.pStringArray[i])).toLatin1();;
+ record.d->values << QStringView(ptr->Data.Txt.pStringArray[i]).toLatin1();
}
reply->textRecords.append(record);
}
}
- DnsRecordListFree(dns_records, DnsFreeRecordList);
+ DnsRecordListFree(results.pQueryRecords, DnsFreeRecordList);
}
QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
index 2f673d8c6d..0330fb091b 100644
--- a/src/network/kernel/qhostaddress.cpp
+++ b/src/network/kernel/qhostaddress.cpp
@@ -164,8 +164,12 @@ AddressClassification QHostAddressPrivate::classify() const
return BroadcastAddress;
return UnknownAddress;
}
+ if (((a & 0xff000000U) == 0x0a000000U) // 10.0.0.0/8
+ || ((a & 0xfff00000U) == 0xac100000U) // 172.16.0.0/12
+ || ((a & 0xffff0000U) == 0xc0a80000U)) // 192.168.0.0/16
+ return PrivateNetworkAddress;
- // Not testing for PrivateNetworkAddress and TestNetworkAddress
+ // Not testing for TestNetworkAddress
// since we don't need them yet.
return GlobalAddress;
}
@@ -673,7 +677,7 @@ QHostAddress::NetworkLayerProtocol QHostAddress::protocol() const
\l{QAbstractSocket::}{IPv6Protocol}.
If the protocol is
\l{QAbstractSocket::}{IPv4Protocol},
- then the address is returned an an IPv4 mapped IPv6 address. (RFC4291)
+ then the address is returned as an IPv4 mapped IPv6 address. (RFC4291)
\sa toString()
*/
@@ -702,7 +706,7 @@ QString QHostAddress::toString() const
} else if (d->protocol == QHostAddress::IPv6Protocol) {
QIPAddressUtils::toString(s, d->a6.c);
if (!d->scopeId.isEmpty())
- s.append(u'%' + d->scopeId);
+ s += u'%' + d->scopeId;
}
return s;
}
@@ -818,7 +822,7 @@ bool QHostAddress::isEqual(const QHostAddress &other, ConversionMode mode) const
return memcmp(&d->a6, &other.d->a6, sizeof(Q_IPV6ADDR)) == 0;
case QHostAddress::AnyIPProtocol:
return (mode & QHostAddress::ConvertUnspecifiedAddress)
- && (other.d->a6_64.c[0] == 0) && (other.d->a6_64.c[1] == 0);
+ && (d->a6_64.c[0] == 0) && (d->a6_64.c[1] == 0);
case QHostAddress::UnknownNetworkLayerProtocol:
return false;
}
@@ -1113,7 +1117,7 @@ bool QHostAddress::isLoopback() const
considered as global in new applications. This function returns true for
site-local addresses too.
- \sa isLoopback(), isSiteLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isSiteLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isGlobal() const
{
@@ -1131,7 +1135,7 @@ bool QHostAddress::isGlobal() const
\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()
+ \sa isLoopback(), isGlobal(), isMulticast(), isSiteLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isLinkLocal() const
{
@@ -1154,7 +1158,7 @@ bool QHostAddress::isLinkLocal() const
isGlobal() also returns true). Site-local addresses were replaced by Unique
Local Addresses (ULA).
- \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isSiteLocal() const
{
@@ -1175,7 +1179,7 @@ bool QHostAddress::isSiteLocal() const
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()
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isUniqueLocalUnicast() const
{
@@ -1188,7 +1192,7 @@ bool QHostAddress::isUniqueLocalUnicast() const
Returns \c true if the address is an IPv4 or IPv6 multicast address, \c
false otherwise.
- \sa isLoopback(), isGlobal(), isLinkLocal(), isSiteLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isLinkLocal(), isSiteLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isMulticast() const
{
@@ -1205,13 +1209,27 @@ bool QHostAddress::isMulticast() const
broadcast address. For that, please use \l QNetworkInterface to obtain the
broadcast addresses of the local machine.
- \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isBroadcast() const
{
return d->classify() == BroadcastAddress;
}
+/*!
+ \since 6.6
+
+ Returns \c true if the address is an IPv6 unique local unicast address or
+ IPv4 address reserved for local networks by \l {RFC 1918}, \c false otherwise.
+
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isBroadcast()
+*/
+bool QHostAddress::isPrivateUse() const
+{
+ const AddressClassification classification = d->classify();
+ return (classification == PrivateNetworkAddress) || (classification == UniqueLocalAddress);
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const QHostAddress &address)
{
diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h
index 456063d247..6aa045c959 100644
--- a/src/network/kernel/qhostaddress.h
+++ b/src/network/kernel/qhostaddress.h
@@ -20,7 +20,7 @@ QT_BEGIN_NAMESPACE
class QHostAddressPrivate;
-class Q_NETWORK_EXPORT QIPv6Address
+class QT6_ONLY(Q_NETWORK_EXPORT) QIPv6Address
{
public:
inline quint8 &operator [](int index) { return c[index]; }
@@ -127,6 +127,7 @@ public:
bool isUniqueLocalUnicast() const;
bool isMulticast() const;
bool isBroadcast() const;
+ bool isPrivateUse() const;
static QPair<QHostAddress, int> parseSubnet(const QString &subnet);
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index 2283766b97..62bb210ca1 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -72,6 +72,16 @@ Q_APPLICATION_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
}
+QHostInfoResult::QHostInfoResult(const QObject *receiver, QtPrivate::SlotObjUniquePtr slot)
+ : receiver{receiver ? receiver : this}, slotObj{std::move(slot)}
+{
+ Q_ASSERT(this->receiver);
+ moveToThread(this->receiver->thread());
+}
+
+QHostInfoResult::~QHostInfoResult()
+ = default;
+
/*
The calling thread is likely the one that executes the lookup via
QHostInfoRunnable. Unless we operate with a queued connection already,
@@ -80,8 +90,8 @@ Q_APPLICATION_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
the thread that made the call to lookupHost. That QHostInfoResult object
then calls the user code in the correct thread.
- The 'result' object deletes itself (via deleteLater) when the metacall
- event is received.
+ The 'result' object deletes itself (via deleteLater) when
+ finalizePostResultsReady is called.
*/
void QHostInfoResult::postResultsReady(const QHostInfo &info)
{
@@ -91,55 +101,33 @@ void QHostInfoResult::postResultsReady(const QHostInfo &info)
return;
}
// we used to have a context object, but it's already destroyed
- if (withContextObject && !receiver)
+ if (!receiver)
return;
- static const int signal_index = []() -> int {
- auto senderMetaObject = &QHostInfoResult::staticMetaObject;
- auto signal = &QHostInfoResult::resultsReady;
- int signal_index = -1;
- void *args[] = { &signal_index, &signal };
- senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
- return signal_index + QMetaObjectPrivate::signalOffset(senderMetaObject);
- }();
-
// a long-living version of this
auto result = new QHostInfoResult(this);
Q_CHECK_PTR(result);
- const int nargs = 2;
- auto metaCallEvent = new QMetaCallEvent(slotObj, nullptr, signal_index, nargs);
- Q_CHECK_PTR(metaCallEvent);
- void **args = metaCallEvent->args();
- QMetaType *types = metaCallEvent->types();
- auto voidType = QMetaType::fromType<void>();
- auto hostInfoType = QMetaType::fromType<QHostInfo>();
- types[0] = voidType;
- types[1] = hostInfoType;
- args[0] = nullptr;
- args[1] = hostInfoType.create(&info);
- Q_CHECK_PTR(args[1]);
- qApp->postEvent(result, metaCallEvent);
+ QMetaObject::invokeMethod(result,
+ &QHostInfoResult::finalizePostResultsReady,
+ Qt::QueuedConnection,
+ info);
}
/*
- Receives the event posted by postResultsReady, and calls the functor.
+ Receives the info from postResultsReady, and calls the functor.
*/
-bool QHostInfoResult::event(QEvent *event)
+void QHostInfoResult::finalizePostResultsReady(const QHostInfo &info)
{
- if (event->type() == QEvent::MetaCall) {
- Q_ASSERT(slotObj);
- auto metaCallEvent = static_cast<QMetaCallEvent *>(event);
- auto args = metaCallEvent->args();
- // we didn't have a context object, or it's still alive
- if (!withContextObject || receiver)
- slotObj->call(const_cast<QObject*>(receiver.data()), args);
- slotObj->destroyIfLastRef();
-
- deleteLater();
- return true;
+ Q_ASSERT(slotObj);
+
+ // we used to have a context object, but it's already destroyed
+ if (receiver) {
+ void *args[] = { nullptr, const_cast<QHostInfo *>(&info) };
+ slotObj->call(const_cast<QObject *>(receiver.data()), args);
}
- return QObject::event(event);
+
+ deleteLater();
}
/*!
@@ -233,10 +221,17 @@ static int nextId()
\note There is no guarantee on the order the signals will be emitted
if you start multiple requests with lookupHost().
+ \note In Qt versions prior to 6.7, this function took \a receiver as
+ (non-const) \c{QObject*}.
+
\sa abortHostLookup(), addresses(), error(), fromName()
*/
-int QHostInfo::lookupHost(const QString &name, QObject *receiver, const char *member)
+int QHostInfo::lookupHost(const QString &name, const QObject *receiver, const char *member)
{
+ if (!receiver || !member) {
+ qWarning("QHostInfo::lookupHost: both the receiver and the member to invoke must be non-null");
+ return -1;
+ }
return QHostInfo::lookupHostImpl(name, receiver, nullptr, member);
}
@@ -262,7 +257,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, const char *me
*/
/*!
- \fn template<typename Functor> int QHostInfo::lookupHost(const QString &name, Functor functor)
+ \fn template<typename Functor> int QHostInfo::lookupHost(const QString &name, Functor &&functor)
\since 5.9
@@ -734,22 +729,25 @@ QString QHostInfo::localHostName()
\internal
Called by the various lookupHost overloads to perform the lookup.
- Signals either the functor encapuslated in the \a slotObj in the context
+ Signals either the functor encapuslated in the \a slotObjRaw in the context
of \a receiver, or the \a member slot of the \a receiver.
- \a receiver might be the nullptr, but only if a \a slotObj is provided.
+ \a receiver might be the nullptr, but only if a \a slotObjRaw is provided.
*/
int QHostInfo::lookupHostImpl(const QString &name,
const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj,
+ QtPrivate::QSlotObjectBase *slotObjRaw,
const char *member)
{
+ QtPrivate::SlotObjUniquePtr slotObj{slotObjRaw};
#if defined QHOSTINFO_DEBUG
qDebug("QHostInfo::lookupHostImpl(\"%s\", %p, %p, %s)",
- name.toLatin1().constData(), receiver, slotObj, member ? member + 1 : 0);
+ name.toLatin1().constData(), receiver, slotObj.get(), member ? member + 1 : 0);
#endif
Q_ASSERT(!member != !slotObj); // one of these must be set, but not both
Q_ASSERT(receiver || slotObj);
+ Q_ASSERT(!member || receiver); // if member is set, also is receiver
+ const bool isUsingStringBasedSlot = static_cast<bool>(member);
if (!QAbstractEventDispatcher::instance(QThread::currentThread())) {
qWarning("QHostInfo::lookupHost() called with no event dispatcher");
@@ -765,10 +763,11 @@ int QHostInfo::lookupHostImpl(const QString &name,
hostInfo.setError(QHostInfo::HostNotFound);
hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given"));
- QHostInfoResult result(receiver, slotObj);
- if (receiver && member)
+ QHostInfoResult result(receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)),
receiver, member, Qt::QueuedConnection);
+ }
result.postResultsReady(hostInfo);
return id;
@@ -782,10 +781,11 @@ int QHostInfo::lookupHostImpl(const QString &name,
QHostInfo hostInfo = QHostInfoAgent::lookup(name);
hostInfo.setLookupId(id);
- QHostInfoResult result(receiver, slotObj);
- if (receiver && member)
+ QHostInfoResult result(receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)),
receiver, member, Qt::QueuedConnection);
+ }
result.postResultsReady(hostInfo);
#else
QHostInfoLookupManager *manager = theHostInfoLookupManager();
@@ -798,20 +798,22 @@ int QHostInfo::lookupHostImpl(const QString &name,
QHostInfo info = manager->cache.get(name, &valid);
if (valid) {
info.setLookupId(id);
- QHostInfoResult result(receiver, slotObj);
- if (receiver && member)
+ QHostInfoResult result(receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)),
receiver, member, Qt::QueuedConnection);
+ }
result.postResultsReady(info);
return id;
}
}
// cache is not enabled or it was not in the cache, do normal lookup
- QHostInfoRunnable *runnable = new QHostInfoRunnable(name, id, receiver, slotObj);
- if (receiver && member)
+ QHostInfoRunnable *runnable = new QHostInfoRunnable(name, id, receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)),
receiver, member, Qt::QueuedConnection);
+ }
manager->scheduleLookup(runnable);
}
#endif // Q_OS_WASM
@@ -819,12 +821,15 @@ int QHostInfo::lookupHostImpl(const QString &name,
}
QHostInfoRunnable::QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj) :
- toBeLookedUp(hn), id(i), resultEmitter(receiver, slotObj)
+ QtPrivate::SlotObjUniquePtr slotObj)
+ : toBeLookedUp{hn}, id{i}, resultEmitter{receiver, std::move(slotObj)}
{
setAutoDelete(true);
}
+QHostInfoRunnable::~QHostInfoRunnable()
+ = default;
+
// the QHostInfoLookupManager will at some point call this via a QThreadPool
void QHostInfoRunnable::run()
{
@@ -962,7 +967,7 @@ void QHostInfoLookupManager::rescheduleWithMutexHeld()
isAlreadyRunning).second,
scheduledLookups.end());
- const int availableThreads = threadPool.maxThreadCount() - currentLookups.size();
+ const int availableThreads = std::max(threadPool.maxThreadCount(), 1) - currentLookups.size();
if (availableThreads > 0) {
int readyToStartCount = qMin(availableThreads, scheduledLookups.size());
auto it = scheduledLookups.begin();
@@ -1000,6 +1005,9 @@ void QHostInfoLookupManager::abortLookup(int id)
if (wasDeleted)
return;
+ if (id == -1)
+ return;
+
#if QT_CONFIG(thread)
// is postponed? delete and return
for (int i = 0; i < postponedLookups.size(); i++) {
diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h
index eac23ac296..3942e41498 100644
--- a/src/network/kernel/qhostinfo.h
+++ b/src/network/kernel/qhostinfo.h
@@ -17,6 +17,7 @@ class QHostInfoPrivate;
class Q_NETWORK_EXPORT QHostInfo
{
+ Q_GADGET
public:
enum HostInfoError {
NoError,
@@ -48,68 +49,42 @@ public:
void setLookupId(int id);
int lookupId() const;
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
static int lookupHost(const QString &name, QObject *receiver, const char *member);
+#endif
+ static int lookupHost(const QString &name, const QObject *receiver, const char *member);
static void abortHostLookup(int lookupId);
static QHostInfo fromName(const QString &name);
static QString localHostName();
static QString localDomainName();
-#ifdef Q_CLANG_QDOC
- template<typename Functor>
- static int lookupHost(const QString &name, Functor functor);
+#ifdef Q_QDOC
template<typename Functor>
static int lookupHost(const QString &name, const QObject *context, Functor functor);
#else
- // lookupHost to a QObject slot
- template <typename Func>
+ // lookupHost to a callable (with context)
+ template <typename Functor>
static inline int lookupHost(const QString &name,
- const typename QtPrivate::FunctionPointer<Func>::Object *receiver,
- Func slot)
+ const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
+ Functor &&func)
{
- typedef QtPrivate::FunctionPointer<Func> SlotType;
-
- typedef QtPrivate::FunctionPointer<void (*)(QHostInfo)> SignalType;
- static_assert(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
- "The slot requires more arguments than the signal provides.");
- static_assert((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments,
- typename SlotType::Arguments>::value),
- "Signal and slot arguments are not compatible.");
- static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType,
- typename SignalType::ReturnType>::value),
- "Return type of the slot is not compatible "
- "with the return type of the signal.");
-
- auto slotObj = new QtPrivate::QSlotObject<Func, typename SlotType::Arguments, void>(slot);
- return lookupHostImpl(name, receiver, slotObj, nullptr);
+ using Prototype = void(*)(QHostInfo);
+ QtPrivate::AssertCompatibleFunctions<Prototype, Functor>();
+ return lookupHostImpl(name, receiver,
+ QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(func)),
+ nullptr);
}
+#endif // Q_QDOC
+#ifndef QT_NO_CONTEXTLESS_CONNECT
// lookupHost to a callable (without context)
- template <typename Func>
- static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction &&
- !std::is_same<const char *, Func>::value, int>::type
- lookupHost(const QString &name, Func slot)
- {
- return lookupHost(name, nullptr, std::move(slot));
- }
-
- // lookupHost to a functor or function pointer (with context)
- template <typename Func1>
- static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
- !std::is_same<const char*, Func1>::value, int>::type
- lookupHost(const QString &name, QObject *context, Func1 slot)
+ template <typename Functor>
+ static inline int lookupHost(const QString &name, Functor &&slot)
{
- typedef QtPrivate::FunctionPointer<Func1> SlotType;
-
- static_assert(int(SlotType::ArgumentCount) <= 1,
- "The slot must not require more than one argument");
-
- auto slotObj = new QtPrivate::QFunctorSlotObject<Func1, 1,
- typename QtPrivate::List<QHostInfo>,
- void>(std::move(slot));
- return lookupHostImpl(name, context, slotObj, nullptr);
+ return lookupHost(name, nullptr, std::forward<Functor>(slot));
}
-#endif // Q_QDOC
+#endif // QT_NO_CONTEXTLESS_CONNECT
private:
QHostInfoPrivate *d_ptr;
diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h
index 22b6ccc017..b229eb1cd8 100644
--- a/src/network/kernel/qhostinfo_p.h
+++ b/src/network/kernel/qhostinfo_p.h
@@ -34,8 +34,6 @@
#include <QElapsedTimer>
#include <QCache>
-#include <QSharedPointer>
-
#include <atomic>
QT_BEGIN_NAMESPACE
@@ -45,26 +43,21 @@ class QHostInfoResult : public QObject
{
Q_OBJECT
public:
- QHostInfoResult(const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj)
- : receiver(receiver), slotObj(slotObj),
- withContextObject(slotObj && receiver)
- {
- if (receiver)
- moveToThread(receiver->thread());
- }
+ explicit QHostInfoResult(const QObject *receiver, QtPrivate::SlotObjUniquePtr slot);
+ ~QHostInfoResult() override;
void postResultsReady(const QHostInfo &info);
Q_SIGNALS:
void resultsReady(const QHostInfo &info);
-protected:
- bool event(QEvent *event) override;
+private Q_SLOTS:
+ void finalizePostResultsReady(const QHostInfo &info);
private:
- QHostInfoResult(const QHostInfoResult *other)
- : receiver(other->receiver), slotObj(other->slotObj),
- withContextObject(other->withContextObject)
+ QHostInfoResult(QHostInfoResult *other)
+ : receiver(other->receiver.get() != other ? other->receiver.get() : this),
+ slotObj{std::move(other->slotObj)}
{
// cleanup if the application terminates before results are delivered
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
@@ -73,9 +66,10 @@ private:
moveToThread(other->thread());
}
+ // receiver is either a QObject provided by the user,
+ // or it's set to `this` (to emulate the behavior of the contextless connect())
QPointer<const QObject> receiver = nullptr;
- QtPrivate::QSlotObjectBase *slotObj = nullptr;
- const bool withContextObject = false;
+ QtPrivate::SlotObjUniquePtr slotObj;
};
class QHostInfoAgent
@@ -143,8 +137,10 @@ private:
class QHostInfoRunnable : public QRunnable
{
public:
- QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj);
+ explicit QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
+ QtPrivate::SlotObjUniquePtr slotObj);
+ ~QHostInfoRunnable() override;
+
void run() override;
QString toBeLookedUp;
diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp
index 12d8c04d10..80d386a13d 100644
--- a/src/network/kernel/qhostinfo_unix.cpp
+++ b/src/network/kernel/qhostinfo_unix.cpp
@@ -3,140 +3,78 @@
//#define QHOSTINFO_DEBUG
-#include "qplatformdefs.h"
-
#include "qhostinfo_p.h"
-#include "private/qnativesocketengine_p.h"
-#include "qiodevice.h"
+
#include <qbytearray.h>
-#if QT_CONFIG(library)
-#include <qlibrary.h>
-#endif
-#include <qbasicatomic.h>
-#include <qurl.h>
#include <qfile.h>
-#include <private/qnet_unix_p.h>
-
-#include "QtCore/qapplicationstatic.h"
+#include <qplatformdefs.h>
+#include <qurl.h>
#include <sys/types.h>
#include <netdb.h>
-#include <arpa/inet.h>
-#if defined(Q_OS_VXWORKS)
-# include <hostLib.h>
-#else
-# include <resolv.h>
-#endif
+#include <netinet/in.h>
-#if defined(__GNU_LIBRARY__) && !defined(__UCLIBC__)
-# include <gnu/lib-names.h>
+#if QT_CONFIG(libresolv)
+# include <resolv.h>
#endif
-#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)
-# include <dlfcn.h>
+#ifndef _PATH_RESCONF
+# define _PATH_RESCONF "/etc/resolv.conf"
#endif
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-enum LibResolvFeature {
- NeedResInit,
- NeedResNInit
-};
-
-typedef struct __res_state *res_state_ptr;
-
-typedef int (*res_init_proto)(void);
-static res_init_proto local_res_init = nullptr;
-typedef int (*res_ninit_proto)(res_state_ptr);
-static res_ninit_proto local_res_ninit = nullptr;
-typedef void (*res_nclose_proto)(res_state_ptr);
-static res_nclose_proto local_res_nclose = nullptr;
-static res_state_ptr local_res = nullptr;
-
-#if QT_CONFIG(library) && !defined(Q_OS_QNX)
-namespace {
-struct LibResolv
-{
- enum {
-#ifdef RES_NORELOAD
- // If RES_NORELOAD is defined, then the libc is capable of watching
- // /etc/resolv.conf for changes and reloading as necessary. So accept
- // whatever is configured.
- ReinitNecessary = false
-#else
- ReinitNecessary = true
-#endif
- };
-
- QLibrary lib;
- LibResolv();
- ~LibResolv() { lib.unload(); }
-};
-}
-
-static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym)
-{
- if (lib.isLoaded())
- return lib.resolve(sym);
-
-#if defined(RTLD_DEFAULT) && (defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen))
- return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_DEFAULT, sym));
-#else
- return nullptr;
-#endif
-}
-
-LibResolv::LibResolv()
+static void maybeRefreshResolver()
{
-#ifdef LIBRESOLV_SO
- lib.setFileName(QStringLiteral(LIBRESOLV_SO));
- if (!lib.load())
+#if defined(RES_NORELOAD)
+ // If RES_NORELOAD is defined, then the libc is capable of watching
+ // /etc/resolv.conf for changes and reloading as necessary. So accept
+ // whatever is configured.
+ return;
+#elif defined(Q_OS_DARWIN)
+ // Apple's libsystem_info.dylib:getaddrinfo() uses the
+ // libsystem_dnssd.dylib to resolve hostnames. Using res_init() has no
+ // effect on it and is thread-unsafe.
+ return;
+#elif defined(Q_OS_FREEBSD)
+ // FreeBSD automatically refreshes:
+ // https://github.com/freebsd/freebsd-src/blob/b3fe5d932264445cbf9a1c4eab01afb6179b499b/lib/libc/resolv/res_state.c#L69
+ return;
+#elif defined(Q_OS_OPENBSD)
+ // OpenBSD automatically refreshes:
+ // https://github.com/ligurio/openbsd-src/blob/b1ce0da17da254cc15b8aff25b3d55d3c7a82cec/lib/libc/asr/asr.c#L367
+ return;
+#elif defined(Q_OS_QNX)
+ // res_init() is not thread-safe; executing it leads to state corruption.
+ // Whether it reloads resolv.conf on its own is unknown.
+ return;
#endif
- {
- lib.setFileName("resolv"_L1);
- lib.load();
- }
-
- // res_ninit is required for localDomainName()
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "__res_ninit"));
- if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_ninit"));
- if (local_res_ninit) {
- // we must now find res_nclose
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_nclose"));
- if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "__res_nclose"));
- if (!local_res_nclose)
- local_res_ninit = nullptr;
- }
-
- if (ReinitNecessary || !local_res_ninit) {
- local_res_init = res_init_proto(resolveSymbol(lib, "__res_init"));
- if (!local_res_init)
- local_res_init = res_init_proto(resolveSymbol(lib, "res_init"));
- if (local_res_init && !local_res_ninit) {
- // if we can't get a thread-safe context, we have to use the global _res state
- local_res = res_state_ptr(resolveSymbol(lib, "_res"));
+#if QT_CONFIG(libresolv)
+ // OSes known or thought to reach here: AIX, NetBSD, Solaris,
+ // Linux with MUSL (though res_init() does nothing and is unnecessary)
+
+ Q_CONSTINIT static QT_STATBUF lastStat = {};
+ Q_CONSTINIT static QBasicMutex mutex = {};
+ if (QT_STATBUF st; QT_STAT(_PATH_RESCONF, &st) == 0) {
+ QMutexLocker locker(&mutex);
+ bool refresh = false;
+ if ((_res.options & RES_INIT) == 0)
+ refresh = true;
+ else if (lastStat.st_ctime != st.st_ctime)
+ refresh = true; // file was updated
+ else if (lastStat.st_dev != st.st_dev || lastStat.st_ino != st.st_ino)
+ refresh = true; // file was replaced
+ if (refresh) {
+ lastStat = st;
+ res_init();
}
}
+#endif
}
-Q_APPLICATION_STATIC(LibResolv, libResolv)
-
-static void resolveLibrary(LibResolvFeature f)
-{
- if (LibResolv::ReinitNecessary || f == NeedResNInit)
- libResolv();
-}
-#else // QT_CONFIG(library) || Q_OS_QNX
-static void resolveLibrary(LibResolvFeature)
-{
-}
-#endif // QT_CONFIG(library) || Q_OS_QNX
-
QHostInfo QHostInfoAgent::fromName(const QString &hostName)
{
QHostInfo results;
@@ -146,12 +84,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
hostName.toLatin1().constData());
#endif
- // Load res_init on demand.
- resolveLibrary(NeedResInit);
-
- // If res_init is available, poll it.
- if (local_res_init)
- local_res_init();
+ maybeRefreshResolver();
QHostAddress address;
if (address.setAddress(hostName))
@@ -162,56 +95,49 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
QString QHostInfo::localDomainName()
{
-#if !defined(Q_OS_VXWORKS) && !defined(Q_OS_ANDROID)
- resolveLibrary(NeedResNInit);
- if (local_res_ninit) {
- // using thread-safe version
- res_state_ptr state = res_state_ptr(malloc(sizeof(*state)));
- Q_CHECK_PTR(state);
- memset(state, 0, sizeof(*state));
- local_res_ninit(state);
- QString domainName = QUrl::fromAce(state->defdname);
+#if QT_CONFIG(libresolv)
+ auto domainNameFromRes = [](res_state r) {
+ QString domainName;
+ if (r->defdname[0])
+ domainName = QUrl::fromAce(r->defdname);
if (domainName.isEmpty())
- domainName = QUrl::fromAce(state->dnsrch[0]);
- local_res_nclose(state);
- free(state);
-
+ domainName = QUrl::fromAce(r->dnsrch[0]);
return domainName;
+ };
+ std::remove_pointer_t<res_state> state = {};
+ if (res_ninit(&state) == 0) {
+ // using thread-safe version
+ auto guard = qScopeGuard([&] { res_nclose(&state); });
+ return domainNameFromRes(&state);
}
- if (local_res_init && local_res) {
- // using thread-unsafe version
+ // using thread-unsafe version
+ maybeRefreshResolver();
+ return domainNameFromRes(&_res);
+#endif // !QT_CONFIG(libresolv)
- local_res_init();
- QString domainName = QUrl::fromAce(local_res->defdname);
- if (domainName.isEmpty())
- domainName = QUrl::fromAce(local_res->dnsrch[0]);
- return domainName;
- }
-#endif
// nothing worked, try doing it by ourselves:
QFile resolvconf;
-#if defined(_PATH_RESCONF)
- resolvconf.setFileName(QFile::decodeName(_PATH_RESCONF));
-#else
- resolvconf.setFileName("/etc/resolv.conf"_L1);
-#endif
+ resolvconf.setFileName(_PATH_RESCONF ""_L1);
if (!resolvconf.open(QIODevice::ReadOnly))
return QString(); // failure
QString domainName;
while (!resolvconf.atEnd()) {
- QByteArray line = resolvconf.readLine().trimmed();
- if (line.startsWith("domain "))
- return QUrl::fromAce(line.mid(sizeof "domain " - 1).trimmed());
+ const QByteArray lineArray = resolvconf.readLine();
+ QByteArrayView line = QByteArrayView(lineArray).trimmed();
+ constexpr QByteArrayView domainWithSpace = "domain ";
+ if (line.startsWith(domainWithSpace))
+ return QUrl::fromAce(line.mid(domainWithSpace.size()).trimmed().toByteArray());
// in case there's no "domain" line, fall back to the first "search" entry
- if (domainName.isEmpty() && line.startsWith("search ")) {
- QByteArray searchDomain = line.mid(sizeof "search " - 1).trimmed();
+ constexpr QByteArrayView searchWithSpace = "search ";
+ if (domainName.isEmpty() && line.startsWith(searchWithSpace)) {
+ QByteArrayView searchDomain = line.mid(searchWithSpace.size()).trimmed();
int pos = searchDomain.indexOf(' ');
if (pos != -1)
searchDomain.truncate(pos);
- domainName = QUrl::fromAce(searchDomain);
+ domainName = QUrl::fromAce(searchDomain.toByteArray());
}
}
diff --git a/src/network/kernel/qnetconmonitor_darwin.mm b/src/network/kernel/qnetconmonitor_darwin.mm
index 639e267c05..60b3cd6581 100644
--- a/src/network/kernel/qnetconmonitor_darwin.mm
+++ b/src/network/kernel/qnetconmonitor_darwin.mm
@@ -1,7 +1,7 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "private/qnativesocketengine_p.h"
+#include "private/qnativesocketengine_p_p.h"
#include "private/qnetconmonitor_p.h"
#include "private/qobject_p.h"
diff --git a/src/network/kernel/qnetconmonitor_win.cpp b/src/network/kernel/qnetconmonitor_win.cpp
index 22bbed2bea..bf6aff1e46 100644
--- a/src/network/kernel/qnetconmonitor_win.cpp
+++ b/src/network/kernel/qnetconmonitor_win.cpp
@@ -9,6 +9,7 @@
#include <QtCore/qmetaobject.h>
#include <QtCore/private/qfunctions_win_p.h>
+#include <QtCore/private/qsystemerror_p.h>
#include <QtNetwork/qnetworkinterface.h>
@@ -16,7 +17,6 @@
#include <netlistmgr.h>
#include <wrl/client.h>
#include <wrl/wrappers/corewrappers.h>
-#include <comdef.h>
#include <iphlpapi.h>
#include <algorithm>
@@ -28,12 +28,6 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor");
namespace {
-QString errorStringFromHResult(HRESULT hr)
-{
- _com_error error(hr);
- return QString::fromWCharArray(error.ErrorMessage());
-}
-
template<typename T>
bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject)
{
@@ -150,7 +144,7 @@ QNetworkConnectionEvents::QNetworkConnectionEvents(QNetworkConnectionMonitorPriv
IID_INetworkListManager, &networkListManager);
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Could not get a NetworkListManager instance:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
return;
}
@@ -162,7 +156,7 @@ QNetworkConnectionEvents::QNetworkConnectionEvents(QNetworkConnectionMonitorPriv
}
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to get connection point for network events:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
}
}
@@ -173,11 +167,17 @@ QNetworkConnectionEvents::~QNetworkConnectionEvents()
ComPtr<INetworkConnection> QNetworkConnectionEvents::getNetworkConnectionFromAdapterGuid(QUuid guid)
{
+ if (!networkListManager) {
+ qCDebug(lcNetMon) << "Failed to enumerate network connections:"
+ << "NetworkListManager was not instantiated";
+ return nullptr;
+ }
+
ComPtr<IEnumNetworkConnections> connections;
auto hr = networkListManager->GetNetworkConnections(connections.GetAddressOf());
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to enumerate network connections:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
return nullptr;
}
ComPtr<INetworkConnection> connection = nullptr;
@@ -185,7 +185,7 @@ ComPtr<INetworkConnection> QNetworkConnectionEvents::getNetworkConnectionFromAda
hr = connections->Next(1, connection.GetAddressOf(), nullptr);
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to get next network connection in enumeration:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
break;
}
if (connection) {
@@ -193,7 +193,7 @@ ComPtr<INetworkConnection> QNetworkConnectionEvents::getNetworkConnectionFromAda
hr = connection->GetAdapterId(&adapterId);
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to get adapter ID from network connection:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
continue;
}
if (guid == adapterId)
@@ -258,7 +258,8 @@ bool QNetworkConnectionEvents::setTarget(const QNetworkInterface &iface)
}
auto hr = connection->GetConnectionId(&guid);
if (FAILED(hr)) {
- qCDebug(lcNetMon) << "Failed to get the connection's GUID:" << errorStringFromHResult(hr);
+ qCDebug(lcNetMon) << "Failed to get the connection's GUID:"
+ << QSystemError::windowsComString(hr);
return false;
}
currentConnectionId = guid;
@@ -281,7 +282,7 @@ bool QNetworkConnectionEvents::startMonitoring()
auto hr = connectionPoint->Advise(this, &cookie);
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to subscribe to network connectivity events:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
return false;
}
return true;
@@ -292,7 +293,7 @@ bool QNetworkConnectionEvents::stopMonitoring()
auto hr = connectionPoint->Unadvise(cookie);
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to unsubscribe from network connection events:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
return false;
}
cookie = 0;
diff --git a/src/network/kernel/qnetworkdatagram.cpp b/src/network/kernel/qnetworkdatagram.cpp
index e9e3a67edf..a97eb25a51 100644
--- a/src/network/kernel/qnetworkdatagram.cpp
+++ b/src/network/kernel/qnetworkdatagram.cpp
@@ -481,7 +481,7 @@ void QNetworkDatagram::makeReply_helper_inplace(const QByteArray &data)
void QNetworkDatagram::destroy(QNetworkDatagramPrivate *d)
{
- Q_ASSUME(d);
+ Q_ASSERT(d);
delete d;
}
diff --git a/src/network/kernel/qnetworkdatagram.h b/src/network/kernel/qnetworkdatagram.h
index 597448a0ea..dcc2f1102f 100644
--- a/src/network/kernel/qnetworkdatagram.h
+++ b/src/network/kernel/qnetworkdatagram.h
@@ -55,7 +55,7 @@ public:
QByteArray data() const;
void setData(const QByteArray &data);
-#if defined(Q_COMPILER_REF_QUALIFIERS) || defined(Q_CLANG_QDOC)
+#if defined(Q_COMPILER_REF_QUALIFIERS) || defined(Q_QDOC)
QNetworkDatagram makeReply(const QByteArray &payload) const &
{ return makeReply_helper(payload); }
QNetworkDatagram makeReply(const QByteArray &payload) &&
diff --git a/src/network/kernel/qnetworkinformation.cpp b/src/network/kernel/qnetworkinformation.cpp
index d48784f1cd..10d6b89e2c 100644
--- a/src/network/kernel/qnetworkinformation.cpp
+++ b/src/network/kernel/qnetworkinformation.cpp
@@ -26,7 +26,7 @@ struct QNetworkInformationDeleter
void operator()(QNetworkInformation *information) { delete information; }
};
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qniLoader,
(QNetworkInformationBackendFactory_iid,
QStringLiteral("/networkinformation")))
@@ -47,14 +47,21 @@ static void networkInfoCleanup()
if (!instance)
return;
- auto needsReinvoke = instance->thread() && instance->thread() != QThread::currentThread();
- if (needsReinvoke) {
- QMetaObject::invokeMethod(dataHolder->instanceHolder.get(), []() { networkInfoCleanup(); });
- return;
- }
dataHolder->instanceHolder.reset();
}
+using namespace Qt::Literals::StringLiterals;
+
+class QNetworkInformationDummyBackend : public QNetworkInformationBackend {
+ Q_OBJECT
+public:
+ QString name() const override { return u"dummy"_s; }
+ QNetworkInformation::Features featuresSupported() const override
+ {
+ return {};
+ }
+};
+
class QNetworkInformationPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QNetworkInformation)
@@ -65,6 +72,7 @@ public:
static QNetworkInformation *create(QNetworkInformation::Features features);
static QNetworkInformation *create(QStringView name);
+ static QNetworkInformation *createDummy();
static QNetworkInformation *instance()
{
if (!dataHolder())
@@ -84,7 +92,7 @@ private:
bool QNetworkInformationPrivate::initializeList()
{
- if (!loader())
+ if (!qniLoader())
return false;
if (!dataHolder())
return false;
@@ -92,11 +100,11 @@ bool QNetworkInformationPrivate::initializeList()
QMutexLocker initLocker(&mutex);
#if QT_CONFIG(library)
- loader->update();
+ qniLoader->update();
#endif
// Instantiates the plugins (and registers the factories)
int index = 0;
- while (loader->instance(index))
+ while (qniLoader->instance(index))
++index;
initLocker.unlock();
@@ -187,8 +195,8 @@ QNetworkInformation *QNetworkInformationPrivate::create(QStringView name)
} else {
QString listNames;
listNames.reserve(8 * dataHolder->factories.count());
- for (const auto *factory : qAsConst(dataHolder->factories))
- listNames += factory->name() + QStringLiteral(", ");
+ for (const auto *factory : std::as_const(dataHolder->factories))
+ listNames += factory->name() + ", "_L1;
listNames.chop(2);
qDebug().nospace() << "Couldn't find " << name << " in list with names: { "
<< listNames << " }";
@@ -230,7 +238,7 @@ QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Fea
return dataHolder->instanceHolder.get();
const auto supportsRequestedFeatures = [features](QNetworkInformationBackendFactory *factory) {
- return factory && (factory->featuresSupported() & features) == features;
+ return factory && factory->featuresSupported().testFlags(features);
};
for (auto it = dataHolder->factories.cbegin(), end = dataHolder->factories.cend(); it != end;
@@ -243,7 +251,7 @@ QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Fea
} else {
QStringList names;
names.reserve(dataHolder->factories.count());
- for (const auto *factory : qAsConst(dataHolder->factories))
+ for (const auto *factory : std::as_const(dataHolder->factories))
names += factory->name();
qDebug() << "None of the following backends has all the requested features:"
<< names << features;
@@ -271,6 +279,20 @@ QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Fea
return nullptr;
}
+QNetworkInformation *QNetworkInformationPrivate::createDummy()
+{
+ if (!dataHolder())
+ return nullptr;
+
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ if (dataHolder->instanceHolder)
+ return dataHolder->instanceHolder.get();
+
+ QNetworkInformationBackend *backend = new QNetworkInformationDummyBackend;
+ dataHolder->instanceHolder.reset(new QNetworkInformation(backend));
+ return dataHolder->instanceHolder.get();
+}
+
/*!
\class QNetworkInformationBackend
\internal (Semi-private)
@@ -492,6 +514,14 @@ QNetworkInformation::QNetworkInformation(QNetworkInformationBackend *backend)
&QNetworkInformation::transportMediumChanged);
connect(backend, &QNetworkInformationBackend::isMeteredChanged, this,
&QNetworkInformation::isMeteredChanged);
+
+ QThread *main = nullptr;
+
+ if (QCoreApplication::instance())
+ main = QCoreApplication::instance()->thread();
+
+ if (main && thread() != main)
+ moveToThread(main);
}
/*!
@@ -599,6 +629,12 @@ QNetworkInformation::Features QNetworkInformation::supportedFeatures() const
Attempts to load the platform-default backend.
+ \note Starting with 6.7 this tries to load any backend that supports
+ \l{QNetworkInformation::Feature::Reachability}{Reachability} if the
+ platform-default backend is not available or fails to load.
+ If this also fails it will fall back to a backend that only returns
+ the default values for all properties.
+
This platform-to-plugin mapping is as follows:
\table
@@ -619,12 +655,14 @@ QNetworkInformation::Features QNetworkInformation::supportedFeatures() const
\li networkmanager
\endtable
- This function is provided for convenience where the default for a given
- platform is good enough. If you are not using the default plugins you must
- use one of the other load() overloads.
+ This function is provided for convenience where the logic earlier
+ is good enough. If you require a specific plugin then you should call
+ loadBackendByName() or loadBackendByFeatures() directly instead.
- Returns \c true if it managed to load the backend or if it was already
- loaded. Returns \c false otherwise.
+ Determines a suitable backend to load and returns \c true if this backend
+ is already loaded or on successful loading of it. Returns \c false if any
+ other backend has already been loaded, or if loading of the selected
+ backend fails.
\sa instance(), load()
*/
@@ -640,9 +678,15 @@ bool QNetworkInformation::loadDefaultBackend()
#elif defined(Q_OS_LINUX)
index = QNetworkInformationBackend::PluginNamesLinuxIndex;
#endif
- if (index == -1)
- return false;
- return loadBackendByName(QNetworkInformationBackend::PluginNames[index]);
+ if (index != -1 && loadBackendByName(QNetworkInformationBackend::PluginNames[index]))
+ return true;
+ // We assume reachability is the most commonly wanted feature, and try to
+ // load the backend that advertises the most features including that:
+ if (loadBackendByFeatures(Feature::Reachability))
+ return true;
+
+ // Fall back to the dummy backend
+ return loadBackendByName(u"dummy");
}
/*!
@@ -658,6 +702,9 @@ bool QNetworkInformation::loadDefaultBackend()
*/
bool QNetworkInformation::loadBackendByName(QStringView backend)
{
+ if (backend == u"dummy")
+ return QNetworkInformationPrivate::createDummy() != nullptr;
+
auto loadedBackend = QNetworkInformationPrivate::create(backend);
return loadedBackend && loadedBackend->backendName().compare(backend, Qt::CaseInsensitive) == 0;
}
@@ -724,3 +771,4 @@ QT_END_NAMESPACE
#include "moc_qnetworkinformation.cpp"
#include "moc_qnetworkinformation_p.cpp"
+#include "qnetworkinformation.moc"
diff --git a/src/network/kernel/qnetworkinformation.h b/src/network/kernel/qnetworkinformation.h
index 818da98aad..57a49f23c8 100644
--- a/src/network/kernel/qnetworkinformation.h
+++ b/src/network/kernel/qnetworkinformation.h
@@ -23,6 +23,7 @@ class Q_NETWORK_EXPORT QNetworkInformation : public QObject
NOTIFY isBehindCaptivePortalChanged)
Q_PROPERTY(TransportMedium transportMedium READ transportMedium NOTIFY transportMediumChanged)
Q_PROPERTY(bool isMetered READ isMetered NOTIFY isMeteredChanged)
+ Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
public:
enum class Reachability {
Unknown,
@@ -82,7 +83,6 @@ Q_SIGNALS:
private:
friend struct QNetworkInformationDeleter;
- friend class QNetworkInformationPrivate;
QNetworkInformation(QNetworkInformationBackend *backend);
~QNetworkInformation() override;
diff --git a/src/network/kernel/qnetworkinformation_p.h b/src/network/kernel/qnetworkinformation_p.h
index 40c12391c5..504955a6e1 100644
--- a/src/network/kernel/qnetworkinformation_p.h
+++ b/src/network/kernel/qnetworkinformation_p.h
@@ -20,6 +20,7 @@
#include <QtNetwork/qnetworkinformation.h>
#include <QtCore/qloggingcategory.h>
+#include <QtCore/qreadwritelock.h>
QT_BEGIN_NAMESPACE
@@ -48,10 +49,29 @@ public:
virtual QString name() const = 0;
virtual QNetworkInformation::Features featuresSupported() const = 0;
- Reachability reachability() const { return m_reachability; }
- bool behindCaptivePortal() const { return m_behindCaptivePortal; }
- TransportMedium transportMedium() const { return m_transportMedium; }
- bool isMetered() const { return m_metered; }
+ Reachability reachability() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_reachability;
+ }
+
+ bool behindCaptivePortal() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_behindCaptivePortal;
+ }
+
+ TransportMedium transportMedium() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_transportMedium;
+ }
+
+ bool isMetered() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_metered;
+ }
Q_SIGNALS:
void reachabilityChanged(Reachability reachability);
@@ -62,37 +82,46 @@ Q_SIGNALS:
protected:
void setReachability(QNetworkInformation::Reachability reachability)
{
+ QWriteLocker locker(&m_lock);
if (m_reachability != reachability) {
m_reachability = reachability;
+ locker.unlock();
emit reachabilityChanged(reachability);
}
}
void setBehindCaptivePortal(bool behindPortal)
{
+ QWriteLocker locker(&m_lock);
if (m_behindCaptivePortal != behindPortal) {
m_behindCaptivePortal = behindPortal;
+ locker.unlock();
emit behindCaptivePortalChanged(behindPortal);
}
}
void setTransportMedium(TransportMedium medium)
{
+ QWriteLocker locker(&m_lock);
if (m_transportMedium != medium) {
m_transportMedium = medium;
+ locker.unlock();
emit transportMediumChanged(medium);
}
}
void setMetered(bool isMetered)
{
+ QWriteLocker locker(&m_lock);
if (m_metered != isMetered) {
m_metered = isMetered;
+ locker.unlock();
emit isMeteredChanged(isMetered);
}
}
private:
+ mutable QReadWriteLock m_lock;
Reachability m_reachability = Reachability::Unknown;
TransportMedium m_transportMedium = TransportMedium::Unknown;
bool m_behindCaptivePortal = false;
diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp
index 8277f57a55..f03e85c7f6 100644
--- a/src/network/kernel/qnetworkinterface.cpp
+++ b/src/network/kernel/qnetworkinterface.cpp
@@ -873,7 +873,7 @@ QList<QHostAddress> QNetworkInterface::allAddresses()
if ((p->flags & QNetworkInterface::IsUp) == 0)
continue;
- for (const QNetworkAddressEntry &entry : qAsConst(p->addressEntries))
+ for (const QNetworkAddressEntry &entry : std::as_const(p->addressEntries))
result += entry.ip();
}
diff --git a/src/network/kernel/qnetworkinterface_linux.cpp b/src/network/kernel/qnetworkinterface_linux.cpp
index 51fb23b547..67ed8006bb 100644
--- a/src/network/kernel/qnetworkinterface_linux.cpp
+++ b/src/network/kernel/qnetworkinterface_linux.cpp
@@ -111,7 +111,10 @@ struct NetlinkSocket
template <typename Lambda> struct ProcessNetlinkRequest
{
using FunctionTraits = QtPrivate::FunctionPointer<decltype(&Lambda::operator())>;
- using FirstArgument = typename FunctionTraits::Arguments::Car;
+ using FirstArgumentPointer = typename FunctionTraits::Arguments::Car;
+ using FirstArgument = std::remove_pointer_t<FirstArgumentPointer>;
+ static_assert(std::is_pointer_v<FirstArgumentPointer>);
+ static_assert(std::is_aggregate_v<FirstArgument>);
static int expectedTypeForRequest(int rtype)
{
@@ -136,7 +139,7 @@ template <typename Lambda> struct ProcessNetlinkRequest
if (!NLMSG_OK(hdr, quint32(len)))
return;
- auto arg = reinterpret_cast<FirstArgument>(NLMSG_DATA(hdr));
+ auto arg = static_cast<FirstArgument *>(NLMSG_DATA(hdr));
size_t payloadLen = NLMSG_PAYLOAD(hdr, 0);
// is this a multipart message?
@@ -156,7 +159,7 @@ template <typename Lambda> struct ProcessNetlinkRequest
// NLMSG_NEXT also updates the len variable
hdr = NLMSG_NEXT(hdr, len);
- arg = reinterpret_cast<FirstArgument>(NLMSG_DATA(hdr));
+ arg = static_cast<FirstArgument *>(NLMSG_DATA(hdr));
payloadLen = NLMSG_PAYLOAD(hdr, 0);
} while (NLMSG_OK(hdr, quint32(len)));
@@ -309,7 +312,7 @@ static void getAddresses(int sock, char *buf, QList<QNetworkInterfacePrivate *>
// find the interface this is relevant to
QNetworkInterfacePrivate *iface = nullptr;
- for (auto candidate : qAsConst(result)) {
+ for (auto candidate : std::as_const(result)) {
if (candidate->index != int(ifa->ifa_index))
continue;
iface = candidate;
diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp
index 51a266b2e3..c0a7d9e00d 100644
--- a/src/network/kernel/qnetworkinterface_unix.cpp
+++ b/src/network/kernel/qnetworkinterface_unix.cpp
@@ -17,11 +17,7 @@
# include "qdatetime.h"
#endif
-#if defined(QT_LINUXBASE)
-# define QT_NO_GETIFADDRS
-#endif
-
-#ifndef QT_NO_GETIFADDRS
+#if QT_CONFIG(getifaddrs)
# include <ifaddrs.h>
#endif
@@ -56,30 +52,38 @@ static QHostAddress addressFromSockaddr(sockaddr *sa, int ifindex = 0, const QSt
}
}
return address;
+}
+
+template <typename Req> [[maybe_unused]]
+static auto &ifreq_index(Req &req, std::enable_if_t<sizeof(std::declval<Req>().ifr_index) != 0, int> = 0)
+{
+ return req.ifr_index;
+}
+template <typename Req> [[maybe_unused]]
+static auto &ifreq_index(Req &req, std::enable_if_t<sizeof(std::declval<Req>().ifr_ifindex) != 0, int> = 0)
+{
+ return req.ifr_ifindex;
}
uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
{
-#ifndef QT_NO_IPV6IFNAME
- return ::if_nametoindex(name.toLatin1());
+#if QT_CONFIG(ipv6ifname)
+ return ::if_nametoindex(name.toLatin1().constData());
#elif defined(SIOCGIFINDEX)
struct ifreq req;
int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0);
if (socket < 0)
return 0;
- QByteArray name8bit = name.toLatin1();
+ const QByteArray name8bit = name.toLatin1();
memset(&req, 0, sizeof(ifreq));
- memcpy(req.ifr_name, name8bit, qMin<int>(name8bit.length() + 1, sizeof(req.ifr_name) - 1));
+ if (!name8bit.isNull())
+ memcpy(req.ifr_name, name8bit.data(), qMin(size_t(name8bit.length()) + 1, sizeof(req.ifr_name) - 1));
uint id = 0;
if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0)
-# if QT_CONFIG(ifr_index)
- id = req.ifr_index;
-# else
- id = req.ifr_ifindex;
-# endif
+ id = ifreq_index(req);
qt_safe_close(socket);
return id;
#else
@@ -90,7 +94,7 @@ uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index)
{
-#ifndef QT_NO_IPV6IFNAME
+#if QT_CONFIG(ipv6ifname)
char buf[IF_NAMESIZE];
if (::if_indextoname(index, buf))
return QString::fromLatin1(buf);
@@ -99,12 +103,7 @@ QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index)
int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0);
if (socket >= 0) {
memset(&req, 0, sizeof(ifreq));
-# if QT_CONFIG(ifr_index)
- req.ifr_index = index;
-# else
- req.ifr_ifindex = index;
-# endif
-
+ ifreq_index(req) = index;
if (qt_safe_ioctl(socket, SIOCGIFNAME, &req) >= 0) {
qt_safe_close(socket);
return QString::fromLatin1(req.ifr_name);
@@ -124,13 +123,13 @@ static int getMtu(int socket, struct ifreq *req)
return 0;
}
-#ifdef QT_NO_GETIFADDRS
+#if !QT_CONFIG(getifaddrs)
// getifaddrs not available
static QSet<QByteArray> interfaceNames(int socket)
{
QSet<QByteArray> result;
-#ifdef QT_NO_IPV6IFNAME
+#if !QT_CONFIG(ipv6ifname)
QByteArray storageBuffer;
struct ifconf interfaceList;
static const int STORAGEBUFFER_GROWTH = 256;
@@ -185,15 +184,11 @@ static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfa
QNetworkInterfacePrivate *iface = nullptr;
int ifindex = 0;
-#if !defined(QT_NO_IPV6IFNAME) || defined(SIOCGIFINDEX)
+#if QT_CONFIG(ipv6ifname) || defined(SIOCGIFINDEX)
// Get the interface index
# ifdef SIOCGIFINDEX
if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0)
-# if QT_CONFIG(ifr_index)
- ifindex = req.ifr_index;
-# else
- ifindex = req.ifr_ifindex;
-# endif
+ ifindex = ifreq_index(req);
# else
ifindex = if_nametoindex(req.ifr_name);
# endif
@@ -241,7 +236,8 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
for ( ; it != names.constEnd(); ++it) {
ifreq req;
memset(&req, 0, sizeof(ifreq));
- memcpy(req.ifr_name, *it, qMin<int>(it->length() + 1, sizeof(req.ifr_name) - 1));
+ if (!it->isNull())
+ memcpy(req.ifr_name, it->constData(), qMin(size_t(it->length()) + 1, sizeof(req.ifr_name) - 1));
QNetworkInterfacePrivate *iface = findInterface(socket, interfaces, req);
@@ -252,7 +248,8 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
iface->name = QString::fromLatin1(req.ifr_name);
// reset the name:
- memcpy(req.ifr_name, oldName, qMin<int>(oldName.length() + 1, sizeof(req.ifr_name) - 1));
+ if (!oldName.isNull())
+ memcpy(req.ifr_name, oldName.constData(), qMin(size_t(oldName.length()) + 1, sizeof(req.ifr_name) - 1));
} else
#endif
{
@@ -459,7 +456,8 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
// ensure both structs start with the name field, of size IFNAMESIZ
static_assert(sizeof(mediareq.ifm_name) == sizeof(req.ifr_name));
- Q_ASSERT(&mediareq.ifm_name == &req.ifr_name);
+ static_assert(offsetof(struct ifmediareq, ifm_name) == 0);
+ static_assert(offsetof(struct ifreq, ifr_name) == 0);
// on NetBSD we use AF_LINK and sockaddr_dl
// scan the list for that family
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index a00b0031ac..9b91b11d6b 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -752,6 +752,53 @@ QNetworkProxy QNetworkProxy::applicationProxy()
}
/*!
+ \since 6.8
+
+ Returns headers that are set in this network request.
+
+ If the proxy is not of type HttpProxy or HttpCachingProxy,
+ default constructed QHttpHeaders is returned.
+
+ \sa setHeaders()
+*/
+QHttpHeaders QNetworkProxy::headers() const
+{
+ if (d->type != HttpProxy && d->type != HttpCachingProxy)
+ return {};
+ return d->headers.headers();
+}
+
+/*!
+ \since 6.8
+
+ Sets \a newHeaders as headers in this network request, overriding
+ any previously set headers.
+
+ If some headers correspond to the known headers, the values will
+ be parsed and the corresponding parsed form will also be set.
+
+ If the proxy is not of type HttpProxy or HttpCachingProxy this has no
+ effect.
+
+ \sa headers(), QNetworkRequest::KnownHeaders
+*/
+void QNetworkProxy::setHeaders(QHttpHeaders &&newHeaders)
+{
+ if (d->type == HttpProxy || d->type == HttpCachingProxy)
+ d->headers.setHeaders(std::move(newHeaders));
+}
+
+/*!
+ \overload
+ \since 6.8
+*/
+void QNetworkProxy::setHeaders(const QHttpHeaders &newHeaders)
+{
+ if (d->type == HttpProxy || d->type == HttpCachingProxy)
+ d->headers.setHeaders(newHeaders);
+}
+
+/*!
\since 5.0
Returns the value of the known network header \a header if it is
in use for this proxy. If it is not present, returns QVariant()
@@ -795,7 +842,7 @@ bool QNetworkProxy::hasRawHeader(const QByteArray &headerName) const
{
if (d->type != HttpProxy && d->type != HttpCachingProxy)
return false;
- return d->headers.findRawHeader(headerName) != d->headers.rawHeaders.constEnd();
+ return d->headers.headers().contains(headerName);
}
/*!
@@ -814,11 +861,7 @@ QByteArray QNetworkProxy::rawHeader(const QByteArray &headerName) const
{
if (d->type != HttpProxy && d->type != HttpCachingProxy)
return QByteArray();
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
- d->headers.findRawHeader(headerName);
- if (it != d->headers.rawHeaders.constEnd())
- return it->second;
- return QByteArray();
+ return d->headers.rawHeader(headerName);
}
/*!
@@ -1466,7 +1509,7 @@ void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *fact
Internet Explorer's settings and use them.
On \macos, this function will obtain the proxy settings using the
- SystemConfiguration framework from Apple. It will apply the FTP,
+ CFNetwork framework from Apple. It will apply the FTP,
HTTP and HTTPS proxy configurations for queries that contain the
protocol tag "ftp", "http" and "https", respectively. If the SOCKS
proxy is enabled in that configuration, this function will use the
@@ -1489,9 +1532,6 @@ void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *fact
listed here.
\list
- \li On \macos, this function will ignore the Proxy Auto Configuration
- settings, since it cannot execute the associated ECMAScript code.
-
\li On Windows platforms, this function may take several seconds to
execute depending on the configuration of the user's system.
\endlist
diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h
index c39fff5318..9f92ffeb12 100644
--- a/src/network/kernel/qnetworkproxy.h
+++ b/src/network/kernel/qnetworkproxy.h
@@ -77,6 +77,7 @@ class QNetworkProxyPrivate;
class Q_NETWORK_EXPORT QNetworkProxy
{
+ Q_GADGET
public:
enum ProxyType {
DefaultProxy,
@@ -135,6 +136,10 @@ public:
static void setApplicationProxy(const QNetworkProxy &proxy);
static QNetworkProxy applicationProxy();
+ QHttpHeaders headers() const;
+ void setHeaders(const QHttpHeaders &newHeaders);
+ void setHeaders(QHttpHeaders &&newHeaders);
+
// "cooked" headers
QVariant header(QNetworkRequest::KnownHeaders header) const;
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
diff --git a/src/network/kernel/qnetworkproxy_android.cpp b/src/network/kernel/qnetworkproxy_android.cpp
index 2e1fb50234..3d37266b70 100644
--- a/src/network/kernel/qnetworkproxy_android.cpp
+++ b/src/network/kernel/qnetworkproxy_android.cpp
@@ -24,7 +24,7 @@ Q_GLOBAL_STATIC(ProxyInfoObject, proxyInfoInstance)
static const char networkClass[] = "org/qtproject/qt/android/network/QtNetwork";
-Q_DECLARE_JNI_TYPE(ProxyInfo, "Landroid/net/ProxyInfo;")
+Q_DECLARE_JNI_CLASS(ProxyInfo, "android/net/ProxyInfo")
Q_DECLARE_JNI_TYPE(JStringArray, "[Ljava/lang/String;")
ProxyInfoObject::ProxyInfoObject()
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_darwin.cpp
index aa691090cc..d2bd4958dd 100644
--- a/src/network/kernel/qnetworkproxy_mac.cpp
+++ b/src/network/kernel/qnetworkproxy_darwin.cpp
@@ -14,6 +14,7 @@
#include <QtCore/QUrl>
#include <QtCore/qendian.h>
#include <QtCore/qstringlist.h>
+#include <QtCore/qsystemdetection.h>
#include "private/qcore_mac_p.h"
/*
@@ -32,11 +33,11 @@
* \li Bypass list (by default: *.local, 169.254/16)
* \endlist
*
- * The matching configuration can be obtained by calling SCDynamicStoreCopyProxies
- * (from <SystemConfiguration/SCDynamicStoreCopySpecific.h>). See
+ * The matching configuration can be obtained by calling CFNetworkCopySystemProxySettings()
+ * (from <CFNetwork/CFProxySupport.h>). See
* Apple's documentation:
*
- * http://developer.apple.com/DOCUMENTATION/Networking/Reference/SysConfig/SCDynamicStoreCopySpecific/CompositePage.html#//apple_ref/c/func/SCDynamicStoreCopyProxies
+ * https://developer.apple.com/documentation/cfnetwork/1426754-cfnetworkcopysystemproxysettings?language=objc
*
*/
@@ -46,13 +47,19 @@ using namespace Qt::StringLiterals;
static bool isHostExcluded(CFDictionaryRef dict, const QString &host)
{
+ Q_ASSERT(dict);
+
if (host.isEmpty())
return true;
+#ifndef Q_OS_IOS
+ // On iOS all those keys are not available, and worse so - entries
+ // for HTTPS are not in the dictionary, but instead in some nested dictionary
+ // with undocumented keys/object types.
bool isSimple = !host.contains(u'.') && !host.contains(u':');
CFNumberRef excludeSimples;
if (isSimple &&
- (excludeSimples = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExcludeSimpleHostnames))) {
+ (excludeSimples = (CFNumberRef)CFDictionaryGetValue(dict, kCFNetworkProxiesExcludeSimpleHostnames))) {
int enabled;
if (CFNumberGetValue(excludeSimples, kCFNumberIntType, &enabled) && enabled)
return true;
@@ -63,7 +70,7 @@ static bool isHostExcluded(CFDictionaryRef dict, const QString &host)
// not a simple host name
// does it match the list of exclusions?
- CFArrayRef exclusionList = (CFArrayRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExceptionsList);
+ CFArrayRef exclusionList = (CFArrayRef)CFDictionaryGetValue(dict, kCFNetworkProxiesExceptionsList);
if (!exclusionList)
return false;
@@ -81,7 +88,9 @@ static bool isHostExcluded(CFDictionaryRef dict, const QString &host)
return true;
}
}
-
+#else
+ Q_UNUSED(dict);
+#endif // Q_OS_IOS
// host was not excluded
return false;
}
@@ -112,7 +121,6 @@ static QNetworkProxy proxyFromDictionary(CFDictionaryRef dict, QNetworkProxy::Pr
return QNetworkProxy();
}
-
static QNetworkProxy proxyFromDictionary(CFDictionaryRef dict)
{
QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
@@ -178,16 +186,43 @@ QCFType<CFStringRef> stringByAddingPercentEscapes(CFStringRef originalPath)
return escaped.toCFString();
}
-} // anon namespace
+#ifdef Q_OS_IOS
+QList<QNetworkProxy> proxiesForQueryUrl(CFDictionaryRef dict, const QUrl &url)
+{
+ Q_ASSERT(dict);
+
+ const QCFType<CFURLRef> cfUrl = url.toCFURL();
+ const QCFType<CFArrayRef> proxies = CFNetworkCopyProxiesForURL(cfUrl, dict);
+ Q_ASSERT(proxies);
+
+ QList<QNetworkProxy> result;
+ const auto count = CFArrayGetCount(proxies);
+ if (!count) // Could be no proper proxy or host excluded.
+ return result;
+
+ for (CFIndex i = 0; i < count; ++i) {
+ const void *obj = CFArrayGetValueAtIndex(proxies, i);
+ if (CFGetTypeID(obj) != CFDictionaryGetTypeID())
+ continue;
+ const QNetworkProxy proxy = proxyFromDictionary(static_cast<CFDictionaryRef>(obj));
+ if (proxy.type() == QNetworkProxy::NoProxy || proxy.type() == QNetworkProxy::DefaultProxy)
+ continue;
+ result << proxy;
+ }
+
+ return result;
+}
+#endif // Q_OS_IOS
+} // unnamed namespace.
QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
{
QList<QNetworkProxy> result;
// obtain a dictionary to the proxy settings:
- const QCFType<CFDictionaryRef> dict = SCDynamicStoreCopyProxies(NULL);
+ const QCFType<CFDictionaryRef> dict = CFNetworkCopySystemProxySettings();
if (!dict) {
- qWarning("QNetworkProxyFactory::systemProxyForQuery: SCDynamicStoreCopyProxies returned NULL");
+ qWarning("QNetworkProxyFactory::systemProxyForQuery: CFNetworkCopySystemProxySettings returned nullptr");
return result; // failed
}
@@ -196,13 +231,13 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
// is there a PAC enabled? If so, use it first.
CFNumberRef pacEnabled;
- if ((pacEnabled = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigEnable))) {
+ if ((pacEnabled = (CFNumberRef)CFDictionaryGetValue(dict, kCFNetworkProxiesProxyAutoConfigEnable))) {
int enabled;
if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) {
// PAC is enabled
// kSCPropNetProxiesProxyAutoConfigURLString returns the URL string
// as entered in the system proxy configuration dialog
- CFStringRef pacLocationSetting = (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString);
+ CFStringRef pacLocationSetting = (CFStringRef)CFDictionaryGetValue(dict, kCFNetworkProxiesProxyAutoConfigURLString);
auto cfPacLocation = stringByAddingPercentEscapes(pacLocationSetting);
QCFType<CFDataRef> pacData;
QCFType<CFURLRef> pacUrl = CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, NULL);
@@ -252,53 +287,77 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
}
}
- // no PAC, decide which proxy we're looking for based on the query
- bool isHttps = false;
- QString protocol = query.protocolTag().toLower();
-
+ // No PAC, decide which proxy we're looking for based on the query
// try the protocol-specific proxy
+ const QString protocol = query.protocolTag();
QNetworkProxy protocolSpecificProxy;
- if (protocol == "ftp"_L1) {
- protocolSpecificProxy =
- proxyFromDictionary(dict, QNetworkProxy::FtpCachingProxy,
- kSCPropNetProxiesFTPEnable,
- kSCPropNetProxiesFTPProxy,
- kSCPropNetProxiesFTPPort);
- } else if (protocol == "http"_L1) {
+ if (protocol.compare("http"_L1, Qt::CaseInsensitive) == 0) {
protocolSpecificProxy =
proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
- kSCPropNetProxiesHTTPEnable,
- kSCPropNetProxiesHTTPProxy,
- kSCPropNetProxiesHTTPPort);
- } else if (protocol == "https"_L1) {
+ kCFNetworkProxiesHTTPEnable,
+ kCFNetworkProxiesHTTPProxy,
+ kCFNetworkProxiesHTTPPort);
+ }
+
+
+#ifdef Q_OS_IOS
+ if (protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy
+ && protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy) {
+ // HTTP proxy is enabled (on iOS there is no separate HTTPS, though
+ // 'dict' contains deeply buried entries which are the same as HTTP.
+ result << protocolSpecificProxy;
+ }
+
+ // TODO: check query.queryType()? It's possible, the exclude list
+ // did exclude it but above we added a proxy because HTTP proxy
+ // is found. We'll deal with such a situation later, since now NMI.
+ const auto proxiesForUrl = proxiesForQueryUrl(dict, query.url());
+ for (const auto &proxy : proxiesForUrl) {
+ if (!result.contains(proxy))
+ result << proxy;
+ }
+#else
+ bool isHttps = false;
+ if (protocol.compare("ftp"_L1, Qt::CaseInsensitive) == 0) {
+ protocolSpecificProxy =
+ proxyFromDictionary(dict, QNetworkProxy::FtpCachingProxy,
+ kCFNetworkProxiesFTPEnable,
+ kCFNetworkProxiesFTPProxy,
+ kCFNetworkProxiesFTPPort);
+ } else if (protocol.compare("https"_L1, Qt::CaseInsensitive) == 0) {
isHttps = true;
protocolSpecificProxy =
proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
- kSCPropNetProxiesHTTPSEnable,
- kSCPropNetProxiesHTTPSProxy,
- kSCPropNetProxiesHTTPSPort);
+ kCFNetworkProxiesHTTPSEnable,
+ kCFNetworkProxiesHTTPSProxy,
+ kCFNetworkProxiesHTTPSPort);
}
+
if (protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy)
result << protocolSpecificProxy;
// let's add SOCKSv5 if present too
QNetworkProxy socks5 = proxyFromDictionary(dict, QNetworkProxy::Socks5Proxy,
- kSCPropNetProxiesSOCKSEnable,
- kSCPropNetProxiesSOCKSProxy,
- kSCPropNetProxiesSOCKSPort);
+ kCFNetworkProxiesSOCKSEnable,
+ kCFNetworkProxiesSOCKSProxy,
+ kCFNetworkProxiesSOCKSPort);
if (socks5.type() != QNetworkProxy::DefaultProxy)
result << socks5;
// let's add the HTTPS proxy if present (and if we haven't added
// yet)
if (!isHttps) {
- QNetworkProxy https = proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
- kSCPropNetProxiesHTTPSEnable,
- kSCPropNetProxiesHTTPSProxy,
- kSCPropNetProxiesHTTPSPort);
+ QNetworkProxy https;
+ https = proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
+ kCFNetworkProxiesHTTPSEnable,
+ kCFNetworkProxiesHTTPSProxy,
+ kCFNetworkProxiesHTTPSPort);
+
+
if (https.type() != QNetworkProxy::DefaultProxy && https != protocolSpecificProxy)
result << https;
}
+#endif // !Q_OS_IOS
return result;
}
diff --git a/src/network/kernel/qnetworkproxy_libproxy.cpp b/src/network/kernel/qnetworkproxy_libproxy.cpp
index 46066b86f7..da1e8fdbd4 100644
--- a/src/network/kernel/qnetworkproxy_libproxy.cpp
+++ b/src/network/kernel/qnetworkproxy_libproxy.cpp
@@ -12,6 +12,7 @@
#include <QtCore/QUrl>
#include <QtCore/private/qeventdispatcher_unix_p.h>
#include <QtCore/private/qthread_p.h>
+#include <QtCore/qapplicationstatic.h>
#include <proxy.h>
#include <dlfcn.h>
@@ -72,7 +73,7 @@ private:
Data *request;
};
-Q_GLOBAL_STATIC(QLibProxyWrapper, libProxyWrapper);
+Q_APPLICATION_STATIC(QLibProxyWrapper, libProxyWrapper)
QLibProxyWrapper::QLibProxyWrapper()
{
@@ -165,13 +166,15 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
break;
// fake URLs to get libproxy to tell us the SOCKS proxy
case QNetworkProxyQuery::TcpSocket:
- queryUrl.setScheme(QStringLiteral("tcp"));
+ if (queryUrl.scheme().isEmpty())
+ queryUrl.setScheme(QStringLiteral("tcp"));
queryUrl.setHost(query.peerHostName());
queryUrl.setPort(query.peerPort());
requiredCapabilities |= QNetworkProxy::TunnelingCapability;
break;
case QNetworkProxyQuery::UdpSocket:
- queryUrl.setScheme(QStringLiteral("udp"));
+ if (queryUrl.scheme().isEmpty())
+ queryUrl.setScheme(QStringLiteral("udp"));
queryUrl.setHost(query.peerHostName());
queryUrl.setPort(query.peerPort());
requiredCapabilities |= QNetworkProxy::UdpTunnelingCapability;
diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp
index eb8b6ab6b1..a2daa62e84 100644
--- a/src/network/kernel/qnetworkproxy_win.cpp
+++ b/src/network/kernel/qnetworkproxy_win.cpp
@@ -295,9 +295,9 @@ public:
}
void clear() {
- for (HANDLE event : qAsConst(m_watchEvents))
+ for (HANDLE event : std::as_const(m_watchEvents))
CloseHandle(event);
- for (HKEY key : qAsConst(m_registryHandles))
+ for (HKEY key : std::as_const(m_registryHandles))
RegCloseKey(key);
m_watchEvents.clear();
diff --git a/src/network/kernel/qtldurl.cpp b/src/network/kernel/qtldurl.cpp
index 7bda9845b9..a7aceddb18 100644
--- a/src/network/kernel/qtldurl.cpp
+++ b/src/network/kernel/qtldurl.cpp
@@ -7,9 +7,7 @@
#if QT_CONFIG(topleveldomain)
-#include "qurl.h"
#include "QtCore/qfile.h"
-#include "QtCore/qfileinfo.h"
#include "QtCore/qloggingcategory.h"
#include "QtCore/qstandardpaths.h"
#include "QtCore/qstring.h"
@@ -214,4 +212,4 @@ Q_NETWORK_EXPORT bool qIsEffectiveTLD(QStringView domain)
QT_END_NAMESPACE
-#endif
+#endif // QT_CONFIG(topleveldomain)
diff --git a/src/network/kernel/qtldurl_p.h b/src/network/kernel/qtldurl_p.h
index f051fba4df..86b163f161 100644
--- a/src/network/kernel/qtldurl_p.h
+++ b/src/network/kernel/qtldurl_p.h
@@ -16,7 +16,6 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "QtCore/qurl.h"
#include "QtCore/qstring.h"
QT_REQUIRE_CONFIG(topleveldomain);
@@ -31,4 +30,4 @@ inline bool qIsEffectiveTLD(const QString &domain)
QT_END_NAMESPACE
-#endif // QDATAURL_P_H
+#endif // QTLDURL_P_H
diff --git a/src/network/kernel/qtnetworkglobal_p.h b/src/network/kernel/qtnetworkglobal_p.h
index ff2b75e3a9..b90e675cb4 100644
--- a/src/network/kernel/qtnetworkglobal_p.h
+++ b/src/network/kernel/qtnetworkglobal_p.h
@@ -18,7 +18,6 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/private/qglobal_p.h>
#include <QtNetwork/private/qtnetwork-config_p.h>
-#include <QtNetwork/private/qtnetworkexports_p.h>
QT_BEGIN_NAMESPACE