summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/http2/http2frames.cpp5
-rw-r--r--src/network/access/http2/http2protocol_p.h3
-rw-r--r--src/network/access/qhsts.cpp4
-rw-r--r--src/network/access/qhttp2configuration.cpp14
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp139
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp159
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h6
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp28
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp4
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h1
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp4
-rw-r--r--src/network/access/qnetworkcookie.cpp9
-rw-r--r--src/network/access/qnetworkcookie_p.h1
-rw-r--r--src/network/access/qnetworkdiskcache.cpp17
-rw-r--r--src/network/access/qnetworkreply.cpp4
-rw-r--r--src/network/access/qnetworkreplyfileimpl.cpp3
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp19
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp2
-rw-r--r--src/network/access/qnetworkreplywasmimpl.cpp119
-rw-r--r--src/network/access/qnetworkreplywasmimpl_p.h2
-rw-r--r--src/network/access/qnetworkrequest.cpp46
-rw-r--r--src/network/access/qspdyprotocolhandler.cpp3
-rw-r--r--src/network/android/jar/src/org/qtproject/qt5/android/network/QtNetwork.java3
-rw-r--r--src/network/doc/src/qtnetwork.qdoc4
-rw-r--r--src/network/doc/src/ssl.qdoc4
-rw-r--r--src/network/kernel/qauthenticator.cpp35
-rw-r--r--src/network/kernel/qauthenticator_p.h1
-rw-r--r--src/network/kernel/qdnslookup_unix.cpp31
-rw-r--r--src/network/kernel/qhostaddress.cpp2
-rw-r--r--src/network/kernel/qhostinfo_unix.cpp1
-rw-r--r--src/network/kernel/qnetworkinterface.cpp10
-rw-r--r--src/network/kernel/qnetworkinterface_unix.cpp2
-rw-r--r--src/network/kernel/qnetworkproxy.cpp4
-rw-r--r--src/network/kernel/qnetworkproxy_mac.cpp2
-rw-r--r--src/network/network.pro2
-rw-r--r--src/network/socket/qabstractsocket.cpp49
-rw-r--r--src/network/socket/qabstractsocket_p.h8
-rw-r--r--src/network/socket/qhttpsocketengine.cpp2
-rw-r--r--src/network/socket/qlocalserver.cpp2
-rw-r--r--src/network/socket/qlocalsocket.cpp2
-rw-r--r--src/network/socket/qlocalsocket.h5
-rw-r--r--src/network/socket/qlocalsocket_win.cpp7
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp5
-rw-r--r--src/network/socket/qsocks5socketengine.cpp6
-rw-r--r--src/network/ssl/qasn1element.cpp23
-rw-r--r--src/network/ssl/qdtls.cpp2
-rw-r--r--src/network/ssl/qdtls_openssl.cpp14
-rw-r--r--src/network/ssl/qocspresponse.cpp6
-rw-r--r--src/network/ssl/qsslcertificate.cpp9
-rw-r--r--src/network/ssl/qsslcertificate_openssl.cpp114
-rw-r--r--src/network/ssl/qsslcertificate_qt.cpp4
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp2
-rw-r--r--src/network/ssl/qsslerror.cpp2
-rw-r--r--src/network/ssl/qsslkey_openssl.cpp5
-rw-r--r--src/network/ssl/qsslsocket.cpp6
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp74
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h16
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp74
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h39
-rw-r--r--src/network/ssl/qsslsocket_p.h2
-rw-r--r--src/network/ssl/qsslsocket_schannel.cpp3
-rw-r--r--src/network/ssl/qsslsocket_schannel_p.h2
62 files changed, 822 insertions, 354 deletions
diff --git a/src/network/access/http2/http2frames.cpp b/src/network/access/http2/http2frames.cpp
index ce33505683..f1f2cdf8f4 100644
--- a/src/network/access/http2/http2frames.cpp
+++ b/src/network/access/http2/http2frames.cpp
@@ -233,7 +233,8 @@ quint32 Frame::dataSize() const
Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
quint32 size = payloadSize();
- if (const uchar pad = padding()) {
+ if (flags().testFlag(FrameFlag::PADDED)) {
+ const uchar pad = padding();
// + 1 one for a byte with padding number itself:
size -= pad + 1;
}
@@ -269,7 +270,7 @@ const uchar *Frame::dataBegin() const
return nullptr;
const uchar *src = &buffer[0] + frameHeaderSize;
- if (padding())
+ if (flags().testFlag(FrameFlag::PADDED))
++src;
if (priority())
diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h
index b0af5aa919..ed5f2bf561 100644
--- a/src/network/access/http2/http2protocol_p.h
+++ b/src/network/access/http2/http2protocol_p.h
@@ -133,9 +133,6 @@ enum Http2PredefinedParameters
maxPayloadSize = (1 << 24) - 1, // HTTP/2 6.5.2
defaultSessionWindowSize = 65535, // HTTP/2 6.5.2
- // Using 1000 (rather arbitrarily), just to
- // impose *some* upper limit:
- maxPeerConcurrentStreams = 1000,
maxConcurrentStreams = 100 // HTTP/2, 6.5.2
};
diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp
index 0cef0ad3dc..be7ef7ff58 100644
--- a/src/network/access/qhsts.cpp
+++ b/src/network/access/qhsts.cpp
@@ -364,8 +364,8 @@ quoted-pair = "\" CHAR
bool QHstsHeaderParser::parse(const QList<QPair<QByteArray, QByteArray>> &headers)
{
for (const auto &h : headers) {
- // We use '==' since header name was already 'trimmed' for us:
- if (h.first == "Strict-Transport-Security") {
+ // We compare directly because header name was already 'trimmed' for us:
+ if (h.first.compare("Strict-Transport-Security", Qt::CaseInsensitive) == 0) {
header = h.second;
// RFC6797, 8.1:
//
diff --git a/src/network/access/qhttp2configuration.cpp b/src/network/access/qhttp2configuration.cpp
index bd4318d4e9..e4126905b2 100644
--- a/src/network/access/qhttp2configuration.cpp
+++ b/src/network/access/qhttp2configuration.cpp
@@ -141,12 +141,12 @@ QHttp2Configuration::QHttp2Configuration(QHttp2Configuration &&other) noexcept
}
/*!
- Copy-assigns to this QHttp2Configuration.
+ Copy-assigns \a other to this QHttp2Configuration.
*/
QHttp2Configuration &QHttp2Configuration::operator=(const QHttp2Configuration &) = default;
/*!
- Move-assigns to this QHttp2Configuration.
+ Move-assigns \a other to this QHttp2Configuration.
*/
QHttp2Configuration &QHttp2Configuration::operator=(QHttp2Configuration &&) noexcept = default;
@@ -159,7 +159,7 @@ QHttp2Configuration::~QHttp2Configuration()
/*!
If \a enable is \c true, a remote server can potentially
- use server push to send reponses in advance.
+ use server push to send responses in advance.
\sa serverPushEnabled
*/
@@ -209,6 +209,8 @@ bool QHttp2Configuration::huffmanCompressionEnabled() const
Sets the window size for connection-level flow control.
\a size cannot be 0 and must not exceed 2147483647 octets.
+ Returns \c true on success, \c false otherwise.
+
\sa sessionReceiveWindowSize
*/
bool QHttp2Configuration::setSessionReceiveWindowSize(unsigned size)
@@ -236,6 +238,8 @@ unsigned QHttp2Configuration::sessionReceiveWindowSize() const
Sets the window size for stream-level flow control.
\a size cannot be 0 and must not exceed 2147483647 octets.
+ Returns \c true on success, \c false otherwise.
+
\sa streamReceiveWindowSize
*/
bool QHttp2Configuration::setStreamReceiveWindowSize(unsigned size)
@@ -265,6 +269,8 @@ unsigned QHttp2Configuration::streamReceiveWindowSize() const
\note While this \a size is required to be within a range between
16384 and 16777215 inclusive, the actual payload size in frames
that carry payload maybe be less than 16384.
+
+ Returns \c true on success, \c false otherwise.
*/
bool QHttp2Configuration::setMaxFrameSize(unsigned size)
{
@@ -278,7 +284,7 @@ bool QHttp2Configuration::setMaxFrameSize(unsigned size)
}
/*!
- The maximum payload size that HTTP/2 frames can
+ Returns the maximum payload size that HTTP/2 frames can
have. The default (initial) value is 16384 octets.
*/
unsigned QHttp2Configuration::maxFrameSize() const
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index 4c9e722166..39dd460881 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -264,7 +264,8 @@ void QHttp2ProtocolHandler::_q_uploadDataDestroyed(QObject *uploadData)
void QHttp2ProtocolHandler::_q_readyRead()
{
- _q_receiveReply();
+ if (!goingAway || activeStreams.size())
+ _q_receiveReply();
}
void QHttp2ProtocolHandler::_q_receiveReply()
@@ -272,6 +273,11 @@ void QHttp2ProtocolHandler::_q_receiveReply()
Q_ASSERT(m_socket);
Q_ASSERT(m_channel);
+ if (goingAway && activeStreams.isEmpty()) {
+ m_channel->close();
+ return;
+ }
+
while (!goingAway || activeStreams.size()) {
const auto result = frameReader.read(*m_socket);
switch (result) {
@@ -387,7 +393,8 @@ bool QHttp2ProtocolHandler::sendRequest()
initReplyFromPushPromise(message, key);
}
- const auto streamsToUse = std::min<quint32>(maxConcurrentStreams - activeStreams.size(),
+ const auto streamsToUse = std::min<quint32>(maxConcurrentStreams > quint32(activeStreams.size())
+ ? maxConcurrentStreams - quint32(activeStreams.size()) : 0,
requests.size());
auto it = requests.begin();
for (quint32 i = 0; i < streamsToUse; ++i) {
@@ -489,6 +496,10 @@ bool QHttp2ProtocolHandler::sendHEADERS(Stream &stream)
#ifndef QT_NO_NETWORKPROXY
useProxy = m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy;
#endif
+ if (stream.request().withCredentials()) {
+ m_connection->d_func()->createAuthorization(m_socket, stream.request());
+ stream.request().d->needResendWithCredentials = false;
+ }
const auto headers = build_headers(stream.request(), maxHeaderListSize, useProxy);
if (!headers.size()) // nothing fits into maxHeaderListSize
return false;
@@ -514,7 +525,7 @@ bool QHttp2ProtocolHandler::sendDATA(Stream &stream)
Q_ASSERT(replyPrivate);
auto slot = std::min<qint32>(sessionSendWindowSize, stream.sendWindow);
- while (!stream.data()->atEnd() && slot) {
+ while (replyPrivate->totallyUploadedData < request.contentLength() && slot) {
qint64 chunkSize = 0;
const uchar *src =
reinterpret_cast<const uchar *>(stream.data()->readPointer(slot, chunkSize));
@@ -1013,8 +1024,10 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
if (activeStreams.contains(streamID)) {
Stream &stream = activeStreams[streamID];
updateStream(stream, decoder.decodedHeader());
- // No DATA frames.
- if (continuedFrames[0].flags() & FrameFlag::END_STREAM) {
+ // Needs to resend the request; we should finish and delete the current stream
+ const bool needResend = stream.request().d->needResendWithCredentials;
+ // No DATA frames. Or needs to resend.
+ if (continuedFrames[0].flags() & FrameFlag::END_STREAM || needResend) {
finishStream(stream);
deleteActiveStream(stream.streamID);
}
@@ -1072,17 +1085,12 @@ bool QHttp2ProtocolHandler::acceptSetting(Http2::Settings identifier, quint32 ne
QMetaObject::invokeMethod(this, "resumeSuspendedStreams", Qt::QueuedConnection);
}
- if (identifier == Settings::MAX_CONCURRENT_STREAMS_ID) {
- if (newValue > maxPeerConcurrentStreams) {
- connectionError(PROTOCOL_ERROR, "SETTINGS invalid number of concurrent streams");
- return false;
- }
+ if (identifier == Settings::MAX_CONCURRENT_STREAMS_ID)
maxConcurrentStreams = newValue;
- }
if (identifier == Settings::MAX_FRAME_SIZE_ID) {
if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) {
- connectionError(PROTOCOL_ERROR, "SETTGINGS max frame size is out of range");
+ connectionError(PROTOCOL_ERROR, "SETTINGS max frame size is out of range");
return false;
}
maxFrameSize = newValue;
@@ -1102,7 +1110,7 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
Qt::ConnectionType connectionType)
{
const auto httpReply = stream.reply();
- const auto &httpRequest = stream.request();
+ auto &httpRequest = stream.request();
Q_ASSERT(httpReply || stream.state == Stream::remoteReserved);
if (!httpReply) {
@@ -1127,8 +1135,6 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
// moment and we are probably not done yet. So we extract url and set it
// here, if needed.
int statusCode = 0;
- QUrl redirectUrl;
-
for (const auto &pair : headers) {
const auto &name = pair.name;
auto value = pair.value;
@@ -1140,6 +1146,7 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
if (name == ":status") {
statusCode = value.left(3).toInt();
httpReply->setStatusCode(statusCode);
+ m_channel->lastStatus = statusCode; // Mostly useless for http/2, needed for auth
httpReplyPrivate->reasonPhrase = QString::fromLatin1(value.mid(4));
} else if (name == ":version") {
httpReplyPrivate->majorVersion = value.at(5) - '0';
@@ -1150,8 +1157,6 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
if (ok)
httpReply->setContentLength(length);
} else {
- if (name == "location")
- redirectUrl = QUrl::fromEncoded(value);
QByteArray binder(", ");
if (name == "set-cookie")
binder = "\n";
@@ -1159,21 +1164,91 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
}
}
- if (QHttpNetworkReply::isHttpRedirect(statusCode) && redirectUrl.isValid())
- httpReply->setRedirectUrl(redirectUrl);
+ const auto handleAuth = [&, this](const QByteArray &authField, bool isProxy) -> bool {
+ Q_ASSERT(httpReply);
+ const auto auth = authField.trimmed();
+ if (auth.startsWith("Negotiate") || auth.startsWith("NTLM")) {
+ // @todo: We're supposed to fall back to http/1.1:
+ // https://docs.microsoft.com/en-us/iis/get-started/whats-new-in-iis-10/http2-on-iis#when-is-http2-not-supported
+ // "Windows authentication (NTLM/Kerberos/Negotiate) is not supported with HTTP/2.
+ // In this case IIS will fall back to HTTP/1.1."
+ // Though it might be OK to ignore this. The server shouldn't let us connect with
+ // HTTP/2 if it doesn't support us using it.
+ } else if (!auth.isEmpty()) {
+ // Somewhat mimics parts of QHttpNetworkConnectionChannel::handleStatus
+ bool resend = false;
+ const bool authenticateHandled = m_connection->d_func()->handleAuthenticateChallenge(
+ m_socket, httpReply, isProxy, resend);
+ if (authenticateHandled && resend) {
+ httpReply->d_func()->eraseData();
+ // Add the request back in queue, we'll retry later now that
+ // we've gotten some username/password set on it:
+ httpRequest.d->needResendWithCredentials = true;
+ m_channel->spdyRequestsToSend.insert(httpRequest.priority(), stream.httpPair);
+ httpReply->d_func()->clearHeaders();
+ // If we have data we were uploading we need to reset it:
+ if (stream.data()) {
+ stream.data()->reset();
+ httpReplyPrivate->totallyUploadedData = 0;
+ }
+ return true;
+ } // else: Authentication failed or was cancelled
+ }
+ return false;
+ };
+
+ if (httpReply) {
+ // See Note further down. These statuses would in HTTP/1.1 be handled
+ // by QHttpNetworkConnectionChannel::handleStatus. But because h2 has
+ // multiple streams/requests in a single channel this structure does not
+ // map properly to that function.
+ if (httpReply->statusCode() == 401) {
+ const auto wwwAuth = httpReply->headerField("www-authenticate");
+ if (handleAuth(wwwAuth, false)) {
+ sendRST_STREAM(stream.streamID, CANCEL);
+ markAsReset(stream.streamID);
+ // The stream is finalized and deleted after returning
+ return;
+ } // else: errors handled later
+ } else if (httpReply->statusCode() == 407) {
+ const auto proxyAuth = httpReply->headerField("proxy-authenticate");
+ if (handleAuth(proxyAuth, true)) {
+ sendRST_STREAM(stream.streamID, CANCEL);
+ markAsReset(stream.streamID);
+ // The stream is finalized and deleted after returning
+ return;
+ } // else: errors handled later
+ }
+ }
+
+ if (QHttpNetworkReply::isHttpRedirect(statusCode) && httpRequest.isFollowRedirects()) {
+ QHttpNetworkConnectionPrivate::ParseRedirectResult result =
+ m_connection->d_func()->parseRedirectResponse(httpReply);
+ if (result.errorCode != QNetworkReply::NoError) {
+ auto errorString = m_connection->d_func()->errorDetail(result.errorCode, m_socket);
+ finishStreamWithError(stream, result.errorCode, errorString);
+ sendRST_STREAM(stream.streamID, INTERNAL_ERROR);
+ markAsReset(stream.streamID);
+ return;
+ }
+
+ if (result.redirectUrl.isValid())
+ httpReply->setRedirectUrl(result.redirectUrl);
+ }
if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress)
httpReplyPrivate->removeAutoDecompressHeader();
- if (QHttpNetworkReply::isHttpRedirect(statusCode)
- || statusCode == 401 || statusCode == 407) {
- // These are the status codes that can trigger uploadByteDevice->reset()
- // in QHttpNetworkConnectionChannel::handleStatus. Alas, we have no
- // single request/reply, we multiplex several requests and thus we never
- // simply call 'handleStatus'. If we have byte-device - we try to reset
- // it here, we don't (and can't) handle any error during reset operation.
- if (stream.data())
+ if (QHttpNetworkReply::isHttpRedirect(statusCode)) {
+ // Note: This status code can trigger uploadByteDevice->reset() in
+ // QHttpNetworkConnectionChannel::handleStatus. Alas, we have no single
+ // request/reply, we multiplex several requests and thus we never simply
+ // call 'handleStatus'. If we have a byte-device - we try to reset it
+ // here, we don't (and can't) handle any error during reset operation.
+ if (stream.data()) {
stream.data()->reset();
+ httpReplyPrivate->totallyUploadedData = 0;
+ }
}
if (connectionType == Qt::DirectConnection)
@@ -1242,10 +1317,12 @@ void QHttp2ProtocolHandler::finishStream(Stream &stream, Qt::ConnectionType conn
if (stream.data())
stream.data()->disconnect(this);
- if (connectionType == Qt::DirectConnection)
- emit httpReply->finished();
- else
- QMetaObject::invokeMethod(httpReply, "finished", connectionType);
+ if (!stream.request().d->needResendWithCredentials) {
+ if (connectionType == Qt::DirectConnection)
+ emit httpReply->finished();
+ else
+ QMetaObject::invokeMethod(httpReply, "finished", connectionType);
+ }
}
qCDebug(QT_HTTP2) << "stream" << stream.streamID << "closed";
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index b0116319b0..20b8d446ba 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -462,7 +462,12 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
else
channels[i].authMethod = priv->method;
- if (priv->phase == QAuthenticatorPrivate::Done) {
+ if (priv->phase == QAuthenticatorPrivate::Done ||
+ (priv->phase == QAuthenticatorPrivate::Start
+ && priv->method == QAuthenticatorPrivate::Ntlm)) {
+ if (priv->phase == QAuthenticatorPrivate::Start)
+ priv->phase = QAuthenticatorPrivate::Phase1;
+
pauseConnection();
if (!isProxy) {
if (channels[i].authenticationCredentialsSent) {
@@ -510,8 +515,8 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
channels[i].authenticator = QAuthenticator();
// authentication is cancelled, send the current contents to the user.
- emit channels[i].reply->headerChanged();
- emit channels[i].reply->readyRead();
+ emit reply->headerChanged();
+ emit reply->readyRead();
QNetworkReply::NetworkError errorCode =
isProxy
? QNetworkReply::ProxyAuthenticationRequiredError
@@ -528,10 +533,23 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
return false;
}
-QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socket, QHttpNetworkReply *reply)
+// Used by the HTTP1 code-path
+QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socket,
+ QHttpNetworkReply *reply)
+{
+ ParseRedirectResult result = parseRedirectResponse(reply);
+ if (result.errorCode != QNetworkReply::NoError) {
+ emitReplyError(socket, reply, result.errorCode);
+ return {};
+ }
+ return std::move(result.redirectUrl);
+}
+
+QHttpNetworkConnectionPrivate::ParseRedirectResult
+QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply)
{
if (!reply->request().isFollowRedirects())
- return QUrl();
+ return {{}, QNetworkReply::NoError};
QUrl redirectUrl;
const QList<QPair<QByteArray, QByteArray> > fields = reply->header();
@@ -542,17 +560,13 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
}
}
- // If the location url is invalid/empty, we emit ProtocolUnknownError
- if (!redirectUrl.isValid()) {
- emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
- return QUrl();
- }
+ // If the location url is invalid/empty, we return ProtocolUnknownError
+ if (!redirectUrl.isValid())
+ return {{}, QNetworkReply::ProtocolUnknownError};
// Check if we have exceeded max redirects allowed
- if (reply->request().redirectCount() <= 0) {
- emitReplyError(socket, reply, QNetworkReply::TooManyRedirectsError);
- return QUrl();
- }
+ if (reply->request().redirectCount() <= 0)
+ return {{}, QNetworkReply::TooManyRedirectsError};
// Resolve the URL if it's relative
if (redirectUrl.isRelative())
@@ -573,8 +587,7 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
if (priorUrl.host() != redirectUrl.host()
|| priorUrl.scheme() != redirectUrl.scheme()
|| priorUrl.port() != redirectUrl.port()) {
- emitReplyError(socket, reply, QNetworkReply::InsecureRedirectError);
- return QUrl();
+ return {{}, QNetworkReply::InsecureRedirectError};
}
break;
case QNetworkRequest::UserVerifiedRedirectPolicy:
@@ -583,10 +596,9 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
Q_ASSERT(!"Unexpected redirect policy");
}
} else {
- emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
- return QUrl();
+ return {{}, QNetworkReply::ProtocolUnknownError};
}
- return redirectUrl;
+ return {std::move(redirectUrl), QNetworkReply::NoError};
}
void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
@@ -597,10 +609,20 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
// Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
if (channels[i].authMethod != QAuthenticatorPrivate::None) {
- if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField("Authorization").isEmpty()) || channels[i].lastStatus == 401) {
- QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
- if (priv && priv->method != QAuthenticatorPrivate::None) {
- QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), request.url().host());
+ QAuthenticatorPrivate *priv =
+ QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
+ if (priv && priv->method != QAuthenticatorPrivate::None) {
+ const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
+ || priv->method == QAuthenticatorPrivate::Negotiate;
+ const bool authNeeded = channels[i].lastStatus == 401;
+ const bool ntlmNegoOk = ntlmNego && authNeeded
+ && (priv->phase != QAuthenticatorPrivate::Done
+ || !channels[i].authenticationCredentialsSent);
+ const bool otherOk =
+ !ntlmNego && (authNeeded || request.headerField("Authorization").isEmpty());
+ if (ntlmNegoOk || otherOk) {
+ QByteArray response = priv->calculateResponse(
+ request.methodName(), request.uri(false), request.url().host());
request.setHeaderField("Authorization", response);
channels[i].authenticationCredentialsSent = true;
}
@@ -610,10 +632,19 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
#if QT_CONFIG(networkproxy)
// Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
if (channels[i].proxyAuthMethod != QAuthenticatorPrivate::None) {
- if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
- QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
- if (priv && priv->method != QAuthenticatorPrivate::None) {
- QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), networkProxy.hostName());
+ QAuthenticatorPrivate *priv =
+ QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
+ if (priv && priv->method != QAuthenticatorPrivate::None) {
+ const bool ntlmNego = channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm
+ || channels[i].proxyAuthMethod == QAuthenticatorPrivate::Negotiate;
+ const bool proxyAuthNeeded = channels[i].lastStatus == 407;
+ const bool ntlmNegoOk = ntlmNego && proxyAuthNeeded
+ && (priv->phase != QAuthenticatorPrivate::Done
+ || !channels[i].proxyCredentialsSent);
+ const bool otherOk = !ntlmNego;
+ if (ntlmNegoOk || otherOk) {
+ QByteArray response = priv->calculateResponse(
+ request.methodName(), request.uri(false), networkProxy.hostName());
request.setHeaderField("Proxy-Authorization", response);
channels[i].proxyCredentialsSent = true;
}
@@ -1077,8 +1108,10 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
case QHttpNetworkConnection::ConnectionTypeHTTP2Direct:
case QHttpNetworkConnection::ConnectionTypeHTTP2:
case QHttpNetworkConnection::ConnectionTypeSPDY: {
- if (channels[0].spdyRequestsToSend.isEmpty() && channels[0].switchedToHttp2)
+ if (channels[0].spdyRequestsToSend.isEmpty() && !channels[0].reply
+ && highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty()) {
return;
+ }
if (networkLayerState == IPv4)
channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol;
@@ -1118,31 +1151,50 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
int normalRequests = queuedRequests - preConnectRequests;
neededOpenChannels = qMax(normalRequests, preConnectRequests);
}
+
+ if (neededOpenChannels <= 0)
+ return;
+
+ QQueue<int> channelsToConnect;
+
+ // use previously used channels first
for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
- bool connectChannel = false;
- if (channels[i].socket) {
- if ((channels[i].socket->state() == QAbstractSocket::ConnectingState)
- || (channels[i].socket->state() == QAbstractSocket::HostLookupState)
- || channels[i].pendingEncrypt) // pendingEncrypt == "EncryptingState"
- neededOpenChannels--;
-
- if (neededOpenChannels <= 0)
- break;
- if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState))
- connectChannel = true;
- } else { // not previously used channel
- connectChannel = true;
+ if (!channels[i].socket)
+ continue;
+
+ if ((channels[i].socket->state() == QAbstractSocket::ConnectingState)
+ || (channels[i].socket->state() == QAbstractSocket::HostLookupState)
+ || channels[i].pendingEncrypt) { // pendingEncrypt == "EncryptingState"
+ neededOpenChannels--;
+ continue;
}
- if (connectChannel) {
- if (networkLayerState == IPv4)
- channels[i].networkLayerPreference = QAbstractSocket::IPv4Protocol;
- else if (networkLayerState == IPv6)
- channels[i].networkLayerPreference = QAbstractSocket::IPv6Protocol;
- channels[i].ensureConnection();
+ if (!channels[i].reply && !channels[i].isSocketBusy()
+ && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) {
+ channelsToConnect.enqueue(i);
neededOpenChannels--;
}
}
+
+ // use other channels
+ for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
+ if (channels[i].socket)
+ continue;
+
+ channelsToConnect.enqueue(i);
+ neededOpenChannels--;
+ }
+
+ while (!channelsToConnect.isEmpty()) {
+ const int channel = channelsToConnect.dequeue();
+
+ if (networkLayerState == IPv4)
+ channels[channel].networkLayerPreference = QAbstractSocket::IPv4Protocol;
+ else if (networkLayerState == IPv6)
+ channels[channel].networkLayerPreference = QAbstractSocket::IPv6Protocol;
+
+ channels[channel].ensureConnection();
+ }
}
@@ -1251,11 +1303,10 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(const QHostInfo &info)
emitReplyError(channels[0].socket, currentReply, QNetworkReply::HostNotFoundError);
}
} else {
- // Should not happen: we start a host lookup before sending a request,
- // so it's natural to have requests either in SPDY/HTTP/2 queue,
- // or in low/high priority queues.
- qWarning("QHttpNetworkConnectionPrivate::_q_hostLookupFinished"
- " could not de-queue request, failed to report HostNotFoundError");
+ // We can end up here if a request has been aborted or otherwise failed (e.g. timeout)
+ // before the host lookup was finished.
+ qDebug("QHttpNetworkConnectionPrivate::_q_hostLookupFinished"
+ " could not de-queue request, failed to report HostNotFoundError");
networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
}
}
@@ -1425,7 +1476,7 @@ void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
d->networkProxy = networkProxy;
// update the authenticator
if (!d->networkProxy.user().isEmpty()) {
- for (int i = 0; i < d->activeChannelCount; ++i) {
+ for (int i = 0; i < d->channelCount; ++i) {
d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
}
@@ -1441,7 +1492,7 @@ QNetworkProxy QHttpNetworkConnection::cacheProxy() const
void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
{
Q_D(QHttpNetworkConnection);
- for (int i = 0; i < d->activeChannelCount; ++i)
+ for (int i = 0; i < d->channelCount; ++i)
d->channels[i].setProxy(networkProxy);
}
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 845b55bc5d..5fed62fc97 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -273,6 +273,12 @@ public:
void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
+ struct ParseRedirectResult {
+ QUrl redirectUrl;
+ QNetworkReply::NetworkError errorCode;
+ };
+ ParseRedirectResult parseRedirectResponse(QHttpNetworkReply *reply);
+ // Used by the HTTP1 code-path
QUrl parseRedirectResponse(QAbstractSocket *socket, QHttpNetworkReply *reply);
#ifndef QT_NO_NETWORKPROXY
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 9325787d5f..7620ca1647 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -697,17 +697,19 @@ bool QHttpNetworkConnectionChannel::resetUploadData()
//this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
return false;
}
- QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
- if (!uploadByteDevice)
- return true;
-
- if (uploadByteDevice->reset()) {
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
+ || switchedToHttp2) {
+ // The else branch doesn't make any sense for HTTP/2, since 1 channel is multiplexed into
+ // many streams. And having one stream fail to reset upload data should not completely close
+ // the channel. Handled in the http2 protocol handler.
+ } else if (QNonContiguousByteDevice *uploadByteDevice = request.uploadByteDevice()) {
+ if (!uploadByteDevice->reset()) {
+ connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
+ return false;
+ }
written = 0;
- return true;
- } else {
- connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
- return false;
}
+ return true;
}
#ifndef QT_NO_NETWORKPROXY
@@ -1005,7 +1007,6 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
&& switchedToHttp2)) {
auto h2Handler = static_cast<QHttp2ProtocolHandler *>(protocolHandler.data());
h2Handler->handleConnectionClosure();
- protocolHandler.reset();
}
}
return;
@@ -1134,9 +1135,12 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
for (int a = 0; a < spdyPairs.count(); ++a) {
// emit error for all replies
QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
+ currentReply->d_func()->errorString = errorString;
+ currentReply->d_func()->httpErrorCode = errorCode;
Q_ASSERT(currentReply);
emit currentReply->finishedWithError(errorCode, errorString);
}
+ spdyRequestsToSend.clear();
}
// send the next request
@@ -1282,6 +1286,10 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
// we call setSpdyWasUsed(true) on the replies in the SPDY handler when the request is sent
if (spdyRequestsToSend.count() > 0) {
+ // Similar to HTTP/1.1 counterpart below:
+ const auto &pairs = spdyRequestsToSend.values(); // (request, reply)
+ const auto &pair = pairs.first();
+ emit pair.second->encrypted();
// In case our peer has sent us its settings (window size, max concurrent streams etc.)
// let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 0ae2498bed..a4e4c673a6 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -65,6 +65,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
withCredentials(other.withCredentials),
ssl(other.ssl),
preConnect(other.preConnect),
+ needResendWithCredentials(other.needResendWithCredentials),
redirectCount(other.redirectCount),
redirectPolicy(other.redirectPolicy),
peerVerifyName(other.peerVerifyName)
@@ -92,7 +93,8 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
&& (ssl == other.ssl)
&& (preConnect == other.preConnect)
&& (redirectPolicy == other.redirectPolicy)
- && (peerVerifyName == other.peerVerifyName);
+ && (peerVerifyName == other.peerVerifyName)
+ && (needResendWithCredentials == other.needResendWithCredentials);
}
QByteArray QHttpNetworkRequest::methodName() const
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index 038cb97b31..e23d874f19 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -183,6 +183,7 @@ public:
bool withCredentials;
bool ssl;
bool preConnect;
+ bool needResendWithCredentials = false;
int redirectCount;
QNetworkRequest::RedirectPolicy redirectPolicy;
QString peerVerifyName;
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index fece765d39..a10fe9e3fe 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -755,7 +755,7 @@ bool QNetworkAccessManager::isStrictTransportSecurityEnabled() const
store is enabled, these policies will be preserved in the store. In case both
cache and store contain the same known hosts, policies from cache are considered
to be more up-to-date (and thus will overwrite the previous values in the store).
- If this behavior is undesired, enable HSTS store before enabling Strict Tranport
+ If this behavior is undesired, enable HSTS store before enabling Strict Transport
Security. By default, the persistent store of HSTS policies is disabled.
\sa isStrictTransportSecurityStoreEnabled(), setStrictTransportSecurityEnabled(),
@@ -1411,7 +1411,6 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
bool isLocalFile = req.url().isLocalFile();
QString scheme = req.url().scheme();
-#ifndef Q_OS_WASM
// fast path for GET on file:// URLs
// The QNetworkAccessFileBackend will right now only be used for PUT
@@ -1446,7 +1445,6 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
return reply;
}
}
-#endif
QNetworkRequest request = req;
if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() &&
outgoingData && !outgoingData->isSequential()) {
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index 47f6112b22..21950b18a5 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -180,7 +180,8 @@ bool QNetworkCookie::operator==(const QNetworkCookie &other) const
d->domain == other.d->domain &&
d->path == other.d->path &&
d->secure == other.d->secure &&
- d->comment == other.d->comment;
+ d->comment == other.d->comment &&
+ d->sameSite == other.d->sameSite;
}
/*!
@@ -459,6 +460,10 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const
result += "; secure";
if (isHttpOnly())
result += "; HttpOnly";
+ if (!d->sameSite.isEmpty()) {
+ result += "; SameSite=";
+ result += d->sameSite;
+ }
if (!isSessionCookie()) {
result += "; expires=";
result += QLocale::c().toString(d->expirationDate.toUTC(),
@@ -991,6 +996,8 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
cookie.setSecure(true);
} else if (field.first == "httponly") {
cookie.setHttpOnly(true);
+ } else if (field.first == "samesite") {
+ cookie.d->sameSite = field.second;
} else {
// ignore unknown fields in the cookie (RFC6265 section 5.2, rule 6)
}
diff --git a/src/network/access/qnetworkcookie_p.h b/src/network/access/qnetworkcookie_p.h
index 13538ad243..e30e611cf5 100644
--- a/src/network/access/qnetworkcookie_p.h
+++ b/src/network/access/qnetworkcookie_p.h
@@ -66,6 +66,7 @@ public:
QString domain;
QString path;
QString comment;
+ QByteArray sameSite;
QByteArray name;
QByteArray value;
bool secure;
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index b30d1c9664..2a7f16637d 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -271,14 +271,12 @@ void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
Q_ASSERT(!fileName.isEmpty());
if (QFile::exists(fileName)) {
- if (!QFile::remove(fileName)) {
+ if (!removeFile(fileName)) {
qWarning() << "QNetworkDiskCache: couldn't remove the cache file " << fileName;
return;
}
}
- if (currentCacheSize > 0)
- currentCacheSize += 1024 + cacheItem->size();
currentCacheSize = q->expire();
if (!cacheItem->file) {
QString templateName = tmpCacheFileName();
@@ -417,18 +415,7 @@ QIODevice *QNetworkDiskCache::data(const QUrl &url)
buffer->setData(d->lastItem.data.data());
} else {
buffer.reset(new QBuffer);
- // ### verify that QFile uses the fd size and not the file name
- qint64 size = file->size() - file->pos();
- const uchar *p = nullptr;
-#if !defined(Q_OS_INTEGRITY)
- p = file->map(file->pos(), size);
-#endif
- if (p) {
- buffer->setData((const char *)p, size);
- file.take()->setParent(buffer.data());
- } else {
- buffer->setData(file->readAll());
- }
+ buffer->setData(file->readAll());
}
}
buffer->open(QBuffer::ReadOnly);
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index 30e4e290ea..dadea58080 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -76,7 +76,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
itself.
QNetworkReply is a sequential-access QIODevice, which means that
- once data is read from the object, it no longer kept by the
+ once data is read from the object, it is no longer kept by the
device. It is therefore the application's responsibility to keep
this data if it needs to. Whenever more data is received from the
network and processed, the readyRead() signal is emitted.
@@ -344,7 +344,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
processing. After this signal is emitted, there will be no more
updates to the reply's data or metadata.
- Unless close() or abort() have been called, the reply will be still be opened
+ Unless close() or abort() have been called, the reply will still be opened
for reading, so the data can be retrieved by calls to read() or
readAll(). In particular, if no calls to read() were made as a
result of readyRead(), a call to readAll() will retrieve the full
diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp
index b6be93147a..6e69b4c4d3 100644
--- a/src/network/access/qnetworkreplyfileimpl.cpp
+++ b/src/network/access/qnetworkreplyfileimpl.cpp
@@ -89,9 +89,10 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
// we handle only local files
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString());
setError(QNetworkReply::ProtocolInvalidOperationError, msg);
+ setFinished(true); // We're finished, will emit finished() after ctor is done.
QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError));
- fileOpenFinished(false);
+ QMetaObject::invokeMethod(this, [this](){ fileOpenFinished(false); }, Qt::QueuedConnection);
return;
}
#endif
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 21916f53f1..cd4a4c68eb 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -199,6 +199,9 @@ QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manage
d->sslConfiguration.reset(new QSslConfiguration(request.sslConfiguration()));
#endif
+ QObjectPrivate::connect(this, &QNetworkReplyHttpImpl::redirectAllowed, d,
+ &QNetworkReplyHttpImplPrivate::followRedirect, Qt::QueuedConnection);
+
// FIXME Later maybe set to Unbuffered, especially if it is zerocopy or from cache?
QIODevice::open(QIODevice::ReadOnly);
@@ -700,6 +703,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
httpRequest.setRedirectPolicy(redirectPolicy);
httpRequest.setPriority(convert(newHttpRequest.priority()));
+ loadingFromCache = false;
switch (operation) {
case QNetworkAccessManager::GetOperation:
@@ -808,7 +812,17 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
// For the synchronous HTTP, this is the normal way the delegate gets deleted
// For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished
- QObject::connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
+ QMetaObject::Connection threadFinishedConnection =
+ QObject::connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
+
+ // QTBUG-88063: When 'delegate' is deleted the connection will be added to 'thread''s orphaned
+ // connections list. This orphaned list will be cleaned up next time 'thread' emits a signal,
+ // unfortunately that's the finished signal. It leads to a soft-leak so we do this to disconnect
+ // it on deletion so that it cleans up the orphan immediately.
+ QObject::connect(delegate, &QObject::destroyed, delegate, [threadFinishedConnection]() {
+ if (bool(threadFinishedConnection))
+ QObject::disconnect(threadFinishedConnection);
+ });
// Set the properties it needs
delegate->httpRequest = httpRequest;
@@ -871,9 +885,6 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
q, SLOT(onRedirected(QUrl,int,int)),
Qt::QueuedConnection);
- QObject::connect(q, SIGNAL(redirectAllowed()), q, SLOT(followRedirect()),
- Qt::QueuedConnection);
-
#ifndef QT_NO_SSL
QObject::connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
q, SLOT(replySslConfigurationChanged(QSslConfiguration)),
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index bcdf6ee2f1..3b6deb82bd 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -680,7 +680,7 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
// read until EOF from data
if (Q_UNLIKELY(copyDevice)) {
qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
- "backend probly needs to be fixed");
+ "backend probably needs to be fixed");
return;
}
diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp
index 0fc5bbf5a8..647d9b6ca1 100644
--- a/src/network/access/qnetworkreplywasmimpl.cpp
+++ b/src/network/access/qnetworkreplywasmimpl.cpp
@@ -45,6 +45,7 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qthread.h>
+#include <QtCore/private/qtools_p.h>
#include <private/qnetworkaccessmanager_p.h>
#include <private/qnetworkfile_p.h>
@@ -67,10 +68,6 @@ QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate()
QNetworkReplyWasmImplPrivate::~QNetworkReplyWasmImplPrivate()
{
- if (m_fetch) {
- emscripten_fetch_close(m_fetch);
- m_fetch = 0;
- }
}
QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent)
@@ -115,12 +112,14 @@ void QNetworkReplyWasmImpl::close()
void QNetworkReplyWasmImpl::abort()
{
- Q_D( QNetworkReplyWasmImpl);
+ Q_D(QNetworkReplyWasmImpl);
if (d->state == QNetworkReplyPrivate::Finished || d->state == QNetworkReplyPrivate::Aborted)
return;
d->state = QNetworkReplyPrivate::Aborted;
- d->doAbort();
+ d->m_fetch->userData = nullptr;
+
+ d->emitReplyError(QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled"));
close();
}
@@ -198,11 +197,6 @@ void QNetworkReplyWasmImplPrivate::setReplyAttributes(quintptr data, int statusC
handler->q_func()->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, statusReason);
}
-void QNetworkReplyWasmImplPrivate::doAbort() const
-{
- emscripten_fetch_close(m_fetch);
-}
-
constexpr int getArraySize (int factor) {
return 2 * factor + 1;
}
@@ -241,10 +235,13 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
}
}
+ QByteArray userName, password;
// username & password
if (!request.url().userInfo().isEmpty()) {
- attr.userName = request.url().userName().toUtf8();
- attr.password = request.url().password().toUtf8();
+ userName = request.url().userName().toUtf8();
+ password = request.url().password().toUtf8();
+ attr.userName = userName.constData();
+ attr.password = password.constData();
}
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
@@ -272,7 +269,8 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
attr.userData = reinterpret_cast<void *>(this);
QString dPath = QStringLiteral("/home/web_user/") + request.url().fileName();
- attr.destinationPath = dPath.toUtf8();
+ QByteArray destinationPath = dPath.toUtf8();
+ attr.destinationPath = destinationPath.constData();
m_fetch = emscripten_fetch(&attr, request.url().toString().toUtf8());
}
@@ -283,7 +281,6 @@ void QNetworkReplyWasmImplPrivate::emitReplyError(QNetworkReply::NetworkError er
q->setError(errorCode, errorString);
emit q->errorOccurred(errorCode);
- emit q->finished();
}
void QNetworkReplyWasmImplPrivate::emitDataReadProgress(qint64 bytesReceived, qint64 bytesTotal)
@@ -324,32 +321,36 @@ static int parseHeaderName(const QByteArray &headerName)
if (headerName.isEmpty())
return -1;
- switch (tolower(headerName.at(0))) {
+ auto is = [&](const char *what) {
+ return qstrnicmp(headerName.data(), headerName.size(), what) == 0;
+ };
+
+ switch (QtMiscUtils::toAsciiLower(headerName.front())) {
case 'c':
- if (qstricmp(headerName.constData(), "content-type") == 0)
+ if (is("content-type"))
return QNetworkRequest::ContentTypeHeader;
- else if (qstricmp(headerName.constData(), "content-length") == 0)
+ else if (is("content-length"))
return QNetworkRequest::ContentLengthHeader;
- else if (qstricmp(headerName.constData(), "cookie") == 0)
+ else if (is("cookie"))
return QNetworkRequest::CookieHeader;
break;
case 'l':
- if (qstricmp(headerName.constData(), "location") == 0)
+ if (is("location"))
return QNetworkRequest::LocationHeader;
- else if (qstricmp(headerName.constData(), "last-modified") == 0)
+ else if (is("last-modified"))
return QNetworkRequest::LastModifiedHeader;
break;
case 's':
- if (qstricmp(headerName.constData(), "set-cookie") == 0)
+ if (is("set-cookie"))
return QNetworkRequest::SetCookieHeader;
- else if (qstricmp(headerName.constData(), "server") == 0)
+ else if (is("server"))
return QNetworkRequest::ServerHeader;
break;
case 'u':
- if (qstricmp(headerName.constData(), "user-agent") == 0)
+ if (is("user-agent"))
return QNetworkRequest::UserAgentHeader;
break;
}
@@ -447,23 +448,18 @@ void QNetworkReplyWasmImplPrivate::_q_bufferOutgoingData()
void QNetworkReplyWasmImplPrivate::downloadSucceeded(emscripten_fetch_t *fetch)
{
- QNetworkReplyWasmImplPrivate *reply =
- reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
+ auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
if (reply) {
- QByteArray buffer(fetch->data, fetch->numBytes);
- reply->dataReceived(buffer, buffer.size());
-
- QByteArray statusText(fetch->statusText);
- reply->setStatusCode(fetch->status, statusText);
- reply->setReplyFinished();
+ if (reply->state != QNetworkReplyPrivate::Aborted) {
+ QByteArray buffer(fetch->data, fetch->numBytes);
+ reply->dataReceived(buffer, buffer.size());
+ QByteArray statusText(fetch->statusText);
+ reply->setStatusCode(fetch->status, statusText);
+ reply->setReplyFinished();
+ }
+ reply->m_fetch = nullptr;
}
-}
-
-void QNetworkReplyWasmImplPrivate::setStatusCode(int status, const QByteArray &statusText)
-{
- Q_Q(QNetworkReplyWasmImpl);
- q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
- q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, statusText);
+ emscripten_fetch_close(fetch);
}
void QNetworkReplyWasmImplPrivate::setReplyFinished()
@@ -474,24 +470,36 @@ void QNetworkReplyWasmImplPrivate::setReplyFinished()
emit q->finished();
}
+void QNetworkReplyWasmImplPrivate::setStatusCode(int status, const QByteArray &statusText)
+{
+ Q_Q(QNetworkReplyWasmImpl);
+ q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
+ q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, statusText);
+}
+
void QNetworkReplyWasmImplPrivate::stateChange(emscripten_fetch_t *fetch)
{
- if (fetch->readyState == /*HEADERS_RECEIVED*/ 2) {
- size_t headerLength = emscripten_fetch_get_response_headers_length(fetch);
- QByteArray str(headerLength, Qt::Uninitialized);
- emscripten_fetch_get_response_headers(fetch, str.data(), str.size());
- QNetworkReplyWasmImplPrivate *reply =
- reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
- reply->headersReceived(str);
+ if (fetch) {
+ if (!quintptr(fetch->userData))
+ return;
+ auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
+ if (reply->state != QNetworkReplyPrivate::Aborted) {
+ if (fetch->readyState == /*HEADERS_RECEIVED*/ 2) {
+ size_t headerLength = emscripten_fetch_get_response_headers_length(fetch);
+ QByteArray str(headerLength, Qt::Uninitialized);
+ emscripten_fetch_get_response_headers(fetch, str.data(), str.size());
+
+ reply->headersReceived(str);
+ }
+ }
}
}
void QNetworkReplyWasmImplPrivate::downloadProgress(emscripten_fetch_t *fetch)
{
- QNetworkReplyWasmImplPrivate *reply =
- reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
- Q_ASSERT(reply);
-
+ auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
+ if (!reply || reply->state == QNetworkReplyPrivate::Aborted)
+ return;
if (fetch->status < 400) {
uint64_t bytes = fetch->dataOffset + fetch->numBytes;
uint64_t tBytes = fetch->totalBytes; // totalBytes can be 0 if server not reporting content length
@@ -503,10 +511,13 @@ void QNetworkReplyWasmImplPrivate::downloadProgress(emscripten_fetch_t *fetch)
void QNetworkReplyWasmImplPrivate::downloadFailed(emscripten_fetch_t *fetch)
{
- QNetworkReplyWasmImplPrivate *reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
+
+ auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
+
if (reply) {
+
QString reasonStr;
- if (fetch->status > 600 || reply->state == QNetworkReplyPrivate::Aborted)
+ if (fetch->status > 600)
reasonStr = QStringLiteral("Operation canceled");
else
reasonStr = QString::fromUtf8(fetch->statusText);
@@ -514,10 +525,10 @@ void QNetworkReplyWasmImplPrivate::downloadFailed(emscripten_fetch_t *fetch)
QByteArray statusText(fetch->statusText);
reply->setStatusCode(fetch->status, statusText);
reply->emitReplyError(reply->statusCodeFromHttp(fetch->status, reply->request.url()), reasonStr);
+ reply->m_fetch = nullptr;
}
- if (fetch->status >= 400)
- emscripten_fetch_close(fetch); // Also free data on failure.
+ emscripten_fetch_close(fetch);
}
//taken from qhttpthreaddelegate.cpp
diff --git a/src/network/access/qnetworkreplywasmimpl_p.h b/src/network/access/qnetworkreplywasmimpl_p.h
index 53e9bc3375..db9fd5657e 100644
--- a/src/network/access/qnetworkreplywasmimpl_p.h
+++ b/src/network/access/qnetworkreplywasmimpl_p.h
@@ -137,8 +137,6 @@ public:
QSharedPointer<QRingBuffer> outgoingDataBuffer;
QByteArray requestData;
- void doAbort() const;
-
static void downloadProgress(emscripten_fetch_t *fetch);
static void downloadFailed(emscripten_fetch_t *fetch);
static void downloadSucceeded(emscripten_fetch_t *fetch);
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 03ed2a455d..6ccd48298d 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -49,6 +49,7 @@
#include "QtCore/qshareddata.h"
#include "QtCore/qlocale.h"
#include "QtCore/qdatetime.h"
+#include "QtCore/private/qtools_p.h"
#include <ctype.h>
#if QT_CONFIG(datestring)
@@ -423,6 +424,11 @@ QT_BEGIN_NAMESPACE
for example, to ask the user whether to
accept the redirect, or to decide
based on some app-specific configuration.
+
+ \note When Qt handles redirects it will, for legacy and compatibility
+ reasons, issue the redirected request using GET when the server returns
+ a 301 or 302 response, regardless of the original method used, unless it was
+ HEAD.
*/
/*!
@@ -1036,9 +1042,10 @@ static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVaria
case QNetworkRequest::LastModifiedHeader:
case QNetworkRequest::IfModifiedSinceHeader:
switch (value.userType()) {
+ // Generate RFC 1123/822 dates:
case QMetaType::QDate:
+ return QNetworkHeadersPrivate::toHttpDate(value.toDate().startOfDay(Qt::UTC));
case QMetaType::QDateTime:
- // generate RFC 1123/822 dates:
return QNetworkHeadersPrivate::toHttpDate(value.toDateTime());
default:
@@ -1086,48 +1093,52 @@ static int parseHeaderName(const QByteArray &headerName)
if (headerName.isEmpty())
return -1;
- switch (tolower(headerName.at(0))) {
+ auto is = [&](const char *what) {
+ return qstrnicmp(headerName.data(), headerName.size(), what) == 0;
+ };
+
+ switch (QtMiscUtils::toAsciiLower(headerName.front())) {
case 'c':
- if (headerName.compare("content-type", Qt::CaseInsensitive) == 0)
+ if (is("content-type"))
return QNetworkRequest::ContentTypeHeader;
- else if (headerName.compare("content-length", Qt::CaseInsensitive) == 0)
+ else if (is("content-length"))
return QNetworkRequest::ContentLengthHeader;
- else if (headerName.compare("cookie", Qt::CaseInsensitive) == 0)
+ else if (is("cookie"))
return QNetworkRequest::CookieHeader;
- else if (qstricmp(headerName.constData(), "content-disposition") == 0)
+ else if (is("content-disposition"))
return QNetworkRequest::ContentDispositionHeader;
break;
case 'e':
- if (qstricmp(headerName.constData(), "etag") == 0)
+ if (is("etag"))
return QNetworkRequest::ETagHeader;
break;
case 'i':
- if (qstricmp(headerName.constData(), "if-modified-since") == 0)
+ if (is("if-modified-since"))
return QNetworkRequest::IfModifiedSinceHeader;
- if (qstricmp(headerName.constData(), "if-match") == 0)
+ if (is("if-match"))
return QNetworkRequest::IfMatchHeader;
- if (qstricmp(headerName.constData(), "if-none-match") == 0)
+ if (is("if-none-match"))
return QNetworkRequest::IfNoneMatchHeader;
break;
case 'l':
- if (headerName.compare("location", Qt::CaseInsensitive) == 0)
+ if (is("location"))
return QNetworkRequest::LocationHeader;
- else if (headerName.compare("last-modified", Qt::CaseInsensitive) == 0)
+ else if (is("last-modified"))
return QNetworkRequest::LastModifiedHeader;
break;
case 's':
- if (headerName.compare("set-cookie", Qt::CaseInsensitive) == 0)
+ if (is("set-cookie"))
return QNetworkRequest::SetCookieHeader;
- else if (headerName.compare("server", Qt::CaseInsensitive) == 0)
+ else if (is("server"))
return QNetworkRequest::ServerHeader;
break;
case 'u':
- if (headerName.compare("user-agent", Qt::CaseInsensitive) == 0)
+ if (is("user-agent"))
return QNetworkRequest::UserAgentHeader;
break;
}
@@ -1482,8 +1493,7 @@ QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
{
- return QLocale::c().toString(dt, u"ddd, dd MMM yyyy hh:mm:ss 'GMT'")
- .toLatin1();
+ return QLocale::c().toString(dt.toUTC(), u"ddd, dd MMM yyyy hh:mm:ss 'GMT'").toLatin1();
}
QT_END_NAMESPACE
diff --git a/src/network/access/qspdyprotocolhandler.cpp b/src/network/access/qspdyprotocolhandler.cpp
index eef8df288d..c327494e5c 100644
--- a/src/network/access/qspdyprotocolhandler.cpp
+++ b/src/network/access/qspdyprotocolhandler.cpp
@@ -762,7 +762,8 @@ bool QSpdyProtocolHandler::uploadData(qint32 streamID)
void QSpdyProtocolHandler::_q_uploadDataReadyRead()
{
QNonContiguousByteDevice *device = qobject_cast<QNonContiguousByteDevice *>(sender());
- Q_ASSERT(device);
+ if (!device)
+ return;
qint32 streamID = m_streamIDs.value(device);
Q_ASSERT(streamID > 0);
uploadData(streamID);
diff --git a/src/network/android/jar/src/org/qtproject/qt5/android/network/QtNetwork.java b/src/network/android/jar/src/org/qtproject/qt5/android/network/QtNetwork.java
index 249ffe61a5..af6c20463c 100644
--- a/src/network/android/jar/src/org/qtproject/qt5/android/network/QtNetwork.java
+++ b/src/network/android/jar/src/org/qtproject/qt5/android/network/QtNetwork.java
@@ -43,6 +43,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Build;
import android.util.Log;
import android.net.ConnectivityManager;
import android.net.Proxy;
@@ -94,7 +95,7 @@ public class QtNetwork
public static ProxyInfo getProxyInfo(final Context context)
{
- if (m_proxyInfo == null)
+ if (m_proxyInfo == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
m_proxyInfo = (ProxyInfo)getConnectivityManager(context).getDefaultProxy();
return m_proxyInfo;
}
diff --git a/src/network/doc/src/qtnetwork.qdoc b/src/network/doc/src/qtnetwork.qdoc
index 57b0210b7f..3bdfaf0027 100644
--- a/src/network/doc/src/qtnetwork.qdoc
+++ b/src/network/doc/src/qtnetwork.qdoc
@@ -43,7 +43,9 @@
Add \c network to the \c QT variable:
- \snippet snippets.pro 0
+ \code
+ QT += network
+ \endcode
\section1 Articles and Guides
diff --git a/src/network/doc/src/ssl.qdoc b/src/network/doc/src/ssl.qdoc
index e485a1b393..efe4111cfe 100644
--- a/src/network/doc/src/ssl.qdoc
+++ b/src/network/doc/src/ssl.qdoc
@@ -36,8 +36,8 @@
the Secure Sockets Layer (SSL) protocol, using the \l{OpenSSL Toolkit}
to perform encryption and protocol handling.
- From Qt version 5.6 onwards, the officially supported version for OpenSSL
- is 1.0.0 or later.
+ From Qt version 5.15 onwards, the officially supported version for OpenSSL
+ is 1.1.1 or later.
\annotatedlist ssl
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index 86242b011f..93b323f0bd 100644
--- a/src/network/kernel/qauthenticator.cpp
+++ b/src/network/kernel/qauthenticator.cpp
@@ -50,6 +50,7 @@
#include <qstring.h>
#include <qdatetime.h>
#include <qrandom.h>
+#include "private/qsystemlibrary_p.h"
#ifdef Q_OS_WIN
#include <qmutex.h>
@@ -423,6 +424,22 @@ void QAuthenticatorPrivate::updateCredentials()
}
}
+static bool verifyDigestMD5(const QByteArray &value)
+{
+ auto opts = QAuthenticatorPrivate::parseDigestAuthenticationChallenge(value);
+ auto it = opts.constFind("algorithm");
+ if (it != opts.cend()) {
+ QByteArray alg = it.value();
+ if (alg.size() < 3)
+ return false;
+ // Just compare the first 3 characters, that way we match other subvariants as well, such as
+ // "MD5-sess"
+ auto view = QByteArray::fromRawData(alg.data(), 3);
+ return view.compare("MD5", Qt::CaseInsensitive) == 0;
+ }
+ return true; // assume it's ok if algorithm is not specified
+}
+
void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray> > &values, bool isProxy, const QString &host)
{
#if !QT_CONFIG(gssapi)
@@ -454,8 +471,13 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
method = Ntlm;
headerVal = current.second.mid(5);
} else if (method < DigestMd5 && str.startsWith("digest")) {
+ // Make sure the algorithm is actually MD5 before committing to it:
+ QByteArray fieldValue = current.second.mid(7);
+ if (!verifyDigestMD5(fieldValue))
+ continue;
+
method = DigestMd5;
- headerVal = current.second.mid(7);
+ headerVal = fieldValue;
} else if (method < Negotiate && str.startsWith("negotiate")) {
#if QT_CONFIG(sspi) || QT_CONFIG(gssapi) // if it's not supported then we shouldn't try to use it
#if QT_CONFIG(gssapi)
@@ -599,9 +621,11 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet
} else {
QByteArray phase3Token;
#if QT_CONFIG(sspi) // SSPI
- phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
+ if (sspiWindowsHandles)
+ phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
#elif QT_CONFIG(gssapi) // GSSAPI
- phase3Token = qGssapiContinue(this, QByteArray::fromBase64(challenge));
+ if (gssApiHandles)
+ phase3Token = qGssapiContinue(this, QByteArray::fromBase64(challenge));
#endif
if (!phase3Token.isEmpty()) {
response = phase3Token.toBase64();
@@ -1542,7 +1566,7 @@ static bool q_SSPI_library_load()
// Initialize security interface
if (pSecurityFunctionTable == nullptr) {
- securityDLLHandle = LoadLibrary(L"secur32.dll");
+ securityDLLHandle = QSystemLibrary::load(L"secur32");
if (securityDLLHandle != nullptr) {
INIT_SECURITY_INTERFACE pInitSecurityInterface =
reinterpret_cast<INIT_SECURITY_INTERFACE>(
@@ -1568,7 +1592,8 @@ static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate
if (!ctx->sspiWindowsHandles)
ctx->sspiWindowsHandles.reset(new QSSPIWindowsHandles);
- memset(&ctx->sspiWindowsHandles->credHandle, 0, sizeof(CredHandle));
+ SecInvalidateHandle(&ctx->sspiWindowsHandles->credHandle);
+ SecInvalidateHandle(&ctx->sspiWindowsHandles->ctxHandle);
SEC_WINNT_AUTH_IDENTITY auth;
auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h
index 1813634ee0..b234925a0e 100644
--- a/src/network/kernel/qauthenticator_p.h
+++ b/src/network/kernel/qauthenticator_p.h
@@ -91,6 +91,7 @@ public:
enum Phase {
Start,
+ Phase1,
Phase2,
Done,
Invalid
diff --git a/src/network/kernel/qdnslookup_unix.cpp b/src/network/kernel/qdnslookup_unix.cpp
index 12b40fc35d..99e999d436 100644
--- a/src/network/kernel/qdnslookup_unix.cpp
+++ b/src/network/kernel/qdnslookup_unix.cpp
@@ -227,7 +227,6 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
// 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;
@@ -260,18 +259,31 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
return;
}
- // 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) {
+ int status;
+
+ if (ntohs(header->qdcount) == 1) {
+ // Skip the query host, type (2 bytes) and class (2 bytes).
+ 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;
+ }
+ if ((p - response) + status + 4 >= responseLength)
+ header->qdcount = 0xffff; // invalid reply below
+ else
+ p += status + 4;
+ }
+ if (ntohs(header->qdcount) > 1) {
reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Could not expand domain name");
+ reply->errorString = tr("Invalid reply received");
return;
}
- p += status + 4;
// 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));
@@ -283,6 +295,11 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
const QString name = QUrl::fromAce(host);
p += status;
+
+ if ((p - response) + 10 > responseLength) {
+ // probably just a truncated reply, return what we have
+ return;
+ }
const quint16 type = (p[0] << 8) | p[1];
p += 2; // RR type
p += 2; // RR class
@@ -290,6 +307,8 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
p += 4;
const quint16 size = (p[0] << 8) | p[1];
p += 2;
+ if ((p - response) + size > responseLength)
+ return; // truncated
if (type == QDnsLookup::A) {
if (size != 4) {
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
index ed1c23ed6e..c33f889c0d 100644
--- a/src/network/kernel/qhostaddress.cpp
+++ b/src/network/kernel/qhostaddress.cpp
@@ -921,7 +921,7 @@ bool QHostAddress::isEqual(const QHostAddress &other, ConversionMode mode) const
return memcmp(&d->a6, &other.d->a6, sizeof(Q_IPV6ADDR)) == 0;
case QAbstractSocket::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 QAbstractSocket::UnknownNetworkLayerProtocol:
return false;
}
diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp
index 9b0a2ee669..73679c9ef1 100644
--- a/src/network/kernel/qhostinfo_unix.cpp
+++ b/src/network/kernel/qhostinfo_unix.cpp
@@ -122,7 +122,6 @@ static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym)
LibResolv::LibResolv()
{
- QLibrary lib;
#ifdef LIBRESOLV_SO
lib.setFileName(QStringLiteral(LIBRESOLV_SO));
if (!lib.load())
diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp
index eed57f8a32..c89d7f898c 100644
--- a/src/network/kernel/qnetworkinterface.cpp
+++ b/src/network/kernel/qnetworkinterface.cpp
@@ -489,7 +489,7 @@ void QNetworkAddressEntry::clearAddressLifetime()
\since 5.11
Returns \c true if this address is permanent on this interface, \c false if
- it's temporary. A permenant address is one which has no expiration time and
+ it's temporary. A permanent address is one which has no expiration time and
is often static (manually configured).
If this information could not be determined, this function returns \c true.
@@ -552,9 +552,11 @@ bool QNetworkAddressEntry::isPermanent() const
Specifies the flags associated with this network interface. The
possible values are:
- \value IsUp the network interface is active
- \value IsRunning the network interface has resources
- allocated
+ \value IsUp the network interface is "up" -
+ enabled by administrative action
+ \value IsRunning the network interface is operational:
+ configured "up" and (typically)
+ physically connected to a network
\value CanBroadcast the network interface works in
broadcast mode
\value IsLoopBack the network interface is a loopback
diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp
index 4c57bff3bc..e7889b6249 100644
--- a/src/network/kernel/qnetworkinterface_unix.cpp
+++ b/src/network/kernel/qnetworkinterface_unix.cpp
@@ -113,6 +113,7 @@ uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
qt_safe_close(socket);
return id;
#else
+ Q_UNUSED(name);
return 0;
#endif
}
@@ -232,6 +233,7 @@ static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfa
break;
}
#else
+ Q_UNUSED(socket);
// Search by name
QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
for ( ; if_it != interfaces.end(); ++if_it)
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index 3cabdd0bd5..745e26ac95 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -189,7 +189,7 @@
QNetworkProxy sets different capabilities by default when the
object is created (see QNetworkProxy::ProxyType for a list of the
- defaults). However, it is possible to change the capabitilies
+ defaults). However, it is possible to change the capabilities
after the object has been created with setCapabilities().
The capabilities that QNetworkProxy supports are:
@@ -1705,4 +1705,6 @@ QDebug operator<<(QDebug debug, const QNetworkProxyQuery &proxyQuery)
QT_END_NAMESPACE
+#include "moc_qnetworkproxy.cpp"
+
#endif // QT_NO_NETWORKPROXY
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp
index 3f3b37f666..97f43420ca 100644
--- a/src/network/kernel/qnetworkproxy_mac.cpp
+++ b/src/network/kernel/qnetworkproxy_mac.cpp
@@ -208,7 +208,7 @@ QCFType<CFStringRef> stringByAddingPercentEscapes(CFStringRef originalPath)
{
Q_ASSERT(originalPath);
const auto qtPath = QString::fromCFString(originalPath);
- const auto escaped = QString::fromUtf8(QUrl::toPercentEncoding(qtPath));
+ const auto escaped = QString::fromUtf8(QUrl(qtPath).toEncoded());
return escaped.toCFString();
}
diff --git a/src/network/network.pro b/src/network/network.pro
index d8453e879c..160e6c802a 100644
--- a/src/network/network.pro
+++ b/src/network/network.pro
@@ -23,7 +23,7 @@ include(ssl/ssl.pri)
QMAKE_LIBS += $$QMAKE_LIBS_NETWORK
qtConfig(bearermanagement) {
- ANDROID_BUNDLED_JAR_DEPENDENCIES = \
+ ANDROID_BUNDLED_JAR_DEPENDENCIES += \
jar/QtAndroidBearer.jar
ANDROID_LIB_DEPENDENCIES = \
plugins/bearer/libplugins_bearer_qandroidbearer.so
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index e7fc17142e..59f8253c1a 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -437,7 +437,7 @@
\value DontShareAddress Bind the address and port exclusively, so that
no other services are allowed to rebind. By passing this option to
- QAbstractSocket::bind(), you are guaranteed that on successs, your service
+ QAbstractSocket::bind(), you are guaranteed that on success, your service
is the only one that listens to the address and port. No services are
allowed to rebind, even if they pass ReuseAddressHint. This option
provides more security than ShareAddress, but on certain operating
@@ -1404,12 +1404,29 @@ void QAbstractSocketPrivate::pauseSocketNotifiers(QAbstractSocket *socket)
QAbstractSocketEngine *socketEngine = socket->d_func()->socketEngine;
if (!socketEngine)
return;
- socket->d_func()->prePauseReadSocketNotifierState = socketEngine->isReadNotificationEnabled();
- socket->d_func()->prePauseWriteSocketNotifierState = socketEngine->isWriteNotificationEnabled();
- socket->d_func()->prePauseExceptionSocketNotifierState = socketEngine->isExceptionNotificationEnabled();
- socketEngine->setReadNotificationEnabled(false);
- socketEngine->setWriteNotificationEnabled(false);
- socketEngine->setExceptionNotificationEnabled(false);
+ bool read = socketEngine->isReadNotificationEnabled();
+ bool write = socketEngine->isWriteNotificationEnabled();
+ bool except = socketEngine->isExceptionNotificationEnabled();
+
+#ifdef QABSTRACTSOCKET_DEBUG
+ qDebug() << socketEngine->socketDescriptor()
+ << "pause notifiers, storing 'true' states, currently read:" << read
+ << "write:" << write << "except:" << except;
+#endif
+ // We do this if-check to avoid accidentally overwriting any previously stored state
+ // It will reset to false once the socket is re-enabled.
+ if (read) {
+ socket->d_func()->prePauseReadSocketNotifierState = true;
+ socketEngine->setReadNotificationEnabled(false);
+ }
+ if (write) {
+ socket->d_func()->prePauseWriteSocketNotifierState = true;
+ socketEngine->setWriteNotificationEnabled(false);
+ }
+ if (except) {
+ socket->d_func()->prePauseExceptionSocketNotifierState = true;
+ socketEngine->setExceptionNotificationEnabled(false);
+ }
}
void QAbstractSocketPrivate::resumeSocketNotifiers(QAbstractSocket *socket)
@@ -1417,9 +1434,19 @@ void QAbstractSocketPrivate::resumeSocketNotifiers(QAbstractSocket *socket)
QAbstractSocketEngine *socketEngine = socket->d_func()->socketEngine;
if (!socketEngine)
return;
- socketEngine->setReadNotificationEnabled(socket->d_func()->prePauseReadSocketNotifierState);
- socketEngine->setWriteNotificationEnabled(socket->d_func()->prePauseWriteSocketNotifierState);
- socketEngine->setExceptionNotificationEnabled(socket->d_func()->prePauseExceptionSocketNotifierState);
+ QAbstractSocketPrivate *priv = socket->d_func();
+#ifdef QABSTRACTSOCKET_DEBUG
+ qDebug() << socketEngine->socketDescriptor()
+ << "Maybe resume notifiers, read:" << priv->prePauseReadSocketNotifierState
+ << "write:" << priv->prePauseWriteSocketNotifierState
+ << "exception:" << priv->prePauseExceptionSocketNotifierState;
+#endif
+ if (qExchange(priv->prePauseReadSocketNotifierState, false))
+ socketEngine->setReadNotificationEnabled(true);
+ if (qExchange(priv->prePauseWriteSocketNotifierState, false))
+ socketEngine->setWriteNotificationEnabled(true);
+ if (qExchange(priv->prePauseExceptionSocketNotifierState, false))
+ socketEngine->setExceptionNotificationEnabled(true);
}
QAbstractSocketEngine* QAbstractSocketPrivate::getSocketEngine(QAbstractSocket *socket)
diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h
index 5aa69d747e..a20e0aacad 100644
--- a/src/network/socket/qabstractsocket_p.h
+++ b/src/network/socket/qabstractsocket_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -162,9 +162,9 @@ public:
QAbstractSocket::NetworkLayerProtocol preferredNetworkLayerProtocol;
- bool prePauseReadSocketNotifierState;
- bool prePauseWriteSocketNotifierState;
- bool prePauseExceptionSocketNotifierState;
+ bool prePauseReadSocketNotifierState = false;
+ bool prePauseWriteSocketNotifierState = false;
+ bool prePauseExceptionSocketNotifierState = false;
static void pauseSocketNotifiers(QAbstractSocket*);
static void resumeSocketNotifiers(QAbstractSocket*);
static QAbstractSocketEngine* getSocketEngine(QAbstractSocket*);
diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp
index ca7680b71f..6629b7eace 100644
--- a/src/network/socket/qhttpsocketengine.cpp
+++ b/src/network/socket/qhttpsocketengine.cpp
@@ -645,7 +645,7 @@ void QHttpSocketEngine::slotSocketReadNotification()
d->socket->readAll();
//We're done with the reply and need to reset it for the next connection
delete d->reply;
- d->reply = new QHttpNetworkReply;
+ d->reply = new QHttpNetworkReply(QUrl(), this);
}
if (priv->phase == QAuthenticatorPrivate::Done)
diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp
index 5ca2db70b9..6190e27875 100644
--- a/src/network/socket/qlocalserver.cpp
+++ b/src/network/socket/qlocalserver.cpp
@@ -147,7 +147,7 @@ QLocalServer::~QLocalServer()
In some cases, such as with Unix domain sockets on Linux, the
access to the socket will be determined by file system permissions,
and are created based on the umask. Setting the access flags will
- overide this and will restrict or permit access as specified.
+ override this and will restrict or permit access as specified.
Other Unix-based operating systems, such as \macos, do not
honor file permissions for Unix domain sockets and by default
diff --git a/src/network/socket/qlocalsocket.cpp b/src/network/socket/qlocalsocket.cpp
index 8d93e1fa61..23f5af472b 100644
--- a/src/network/socket/qlocalsocket.cpp
+++ b/src/network/socket/qlocalsocket.cpp
@@ -90,7 +90,7 @@ QT_BEGIN_NAMESPACE
Note that unlike in most other QIODevice subclasses, open() may not open the device directly.
The function return false if the socket was already connected or if the server to connect
to was not defined and true in any other case. The connected() or errorOccurred() signals will be
- emitted once the device is actualy open (or the connection failed).
+ emitted once the device is actually open (or the connection failed).
See connectToServer() for more details.
*/
diff --git a/src/network/socket/qlocalsocket.h b/src/network/socket/qlocalsocket.h
index 200d59a309..4034329d64 100644
--- a/src/network/socket/qlocalsocket.h
+++ b/src/network/socket/qlocalsocket.h
@@ -44,6 +44,10 @@
#include <QtCore/qiodevice.h>
#include <QtNetwork/qabstractsocket.h>
+#ifndef QT_NO_DEBUG_STREAM
+#include <QtCore/qdebug.h>
+#endif
+
QT_REQUIRE_CONFIG(localserver);
QT_BEGIN_NAMESPACE
@@ -146,7 +150,6 @@ private:
};
#ifndef QT_NO_DEBUG_STREAM
-#include <QtCore/qdebug.h>
Q_NETWORK_EXPORT QDebug operator<<(QDebug, QLocalSocket::LocalSocketError);
Q_NETWORK_EXPORT QDebug operator<<(QDebug, QLocalSocket::LocalSocketState);
#endif
diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp
index 6d82656583..2599a38a36 100644
--- a/src/network/socket/qlocalsocket_win.cpp
+++ b/src/network/socket/qlocalsocket_win.cpp
@@ -255,13 +255,10 @@ void QLocalSocketPrivate::_q_pipeClosed()
emit q->disconnected();
pipeReader->stop();
+ delete pipeWriter;
+ pipeWriter = nullptr;
destroyPipeHandles();
handle = INVALID_HANDLE_VALUE;
-
- if (pipeWriter) {
- delete pipeWriter;
- pipeWriter = 0;
- }
}
qint64 QLocalSocket::bytesAvailable() const
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index e5b9fbbdb2..feb102b42a 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -46,6 +46,7 @@
#include "qelapsedtimer.h"
#include "qvarlengtharray.h"
#include "qnetworkinterface.h"
+#include "qendian.h"
#include <time.h>
#include <errno.h>
#include <fcntl.h>
@@ -364,12 +365,12 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co
}
int n, level;
- int v = -1;
+ int v = 0;
QT_SOCKOPTLEN_T len = sizeof(v);
convertToLevelAndOption(opt, socketProtocol, level, n);
if (n != -1 && ::getsockopt(socketDescriptor, level, n, (char *) &v, &len) != -1)
- return v;
+ return len == 1 ? qFromUnaligned<quint8>(&v) : v;
return -1;
}
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index 4f866e4da0..97fadca0b3 100644
--- a/src/network/socket/qsocks5socketengine.cpp
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -1296,7 +1296,7 @@ void QSocks5SocketEnginePrivate::_q_udpSocketReadNotification()
int pos = 0;
const char *buf = inBuf.constData();
if (inBuf.size() < 4) {
- QSOCKS5_D_DEBUG << "bugus udp data, discarding";
+ QSOCKS5_D_DEBUG << "bogus udp data, discarding";
return;
}
QSocks5RevivedDatagram datagram;
@@ -1641,9 +1641,7 @@ qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QI
QByteArray outBuf;
outBuf.reserve(270 + len);
- outBuf[0] = 0x00;
- outBuf[1] = 0x00;
- outBuf[2] = 0x00;
+ outBuf.append(3, '\0');
if (!qt_socks5_set_host_address_and_port(header.destinationAddress, header.destinationPort, &outBuf)) {
}
outBuf += QByteArray(data, len);
diff --git a/src/network/ssl/qasn1element.cpp b/src/network/ssl/qasn1element.cpp
index 5634332a67..e8b70a14df 100644
--- a/src/network/ssl/qasn1element.cpp
+++ b/src/network/ssl/qasn1element.cpp
@@ -142,12 +142,20 @@ bool QAsn1Element::read(QDataStream &stream)
if (length > quint64(std::numeric_limits<int>::max()))
return false;
- // value
+
+ // read value in blocks to avoid being fooled by incorrect length
+ const int BUFFERSIZE = 4 * 1024;
QByteArray tmpValue;
- tmpValue.resize(length);
- int count = stream.readRawData(tmpValue.data(), tmpValue.size());
- if (count != int(length))
- return false;
+ int remainingLength = length;
+ while (remainingLength) {
+ char readBuffer[BUFFERSIZE];
+ const int bytesToRead = qMin(remainingLength, BUFFERSIZE);
+ const int count = stream.readRawData(readBuffer, bytesToRead);
+ if (count != int(bytesToRead))
+ return false;
+ tmpValue.append(readBuffer, bytesToRead);
+ remainingLength -= bytesToRead;
+ }
mType = tmpType;
mValue.swap(tmpValue);
@@ -310,8 +318,9 @@ qint64 QAsn1Element::toInteger(bool *ok) const
return 0;
}
- // NOTE: negative numbers are not handled
- if (mValue.at(0) & 0x80) {
+ // NOTE: - negative numbers are not handled
+ // - greater sizes would overflow
+ if (mValue.at(0) & 0x80 || mValue.size() > 8) {
if (ok)
*ok = false;
return 0;
diff --git a/src/network/ssl/qdtls.cpp b/src/network/ssl/qdtls.cpp
index 74fb691dde..29a4df0f41 100644
--- a/src/network/ssl/qdtls.cpp
+++ b/src/network/ssl/qdtls.cpp
@@ -876,7 +876,7 @@ bool QDtls::startHandshake(QUdpSocket *socket, const QByteArray &datagram)
}
/*!
- If a timeout occures during the handshake, the handshakeTimeout() signal
+ If a timeout occurs during the handshake, the handshakeTimeout() signal
is emitted. The application must call handleTimeout() to retransmit handshake
messages; handleTimeout() returns \c true if a timeout has occurred, false
otherwise. \a socket must be a valid pointer.
diff --git a/src/network/ssl/qdtls_openssl.cpp b/src/network/ssl/qdtls_openssl.cpp
index 25a6c5f49c..ede9595f19 100644
--- a/src/network/ssl/qdtls_openssl.cpp
+++ b/src/network/ssl/qdtls_openssl.cpp
@@ -170,16 +170,6 @@ void delete_bio_method(BIO_METHOD *method)
q_BIO_meth_free(method);
}
-// The 'deleter' for QScopedPointer<BIO>.
-struct bio_deleter
-{
- static void cleanup(BIO *bio)
- {
- if (bio)
- q_BIO_free(bio);
- }
-};
-
// The path MTU discovery is non-trivial: it's a mix of getsockopt/setsockopt
// (IP_MTU/IP6_MTU/IP_MTU_DISCOVER) and fallback MTU values. It's not
// supported on all platforms, worse so - imposes specific requirements on
@@ -746,8 +736,7 @@ bool DtlsState::initBIO(QDtlsBasePrivate *dtlsBase)
q_BIO_meth_set_puts(biom, dtlsbio::q_dgram_puts);
q_BIO_meth_set_ctrl(biom, dtlsbio::q_dgram_ctrl);
- QScopedPointer<BIO, dtlsutil::bio_deleter> newBio(q_BIO_new(biom));
- BIO *bio = newBio.data();
+ BIO *bio = q_BIO_new(biom);
if (!bio) {
dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
msgFunctionFailed("BIO_new"));
@@ -755,7 +744,6 @@ bool DtlsState::initBIO(QDtlsBasePrivate *dtlsBase)
}
q_SSL_set_bio(tlsConnection.data(), bio, bio);
- newBio.take();
bioMethod.swap(customMethod);
diff --git a/src/network/ssl/qocspresponse.cpp b/src/network/ssl/qocspresponse.cpp
index bf27bb768b..51c9f71b71 100644
--- a/src/network/ssl/qocspresponse.cpp
+++ b/src/network/ssl/qocspresponse.cpp
@@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE
\ingroup ssl
\inmodule QtNetwork
- The QOcspResponse class represents the revocation status of a server's certficate,
+ The QOcspResponse class represents the revocation status of a server's certificate,
received by the client-side socket during the TLS handshake. QSslSocket must be
configured with OCSP stapling enabled.
@@ -145,14 +145,14 @@ QOcspResponse::~QOcspResponse() = default;
/*!
\since 5.13
- Copy-assigns and returns a reference to this response.
+ Copy-assigns \a other and returns a reference to this response.
*/
QOcspResponse &QOcspResponse::operator=(const QOcspResponse &) = default;
/*!
\since 5.13
- Move-assigns to this QOcspResponse instance.
+ Move-assigns \a other to this QOcspResponse instance.
*/
QOcspResponse &QOcspResponse::operator=(QOcspResponse &&) noexcept = default;
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
index f11a59395d..11d2c8717d 100644
--- a/src/network/ssl/qsslcertificate.cpp
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -121,7 +121,8 @@
\value Wildcard This provides a simple pattern matching syntax
similar to that used by shells (command interpreters) for "file
- globbing". See \l{QRegularExpression Wildcard matching}.
+ globbing". See \l {QRegularExpression#Wildcard matching}
+ {QRegularExpression Wildcard Matching}.
\value FixedString The pattern is a fixed string. This is
equivalent to using the RegularExpression pattern on a string in
@@ -701,9 +702,9 @@ QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::E
the specified host name.
Note that the root (CA) certificate should not be included in the list to be verified,
- this will be looked up automatically either using the CA list specified by
- QSslSocket::defaultCaCertificates() or, if possible, it will be loaded on demand
- on Unix.
+ this will be looked up automatically using the CA list specified in the
+ default QSslConfiguration, and, in addition, if possible, CA certificates loaded on
+ demand on Unix and Windows.
\since 5.0
*/
diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp
index ca9d61ccb1..d1794d4dbd 100644
--- a/src/network/ssl/qsslcertificate_openssl.cpp
+++ b/src/network/ssl/qsslcertificate_openssl.cpp
@@ -44,6 +44,7 @@
#include "qsslkey_p.h"
#include "qsslcertificateextension_p.h"
+#include <QtCore/qscopeguard.h>
#include <QtCore/qendian.h>
#include <QtCore/qmutex.h>
@@ -65,10 +66,17 @@ bool QSslCertificate::operator==(const QSslCertificate &other) const
{
if (d == other.d)
return true;
+
if (d->null && other.d->null)
return true;
- if (d->x509 && other.d->x509)
- return q_X509_cmp(d->x509, other.d->x509) == 0;
+
+ if (d->x509 && other.d->x509) {
+ const int ret = q_X509_cmp(d->x509, other.d->x509);
+ if (ret >= -1 && ret <= 1)
+ return ret == 0;
+ QSslSocketBackendPrivate::logAndClearErrorQueue();
+ }
+
return false;
}
@@ -323,30 +331,48 @@ QSslKey QSslCertificate::publicKey() const
*/
static QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext)
{
- // Get the extension specific method object if available
+ Q_ASSERT(ext);
+ // Get the extension specific method object if available,
// we cast away the const-ness here because some versions of openssl
// don't use const for the parameters in the functions pointers stored
// in the object.
X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
if (!meth) {
ASN1_OCTET_STRING *value = q_X509_EXTENSION_get_data(ext);
+ Q_ASSERT(value);
QByteArray result( reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(value)),
q_ASN1_STRING_length(value));
return result;
}
- //const unsigned char *data = ext->value->data;
void *ext_internal = q_X509V3_EXT_d2i(ext);
+ if (!ext_internal)
+ return {};
+
+ const auto extCleaner = qScopeGuard([meth, ext_internal]{
+ Q_ASSERT(ext_internal && meth);
+
+ if (meth->it)
+ q_ASN1_item_free(static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it));
+ else if (meth->ext_free)
+ meth->ext_free(ext_internal);
+ else
+ qCWarning(lcSsl, "No method to free an unknown extension, a potential memory leak?");
+ });
// If this extension can be converted
- if (meth->i2v && ext_internal) {
+ if (meth->i2v) {
STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, nullptr);
+ const auto stackCleaner = qScopeGuard([val]{
+ if (val)
+ q_OPENSSL_sk_pop_free((OPENSSL_STACK *)val, (void(*)(void*))q_X509V3_conf_free);
+ });
QVariantMap map;
QVariantList list;
bool isMap = false;
- for (int j = 0; j < q_SKM_sk_num(CONF_VALUE, val); j++) {
+ for (int j = 0; j < q_SKM_sk_num(val); j++) {
CONF_VALUE *nval = q_SKM_sk_value(CONF_VALUE, val, j);
if (nval->name && nval->value) {
isMap = true;
@@ -362,11 +388,12 @@ static QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext)
return map;
else
return list;
- } else if (meth->i2s && ext_internal) {
- //qCDebug(lcSsl) << meth->i2s(meth, ext_internal);
- QVariant result(QString::fromUtf8(meth->i2s(meth, ext_internal)));
+ } else if (meth->i2s) {
+ const char *hexString = meth->i2s(meth, ext_internal);
+ QVariant result(hexString ? QString::fromUtf8(hexString) : QString{});
+ q_OPENSSL_free((void *)hexString);
return result;
- } else if (meth->i2r && ext_internal) {
+ } else if (meth->i2r) {
QByteArray result;
BIO *bio = q_BIO_new(q_BIO_s_mem());
@@ -396,11 +423,36 @@ static QVariant x509ExtensionToValue(X509_EXTENSION *ext)
ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
int nid = q_OBJ_obj2nid(obj);
+ // We cast away the const-ness here because some versions of openssl
+ // don't use const for the parameters in the functions pointers stored
+ // in the object.
+ X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
+
+ void *ext_internal = nullptr; // The value, returned by X509V3_EXT_d2i.
+ const auto extCleaner = qScopeGuard([meth, &ext_internal]() {
+ if (!meth || !ext_internal)
+ return;
+
+ if (meth->it)
+ q_ASN1_item_free(static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it));
+ else if (meth->ext_free)
+ meth->ext_free(ext_internal);
+ else
+ qWarning(lcSsl, "Cannot free an extension, a potential memory leak?");
+ });
+
+ const char * hexString = nullptr; // The value returned by meth->i2s.
+ const auto hexStringCleaner = qScopeGuard([&hexString](){
+ if (hexString)
+ q_OPENSSL_free((void*)hexString);
+ });
+
switch (nid) {
case NID_basic_constraints:
{
BASIC_CONSTRAINTS *basic = reinterpret_cast<BASIC_CONSTRAINTS *>(q_X509V3_EXT_d2i(ext));
-
+ if (!basic)
+ return {};
QVariantMap result;
result[QLatin1String("ca")] = basic->ca ? true : false;
if (basic->pathlen)
@@ -413,9 +465,10 @@ static QVariant x509ExtensionToValue(X509_EXTENSION *ext)
case NID_info_access:
{
AUTHORITY_INFO_ACCESS *info = reinterpret_cast<AUTHORITY_INFO_ACCESS *>(q_X509V3_EXT_d2i(ext));
-
+ if (!info)
+ return {};
QVariantMap result;
- for (int i=0; i < q_SKM_sk_num(ACCESS_DESCRIPTION, info); i++) {
+ for (int i=0; i < q_SKM_sk_num(info); i++) {
ACCESS_DESCRIPTION *ad = q_SKM_sk_value(ACCESS_DESCRIPTION, info, i);
GENERAL_NAME *name = ad->location;
@@ -435,26 +488,25 @@ static QVariant x509ExtensionToValue(X509_EXTENSION *ext)
}
}
- q_OPENSSL_sk_pop_free((OPENSSL_STACK*)info, reinterpret_cast<void(*)(void *)>(q_OPENSSL_sk_free));
+ q_AUTHORITY_INFO_ACCESS_free(info);
return result;
}
break;
case NID_subject_key_identifier:
{
- void *ext_internal = q_X509V3_EXT_d2i(ext);
-
- // we cast away the const-ness here because some versions of openssl
- // don't use const for the parameters in the functions pointers stored
- // in the object.
- X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
+ ext_internal = q_X509V3_EXT_d2i(ext);
+ if (!ext_internal)
+ return {};
- return QVariant(QString::fromUtf8(meth->i2s(meth, ext_internal)));
+ hexString = meth->i2s(meth, ext_internal);
+ return QVariant(QString::fromUtf8(hexString));
}
break;
- case NID_authority_key_identifier:
+ case NID_authority_key_identifier:
{
AUTHORITY_KEYID *auth_key = reinterpret_cast<AUTHORITY_KEYID *>(q_X509V3_EXT_d2i(ext));
-
+ if (!auth_key)
+ return {};
QVariantMap result;
// keyid
@@ -477,14 +529,21 @@ static QVariant x509ExtensionToValue(X509_EXTENSION *ext)
break;
}
- return QVariant();
+ return {};
}
QSslCertificateExtension QSslCertificatePrivate::convertExtension(X509_EXTENSION *ext)
{
+ Q_ASSERT(ext);
+
QSslCertificateExtension result;
ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
+ if (!obj) {
+ qCWarning(lcSsl, "Invalid (nullptr) ASN1_OBJECT");
+ return result;
+ }
+
QByteArray oid = QSslCertificatePrivate::asn1ObjectId(obj);
QByteArray name = QSslCertificatePrivate::asn1ObjectName(obj);
@@ -521,10 +580,17 @@ QList<QSslCertificateExtension> QSslCertificate::extensions() const
return result;
int count = q_X509_get_ext_count(d->x509);
+ if (count <= 0)
+ return result;
+
result.reserve(count);
for (int i = 0; i < count; i++) {
X509_EXTENSION *ext = q_X509_get_ext(d->x509, i);
+ if (!ext) {
+ qCWarning(lcSsl) << "Invalid (nullptr) extension at index" << i;
+ continue;
+ }
result << QSslCertificatePrivate::convertExtension(ext);
}
diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp
index 8b5035ad96..4b1510eb3c 100644
--- a/src/network/ssl/qsslcertificate_qt.cpp
+++ b/src/network/ssl/qsslcertificate_qt.cpp
@@ -311,7 +311,9 @@ bool QSslCertificatePrivate::parse(const QByteArray &data)
if (elem.type() == QAsn1Element::Context0Type) {
QDataStream versionStream(elem.value());
- if (!elem.read(versionStream) || elem.type() != QAsn1Element::IntegerType)
+ if (!elem.read(versionStream)
+ || elem.type() != QAsn1Element::IntegerType
+ || elem.value().isEmpty())
return false;
versionString = QByteArray::number(elem.value().at(0) + 1);
diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp
index c9f202f573..c30192a4eb 100644
--- a/src/network/ssl/qsslcontext_openssl.cpp
+++ b/src/network/ssl/qsslcontext_openssl.cpp
@@ -455,7 +455,7 @@ init_context:
}
// Enable bug workarounds.
- long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions);
+ const qssloptions options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions);
q_SSL_CTX_set_options(sslContext->ctx, options);
// Tell OpenSSL to release memory early
diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp
index cdc018a508..9db82b29d8 100644
--- a/src/network/ssl/qsslerror.cpp
+++ b/src/network/ssl/qsslerror.cpp
@@ -385,3 +385,5 @@ QDebug operator<<(QDebug debug, const QSslError::SslError &error)
#endif
QT_END_NAMESPACE
+
+#include "moc_qsslerror.cpp"
diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp
index 43cb8c6de8..df54b6a0d5 100644
--- a/src/network/ssl/qsslkey_openssl.cpp
+++ b/src/network/ssl/qsslkey_openssl.cpp
@@ -348,7 +348,10 @@ static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
EVP_CIPHER_CTX *ctx = q_EVP_CIPHER_CTX_new();
q_EVP_CIPHER_CTX_reset(ctx);
- q_EVP_CipherInit(ctx, type, nullptr, nullptr, enc);
+ if (q_EVP_CipherInit(ctx, type, nullptr, nullptr, enc) != 1) {
+ QSslSocketBackendPrivate::logAndClearErrorQueue();
+ return QByteArray();
+ }
q_EVP_CIPHER_CTX_set_key_length(ctx, key.size());
if (cipher == QSslKeyPrivate::Rc2Cbc)
q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), nullptr);
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index fbeb9de18b..5bb6e7ee4a 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -188,7 +188,7 @@
behavior is identical to QTcpSocket.
\value SslClientMode The socket is a client-side SSL socket.
- It is either alreayd encrypted, or it is in the SSL handshake
+ It is either already encrypted, or it is in the SSL handshake
phase (see QSslSocket::isEncrypted()).
\value SslServerMode The socket is a server-side SSL socket.
@@ -2172,7 +2172,7 @@ qint64 QSslSocket::readData(char *data, qint64 maxlen)
#endif
} else {
// possibly trigger another transmit() to decrypt more data from the socket
- if (d->plainSocket->bytesAvailable())
+ if (d->plainSocket->bytesAvailable() || d->hasUndecryptedData())
QMetaObject::invokeMethod(this, "_q_flushReadBuffer", Qt::QueuedConnection);
else if (d->state != QAbstractSocket::ConnectedState)
return maxlen ? qint64(-1) : qint64(0);
@@ -2709,7 +2709,7 @@ void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error)
qCDebug(lcSsl) << "\terrorString =" << q->errorString();
#endif
// this moves encrypted bytes from plain socket into our buffer
- if (plainSocket->bytesAvailable()) {
+ if (plainSocket->bytesAvailable() && mode != QSslSocket::UnencryptedMode) {
qint64 tmpReadBufferMaxSize = readBufferMaxSize;
readBufferMaxSize = 0; // reset temporarily so the plain sockets completely drained drained
transmit();
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 277037e59c..8f6858c867 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -213,15 +213,22 @@ static unsigned int q_ssl_psk_restore_client(SSL *ssl,
Q_ASSERT(d);
Q_ASSERT(d->mode == QSslSocket::SslClientMode);
#endif
+ unsigned retVal = 0;
+
+ // Let developers opt-in to having the normal PSK callback get called for TLS 1.3
+ // PSK (which works differently in a few ways, and is called at the start of every connection).
+ // When they do opt-in we just call the old callback from here.
+ if (qEnvironmentVariableIsSet("QT_USE_TLS_1_3_PSK"))
+ retVal = q_ssl_psk_client_callback(ssl, hint, identity, max_identity_len, psk, max_psk_len);
+
q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
- return 0;
+ return retVal;
}
static int q_ssl_psk_use_session_callback(SSL *ssl, const EVP_MD *md, const unsigned char **id,
size_t *idlen, SSL_SESSION **sess)
{
- Q_UNUSED(ssl);
Q_UNUSED(md);
Q_UNUSED(id);
Q_UNUSED(idlen);
@@ -455,7 +462,7 @@ bool qt_OCSP_certificate_match(OCSP_SINGLERESP *singleResponse, X509 *peerCert,
const QSharedPointer<OCSP_CERTID> guard(recreatedId, q_OCSP_CERTID_free);
if (q_OCSP_id_cmp(const_cast<OCSP_CERTID *>(certId), recreatedId)) {
- qDebug(lcSsl, "Certificate ID mismatch");
+ qCDebug(lcSsl, "Certificate ID mismatch");
return false;
}
// Bingo!
@@ -484,8 +491,23 @@ int q_X509Callback(int ok, X509_STORE_CTX *ctx)
// during a handshake, a pointer to the SSL object is stored into the X509_STORE_CTX object
// to identify the connection affected. To retrieve this pointer the X509_STORE_CTX_get_ex_data()
// function can be used with the correct index."
- if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx())))
+ if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(
+ ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx()))) {
+
+ // We may be in a renegotiation, check if we are inside a call to SSL_read:
+ const auto tlsOffset = QSslSocketBackendPrivate::s_indexForSSLExtraData;
+ auto tls = static_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, tlsOffset));
+ Q_ASSERT(tls);
+ if (tls->isInSslRead()) {
+ // We are in a renegotiation, make a note of this for later.
+ // We'll check that the certificate is the same as the one we got during
+ // the initial handshake
+ tls->setRenegotiated(true);
+ return 1;
+ }
+
errors = ErrorListPtr(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData + 1));
+ }
}
if (!errors) {
@@ -528,9 +550,9 @@ static void q_loadCiphersForConnection(SSL *connection, QList<QSslCipher> &ciphe
// Defined in qsslsocket.cpp
void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers);
-long QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions)
+qssloptions QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions)
{
- long options;
+ qssloptions options;
if (protocol == QSsl::TlsV1SslV3)
options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3;
else if (protocol == QSsl::SecureProtocols)
@@ -1149,7 +1171,25 @@ void QSslSocketBackendPrivate::transmit()
break;
}
// Don't use SSL_pending(). It's very unreliable.
+ inSslRead = true;
readBytes = q_SSL_read(ssl, buffer.reserve(bytesToRead), bytesToRead);
+ inSslRead = false;
+ if (renegotiated) {
+ renegotiated = false;
+ X509 *x509 = q_SSL_get_peer_certificate(ssl);
+ const auto peerCertificate =
+ QSslCertificatePrivate::QSslCertificate_from_X509(x509);
+ // Fail the renegotiate if the certificate has changed, else: continue.
+ if (peerCertificate != q->peerCertificate()) {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(
+ QAbstractSocket::RemoteHostClosedError,
+ QSslSocket::tr(
+ "TLS certificate unexpectedly changed during renegotiation!"));
+ q->abort();
+ return;
+ }
+ }
if (readBytes > 0) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: decrypted" << readBytes << "bytes";
@@ -1593,6 +1633,16 @@ unsigned int QSslSocketBackendPrivate::tlsPskServerCallback(const char *identity
return pskLength;
}
+bool QSslSocketBackendPrivate::isInSslRead() const
+{
+ return inSslRead;
+}
+
+void QSslSocketBackendPrivate::setRenegotiated(bool renegotiated)
+{
+ this->renegotiated = renegotiated;
+}
+
#ifdef Q_OS_WIN
void QSslSocketBackendPrivate::fetchCaRootForCert(const QSslCertificate &cert)
@@ -2090,10 +2140,16 @@ QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates
QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain,
const QString &hostName)
{
+ auto roots = QSslConfiguration::defaultConfiguration().caCertificates();
+#ifndef Q_OS_WIN
+ // On Windows, system CA certificates are already set as default ones.
+ // No need to add them again (and again) and also, if the default configuration
+ // has its own set of CAs, this probably should not be amended by the ones
+ // from the 'ROOT' store, since it's not what an application chose to trust.
if (s_loadRootCertsOnDemand)
- setDefaultCaCertificates(defaultCaCertificates() + systemCaCertificates());
-
- return verify(QSslConfiguration::defaultConfiguration().caCertificates(), certificateChain, hostName);
+ roots.append(systemCaCertificates());
+#endif // Q_OS_WIN
+ return verify(roots, certificateChain, hostName);
}
QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &caCertificates,
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
index 67f267aec1..26afcad8cd 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -100,6 +100,7 @@
#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/tls1.h>
+#include <openssl/opensslv.h>
#if QT_CONFIG(opensslv11)
#include <openssl/dh.h>
@@ -134,6 +135,9 @@ public:
bool inSetAndEmitError = false;
+ bool inSslRead = false;
+ bool renegotiated = false;
+
// Platform specific functions
void startClientEncryption() override;
void startServerEncryption() override;
@@ -149,6 +153,10 @@ public:
int handleNewSessionTicket(SSL *context);
unsigned int tlsPskClientCallback(const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
unsigned int tlsPskServerCallback(const char *identity, unsigned char *psk, unsigned int max_psk_len);
+
+ bool isInSslRead() const;
+ void setRenegotiated(bool renegotiated);
+
#ifdef Q_OS_WIN
void fetchCaRootForCert(const QSslCertificate &cert);
void _q_caRootLoaded(QSslCertificate,QSslCertificate) override;
@@ -164,7 +172,13 @@ public:
QVector<QSslError> ocspErrors;
QByteArray ocspResponseDer;
- Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
+#if OPENSSL_VERSION_MAJOR < 3
+ using qssloptions = unsigned long;
+#else
+ using qssloptions = uint64_t;
+#endif // OPENSSL_VERSION_MAJOR
+
+ Q_AUTOTEST_EXPORT static qssloptions setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher);
static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509);
static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName);
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index ed80fc14bd..0ace951c77 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -148,7 +148,6 @@ DEFINEFUNC(int, EVP_PKEY_up_ref, EVP_PKEY *a, a, return 0, return)
DEFINEFUNC2(EVP_PKEY_CTX *, EVP_PKEY_CTX_new, EVP_PKEY *pkey, pkey, ENGINE *e, e, return nullptr, return)
DEFINEFUNC(int, EVP_PKEY_param_check, EVP_PKEY_CTX *ctx, ctx, return 0, return)
DEFINEFUNC(void, EVP_PKEY_CTX_free, EVP_PKEY_CTX *ctx, ctx, return, return)
-DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return)
DEFINEFUNC(int, RSA_bits, RSA *a, a, return 0, return)
DEFINEFUNC(int, DSA_bits, DSA *a, a, return 0, return)
DEFINEFUNC(int, OPENSSL_sk_num, OPENSSL_STACK *a, a, return -1, return)
@@ -158,7 +157,7 @@ DEFINEFUNC2(void, OPENSSL_sk_push, OPENSSL_STACK *a, a, void *b, b, return, DUMM
DEFINEFUNC(void, OPENSSL_sk_free, OPENSSL_STACK *a, a, return, DUMMYARG)
DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return nullptr, return)
DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return)
-DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return)
+DEFINEFUNC2(qssloptions, SSL_CTX_set_options, SSL_CTX *ctx, ctx, qssloptions op, op, return 0, return)
DEFINEFUNC(int, SSL_CTX_get_security_level, const SSL_CTX *ctx, ctx, return -1, return)
DEFINEFUNC2(void, SSL_CTX_set_security_level, SSL_CTX *ctx, ctx, int level, level, return, return)
#ifdef TLS1_3_VERSION
@@ -178,6 +177,8 @@ DEFINEFUNC(const SSL_METHOD *, TLS_server_method, DUMMYARG, DUMMYARG, return nul
DEFINEFUNC(void, X509_up_ref, X509 *a, a, return, DUMMYARG)
DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return nullptr, return)
DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return)
+DEFINEFUNC2(void, ASN1_item_free, ASN1_VALUE *val, val, const ASN1_ITEM *it, it, return, return)
+DEFINEFUNC(void, X509V3_conf_free, CONF_VALUE *val, val, return, return)
DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return)
DEFINEFUNC(EVP_PKEY *, X509_get_pubkey, X509 *a, a, return nullptr, return)
DEFINEFUNC2(void, X509_STORE_set_verify_cb, X509_STORE *a, a, X509_STORE_CTX_verify_cb verify_cb, verify_cb, return, DUMMYARG)
@@ -234,6 +235,7 @@ DEFINEFUNC6(int, OCSP_basic_sign, OCSP_BASICRESP *br, br, X509 *signer, signer,
const EVP_MD *dg, dg, STACK_OF(X509) *cs, cs, unsigned long flags, flags, return 0, return)
#endif // ocsp
+DEFINEFUNC(void, AUTHORITY_INFO_ACCESS_free, AUTHORITY_INFO_ACCESS *p, p, return, return)
DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG)
DEFINEFUNC(void *, BIO_get_data, BIO *a, a, return nullptr, return)
DEFINEFUNC2(void, BIO_set_init, BIO *a, a, int init, init, return, DUMMYARG)
@@ -368,7 +370,15 @@ DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return nullptr
DEFINEFUNC(int, SSL_version, const SSL *a, a, return 0, return)
DEFINEFUNC2(int, SSL_get_error, SSL *a, a, int b, b, return -1, return)
DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return nullptr, return)
+
+#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
+DEFINEFUNC(X509 *, SSL_get1_peer_certificate, SSL *a, a, return nullptr, return)
+DEFINEFUNC(int, EVP_PKEY_get_base_id, const EVP_PKEY *pkey, pkey, return -1, return)
+#else
DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return nullptr, return)
+DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return)
+#endif // OPENSSL_VERSION_MAJOR >= 3
+
DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return)
DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return nullptr, return)
DEFINEFUNC(SSL_CTX *, SSL_get_SSL_CTX, SSL *a, a, return nullptr, return)
@@ -644,6 +654,12 @@ static QStringList findAllLibCrypto()
}
# endif
+#if (OPENSSL_VERSION_NUMBER >> 28) < 3
+#define QT_OPENSSL_VERSION "1_1"
+#elif OPENSSL_VERSION_MAJOR == 3 // Starting with 3.0 this define is available
+#define QT_OPENSSL_VERSION "3"
+#endif // > 3 intentionally left undefined
+
#ifdef Q_OS_WIN
struct LoadedOpenSsl {
@@ -671,22 +687,27 @@ static LoadedOpenSsl loadOpenSsl()
{
LoadedOpenSsl result;
- // With OpenSSL 1.1 the names have changed to libssl-1_1(-x64) and libcrypto-1_1(-x64), for builds using
- // MSVC and GCC, (-x64 suffix for 64-bit builds).
+ // With OpenSSL 1.1 the names have changed to libssl-1_1 and libcrypto-1_1 for builds using
+ // MSVC and GCC. For 3.0 the version suffix changed again, to just '3'.
+ // For non-x86 builds, an architecture suffix is also appended.
-#ifdef Q_PROCESSOR_X86_64
+#if defined(Q_PROCESSOR_X86_64)
#define QT_SSL_SUFFIX "-x64"
-#else // !Q_PROCESSOFR_X86_64
+#elif defined(Q_PROCESSOR_ARM_64)
+#define QT_SSL_SUFFIX "-arm64"
+#elif defined(Q_PROCESSOR_ARM_32)
+#define QT_SSL_SUFFIX "-arm"
+#else
#define QT_SSL_SUFFIX
-#endif // !Q_PROCESSOR_x86_64
+#endif
- tryToLoadOpenSslWin32Library(QLatin1String("libssl-1_1" QT_SSL_SUFFIX),
- QLatin1String("libcrypto-1_1" QT_SSL_SUFFIX), result);
+ tryToLoadOpenSslWin32Library(QLatin1String("libssl-" QT_OPENSSL_VERSION QT_SSL_SUFFIX),
+ QLatin1String("libcrypto-" QT_OPENSSL_VERSION QT_SSL_SUFFIX), result);
#undef QT_SSL_SUFFIX
return result;
}
-#else
+#else // !Q_OS_WIN:
struct LoadedOpenSsl {
std::unique_ptr<QLibrary> ssl, crypto;
@@ -765,7 +786,7 @@ static LoadedOpenSsl loadOpenSsl()
return suffix;
};
- static QString suffix = QString::fromLatin1(openSSLSuffix("_1_1"));
+ static QString suffix = QString::fromLatin1(openSSLSuffix("_" QT_OPENSSL_VERSION));
libssl->setFileNameAndVersion(QLatin1String("ssl") + suffix, -1);
libcrypto->setFileNameAndVersion(QLatin1String("crypto") + suffix, -1);
@@ -787,6 +808,11 @@ static LoadedOpenSsl loadOpenSsl()
const QStringList cryptoList = findAllLibCrypto();
for (const QString &crypto : cryptoList) {
+#ifdef Q_OS_DARWIN
+ // Clients should not load the unversioned libcrypto dylib as it does not have a stable ABI
+ if (crypto.endsWith("libcrypto.dylib"))
+ continue;
+#endif
libcrypto->setFileNameAndVersion(crypto, -1);
if (libcrypto->load()) {
QFileInfo fi(crypto);
@@ -844,11 +870,11 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(OPENSSL_init_crypto)
RESOLVEFUNC(ASN1_STRING_get0_data)
RESOLVEFUNC(EVP_CIPHER_CTX_reset)
+ RESOLVEFUNC(AUTHORITY_INFO_ACCESS_free)
RESOLVEFUNC(EVP_PKEY_up_ref)
RESOLVEFUNC(EVP_PKEY_CTX_new)
RESOLVEFUNC(EVP_PKEY_param_check)
RESOLVEFUNC(EVP_PKEY_CTX_free)
- RESOLVEFUNC(EVP_PKEY_base_id)
RESOLVEFUNC(RSA_bits)
RESOLVEFUNC(OPENSSL_sk_new_null)
RESOLVEFUNC(OPENSSL_sk_push)
@@ -880,6 +906,8 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(X509_STORE_CTX_get0_chain)
RESOLVEFUNC(X509_getm_notBefore)
RESOLVEFUNC(X509_getm_notAfter)
+ RESOLVEFUNC(ASN1_item_free)
+ RESOLVEFUNC(X509V3_conf_free)
RESOLVEFUNC(X509_get_version)
RESOLVEFUNC(X509_get_pubkey)
RESOLVEFUNC(X509_STORE_set_verify_cb)
@@ -889,13 +917,25 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(OpenSSL_version_num)
RESOLVEFUNC(OpenSSL_version)
- if (!_q_OpenSSL_version) {
+ if (!_q_OpenSSL_version || !_q_OpenSSL_version_num) {
// Apparently, we were built with OpenSSL 1.1 enabled but are now using
// a wrong library.
qCWarning(lcSsl, "Incompatible version of OpenSSL");
return false;
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+ if (q_OpenSSL_version_num() < 0x30000000) {
+ qCWarning(lcSsl, "Incompatible version of OpenSSL (built with OpenSSL >= 3.x, runtime version is < 3.x)");
+ return false;
+ }
+#else
+ if (q_OpenSSL_version_num() >= 0x30000000) {
+ qCWarning(lcSsl, "Incompatible version of OpenSSL (built with OpenSSL 1.x, runtime version is >= 3.x)");
+ return false;
+ }
+#endif // OPENSSL_VERSION_NUMBER
+
RESOLVEFUNC(SSL_SESSION_get_ticket_lifetime_hint)
RESOLVEFUNC(DH_bits)
RESOLVEFUNC(DSA_bits)
@@ -1073,7 +1113,15 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_version)
RESOLVEFUNC(SSL_get_error)
RESOLVEFUNC(SSL_get_peer_cert_chain)
+
+#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
+ RESOLVEFUNC(SSL_get1_peer_certificate)
+ RESOLVEFUNC(EVP_PKEY_get_base_id)
+#else
RESOLVEFUNC(SSL_get_peer_certificate)
+ RESOLVEFUNC(EVP_PKEY_base_id)
+#endif // OPENSSL_VERSION_MAJOR >= 3
+
RESOLVEFUNC(SSL_get_verify_result)
RESOLVEFUNC(SSL_new)
RESOLVEFUNC(SSL_get_SSL_CTX)
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index c46afcf53e..5e9faae291 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -231,12 +231,12 @@ Q_AUTOTEST_EXPORT BIO *q_BIO_new(const BIO_METHOD *a);
Q_AUTOTEST_EXPORT const BIO_METHOD *q_BIO_s_mem();
int q_DSA_bits(DSA *a);
+void q_AUTHORITY_INFO_ACCESS_free(AUTHORITY_INFO_ACCESS *a);
int q_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c);
Q_AUTOTEST_EXPORT int q_EVP_PKEY_up_ref(EVP_PKEY *a);
EVP_PKEY_CTX *q_EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
void q_EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
int q_EVP_PKEY_param_check(EVP_PKEY_CTX *ctx);
-int q_EVP_PKEY_base_id(EVP_PKEY *a);
int q_RSA_bits(RSA *a);
Q_AUTOTEST_EXPORT int q_OPENSSL_sk_num(OPENSSL_STACK *a);
Q_AUTOTEST_EXPORT void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *));
@@ -245,7 +245,14 @@ Q_AUTOTEST_EXPORT void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data);
Q_AUTOTEST_EXPORT void q_OPENSSL_sk_free(OPENSSL_STACK *a);
Q_AUTOTEST_EXPORT void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b);
int q_SSL_session_reused(SSL *a);
-unsigned long q_SSL_CTX_set_options(SSL_CTX *ctx, unsigned long op);
+
+#if OPENSSL_VERSION_MAJOR < 3
+using qssloptions = unsigned long;
+#else
+using qssloptions = uint64_t;
+#endif // OPENSSL_VERSION_MAJOR
+
+qssloptions q_SSL_CTX_set_options(SSL_CTX *ctx, qssloptions op);
int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings);
size_t q_SSL_get_client_random(SSL *a, unsigned char *out, size_t outlen);
size_t q_SSL_SESSION_get_master_key(const SSL_SESSION *session, unsigned char *out, size_t outlen);
@@ -255,6 +262,8 @@ const SSL_METHOD *q_TLS_client_method();
const SSL_METHOD *q_TLS_server_method();
ASN1_TIME *q_X509_getm_notBefore(X509 *a);
ASN1_TIME *q_X509_getm_notAfter(X509 *a);
+void q_ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it);
+void q_X509V3_conf_free(CONF_VALUE *val);
Q_AUTOTEST_EXPORT void q_X509_up_ref(X509 *a);
long q_X509_get_version(X509 *a);
@@ -269,8 +278,8 @@ int q_DH_bits(DH *dh);
# define q_SSL_load_error_strings() q_OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS \
| OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL)
-#define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_OPENSSL_sk_num)(st)
-#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_OPENSSL_sk_value)(st, i)
+#define q_SKM_sk_num(st) q_OPENSSL_sk_num((OPENSSL_STACK *)st)
+#define q_SKM_sk_value(type, st,i) (type *)q_OPENSSL_sk_value((OPENSSL_STACK *)st, i)
#define q_OPENSSL_add_all_algorithms_conf() q_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
| OPENSSL_INIT_ADD_ALL_DIGESTS \
@@ -279,8 +288,6 @@ int q_DH_bits(DH *dh);
| OPENSSL_INIT_ADD_ALL_DIGESTS, NULL)
int q_OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings);
-void q_CRYPTO_free(void *str, const char *file, int line);
-
long q_OpenSSL_version_num();
const char *q_OpenSSL_version(int type);
@@ -382,6 +389,17 @@ const EC_GROUP* q_EC_KEY_get0_group(const EC_KEY* k);
int q_EC_GROUP_get_degree(const EC_GROUP* g);
#endif // OPENSSL_NO_EC
+// Here we have the ones that make difference between OpenSSL pre/post v3:
+#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
+X509 *q_SSL_get1_peer_certificate(SSL *a);
+#define q_SSL_get_peer_certificate q_SSL_get1_peer_certificate
+int q_EVP_PKEY_get_base_id(const EVP_PKEY *pkey);
+#define q_EVP_PKEY_base_id q_EVP_PKEY_get_base_id
+#else
+X509 *q_SSL_get_peer_certificate(SSL *a);
+int q_EVP_PKEY_base_id(EVP_PKEY *a);
+#endif // OPENSSL_VERSION_MAJOR >= 3
+
DSA *q_DSA_new();
void q_DSA_free(DSA *a);
X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c);
@@ -509,7 +527,6 @@ const SSL_CIPHER *q_SSL_get_current_cipher(SSL *a);
int q_SSL_version(const SSL *a);
int q_SSL_get_error(SSL *a, int b);
STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a);
-X509 *q_SSL_get_peer_certificate(SSL *a);
long q_SSL_get_verify_result(const SSL *a);
SSL *q_SSL_new(SSL_CTX *a);
SSL_CTX *q_SSL_get_SSL_CTX(SSL *a);
@@ -611,14 +628,14 @@ void q_PKCS12_free(PKCS12 *pkcs12);
#define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp)
#define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL)
#define q_SSL_CTX_set_mode(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,(op),NULL)
-#define q_sk_GENERAL_NAME_num(st) q_SKM_sk_num(GENERAL_NAME, (st))
+#define q_sk_GENERAL_NAME_num(st) q_SKM_sk_num((st))
#define q_sk_GENERAL_NAME_value(st, i) q_SKM_sk_value(GENERAL_NAME, (st), (i))
void q_GENERAL_NAME_free(GENERAL_NAME *a);
-#define q_sk_X509_num(st) q_SKM_sk_num(X509, (st))
+#define q_sk_X509_num(st) q_SKM_sk_num((st))
#define q_sk_X509_value(st, i) q_SKM_sk_value(X509, (st), (i))
-#define q_sk_SSL_CIPHER_num(st) q_SKM_sk_num(SSL_CIPHER, (st))
+#define q_sk_SSL_CIPHER_num(st) q_SKM_sk_num((st))
#define q_sk_SSL_CIPHER_value(st, i) q_SKM_sk_value(SSL_CIPHER, (st), (i))
#define q_SSL_CTX_add_extra_chain_cert(ctx,x509) \
q_SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509)
@@ -747,6 +764,8 @@ int q_OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b);
void *q_CRYPTO_malloc(size_t num, const char *file, int line);
#define q_OPENSSL_malloc(num) q_CRYPTO_malloc(num, "", 0)
+void q_CRYPTO_free(void *str, const char *file, int line);
+#define q_OPENSSL_free(addr) q_CRYPTO_free(addr, "", 0)
int q_SSL_CTX_get_security_level(const SSL_CTX *ctx);
void q_SSL_CTX_set_security_level(SSL_CTX *ctx, int level);
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index 4618b723a1..1613c16809 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -220,6 +220,8 @@ private:
static bool s_loadedCiphersAndCerts;
protected:
bool verifyErrorsHaveBeenIgnored();
+ // Only implemented/useful in Schannel for now
+ virtual bool hasUndecryptedData() { return false; };
bool paused;
bool flushTriggered;
bool systemOrSslErrorDetected = false;
diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp
index f91709690a..c956ce3c2b 100644
--- a/src/network/ssl/qsslsocket_schannel.cpp
+++ b/src/network/ssl/qsslsocket_schannel.cpp
@@ -1253,6 +1253,9 @@ void QSslSocketBackendPrivate::transmit()
{
Q_Q(QSslSocket);
+ if (mode == QSslSocket::UnencryptedMode)
+ return; // This function should not have been called
+
// Can happen if called through QSslSocket::abort->QSslSocket::close->QSslSocket::flush->here
if (plainSocket->state() == QAbstractSocket::SocketState::UnconnectedState)
return;
diff --git a/src/network/ssl/qsslsocket_schannel_p.h b/src/network/ssl/qsslsocket_schannel_p.h
index fe29dadec0..e8b6836125 100644
--- a/src/network/ssl/qsslsocket_schannel_p.h
+++ b/src/network/ssl/qsslsocket_schannel_p.h
@@ -120,6 +120,8 @@ private:
bool rootCertOnDemandLoadingAllowed();
+ bool hasUndecryptedData() override { return intermediateBuffer.size() > 0; }
+
SecPkgContext_ConnectionInfo connectionInfo = {};
SecPkgContext_StreamSizes streamSizes = {};