summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/access.pri6
-rw-r--r--src/network/access/qhsts.cpp180
-rw-r--r--src/network/access/qhsts_p.h81
-rw-r--r--src/network/access/qhstspolicy.cpp218
-rw-r--r--src/network/access/qhstspolicy.h82
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp41
-rw-r--r--src/network/access/qnetworkaccessmanager.h3
-rw-r--r--src/network/access/qnetworkdiskcache.cpp2
-rw-r--r--src/network/bearer/qnetworkconfiguration.cpp2
-rw-r--r--src/network/kernel/qhostinfo.cpp148
-rw-r--r--src/network/kernel/qhostinfo.h63
-rw-r--r--src/network/kernel/qhostinfo_p.h42
-rw-r--r--src/network/kernel/qnetworkproxy.cpp19
-rw-r--r--src/network/kernel/qnetworkproxy.h4
14 files changed, 730 insertions, 161 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index 766d72590c..13d52ea44a 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -40,7 +40,8 @@ HEADERS += \
access/qhttpmultipart_p.h \
access/qnetworkfile_p.h \
access/qhttp2protocolhandler_p.h \
- access/qhsts_p.h
+ access/qhsts_p.h \
+ access/qhstspolicy.h
SOURCES += \
access/qftp.cpp \
@@ -74,7 +75,8 @@ SOURCES += \
access/qhttpmultipart.cpp \
access/qnetworkfile.cpp \
access/qhttp2protocolhandler.cpp \
- access/qhsts.cpp
+ access/qhsts.cpp \
+ access/qhstspolicy.cpp
mac: LIBS_PRIVATE += -framework Security
diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp
index 2352c3e4f2..5e4f75b0ed 100644
--- a/src/network/access/qhsts.cpp
+++ b/src/network/access/qhsts.cpp
@@ -45,17 +45,8 @@
QT_BEGIN_NAMESPACE
-static bool expired_policy(const QDateTime &expires)
+static bool is_valid_domain_name(const QString &host)
{
- return !expires.isValid() || expires <= QDateTime::currentDateTimeUtc();
-}
-
-static bool has_valid_domain_name(const QUrl &url)
-{
- if (!url.isValid())
- return false;
-
- const QString host(url.host());
if (!host.size())
return false;
@@ -82,117 +73,106 @@ static bool has_valid_domain_name(const QUrl &url)
return true;
}
-QHstsCache::QHstsCache()
-{
- // Top-level domain without any label.
- children.push_back(Domain());
-}
-
void QHstsCache::updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
const QUrl &url)
{
- if (!has_valid_domain_name(url))
+ if (!url.isValid())
return;
QHstsHeaderParser parser;
if (parser.parse(headers))
- updateKnownHost(url, parser.expirationDate(), parser.includeSubDomains());
+ updateKnownHost(url.host(), parser.expirationDate(), parser.includeSubDomains());
+}
+
+void QHstsCache::updateFromPolicies(const QList<QHstsPolicy> &policies)
+{
+ for (const auto &policy : policies)
+ updateKnownHost(policy.host(), policy.expiry(), policy.includesSubDomains());
}
-void QHstsCache::updateKnownHost(const QUrl &originalUrl, const QDateTime &expires,
+void QHstsCache::updateKnownHost(const QUrl &url, const QDateTime &expires,
bool includeSubDomains)
{
- if (!has_valid_domain_name(originalUrl))
+ if (!url.isValid())
return;
- // HSTS is a per-host policy, regardless of protocol, port or any of the other
- // details in an URL; so we only want the host part. We still package this as
- // a QUrl since this handles IDNA 2003 (RFC3490) for us, as required by
- // HSTS (RFC6797, section 10).
- QUrl url;
- url.setHost(originalUrl.host());
-
- // 1. Update our hosts:
- QStringList labels(url.host().split(QLatin1Char('.')));
- std::reverse(labels.begin(), labels.end());
-
- size_type domainIndex = 0;
- for (int i = 0, e = labels.size(); i < e; ++i) {
- Q_ASSERT(domainIndex < children.size());
- auto &subDomains = children[domainIndex].labels;
- const auto &label = labels[i];
- auto pos = std::lower_bound(subDomains.begin(), subDomains.end(), label);
- if (pos == subDomains.end() || pos->label != label) {
- // A new, previously unknown host.
- if (expired_policy(expires)) {
- // Nothing to do at all - we did not know this host previously,
- // we do not have to - since its policy expired.
- return;
- }
-
- pos = subDomains.insert(pos, label);
- domainIndex = children.size();
- pos->domainIndex = domainIndex;
- children.resize(children.size() + (e - i));
+ updateKnownHost(url.host(), expires, includeSubDomains);
+}
- for (int j = i + 1; j < e; ++j) {
- auto &newDomain = children[domainIndex];
- newDomain.labels.push_back(labels[j]);
- newDomain.labels.back().domainIndex = ++domainIndex;
- }
+void QHstsCache::updateKnownHost(const QString &host, const QDateTime &expires,
+ bool includeSubDomains)
+{
+ if (!is_valid_domain_name(host))
+ return;
- break;
+ // HSTS is a per-host policy, regardless of protocol, port or any of the other
+ // details in an URL; so we only want the host part. QUrl::host handles
+ // IDNA 2003 (RFC3490) for us, as required by HSTS (RFC6797, section 10).
+ const HostName hostName(host);
+ const auto pos = knownHosts.find(hostName);
+ const QHstsPolicy newPolicy(expires, includeSubDomains, hostName.name);
+ if (pos == knownHosts.end()) {
+ // A new, previously unknown host.
+ if (newPolicy.isExpired()) {
+ // Nothing to do at all - we did not know this host previously,
+ // we do not have to - since its policy expired.
+ return;
}
- domainIndex = pos->domainIndex;
+ knownHosts.insert(pos, hostName, newPolicy);
+ return;
}
- Q_ASSERT(domainIndex > 0 && domainIndex < children.size());
- children[domainIndex].setHostPolicy(expires, includeSubDomains);
+ if (newPolicy.isExpired())
+ knownHosts.erase(pos);
+ else
+ *pos = std::move(newPolicy);
}
-bool QHstsCache::isKnownHost(const QUrl &originalUrl) const
+bool QHstsCache::isKnownHost(const QUrl &url) const
{
- if (!has_valid_domain_name(originalUrl))
+ if (!url.isValid() || !is_valid_domain_name(url.host()))
return false;
- QUrl url;
- url.setHost(originalUrl.host());
-
- QStringList labels(url.host().split(QLatin1Char('.')));
- std::reverse(labels.begin(), labels.end());
+ /*
+ RFC6797, 8.2. Known HSTS Host Domain Name Matching
+
+ * Superdomain Match
+ If a label-for-label match between an entire Known HSTS Host's
+ domain name and a right-hand portion of the given domain name
+ is found, then this Known HSTS Host's domain name is a
+ superdomain match for the given domain name. There could be
+ multiple superdomain matches for a given domain name.
+ * Congruent Match
+ If a label-for-label match between a Known HSTS Host's domain
+ name and the given domain name is found -- i.e., there are no
+ further labels to compare -- then the given domain name
+ congruently matches this Known HSTS Host.
+
+ We start from the congruent match, and then chop labels and dots and
+ proceed with superdomain match. While RFC6797 recommends to start from
+ superdomain, the result is the same - some valid policy will make a host
+ known.
+ */
+
+ bool superDomainMatch = false;
+ const QString hostNameAsString(url.host());
+ HostName nameToTest(static_cast<QStringRef>(&hostNameAsString));
+ while (nameToTest.fragment.size()) {
+ auto const pos = knownHosts.find(nameToTest);
+ if (pos != knownHosts.end()) {
+ if (pos.value().isExpired())
+ knownHosts.erase(pos);
+ else if (!superDomainMatch || pos.value().includesSubDomains())
+ return true;
+ }
- Q_ASSERT(children.size());
- size_type domainIndex = 0;
- for (int i = 0, e = labels.size(); i < e; ++i) {
- Q_ASSERT(domainIndex < children.size());
- const auto &subDomains = children[domainIndex].labels;
- auto pos = std::lower_bound(subDomains.begin(), subDomains.end(), labels[i]);
- if (pos == subDomains.end() || pos->label != labels[i])
- return false;
+ const int dot = nameToTest.fragment.indexOf(QLatin1Char('.'));
+ if (dot == -1)
+ break;
- Q_ASSERT(pos->domainIndex < children.size());
- domainIndex = pos->domainIndex;
- auto &domain = children[domainIndex];
- if (domain.validateHostPolicy() && (i + 1 == e || domain.includeSubDomains)) {
- /*
- RFC6797, 8.2. Known HSTS Host Domain Name Matching
-
- * Superdomain Match
- If a label-for-label match between an entire Known HSTS Host's
- domain name and a right-hand portion of the given domain name
- is found, then this Known HSTS Host's domain name is a
- superdomain match for the given domain name. There could be
- multiple superdomain matches for a given domain name.
- * Congruent Match
- If a label-for-label match between a Known HSTS Host's domain
- name and the given domain name is found -- i.e., there are no
- further labels to compare -- then the given domain name
- congruently matches this Known HSTS Host.
- */
-
- return true;
- }
+ nameToTest.fragment = nameToTest.fragment.mid(dot + 1);
+ superDomainMatch = true;
}
return false;
@@ -200,10 +180,12 @@ bool QHstsCache::isKnownHost(const QUrl &originalUrl) const
void QHstsCache::clear()
{
- children.resize(1);
- children[0].labels.clear();
- // Top-level is never known:
- Q_ASSERT(!children[0].isKnownHost);
+ knownHosts.clear();
+}
+
+QList<QHstsPolicy> QHstsCache::policies() const
+{
+ return knownHosts.values();
}
// The parser is quite simple: 'nextToken' knowns exactly what kind of tokens
diff --git a/src/network/access/qhsts_p.h b/src/network/access/qhsts_p.h
index f3d5da9d23..5d95f39b96 100644
--- a/src/network/access/qhsts_p.h
+++ b/src/network/access/qhsts_p.h
@@ -51,17 +51,16 @@
// We mean it.
//
+#include <QtNetwork/qhstspolicy.h>
+
#include <QtCore/qbytearray.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qstring.h>
#include <QtCore/qglobal.h>
-#include <QtCore/qvector.h>
#include <QtCore/qlist.h>
#include <QtCore/qpair.h>
#include <QtCore/qurl.h>
-
-#include <algorithm>
-#include <vector>
+#include <QtCore/qmap.h>
QT_BEGIN_NAMESPACE
@@ -69,72 +68,48 @@ class Q_AUTOTEST_EXPORT QHstsCache
{
public:
- QHstsCache();
-
void updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
const QUrl &url);
+ void updateFromPolicies(const QList<QHstsPolicy> &hosts);
void updateKnownHost(const QUrl &url, const QDateTime &expires,
bool includeSubDomains);
bool isKnownHost(const QUrl &url) const;
-
void clear();
-private:
-
- using size_type = std::vector<int>::size_type;
-
- struct DomainLabel
- {
- DomainLabel(const QString &name = QString()) : label(name), domainIndex(0) {}
+ QList<QHstsPolicy> policies() const;
- bool operator < (const DomainLabel &rhs) const
- { return label < rhs.label; }
+private:
- QString label;
- size_type domainIndex;
- };
+ void updateKnownHost(const QString &hostName, const QDateTime &expires,
+ bool includeSubDomains);
- struct Domain
+ struct HostName
{
- void setHostPolicy(const QDateTime &expiration, bool subs)
- {
- expirationTime = expiration;
- isKnownHost = expirationTime.isValid()
- && expirationTime > QDateTime::currentDateTimeUtc();
- includeSubDomains = subs;
- }
+ explicit HostName(const QString &n) : name(n) { }
+ explicit HostName(const QStringRef &r) : fragment(r) { }
- bool validateHostPolicy()
+ bool operator < (const HostName &rhs) const
{
- if (!isKnownHost)
- return false;
-
- if (expirationTime > QDateTime::currentDateTimeUtc())
- return true;
-
- isKnownHost = false;
- includeSubDomains = false;
- return false;
+ if (fragment.size()) {
+ if (rhs.fragment.size())
+ return fragment < rhs.fragment;
+ return fragment < QStringRef(&rhs.name);
+ }
+
+ if (rhs.fragment.size())
+ return QStringRef(&name) < rhs.fragment;
+ return name < rhs.name;
}
- bool isKnownHost = false;
- bool includeSubDomains = false;
- QDateTime expirationTime;
- std::vector<DomainLabel> labels;
+ // We use 'name' for a HostName object contained in our dictionary;
+ // we use 'fragment' only during lookup, when chopping the complete host
+ // name, removing subdomain names (such HostName object is 'transient', it
+ // must not outlive the original QString object.
+ QString name;
+ QStringRef fragment;
};
- /*
- Each Domain represents a DNS name or prefix thereof; each entry in its
- std::vector<DomainLabel> labels pairs the next fragment of a DNS name
- with the index into 'children' at which to find another Domain object.
- The root Domain, children[0], has top-level-domain DomainLabel entries,
- such as "com", "org" and "net"; the entry in 'children' at the index it
- pairs with "com" is the Domain entry for .com; if that has "example" in
- its labels, it'll be paired with the index of the entry in 'children'
- that represents example.com; from which, in turn, we can find the
- Domain object for www.example.com, and so on.
- */
- mutable std::vector<Domain> children;
+ mutable QMap<HostName, QHstsPolicy> knownHosts;
};
class Q_AUTOTEST_EXPORT QHstsHeaderParser
diff --git a/src/network/access/qhstspolicy.cpp b/src/network/access/qhstspolicy.cpp
new file mode 100644
index 0000000000..2cec587f4d
--- /dev/null
+++ b/src/network/access/qhstspolicy.cpp
@@ -0,0 +1,218 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhstspolicy.h"
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QHstsPolicy
+ \brief The QHstsPolicy class specifies that a host supports HTTP Strict Transport
+ Security policy (HSTS).
+ \since 5.9
+ \ingroup network
+ \inmodule QtNetwork
+
+ HSTS policy defines a period of time during which QNetworkAccessManager
+ should only access a host in a secure fashion. HSTS policy is defined by
+ RFC6797.
+
+ You can set expiry time and host name for this policy, and control whether it
+ applies to subdomains, either in the constructor or by calling setExpiry(),
+ setHost() and setIncludesSubdomains().
+
+ \sa QNetworkAccessManager::enableStrictTransportSecurity()
+*/
+
+class QHstsPolicyPrivate
+{
+public:
+ QUrl url;
+ QDateTime expiry;
+ bool includeSubDomains = false;
+
+ bool operator == (const QHstsPolicyPrivate &other) const
+ {
+ return url.host() == other.url.host() && expiry == other.expiry
+ && includeSubDomains == other.includeSubDomains;
+ }
+};
+
+/*!
+ Constructs an invalid (expired) policy with empty host name and subdomains
+ not included.
+*/
+QHstsPolicy::QHstsPolicy() : d(new QHstsPolicyPrivate)
+{
+}
+
+/*!
+ Constructs QHstsPolicy with \a expiry (in UTC); \a includeSubDomains parameter
+ defines if this policy must also include subdomains, \a host data is interpreted
+ according to \a mode.
+
+ \sa QUrl::setHost(), QUrl::ParsingMode
+*/
+QHstsPolicy::QHstsPolicy(const QDateTime &expiry, bool includeSubDomains, const QString &host,
+ QUrl::ParsingMode mode)
+ : d(new QHstsPolicyPrivate)
+{
+ d->url.setHost(host, mode);
+ d->expiry = expiry;
+ d->includeSubDomains = includeSubDomains;
+}
+
+/*!
+ Creates a copy of \a other object.
+*/
+QHstsPolicy::QHstsPolicy(const QHstsPolicy &other)
+ : d(new QHstsPolicyPrivate(*other.d))
+{
+}
+
+/*!
+ Destructor.
+*/
+QHstsPolicy::~QHstsPolicy()
+{
+}
+
+/*!
+ Copy-assignment operator, makes a copy of \a other.
+*/
+QHstsPolicy &QHstsPolicy::operator=(const QHstsPolicy &other)
+{
+ *d = *other.d;
+ return *this;
+}
+
+
+/*!
+ Move-assignment operator.
+*/
+QHstsPolicy &QHstsPolicy::operator=(QHstsPolicy &&other) Q_DECL_NOTHROW
+{
+ qSwap(d, other.d);
+ return *this;
+}
+
+/*!
+ Sets a host, \a host data is interpreted according to \a mode parameter.
+
+ \sa host(), QUrl::setHost(), QUrl::ParsingMode
+*/
+void QHstsPolicy::setHost(const QString &host, QUrl::ParsingMode mode)
+{
+ d->url.setHost(host, mode);
+}
+
+/*!
+ Returns a host for a given policy, formatted according to \a options.
+
+ \sa setHost(), QUrl::host(), QUrl::ComponentFormattingOptions
+*/
+QString QHstsPolicy::host(QUrl::ComponentFormattingOptions options) const
+{
+ return d->url.host(options);
+}
+
+/*!
+ Sets the expiration date for the policy (in UTC).
+
+ \sa expiry()
+*/
+void QHstsPolicy::setExpiry(const QDateTime &expiry)
+{
+ d->expiry = expiry;
+}
+
+/*!
+ Returns the expiration date for the policy (in UTC).
+
+ \sa setExpiry()
+*/
+QDateTime QHstsPolicy::expiry() const
+{
+ return d->expiry;
+}
+
+/*!
+ Includes or excludes subdomains for this policy.
+
+ \sa includeSubdomains()
+*/
+void QHstsPolicy::setIncludesSubDomains(bool include)
+{
+ d->includeSubDomains = include;
+}
+
+/*!
+ Returns \c true if this policy also includes subdomains.
+
+ \sa setIncludesSubDomains()
+ */
+bool QHstsPolicy::includesSubDomains() const
+{
+ return d->includeSubDomains;
+}
+
+/*!
+ Returns \c true if the two policies have the same host and expriration date
+ while agreeing on whether to include or exclude subdomains.
+*/
+bool QHstsPolicy::operator==(const QHstsPolicy &other) const
+{
+ return *d == *other.d;
+}
+
+/*!
+ Return \c true if this policy has a valid expiration date and this date
+ is greater than QDateTime::currentGetDateTimeUtc().
+
+ \sa setExpiry(), expiry()
+*/
+bool QHstsPolicy::isExpired() const
+{
+ return !d->expiry.isValid() || d->expiry <= QDateTime::currentDateTimeUtc();
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhstspolicy.h b/src/network/access/qhstspolicy.h
new file mode 100644
index 0000000000..4260ac278c
--- /dev/null
+++ b/src/network/access/qhstspolicy.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHSTSPOLICY_H
+#define QHSTSPOLICY_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHstsPolicyPrivate;
+class QDateTime;
+class QString;
+class Q_NETWORK_EXPORT QHstsPolicy
+{
+public:
+
+ QHstsPolicy();
+ QHstsPolicy(const QDateTime &expiry, bool includeSubDomains, const QString &host,
+ QUrl::ParsingMode mode = QUrl::DecodedMode);
+ QHstsPolicy(const QHstsPolicy &rhs);
+ QHstsPolicy &operator=(const QHstsPolicy &rhs);
+ QHstsPolicy &operator=(QHstsPolicy &&rhs) Q_DECL_NOTHROW;
+ ~QHstsPolicy();
+
+ void setHost(const QString &host, QUrl::ParsingMode mode = QUrl::DecodedMode);
+ QString host(QUrl::ComponentFormattingOptions options = QUrl::FullyDecoded) const;
+ void setExpiry(const QDateTime &expiry);
+ QDateTime expiry() const;
+ void setIncludesSubDomains(bool include);
+ bool includesSubDomains() const;
+
+ bool operator==(const QHstsPolicy &rhs) const;
+ bool isExpired() const;
+
+private:
+
+ QScopedPointer<QHstsPolicyPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QHSTSPOLICY_H
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 7aa8e61d26..19e9ecc265 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -45,6 +45,8 @@
#include "qnetworkcookie.h"
#include "qnetworkcookiejar.h"
#include "qabstractnetworkcache.h"
+#include "qhstspolicy.h"
+#include "qhsts_p.h"
#include "QtNetwork/qnetworksession.h"
#include "QtNetwork/private/qsharednetworksession_p.h"
@@ -742,6 +744,45 @@ bool QNetworkAccessManager::strictTransportSecurityEnabled() const
}
/*!
+ \since 5.9
+
+ Adds HTTP Strict Transport Security policies into HSTS cache.
+
+ \note An expired policy will remove a known host from the cache, if previously
+ present.
+
+ \note While processing HTTP responses, QNetworkAccessManager can also update
+ the HSTS cache, removing or updating exitsting policies or introducing new
+ known hosts. The current implementation thus is server-driven, client code
+ can provide QNetworkAccessManager with previously known or discovered
+ policies, but this information can be overridden by "Strict-Transport-Security"
+ response headers.
+
+ \sa addStrictTransportSecurityHosts(), QHstsPolicy
+*/
+
+void QNetworkAccessManager::addStrictTransportSecurityHosts(const QList<QHstsPolicy> &knownHosts)
+{
+ Q_D(QNetworkAccessManager);
+ d->stsCache.updateFromPolicies(knownHosts);
+}
+
+/*!
+ \since 5.9
+
+ Returns the list of HTTP Strict Transport Security policies. This list can
+ differ from what was initially set via addStrictTransportSecurityHosts() if
+ HSTS cache was updated from a "Strict-Transport-Security" response header.
+
+ \sa addStrictTransportSecurityHosts(), QHstsPolicy
+*/
+QList<QHstsPolicy> QNetworkAccessManager::strictTransportSecurityHosts() const
+{
+ Q_D(const QNetworkAccessManager);
+ return d->stsCache.policies();
+}
+
+/*!
Posts a request to obtain the network headers for \a request
and returns a new QNetworkReply object which will contain such headers.
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index 143407fb25..52769627f3 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -61,6 +61,7 @@ class QNetworkReply;
class QNetworkProxy;
class QNetworkProxyFactory;
class QSslError;
+class QHstsPolicy;
#ifndef QT_NO_BEARERMANAGEMENT
class QNetworkConfiguration;
#endif
@@ -123,6 +124,8 @@ public:
void enableStrictTransportSecurity();
void disableStrictTransportSecurity();
bool strictTransportSecurityEnabled() const;
+ void addStrictTransportSecurityHosts(const QList<QHstsPolicy> &knownHosts);
+ QList<QHstsPolicy> strictTransportSecurityHosts() const;
QNetworkReply *head(const QNetworkRequest &request);
QNetworkReply *get(const QNetworkRequest &request);
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index ce3b773c64..d72791c1f0 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -606,7 +606,7 @@ QString QNetworkDiskCachePrivate::uniqueFileName(const QUrl &url)
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(cleanUrl.toEncoded());
// convert sha1 to base36 form and return first 8 bytes for use as string
- QByteArray id = QByteArray::number(*(qlonglong*)hash.result().data(), 36).left(8);
+ const QByteArray id = QByteArray::number(*(qlonglong*)hash.result().constData(), 36).left(8);
// generates <one-char subdir>/<8-char filname.d>
uint code = (uint)id.at(id.length()-1) % 16;
QString pathFragment = QString::number(code, 16) + QLatin1Char('/')
diff --git a/src/network/bearer/qnetworkconfiguration.cpp b/src/network/bearer/qnetworkconfiguration.cpp
index d2feffb316..f1619ab7c0 100644
--- a/src/network/bearer/qnetworkconfiguration.cpp
+++ b/src/network/bearer/qnetworkconfiguration.cpp
@@ -343,7 +343,7 @@ int QNetworkConfiguration::connectTimeout() const
/*!
\since 5.9
- Sets the connect timeout of this configuration.
+ Sets the connect timeout of this configuration to \a timeout.
This allows control of the timeout used by \c QAbstractSocket
to establish a connection.
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index 88df65dbcb..193c990d8c 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -95,6 +95,38 @@ std::pair<OutputIt1, OutputIt2> separate_if(InputIt first, InputIt last, OutputI
}
return std::make_pair(dest1, dest2);
}
+
+int get_signal_index()
+{
+ static auto senderMetaObject = &QHostInfoResult::staticMetaObject;
+ static 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);
+}
+
+void emit_results_ready(const QHostInfo &hostInfo, const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObj)
+{
+ static const int signal_index = get_signal_index();
+ auto result = new QHostInfoResult(receiver, slotObj);
+ Q_CHECK_PTR(result);
+ const int nargs = 2;
+ auto types = reinterpret_cast<int *>(malloc(nargs * sizeof(int)));
+ Q_CHECK_PTR(types);
+ types[0] = QMetaType::type("void");
+ types[1] = QMetaType::type("QHostInfo");
+ auto args = reinterpret_cast<void **>(malloc(nargs * sizeof(void *)));
+ Q_CHECK_PTR(args);
+ args[0] = 0;
+ args[1] = QMetaType::create(types[1], &hostInfo);
+ Q_CHECK_PTR(args[1]);
+ auto metaCallEvent = new QMetaCallEvent(slotObj, nullptr, signal_index, nargs, types, args);
+ Q_CHECK_PTR(metaCallEvent);
+ qApp->postEvent(result, metaCallEvent);
+}
+
}
/*!
@@ -243,6 +275,67 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver,
}
/*!
+ \fn int QHostInfo::lookupHost(const QString &name, const QObject *receiver, PointerToMemberFunction function)
+
+ \since 5.9
+
+ \overload
+
+ Looks up the IP address(es) associated with host name \a name, and
+ returns an ID for the lookup. When the result of the lookup is
+ ready, the slot or signal \a function in \a receiver is called with
+ a QHostInfo argument. The QHostInfo object can then be inspected
+ to get the results of the lookup.
+
+ \note There is no guarantee on the order the signals will be emitted
+ if you start multiple requests with lookupHost().
+
+ \sa abortHostLookup(), addresses(), error(), fromName()
+*/
+
+/*!
+ \fn int QHostInfo::lookupHost(const QString &name, Functor functor)
+
+ \since 5.9
+
+ \overload
+
+ Looks up the IP address(es) associated with host name \a name, and
+ returns an ID for the lookup. When the result of the lookup is
+ ready, the \a functor is called with a QHostInfo argument. The
+ QHostInfo object can then be inspected to get the results of the
+ lookup.
+ \note There is no guarantee on the order the signals will be emitted
+ if you start multiple requests with lookupHost().
+
+ \sa abortHostLookup(), addresses(), error(), fromName()
+*/
+
+/*!
+ \fn int QHostInfo::lookupHost(const QString &name, const QObject *context, Functor functor)
+
+ \since 5.9
+
+ \overload
+
+ Looks up the IP address(es) associated with host name \a name, and
+ returns an ID for the lookup. When the result of the lookup is
+ ready, the \a functor is called with a QHostInfo argument. The
+ QHostInfo object can then be inspected to get the results of the
+ lookup.
+
+ If \a context is destroyed before the lookup completes, the
+ \a functor will not be called. The \a functor will be run in the
+ thread of \a context. The context's thread must have a running Qt
+ event loop.
+
+ \note There is no guarantee on the order the signals will be emitted
+ if you start multiple requests with lookupHost().
+
+ \sa abortHostLookup(), addresses(), error(), fromName()
+*/
+
+/*!
Aborts the host lookup with the ID \a id, as returned by lookupHost().
\sa lookupHost(), lookupId()
@@ -487,11 +580,66 @@ QString QHostInfo::localHostName()
\sa hostName()
*/
+int QHostInfo::lookupHostImpl(const QString &name,
+ const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObj)
+{
+#if defined QHOSTINFO_DEBUG
+ qDebug("QHostInfo::lookupHost(\"%s\", %p, %p)",
+ name.toLatin1().constData(), receiver, slotObj);
+#endif
+
+ if (!QAbstractEventDispatcher::instance(QThread::currentThread())) {
+ qWarning("QHostInfo::lookupHost() called with no event dispatcher");
+ return -1;
+ }
+
+ qRegisterMetaType<QHostInfo>();
+
+ int id = theIdCounter.fetchAndAddRelaxed(1); // generate unique ID
+
+ if (Q_UNLIKELY(name.isEmpty())) {
+ QHostInfo hostInfo(id);
+ hostInfo.setError(QHostInfo::HostNotFound);
+ hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given"));
+ emit_results_ready(hostInfo, receiver, slotObj);
+ return id;
+ }
+
+ QHostInfoLookupManager *manager = theHostInfoLookupManager();
+
+ if (Q_LIKELY(manager)) {
+ // the application is still alive
+ if (manager->cache.isEnabled()) {
+ // check cache first
+ bool valid = false;
+ QHostInfo info = manager->cache.get(name, &valid);
+ if (valid) {
+ info.setLookupId(id);
+ emit_results_ready(info, receiver, slotObj);
+ return id;
+ }
+ }
+
+ // cache is not enabled or it was not in the cache, do normal lookup
+ QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id, receiver, slotObj);
+ manager->scheduleLookup(runnable);
+ }
+ return id;
+}
+
QHostInfoRunnable::QHostInfoRunnable(const QString &hn, int i) : toBeLookedUp(hn), id(i)
{
setAutoDelete(true);
}
+QHostInfoRunnable::QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObj) :
+ toBeLookedUp(hn), id(i), resultEmitter(receiver, slotObj)
+{
+ setAutoDelete(true);
+}
+
// the QHostInfoLookupManager will at some point call this via a QThreadPool
void QHostInfoRunnable::run()
{
diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h
index 9b4a4853d9..4484d718bd 100644
--- a/src/network/kernel/qhostinfo.h
+++ b/src/network/kernel/qhostinfo.h
@@ -87,8 +87,71 @@ public:
static QString localHostName();
static QString localDomainName();
+#ifdef Q_QDOC
+ template<typename PointerToMemberFunction>
+ static int QHostInfo::lookupHost(const QString &name, const QObject *receiver,
+ PointerToMemberFunction function);
+ template<typename Functor>
+ static int QHostInfo::lookupHost(const QString &name, Functor functor);
+ template<typename Functor>
+ static int QHostInfo::lookupHost(const QString &name, const QObject *context, Functor functor);
+#else
+ // lookupHost to a QObject slot
+ template <typename Func>
+ static inline int lookupHost(const QString &name,
+ const typename QtPrivate::FunctionPointer<Func>::Object *receiver,
+ Func slot)
+ {
+ typedef QtPrivate::FunctionPointer<Func> SlotType;
+
+ typedef QtPrivate::FunctionPointer<void (*)(QHostInfo)> SignalType;
+ Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
+ "The slot requires more arguments than the signal provides.");
+ Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments,
+ typename SlotType::Arguments>::value),
+ "Signal and slot arguments are not compatible.");
+ Q_STATIC_ASSERT_X((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);
+ }
+
+ // 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, 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)
+ {
+ typedef QtPrivate::FunctionPointer<Func1> SlotType;
+
+ Q_STATIC_ASSERT_X(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>(slot);
+ return lookupHostImpl(name, context, slotObj);
+ }
+#endif // Q_QDOC
+
private:
QScopedPointer<QHostInfoPrivate> d;
+
+ static int lookupHostImpl(const QString &name,
+ const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObj);
};
QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h
index ba342bf533..dd46818a19 100644
--- a/src/network/kernel/qhostinfo_p.h
+++ b/src/network/kernel/qhostinfo_p.h
@@ -54,6 +54,7 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qcoreapplication.h"
#include "private/qcoreapplication_p.h"
+#include "private/qmetaobject_p.h"
#include "QtNetwork/qhostinfo.h"
#include "QtCore/qmutex.h"
#include "QtCore/qwaitcondition.h"
@@ -77,10 +78,47 @@ QT_BEGIN_NAMESPACE
class QHostInfoResult : public QObject
{
Q_OBJECT
+
+ QPointer<const QObject> receiver = nullptr;
+ QtPrivate::QSlotObjectBase *slotObj = nullptr;
+
+public:
+ QHostInfoResult() = default;
+ QHostInfoResult(const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj) :
+ receiver(receiver),
+ slotObj(slotObj)
+ {
+ connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this,
+ &QObject::deleteLater);
+ if (slotObj && receiver)
+ moveToThread(receiver->thread());
+ }
+
public Q_SLOTS:
inline void emitResultsReady(const QHostInfo &info)
{
- emit resultsReady(info);
+ if (slotObj) {
+ QHostInfo copy = info;
+ void *args[2] = { 0, reinterpret_cast<void *>(&copy) };
+ slotObj->call(const_cast<QObject*>(receiver.data()), args);
+ slotObj->destroyIfLastRef();
+ } else {
+ emit resultsReady(info);
+ }
+ }
+
+protected:
+ bool event(QEvent *event)
+ {
+ if (event->type() == QEvent::MetaCall) {
+ auto metaCallEvent = static_cast<QMetaCallEvent *>(event);
+ auto args = metaCallEvent->args();
+ auto hostInfo = reinterpret_cast<QHostInfo *>(args[1]);
+ emitResultsReady(*hostInfo);
+ deleteLater();
+ return true;
+ }
+ return QObject::event(event);
}
Q_SIGNALS:
@@ -154,6 +192,8 @@ class QHostInfoRunnable : public QRunnable
{
public:
QHostInfoRunnable(const QString &hn, int i);
+ QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObj);
void run() Q_DECL_OVERRIDE;
QString toBeLookedUp;
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index 0be8a7f79e..11e8fa6264 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -1636,10 +1636,6 @@ QList<QNetworkProxy> QNetworkProxyFactory::proxyForQuery(const QNetworkProxyQuer
}
#ifndef QT_NO_DEBUG_STREAM
-/*!
- \since 5.0
- Outputs a QNetworkProxy details to a debug stream
-*/
QDebug operator<<(QDebug debug, const QNetworkProxy &proxy)
{
QDebugStateSaver saver(debug);
@@ -1688,6 +1684,21 @@ QDebug operator<<(QDebug debug, const QNetworkProxy &proxy)
debug << '[' << scaps.join(QLatin1Char(' ')) << ']';
return debug;
}
+
+QDebug operator<<(QDebug debug, const QNetworkProxyQuery &proxyQuery)
+{
+ QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace()
+ << "ProxyQuery("
+ << "type: " << proxyQuery.queryType()
+ << ", protocol: " << proxyQuery.protocolTag()
+ << ", peerPort: " << proxyQuery.peerPort()
+ << ", peerHostName: " << proxyQuery.peerHostName()
+ << ", localPort: " << proxyQuery.localPort()
+ << ", url: " << proxyQuery.url()
+ << ')';
+ return debug;
+}
#endif
QT_END_NAMESPACE
diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h
index 8fcb7e33cf..8699c313e9 100644
--- a/src/network/kernel/qnetworkproxy.h
+++ b/src/network/kernel/qnetworkproxy.h
@@ -56,6 +56,8 @@ class QNetworkConfiguration;
class QNetworkProxyQueryPrivate;
class Q_NETWORK_EXPORT QNetworkProxyQuery
{
+ Q_GADGET
+
public:
enum QueryType {
TcpSocket,
@@ -65,6 +67,7 @@ public:
UrlRequest,
SctpServer
};
+ Q_ENUM(QueryType)
QNetworkProxyQuery();
explicit QNetworkProxyQuery(const QUrl &requestUrl, QueryType queryType = UrlRequest);
@@ -222,6 +225,7 @@ public:
#ifndef QT_NO_DEBUG_STREAM
Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkProxy &proxy);
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkProxyQuery &proxyQuery);
#endif
QT_END_NAMESPACE