summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp2
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp44
-rw-r--r--src/network/access/qnetworkreplyhttpimpl_p.h7
-rw-r--r--src/network/access/qnetworkrequest.cpp65
-rw-r--r--src/network/access/qnetworkrequest.h18
-rw-r--r--src/network/kernel/qnetworkproxy_mac.cpp7
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp15
-rw-r--r--src/network/ssl/qsslconfiguration.cpp78
-rw-r--r--src/network/ssl/qsslconfiguration.h5
-rw-r--r--src/network/ssl/qsslsocket.cpp47
-rw-r--r--src/network/ssl/qsslsocket.h16
-rw-r--r--src/network/ssl/qsslsocket_schannel.cpp131
-rw-r--r--src/network/ssl/qsslsocket_schannel_p.h1
13 files changed, 351 insertions, 85 deletions
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 76b95b5823..98c82c81ae 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -1240,7 +1240,7 @@ void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quin
// There is no way to enable SPDY/HTTP2 via a request, so we need to check
// the ssl configuration whether SPDY/HTTP2 is allowed here.
if (sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::ALPNProtocolHTTP2))
- request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
+ request.setAttribute(QNetworkRequest::Http2AllowedAttribute, true);
else if (sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::NextProtocolSpdy3_0))
request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true);
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 8ac81d1780..a13c2b144c 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -461,6 +461,7 @@ QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate()
, preMigrationDownloaded(-1)
, bytesDownloaded(0)
, bytesBuffered(0)
+ , transferTimeout(nullptr)
, downloadBufferReadPosition(0)
, downloadBufferCurrentSize(0)
, downloadZerocopyBuffer(nullptr)
@@ -777,7 +778,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
if (request.attribute(QNetworkRequest::SpdyAllowedAttribute).toBool())
httpRequest.setSPDYAllowed(true);
- if (request.attribute(QNetworkRequest::HTTP2AllowedAttribute).toBool())
+ if (request.attribute(QNetworkRequest::Http2AllowedAttribute).toBool())
httpRequest.setHTTP2Allowed(true);
if (request.attribute(QNetworkRequest::Http2DirectAttribute).toBool()) {
@@ -1067,6 +1068,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
if (!isHttpRedirectResponse()) {
buffer.append(d);
bytesDownloaded += d.size();
+ setupTransferTimeout();
}
bytesBuffered += d.size();
@@ -1280,15 +1282,15 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
}
q->setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu);
- const QVariant http2Allowed = request.attribute(QNetworkRequest::HTTP2AllowedAttribute);
+ const QVariant http2Allowed = request.attribute(QNetworkRequest::Http2AllowedAttribute);
const QVariant http2Direct = request.attribute(QNetworkRequest::Http2DirectAttribute);
if ((http2Allowed.isValid() && http2Allowed.toBool())
|| (http2Direct.isValid() && http2Direct.toBool())) {
- q->setAttribute(QNetworkRequest::HTTP2WasUsedAttribute, spdyWasUsed);
+ q->setAttribute(QNetworkRequest::Http2WasUsedAttribute, spdyWasUsed);
q->setAttribute(QNetworkRequest::SpdyWasUsedAttribute, false);
} else {
q->setAttribute(QNetworkRequest::SpdyWasUsedAttribute, spdyWasUsed);
- q->setAttribute(QNetworkRequest::HTTP2WasUsedAttribute, false);
+ q->setAttribute(QNetworkRequest::Http2WasUsedAttribute, false);
}
// reconstruct the HTTP header
@@ -1401,6 +1403,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadProgressSlot(qint64 bytesReceive
return;
bytesDownloaded = bytesReceived;
+ setupTransferTimeout();
downloadBufferCurrentSize = bytesReceived;
@@ -1857,7 +1860,6 @@ bool QNetworkReplyHttpImplPrivate::startWaitForSession(QSharedPointer<QNetworkSe
void QNetworkReplyHttpImplPrivate::_q_startOperation()
{
Q_Q(QNetworkReplyHttpImpl);
-
if (state == Working) // ensure this function is only being called once
return;
@@ -1897,6 +1899,7 @@ void QNetworkReplyHttpImplPrivate::_q_startOperation()
}
#endif // QT_NO_BEARERMANAGEMENT
+ setupTransferTimeout();
if (synchronous) {
state = Finished;
q_func()->setFinished(true);
@@ -2033,6 +2036,31 @@ void QNetworkReplyHttpImplPrivate::_q_bufferOutgoingData()
}
}
+void QNetworkReplyHttpImplPrivate::_q_transferTimedOut()
+{
+ Q_Q(QNetworkReplyHttpImpl);
+ q->abort();
+}
+
+void QNetworkReplyHttpImplPrivate::setupTransferTimeout()
+{
+ Q_Q(QNetworkReplyHttpImpl);
+ if (!transferTimeout) {
+ transferTimeout = new QTimer(q);
+ QObject::connect(transferTimeout, SIGNAL(timeout()),
+ q, SLOT(_q_transferTimedOut()),
+ Qt::QueuedConnection);
+ }
+ transferTimeout->stop();
+ if (request.transferTimeout()) {
+ transferTimeout->setSingleShot(true);
+ transferTimeout->setInterval(request.transferTimeout());
+ QMetaObject::invokeMethod(transferTimeout, "start",
+ Qt::QueuedConnection);
+
+ }
+}
+
#ifndef QT_NO_BEARERMANAGEMENT
void QNetworkReplyHttpImplPrivate::_q_networkSessionConnected()
{
@@ -2115,6 +2143,8 @@ void QNetworkReplyHttpImplPrivate::emitReplyUploadProgress(qint64 bytesSent, qin
if (isFinished)
return;
+ setupTransferTimeout();
+
if (!emitAllUploadProgressSignals) {
//choke signal emissions, except the first and last signals which are unconditional
if (uploadProgressSignalChoke.isValid()) {
@@ -2126,7 +2156,6 @@ void QNetworkReplyHttpImplPrivate::emitReplyUploadProgress(qint64 bytesSent, qin
uploadProgressSignalChoke.start();
}
}
-
emit q->uploadProgress(bytesSent, bytesTotal);
}
@@ -2159,7 +2188,8 @@ void QNetworkReplyHttpImplPrivate::_q_finished()
void QNetworkReplyHttpImplPrivate::finished()
{
Q_Q(QNetworkReplyHttpImpl);
-
+ if (transferTimeout)
+ transferTimeout->stop();
if (state == Finished || state == Aborted || state == WaitingForSession)
return;
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
index ef69ce0653..dec0c4c589 100644
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
@@ -59,6 +59,7 @@
#include "QtCore/qdatetime.h"
#include "QtCore/qsharedpointer.h"
#include "QtCore/qscopedpointer.h"
+#include "QtCore/qtimer.h"
#include "qatomic.h"
#include <QtNetwork/QNetworkCacheMetaData>
@@ -100,6 +101,7 @@ public:
Q_PRIVATE_SLOT(d_func(), void _q_cacheLoadReadyRead())
Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingData())
Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingDataFinished())
+ Q_PRIVATE_SLOT(d_func(), void _q_transferTimedOut())
#ifndef QT_NO_BEARERMANAGEMENT
Q_PRIVATE_SLOT(d_func(), void _q_networkSessionConnected())
Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed())
@@ -181,6 +183,9 @@ public:
void _q_cacheSaveDeviceAboutToClose();
+ void _q_transferTimedOut();
+ void setupTransferTimeout();
+
#ifndef QT_NO_BEARERMANAGEMENT
void _q_networkSessionConnected();
void _q_networkSessionFailed();
@@ -250,6 +255,8 @@ public:
qint64 bytesDownloaded;
qint64 bytesBuffered;
+ QTimer *transferTimeout;
+
// Only used when the "zero copy" style is used.
// Please note that the whole "zero copy" download buffer API is private right now. Do not use it.
qint64 downloadBufferReadPosition;
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 118fb6b1fb..e03d844af9 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -277,23 +277,30 @@ QT_BEGIN_NAMESPACE
Indicates whether the QNetworkAccessManager code is
allowed to use SPDY with this request. This applies only
to SSL requests, and depends on the server supporting SPDY.
+ Obsolete, use Http2 instead of Spdy.
\value SpdyWasUsedAttribute
Replies only, type: QMetaType::Bool
Indicates whether SPDY was used for receiving
- this reply.
+ this reply. Obsolete, use Http2 instead of Spdy.
- \value HTTP2AllowedAttribute
+ \value Http2AllowedAttribute
Requests only, type: QMetaType::Bool (default: false)
Indicates whether the QNetworkAccessManager code is
allowed to use HTTP/2 with this request. This applies
to SSL requests or 'cleartext' HTTP/2.
- \value HTTP2WasUsedAttribute
+ \value Http2WasUsedAttribute
Replies only, type: QMetaType::Bool (default: false)
Indicates whether HTTP/2 was used for receiving this reply.
(This value was introduced in 5.9.)
+ \value HTTP2AllowedAttribute
+ Obsolete alias for Http2AllowedAttribute.
+
+ \value HTTP2WasUsedAttribute
+ Obsolete alias for Http2WasUsedAttribute.
+
\value EmitAllUploadProgressSignalsAttribute
Requests only, type: QMetaType::Bool (default: false)
Indicates whether all upload signals should be emitted.
@@ -329,7 +336,7 @@ QT_BEGIN_NAMESPACE
server supports HTTP/2. The attribute works with SSL or 'cleartext'
HTTP/2. If a server turns out to not support HTTP/2, when HTTP/2 direct
was specified, QNetworkAccessManager gives up, without attempting to
- fall back to HTTP/1.1. If both HTTP2AllowedAttribute and
+ fall back to HTTP/1.1. If both Http2AllowedAttribute and
Http2DirectAttribute are set, Http2DirectAttribute takes priority.
(This value was introduced in 5.11.)
@@ -418,6 +425,18 @@ QT_BEGIN_NAMESPACE
based on some app-specific configuration.
*/
+/*!
+ \enum QNetworkRequest::TransferTimeoutConstant
+ \since 5.15
+
+ A constant that can be used for enabling transfer
+ timeouts with a preset value.
+
+ \value TransferTimeoutPreset The transfer timeout in milliseconds.
+ Used if setTimeout() is called
+ without an argument.
+ */
+
class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate
{
public:
@@ -428,6 +447,7 @@ public:
, sslConfiguration(0)
#endif
, maxRedirectsAllowed(maxRedirectCount)
+ , transferTimeout(0)
{ qRegisterMetaType<QNetworkRequest>(); }
~QNetworkRequestPrivate()
{
@@ -452,6 +472,7 @@ public:
#if QT_CONFIG(http)
h2Configuration = other.h2Configuration;
#endif
+ transferTimeout = other.transferTimeout;
}
inline bool operator==(const QNetworkRequestPrivate &other) const
@@ -465,6 +486,7 @@ public:
#if QT_CONFIG(http)
&& h2Configuration == other.h2Configuration
#endif
+ && transferTimeout == other.transferTimeout
;
// don't compare cookedHeaders
}
@@ -479,6 +501,7 @@ public:
#if QT_CONFIG(http)
QHttp2Configuration h2Configuration;
#endif
+ int transferTimeout;
};
/*!
@@ -902,6 +925,40 @@ void QNetworkRequest::setHttp2Configuration(const QHttp2Configuration &configura
{
d->h2Configuration = configuration;
}
+
+/*!
+ \since 5.15
+
+ Returns the timeout used for transfers, in milliseconds.
+
+ This timeout is zero if setTransferTimeout hasn't been
+ called, which means that the timeout is not used.
+
+ \sa setTransferTimeout
+*/
+int QNetworkRequest::transferTimeout()
+{
+ return d->transferTimeout;
+}
+
+/*!
+ \since 5.15
+
+ Sets \a timeout as the transfer timeout in milliseconds.
+
+ Transfers are aborted if no bytes are transferred before
+ the timeout expires. Zero means no timer is set. If no
+ argument is provided, the timeout is
+ QNetworkRequest::TransferTimeoutPreset. If this function
+ is not called, the timeout is disabled and has the
+ value zero.
+
+ \sa transferTimeout
+*/
+void QNetworkRequest::setTransferTimeout(int timeout)
+{
+ d->transferTimeout = timeout;
+}
#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC)
static QByteArray headerName(QNetworkRequest::KnownHeaders header)
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index e09ff8aaae..5d9969bd9b 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -89,12 +89,18 @@ public:
DownloadBufferAttribute, // internal
SynchronousRequestAttribute, // internal
BackgroundRequestAttribute,
+#if QT_DEPRECATED_SINCE(5, 15)
SpdyAllowedAttribute,
SpdyWasUsedAttribute,
- EmitAllUploadProgressSignalsAttribute,
+#endif // QT_DEPRECATED_SINCE(5, 15)
+ EmitAllUploadProgressSignalsAttribute = BackgroundRequestAttribute + 3,
FollowRedirectsAttribute,
- HTTP2AllowedAttribute,
- HTTP2WasUsedAttribute,
+ Http2AllowedAttribute,
+ Http2WasUsedAttribute,
+#if QT_DEPRECATED_SINCE(5, 15)
+ HTTP2AllowedAttribute Q_DECL_ENUMERATOR_DEPRECATED_X("Use Http2AllowedAttribute") = Http2AllowedAttribute,
+ HTTP2WasUsedAttribute Q_DECL_ENUMERATOR_DEPRECATED_X("Use Http2WasUsedAttribute"),
+#endif // QT_DEPRECATED_SINCE(5, 15)
OriginalContentLengthAttribute,
RedirectPolicyAttribute,
Http2DirectAttribute,
@@ -128,6 +134,9 @@ public:
UserVerifiedRedirectPolicy
};
+ enum TransferTimeoutConstant {
+ TransferTimeoutPreset = 30000
+ };
QNetworkRequest();
explicit QNetworkRequest(const QUrl &url);
@@ -179,6 +188,9 @@ public:
#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
QHttp2Configuration http2Configuration() const;
void setHttp2Configuration(const QHttp2Configuration &configuration);
+
+ int transferTimeout();
+ void setTransferTimeout(int timeout = TransferTimeoutPreset);
#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC)
private:
QSharedDataPointer<QNetworkRequestPrivate> d;
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp
index 92f91956b9..67fda24ea6 100644
--- a/src/network/kernel/qnetworkproxy_mac.cpp
+++ b/src/network/kernel/qnetworkproxy_mac.cpp
@@ -210,16 +210,14 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
QList<QNetworkProxy> result;
// obtain a dictionary to the proxy settings:
- CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
+ const QCFType<CFDictionaryRef> dict = SCDynamicStoreCopyProxies(NULL);
if (!dict) {
qWarning("QNetworkProxyFactory::systemProxyForQuery: SCDynamicStoreCopyProxies returned NULL");
return result; // failed
}
- if (isHostExcluded(dict, query.peerHostName())) {
- CFRelease(dict);
+ if (isHostExcluded(dict, query.peerHostName()))
return result; // no proxy for this host
- }
// is there a PAC enabled? If so, use it first.
CFNumberRef pacEnabled;
@@ -329,7 +327,6 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
result << https;
}
- CFRelease(dict);
return result;
}
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 9edabd7822..dd115c33dc 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -53,6 +53,8 @@
#include <qnetworkinterface.h>
#include <qoperatingsystemversion.h>
+#include <algorithm>
+
//#define QNATIVESOCKETENGINE_DEBUG
#if defined(QNATIVESOCKETENGINE_DEBUG)
# include <qstring.h>
@@ -1141,13 +1143,14 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
qint64 ret = -1;
int recvResult = 0;
DWORD flags;
- // We start at 1500 bytes (the MTU for Ethernet V2), which should catch
- // almost all uses (effective MTU for UDP under IPv4 is 1468), except
- // for localhost datagrams and those reassembled by the IP layer.
- char udpMessagePeekBuffer[1500];
- std::vector<WSABUF> buf;
+ // We increase the amount we peek by 2048 * 5 on each iteration
+ // Grabs most cases fast and early.
+ char udpMessagePeekBuffer[2048];
+ const int increments = 5;
+ QVarLengthArray<WSABUF, 10> buf;
for (;;) {
- buf.resize(buf.size() + 5, {sizeof(udpMessagePeekBuffer), udpMessagePeekBuffer});
+ buf.reserve(buf.size() + increments);
+ std::fill_n(std::back_inserter(buf), increments, WSABUF{sizeof(udpMessagePeekBuffer), udpMessagePeekBuffer});
flags = MSG_PEEK;
DWORD bytesRead = 0;
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index 7e92d3a526..a2e694ec92 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -631,11 +631,10 @@ QList<QSslCipher> QSslConfiguration::supportedCiphers()
Returns this connection's CA certificate database. The CA certificate
database is used by the socket during the handshake phase to
validate the peer's certificate. It can be modified prior to the
- handshake with setCaCertificates(), or with \l{QSslSocket}'s
- \l{QSslSocket::}{addCaCertificate()} and
- \l{QSslSocket::}{addCaCertificates()}.
+ handshake with setCaCertificates(), or with addCaCertificate() and
+ addCaCertificates().
- \sa setCaCertificates()
+ \sa setCaCertificates(), addCaCertificate(), addCaCertificates()
*/
QList<QSslCertificate> QSslConfiguration::caCertificates() const
{
@@ -652,7 +651,7 @@ QList<QSslCertificate> QSslConfiguration::caCertificates() const
that is not available (as is commonly the case on iOS), the default database
is empty.
- \sa caCertificates()
+ \sa caCertificates(), addCaCertificates(), addCaCertificate()
*/
void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certificates)
{
@@ -661,6 +660,72 @@ void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certific
}
/*!
+ Searches all files in the \a path for certificates encoded in the
+ specified \a format and adds them to this socket's CA certificate
+ database. \a path must be a file or a pattern matching one or more
+ files, as specified by \a syntax. Returns \c true if one or more
+ certificates are added to the socket's CA certificate database;
+ otherwise returns \c false.
+
+ The CA certificate database is used by the socket during the
+ handshake phase to validate the peer's certificate.
+
+ For more precise control, use addCaCertificate().
+
+ \sa addCaCertificate(), QSslCertificate::fromPath()
+*/
+bool QSslConfiguration::addCaCertificates(const QString &path, QSsl::EncodingFormat format,
+ QRegExp::PatternSyntax syntax)
+{
+ QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax);
+ if (certs.isEmpty())
+ return false;
+
+ d->caCertificates += certs;
+ return true;
+}
+
+/*!
+ \since 5.15
+
+ Adds \a certificate to this configuration's CA certificate database.
+ The certificate database must be set prior to the SSL handshake.
+ The CA certificate database is used by the socket during the
+ handshake phase to validate the peer's certificate.
+
+ \note The default configuration uses the system CA certificate database. If
+ that is not available (as is commonly the case on iOS), the default database
+ is empty.
+
+ \sa caCertificates(), setCaCertificates(), addCaCertificates()
+*/
+void QSslConfiguration::addCaCertificate(const QSslCertificate &certificate)
+{
+ d->caCertificates += certificate;
+ d->allowRootCertOnDemandLoading = false;
+}
+
+/*!
+ \since 5.15
+
+ Adds \a certificates to this configuration's CA certificate database.
+ The certificate database must be set prior to the SSL handshake.
+ The CA certificate database is used by the socket during the
+ handshake phase to validate the peer's certificate.
+
+ \note The default configuration uses the system CA certificate database. If
+ that is not available (as is commonly the case on iOS), the default database
+ is empty.
+
+ \sa caCertificates(), setCaCertificates(), addCaCertificate()
+*/
+void QSslConfiguration::addCaCertificates(const QList<QSslCertificate> &certificates)
+{
+ d->caCertificates += certificates;
+ d->allowRootCertOnDemandLoading = false;
+}
+
+/*!
\since 5.5
This function provides the CA certificate database
@@ -668,7 +733,8 @@ void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certific
returned by this function is used to initialize the database
returned by caCertificates() on the default QSslConfiguration.
- \sa caCertificates(), setCaCertificates(), defaultConfiguration()
+ \sa caCertificates(), setCaCertificates(), defaultConfiguration(),
+ addCaCertificate(), addCaCertificates()
*/
QList<QSslCertificate> QSslConfiguration::systemCaCertificates()
{
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index c25c2686de..247f3aecc9 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -131,6 +131,11 @@ public:
// Certificate Authority (CA) settings
QList<QSslCertificate> caCertificates() const;
void setCaCertificates(const QList<QSslCertificate> &certificates);
+ bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
+ QRegExp::PatternSyntax syntax = QRegExp::FixedString);
+ void addCaCertificate(const QSslCertificate &certificate);
+ void addCaCertificates(const QList<QSslCertificate> &certificates);
+
static QList<QSslCertificate> systemCaCertificates();
void setSslOption(QSsl::SslOption option, bool on);
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index e302aa1761..690251727d 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -139,10 +139,21 @@
before the handshake phase with setLocalCertificate() and
setPrivateKey().
\li The CA certificate database can be extended and customized with
- addCaCertificate(), addCaCertificates(), addDefaultCaCertificate(),
- addDefaultCaCertificates(), and QSslConfiguration::defaultConfiguration().setCaCertificates().
+ QSslConfiguration::addCaCertificate(),
+ QSslConfiguration::addCaCertificates().
\endlist
+ To extend the list of \e default CA certificates used by the SSL sockets
+ during the SSL handshake you must update the default configuration, as
+ in the snippet below:
+
+ \code
+ QList<QSslCertificate> certificates = getCertificates();
+ QSslConfiguration configuration = QSslConfiguration::defaultConfiguration();
+ configuration.addCaCertificates(certificates);
+ QSslConfiguration::setDefaultConfiguration(configuration);
+ \endcode
+
\note If available, root certificates on Unix (excluding \macos) will be
loaded on demand from the standard certificate directories. If you do not
want to load root certificates on demand, you need to call either
@@ -1384,6 +1395,10 @@ QList<QSslCipher> QSslSocket::supportedCiphers()
#endif // #if QT_DEPRECATED_SINCE(5, 5)
/*!
+ \deprecated
+
+ Use QSslConfiguration::addCaCertificates() instead.
+
Searches all files in the \a path for certificates encoded in the
specified \a format and adds them to this socket's CA certificate
database. \a path must be a file or a pattern matching one or more
@@ -1411,6 +1426,10 @@ bool QSslSocket::addCaCertificates(const QString &path, QSsl::EncodingFormat for
}
/*!
+ \deprecated
+
+ Use QSslConfiguration::addCaCertificate() instead.
+
Adds the \a certificate to this socket's CA certificate database.
The CA certificate database is used by the socket during the
handshake phase to validate the peer's certificate.
@@ -1427,6 +1446,10 @@ void QSslSocket::addCaCertificate(const QSslCertificate &certificate)
}
/*!
+ \deprecated
+
+ Use QSslConfiguration::addCaCertificates() instead.
+
Adds the \a certificates to this socket's CA certificate database.
The CA certificate database is used by the socket during the
handshake phase to validate the peer's certificate.
@@ -1489,6 +1512,10 @@ QList<QSslCertificate> QSslSocket::caCertificates() const
#endif // #if QT_DEPRECATED_SINCE(5, 5)
/*!
+ \deprecated
+
+ Use QSslConfiguration::addCaCertificates() on the default QSslConfiguration instead.
+
Searches all files in the \a path for certificates with the
specified \a encoding and adds them to the default CA certificate
database. \a path can be an explicit file, or it can contain
@@ -1498,8 +1525,8 @@ QList<QSslCertificate> QSslSocket::caCertificates() const
Each SSL socket's CA certificate database is initialized to the
default CA certificate database.
- \sa QSslConfiguration::caCertificates(), addCaCertificates(),
- addDefaultCaCertificate()
+ \sa QSslConfiguration::caCertificates(), QSslConfiguration::addCaCertificates(),
+ QSslConfiguration::addDefaultCaCertificate()
*/
bool QSslSocket::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat encoding,
QRegExp::PatternSyntax syntax)
@@ -1508,11 +1535,15 @@ bool QSslSocket::addDefaultCaCertificates(const QString &path, QSsl::EncodingFor
}
/*!
+ \deprecated
+
+ Use QSslConfiguration::addCaCertificate() on the default QSslConfiguration instead.
+
Adds \a certificate to the default CA certificate database. Each
SSL socket's CA certificate database is initialized to the default
CA certificate database.
- \sa QSslConfiguration::caCertificates(), addCaCertificates()
+ \sa QSslConfiguration::caCertificates(), QSslConfiguration::addCaCertificates()
*/
void QSslSocket::addDefaultCaCertificate(const QSslCertificate &certificate)
{
@@ -1520,11 +1551,15 @@ void QSslSocket::addDefaultCaCertificate(const QSslCertificate &certificate)
}
/*!
+ \deprecated
+
+ Use QSslConfiguration::addCaCertificates() on the default QSslConfiguration instead.
+
Adds \a certificates to the default CA certificate database. Each
SSL socket's CA certificate database is initialized to the default
CA certificate database.
- \sa QSslConfiguration::caCertificates(), addCaCertificates()
+ \sa QSslConfiguration::caCertificates(), QSslConfiguration::addCaCertificates()
*/
void QSslSocket::addDefaultCaCertificates(const QList<QSslCertificate> &certificates)
{
diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h
index 35943c7d7e..843e2d15f5 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -164,18 +164,22 @@ public:
#endif // QT_DEPRECATED_SINCE(5, 5)
// CA settings.
- bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
+#if QT_DEPRECATED_SINCE(5, 15)
+ QT_DEPRECATED_X("Use QSslConfiguration::addCaCertificates()") bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
QRegExp::PatternSyntax syntax = QRegExp::FixedString);
- void addCaCertificate(const QSslCertificate &certificate);
- void addCaCertificates(const QList<QSslCertificate> &certificates);
+ QT_DEPRECATED_X("Use QSslConfiguration::addCaCertificate()") void addCaCertificate(const QSslCertificate &certificate);
+ QT_DEPRECATED_X("Use QSslConfiguration::addCaCertificates()") void addCaCertificates(const QList<QSslCertificate> &certificates);
+#endif // QT_DEPRECATED_SINCE(5, 15)
#if QT_DEPRECATED_SINCE(5, 5)
QT_DEPRECATED_X("Use QSslConfiguration::setCaCertificates()") void setCaCertificates(const QList<QSslCertificate> &certificates);
QT_DEPRECATED_X("Use QSslConfiguration::caCertificates()") QList<QSslCertificate> caCertificates() const;
#endif // QT_DEPRECATED_SINCE(5, 5)
- static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
+#if QT_DEPRECATED_SINCE(5, 15)
+ QT_DEPRECATED static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
QRegExp::PatternSyntax syntax = QRegExp::FixedString);
- static void addDefaultCaCertificate(const QSslCertificate &certificate);
- static void addDefaultCaCertificates(const QList<QSslCertificate> &certificates);
+ QT_DEPRECATED static void addDefaultCaCertificate(const QSslCertificate &certificate);
+ QT_DEPRECATED static void addDefaultCaCertificates(const QList<QSslCertificate> &certificates);
+#endif // QT_DEPRECATED_SINCE(5, 15)
#if QT_DEPRECATED_SINCE(5, 5)
QT_DEPRECATED static void setDefaultCaCertificates(const QList<QSslCertificate> &certificates);
QT_DEPRECATED static QList<QSslCertificate> defaultCaCertificates();
diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp
index d7fb080b49..78d807106b 100644
--- a/src/network/ssl/qsslsocket_schannel.cpp
+++ b/src/network/ssl/qsslsocket_schannel.cpp
@@ -431,6 +431,53 @@ QByteArray createAlpnString(const QByteArrayList &nextAllowedProtocols)
return alpnString;
}
#endif // SUPPORTS_ALPN
+
+qint64 readToBuffer(QByteArray &buffer, QTcpSocket *plainSocket)
+{
+ Q_ASSERT(plainSocket);
+ static const qint64 shrinkCutoff = 1024 * 12;
+ static const qint64 defaultRead = 1024 * 16;
+ qint64 bytesRead = 0;
+
+ const auto toRead = std::min(defaultRead, plainSocket->bytesAvailable());
+ if (toRead > 0) {
+ const auto bufferSize = buffer.size();
+ buffer.reserve(bufferSize + toRead); // avoid growth strategy kicking in
+ buffer.resize(bufferSize + toRead);
+ bytesRead = plainSocket->read(buffer.data() + bufferSize, toRead);
+ buffer.resize(bufferSize + bytesRead);
+ // In case of excessive memory usage we shrink:
+ if (buffer.size() < shrinkCutoff && buffer.capacity() > defaultRead)
+ buffer.shrink_to_fit();
+ }
+
+ return bytesRead;
+}
+
+void retainExtraData(QByteArray &buffer, const SecBuffer &secBuffer)
+{
+ Q_ASSERT(secBuffer.BufferType == SECBUFFER_EXTRA);
+ if (int(secBuffer.cbBuffer) >= buffer.size())
+ return;
+
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl, "We got SECBUFFER_EXTRA, will retain %lu bytes", secBuffer.cbBuffer);
+#endif
+ std::move(buffer.end() - secBuffer.cbBuffer, buffer.end(), buffer.begin());
+ buffer.resize(secBuffer.cbBuffer);
+}
+
+qint64 checkIncompleteData(const SecBuffer &secBuffer)
+{
+ if (secBuffer.BufferType == SECBUFFER_MISSING) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl, "Need %lu more bytes.", secBuffer.cbBuffer);
+#endif
+ return secBuffer.cbBuffer;
+}
+ return 0;
+}
+
} // anonymous namespace
bool QSslSocketPrivate::s_loadRootCertsOnDemand = true;
@@ -619,8 +666,8 @@ bool QSslSocketBackendPrivate::acquireCredentialsHandle()
nullptr);
if (!chainContext) {
const QString message = isClient
- ? QSslSocket::tr("The certificate provided cannot be used for a client.")
- : QSslSocket::tr("The certificate provided cannot be used for a server.");
+ ? QSslSocket::tr("The certificate provided cannot be used for a client.")
+ : QSslSocket::tr("The certificate provided cannot be used for a server.");
setErrorAndEmit(QAbstractSocket::SocketError::SslInvalidUserDataError, message);
return false;
}
@@ -774,7 +821,11 @@ bool QSslSocketBackendPrivate::acceptContext()
Q_ASSERT(mode == QSslSocket::SslServerMode);
ULONG contextReq = getContextRequirements();
- intermediateBuffer += plainSocket->read(16384);
+ if (missingData > plainSocket->bytesAvailable())
+ return true;
+
+ missingData = 0;
+ readToBuffer(intermediateBuffer, plainSocket);
if (intermediateBuffer.isEmpty())
return true; // definitely need more data..
@@ -830,6 +881,7 @@ bool QSslSocketBackendPrivate::acceptContext()
if (status == SEC_E_INCOMPLETE_MESSAGE) {
// Need more data
+ missingData = checkIncompleteData(outBuffers[0]);
return true;
}
@@ -837,9 +889,9 @@ bool QSslSocketBackendPrivate::acceptContext()
// https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
// inBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to
// be stored.
- intermediateBuffer = intermediateBuffer.right(int(inBuffers[1].cbBuffer));
+ retainExtraData(intermediateBuffer, inBuffers[1]);
} else { /* No 'extra' data, message not incomplete */
- intermediateBuffer.clear();
+ intermediateBuffer.resize(0);
}
if (status != SEC_I_CONTINUE_NEEDED) {
@@ -865,11 +917,15 @@ bool QSslSocketBackendPrivate::performHandshake()
Q_ASSERT(schannelState == SchannelState::PerformHandshake);
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "Bytes available from socket:" << plainSocket->bytesAvailable();
- qCDebug(lcSsl) << "intermediateBuffer size:" << intermediateBuffer.size();
+ qCDebug(lcSsl, "Bytes available from socket: %lld", plainSocket->bytesAvailable());
+ qCDebug(lcSsl, "intermediateBuffer size: %d", intermediateBuffer.size());
#endif
- intermediateBuffer += plainSocket->read(16384);
+ if (missingData > plainSocket->bytesAvailable())
+ return true;
+
+ missingData = 0;
+ readToBuffer(intermediateBuffer, plainSocket);
if (intermediateBuffer.isEmpty())
return true; // no data, will fail
@@ -918,11 +974,10 @@ bool QSslSocketBackendPrivate::performHandshake()
// https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
// inputBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to
// be stored.
- intermediateBuffer = intermediateBuffer.right(int(inputBuffers[1].cbBuffer));
- } else {
+ retainExtraData(intermediateBuffer, inputBuffers[1]);
+ } else if (status != SEC_E_INCOMPLETE_MESSAGE) {
// Clear the buffer if we weren't asked for more data
- if (status != SEC_E_INCOMPLETE_MESSAGE)
- intermediateBuffer.clear();
+ intermediateBuffer.resize(0);
}
switch (status) {
case SEC_E_OK:
@@ -955,6 +1010,7 @@ bool QSslSocketBackendPrivate::performHandshake()
return true;
case SEC_E_INCOMPLETE_MESSAGE:
// Simply incomplete, wait for more data
+ missingData = checkIncompleteData(outBuffers[0]);
return true;
case SEC_E_ALGORITHM_MISMATCH:
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
@@ -1157,6 +1213,8 @@ void QSslSocketBackendPrivate::reset()
connectionEncrypted = false;
shutdown = false;
renegotiating = false;
+
+ missingData = 0;
}
void QSslSocketBackendPrivate::startClientEncryption()
@@ -1228,8 +1286,7 @@ void QSslSocketBackendPrivate::transmit()
fullMessage.resize(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer + inputBuffers[2].cbBuffer);
const qint64 bytesWritten = plainSocket->write(fullMessage);
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "Wrote" << bytesWritten << "of total"
- << fullMessage.length() << "bytes";
+ qCDebug(lcSsl, "Wrote %lld of total %d bytes", bytesWritten, fullMessage.length());
#endif
if (bytesWritten >= 0) {
totalBytesWritten += bytesWritten;
@@ -1254,36 +1311,32 @@ void QSslSocketBackendPrivate::transmit()
int totalRead = 0;
bool hadIncompleteData = false;
while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) {
- QByteArray ciphertext;
- if (intermediateBuffer.length()) {
+ if (missingData > plainSocket->bytesAvailable()) {
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "Restoring data from intermediateBuffer:"
- << intermediateBuffer.length() << "bytes";
+ qCDebug(lcSsl, "We're still missing %lld bytes, will check later.", missingData);
#endif
- ciphertext.swap(intermediateBuffer);
+ break;
}
- int initialLength = ciphertext.length();
- ciphertext += plainSocket->read(16384);
+
+ missingData = 0;
+ const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket);
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "Read" << ciphertext.length() - initialLength
- << "encrypted bytes from the socket";
+ qCDebug(lcSsl, "Read %lld encrypted bytes from the socket", bytesRead);
#endif
- if (ciphertext.length() == 0 || (hadIncompleteData && initialLength == ciphertext.length())) {
+ if (intermediateBuffer.length() == 0 || (hadIncompleteData && bytesRead == 0)) {
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << (hadIncompleteData ? "No new data received, leaving loop!"
- : "Nothing to decrypt, leaving loop!");
+ qCDebug(lcSsl, (hadIncompleteData ? "No new data received, leaving loop!"
+ : "Nothing to decrypt, leaving loop!"));
#endif
- if (ciphertext.length()) // We have data, it came from intermediateBuffer, swap back
- intermediateBuffer.swap(ciphertext);
break;
}
hadIncompleteData = false;
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "Total amount of bytes to decrypt:" << ciphertext.length();
+ qCDebug(lcSsl, "Total amount of bytes to decrypt: %d", intermediateBuffer.length());
#endif
SecBuffer dataBuffer[4]{
- createSecBuffer(ciphertext, SECBUFFER_DATA),
+ createSecBuffer(intermediateBuffer, SECBUFFER_DATA),
createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
@@ -1299,34 +1352,30 @@ void QSslSocketBackendPrivate::transmit()
if (dataBuffer[1].cbBuffer > 0) {
// It is always decrypted in-place.
// But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER.
- // The pointers in all of those still point into the 'ciphertext' byte array.
+ // The pointers in all of those still point into 'intermediateBuffer'.
buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer),
dataBuffer[1].cbBuffer);
totalRead += dataBuffer[1].cbBuffer;
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "Decrypted" << dataBuffer[1].cbBuffer
- << "bytes. New read buffer size:" << buffer.size();
+ qCDebug(lcSsl, "Decrypted %lu bytes. New read buffer size: %d",
+ dataBuffer[1].cbBuffer, buffer.size());
#endif
}
if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) {
// https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
// dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed,
// the rest need to be stored.
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "We've got excess data, moving it to the intermediate buffer:"
- << dataBuffer[3].cbBuffer << "bytes";
-#endif
- intermediateBuffer = ciphertext.right(int(dataBuffer[3].cbBuffer));
+ retainExtraData(intermediateBuffer, dataBuffer[3]);
+ } else {
+ intermediateBuffer.resize(0);
}
}
if (status == SEC_E_INCOMPLETE_MESSAGE) {
- // Need more data before we can decrypt.. to the buffer it goes!
+ missingData = checkIncompleteData(dataBuffer[0]);
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl, "We didn't have enough data to decrypt anything, will try again!");
#endif
- Q_ASSERT(intermediateBuffer.isEmpty());
- intermediateBuffer.swap(ciphertext);
// We try again, but if we don't get any more data then we leave
hadIncompleteData = true;
} else if (status == SEC_E_INVALID_HANDLE) {
diff --git a/src/network/ssl/qsslsocket_schannel_p.h b/src/network/ssl/qsslsocket_schannel_p.h
index 6ab200e1f9..a184deef49 100644
--- a/src/network/ssl/qsslsocket_schannel_p.h
+++ b/src/network/ssl/qsslsocket_schannel_p.h
@@ -145,6 +145,7 @@ private:
const CERT_CONTEXT *localCertContext = nullptr;
ULONG contextAttributes = 0;
+ qint64 missingData = 0;
bool renegotiating = false;
};