From 5133e22ae200eb3c5169340a16b419c9fce747cb Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 30 Jan 2019 12:05:31 +0100 Subject: [PATCH] Add support for setting the peer verify name via the QNetwork* classes This adds functions to QNetworkRequest to be able to set the peerVerifyName. An overload of connectToHostEncrypted is also added to have an extra argument to allow setting it directly in this manner too. Fixes: QTBUG-73125 Change-Id: I371e90035b53a74c9eb3cef64f367e307dce073e Reviewed-by: Timur Pocheptsov --- src/network/access/qhttpnetworkconnection.cpp | 12 +++++++ src/network/access/qhttpnetworkconnection_p.h | 4 +++ .../access/qhttpnetworkconnectionchannel.cpp | 1 + src/network/access/qhttpnetworkrequest.cpp | 15 +++++++-- src/network/access/qhttpnetworkrequest_p.h | 3 ++ src/network/access/qhttpthreaddelegate.cpp | 13 ++++---- src/network/access/qnetworkaccessmanager.cpp | 29 +++++++++++++++++ src/network/access/qnetworkaccessmanager.h | 3 ++ src/network/access/qnetworkreplyhttpimpl.cpp | 1 + src/network/access/qnetworkrequest.cpp | 31 ++++++++++++++++++- src/network/access/qnetworkrequest.h | 2 ++ 11 files changed, 105 insertions(+), 9 deletions(-) diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 9fd6acb26c7..681d84fee86 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -1518,6 +1518,18 @@ void QHttpNetworkConnection::preConnectFinished() d_func()->preConnectRequests--; } +QString QHttpNetworkConnection::peerVerifyName() const +{ + Q_D(const QHttpNetworkConnection); + return d->peerVerifyName; +} + +void QHttpNetworkConnection::setPeerVerifyName(const QString &peerName) +{ + Q_D(QHttpNetworkConnection); + d->peerVerifyName = peerName; +} + #ifndef QT_NO_NETWORKPROXY // only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not // from QHttpNetworkConnectionChannel::handleAuthenticationChallenge diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index da54fbac2c4..2bd727e0afb 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -154,6 +154,8 @@ public: void preConnectFinished(); + QString peerVerifyName() const; + void setPeerVerifyName(const QString &peerName); private: Q_DECLARE_PRIVATE(QHttpNetworkConnection) Q_DISABLE_COPY_MOVE(QHttpNetworkConnection) @@ -289,6 +291,8 @@ public: Http2::ProtocolParameters http2Parameters; + QString peerVerifyName; + friend class QHttpNetworkConnectionChannel; }; diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 5726925cb03..9df3d73dd7d 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -392,6 +392,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection() if (!connection->sslContext().isNull()) QSslSocketPrivate::checkSettingSslContext(sslSocket, connection->sslContext()); + sslSocket->setPeerVerifyName(connection->d_func()->peerVerifyName); sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference); if (ignoreAllSslErrors) sslSocket->ignoreSslErrors(); diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp index 8de97607109..a3f71b8d2ff 100644 --- a/src/network/access/qhttpnetworkrequest.cpp +++ b/src/network/access/qhttpnetworkrequest.cpp @@ -66,7 +66,8 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest ssl(other.ssl), preConnect(other.preConnect), redirectCount(other.redirectCount), - redirectPolicy(other.redirectPolicy) + redirectPolicy(other.redirectPolicy), + peerVerifyName(other.peerVerifyName) { } @@ -90,7 +91,8 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot && (withCredentials == other.withCredentials) && (ssl == other.ssl) && (preConnect == other.preConnect) - && (redirectPolicy == other.redirectPolicy); + && (redirectPolicy == other.redirectPolicy) + && (peerVerifyName == other.peerVerifyName); } QByteArray QHttpNetworkRequest::methodName() const @@ -397,6 +399,15 @@ int QHttpNetworkRequest::minorVersion() const return 1; } +QString QHttpNetworkRequest::peerVerifyName() const +{ + return d->peerVerifyName; +} + +void QHttpNetworkRequest::setPeerVerifyName(const QString &peerName) +{ + d->peerVerifyName = peerName; +} QT_END_NAMESPACE diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h index bc797537ae2..fb4896195b8 100644 --- a/src/network/access/qhttpnetworkrequest_p.h +++ b/src/network/access/qhttpnetworkrequest_p.h @@ -147,6 +147,8 @@ public: QByteArray methodName() const; QByteArray uri(bool throughProxy) const; + QString peerVerifyName() const; + void setPeerVerifyName(const QString &peerName); private: QSharedDataPointer d; friend class QHttpNetworkRequestPrivate; @@ -182,6 +184,7 @@ public: bool preConnect; int redirectCount; QNetworkRequest::RedirectPolicy redirectPolicy; + QString peerVerifyName; }; diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp index 0e97acdd9d2..1fdf28df9d5 100644 --- a/src/network/access/qhttpthreaddelegate.cpp +++ b/src/network/access/qhttpthreaddelegate.cpp @@ -123,7 +123,7 @@ static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const } -static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy) +static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &peerVerifyName) { QString result; QUrl copy = url; @@ -170,7 +170,8 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy) #else Q_UNUSED(proxy) #endif - + if (!peerVerifyName.isEmpty()) + result += QLatin1Char(':') + peerVerifyName; return "http-connection:" + std::move(result).toLatin1(); } @@ -317,12 +318,12 @@ void QHttpThreadDelegate::startRequest() #ifndef QT_NO_NETWORKPROXY if (transparentProxy.type() != QNetworkProxy::NoProxy) - cacheKey = makeCacheKey(urlCopy, &transparentProxy); + cacheKey = makeCacheKey(urlCopy, &transparentProxy, httpRequest.peerVerifyName()); else if (cacheProxy.type() != QNetworkProxy::NoProxy) - cacheKey = makeCacheKey(urlCopy, &cacheProxy); + cacheKey = makeCacheKey(urlCopy, &cacheProxy, httpRequest.peerVerifyName()); else #endif - cacheKey = makeCacheKey(urlCopy, 0); + cacheKey = makeCacheKey(urlCopy, 0, httpRequest.peerVerifyName()); // the http object is actually a QHttpNetworkConnection @@ -352,7 +353,7 @@ void QHttpThreadDelegate::startRequest() httpConnection->setTransparentProxy(transparentProxy); httpConnection->setCacheProxy(cacheProxy); #endif - + httpConnection->setPeerVerifyName(httpRequest.peerVerifyName()); // cache the QHttpNetworkConnection corresponding to this cache key connections.localData()->addEntry(cacheKey, httpConnection); } else { diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 1f0d2f92e2d..8e788572590 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -1181,8 +1181,36 @@ QSharedPointer QNetworkAccessManagerPrivate::getNetworkSession( \sa connectToHost(), get(), post(), put(), deleteResource() */ + void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port, const QSslConfiguration &sslConfiguration) +{ + connectToHostEncrypted(hostName, port, sslConfiguration, QString()); +} + +/*! + \since 5.13 + \overload + + Initiates a connection to the host given by \a hostName at port \a port, using + \a sslConfiguration with \a peerName set to be the hostName used for certificate + validation. This function is useful to complete the TCP and SSL handshake + to a host before the HTTPS request is made, resulting in a lower network latency. + + \note Preconnecting a SPDY connection can be done by calling setAllowedNextProtocols() + on \a sslConfiguration with QSslConfiguration::NextProtocolSpdy3_0 contained in + the list of allowed protocols. When using SPDY, one single connection per host is + enough, i.e. calling this method multiple times per host will not result in faster + network transactions. + + \note This function has no possibility to report errors. + + \sa connectToHost(), get(), post(), put(), deleteResource() +*/ + +void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port, + const QSslConfiguration &sslConfiguration, + const QString &peerName) { QUrl url; url.setHost(hostName); @@ -1198,6 +1226,7 @@ void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quin QSslConfiguration::NextProtocolSpdy3_0)) request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); + request.setPeerVerifyName(peerName); get(request); } #endif diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h index 67b3a8b71b5..7e2f7683d0b 100644 --- a/src/network/access/qnetworkaccessmanager.h +++ b/src/network/access/qnetworkaccessmanager.h @@ -158,6 +158,9 @@ public: #ifndef QT_NO_SSL void connectToHostEncrypted(const QString &hostName, quint16 port = 443, const QSslConfiguration &sslConfiguration = QSslConfiguration::defaultConfiguration()); + void connectToHostEncrypted(const QString &hostName, quint16 port, + const QSslConfiguration &sslConfiguration, + const QString &peerName); #endif void connectToHost(const QString &hostName, quint16 port = 80); diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index ef54c198ba5..9378e058b13 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -785,6 +785,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq if (request.attribute(QNetworkRequest::EmitAllUploadProgressSignalsAttribute).toBool()) emitAllUploadProgressSignals = true; + httpRequest.setPeerVerifyName(newHttpRequest.peerVerifyName()); // Create the HTTP thread delegate QHttpThreadDelegate *delegate = new QHttpThreadDelegate; diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index e4c46c31839..f15c43cdd83 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -438,6 +438,7 @@ public: if (other.sslConfiguration) sslConfiguration = new QSslConfiguration(*other.sslConfiguration); #endif + peerVerifyName = other.peerVerifyName; } inline bool operator==(const QNetworkRequestPrivate &other) const @@ -446,7 +447,8 @@ public: priority == other.priority && rawHeaders == other.rawHeaders && attributes == other.attributes && - maxRedirectsAllowed == other.maxRedirectsAllowed; + maxRedirectsAllowed == other.maxRedirectsAllowed && + peerVerifyName == other.peerVerifyName; // don't compare cookedHeaders } @@ -456,6 +458,7 @@ public: mutable QSslConfiguration *sslConfiguration; #endif int maxRedirectsAllowed; + QString peerVerifyName; }; /*! @@ -789,6 +792,32 @@ void QNetworkRequest::setMaximumRedirectsAllowed(int maxRedirectsAllowed) d->maxRedirectsAllowed = maxRedirectsAllowed; } +/*! + \since 5.13 + + Returns the host name set for the certificate validation, as set by + setPeerVerifyName. By default this returns a null string. + + \sa setPeerVerifyName +*/ +QString QNetworkRequest::peerVerifyName() const +{ + return d->peerVerifyName; +} + +/*! + \since 5.13 + + Sets \a peerName as host name for the certificate validation, instead of the one used for the + TCP connection. + + \sa peerVerifyName +*/ +void QNetworkRequest::setPeerVerifyName(const QString &peerName) +{ + d->peerVerifyName = peerName; +} + static QByteArray headerName(QNetworkRequest::KnownHeaders header) { switch (header) { diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index 8462eae8c8e..efb9cbecba0 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -173,6 +173,8 @@ public: int maximumRedirectsAllowed() const; void setMaximumRedirectsAllowed(int maximumRedirectsAllowed); + QString peerVerifyName() const; + void setPeerVerifyName(const QString &peerName); private: QSharedDataPointer d; friend class QNetworkRequestPrivate;