diff options
Diffstat (limited to 'src/network')
129 files changed, 3548 insertions, 1389 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri index 8a92308f12..cfb20dcd71 100644 --- a/src/network/access/access.pri +++ b/src/network/access/access.pri @@ -97,7 +97,8 @@ qtConfig(http) { access/qhttpnetworkrequest.cpp \ access/qhttpprotocolhandler.cpp \ access/qhttpthreaddelegate.cpp \ - access/qnetworkreplyhttpimpl.cpp + access/qnetworkreplyhttpimpl.cpp \ + access/qhttp2configuration.cpp HEADERS += \ access/qabstractprotocolhandler_p.h \ @@ -111,7 +112,8 @@ qtConfig(http) { access/qhttpnetworkrequest_p.h \ access/qhttpprotocolhandler_p.h \ access/qhttpthreaddelegate_p.h \ - access/qnetworkreplyhttpimpl_p.h + access/qnetworkreplyhttpimpl_p.h \ + access/qhttp2configuration.h qtConfig(ssl) { SOURCES += \ diff --git a/src/network/access/http2/hpack.cpp b/src/network/access/http2/hpack.cpp index 2d324d5092..b40cc29e1a 100644 --- a/src/network/access/http2/hpack.cpp +++ b/src/network/access/http2/hpack.cpp @@ -208,6 +208,11 @@ void Encoder::setMaxDynamicTableSize(quint32 size) lookupTable.setMaxDynamicTableSize(size); } +void Encoder::setCompressStrings(bool compress) +{ + compressStrings = compress; +} + bool Encoder::encodeRequestPseudoHeaders(BitOStream &outputStream, const HttpHeader &header) { diff --git a/src/network/access/http2/hpack_p.h b/src/network/access/http2/hpack_p.h index 6a1d30d87b..8c2701e7af 100644 --- a/src/network/access/http2/hpack_p.h +++ b/src/network/access/http2/hpack_p.h @@ -83,6 +83,7 @@ public: quint32 newSize); void setMaxDynamicTableSize(quint32 size); + void setCompressStrings(bool compress); private: bool encodeRequestPseudoHeaders(BitOStream &outputStream, diff --git a/src/network/access/http2/http2frames.cpp b/src/network/access/http2/http2frames.cpp index e695b4dd9e..ce33505683 100644 --- a/src/network/access/http2/http2frames.cpp +++ b/src/network/access/http2/http2frames.cpp @@ -305,7 +305,7 @@ FrameStatus FrameReader::read(QAbstractSocket &socket) return status; } - if (Http2PredefinedParameters::maxFrameSize < frame.payloadSize()) + if (Http2PredefinedParameters::maxPayloadSize < frame.payloadSize()) return FrameStatus::sizeError; frame.buffer.resize(frame.payloadSize() + frameHeaderSize); @@ -388,7 +388,7 @@ void FrameWriter::setPayloadSize(quint32 size) auto &buffer = frame.buffer; Q_ASSERT(buffer.size() >= frameHeaderSize); - Q_ASSERT(size < maxPayloadSize); + Q_ASSERT(size <= maxPayloadSize); buffer[0] = size >> 16; buffer[1] = size >> 8; diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp index 0be72042c6..31da6fd616 100644 --- a/src/network/access/http2/http2protocol.cpp +++ b/src/network/access/http2/http2protocol.cpp @@ -43,6 +43,8 @@ #include "private/qhttpnetworkrequest_p.h" #include "private/qhttpnetworkreply_p.h" +#include <access/qhttp2configuration.h> + #include <QtCore/qbytearray.h> #include <QtCore/qstring.h> @@ -62,88 +64,29 @@ const char Http2clientPreface[clientPrefaceLength] = 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a}; -// TODO: (in 5.11) - remove it! -const char *http2ParametersPropertyName = "QT_HTTP2_PARAMETERS_PROPERTY"; - -ProtocolParameters::ProtocolParameters() -{ - settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID] = qtDefaultStreamReceiveWindowSize; - settingsFrameData[Settings::ENABLE_PUSH_ID] = 0; -} - -bool ProtocolParameters::validate() const +Frame configurationToSettingsFrame(const QHttp2Configuration &config) { - // 0. Huffman/indexing: any values are valid and allowed. - - // 1. Session receive window size (client side): HTTP/2 starts from the - // default value of 64Kb, if a client code tries to set lesser value, - // the delta would become negative, but this is not allowed. - if (maxSessionReceiveWindowSize < qint32(defaultSessionWindowSize)) { - qCWarning(QT_HTTP2, "Session receive window must be at least 65535 bytes"); - return false; - } - - // 2. HEADER_TABLE_SIZE: we do not validate HEADER_TABLE_SIZE, considering - // all values as valid. RFC 7540 and 7541 do not provide any lower/upper - // limits. If it's 0 - we do not index anything, if it's too huge - a user - // who provided such a value can potentially have a huge memory footprint, - // up to them to decide. - - // 3. SETTINGS_ENABLE_PUSH: RFC 7540, 6.5.2, a value other than 0 or 1 will - // be treated by our peer as a PROTOCOL_ERROR. - if (settingsFrameData.contains(Settings::ENABLE_PUSH_ID) - && settingsFrameData[Settings::ENABLE_PUSH_ID] > 1) { - qCWarning(QT_HTTP2, "SETTINGS_ENABLE_PUSH can be only 0 or 1"); - return false; - } - - // 4. SETTINGS_MAX_CONCURRENT_STREAMS : RFC 7540 recommends 100 as the lower - // limit, says nothing about the upper limit. The RFC allows 0, but this makes - // no sense to us at all: there is no way a user can change this later and - // we'll not be able to get any responses on such a connection. - if (settingsFrameData.contains(Settings::MAX_CONCURRENT_STREAMS_ID) - && !settingsFrameData[Settings::MAX_CONCURRENT_STREAMS_ID]) { - qCWarning(QT_HTTP2, "MAX_CONCURRENT_STREAMS must be a positive number"); - return false; - } - - // 5. SETTINGS_INITIAL_WINDOW_SIZE. - if (settingsFrameData.contains(Settings::INITIAL_WINDOW_SIZE_ID)) { - const quint32 value = settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID]; - // RFC 7540, 6.5.2 (the upper limit). The lower limit is our own - we send - // SETTINGS frame only once and will not be able to change this 0, thus - // we'll suspend all streams. - if (!value || value > quint32(maxSessionReceiveWindowSize)) { - qCWarning(QT_HTTP2, "INITIAL_WINDOW_SIZE must be in the range " - "(0, 2^31-1]"); - return false; - } - } - - // 6. SETTINGS_MAX_FRAME_SIZE: RFC 7540, 6.5.2, a value outside of the range - // [2^14-1, 2^24-1] will be treated by our peer as a PROTOCOL_ERROR. - if (settingsFrameData.contains(Settings::MAX_FRAME_SIZE_ID)) { - const quint32 value = settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID]; - if (value < maxFrameSize || value > maxPayloadSize) { - qCWarning(QT_HTTP2, "MAX_FRAME_SIZE must be in the range [2^14, 2^24-1]"); - return false; - } + // 6.5 SETTINGS + FrameWriter builder(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID); + // Server push: + builder.append(Settings::ENABLE_PUSH_ID); + builder.append(int(config.serverPushEnabled())); + // Stream receive window size: + builder.append(Settings::INITIAL_WINDOW_SIZE_ID); + builder.append(config.streamReceiveWindowSize()); + + if (config.maxFrameSize() != minPayloadLimit) { + builder.append(Settings::MAX_FRAME_SIZE_ID); + builder.append(config.maxFrameSize()); } - - // For SETTINGS_MAX_HEADER_LIST_SIZE RFC 7540 does not provide any specific - // numbers. It's clear, if a value is too small, no header can ever be sent - // by our peer at all. The default value is unlimited and we normally do not - // change this. - // - // Note: the size is calculated as the length of uncompressed (no HPACK) - // name + value + 32 bytes. - - return true; + // TODO: In future, if the need is proven, we can + // also send decoding table size and header list size. + // For now, defaults suffice. + return builder.outboundFrame(); } -QByteArray ProtocolParameters::settingsFrameToBase64() const +QByteArray settingsFrameToBase64(const Frame &frame) { - Frame frame(settingsFrame()); // SETTINGS frame's payload consists of pairs: // 2-byte-identifier | 4-byte-value == multiple of 6. Q_ASSERT(frame.payloadSize() && !(frame.payloadSize() % 6)); @@ -157,20 +100,7 @@ QByteArray ProtocolParameters::settingsFrameToBase64() const return wrapper.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); } -Frame ProtocolParameters::settingsFrame() const -{ - // 6.5 SETTINGS - FrameWriter builder(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID); - for (auto it = settingsFrameData.cbegin(), end = settingsFrameData.cend(); - it != end; ++it) { - builder.append(it.key()); - builder.append(it.value()); - } - - return builder.outboundFrame(); -} - -void ProtocolParameters::addProtocolUpgradeHeaders(QHttpNetworkRequest *request) const +void appendProtocolUpgradeHeaders(const QHttp2Configuration &config, QHttpNetworkRequest *request) { Q_ASSERT(request); // RFC 2616, 14.10 @@ -184,8 +114,10 @@ void ProtocolParameters::addProtocolUpgradeHeaders(QHttpNetworkRequest *request) request->setHeaderField("Connection", value); // This we just (re)write. request->setHeaderField("Upgrade", "h2c"); + + const Frame frame(configurationToSettingsFrame(config)); // This we just (re)write. - request->setHeaderField("HTTP2-Settings", settingsFrameToBase64()); + request->setHeaderField("HTTP2-Settings", settingsFrameToBase64(frame)); } void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h index 7142d6f1fa..b0af5aa919 100644 --- a/src/network/access/http2/http2protocol_p.h +++ b/src/network/access/http2/http2protocol_p.h @@ -55,12 +55,14 @@ #include <QtCore/qloggingcategory.h> #include <QtCore/qmetatype.h> #include <QtCore/qglobal.h> +#include <QtCore/qmap.h> // Different HTTP/2 constants/values as defined by RFC 7540. QT_BEGIN_NAMESPACE class QHttpNetworkRequest; +class QHttp2Configuration; class QHttpNetworkReply; class QByteArray; class QString; @@ -118,13 +120,19 @@ enum Http2PredefinedParameters connectionStreamID = 0, // HTTP/2, 5.1.1 frameHeaderSize = 9, // HTTP/2, 4.1 - // It's our max frame size we send in SETTINGS frame, - // it's also the default one and we also use it to later - // validate incoming frames: - maxFrameSize = 16384, // HTTP/2 6.5.2 + // The initial allowed payload size. We would use it as an + // upper limit for a frame payload we send, until our peer + // updates us with a larger SETTINGS_MAX_FRAME_SIZE. - defaultSessionWindowSize = 65535, // HTTP/2 6.5.2 + // The initial maximum payload size that an HTTP/2 frame + // can contain is 16384. It's also the minimal size that + // can be advertised via 'SETTINGS' frames. A real frame + // can have a payload smaller than 16384. + minPayloadLimit = 16384, // HTTP/2 6.5.2 + // The maximum allowed payload size. 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, @@ -145,48 +153,9 @@ const quint32 lastValidStreamID((quint32(1) << 31) - 1); // HTTP/2, 5.1.1 const qint32 maxSessionReceiveWindowSize((quint32(1) << 31) - 1); const qint32 qtDefaultStreamReceiveWindowSize = maxSessionReceiveWindowSize / maxConcurrentStreams; -// The class ProtocolParameters allows client code to customize HTTP/2 protocol -// handler, if needed. Normally, we use our own default parameters (see below). -// In 5.10 we can also use setProperty/property on a QNAM object to pass the -// non-default values to the protocol handler. In 5.11 this will probably become -// a public API. - -using RawSettings = QMap<Settings, quint32>; - -struct Q_AUTOTEST_EXPORT ProtocolParameters -{ - ProtocolParameters(); - - bool validate() const; - QByteArray settingsFrameToBase64() const; - struct Frame settingsFrame() const; - void addProtocolUpgradeHeaders(QHttpNetworkRequest *request) const; - - // HPACK: - // TODO: for now we ignore them (fix it for 5.11, would require changes in HPACK) - bool useHuffman = true; - bool indexStrings = true; - - // This parameter is not negotiated via SETTINGS frames, so we have it - // as a member and will convey it to our peer as a WINDOW_UPDATE frame. - // Note, some servers do not accept our WINDOW_UPDATE from the default - // 64 KB to the possible maximum. Let's use a half of it: - qint32 maxSessionReceiveWindowSize = Http2::maxSessionReceiveWindowSize / 2; - - // This is our default SETTINGS frame: - // - // SETTINGS_INITIAL_WINDOW_SIZE: (2^31 - 1) / 100 - // SETTINGS_ENABLE_PUSH: 0. - // - // Note, whenever we skip some value in our SETTINGS frame, our peer - // will assume the defaults recommended by RFC 7540, which in general - // are good enough, although we (and most browsers) prefer to work - // with larger window sizes. - RawSettings settingsFrameData; -}; - -// TODO: remove in 5.11 -extern const Q_AUTOTEST_EXPORT char *http2ParametersPropertyName; +struct Frame configurationToSettingsFrame(const QHttp2Configuration &configuration); +QByteArray settingsFrameToBase64(const Frame &settingsFrame); +void appendProtocolUpgradeHeaders(const QHttp2Configuration &configuration, QHttpNetworkRequest *request); extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength]; @@ -235,6 +204,5 @@ Q_DECLARE_LOGGING_CATEGORY(QT_HTTP2) QT_END_NAMESPACE Q_DECLARE_METATYPE(Http2::Settings) -Q_DECLARE_METATYPE(Http2::ProtocolParameters) #endif diff --git a/src/network/access/qabstractnetworkcache.h b/src/network/access/qabstractnetworkcache.h index 678bae2d6e..e357dfe58f 100644 --- a/src/network/access/qabstractnetworkcache.h +++ b/src/network/access/qabstractnetworkcache.h @@ -67,12 +67,10 @@ public: QNetworkCacheMetaData(const QNetworkCacheMetaData &other); ~QNetworkCacheMetaData(); -#ifdef Q_COMPILER_RVALUE_REFS - QNetworkCacheMetaData &operator=(QNetworkCacheMetaData &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QNetworkCacheMetaData &operator=(QNetworkCacheMetaData &&other) noexcept { swap(other); return *this; } QNetworkCacheMetaData &operator=(const QNetworkCacheMetaData &other); - void swap(QNetworkCacheMetaData &other) Q_DECL_NOTHROW + void swap(QNetworkCacheMetaData &other) noexcept { qSwap(d, other.d); } bool operator==(const QNetworkCacheMetaData &other) const; diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp index 4e399f018f..cc230a5411 100644 --- a/src/network/access/qftp.cpp +++ b/src/network/access/qftp.cpp @@ -955,11 +955,9 @@ void QFtpPI::readyRead() } } } - QString endOfMultiLine; - endOfMultiLine[0] = '0' + replyCode[0]; - endOfMultiLine[1] = '0' + replyCode[1]; - endOfMultiLine[2] = '0' + replyCode[2]; - endOfMultiLine[3] = QLatin1Char(' '); + const char count[4] = { char('0' + replyCode[0]), char('0' + replyCode[1]), + char('0' + replyCode[2]), char(' ') }; + QString endOfMultiLine(QLatin1String(count, 4)); QString lineCont(endOfMultiLine); lineCont[3] = QLatin1Char('-'); QStringRef lineLeft4 = line.leftRef(4); @@ -2124,6 +2122,17 @@ void QFtp::abort() /*! \internal + Clears the last error. + + \sa currentCommand() +*/ +void QFtp::clearError() +{ + d_func()->error = NoError; +} + +/*! + \internal Returns the identifier of the FTP command that is being executed or 0 if there is no command being executed. diff --git a/src/network/access/qftp_p.h b/src/network/access/qftp_p.h index 0516c3d1f9..a55429933b 100644 --- a/src/network/access/qftp_p.h +++ b/src/network/access/qftp_p.h @@ -67,7 +67,7 @@ class Q_AUTOTEST_EXPORT QFtp : public QObject Q_OBJECT public: - explicit QFtp(QObject *parent = 0); + explicit QFtp(QObject *parent = nullptr); virtual ~QFtp(); enum State { @@ -118,7 +118,7 @@ public: int setTransferMode(TransferMode mode); int list(const QString &dir = QString()); int cd(const QString &dir); - int get(const QString &file, QIODevice *dev=0, TransferType type = Binary); + int get(const QString &file, QIODevice *dev=nullptr, TransferType type = Binary); int put(const QByteArray &data, const QString &file, TransferType type = Binary); int put(QIODevice *dev, const QString &file, TransferType type = Binary); int remove(const QString &file); @@ -157,6 +157,9 @@ Q_SIGNALS: void commandFinished(int, bool); void done(bool); +protected: + void clearError(); + private: Q_DISABLE_COPY_MOVE(QFtp) Q_DECLARE_PRIVATE(QFtp) diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp index ce70b6af90..0cef0ad3dc 100644 --- a/src/network/access/qhsts.cpp +++ b/src/network/access/qhsts.cpp @@ -145,7 +145,7 @@ void QHstsCache::updateKnownHost(const QString &host, const QDateTime &expires, return; } - knownHosts.insert(pos, {hostName, newPolicy}); + knownHosts.insert({hostName, newPolicy}); #if QT_CONFIG(settings) if (hstsStore) hstsStore->addToObserved(newPolicy); @@ -156,7 +156,7 @@ void QHstsCache::updateKnownHost(const QString &host, const QDateTime &expires, if (newPolicy.isExpired()) knownHosts.erase(pos); else if (pos->second != newPolicy) - pos->second = std::move(newPolicy); + pos->second = newPolicy; else return; diff --git a/src/network/access/qhstspolicy.h b/src/network/access/qhstspolicy.h index 176a8fa635..f1b2ee99e5 100644 --- a/src/network/access/qhstspolicy.h +++ b/src/network/access/qhstspolicy.h @@ -65,10 +65,10 @@ public: QUrl::ParsingMode mode = QUrl::DecodedMode); QHstsPolicy(const QHstsPolicy &rhs); QHstsPolicy &operator=(const QHstsPolicy &rhs); - QHstsPolicy &operator=(QHstsPolicy &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QHstsPolicy &operator=(QHstsPolicy &&other) noexcept { swap(other); return *this; } ~QHstsPolicy(); - void swap(QHstsPolicy &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QHstsPolicy &other) noexcept { qSwap(d, other.d); } void setHost(const QString &host, QUrl::ParsingMode mode = QUrl::DecodedMode); QString host(QUrl::ComponentFormattingOptions options = QUrl::FullyDecoded) const; diff --git a/src/network/access/qhttp2configuration.cpp b/src/network/access/qhttp2configuration.cpp new file mode 100644 index 0000000000..bd4318d4e9 --- /dev/null +++ b/src/network/access/qhttp2configuration.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhttp2configuration.h" + +#include "private/http2protocol_p.h" +#include "private/hpack_p.h" + +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QHttp2Configuration + \brief The QHttp2Configuration class controls HTTP/2 parameters and settings. + \since 5.14 + + \reentrant + \inmodule QtNetwork + \ingroup network + \ingroup shared + + QHttp2Configuration controls HTTP/2 parameters and settings that + QNetworkAccessManager will use to send requests and process responses + when the HTTP/2 protocol is enabled. + + The HTTP/2 parameters that QHttp2Configuration currently supports include: + + \list + \li The session window size for connection-level flow control. + Will be sent to a remote peer when needed as 'WINDOW_UPDATE' + frames on the stream with an identifier 0. + \li The stream receiving window size for stream-level flow control. + Sent as 'SETTINGS_INITIAL_WINDOW_SIZE' parameter in the initial + SETTINGS frame and, when needed, 'WINDOW_UPDATE' frames will be + sent on streams that QNetworkAccessManager opens. + \li The maximum frame size. This parameter limits the maximum payload + a frame coming from the remote peer can have. Sent by QNetworkAccessManager + as 'SETTINGS_MAX_FRAME_SIZE' parameter in the initial 'SETTINGS' + frame. + \li The server push. Allows to enable or disable server push. Sent + as 'SETTINGS_ENABLE_PUSH' parameter in the initial 'SETTINGS' + frame. + \endlist + + The QHttp2Configuration class also controls if the header compression + algorithm (HPACK) is additionally using Huffman coding for string + compression. + + \note The configuration must be set before the first request + was sent to a given host (and thus an HTTP/2 session established). + + \note Details about flow control, server push and 'SETTINGS' + can be found in \l {https://httpwg.org/specs/rfc7540.html}{RFC 7540}. + Different modes and parameters of the HPACK compression algorithm + are described in \l {https://httpwg.org/specs/rfc7541.html}{RFC 7541}. + + \sa QNetworkRequest::setHttp2Configuration(), QNetworkRequest::http2Configuration(), QNetworkAccessManager +*/ + +class QHttp2ConfigurationPrivate : public QSharedData +{ +public: + unsigned sessionWindowSize = Http2::defaultSessionWindowSize; + // The size below is quite a limiting default value, QNetworkRequest + // by default sets a larger number, an application can change this using + // QNetworkRequest::setHttp2Configuration. + unsigned streamWindowSize = Http2::defaultSessionWindowSize; + + unsigned maxFrameSize = Http2::minPayloadLimit; // Initial (default) value of 16Kb. + + bool pushEnabled = false; + // TODO: for now those two below are noop. + bool huffmanCompressionEnabled = true; +}; + +/*! + Default constructs a QHttp2Configuration object. + + Such a configuration has the following values: + \list + \li Server push is disabled + \li Huffman string compression is enabled + \li Window size for connection-level flow control is 65535 octets + \li Window size for stream-level flow control is 65535 octets + \li Frame size is 16384 octets + \endlist +*/ +QHttp2Configuration::QHttp2Configuration() + : d(new QHttp2ConfigurationPrivate) +{ +} + +/*! + Copy-constructs this QHttp2Configuration. +*/ +QHttp2Configuration::QHttp2Configuration(const QHttp2Configuration &) = default; + +/*! + Move-constructs this QHttp2Configuration from \a other +*/ +QHttp2Configuration::QHttp2Configuration(QHttp2Configuration &&other) noexcept +{ + swap(other); +} + +/*! + Copy-assigns to this QHttp2Configuration. +*/ +QHttp2Configuration &QHttp2Configuration::operator=(const QHttp2Configuration &) = default; + +/*! + Move-assigns to this QHttp2Configuration. +*/ +QHttp2Configuration &QHttp2Configuration::operator=(QHttp2Configuration &&) noexcept = default; + +/*! + Destructor. +*/ +QHttp2Configuration::~QHttp2Configuration() +{ +} + +/*! + If \a enable is \c true, a remote server can potentially + use server push to send reponses in advance. + + \sa serverPushEnabled +*/ +void QHttp2Configuration::setServerPushEnabled(bool enable) +{ + d->pushEnabled = enable; +} + +/*! + Returns true if server push was enabled. + + \note By default, QNetworkAccessManager disables server + push via the 'SETTINGS' frame. + + \sa setServerPushEnabled +*/ +bool QHttp2Configuration::serverPushEnabled() const +{ + return d->pushEnabled; +} + +/*! + If \a enable is \c true, HPACK compression will additionally + compress string using the Huffman coding. Enabled by default. + + \note This parameter only affects 'HEADERS' frames that + QNetworkAccessManager is sending. + + \sa huffmanCompressionEnabled +*/ +void QHttp2Configuration::setHuffmanCompressionEnabled(bool enable) +{ + d->huffmanCompressionEnabled = enable; +} + +/*! + Returns \c true if the Huffman coding in HPACK is enabled. + + \sa setHuffmanCompressionEnabled +*/ +bool QHttp2Configuration::huffmanCompressionEnabled() const +{ + return d->huffmanCompressionEnabled; +} + +/*! + Sets the window size for connection-level flow control. + \a size cannot be 0 and must not exceed 2147483647 octets. + + \sa sessionReceiveWindowSize +*/ +bool QHttp2Configuration::setSessionReceiveWindowSize(unsigned size) +{ + if (!size || size > Http2::maxSessionReceiveWindowSize) { // RFC-7540, 6.9 + qCWarning(QT_HTTP2) << "Invalid session window size"; + return false; + } + + d->sessionWindowSize = size; + return true; +} + +/*! + Returns the window size for connection-level flow control. + The default value QNetworkAccessManager will be using is + 2147483647 octets. +*/ +unsigned QHttp2Configuration::sessionReceiveWindowSize() const +{ + return d->sessionWindowSize; +} + +/*! + Sets the window size for stream-level flow control. + \a size cannot be 0 and must not exceed 2147483647 octets. + + \sa streamReceiveWindowSize + */ +bool QHttp2Configuration::setStreamReceiveWindowSize(unsigned size) +{ + if (!size || size > Http2::maxSessionReceiveWindowSize) { // RFC-7540, 6.9 + qCWarning(QT_HTTP2) << "Invalid stream window size"; + return false; + } + + d->streamWindowSize = size; + return true; +} + +/*! + Returns the window size for stream-level flow control. + The default value QNetworkAccessManager will be using is + 21474836 octets. +*/ +unsigned QHttp2Configuration::streamReceiveWindowSize() const +{ + return d->streamWindowSize; +} + +/*! + Sets the maximum frame size that QNetworkAccessManager + will advertise to the server when sending its initial SETTINGS frame. + \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. +*/ +bool QHttp2Configuration::setMaxFrameSize(unsigned size) +{ + if (size < Http2::minPayloadLimit || size > Http2::maxPayloadSize) { + qCWarning(QT_HTTP2) << "Maximum frame size to advertise is invalid"; + return false; + } + + d->maxFrameSize = size; + return true; +} + +/*! + The maximum payload size that HTTP/2 frames can + have. The default (initial) value is 16384 octets. +*/ +unsigned QHttp2Configuration::maxFrameSize() const +{ + return d->maxFrameSize; +} + +/*! + Swaps this configuration with the \a other configuration. +*/ +void QHttp2Configuration::swap(QHttp2Configuration &other) noexcept +{ + d.swap(other.d); +} + +/*! + Returns \c true if \a lhs and \a rhs have the same set of HTTP/2 + parameters. +*/ +bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs) +{ + if (lhs.d == rhs.d) + return true; + + return lhs.d->pushEnabled == rhs.d->pushEnabled + && lhs.d->huffmanCompressionEnabled == rhs.d->huffmanCompressionEnabled + && lhs.d->sessionWindowSize == rhs.d->sessionWindowSize + && lhs.d->streamWindowSize == rhs.d->streamWindowSize; +} + +QT_END_NAMESPACE diff --git a/src/network/access/qhttp2configuration.h b/src/network/access/qhttp2configuration.h new file mode 100644 index 0000000000..e5c235e2be --- /dev/null +++ b/src/network/access/qhttp2configuration.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTP2CONFIGURATION_H +#define QHTTP2CONFIGURATION_H + +#include <QtNetwork/qtnetworkglobal.h> + +#include <QtCore/qshareddata.h> + +#ifndef Q_CLANG_QDOC +QT_REQUIRE_CONFIG(http); +#endif + +QT_BEGIN_NAMESPACE + +class QHttp2ConfigurationPrivate; +class Q_NETWORK_EXPORT QHttp2Configuration +{ + friend Q_NETWORK_EXPORT bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs); + +public: + QHttp2Configuration(); + QHttp2Configuration(const QHttp2Configuration &other); + QHttp2Configuration(QHttp2Configuration &&other) noexcept; + QHttp2Configuration &operator = (const QHttp2Configuration &other); + QHttp2Configuration &operator = (QHttp2Configuration &&other) noexcept; + + ~QHttp2Configuration(); + + void setServerPushEnabled(bool enable); + bool serverPushEnabled() const; + + void setHuffmanCompressionEnabled(bool enable); + bool huffmanCompressionEnabled() const; + + bool setSessionReceiveWindowSize(unsigned size); + unsigned sessionReceiveWindowSize() const; + + bool setStreamReceiveWindowSize(unsigned size); + unsigned streamReceiveWindowSize() const; + + bool setMaxFrameSize(unsigned size); + unsigned maxFrameSize() const; + + void swap(QHttp2Configuration &other) noexcept; + +private: + + QSharedDataPointer<QHttp2ConfigurationPrivate> d; +}; + +Q_DECLARE_SHARED(QHttp2Configuration) + +Q_NETWORK_EXPORT bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs); + +inline bool operator!=(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs) +{ + return !(lhs == rhs); +} + +QT_END_NAMESPACE + +#endif // QHTTP2CONFIGURATION_H diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp index 6609aa0594..dce51d4fd5 100644 --- a/src/network/access/qhttp2protocolhandler.cpp +++ b/src/network/access/qhttp2protocolhandler.cpp @@ -40,6 +40,7 @@ #include "qhttpnetworkconnection_p.h" #include "qhttp2protocolhandler_p.h" +#include "http2/http2frames_p.h" #include "http2/bitstreams_p.h" #include <private/qnoncontiguousbytedevice_p.h> @@ -51,6 +52,8 @@ #include <QtCore/qlist.h> #include <QtCore/qurl.h> +#include <qhttp2configuration.h> + #ifndef QT_NO_NETWORKPROXY #include <QtNetwork/qnetworkproxy.h> #endif @@ -174,30 +177,11 @@ QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *chan Q_ASSERT(channel && m_connection); continuedFrames.reserve(20); - const ProtocolParameters params(m_connection->http2Parameters()); - Q_ASSERT(params.validate()); - - maxSessionReceiveWindowSize = params.maxSessionReceiveWindowSize; - - const RawSettings &data = params.settingsFrameData; - for (auto param = data.cbegin(), end = data.cend(); param != end; ++param) { - switch (param.key()) { - case Settings::INITIAL_WINDOW_SIZE_ID: - streamInitialReceiveWindowSize = param.value(); - break; - case Settings::ENABLE_PUSH_ID: - pushPromiseEnabled = param.value(); - break; - case Settings::HEADER_TABLE_SIZE_ID: - case Settings::MAX_CONCURRENT_STREAMS_ID: - case Settings::MAX_FRAME_SIZE_ID: - case Settings::MAX_HEADER_LIST_SIZE_ID: - // These other settings are just recommendations to our peer. We - // only check they are not crazy in ProtocolParameters::validate(). - default: - break; - } - } + const auto h2Config = m_connection->http2Parameters(); + maxSessionReceiveWindowSize = h2Config.sessionReceiveWindowSize(); + pushPromiseEnabled = h2Config.serverPushEnabled(); + streamInitialReceiveWindowSize = h2Config.streamReceiveWindowSize(); + encoder.setCompressStrings(h2Config.huffmanCompressionEnabled()); if (!channel->ssl && m_connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { // We upgraded from HTTP/1.1 to HTTP/2. channel->request was already sent @@ -243,7 +227,8 @@ void QHttp2ProtocolHandler::_q_uploadDataReadyRead() auto data = qobject_cast<QNonContiguousByteDevice *>(sender()); Q_ASSERT(data); - const qint32 streamID = data->property("HTTP2StreamID").toInt(); + const qint32 streamID = streamIDs.value(data); + Q_ASSERT(streamID != 0); Q_ASSERT(activeStreams.contains(streamID)); auto &stream = activeStreams[streamID]; @@ -258,7 +243,7 @@ void QHttp2ProtocolHandler::_q_uploadDataReadyRead() void QHttp2ProtocolHandler::_q_replyDestroyed(QObject *reply) { - const quint32 streamID = reply->property("HTTP2StreamID").toInt(); + const quint32 streamID = streamIDs.take(reply); if (activeStreams.contains(streamID)) { sendRST_STREAM(streamID, CANCEL); markAsReset(streamID); @@ -266,6 +251,11 @@ void QHttp2ProtocolHandler::_q_replyDestroyed(QObject *reply) } } +void QHttp2ProtocolHandler::_q_uploadDataDestroyed(QObject *uploadData) +{ + streamIDs.remove(uploadData); +} + void QHttp2ProtocolHandler::_q_readyRead() { _q_receiveReply(); @@ -441,20 +431,17 @@ bool QHttp2ProtocolHandler::sendClientPreface() return false; // 6.5 SETTINGS - const ProtocolParameters params(m_connection->http2Parameters()); - Q_ASSERT(params.validate()); - frameWriter.setOutboundFrame(params.settingsFrame()); + frameWriter.setOutboundFrame(Http2::configurationToSettingsFrame(m_connection->http2Parameters())); Q_ASSERT(frameWriter.outboundFrame().payloadSize()); if (!frameWriter.write(*m_socket)) return false; sessionReceiveWindowSize = maxSessionReceiveWindowSize; - // ProtocolParameters::validate does not allow maxSessionReceiveWindowSize - // to be smaller than defaultSessionWindowSize, so everything is OK here with - // 'delta': + // We only send WINDOW_UPDATE for the connection if the size differs from the + // default 64 KB: const auto delta = maxSessionReceiveWindowSize - Http2::defaultSessionWindowSize; - if (!sendWINDOW_UPDATE(Http2::connectionStreamID, delta)) + if (delta && !sendWINDOW_UPDATE(Http2::connectionStreamID, delta)) return false; prefaceSent = true; @@ -1088,7 +1075,7 @@ bool QHttp2ProtocolHandler::acceptSetting(Http2::Settings identifier, quint32 ne } if (identifier == Settings::MAX_FRAME_SIZE_ID) { - if (newValue < Http2::maxFrameSize || newValue > Http2::maxPayloadSize) { + if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) { connectionError(PROTOCOL_ERROR, "SETTGINGS max frame size is out of range"); return false; } @@ -1295,7 +1282,7 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, b replyPrivate->connection = m_connection; replyPrivate->connectionChannel = m_channel; reply->setSpdyWasUsed(true); - reply->setProperty("HTTP2StreamID", newStreamID); + streamIDs.insert(reply, newStreamID); connect(reply, SIGNAL(destroyed(QObject*)), this, SLOT(_q_replyDestroyed(QObject*))); @@ -1307,7 +1294,9 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, b if (auto src = newStream.data()) { connect(src, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection); - src->setProperty("HTTP2StreamID", newStreamID); + connect(src, &QHttp2ProtocolHandler::destroyed, + this, &QHttp2ProtocolHandler::_q_uploadDataDestroyed); + streamIDs.insert(src, newStreamID); } } @@ -1350,14 +1339,13 @@ void QHttp2ProtocolHandler::markAsReset(quint32 streamID) quint32 QHttp2ProtocolHandler::popStreamToResume() { quint32 streamID = connectionStreamID; - const int nQ = sizeof suspendedStreams / sizeof suspendedStreams[0]; using QNR = QHttpNetworkRequest; - const QNR::Priority ranks[nQ] = {QNR::HighPriority, - QNR::NormalPriority, - QNR::LowPriority}; + const QNR::Priority ranks[] = {QNR::HighPriority, + QNR::NormalPriority, + QNR::LowPriority}; - for (int i = 0; i < nQ; ++i) { - auto &queue = suspendedStreams[ranks[i]]; + for (const QNR::Priority rank : ranks) { + auto &queue = suspendedStreams[rank]; auto it = queue.begin(); for (; it != queue.end(); ++it) { if (!activeStreams.contains(*it)) @@ -1378,9 +1366,7 @@ quint32 QHttp2ProtocolHandler::popStreamToResume() void QHttp2ProtocolHandler::removeFromSuspended(quint32 streamID) { - const int nQ = sizeof suspendedStreams / sizeof suspendedStreams[0]; - for (int i = 0; i < nQ; ++i) { - auto &q = suspendedStreams[i]; + for (auto &q : suspendedStreams) { q.erase(std::remove(q.begin(), q.end(), streamID), q.end()); } } @@ -1389,10 +1375,14 @@ void QHttp2ProtocolHandler::deleteActiveStream(quint32 streamID) { if (activeStreams.contains(streamID)) { auto &stream = activeStreams[streamID]; - if (stream.reply()) + if (stream.reply()) { stream.reply()->disconnect(this); - if (stream.data()) + streamIDs.remove(stream.reply()); + } + if (stream.data()) { stream.data()->disconnect(this); + streamIDs.remove(stream.data()); + } activeStreams.remove(streamID); } diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h index d91853f613..43fdb136cd 100644 --- a/src/network/access/qhttp2protocolhandler_p.h +++ b/src/network/access/qhttp2protocolhandler_p.h @@ -55,6 +55,8 @@ #include <private/qabstractprotocolhandler_p.h> #include <private/qhttpnetworkrequest_p.h> +#include <access/qhttp2configuration.h> + #include <private/http2protocol_p.h> #include <private/http2streams_p.h> #include <private/http2frames_p.h> @@ -95,6 +97,7 @@ public: private slots: void _q_uploadDataReadyRead(); void _q_replyDestroyed(QObject* reply); + void _q_uploadDataDestroyed(QObject* uploadData); private: using Stream = Http2::Stream; @@ -158,13 +161,15 @@ private: HPack::Decoder decoder; HPack::Encoder encoder; + QHash<QObject *, int> streamIDs; QHash<quint32, Stream> activeStreams; std::deque<quint32> suspendedStreams[3]; // 3 for priorities: High, Normal, Low. static const std::deque<quint32>::size_type maxRecycledStreams; std::deque<quint32> recycledStreams; - // Peer's max frame size. - quint32 maxFrameSize = Http2::maxFrameSize; + // Peer's max frame size (this min is the default value + // we start with, that can be updated by SETTINGS frame): + quint32 maxFrameSize = Http2::minPayloadLimit; Http2::FrameReader frameReader; Http2::Frame inboundFrame; @@ -176,28 +181,28 @@ private: // Control flow: - // This is how many concurrent streams our peer expects from us: - // 100 is the default value, can be updated by the server's SETTINGS - // frame(s): + // This is how many concurrent streams our peer allows us, 100 is the + // initial value, can be updated by the server's SETTINGS frame(s): quint32 maxConcurrentStreams = Http2::maxConcurrentStreams; // While we allow sending SETTTINGS_MAX_CONCURRENT_STREAMS to limit our peer, // it's just a hint and we do not actually enforce it (and we can continue // sending requests and creating streams while maxConcurrentStreams allows). - // This is the max value, we set it in a ctor from Http2::ProtocolParameters, - // it does not change after that. + // This is our (client-side) maximum possible receive window size, we set + // it in a ctor from QHttp2Configuration, it does not change after that. + // The default is 64Kb: qint32 maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize; - // Our session receive window size, default is 64Kb. We'll update it from QNAM's - // Http2::ProtocolParameters. Signed integer since it can become negative + // Our session current receive window size, updated in a ctor from + // QHttp2Configuration. Signed integer since it can become negative // (it's still a valid window size). qint32 sessionReceiveWindowSize = Http2::defaultSessionWindowSize; // Our per-stream receive window size, default is 64 Kb, will be updated - // from QNAM's Http2::ProtocolParameters. Again, signed - can become negative. + // from QHttp2Configuration. Again, signed - can become negative. qint32 streamInitialReceiveWindowSize = Http2::defaultSessionWindowSize; // These are our peer's receive window sizes, they will be updated by the - // peer's SETTINGS and WINDOW_UPDATE frames. + // peer's SETTINGS and WINDOW_UPDATE frames, defaults presumed to be 64Kb. qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize; qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize; diff --git a/src/network/access/qhttpmultipart.h b/src/network/access/qhttpmultipart.h index 78585a704d..56db83779a 100644 --- a/src/network/access/qhttpmultipart.h +++ b/src/network/access/qhttpmultipart.h @@ -60,12 +60,10 @@ public: QHttpPart(); QHttpPart(const QHttpPart &other); ~QHttpPart(); -#ifdef Q_COMPILER_RVALUE_REFS - QHttpPart &operator=(QHttpPart &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QHttpPart &operator=(QHttpPart &&other) noexcept { swap(other); return *this; } QHttpPart &operator=(const QHttpPart &other); - void swap(QHttpPart &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QHttpPart &other) noexcept { qSwap(d, other.d); } bool operator==(const QHttpPart &other) const; inline bool operator!=(const QHttpPart &other) const diff --git a/src/network/access/qhttpmultipart_p.h b/src/network/access/qhttpmultipart_p.h index 363e0b346c..ead1eadf3b 100644 --- a/src/network/access/qhttpmultipart_p.h +++ b/src/network/access/qhttpmultipart_p.h @@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE class QHttpPartPrivate: public QSharedData, public QNetworkHeadersPrivate { public: - inline QHttpPartPrivate() : bodyDevice(0), headerCreated(false), readPointer(0) + inline QHttpPartPrivate() : bodyDevice(nullptr), headerCreated(false), readPointer(0) { } ~QHttpPartPrivate() diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 294273d751..21c6359807 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -398,11 +398,12 @@ void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthentica { Q_ASSERT(auth); - // NTLM is a multi phase authentication. Copying credentials between authenticators would mess things up. + // NTLM and Negotiate do multi-phase authentication. + // Copying credentialsbetween authenticators would mess things up. if (fromChannel >= 0) { - if (!isProxy && channels[fromChannel].authMethod == QAuthenticatorPrivate::Ntlm) - return; - if (isProxy && channels[fromChannel].proxyAuthMethod == QAuthenticatorPrivate::Ntlm) + const QHttpNetworkConnectionChannel &channel = channels[fromChannel]; + const QAuthenticatorPrivate::Method method = isProxy ? channel.proxyAuthMethod : channel.authMethod; + if (method == QAuthenticatorPrivate::Ntlm || method == QAuthenticatorPrivate::Negotiate) return; } @@ -592,24 +593,26 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, 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)); + QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), request.url().host()); request.setHeaderField("Authorization", response); channels[i].authenticationCredentialsSent = true; } } } +#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)); + QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), networkProxy.hostName()); request.setHeaderField("Proxy-Authorization", response); channels[i].proxyCredentialsSent = true; } } } +#endif // QT_CONFIG(networkproxy) } QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request) @@ -1318,8 +1321,12 @@ QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt, connectionType)), parent) { Q_D(QHttpNetworkConnection); - d->networkSession = qMove(networkSession); + d->networkSession = std::move(networkSession); d->init(); + if (QNetworkStatusMonitor::isEnabled()) { + connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged, + this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection); + } } QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, @@ -1330,8 +1337,12 @@ QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QS connectionType)), parent) { Q_D(QHttpNetworkConnection); - d->networkSession = qMove(networkSession); + d->networkSession = std::move(networkSession); d->init(); + if (QNetworkStatusMonitor::isEnabled()) { + connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged, + this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection); + } } #else QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, @@ -1340,6 +1351,10 @@ QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 { Q_D(QHttpNetworkConnection); d->init(); + if (QNetworkStatusMonitor::isEnabled()) { + connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged, + this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection); + } } QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, @@ -1350,8 +1365,12 @@ QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QS { Q_D(QHttpNetworkConnection); d->init(); + if (QNetworkStatusMonitor::isEnabled()) { + connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged, + this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection); + } } -#endif +#endif // QT_NO_BEARERMANAGEMENT QHttpNetworkConnection::~QHttpNetworkConnection() { @@ -1438,21 +1457,16 @@ void QHttpNetworkConnection::setConnectionType(ConnectionType type) d->connectionType = type; } -Http2::ProtocolParameters QHttpNetworkConnection::http2Parameters() const +QHttp2Configuration QHttpNetworkConnection::http2Parameters() const { Q_D(const QHttpNetworkConnection); return d->http2Parameters; } -void QHttpNetworkConnection::setHttp2Parameters(const Http2::ProtocolParameters ¶ms) +void QHttpNetworkConnection::setHttp2Parameters(const QHttp2Configuration ¶ms) { Q_D(QHttpNetworkConnection); - if (params.validate()) { - d->http2Parameters = params; - } else { - qCWarning(QT_HTTP2) - << "invalid HTTP/2 parameters, falling back to defaults instead"; - } + d->http2Parameters = params; } // SSL support below @@ -1477,7 +1491,7 @@ QSharedPointer<QSslContext> QHttpNetworkConnection::sslContext() void QHttpNetworkConnection::setSslContext(QSharedPointer<QSslContext> context) { Q_D(QHttpNetworkConnection); - d->sslContext = qMove(context); + d->sslContext = std::move(context); } void QHttpNetworkConnection::ignoreSslErrors(int channel) @@ -1531,6 +1545,26 @@ void QHttpNetworkConnection::setPeerVerifyName(const QString &peerName) d->peerVerifyName = peerName; } +void QHttpNetworkConnection::onlineStateChanged(bool isOnline) +{ + Q_D(QHttpNetworkConnection); + + if (isOnline) { + // If we did not have any 'isOffline' previously - well, good + // to know, we are 'online' apparently. + return; + } + + for (int i = 0; i < d->activeChannelCount; i++) { + auto &channel = d->channels[i]; + channel.emitFinishedWithError(QNetworkReply::TemporaryNetworkFailureError, "Temporary network failure."); + channel.close(); + } + + // We don't care, this connection is broken from our POV. + d->connectionMonitor.stopMonitoring(); +} + #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 2bd727e0af..6808a0c0ac 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -57,6 +57,8 @@ #include <QtNetwork/qabstractsocket.h> #include <QtNetwork/qnetworksession.h> +#include <qhttp2configuration.h> + #include <private/qobject_p.h> #include <qauthenticator.h> #include <qnetworkproxy.h> @@ -67,6 +69,7 @@ #include <private/qhttpnetworkheader_p.h> #include <private/qhttpnetworkrequest_p.h> #include <private/qhttpnetworkreply_p.h> +#include <private/qnetconmonitor_p.h> #include <private/http2protocol_p.h> #include <private/qhttpnetworkconnectionchannel_p.h> @@ -101,10 +104,10 @@ public: #ifndef QT_NO_BEARERMANAGEMENT explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, ConnectionType connectionType = ConnectionTypeHTTP, - QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession + QObject *parent = nullptr, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>()); QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, - bool encrypt = false, QObject *parent = 0, + bool encrypt = false, QObject *parent = nullptr, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>(), ConnectionType connectionType = ConnectionTypeHTTP); #else @@ -141,8 +144,8 @@ public: ConnectionType connectionType(); void setConnectionType(ConnectionType type); - Http2::ProtocolParameters http2Parameters() const; - void setHttp2Parameters(const Http2::ProtocolParameters ¶ms); + QHttp2Configuration http2Parameters() const; + void setHttp2Parameters(const QHttp2Configuration ¶ms); #ifndef QT_NO_SSL void setSslConfiguration(const QSslConfiguration &config); @@ -156,6 +159,10 @@ public: QString peerVerifyName() const; void setPeerVerifyName(const QString &peerName); + +public slots: + void onlineStateChanged(bool isOnline); + private: Q_DECLARE_PRIVATE(QHttpNetworkConnection) Q_DISABLE_COPY_MOVE(QHttpNetworkConnection) @@ -289,9 +296,17 @@ public: QSharedPointer<QNetworkSession> networkSession; #endif - Http2::ProtocolParameters http2Parameters; + QHttp2Configuration http2Parameters; QString peerVerifyName; + // If network status monitoring is enabled, we activate connectionMonitor + // as soons as one of channels managed to connect to host (and we + // have a pair of addresses (us,peer). + // NETMONTODO: consider activating a monitor on a change from + // HostLookUp state to ConnectingState (means we have both + // local/remote addresses known and can start monitoring this + // early). + QNetworkConnectionMonitor connectionMonitor; friend class QHttpNetworkConnectionChannel; }; diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 8f94cef32b..39f392a79b 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -40,6 +40,7 @@ #include "qhttpnetworkconnectionchannel_p.h" #include "qhttpnetworkconnection_p.h" +#include "qhttp2configuration.h" #include "private/qnoncontiguousbytedevice_p.h" #include <qpair.h> @@ -48,6 +49,7 @@ #include <private/qhttp2protocolhandler_p.h> #include <private/qhttpprotocolhandler_p.h> #include <private/qspdyprotocolhandler_p.h> +#include <private/http2protocol_p.h> #ifndef QT_NO_SSL # include <private/qsslsocket_p.h> @@ -59,6 +61,8 @@ #include "private/qnetworksession_p.h" #endif +#include "private/qnetconmonitor_p.h" + QT_BEGIN_NAMESPACE namespace @@ -902,6 +906,16 @@ void QHttpNetworkConnectionChannel::_q_connected() pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; + if (QNetworkStatusMonitor::isEnabled()) { + auto connectionPrivate = connection->d_func(); + if (!connectionPrivate->connectionMonitor.isMonitoring()) { + // Now that we have a pair of addresses, we can start monitoring the + // connection status to handle its loss properly. + if (connectionPrivate->connectionMonitor.setTargets(socket->localAddress(), socket->peerAddress())) + connectionPrivate->connectionMonitor.startMonitoring(); + } + } + // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! //channels[i].reconnectAttempts = 2; if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState @@ -938,9 +952,7 @@ void QHttpNetworkConnectionChannel::_q_connected() if (tryProtocolUpgrade) { // Let's augment our request with some magic headers and try to // switch to HTTP/2. - const Http2::ProtocolParameters params(connection->http2Parameters()); - Q_ASSERT(params.validate()); - params.addProtocolUpgradeHeaders(&request); + Http2::appendProtocolUpgradeHeaders(connection->http2Parameters(), &request); } sendRequest(); } diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index c9c3172304..a8b635c45a 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -444,6 +444,9 @@ QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(boo } else if (method < QAuthenticatorPrivate::DigestMd5 && line.startsWith("digest")) { method = QAuthenticatorPrivate::DigestMd5; + } else if (method < QAuthenticatorPrivate::Negotiate + && line.startsWith("negotiate")) { + method = QAuthenticatorPrivate::Negotiate; } } return method; diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 863e21ea3e..12cfe359aa 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -89,7 +89,7 @@ class Q_AUTOTEST_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkH Q_OBJECT public: - explicit QHttpNetworkReply(const QUrl &url = QUrl(), QObject *parent = 0); + explicit QHttpNetworkReply(const QUrl &url = QUrl(), QObject *parent = nullptr); virtual ~QHttpNetworkReply(); QUrl url() const override; diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp index 46a6615f4d..63a3c4f204 100644 --- a/src/network/access/qhttpthreaddelegate.cpp +++ b/src/network/access/qhttpthreaddelegate.cpp @@ -190,7 +190,7 @@ public: QHttpNetworkConnection::ConnectionType connectionType, QSharedPointer<QNetworkSession> networkSession) : QHttpNetworkConnection(hostName, port, encrypt, connectionType, /*parent=*/0, - qMove(networkSession)) + std::move(networkSession)) #endif { setExpires(true); @@ -354,9 +354,9 @@ void QHttpThreadDelegate::startRequest() networkSession); #endif // QT_NO_BEARERMANAGEMENT if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 - && http2Parameters.validate()) { + || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { httpConnection->setHttp2Parameters(http2Parameters); - } // else we ignore invalid parameters and use our own defaults. + } #ifndef QT_NO_SSL // Set the QSslConfiguration from this QNetworkRequest. if (ssl) diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h index 019a8b8b74..355d1afc30 100644 --- a/src/network/access/qhttpthreaddelegate_p.h +++ b/src/network/access/qhttpthreaddelegate_p.h @@ -62,6 +62,7 @@ #include <QNetworkReply> #include "qhttpnetworkrequest_p.h" #include "qhttpnetworkconnection_p.h" +#include "qhttp2configuration.h" #include <QSharedPointer> #include <QScopedPointer> #include "private/qnoncontiguousbytedevice_p.h" @@ -82,7 +83,7 @@ class QHttpThreadDelegate : public QObject { Q_OBJECT public: - explicit QHttpThreadDelegate(QObject *parent = 0); + explicit QHttpThreadDelegate(QObject *parent = nullptr); ~QHttpThreadDelegate(); @@ -116,7 +117,7 @@ public: qint64 removedContentLength; QNetworkReply::NetworkError incomingErrorCode; QString incomingErrorDetail; - Http2::ProtocolParameters http2Parameters; + QHttp2Configuration http2Parameters; #ifndef QT_NO_BEARERMANAGEMENT QSharedPointer<QNetworkSession> networkSession; #endif @@ -207,7 +208,7 @@ public: : QNonContiguousByteDevice(), wantDataPending(false), m_amount(0), - m_data(0), + m_data(nullptr), m_atEnd(aE), m_size(s), m_pos(0) @@ -240,12 +241,12 @@ public: // Do nothing, we already sent a wantData signal and wait for results len = 0; } - return 0; + return nullptr; } bool advanceReadPointer(qint64 a) override { - if (m_data == 0) + if (m_data == nullptr) return false; m_amount -= a; @@ -269,7 +270,7 @@ public: bool reset() override { m_amount = 0; - m_data = 0; + m_data = nullptr; m_dataArray.clear(); if (wantDataPending) { diff --git a/src/network/access/qnetworkaccessauthenticationmanager_p.h b/src/network/access/qnetworkaccessauthenticationmanager_p.h index 548675728f..31111ca2a5 100644 --- a/src/network/access/qnetworkaccessauthenticationmanager_p.h +++ b/src/network/access/qnetworkaccessauthenticationmanager_p.h @@ -90,12 +90,12 @@ public: void cacheCredentials(const QUrl &url, const QAuthenticator *auth); QNetworkAuthenticationCredential fetchCachedCredentials(const QUrl &url, - const QAuthenticator *auth = 0); + const QAuthenticator *auth = nullptr); #ifndef QT_NO_NETWORKPROXY void cacheProxyCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth); QNetworkAuthenticationCredential fetchCachedProxyCredentials(const QNetworkProxy &proxy, - const QAuthenticator *auth = 0); + const QAuthenticator *auth = nullptr); #endif void clearCache(); diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index 272dd22097..566e410051 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE class QNetworkAccessBackendFactoryData: public QList<QNetworkAccessBackendFactory *> { public: - QNetworkAccessBackendFactoryData() : mutex(QMutex::Recursive) + QNetworkAccessBackendFactoryData() { valid.ref(); } @@ -68,7 +68,7 @@ public: valid.deref(); } - QMutex mutex; + QRecursiveMutex mutex; //this is used to avoid (re)constructing factory data from destructors of other global classes static QBasicAtomicInt valid; }; @@ -83,7 +83,7 @@ QNetworkAccessBackendFactory::QNetworkAccessBackendFactory() QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory() { - if (QNetworkAccessBackendFactoryData::valid.load()) { + if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) { QMutexLocker locker(&factoryData()->mutex); factoryData()->removeAll(this); } @@ -92,7 +92,7 @@ QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory() QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op, const QNetworkRequest &request) { - if (QNetworkAccessBackendFactoryData::valid.load()) { + if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) { QMutexLocker locker(&factoryData()->mutex); QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin(), end = factoryData()->constEnd(); @@ -110,7 +110,7 @@ QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessM QStringList QNetworkAccessManagerPrivate::backendSupportedSchemes() const { - if (QNetworkAccessBackendFactoryData::valid.load()) { + if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) { QMutexLocker locker(&factoryData()->mutex); QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin(); QNetworkAccessBackendFactoryData::ConstIterator end = factoryData()->constEnd(); diff --git a/src/network/access/qnetworkaccesscache.cpp b/src/network/access/qnetworkaccesscache.cpp index 00bb18cb82..b694a2c999 100644 --- a/src/network/access/qnetworkaccesscache.cpp +++ b/src/network/access/qnetworkaccesscache.cpp @@ -40,11 +40,12 @@ #include "qnetworkaccesscache_p.h" #include "QtCore/qpointer.h" #include "QtCore/qdatetime.h" -#include "QtCore/qqueue.h" #include "qnetworkaccessmanager_p.h" #include "qnetworkreply_p.h" #include "qnetworkrequest.h" +#include <vector> + QT_BEGIN_NAMESPACE enum ExpiryTimeEnum { @@ -63,7 +64,7 @@ namespace { struct QNetworkAccessCache::Node { QDateTime timestamp; - QQueue<Receiver> receiverQueue; + std::vector<Receiver> receiverQueue; QByteArray key; Node *older, *newer; @@ -277,10 +278,7 @@ bool QNetworkAccessCache::requestEntry(const QByteArray &key, QObject *target, c // object is not shareable and is in use // queue for later use Q_ASSERT(node->older == 0 && node->newer == 0); - Receiver receiver; - receiver.object = target; - receiver.member = member; - node->receiverQueue.enqueue(receiver); + node->receiverQueue.push_back({target, member}); // request queued return true; @@ -331,17 +329,19 @@ void QNetworkAccessCache::releaseEntry(const QByteArray &key) Q_ASSERT(node->useCount > 0); // are there other objects waiting? - if (!node->receiverQueue.isEmpty()) { + const auto objectStillExists = [](const Receiver &r) { return !r.object.isNull(); }; + + auto &queue = node->receiverQueue; + auto qit = std::find_if(queue.begin(), queue.end(), objectStillExists); + + const Receiver receiver = qit == queue.end() ? Receiver{} : std::move(*qit++) ; + + queue.erase(queue.begin(), qit); + + if (receiver.object) { // queue another activation - Receiver receiver; - do { - receiver = node->receiverQueue.dequeue(); - } while (receiver.object.isNull() && !node->receiverQueue.isEmpty()); - - if (!receiver.object.isNull()) { - emitEntryReady(node, receiver.object, receiver.member); - return; - } + emitEntryReady(node, receiver.object, receiver.member); + return; } if (!--node->useCount) { diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp index 5ad820eba0..51ed2f5a55 100644 --- a/src/network/access/qnetworkaccessftpbackend.cpp +++ b/src/network/access/qnetworkaccessftpbackend.cpp @@ -99,6 +99,8 @@ public: connect(this, SIGNAL(done(bool)), this, SLOT(deleteLater())); close(); } + + using QFtp::clearError; }; QNetworkAccessFtpBackend::QNetworkAccessFtpBackend() @@ -282,7 +284,10 @@ void QNetworkAccessFtpBackend::ftpDone() } // check for errors: - if (ftp->error() != QFtp::NoError) { + if (state == CheckingFeatures && ftp->error() == QFtp::UnknownError) { + qWarning("QNetworkAccessFtpBackend: HELP command failed, ignoring it"); + ftp->clearError(); + } else if (ftp->error() != QFtp::NoError) { QString msg; if (operation() == QNetworkAccessManager::GetOperation) msg = tr("Error while downloading %1: %2"); diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index b9c8116421..76b95b5823 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -70,6 +70,7 @@ #include "QtNetwork/private/qauthenticator_p.h" #include "QtNetwork/qsslconfiguration.h" #include "QtNetwork/qnetworkconfigmanager.h" +#include "QtNetwork/private/http2protocol_p.h" #if QT_CONFIG(http) #include "qhttpmultipart.h" @@ -90,6 +91,8 @@ #include "qnetworkreplywasmimpl_p.h" #endif +#include "qnetconmonitor_p.h" + QT_BEGIN_NAMESPACE Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend) @@ -486,18 +489,26 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent) qRegisterMetaType<QNetworkReply::NetworkError>(); qRegisterMetaType<QSharedPointer<char> >(); -#ifndef QT_NO_BEARERMANAGEMENT Q_D(QNetworkAccessManager); - // if a session is required, we track online state through - // the QNetworkSession's signals if a request is already made. - // we need to track current accessibility state by default - // - connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)), - SLOT(_q_onlineStateChanged(bool))); - connect(&d->networkConfigurationManager, SIGNAL(configurationChanged(QNetworkConfiguration)), - SLOT(_q_configurationChanged(QNetworkConfiguration))); -#endif + if (QNetworkStatusMonitor::isEnabled()) { + connect(&d->statusMonitor, SIGNAL(onlineStateChanged(bool)), + SLOT(_q_onlineStateChanged(bool))); +#ifdef QT_NO_BEARERMANAGEMENT + d->networkAccessible = d->statusMonitor.isNetworkAccessible(); +#else + d->networkAccessible = d->statusMonitor.isNetworkAccessible() ? Accessible : NotAccessible; + } else { + // if a session is required, we track online state through + // the QNetworkSession's signals if a request is already made. + // we need to track current accessibility state by default + // + connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)), + SLOT(_q_onlineStateChanged(bool))); + connect(&d->networkConfigurationManager, SIGNAL(configurationChanged(QNetworkConfiguration)), + SLOT(_q_configurationChanged(QNetworkConfiguration))); +#endif // QT_NO_BEARERMANAGEMENT + } } /*! @@ -1030,6 +1041,7 @@ QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &requ void QNetworkAccessManager::setConfiguration(const QNetworkConfiguration &config) { Q_D(QNetworkAccessManager); + d->networkConfiguration = config; d->customNetworkConfiguration = true; d->createSession(config); @@ -1048,7 +1060,7 @@ QNetworkConfiguration QNetworkAccessManager::configuration() const Q_D(const QNetworkAccessManager); QSharedPointer<QNetworkSession> session(d->getNetworkSession()); - if (session) { + if (session && !d->statusMonitor.isEnabled()) { return session->configuration(); } else { return d->networkConfigurationManager.defaultConfiguration(); @@ -1075,7 +1087,7 @@ QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const Q_D(const QNetworkAccessManager); QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession()); - if (networkSession) { + if (networkSession && !d->statusMonitor.isEnabled()) { return d->networkConfigurationManager.configurationFromIdentifier( networkSession->sessionProperty(QLatin1String("ActiveConfiguration")).toString()); } else { @@ -1114,6 +1126,12 @@ QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccess { Q_D(const QNetworkAccessManager); + if (d->statusMonitor.isEnabled()) { + if (!d->statusMonitor.isMonitoring()) + d->statusMonitor.start(); + return d->networkAccessible; + } + if (d->customNetworkConfiguration && d->networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined)) return UnknownAccessibility; @@ -1162,7 +1180,6 @@ QSharedPointer<QNetworkSession> QNetworkAccessManagerPrivate::getNetworkSession( #endif // QT_NO_BEARERMANAGEMENT - #ifndef QT_NO_SSL /*! \since 5.2 @@ -1387,6 +1404,11 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, redirectPolicy()); } + if (autoDeleteReplies() + && req.attribute(QNetworkRequest::AutoDeleteReplyOnFinishAttribute).isNull()) { + req.setAttribute(QNetworkRequest::AutoDeleteReplyOnFinishAttribute, true); + } + bool isLocalFile = req.url().isLocalFile(); QString scheme = req.url().scheme(); @@ -1435,35 +1457,57 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera } } -#ifndef QT_NO_BEARERMANAGEMENT + if (d->statusMonitor.isEnabled()) { + // See the code in ctor - QNetworkStatusMonitor allows us to + // immediately set 'networkAccessible' even before we start + // the monitor. +#ifdef QT_NO_BEARERMANAGEMENT + if (d->networkAccessible +#else + if (d->networkAccessible == NotAccessible +#endif // QT_NO_BEARERMANAGEMENT + && !isLocalFile) { + QHostAddress dest; + QString host = req.url().host().toLower(); + if (!(dest.setAddress(host) && dest.isLoopback()) + && host != QLatin1String("localhost") + && host != QHostInfo::localHostName().toLower()) { + return new QDisabledNetworkReply(this, req, op); + } + } - // Return a disabled network reply if network access is disabled. - // Except if the scheme is empty or file:// or if the host resolves to a loopback address. - if (d->networkAccessible == NotAccessible && !isLocalFile) { - QHostAddress dest; - QString host = req.url().host().toLower(); - if (!(dest.setAddress(host) && dest.isLoopback()) && host != QLatin1String("localhost") + if (!d->statusMonitor.isMonitoring() && !d->statusMonitor.start()) + qWarning(lcNetMon, "failed to start network status monitoring"); + } else { +#ifndef QT_NO_BEARERMANAGEMENT + // Return a disabled network reply if network access is disabled. + // Except if the scheme is empty or file:// or if the host resolves to a loopback address. + if (d->networkAccessible == NotAccessible && !isLocalFile) { + QHostAddress dest; + QString host = req.url().host().toLower(); + if (!(dest.setAddress(host) && dest.isLoopback()) && host != QLatin1String("localhost") && host != QHostInfo::localHostName().toLower()) { - return new QDisabledNetworkReply(this, req, op); + return new QDisabledNetworkReply(this, req, op); + } } - } - if (!d->networkSessionStrongRef && (d->initializeSession || !d->networkConfiguration.identifier().isEmpty())) { - if (!d->networkConfiguration.identifier().isEmpty()) { - if ((d->networkConfiguration.state() & QNetworkConfiguration::Defined) - && d->networkConfiguration != d->networkConfigurationManager.defaultConfiguration()) - d->createSession(d->networkConfigurationManager.defaultConfiguration()); - else - d->createSession(d->networkConfiguration); + if (!d->networkSessionStrongRef && (d->initializeSession || !d->networkConfiguration.identifier().isEmpty())) { + if (!d->networkConfiguration.identifier().isEmpty()) { + if ((d->networkConfiguration.state() & QNetworkConfiguration::Defined) + && d->networkConfiguration != d->networkConfigurationManager.defaultConfiguration()) + d->createSession(d->networkConfigurationManager.defaultConfiguration()); + else + d->createSession(d->networkConfiguration); - } else { - if (d->networkSessionRequired) - d->createSession(d->networkConfigurationManager.defaultConfiguration()); - else - d->initializeSession = false; + } else { + if (d->networkSessionRequired) + d->createSession(d->networkConfigurationManager.defaultConfiguration()); + else + d->initializeSession = false; + } } - } #endif + } QNetworkRequest request = req; if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() && @@ -1510,8 +1554,10 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera #endif QNetworkReplyHttpImpl *reply = new QNetworkReplyHttpImpl(this, request, op, outgoingData); #ifndef QT_NO_BEARERMANAGEMENT - connect(this, SIGNAL(networkSessionConnected()), - reply, SLOT(_q_networkSessionConnected())); + if (!d->statusMonitor.isEnabled()) { + connect(this, SIGNAL(networkSessionConnected()), + reply, SLOT(_q_networkSessionConnected())); + } #endif return reply; } @@ -1520,7 +1566,9 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera // first step: create the reply QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); #ifndef QT_NO_BEARERMANAGEMENT - if (!isLocalFile) { + // NETMONTODO: network reply impl must be augmented to use the same monitoring + // capabilities as http network reply impl does. Once it does: uncomment the condition below + if (!isLocalFile /*&& !d->statusMonitor.isEnabled()*/) { connect(this, SIGNAL(networkSessionConnected()), reply, SLOT(_q_networkSessionConnected())); } @@ -1631,13 +1679,50 @@ void QNetworkAccessManager::clearConnectionCache() QNetworkAccessManagerPrivate::clearConnectionCache(this); } + +/*! + \since 5.14 + + Returns the true if QNetworkAccessManager is currently configured + to automatically delete QNetworkReplies, false otherwise. + + \sa setAutoDeleteReplies, + QNetworkRequest::AutoDeleteReplyOnFinishAttribute +*/ +bool QNetworkAccessManager::autoDeleteReplies() const +{ + return d_func()->autoDeleteReplies; +} + +/*! + \since 5.14 + + Enables or disables automatic deletion of \l {QNetworkReply} {QNetworkReplies}. + + Setting \a shouldAutoDelete to true is the same as setting the + QNetworkRequest::AutoDeleteReplyOnFinishAttribute attribute to + true on all \e{future} \l {QNetworkRequest} {QNetworkRequests} + passed to this instance of QNetworkAccessManager unless the + attribute was already explicitly set on the QNetworkRequest. + + \sa autoDeleteReplies, + QNetworkRequest::AutoDeleteReplyOnFinishAttribute +*/ +void QNetworkAccessManager::setAutoDeleteReplies(bool shouldAutoDelete) +{ + d_func()->autoDeleteReplies = shouldAutoDelete; +} + void QNetworkAccessManagerPrivate::_q_replyFinished() { Q_Q(QNetworkAccessManager); QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender()); - if (reply) + if (reply) { emit q->finished(reply); + if (reply->request().attribute(QNetworkRequest::AutoDeleteReplyOnFinishAttribute, false).toBool()) + QMetaObject::invokeMethod(reply, [reply] { reply->deleteLater(); }, Qt::QueuedConnection); + } #ifndef QT_NO_BEARERMANAGEMENT // If there are no active requests, release our reference to the network session. @@ -1989,7 +2074,13 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline) { - Q_Q(QNetworkAccessManager); + Q_Q(QNetworkAccessManager); + + if (statusMonitor.isEnabled()) { + networkAccessible = isOnline ? QNetworkAccessManager::Accessible : QNetworkAccessManager::NotAccessible; + return; + } + // if the user set a config, we only care whether this one is active. // Otherwise, this QNAM is online if there is an online config. @@ -2019,6 +2110,9 @@ void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline) void QNetworkAccessManagerPrivate::_q_configurationChanged(const QNetworkConfiguration &configuration) { + if (statusMonitor.isEnabled()) + return; + const QString id = configuration.identifier(); if (configuration.state().testFlag(QNetworkConfiguration::Active)) { if (!onlineConfigurations.contains(id)) { @@ -2051,6 +2145,9 @@ void QNetworkAccessManagerPrivate::_q_configurationChanged(const QNetworkConfigu void QNetworkAccessManagerPrivate::_q_networkSessionFailed(QNetworkSession::SessionError) { + if (statusMonitor.isEnabled()) + return; + const auto cfgs = networkConfigurationManager.allConfigurations(); for (const QNetworkConfiguration &cfg : cfgs) { if (cfg.state().testFlag(QNetworkConfiguration::Active)) { @@ -2062,6 +2159,13 @@ void QNetworkAccessManagerPrivate::_q_networkSessionFailed(QNetworkSession::Sess } } +#else + +void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline) +{ + networkAccessible = isOnline; +} + #endif // QT_NO_BEARERMANAGEMENT #if QT_CONFIG(http) diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h index 7e2f7683d0..98498d07d2 100644 --- a/src/network/access/qnetworkaccessmanager.h +++ b/src/network/access/qnetworkaccessmanager.h @@ -167,6 +167,9 @@ public: void setRedirectPolicy(QNetworkRequest::RedirectPolicy policy); QNetworkRequest::RedirectPolicy redirectPolicy() const; + bool autoDeleteReplies() const; + void setAutoDeleteReplies(bool autoDelete); + Q_SIGNALS: #ifndef QT_NO_NETWORKPROXY void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator); @@ -209,10 +212,10 @@ private: #ifndef QT_NO_BEARERMANAGEMENT Q_PRIVATE_SLOT(d_func(), void _q_networkSessionClosed()) Q_PRIVATE_SLOT(d_func(), void _q_networkSessionStateChanged(QNetworkSession::State)) - Q_PRIVATE_SLOT(d_func(), void _q_onlineStateChanged(bool)) Q_PRIVATE_SLOT(d_func(), void _q_configurationChanged(const QNetworkConfiguration &)) Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed(QNetworkSession::SessionError)) #endif + Q_PRIVATE_SLOT(d_func(), void _q_onlineStateChanged(bool)) }; QT_END_NAMESPACE diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h index 5cab4928e4..67ea2094b3 100644 --- a/src/network/access/qnetworkaccessmanager_p.h +++ b/src/network/access/qnetworkaccessmanager_p.h @@ -55,6 +55,7 @@ #include "qnetworkaccessmanager.h" #include "qnetworkaccesscache_p.h" #include "qnetworkaccessbackend_p.h" +#include "private/qnetconmonitor_p.h" #include "qnetworkrequest.h" #include "qhsts_p.h" #include "private/qobject_p.h" @@ -151,6 +152,7 @@ public: QNetworkAccessBackend *findBackend(QNetworkAccessManager::Operation op, const QNetworkRequest &request); QStringList backendSupportedSchemes() const; + void _q_onlineStateChanged(bool isOnline); #ifndef QT_NO_BEARERMANAGEMENT void createSession(const QNetworkConfiguration &config); QSharedPointer<QNetworkSession> getNetworkSession() const; @@ -160,12 +162,11 @@ public: void _q_networkSessionPreferredConfigurationChanged(const QNetworkConfiguration &config, bool isSeamless); void _q_networkSessionStateChanged(QNetworkSession::State state); - void _q_onlineStateChanged(bool isOnline); + void _q_configurationChanged(const QNetworkConfiguration &configuration); void _q_networkSessionFailed(QNetworkSession::SessionError error); QSet<QString> onlineConfigurations; - #endif #if QT_CONFIG(http) @@ -199,6 +200,8 @@ public: int activeReplyCount; bool online; bool initializeSession; +#else + bool networkAccessible = true; #endif bool cookieJarCreated; @@ -222,6 +225,9 @@ public: QScopedPointer<QHstsStore> stsStore; #endif // QT_CONFIG(settings) bool stsEnabled = false; + mutable QNetworkStatusMonitor statusMonitor; + + bool autoDeleteReplies = false; #ifndef QT_NO_BEARERMANAGEMENT Q_AUTOTEST_EXPORT static const QWeakPointer<const QNetworkSession> getNetworkSession(const QNetworkAccessManager *manager); diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp index b7cf989477..903de322ff 100644 --- a/src/network/access/qnetworkcookie.cpp +++ b/src/network/access/qnetworkcookie.cpp @@ -46,6 +46,7 @@ #include "QtCore/qdebug.h" #include "QtCore/qlist.h" #include "QtCore/qlocale.h" +#include <QtCore/qregexp.h> #include "QtCore/qstring.h" #include "QtCore/qstringlist.h" #include "QtCore/qurl.h" diff --git a/src/network/access/qnetworkcookie.h b/src/network/access/qnetworkcookie.h index e462b98555..b712b63849 100644 --- a/src/network/access/qnetworkcookie.h +++ b/src/network/access/qnetworkcookie.h @@ -66,12 +66,10 @@ public: explicit QNetworkCookie(const QByteArray &name = QByteArray(), const QByteArray &value = QByteArray()); QNetworkCookie(const QNetworkCookie &other); ~QNetworkCookie(); -#ifdef Q_COMPILER_RVALUE_REFS - QNetworkCookie &operator=(QNetworkCookie &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QNetworkCookie &operator=(QNetworkCookie &&other) noexcept { swap(other); return *this; } QNetworkCookie &operator=(const QNetworkCookie &other); - void swap(QNetworkCookie &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QNetworkCookie &other) noexcept { qSwap(d, other.d); } bool operator==(const QNetworkCookie &other) const; inline bool operator!=(const QNetworkCookie &other) const diff --git a/src/network/access/qnetworkdiskcache_p.h b/src/network/access/qnetworkdiskcache_p.h index f7988e7dda..c797e63830 100644 --- a/src/network/access/qnetworkdiskcache_p.h +++ b/src/network/access/qnetworkdiskcache_p.h @@ -67,7 +67,7 @@ class QFile; class QCacheItem { public: - QCacheItem() : file(0) + QCacheItem() : file(nullptr) { } ~QCacheItem() @@ -85,7 +85,7 @@ public: metaData = QNetworkCacheMetaData(); data.close(); delete file; - file = 0; + file = nullptr; } void writeHeader(QFile *device) const; void writeCompressedData(QFile *device) const; diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 489c489e5c..8ac81d1780 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -59,6 +59,7 @@ #include <QtCore/private/qthread_p.h> #include "qnetworkcookiejar.h" +#include "qnetconmonitor_p.h" #include <string.h> // for strchr @@ -166,6 +167,11 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea #if QT_CONFIG(bearermanagement) static bool isSessionNeeded(const QUrl &url) { + if (QNetworkStatusMonitor::isEnabled()) { + // In case QNetworkStatus/QNetConManager are in business, + // no session, no bearer manager are involved. + return false; + } // Connections to the local machine does not require a session QString host = url.host().toLower(); return !QHostAddress(host).isLoopback() && host != QLatin1String("localhost") @@ -792,12 +798,11 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq // Create the HTTP thread delegate QHttpThreadDelegate *delegate = new QHttpThreadDelegate; - // Propagate Http/2 settings if any - const QVariant blob(manager->property(Http2::http2ParametersPropertyName)); - if (blob.isValid() && blob.canConvert<Http2::ProtocolParameters>()) - delegate->http2Parameters = blob.value<Http2::ProtocolParameters>(); + // Propagate Http/2 settings: + delegate->http2Parameters = request.http2Configuration(); #ifndef QT_NO_BEARERMANAGEMENT - delegate->networkSession = managerPrivate->getNetworkSession(); + if (!QNetworkStatusMonitor::isEnabled()) + delegate->networkSession = managerPrivate->getNetworkSession(); #endif // For the synchronous HTTP, this is the normal way the delegate gets deleted @@ -1048,59 +1053,39 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d) if (!q->isOpen()) return; - int pendingSignals = (int)pendingDownloadDataEmissions->fetchAndAddAcquire(-1) - 1; + if (cacheEnabled && isCachingAllowed() && !cacheSaveDevice) + initCacheSaveDevice(); + + // This is going to look a little strange. When downloading data while a + // HTTP redirect is happening (and enabled), we write the redirect + // response to the cache. However, we do not append it to our internal + // buffer as that will contain the response data only for the final + // response + if (cacheSaveDevice) + cacheSaveDevice->write(d); + if (!isHttpRedirectResponse()) { + buffer.append(d); + bytesDownloaded += d.size(); + } + bytesBuffered += d.size(); + + int pendingSignals = pendingDownloadDataEmissions->fetchAndSubAcquire(1) - 1; if (pendingSignals > 0) { // Some more signal emissions to this slot are pending. // Instead of writing the downstream data, we wait // and do it in the next call we get // (signal comppression) - pendingDownloadData.append(d); return; } - pendingDownloadData.append(d); - d.clear(); - // We need to usa a copy for calling writeDownstreamData as we could - // possibly recurse into this this function when we call - // appendDownstreamDataSignalEmissions because the user might call - // processEvents() or spin an event loop when this occur. - QByteDataBuffer pendingDownloadDataCopy = pendingDownloadData; - pendingDownloadData.clear(); - - if (cacheEnabled && isCachingAllowed() && !cacheSaveDevice) { - initCacheSaveDevice(); - } - - qint64 bytesWritten = 0; - for (int i = 0; i < pendingDownloadDataCopy.bufferCount(); i++) { - QByteArray const &item = pendingDownloadDataCopy[i]; - - // This is going to look a little strange. When downloading data while a - // HTTP redirect is happening (and enabled), we write the redirect - // response to the cache. However, we do not append it to our internal - // buffer as that will contain the response data only for the final - // response - if (cacheSaveDevice) - cacheSaveDevice->write(item.constData(), item.size()); - - if (!isHttpRedirectResponse()) - buffer.append(item); - - bytesWritten += item.size(); - } - bytesBuffered += bytesWritten; - pendingDownloadDataCopy.clear(); + if (isHttpRedirectResponse()) + return; QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); if (preMigrationDownloaded != Q_INT64_C(-1)) totalSize = totalSize.toLongLong() + preMigrationDownloaded; - if (isHttpRedirectResponse()) - return; - - bytesDownloaded += bytesWritten; - emit q->readyRead(); // emit readyRead before downloadProgress incase this will cause events to be // processed and we get into a recursive call (as in QProgressDialog). @@ -1808,7 +1793,7 @@ bool QNetworkReplyHttpImplPrivate::start(const QNetworkRequest &newHttpRequest) { #ifndef QT_NO_BEARERMANAGEMENT QSharedPointer<QNetworkSession> networkSession(managerPrivate->getNetworkSession()); - if (!networkSession) { + if (!networkSession || QNetworkStatusMonitor::isEnabled()) { #endif postRequest(newHttpRequest); return true; @@ -1896,7 +1881,7 @@ void QNetworkReplyHttpImplPrivate::_q_startOperation() // state changes. if (!startWaitForSession(session)) return; - } else if (session) { + } else if (session && !QNetworkStatusMonitor::isEnabled()) { QObject::connect(session.data(), SIGNAL(stateChanged(QNetworkSession::State)), q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)), Qt::QueuedConnection); @@ -2185,7 +2170,7 @@ void QNetworkReplyHttpImplPrivate::finished() #ifndef QT_NO_BEARERMANAGEMENT Q_ASSERT(managerPrivate); QSharedPointer<QNetworkSession> session = managerPrivate->getNetworkSession(); - if (session && session->state() == QNetworkSession::Roaming && + if (!QNetworkStatusMonitor::isEnabled() && session && session->state() == QNetworkSession::Roaming && state == Working && errorCode != QNetworkReply::OperationCanceledError) { // only content with a known size will fail with a temporary network failure error if (!totalSize.isNull()) { diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h index f5f01d0811..ef69ce0653 100644 --- a/src/network/access/qnetworkreplyhttpimpl_p.h +++ b/src/network/access/qnetworkreplyhttpimpl_p.h @@ -63,7 +63,6 @@ #include <QtNetwork/QNetworkCacheMetaData> #include <private/qhttpnetworkrequest_p.h> -#include <private/qbytedata_p.h> #include <private/qnetworkreply_p.h> #include <QtNetwork/QNetworkProxy> #include <QtNetwork/QNetworkSession> @@ -248,7 +247,6 @@ public: quint64 resumeOffset; qint64 preMigrationDownloaded; - QByteDataBuffer pendingDownloadData; // For signal compression qint64 bytesDownloaded; qint64 bytesBuffered; diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index a794b492e7..6eab500e8c 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -158,7 +158,7 @@ void QNetworkReplyImplPrivate::_q_startOperation() } else { if (state != Finished) { if (operation == QNetworkAccessManager::GetOperation) - pendingNotifications.append(NotifyDownstreamReadyWrite); + pendingNotifications.push_back(NotifyDownstreamReadyWrite); handleNotifications(); } @@ -368,6 +368,7 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const outgoingData = data; request = req; + originalRequest = req; url = request.url(); operation = op; @@ -432,8 +433,9 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification) { Q_Q(QNetworkReplyImpl); - if (!pendingNotifications.contains(notification)) - pendingNotifications.enqueue(notification); + const auto it = std::find(pendingNotifications.cbegin(), pendingNotifications.cend(), notification); + if (it == pendingNotifications.cend()) + pendingNotifications.push_back(notification); if (pendingNotifications.size() == 1) QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated)); @@ -444,14 +446,9 @@ void QNetworkReplyImplPrivate::handleNotifications() if (notificationHandlingPaused) return; - NotificationQueue current = pendingNotifications; - pendingNotifications.clear(); - - if (state != Working) - return; - - while (state == Working && !current.isEmpty()) { - InternalNotifications notification = current.dequeue(); + for (InternalNotifications notification : qExchange(pendingNotifications, {})) { + if (state != Working) + return; switch (notification) { case NotifyDownstreamReadyWrite: if (copyDevice) @@ -465,8 +462,7 @@ void QNetworkReplyImplPrivate::handleNotifications() break; case NotifyCopyFinished: { - QIODevice *dev = copyDevice; - copyDevice = 0; + QIODevice *dev = qExchange(copyDevice, nullptr); backend->copyFinished(dev); break; } @@ -1116,7 +1112,6 @@ bool QNetworkReplyImplPrivate::migrateBackend() return true; } -#ifndef QT_NO_BEARERMANAGEMENT QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent, const QNetworkRequest &req, QNetworkAccessManager::Operation op) @@ -1141,7 +1136,6 @@ QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent, QDisabledNetworkReply::~QDisabledNetworkReply() { } -#endif QT_END_NAMESPACE diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h index f4e8284ab6..8cec79541a 100644 --- a/src/network/access/qnetworkreplyimpl_p.h +++ b/src/network/access/qnetworkreplyimpl_p.h @@ -74,7 +74,7 @@ class QNetworkReplyImpl: public QNetworkReply { Q_OBJECT public: - QNetworkReplyImpl(QObject *parent = 0); + QNetworkReplyImpl(QObject *parent = nullptr); ~QNetworkReplyImpl(); virtual void abort() override; @@ -117,8 +117,6 @@ public: NotifyCopyFinished }; - typedef QQueue<InternalNotifications> NotificationQueue; - QNetworkReplyImplPrivate(); void _q_startOperation(); @@ -178,7 +176,7 @@ public: bool cacheEnabled; QIODevice *cacheSaveDevice; - NotificationQueue pendingNotifications; + std::vector<InternalNotifications> pendingNotifications; bool notificationHandlingPaused; QUrl urlForLastAuthentication; @@ -209,7 +207,6 @@ public: }; Q_DECLARE_TYPEINFO(QNetworkReplyImplPrivate::InternalNotifications, Q_PRIMITIVE_TYPE); -#ifndef QT_NO_BEARERMANAGEMENT class QDisabledNetworkReply : public QNetworkReply { Q_OBJECT @@ -223,7 +220,6 @@ public: protected: qint64 readData(char *, qint64) override { return -1; } }; -#endif QT_END_NAMESPACE diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index f15c43cdd8..118fb6b1fb 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -42,6 +42,10 @@ #include "qplatformdefs.h" #include "qnetworkcookie.h" #include "qsslconfiguration.h" +#if QT_CONFIG(http) || defined(Q_CLANG_QDOC) +#include "qhttp2configuration.h" +#include "private/http2protocol_p.h" +#endif #include "QtCore/qshareddata.h" #include "QtCore/qlocale.h" #include "QtCore/qdatetime.h" @@ -331,6 +335,12 @@ QT_BEGIN_NAMESPACE \omitvalue ResourceTypeAttribute + \value AutoDeleteReplyOnFinishAttribute + Requests only, type: QMetaType::Bool (default: false) + If set, this attribute will make QNetworkAccessManager delete + the QNetworkReply after having emitted "finished". + (This value was introduced in 5.14.) + \value User Special type. Additional information can be passed in QVariants with types ranging from User to UserMax. The default @@ -439,6 +449,9 @@ public: sslConfiguration = new QSslConfiguration(*other.sslConfiguration); #endif peerVerifyName = other.peerVerifyName; +#if QT_CONFIG(http) + h2Configuration = other.h2Configuration; +#endif } inline bool operator==(const QNetworkRequestPrivate &other) const @@ -448,7 +461,11 @@ public: rawHeaders == other.rawHeaders && attributes == other.attributes && maxRedirectsAllowed == other.maxRedirectsAllowed && - peerVerifyName == other.peerVerifyName; + peerVerifyName == other.peerVerifyName +#if QT_CONFIG(http) + && h2Configuration == other.h2Configuration +#endif + ; // don't compare cookedHeaders } @@ -459,16 +476,39 @@ public: #endif int maxRedirectsAllowed; QString peerVerifyName; +#if QT_CONFIG(http) + QHttp2Configuration h2Configuration; +#endif }; /*! + Constructs a QNetworkRequest object with no URL to be requested. + Use setUrl() to set one. + + \sa url(), setUrl() +*/ +QNetworkRequest::QNetworkRequest() + : d(new QNetworkRequestPrivate) +{ +#if QT_CONFIG(http) + // Initial values proposed by RFC 7540 are quite draconian, + // so unless an application will set its own parameters, we + // make stream window size larger and increase (via WINDOW_UPDATE) + // the session window size. These are our 'defaults': + d->h2Configuration.setStreamReceiveWindowSize(Http2::qtDefaultStreamReceiveWindowSize); + d->h2Configuration.setSessionReceiveWindowSize(Http2::maxSessionReceiveWindowSize); + d->h2Configuration.setServerPushEnabled(false); +#endif // QT_CONFIG(http) +} + +/*! Constructs a QNetworkRequest object with \a url as the URL to be requested. \sa url(), setUrl() */ QNetworkRequest::QNetworkRequest(const QUrl &url) - : d(new QNetworkRequestPrivate) + : QNetworkRequest() { d->url = url; } @@ -818,6 +858,52 @@ void QNetworkRequest::setPeerVerifyName(const QString &peerName) d->peerVerifyName = peerName; } +#if QT_CONFIG(http) || defined(Q_CLANG_QDOC) +/*! + \since 5.14 + + Returns the current parameters that QNetworkAccessManager is + using for this request and its underlying HTTP/2 connection. + This is either a configuration previously set by an application + or a default configuration. + + The default values that QNetworkAccessManager is using are: + + \list + \li Window size for connection-level flowcontrol is 2147483647 octets + \li Window size for stream-level flowcontrol is 21474836 octets + \li Max frame size is 16384 + \endlist + + By default, server push is disabled, Huffman compression and + string indexing are enabled. + + \sa setHttp2Configuration +*/ +QHttp2Configuration QNetworkRequest::http2Configuration() const +{ + return d->h2Configuration; +} + +/*! + \since 5.14 + + Sets request's HTTP/2 parameters from \a configuration. + + \note The configuration must be set prior to making a request. + \note HTTP/2 multiplexes several streams in a single HTTP/2 + connection. This implies that QNetworkAccessManager will use + the configuration found in the first request from a series + of requests sent to the same host. + + \sa http2Configuration, QNetworkAccessManager, QHttp2Configuration +*/ +void QNetworkRequest::setHttp2Configuration(const QHttp2Configuration &configuration) +{ + d->h2Configuration = configuration; +} +#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) + static QByteArray headerName(QNetworkRequest::KnownHeaders header) { switch (header) { @@ -1338,7 +1424,7 @@ QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value) QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt) { - return QLocale::c().toString(dt, QLatin1String("ddd, dd MMM yyyy hh:mm:ss 'GMT'")) + return QLocale::c().toString(dt, u"ddd, dd MMM yyyy hh:mm:ss 'GMT'") .toLatin1(); } diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index efb9cbecba..e09ff8aaae 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE class QSslConfiguration; +class QHttp2Configuration; class QNetworkRequestPrivate; class Q_NETWORK_EXPORT QNetworkRequest @@ -98,6 +99,7 @@ public: RedirectPolicyAttribute, Http2DirectAttribute, ResourceTypeAttribute, // internal + AutoDeleteReplyOnFinishAttribute, User = 1000, UserMax = 32767 @@ -127,15 +129,14 @@ public: }; - explicit QNetworkRequest(const QUrl &url = QUrl()); + QNetworkRequest(); + explicit QNetworkRequest(const QUrl &url); QNetworkRequest(const QNetworkRequest &other); ~QNetworkRequest(); -#ifdef Q_COMPILER_RVALUE_REFS - QNetworkRequest &operator=(QNetworkRequest &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QNetworkRequest &operator=(QNetworkRequest &&other) noexcept { swap(other); return *this; } QNetworkRequest &operator=(const QNetworkRequest &other); - void swap(QNetworkRequest &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QNetworkRequest &other) noexcept { qSwap(d, other.d); } bool operator==(const QNetworkRequest &other) const; inline bool operator!=(const QNetworkRequest &other) const @@ -175,6 +176,10 @@ public: QString peerVerifyName() const; void setPeerVerifyName(const QString &peerName); +#if QT_CONFIG(http) || defined(Q_CLANG_QDOC) + QHttp2Configuration http2Configuration() const; + void setHttp2Configuration(const QHttp2Configuration &configuration); +#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) private: QSharedDataPointer<QNetworkRequestPrivate> d; friend class QNetworkRequestPrivate; diff --git a/src/network/access/qspdyprotocolhandler.cpp b/src/network/access/qspdyprotocolhandler.cpp index 403c01e974..f845235bf7 100644 --- a/src/network/access/qspdyprotocolhandler.cpp +++ b/src/network/access/qspdyprotocolhandler.cpp @@ -305,7 +305,7 @@ bool QSpdyProtocolHandler::sendRequest() currentReply->setSpdyWasUsed(true); qint32 streamID = generateNextStreamID(); - currentReply->setProperty("SPDYStreamID", streamID); + m_streamIDs.insert(currentReply, streamID); currentReply->setRequest(currentRequest); currentReply->d_func()->connection = m_connection; @@ -322,7 +322,7 @@ bool QSpdyProtocolHandler::sendRequest() void QSpdyProtocolHandler::_q_replyDestroyed(QObject* reply) { - qint32 streamID = reply->property("SPDYStreamID").toInt(); + qint32 streamID = m_streamIDs.take(reply); if (m_inFlightStreams.remove(streamID)) sendRST_STREAM(streamID, RST_STREAM_CANCEL); } @@ -624,10 +624,12 @@ void QSpdyProtocolHandler::sendSYN_STREAM(const HttpMessagePair &messagePair, // hack: set the stream ID on the device directly, so when we get // the signal for uploading we know which stream we are sending on - request.uploadByteDevice()->setProperty("SPDYStreamID", streamID); + m_streamIDs.insert(request.uploadByteDevice(), streamID); QObject::connect(request.uploadByteDevice(), SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection); + QObject::connect(request.uploadByteDevice(), SIGNAL(destroyed(QObject*)), this, + SLOT(_q_uploadDataDestroyed(QObject *))); } QByteArray namesAndValues = composeHeader(request); @@ -663,6 +665,11 @@ void QSpdyProtocolHandler::sendSYN_STREAM(const HttpMessagePair &messagePair, uploadData(streamID); } +void QSpdyProtocolHandler::_q_uploadDataDestroyed(QObject *uploadData) +{ + m_streamIDs.remove(uploadData); +} + void QSpdyProtocolHandler::sendRST_STREAM(qint32 streamID, RST_STREAM_STATUS_CODE statusCode) { char wireData[8]; @@ -756,7 +763,7 @@ void QSpdyProtocolHandler::_q_uploadDataReadyRead() { QNonContiguousByteDevice *device = qobject_cast<QNonContiguousByteDevice *>(sender()); Q_ASSERT(device); - qint32 streamID = device->property("SPDYStreamID").toInt(); + qint32 streamID = m_streamIDs.value(device); Q_ASSERT(streamID > 0); uploadData(streamID); } diff --git a/src/network/access/qspdyprotocolhandler_p.h b/src/network/access/qspdyprotocolhandler_p.h index dd93a9aba2..14e2ff388a 100644 --- a/src/network/access/qspdyprotocolhandler_p.h +++ b/src/network/access/qspdyprotocolhandler_p.h @@ -110,6 +110,7 @@ public: private slots: void _q_uploadDataReadyRead(); void _q_replyDestroyed(QObject*); + void _q_uploadDataDestroyed(QObject *); private: @@ -216,6 +217,7 @@ private: bool m_waitingForCompleteStream; z_stream m_deflateStream; z_stream m_inflateStream; + QHash<QObject *, qint32> m_streamIDs; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::DataFrameFlags) diff --git a/src/network/bearer/qbearerengine.cpp b/src/network/bearer/qbearerengine.cpp index 677da08cb6..06bf449611 100644 --- a/src/network/bearer/qbearerengine.cpp +++ b/src/network/bearer/qbearerengine.cpp @@ -38,6 +38,8 @@ ****************************************************************************/ #include "qbearerengine_p.h" +#include <QtCore/private/qlocking_p.h> + #include <algorithm> #ifndef QT_NO_BEARERMANAGEMENT @@ -46,24 +48,23 @@ QT_BEGIN_NAMESPACE static void cleanUpConfigurations(QHash<QString, QNetworkConfigurationPrivatePointer> &configurations) { - for (const auto &ptr : qAsConst(configurations)) { + for (auto &ptr : qExchange(configurations, {})) { ptr->isValid = false; ptr->id.clear(); } - configurations.clear(); } static bool hasUsedConfiguration(const QHash<QString, QNetworkConfigurationPrivatePointer> &configurations) { auto isUsed = [](const QNetworkConfigurationPrivatePointer &ptr) { - return ptr->ref.load() > 1; + return ptr->ref.loadRelaxed() > 1; }; const auto end = configurations.end(); return std::find_if(configurations.begin(), end, isUsed) != end; } QBearerEngine::QBearerEngine(QObject *parent) - : QObject(parent), mutex(QMutex::Recursive) + : QObject(parent) { } @@ -87,7 +88,7 @@ bool QBearerEngine::requiresPolling() const */ bool QBearerEngine::configurationsInUse() const { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); return hasUsedConfiguration(accessPointConfigurations) || hasUsedConfiguration(snapConfigurations) || hasUsedConfiguration(userChoiceConfigurations); diff --git a/src/network/bearer/qbearerengine_p.h b/src/network/bearer/qbearerengine_p.h index 5fc2578a78..c69f478b26 100644 --- a/src/network/bearer/qbearerengine_p.h +++ b/src/network/bearer/qbearerengine_p.h @@ -77,7 +77,7 @@ class Q_NETWORK_EXPORT QBearerEngine : public QObject friend class QNetworkConfigurationManagerPrivate; public: - explicit QBearerEngine(QObject *parent = 0); + explicit QBearerEngine(QObject *parent = nullptr); virtual ~QBearerEngine(); virtual bool hasIdentifier(const QString &id) = 0; @@ -105,7 +105,7 @@ protected: QHash<QString, QNetworkConfigurationPrivatePointer> snapConfigurations; QHash<QString, QNetworkConfigurationPrivatePointer> userChoiceConfigurations; - mutable QMutex mutex; + mutable QRecursiveMutex mutex; }; QT_END_NAMESPACE diff --git a/src/network/bearer/qbearerplugin_p.h b/src/network/bearer/qbearerplugin_p.h index 0cdde3c06c..ac787d0541 100644 --- a/src/network/bearer/qbearerplugin_p.h +++ b/src/network/bearer/qbearerplugin_p.h @@ -68,7 +68,7 @@ class Q_NETWORK_EXPORT QBearerEnginePlugin : public QObject { Q_OBJECT public: - explicit QBearerEnginePlugin(QObject *parent = 0); + explicit QBearerEnginePlugin(QObject *parent = nullptr); virtual ~QBearerEnginePlugin(); virtual QBearerEngine *create(const QString &key) const = 0; diff --git a/src/network/bearer/qnetworkconfigmanager_p.cpp b/src/network/bearer/qnetworkconfigmanager_p.cpp index a903ecda5f..b432444669 100644 --- a/src/network/bearer/qnetworkconfigmanager_p.cpp +++ b/src/network/bearer/qnetworkconfigmanager_p.cpp @@ -45,6 +45,7 @@ #include <QtCore/qstringlist.h> #include <QtCore/qthread.h> #include <QtCore/private/qcoreapplication_p.h> +#include <QtCore/private/qlocking_p.h> #include <QtCore/private/qthread_p.h> #include <QtCore/qbytearray.h> @@ -58,7 +59,7 @@ QT_BEGIN_NAMESPACE QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate() - : QObject(), pollTimer(0), mutex(QMutex::Recursive), + : QObject(), pollTimer(0), loader(QBearerEngineFactoryInterface_iid, QLatin1String("/bearer")), forcedPolling(0), firstUpdate(true) { @@ -115,10 +116,10 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration( QNetworkConfigurationPrivatePointer defaultConfiguration; for (QBearerEngine *engine : sessionEngines) { - QMutexLocker locker(&engine->mutex); + const auto locker = qt_scoped_lock(engine->mutex); for (const auto &ptr : qAsConst(engine->snapConfigurations)) { - QMutexLocker configLocker(&ptr->mutex); + const auto locker = qt_scoped_lock(ptr->mutex); if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) { QNetworkConfiguration config; @@ -211,11 +212,11 @@ QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurati for (QBearerEngine *engine : sessionEngines) { - QMutexLocker locker(&engine->mutex); + const auto locker = qt_scoped_lock(engine->mutex); //find all InternetAccessPoints for (const auto &ptr : qAsConst(engine->accessPointConfigurations)) { - QMutexLocker configLocker(&ptr->mutex); + const auto locker = qt_scoped_lock(ptr->mutex); if ((ptr->state & filter) == filter) { QNetworkConfiguration pt; @@ -226,7 +227,7 @@ QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurati //find all service networks for (const auto &ptr : qAsConst(engine->snapConfigurations)) { - QMutexLocker configLocker(&ptr->mutex); + const auto locker = qt_scoped_lock(ptr->mutex); if ((ptr->state & filter) == filter) { QNetworkConfiguration pt; @@ -243,10 +244,10 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIden { QNetworkConfiguration item; - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); for (QBearerEngine *engine : sessionEngines) { - QMutexLocker locker(&engine->mutex); + const auto locker = qt_scoped_lock(engine->mutex); if (auto ptr = engine->accessPointConfigurations.value(identifier)) { item.d = std::move(ptr); break; @@ -266,7 +267,7 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIden bool QNetworkConfigurationManagerPrivate::isOnline() const { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); // We need allConfigurations since onlineConfigurations is filled with queued connections // and thus is not always (more importantly just after creation) up to date @@ -275,7 +276,7 @@ bool QNetworkConfigurationManagerPrivate::isOnline() const QNetworkConfigurationManager::Capabilities QNetworkConfigurationManagerPrivate::capabilities() const { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); QNetworkConfigurationManager::Capabilities capFlags; @@ -287,7 +288,7 @@ QNetworkConfigurationManager::Capabilities QNetworkConfigurationManagerPrivate:: void QNetworkConfigurationManagerPrivate::configurationAdded(QNetworkConfigurationPrivatePointer ptr) { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); if (!firstUpdate) { QNetworkConfiguration item; @@ -295,24 +296,24 @@ void QNetworkConfigurationManagerPrivate::configurationAdded(QNetworkConfigurati emit configurationAdded(item); } - ptr->mutex.lock(); + auto ptrLocker = qt_unique_lock(ptr->mutex); if (ptr->state == QNetworkConfiguration::Active) { - ptr->mutex.unlock(); - onlineConfigurations.insert(ptr->id); + const auto id = ptr->id; + ptrLocker.unlock(); + onlineConfigurations.insert(id); if (!firstUpdate && onlineConfigurations.count() == 1) emit onlineStateChanged(true); - } else { - ptr->mutex.unlock(); } } void QNetworkConfigurationManagerPrivate::configurationRemoved(QNetworkConfigurationPrivatePointer ptr) { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); - ptr->mutex.lock(); - ptr->isValid = false; - ptr->mutex.unlock(); + { + const auto locker = qt_scoped_lock(ptr->mutex); + ptr->isValid = false; + } if (!firstUpdate) { QNetworkConfiguration item; @@ -327,7 +328,7 @@ void QNetworkConfigurationManagerPrivate::configurationRemoved(QNetworkConfigura void QNetworkConfigurationManagerPrivate::configurationChanged(QNetworkConfigurationPrivatePointer ptr) { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); if (!firstUpdate) { QNetworkConfiguration item; @@ -337,12 +338,13 @@ void QNetworkConfigurationManagerPrivate::configurationChanged(QNetworkConfigura bool previous = !onlineConfigurations.isEmpty(); - ptr->mutex.lock(); - if (ptr->state == QNetworkConfiguration::Active) - onlineConfigurations.insert(ptr->id); - else - onlineConfigurations.remove(ptr->id); - ptr->mutex.unlock(); + { + const auto locker = qt_scoped_lock(ptr->mutex); + if (ptr->state == QNetworkConfiguration::Active) + onlineConfigurations.insert(ptr->id); + else + onlineConfigurations.remove(ptr->id); + } bool online = !onlineConfigurations.isEmpty(); @@ -354,7 +356,8 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations() { typedef QMultiMap<int, QString> PluginKeyMap; typedef PluginKeyMap::const_iterator PluginKeyMapConstIterator; - QMutexLocker locker(&mutex); + + auto locker = qt_unique_lock(mutex); if (firstUpdate) { if (qobject_cast<QBearerEngine *>(sender())) @@ -432,7 +435,7 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations() void QNetworkConfigurationManagerPrivate::performAsyncConfigurationUpdate() { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); if (sessionEngines.isEmpty()) { emit configurationUpdateComplete(); @@ -449,14 +452,14 @@ void QNetworkConfigurationManagerPrivate::performAsyncConfigurationUpdate() QList<QBearerEngine *> QNetworkConfigurationManagerPrivate::engines() const { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); return sessionEngines; } void QNetworkConfigurationManagerPrivate::startPolling() { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); if (!pollTimer) { pollTimer = new QTimer(this); bool ok; @@ -482,7 +485,7 @@ void QNetworkConfigurationManagerPrivate::startPolling() void QNetworkConfigurationManagerPrivate::pollEngines() { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); for (QBearerEngine *engine : qAsConst(sessionEngines)) { if (engine->requiresPolling() && (forcedPolling || engine->configurationsInUse())) { @@ -494,7 +497,7 @@ void QNetworkConfigurationManagerPrivate::pollEngines() void QNetworkConfigurationManagerPrivate::enablePolling() { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); ++forcedPolling; @@ -504,7 +507,7 @@ void QNetworkConfigurationManagerPrivate::enablePolling() void QNetworkConfigurationManagerPrivate::disablePolling() { - QMutexLocker locker(&mutex); + const auto locker = qt_scoped_lock(mutex); --forcedPolling; } diff --git a/src/network/bearer/qnetworkconfigmanager_p.h b/src/network/bearer/qnetworkconfigmanager_p.h index 380e25c22f..4819c2027c 100644 --- a/src/network/bearer/qnetworkconfigmanager_p.h +++ b/src/network/bearer/qnetworkconfigmanager_p.h @@ -117,7 +117,7 @@ private: QThread *bearerThread; private: - mutable QMutex mutex; + mutable QRecursiveMutex mutex; QFactoryLoader loader; QList<QBearerEngine *> sessionEngines; diff --git a/src/network/bearer/qnetworkconfiguration.cpp b/src/network/bearer/qnetworkconfiguration.cpp index f5ced0693a..19bc44e02a 100644 --- a/src/network/bearer/qnetworkconfiguration.cpp +++ b/src/network/bearer/qnetworkconfiguration.cpp @@ -414,34 +414,7 @@ bool QNetworkConfiguration::isRoamingAvailable() const */ QList<QNetworkConfiguration> QNetworkConfiguration::children() const { - QList<QNetworkConfiguration> results; - - if (!d) - return results; - - QMutexLocker locker(&d->mutex); - - if (d->type != QNetworkConfiguration::ServiceNetwork || !d->isValid) - return results; - - for (auto it = d->serviceNetworkMembers.begin(), end = d->serviceNetworkMembers.end(); it != end;) { - QNetworkConfigurationPrivatePointer p = it.value(); - //if we have an invalid member get rid of it -> was deleted earlier on - { - QMutexLocker childLocker(&p->mutex); - - if (!p->isValid) { - it = d->serviceNetworkMembers.erase(it); - continue; - } - } - QNetworkConfiguration item; - item.d = p; - results << item; - ++it; - } - - return results; + return {}; } /*! diff --git a/src/network/bearer/qnetworkconfiguration.h b/src/network/bearer/qnetworkconfiguration.h index e7b74034fc..048abc2fc8 100644 --- a/src/network/bearer/qnetworkconfiguration.h +++ b/src/network/bearer/qnetworkconfiguration.h @@ -55,13 +55,11 @@ class Q_NETWORK_EXPORT QNetworkConfiguration public: QNetworkConfiguration(); QNetworkConfiguration(const QNetworkConfiguration& other); -#ifdef Q_COMPILER_RVALUE_REFS - QNetworkConfiguration &operator=(QNetworkConfiguration &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QNetworkConfiguration &operator=(QNetworkConfiguration &&other) noexcept { swap(other); return *this; } QNetworkConfiguration &operator=(const QNetworkConfiguration &other); ~QNetworkConfiguration(); - void swap(QNetworkConfiguration &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QNetworkConfiguration &other) noexcept { qSwap(d, other.d); } bool operator==(const QNetworkConfiguration &other) const; inline bool operator!=(const QNetworkConfiguration &other) const diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h index 1b1ece39b7..96854fe831 100644 --- a/src/network/bearer/qnetworkconfiguration_p.h +++ b/src/network/bearer/qnetworkconfiguration_p.h @@ -65,22 +65,14 @@ class QNetworkConfigurationPrivate : public QSharedData { public: QNetworkConfigurationPrivate() : - mutex(QMutex::Recursive), type(QNetworkConfiguration::Invalid), purpose(QNetworkConfiguration::UnknownPurpose), bearerType(QNetworkConfiguration::BearerUnknown), isValid(false), roamingSupported(false), timeout(DefaultTimeout) {} - virtual ~QNetworkConfigurationPrivate() - { - //release pointers to member configurations - serviceNetworkMembers.clear(); - } - QMap<unsigned int, QNetworkConfigurationPrivatePointer> serviceNetworkMembers; - - mutable QMutex mutex; + mutable QRecursiveMutex mutex; QString name; QString id; diff --git a/src/network/bearer/qnetworksession_p.h b/src/network/bearer/qnetworksession_p.h index 661587603c..7c1ff63b68 100644 --- a/src/network/bearer/qnetworksession_p.h +++ b/src/network/bearer/qnetworksession_p.h @@ -68,7 +68,7 @@ class Q_NETWORK_EXPORT QNetworkSessionPrivate : public QObject public: QNetworkSessionPrivate() : QObject(), - state(QNetworkSession::Invalid), isOpen(false), mutex(QMutex::Recursive) + state(QNetworkSession::Invalid), isOpen(false) {} virtual ~QNetworkSessionPrivate() {} @@ -147,7 +147,7 @@ protected: QNetworkSession::State state; bool isOpen; - QMutex mutex; + QRecursiveMutex mutex; }; QT_END_NAMESPACE diff --git a/src/network/bearer/qsharednetworksession.cpp b/src/network/bearer/qsharednetworksession.cpp index fc01acb8b4..b3e9892f4b 100644 --- a/src/network/bearer/qsharednetworksession.cpp +++ b/src/network/bearer/qsharednetworksession.cpp @@ -57,36 +57,44 @@ inline QSharedNetworkSessionManager* sharedNetworkSessionManager() return rv; } -static void doDeleteLater(QObject* obj) +struct DeleteLater { + void operator()(QObject* obj) const + { + obj->deleteLater(); + } +}; + +template <typename Container> +static void maybe_prune_expired(Container &c) { - obj->deleteLater(); + if (c.size() > 16) { + for (auto it = c.cbegin(), end = c.cend(); it != end; /*erasing*/) { + if (!it->second.lock()) + it = c.erase(it); + else + ++it; + } + } } QSharedPointer<QNetworkSession> QSharedNetworkSessionManager::getSession(const QNetworkConfiguration &config) { - QSharedNetworkSessionManager *m(sharedNetworkSessionManager()); - const auto it = m->sessions.constFind(config); + QSharedNetworkSessionManager *m = sharedNetworkSessionManager(); + maybe_prune_expired(m->sessions); + auto &entry = m->sessions[config]; //if already have a session, return it - if (it != m->sessions.cend()) { - QSharedPointer<QNetworkSession> p = it.value().toStrongRef(); - if (!p.isNull()) - return p; - } + if (auto p = entry.toStrongRef()) + return p; //otherwise make one - QSharedPointer<QNetworkSession> session(new QNetworkSession(config), doDeleteLater); - m->sessions[config] = session; + QSharedPointer<QNetworkSession> session(new QNetworkSession(config), DeleteLater{}); + entry = session; return session; } void QSharedNetworkSessionManager::setSession(const QNetworkConfiguration &config, QSharedPointer<QNetworkSession> session) { - QSharedNetworkSessionManager *m(sharedNetworkSessionManager()); - m->sessions[config] = session; -} - -uint qHash(const QNetworkConfiguration& config) -{ - return ((uint)config.type()) | (((uint)config.bearerType()) << 8) | (((uint)config.purpose()) << 16); + QSharedNetworkSessionManager *m = sharedNetworkSessionManager(); + m->sessions[config] = std::move(session); } QT_END_NAMESPACE diff --git a/src/network/bearer/qsharednetworksession_p.h b/src/network/bearer/qsharednetworksession_p.h index 001b8af02a..f22f9eeacb 100644 --- a/src/network/bearer/qsharednetworksession_p.h +++ b/src/network/bearer/qsharednetworksession_p.h @@ -54,16 +54,26 @@ #include <QtNetwork/private/qtnetworkglobal_p.h> #include "qnetworksession.h" #include "qnetworkconfiguration.h" -#include <QHash> #include <QSharedPointer> #include <QWeakPointer> #include <QMutex> +#include <unordered_map> + #ifndef QT_NO_BEARERMANAGEMENT QT_BEGIN_NAMESPACE -uint qHash(const QNetworkConfiguration& config); +namespace QtPrivate { +struct NetworkConfigurationHash { + using result_type = size_t; + using argument_type = QNetworkConfiguration; + size_t operator()(const QNetworkConfiguration &config) const noexcept + { + return std::hash<size_t>{}(size_t(config.type()) | (size_t(config.bearerType()) << 8) | (size_t(config.purpose()) << 16)); + } +}; +} class QSharedNetworkSessionManager { @@ -71,7 +81,7 @@ public: static QSharedPointer<QNetworkSession> getSession(const QNetworkConfiguration &config); static void setSession(const QNetworkConfiguration &config, QSharedPointer<QNetworkSession> session); private: - QHash<QNetworkConfiguration, QWeakPointer<QNetworkSession> > sessions; + std::unordered_map<QNetworkConfiguration, QWeakPointer<QNetworkSession>, QtPrivate::NetworkConfigurationHash> sessions; }; QT_END_NAMESPACE diff --git a/src/network/configure.json b/src/network/configure.json index f74c481f38..f501465c91 100644 --- a/src/network/configure.json +++ b/src/network/configure.json @@ -98,6 +98,30 @@ "condition": "!config.msvc" } ] + }, + "gssapi": { + "label": "KRB5 GSSAPI Support", + "test": { + "head": [ + "#if defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__))", + "# include <TargetConditionals.h>", + "# if defined(TARGET_OS_MAC) && TARGET_OS_MAC", + "# include <GSS/GSS.h>", + "# endif", + "#else", + "# include <gssapi/gssapi.h>", + "#endif" + ], + "main": [ + "gss_ctx_id_t ctx;", + "gss_context_time(nullptr, ctx, nullptr);" + ] + }, + "sources": [ + { "libs": "-framework GSS", "condition": "config.darwin" }, + { "type": "pkgConfig", "args": "krb5-gssapi" }, + "-lgssapi_krb5" + ] } }, @@ -199,6 +223,22 @@ ] }, "use": "openssl" + }, + "netlistmgr": { + "label": "Network List Manager", + "type": "compile", + "test": { + "include": [ "netlistmgr.h", "wrl/client.h" ], + "main": [ + "using namespace Microsoft::WRL;", + "ComPtr<INetworkListManager> networkListManager;", + "ComPtr<IConnectionPoint> connectionPoint;", + "ComPtr<IConnectionPointContainer> connectionPointContainer;", + "networkListManager.As(&connectionPointContainer);", + "connectionPointContainer->FindConnectionPoint(IID_INetworkConnectionEvents, &connectionPoint);" + ], + "qmake": "LIBS += -lOle32" + } } }, @@ -261,7 +301,7 @@ "disable": "input.securetransport == 'no' || input.ssl == 'no'", "condition": "config.darwin && (input.openssl == '' || input.openssl == 'no')", "output": [ - "privateFeature", + "publicFeature", { "type": "define", "name": "QT_SECURETRANSPORT" } ] }, @@ -283,7 +323,7 @@ "label": "DTLS", "purpose": "Provides a DTLS implementation", "section": "Networking", - "condition": "features.openssl && tests.dtls", + "condition": "features.openssl && features.udpsocket && tests.dtls", "output": [ "publicFeature" ] }, "ocsp": { @@ -378,6 +418,27 @@ "purpose": "Provides API for DNS lookups.", "section": "Networking", "output": [ "publicFeature" ] + }, + "gssapi": { + "label": "GSSAPI", + "purpose": "Enable SPNEGO authentication through GSSAPI", + "section": "Networking", + "condition": "!config.win32 && libs.gssapi", + "output": [ "publicFeature", "feature" ] + }, + "sspi": { + "label": "SSPI", + "purpose": "Enable NTLM/SPNEGO authentication through SSPI", + "section": "Networking", + "condition": "config.win32 && !config.winrt", + "output": [ "publicFeature", "feature" ] + }, + "netlistmgr": { + "label": "Network List Manager", + "purpose": "Use Network List Manager to keep track of network connectivity", + "section": "Networking", + "condition": "config.win32 && tests.netlistmgr", + "output": [ "privateFeature" ] } }, @@ -437,7 +498,8 @@ For example: "dtls", "ocsp", "sctp", - "system-proxies" + "system-proxies", + "gssapi" ] } ] diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri index 7074fcd5eb..110d9f56bf 100644 --- a/src/network/kernel/kernel.pri +++ b/src/network/kernel/kernel.pri @@ -16,7 +16,8 @@ HEADERS += kernel/qtnetworkglobal.h \ kernel/qnetworkinterface.h \ kernel/qnetworkinterface_p.h \ kernel/qnetworkinterface_unix_p.h \ - kernel/qnetworkproxy.h + kernel/qnetworkproxy.h \ + kernel/qnetconmonitor_p.h SOURCES += kernel/qauthenticator.cpp \ kernel/qhostaddress.cpp \ @@ -71,6 +72,19 @@ mac { !uikit: LIBS_PRIVATE += -framework CoreServices -framework SystemConfiguration } +macos | ios { + OBJECTIVE_SOURCES += \ + kernel/qnetconmonitor_darwin.mm + + LIBS_PRIVATE += -framework SystemConfiguration +} else:qtConfig(netlistmgr) { + SOURCES += kernel/qnetconmonitor_win.cpp +} else { + SOURCES += kernel/qnetconmonitor_stub.cpp +} + +qtConfig(gssapi): QMAKE_USE_PRIVATE += gssapi + uikit:HEADERS += kernel/qnetworkinterface_uikit_p.h osx:SOURCES += kernel/qnetworkproxy_mac.cpp else:win32:!winrt: SOURCES += kernel/qnetworkproxy_win.cpp diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index 47ce9ab0c6..33a30eb1cd 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -52,22 +52,34 @@ #ifdef Q_OS_WIN #include <qmutex.h> -#include <private/qmutexpool_p.h> #include <rpc.h> -#ifndef Q_OS_WINRT +#endif + +#if QT_CONFIG(sspi) // SSPI #define SECURITY_WIN32 1 #include <security.h> -#endif -#endif +#elif QT_CONFIG(gssapi) // GSSAPI +#if defined(Q_OS_DARWIN) +#include <GSS/GSS.h> +#else +#include <gssapi/gssapi.h> +#endif // Q_OS_DARWIN +#endif // Q_CONFIG(sspi) QT_BEGIN_NAMESPACE static QByteArray qNtlmPhase1(); static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data); -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) -static QByteArray qNtlmPhase1_SSPI(QAuthenticatorPrivate *ctx); -static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray& phase2data); -#endif +#if QT_CONFIG(sspi) // SSPI +static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method, + const QString& host); +static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method, + const QString& host, const QByteArray& challenge = QByteArray()); +#elif QT_CONFIG(gssapi) // GSSAPI +static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString& host); +static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, + const QByteArray& challenge = QByteArray()); +#endif // gssapi /*! \class QAuthenticator @@ -90,6 +102,7 @@ static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray& \li Basic \li NTLM version 2 \li Digest-MD5 + \li SPNEGO/Negotiate \endlist \target qauthenticator-options @@ -133,6 +146,10 @@ static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray& The Digest-MD5 authentication mechanism supports no outgoing options. + \section2 SPNEGO/Negotiate + + This authentication mechanism currently supports no incoming or outgoing options. + \sa QSslSocket */ @@ -187,7 +204,7 @@ QAuthenticator &QAuthenticator::operator=(const QAuthenticator &other) d->options = other.d->options; } else if (d->phase == QAuthenticatorPrivate::Start) { delete d; - d = 0; + d = nullptr; } return *this; } @@ -339,21 +356,25 @@ bool QAuthenticator::isNull() const return !d; } -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) -class QNtlmWindowsHandles +#if QT_CONFIG(sspi) // SSPI +class QSSPIWindowsHandles { public: CredHandle credHandle; CtxtHandle ctxHandle; }; -#endif +#elif QT_CONFIG(gssapi) // GSSAPI +class QGssApiHandles +{ +public: + gss_ctx_id_t gssCtx = nullptr; + gss_name_t targetName; +}; +#endif // gssapi QAuthenticatorPrivate::QAuthenticatorPrivate() : method(None) - #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - , ntlmWindowsHandles(0) - #endif , hasFailed(false) , phase(Start) , nonceCount(0) @@ -363,13 +384,7 @@ QAuthenticatorPrivate::QAuthenticatorPrivate() nonceCount = 0; } -QAuthenticatorPrivate::~QAuthenticatorPrivate() -{ -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - if (ntlmWindowsHandles) - delete ntlmWindowsHandles; -#endif -} +QAuthenticatorPrivate::~QAuthenticatorPrivate() = default; void QAuthenticatorPrivate::updateCredentials() { @@ -424,6 +439,9 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt } else if (method < DigestMd5 && str.startsWith("digest")) { method = DigestMd5; headerVal = current.second.mid(7); + } else if (method < Negotiate && str.startsWith("negotiate")) { + method = Negotiate; + headerVal = current.second.mid(10); } } @@ -439,6 +457,7 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt phase = Done; break; case Ntlm: + case Negotiate: // work is done in calculateResponse() break; case DigestMd5: { @@ -456,33 +475,36 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt } } -QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path) +QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path, const QString& host) { +#if !QT_CONFIG(sspi) && !QT_CONFIG(gssapi) + Q_UNUSED(host); +#endif QByteArray response; - const char *methodString = 0; + const char* methodString = nullptr; switch(method) { case QAuthenticatorPrivate::None: methodString = ""; phase = Done; break; case QAuthenticatorPrivate::Basic: - methodString = "Basic "; + methodString = "Basic"; response = user.toLatin1() + ':' + password.toLatin1(); response = response.toBase64(); phase = Done; break; case QAuthenticatorPrivate::DigestMd5: - methodString = "Digest "; + methodString = "Digest"; response = digestMd5Response(challenge, requestMethod, path); phase = Done; break; case QAuthenticatorPrivate::Ntlm: - methodString = "NTLM "; + methodString = "NTLM"; if (challenge.isEmpty()) { -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +#if QT_CONFIG(sspi) // SSPI QByteArray phase1Token; if (user.isEmpty()) // Only pull from system if no user was specified in authenticator - phase1Token = qNtlmPhase1_SSPI(this); + phase1Token = qSspiStartup(this, method, host); if (!phase1Token.isEmpty()) { response = phase1Token.toBase64(); phase = Phase2; @@ -496,10 +518,10 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet phase = Phase2; } } else { -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +#if QT_CONFIG(sspi) // SSPI QByteArray phase3Token; - if (ntlmWindowsHandles) - phase3Token = qNtlmPhase3_SSPI(this, QByteArray::fromBase64(challenge)); + if (sspiWindowsHandles) + phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge)); if (!phase3Token.isEmpty()) { response = phase3Token.toBase64(); phase = Done; @@ -512,8 +534,39 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet } break; + case QAuthenticatorPrivate::Negotiate: + methodString = "Negotiate"; + if (challenge.isEmpty()) { + QByteArray phase1Token; +#if QT_CONFIG(sspi) // SSPI + phase1Token = qSspiStartup(this, method, host); +#elif QT_CONFIG(gssapi) // GSSAPI + phase1Token = qGssapiStartup(this, host); +#endif + + if (!phase1Token.isEmpty()) { + response = phase1Token.toBase64(); + phase = Phase2; + } else { + phase = Done; + } + } else { + QByteArray phase3Token; +#if QT_CONFIG(sspi) // SSPI + phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge)); +#elif QT_CONFIG(gssapi) // GSSAPI + phase3Token = qGssapiContinue(this, QByteArray::fromBase64(challenge)); +#endif + if (!phase3Token.isEmpty()) { + response = phase3Token.toBase64(); + phase = Done; + } + } + + break; } - return QByteArray(methodString) + response; + + return QByteArray::fromRawData(methodString, qstrlen(methodString)) + ' ' + response; } @@ -699,9 +752,10 @@ QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge, return credentials; } -// ---------------------------- Digest Md5 code ---------------------------------------- +// ---------------------------- End of Digest Md5 code --------------------------------- +// ---------------------------- NTLM code ---------------------------------------------- /* * NTLM message flags. @@ -1419,156 +1473,237 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas return rc; } -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +// ---------------------------- End of NTLM code --------------------------------------- + +#if QT_CONFIG(sspi) // SSPI +// ---------------------------- SSPI code ---------------------------------------------- // See http://davenport.sourceforge.net/ntlm.html // and libcurl http_ntlm.c // Handle of secur32.dll -static HMODULE securityDLLHandle = NULL; +static HMODULE securityDLLHandle = nullptr; // Pointer to SSPI dispatch table -static PSecurityFunctionTable pSecurityFunctionTable = NULL; - +static PSecurityFunctionTable pSecurityFunctionTable = nullptr; -static bool q_NTLM_SSPI_library_load() +static bool q_SSPI_library_load() { static QBasicMutex mutex; QMutexLocker l(&mutex); // Initialize security interface - if (pSecurityFunctionTable == NULL) { + if (pSecurityFunctionTable == nullptr) { securityDLLHandle = LoadLibrary(L"secur32.dll"); - if (securityDLLHandle != NULL) { + if (securityDLLHandle != nullptr) { INIT_SECURITY_INTERFACE pInitSecurityInterface = reinterpret_cast<INIT_SECURITY_INTERFACE>( reinterpret_cast<QFunctionPointer>(GetProcAddress(securityDLLHandle, "InitSecurityInterfaceW"))); - if (pInitSecurityInterface != NULL) + if (pInitSecurityInterface != nullptr) pSecurityFunctionTable = pInitSecurityInterface(); } } - if (pSecurityFunctionTable == NULL) + if (pSecurityFunctionTable == nullptr) return false; return true; } -// Phase 1: -static QByteArray qNtlmPhase1_SSPI(QAuthenticatorPrivate *ctx) +static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method, + const QString& host) { - QByteArray result; + if (!q_SSPI_library_load()) + return QByteArray(); + + TimeStamp expiry; // For Windows 9x compatibility of SSPI calls - if (!q_NTLM_SSPI_library_load()) - return result; + if (!ctx->sspiWindowsHandles) + ctx->sspiWindowsHandles.reset(new QSSPIWindowsHandles); + memset(&ctx->sspiWindowsHandles->credHandle, 0, sizeof(CredHandle)); - // 1. The client obtains a representation of the credential set - // for the user via the SSPI AcquireCredentialsHandle function. - if (!ctx->ntlmWindowsHandles) - ctx->ntlmWindowsHandles = new QNtlmWindowsHandles; - memset(&ctx->ntlmWindowsHandles->credHandle, 0, sizeof(CredHandle)); - TimeStamp tsDummy; + // Acquire our credentials handle SECURITY_STATUS secStatus = pSecurityFunctionTable->AcquireCredentialsHandle( - NULL, (SEC_WCHAR*)L"NTLM", SECPKG_CRED_OUTBOUND, NULL, NULL, - NULL, NULL, &ctx->ntlmWindowsHandles->credHandle, &tsDummy); + nullptr, + (SEC_WCHAR*)(method == QAuthenticatorPrivate::Negotiate ? L"Negotiate" : L"NTLM"), + SECPKG_CRED_OUTBOUND, nullptr, nullptr, nullptr, nullptr, + &ctx->sspiWindowsHandles->credHandle, &expiry + ); if (secStatus != SEC_E_OK) { - delete ctx->ntlmWindowsHandles; - ctx->ntlmWindowsHandles = 0; - return result; + ctx->sspiWindowsHandles.reset(nullptr); + return QByteArray(); } - // 2. The client calls the SSPI InitializeSecurityContext function - // to obtain an authentication request token (in our case, a Type 1 message). - // The client sends this token to the server. - SecBufferDesc desc; - SecBuffer buf; - desc.ulVersion = SECBUFFER_VERSION; - desc.cBuffers = 1; - desc.pBuffers = &buf; - buf.cbBuffer = 0; - buf.BufferType = SECBUFFER_TOKEN; - buf.pvBuffer = NULL; - ULONG attrs; - - secStatus = pSecurityFunctionTable->InitializeSecurityContext(&ctx->ntlmWindowsHandles->credHandle, NULL, - const_cast<SEC_WCHAR*>(L"") /* host */, - ISC_REQ_ALLOCATE_MEMORY, - 0, SECURITY_NETWORK_DREP, - NULL, 0, - &ctx->ntlmWindowsHandles->ctxHandle, &desc, - &attrs, &tsDummy); - if (secStatus == SEC_I_COMPLETE_AND_CONTINUE || - secStatus == SEC_I_CONTINUE_NEEDED) { - pSecurityFunctionTable->CompleteAuthToken(&ctx->ntlmWindowsHandles->ctxHandle, &desc); - } else if (secStatus != SEC_E_OK) { - if ((const char*)buf.pvBuffer) - pSecurityFunctionTable->FreeContextBuffer(buf.pvBuffer); - pSecurityFunctionTable->FreeCredentialsHandle(&ctx->ntlmWindowsHandles->credHandle); - delete ctx->ntlmWindowsHandles; - ctx->ntlmWindowsHandles = 0; - return result; + return qSspiContinue(ctx, method, host); +} + +static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method, + const QString &host, const QByteArray &challenge) +{ + QByteArray result; + SecBuffer challengeBuf; + SecBuffer responseBuf; + SecBufferDesc challengeDesc; + SecBufferDesc responseDesc; + unsigned long attrs; + TimeStamp expiry; // For Windows 9x compatibility of SSPI calls + + if (!challenge.isEmpty()) + { + // Setup the challenge "input" security buffer + challengeDesc.ulVersion = SECBUFFER_VERSION; + challengeDesc.cBuffers = 1; + challengeDesc.pBuffers = &challengeBuf; + challengeBuf.BufferType = SECBUFFER_TOKEN; + challengeBuf.pvBuffer = (PVOID)(challenge.data()); + challengeBuf.cbBuffer = challenge.length(); } - result = QByteArray((const char*)buf.pvBuffer, buf.cbBuffer); - pSecurityFunctionTable->FreeContextBuffer(buf.pvBuffer); + // Setup the response "output" security buffer + responseDesc.ulVersion = SECBUFFER_VERSION; + responseDesc.cBuffers = 1; + responseDesc.pBuffers = &responseBuf; + responseBuf.BufferType = SECBUFFER_TOKEN; + responseBuf.pvBuffer = nullptr; + responseBuf.cbBuffer = 0; + + // Calculate target (SPN for Negotiate, empty for NTLM) + std::wstring targetNameW = (method == QAuthenticatorPrivate::Negotiate + ? QLatin1String("HTTP/") + host : QString()).toStdWString(); + + // Generate our challenge-response message + SECURITY_STATUS secStatus = pSecurityFunctionTable->InitializeSecurityContext( + &ctx->sspiWindowsHandles->credHandle, + !challenge.isEmpty() ? &ctx->sspiWindowsHandles->ctxHandle : nullptr, + const_cast<wchar_t*>(targetNameW.data()), + ISC_REQ_ALLOCATE_MEMORY, + 0, SECURITY_NATIVE_DREP, + !challenge.isEmpty() ? &challengeDesc : nullptr, + 0, &ctx->sspiWindowsHandles->ctxHandle, + &responseDesc, &attrs, + &expiry + ); + + if (secStatus == SEC_I_COMPLETE_NEEDED || secStatus == SEC_I_COMPLETE_AND_CONTINUE) { + secStatus = pSecurityFunctionTable->CompleteAuthToken(&ctx->sspiWindowsHandles->ctxHandle, + &responseDesc); + } + + if (secStatus != SEC_I_COMPLETE_AND_CONTINUE && secStatus != SEC_I_CONTINUE_NEEDED) { + pSecurityFunctionTable->FreeCredentialsHandle(&ctx->sspiWindowsHandles->credHandle); + pSecurityFunctionTable->DeleteSecurityContext(&ctx->sspiWindowsHandles->ctxHandle); + ctx->sspiWindowsHandles.reset(nullptr); + } + + result = QByteArray((const char*)responseBuf.pvBuffer, responseBuf.cbBuffer); + pSecurityFunctionTable->FreeContextBuffer(responseBuf.pvBuffer); + return result; } -// Phase 2: -// 3. The server receives the token from the client, and uses it as input to the -// AcceptSecurityContext SSPI function. This creates a local security context on -// the server to represent the client, and yields an authentication response token -// (the Type 2 message), which is sent to the client. +// ---------------------------- End of SSPI code --------------------------------------- + +#elif QT_CONFIG(gssapi) // GSSAPI + +// ---------------------------- GSSAPI code ---------------------------------------------- +// See postgres src/interfaces/libpq/fe-auth.c + +// Fetch all errors of a specific type +static void q_GSSAPI_error_int(const char *message, OM_uint32 stat, int type) +{ + OM_uint32 minStat, msgCtx = 0; + gss_buffer_desc msg; + + do { + gss_display_status(&minStat, stat, type, GSS_C_NO_OID, &msgCtx, &msg); + qDebug() << message << ": " << reinterpret_cast<const char*>(msg.value); + gss_release_buffer(&minStat, &msg); + } while (msgCtx); +} -// Phase 3: -static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray& phase2data) +// GSSAPI errors contain two parts; extract both +static void q_GSSAPI_error(const char *message, OM_uint32 majStat, OM_uint32 minStat) { - // 4. The client receives the response token from the server and calls - // InitializeSecurityContext again, passing the server's token as input. - // This provides us with another authentication request token (the Type 3 message). - // The return value indicates that the security context was successfully initialized; - // the token is sent to the server. + // Fetch major error codes + q_GSSAPI_error_int(message, majStat, GSS_C_GSS_CODE); + // Add the minor codes as well + q_GSSAPI_error_int(message, minStat, GSS_C_MECH_CODE); +} + +// Send initial GSS authentication token +static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString &host) +{ + OM_uint32 majStat, minStat; + + if (!ctx->gssApiHandles) + ctx->gssApiHandles.reset(new QGssApiHandles); + + // Convert target name to internal form + QByteArray serviceName = QStringLiteral("HTTPS@%1").arg(host).toLocal8Bit(); + gss_buffer_desc nameDesc = {static_cast<std::size_t>(serviceName.size()), serviceName.data()}; + + majStat = gss_import_name(&minStat, &nameDesc, + GSS_C_NT_HOSTBASED_SERVICE, &ctx->gssApiHandles->targetName); + + if (majStat != GSS_S_COMPLETE) { + q_GSSAPI_error("gss_import_name error", majStat, minStat); + ctx->gssApiHandles.reset(nullptr); + return QByteArray(); + } + + // Call qGssapiContinue with GSS_C_NO_CONTEXT to get initial packet + ctx->gssApiHandles->gssCtx = GSS_C_NO_CONTEXT; + return qGssapiContinue(ctx); +} + +// Continue GSS authentication with next token as needed +static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, const QByteArray& challenge) +{ + OM_uint32 majStat, minStat, ignored; QByteArray result; + gss_buffer_desc inBuf = {0, nullptr}; // GSS input token + gss_buffer_desc outBuf; // GSS output token - if (pSecurityFunctionTable == NULL) - return result; - - SecBuffer type_2, type_3; - SecBufferDesc type_2_desc, type_3_desc; - ULONG attrs; - TimeStamp tsDummy; // For Windows 9x compatibility of SPPI calls - - type_2_desc.ulVersion = type_3_desc.ulVersion = SECBUFFER_VERSION; - type_2_desc.cBuffers = type_3_desc.cBuffers = 1; - type_2_desc.pBuffers = &type_2; - type_3_desc.pBuffers = &type_3; - - type_2.BufferType = SECBUFFER_TOKEN; - type_2.pvBuffer = (PVOID)phase2data.data(); - type_2.cbBuffer = phase2data.length(); - type_3.BufferType = SECBUFFER_TOKEN; - type_3.pvBuffer = 0; - type_3.cbBuffer = 0; - - SECURITY_STATUS secStatus = pSecurityFunctionTable->InitializeSecurityContext(&ctx->ntlmWindowsHandles->credHandle, - &ctx->ntlmWindowsHandles->ctxHandle, - const_cast<SEC_WCHAR*>(L"") /* host */, - ISC_REQ_ALLOCATE_MEMORY, - 0, SECURITY_NETWORK_DREP, &type_2_desc, - 0, &ctx->ntlmWindowsHandles->ctxHandle, &type_3_desc, - &attrs, &tsDummy); - - if (secStatus == SEC_E_OK && ((const char*)type_3.pvBuffer)) { - result = QByteArray((const char*)type_3.pvBuffer, type_3.cbBuffer); - pSecurityFunctionTable->FreeContextBuffer(type_3.pvBuffer); + if (!challenge.isEmpty()) { + inBuf.value = const_cast<char*>(challenge.data()); + inBuf.length = challenge.length(); } - pSecurityFunctionTable->FreeCredentialsHandle(&ctx->ntlmWindowsHandles->credHandle); - pSecurityFunctionTable->DeleteSecurityContext(&ctx->ntlmWindowsHandles->ctxHandle); - delete ctx->ntlmWindowsHandles; - ctx->ntlmWindowsHandles = 0; + majStat = gss_init_sec_context(&minStat, + GSS_C_NO_CREDENTIAL, + &ctx->gssApiHandles->gssCtx, + ctx->gssApiHandles->targetName, + GSS_C_NO_OID, + GSS_C_MUTUAL_FLAG, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + challenge.isEmpty() ? GSS_C_NO_BUFFER : &inBuf, + nullptr, + &outBuf, + nullptr, + nullptr); + + if (outBuf.length != 0) + result = QByteArray(reinterpret_cast<const char*>(outBuf.value), outBuf.length); + gss_release_buffer(&ignored, &outBuf); + + if (majStat != GSS_S_COMPLETE && majStat != GSS_S_CONTINUE_NEEDED) { + q_GSSAPI_error("gss_init_sec_context error", majStat, minStat); + gss_release_name(&ignored, &ctx->gssApiHandles->targetName); + if (ctx->gssApiHandles->gssCtx) + gss_delete_sec_context(&ignored, &ctx->gssApiHandles->gssCtx, GSS_C_NO_BUFFER); + ctx->gssApiHandles.reset(nullptr); + } + + if (majStat == GSS_S_COMPLETE) { + gss_release_name(&ignored, &ctx->gssApiHandles->targetName); + ctx->gssApiHandles.reset(nullptr); + } return result; } -#endif // Q_OS_WIN && !Q_OS_WINRT + +// ---------------------------- End of GSSAPI code ---------------------------------------------- + +#endif // gssapi QT_END_NAMESPACE diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h index 265cb7afe2..e201d22650 100644 --- a/src/network/kernel/qauthenticator_p.h +++ b/src/network/kernel/qauthenticator_p.h @@ -54,6 +54,7 @@ #include <QtNetwork/private/qtnetworkglobal_p.h> #include <qhash.h> #include <qbytearray.h> +#include <qscopedpointer.h> #include <qstring.h> #include <qauthenticator.h> #include <qvariant.h> @@ -61,14 +62,16 @@ QT_BEGIN_NAMESPACE class QHttpResponseHeader; -#ifdef Q_OS_WIN -class QNtlmWindowsHandles; +#if QT_CONFIG(sspi) // SSPI +class QSSPIWindowsHandles; +#elif QT_CONFIG(gssapi) // GSSAPI +class QGssApiHandles; #endif class Q_AUTOTEST_EXPORT QAuthenticatorPrivate { public: - enum Method { None, Basic, Ntlm, DigestMd5 }; + enum Method { None, Basic, Ntlm, DigestMd5, Negotiate }; QAuthenticatorPrivate(); ~QAuthenticatorPrivate(); @@ -79,8 +82,10 @@ public: Method method; QString realm; QByteArray challenge; -#ifdef Q_OS_WIN - QNtlmWindowsHandles *ntlmWindowsHandles; +#if QT_CONFIG(sspi) // SSPI + QScopedPointer<QSSPIWindowsHandles> sspiWindowsHandles; +#elif QT_CONFIG(gssapi) // GSSAPI + QScopedPointer<QGssApiHandles> gssApiHandles; #endif bool hasFailed; //credentials have been tried but rejected by server. @@ -100,7 +105,7 @@ public: QString workstation; QString userDomain; - QByteArray calculateResponse(const QByteArray &method, const QByteArray &path); + QByteArray calculateResponse(const QByteArray &method, const QByteArray &path, const QString& host); inline static QAuthenticatorPrivate *getPrivate(QAuthenticator &auth) { return auth.d; } inline static const QAuthenticatorPrivate *getPrivate(const QAuthenticator &auth) { return auth.d; } diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h index eebd0abe66..110a74da44 100644 --- a/src/network/kernel/qdnslookup.h +++ b/src/network/kernel/qdnslookup.h @@ -64,13 +64,11 @@ class Q_NETWORK_EXPORT QDnsDomainNameRecord public: QDnsDomainNameRecord(); QDnsDomainNameRecord(const QDnsDomainNameRecord &other); -#ifdef Q_COMPILER_RVALUE_REFS - QDnsDomainNameRecord &operator=(QDnsDomainNameRecord &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QDnsDomainNameRecord &operator=(QDnsDomainNameRecord &&other) noexcept { swap(other); return *this; } QDnsDomainNameRecord &operator=(const QDnsDomainNameRecord &other); ~QDnsDomainNameRecord(); - void swap(QDnsDomainNameRecord &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QDnsDomainNameRecord &other) noexcept { qSwap(d, other.d); } QString name() const; quint32 timeToLive() const; @@ -88,13 +86,11 @@ class Q_NETWORK_EXPORT QDnsHostAddressRecord public: QDnsHostAddressRecord(); QDnsHostAddressRecord(const QDnsHostAddressRecord &other); -#ifdef Q_COMPILER_RVALUE_REFS - QDnsHostAddressRecord &operator=(QDnsHostAddressRecord &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QDnsHostAddressRecord &operator=(QDnsHostAddressRecord &&other) noexcept { swap(other); return *this; } QDnsHostAddressRecord &operator=(const QDnsHostAddressRecord &other); ~QDnsHostAddressRecord(); - void swap(QDnsHostAddressRecord &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QDnsHostAddressRecord &other) noexcept { qSwap(d, other.d); } QString name() const; quint32 timeToLive() const; @@ -112,13 +108,11 @@ class Q_NETWORK_EXPORT QDnsMailExchangeRecord public: QDnsMailExchangeRecord(); QDnsMailExchangeRecord(const QDnsMailExchangeRecord &other); -#ifdef Q_COMPILER_RVALUE_REFS - QDnsMailExchangeRecord &operator=(QDnsMailExchangeRecord &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QDnsMailExchangeRecord &operator=(QDnsMailExchangeRecord &&other) noexcept { swap(other); return *this; } QDnsMailExchangeRecord &operator=(const QDnsMailExchangeRecord &other); ~QDnsMailExchangeRecord(); - void swap(QDnsMailExchangeRecord &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QDnsMailExchangeRecord &other) noexcept { qSwap(d, other.d); } QString exchange() const; QString name() const; @@ -137,13 +131,11 @@ class Q_NETWORK_EXPORT QDnsServiceRecord public: QDnsServiceRecord(); QDnsServiceRecord(const QDnsServiceRecord &other); -#ifdef Q_COMPILER_RVALUE_REFS - QDnsServiceRecord &operator=(QDnsServiceRecord &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QDnsServiceRecord &operator=(QDnsServiceRecord &&other) noexcept { swap(other); return *this; } QDnsServiceRecord &operator=(const QDnsServiceRecord &other); ~QDnsServiceRecord(); - void swap(QDnsServiceRecord &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QDnsServiceRecord &other) noexcept { qSwap(d, other.d); } QString name() const; quint16 port() const; @@ -164,13 +156,11 @@ class Q_NETWORK_EXPORT QDnsTextRecord public: QDnsTextRecord(); QDnsTextRecord(const QDnsTextRecord &other); -#ifdef Q_COMPILER_RVALUE_REFS - QDnsTextRecord &operator=(QDnsTextRecord &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QDnsTextRecord &operator=(QDnsTextRecord &&other) noexcept { swap(other); return *this; } QDnsTextRecord &operator=(const QDnsTextRecord &other); ~QDnsTextRecord(); - void swap(QDnsTextRecord &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QDnsTextRecord &other) noexcept { qSwap(d, other.d); } QString name() const; quint32 timeToLive() const; diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h index 2dc98e527a..8c3c2ed3e1 100644 --- a/src/network/kernel/qdnslookup_p.h +++ b/src/network/kernel/qdnslookup_p.h @@ -95,7 +95,7 @@ public: QDnsLookupPrivate() : isFinished(false) , type(QDnsLookup::A) - , runnable(0) + , runnable(nullptr) { } void _q_lookupFinished(const QDnsLookupReply &reply); diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp index cfdb9ca633..262893179c 100644 --- a/src/network/kernel/qdnslookup_win.cpp +++ b/src/network/kernel/qdnslookup_win.cpp @@ -41,7 +41,6 @@ #include "qdnslookup_p.h" #include <qurl.h> -#include <private/qmutexpool_p.h> #include <private/qsystemerror_p.h> #include <qt_windows.h> diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp index 5d0ef150f3..b54fb349fb 100644 --- a/src/network/kernel/qhostaddress.cpp +++ b/src/network/kernel/qhostaddress.cpp @@ -1333,7 +1333,7 @@ QDebug operator<<(QDebug d, const QHostAddress &address) \relates QHostAddress Returns a hash of the host address \a key, using \a seed to seed the calculation. */ -uint qHash(const QHostAddress &key, uint seed) Q_DECL_NOTHROW +uint qHash(const QHostAddress &key, uint seed) noexcept { return qHashBits(key.d->a6.c, 16, seed); } diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h index 00555f3d8e..799247695e 100644 --- a/src/network/kernel/qhostaddress.h +++ b/src/network/kernel/qhostaddress.h @@ -66,7 +66,7 @@ typedef QIPv6Address Q_IPV6ADDR; class QHostAddress; // qHash is a friend, but we can't use default arguments for friends (§8.3.6.4) -Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed = 0) Q_DECL_NOTHROW; +Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed = 0) noexcept; class Q_NETWORK_EXPORT QHostAddress { @@ -102,11 +102,8 @@ public: QHostAddress(SpecialAddress address); ~QHostAddress(); -#ifdef Q_COMPILER_RVALUE_REFS - QHostAddress &operator=(QHostAddress &&other) Q_DECL_NOTHROW + QHostAddress &operator=(QHostAddress &&other) noexcept { swap(other); return *this; } -#endif - QHostAddress &operator=(const QHostAddress &other); #if QT_DEPRECATED_SINCE(5, 8) QT_DEPRECATED_X("use = QHostAddress(string) instead") @@ -114,7 +111,7 @@ public: #endif QHostAddress &operator=(SpecialAddress address); - void swap(QHostAddress &other) Q_DECL_NOTHROW { d.swap(other.d); } + void swap(QHostAddress &other) noexcept { d.swap(other.d); } void setAddress(quint32 ip4Addr); void setAddress(quint8 *ip6Addr); // ### Qt 6: remove me @@ -157,7 +154,7 @@ public: static QPair<QHostAddress, int> parseSubnet(const QString &subnet); - friend Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed) Q_DECL_NOTHROW; + friend Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed) noexcept; protected: friend class QHostAddressPrivate; QExplicitlySharedDataPointer<QHostAddressPrivate> d; diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index f4348b690e..f9335c3bb9 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -37,13 +37,17 @@ ** ****************************************************************************/ +//#define QHOSTINFO_DEBUG + #include "qhostinfo.h" #include "qhostinfo_p.h" +#include <qplatformdefs.h> #include "QtCore/qscopedpointer.h" #include <qabstracteventdispatcher.h> #include <qcoreapplication.h> #include <qmetaobject.h> +#include <qscopeguard.h> #include <qstringlist.h> #include <qthread.h> #include <qurl.h> @@ -53,6 +57,15 @@ #ifdef Q_OS_UNIX # include <unistd.h> +# include <netdb.h> +# include <netinet/in.h> +# if defined(AI_ADDRCONFIG) +# define Q_ADDRCONFIG AI_ADDRCONFIG +# endif +#elif defined Q_OS_WIN +# include <ws2tcpip.h> + +# define QT_SOCKLEN_T int #endif QT_BEGIN_NAMESPACE @@ -64,8 +77,8 @@ Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager) namespace { struct ToBeLookedUpEquals { typedef bool result_type; - explicit ToBeLookedUpEquals(const QString &toBeLookedUp) Q_DECL_NOTHROW : m_toBeLookedUp(toBeLookedUp) {} - result_type operator()(QHostInfoRunnable* lookup) const Q_DECL_NOTHROW + explicit ToBeLookedUpEquals(const QString &toBeLookedUp) noexcept : m_toBeLookedUp(toBeLookedUp) {} + result_type operator()(QHostInfoRunnable* lookup) const noexcept { return m_toBeLookedUp == lookup->toBeLookedUp; } @@ -73,13 +86,6 @@ private: QString m_toBeLookedUp; }; -// ### C++11: remove once we can use std::any_of() -template<class InputIt, class UnaryPredicate> -bool any_of(InputIt first, InputIt last, UnaryPredicate p) -{ - return std::find_if(first, last, p) != last; -} - template <typename InputIt, typename OutputIt1, typename OutputIt2, typename UnaryPredicate> std::pair<OutputIt1, OutputIt2> separate_if(InputIt first, InputIt last, OutputIt1 dest1, OutputIt2 dest2, UnaryPredicate p) { @@ -96,16 +102,6 @@ std::pair<OutputIt1, OutputIt2> separate_if(InputIt first, InputIt last, OutputI return std::make_pair(dest1, dest2); } -int get_signal_index() -{ - static auto senderMetaObject = &QHostInfoResult::staticMetaObject; - static auto signal = &QHostInfoResult::resultsReady; - int signal_index = -1; - void *args[] = { &signal_index, &signal }; - senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args); - return signal_index + QMetaObjectPrivate::signalOffset(senderMetaObject); -} - } /* @@ -123,38 +119,59 @@ void QHostInfoResult::postResultsReady(const QHostInfo &info) { // queued connection will take care of dispatching to right thread if (!slotObj) { - emitResultsReady(info); + emit resultsReady(info); return; } - static const int signal_index = get_signal_index(); - // we used to have a context object, but it's already destroyed if (withContextObject && !receiver) return; - /* QHostInfoResult c'tor moves the result object to the thread of receiver. - If we don't have a receiver, then the result object will not live in a - thread that runs an event loop - so move it to this' thread, which is the thread - that initiated the lookup, and required to have a running event loop. */ - auto result = new QHostInfoResult(receiver, slotObj); - if (!receiver) - result->moveToThread(thread()); + static const int signal_index = []() -> int { + auto senderMetaObject = &QHostInfoResult::staticMetaObject; + auto signal = &QHostInfoResult::resultsReady; + int signal_index = -1; + void *args[] = { &signal_index, &signal }; + senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args); + return signal_index + QMetaObjectPrivate::signalOffset(senderMetaObject); + }(); + + // a long-living version of this + auto result = new QHostInfoResult(this); Q_CHECK_PTR(result); + const int nargs = 2; - auto types = reinterpret_cast<int *>(malloc(nargs * sizeof(int))); - Q_CHECK_PTR(types); + auto metaCallEvent = new QMetaCallEvent(slotObj, nullptr, signal_index, nargs); + Q_CHECK_PTR(metaCallEvent); + void **args = metaCallEvent->args(); + int *types = metaCallEvent->types(); types[0] = QMetaType::type("void"); types[1] = QMetaType::type("QHostInfo"); - auto args = reinterpret_cast<void **>(malloc(nargs * sizeof(void *))); - Q_CHECK_PTR(args); - args[0] = 0; + args[0] = nullptr; args[1] = QMetaType::create(types[1], &info); Q_CHECK_PTR(args[1]); - auto metaCallEvent = new QMetaCallEvent(slotObj, nullptr, signal_index, nargs, types, args); - Q_CHECK_PTR(metaCallEvent); qApp->postEvent(result, metaCallEvent); } +/* + Receives the event posted by postResultsReady, and calls the functor. +*/ +bool QHostInfoResult::event(QEvent *event) +{ + if (event->type() == QEvent::MetaCall) { + Q_ASSERT(slotObj); + auto metaCallEvent = static_cast<QMetaCallEvent *>(event); + auto args = metaCallEvent->args(); + // we didn't have a context object, or it's still alive + if (!withContextObject || receiver) + slotObj->call(const_cast<QObject*>(receiver.data()), args); + slotObj->destroyIfLastRef(); + + deleteLater(); + return true; + } + return QObject::event(event); +} + /*! \class QHostInfo \brief The QHostInfo class provides static functions for host name lookups. @@ -249,64 +266,9 @@ static int nextId() \sa abortHostLookup(), addresses(), error(), fromName() */ -int QHostInfo::lookupHost(const QString &name, QObject *receiver, - const char *member) +int QHostInfo::lookupHost(const QString &name, QObject *receiver, const char *member) { -#if defined QHOSTINFO_DEBUG - qDebug("QHostInfo::lookupHost(\"%s\", %p, %s)", - name.toLatin1().constData(), receiver, member ? member + 1 : 0); -#endif - - if (!QAbstractEventDispatcher::instance(QThread::currentThread())) { - qWarning("QHostInfo::lookupHost() called with no event dispatcher"); - return -1; - } - - qRegisterMetaType<QHostInfo>(); - - int id = nextId(); // generate unique ID - - if (name.isEmpty()) { - if (!receiver) - return -1; - - QHostInfo hostInfo(id); - hostInfo.setError(QHostInfo::HostNotFound); - hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given")); - QScopedPointer<QHostInfoResult> result(new QHostInfoResult); - QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)), - receiver, member, Qt::QueuedConnection); - result.data()->emitResultsReady(hostInfo); - return id; - } - - QHostInfoLookupManager *manager = theHostInfoLookupManager(); - - if (manager) { - // the application is still alive - if (manager->cache.isEnabled()) { - // check cache first - bool valid = false; - QHostInfo info = manager->cache.get(name, &valid); - if (valid) { - if (!receiver) - return -1; - - info.setLookupId(id); - QHostInfoResult result; - QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); - result.emitResultsReady(info); - return id; - } - } - - // cache is not enabled or it was not in the cache, do normal lookup - QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id); - if (receiver) - QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); - manager->scheduleLookup(runnable); - } - return id; + return QHostInfoPrivate::lookupHostImpl(name, receiver, nullptr, member); } /*! @@ -416,7 +378,7 @@ QHostInfo QHostInfo::fromName(const QString &name) #endif QHostInfo hostInfo = QHostInfoAgent::fromName(name); - QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager(); + QHostInfoLookupManager* manager = theHostInfoLookupManager(); manager->cache.put(name, hostInfo); return hostInfo; } @@ -429,7 +391,7 @@ QHostInfo QHostInfoPrivate::fromName(const QString &name, QSharedPointer<QNetwor #endif QHostInfo hostInfo = QHostInfoAgent::fromName(name, session); - QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager(); + QHostInfoLookupManager* manager = theHostInfoLookupManager(); manager->cache.put(name, hostInfo); return hostInfo; } @@ -442,6 +404,162 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetw } #endif +QHostInfo QHostInfoAgent::reverseLookup(const QHostAddress &address) +{ + QHostInfo results; + // Reverse lookup + sockaddr_in sa4; + sockaddr_in6 sa6; + sockaddr *sa = 0; + QT_SOCKLEN_T saSize; + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + sa = reinterpret_cast<sockaddr *>(&sa4); + saSize = sizeof(sa4); + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + sa4.sin_addr.s_addr = htonl(address.toIPv4Address()); + } else { + sa = reinterpret_cast<sockaddr *>(&sa6); + saSize = sizeof(sa6); + memset(&sa6, 0, sizeof(sa6)); + sa6.sin6_family = AF_INET6; + memcpy(&sa6.sin6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr)); + } + + char hbuf[NI_MAXHOST]; + if (sa && getnameinfo(sa, saSize, hbuf, sizeof(hbuf), nullptr, 0, 0) == 0) + results.setHostName(QString::fromLatin1(hbuf)); + + if (results.hostName().isEmpty()) + results.setHostName(address.toString()); + results.setAddresses(QList<QHostAddress>() << address); + + return results; +} + +/* + Call getaddrinfo, and returns the results as QHostInfo::addresses +*/ +QHostInfo QHostInfoAgent::lookup(const QString &hostName) +{ + QHostInfo results; + + // IDN support + QByteArray aceHostname = QUrl::toAce(hostName); + results.setHostName(hostName); + if (aceHostname.isEmpty()) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(hostName.isEmpty() ? + QCoreApplication::translate("QHostInfoAgent", "No host name given") : + QCoreApplication::translate("QHostInfoAgent", "Invalid hostname")); + return results; + } + + addrinfo *res = 0; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; +#ifdef Q_ADDRCONFIG + hints.ai_flags = Q_ADDRCONFIG; +#endif + + int result = getaddrinfo(aceHostname.constData(), nullptr, &hints, &res); +# ifdef Q_ADDRCONFIG + if (result == EAI_BADFLAGS) { + // if the lookup failed with AI_ADDRCONFIG set, try again without it + hints.ai_flags = 0; + result = getaddrinfo(aceHostname.constData(), nullptr, &hints, &res); + } +# endif + + if (result == 0) { + addrinfo *node = res; + QList<QHostAddress> addresses; + while (node) { +#ifdef QHOSTINFO_DEBUG + qDebug() << "getaddrinfo node: flags:" << node->ai_flags << "family:" << node->ai_family + << "ai_socktype:" << node->ai_socktype << "ai_protocol:" << node->ai_protocol + << "ai_addrlen:" << node->ai_addrlen; +#endif + switch (node->ai_family) { + case AF_INET: { + QHostAddress addr; + addr.setAddress(ntohl(((sockaddr_in *) node->ai_addr)->sin_addr.s_addr)); + if (!addresses.contains(addr)) + addresses.append(addr); + break; + } + case AF_INET6: { + QHostAddress addr; + sockaddr_in6 *sa6 = (sockaddr_in6 *) node->ai_addr; + addr.setAddress(sa6->sin6_addr.s6_addr); + if (sa6->sin6_scope_id) + addr.setScopeId(QString::number(sa6->sin6_scope_id)); + if (!addresses.contains(addr)) + addresses.append(addr); + break; + } + default: + results.setError(QHostInfo::UnknownError); + results.setErrorString(QCoreApplication::translate("QHostInfoAgent", "Unknown address type")); + } + node = node->ai_next; + } + if (addresses.isEmpty()) { + // Reached the end of the list, but no addresses were found; this + // means the list contains one or more unknown address types. + results.setError(QHostInfo::UnknownError); + results.setErrorString(QCoreApplication::translate("QHostInfoAgent", "Unknown address type")); + } + + results.setAddresses(addresses); + freeaddrinfo(res); + } else { + switch (result) { +#ifdef Q_OS_WIN + case WSAHOST_NOT_FOUND: //authoritative not found + case WSATRY_AGAIN: //non authoritative not found + case WSANO_DATA: //valid name, no associated address +#else + case EAI_NONAME: + case EAI_FAIL: +# ifdef EAI_NODATA // EAI_NODATA is deprecated in RFC 3493 + case EAI_NODATA: +# endif +#endif + results.setError(QHostInfo::HostNotFound); + results.setErrorString(QCoreApplication::translate("QHostInfoAgent", "Host not found")); + break; + default: + results.setError(QHostInfo::UnknownError); +#ifdef Q_OS_WIN + results.setErrorString(QString::fromWCharArray(gai_strerror(result))); +#else + results.setErrorString(QString::fromLocal8Bit(gai_strerror(result))); +#endif + break; + } + } + +#if defined(QHOSTINFO_DEBUG) + if (results.error() != QHostInfo::NoError) { + qDebug("QHostInfoAgent::fromName(): error #%d %s", + h_errno, results.errorString().toLatin1().constData()); + } else { + QString tmp; + QList<QHostAddress> addresses = results.addresses(); + for (int i = 0; i < addresses.count(); ++i) { + if (i != 0) tmp += QLatin1String(", "); + tmp += addresses.at(i).toString(); + } + qDebug("QHostInfoAgent::fromName(): found %i entries for \"%s\": {%s}", + addresses.count(), aceHostname.constData(), + tmp.toLatin1().constData()); + } +#endif + + return results; +} /*! \enum QHostInfo::HostInfoError @@ -462,8 +580,9 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetw \sa lookupId() */ QHostInfo::QHostInfo(int id) - : d(new QHostInfoPrivate) + : d_ptr(new QHostInfoPrivate) { + Q_D(QHostInfo); d->lookupId = id; } @@ -471,17 +590,32 @@ QHostInfo::QHostInfo(int id) Constructs a copy of \a other. */ QHostInfo::QHostInfo(const QHostInfo &other) - : d(new QHostInfoPrivate(*other.d.data())) + : d_ptr(new QHostInfoPrivate(*other.d_ptr)) { } /*! + \fn QHostInfo(QHostInfo &&other) + + Move-constructs a new QHostInfo from \a other. + + \note The moved-from object \a other is placed in a + partially-formed state, in which the only valid operations are + destruction and assignment of a new value. + + \since 5.14 +*/ + +/*! Assigns the data of the \a other object to this host info object, and returns a reference to it. */ QHostInfo &QHostInfo::operator=(const QHostInfo &other) { - *d.data() = *other.d.data(); + if (d_ptr) + *d_ptr = *other.d_ptr; + else + d_ptr = new QHostInfoPrivate(*other.d_ptr); return *this; } @@ -490,6 +624,7 @@ QHostInfo &QHostInfo::operator=(const QHostInfo &other) */ QHostInfo::~QHostInfo() { + delete d_ptr; } /*! @@ -504,6 +639,7 @@ QHostInfo::~QHostInfo() */ QList<QHostAddress> QHostInfo::addresses() const { + Q_D(const QHostInfo); return d->addrs; } @@ -514,6 +650,7 @@ QList<QHostAddress> QHostInfo::addresses() const */ void QHostInfo::setAddresses(const QList<QHostAddress> &addresses) { + Q_D(QHostInfo); d->addrs = addresses; } @@ -524,6 +661,7 @@ void QHostInfo::setAddresses(const QList<QHostAddress> &addresses) */ QString QHostInfo::hostName() const { + Q_D(const QHostInfo); return d->hostName; } @@ -534,6 +672,7 @@ QString QHostInfo::hostName() const */ void QHostInfo::setHostName(const QString &hostName) { + Q_D(QHostInfo); d->hostName = hostName; } @@ -545,6 +684,7 @@ void QHostInfo::setHostName(const QString &hostName) */ QHostInfo::HostInfoError QHostInfo::error() const { + Q_D(const QHostInfo); return d->err; } @@ -555,6 +695,7 @@ QHostInfo::HostInfoError QHostInfo::error() const */ void QHostInfo::setError(HostInfoError error) { + Q_D(QHostInfo); d->err = error; } @@ -565,6 +706,7 @@ void QHostInfo::setError(HostInfoError error) */ int QHostInfo::lookupId() const { + Q_D(const QHostInfo); return d->lookupId; } @@ -575,6 +717,7 @@ int QHostInfo::lookupId() const */ void QHostInfo::setLookupId(int id) { + Q_D(QHostInfo); d->lookupId = id; } @@ -586,6 +729,7 @@ void QHostInfo::setLookupId(int id) */ QString QHostInfo::errorString() const { + Q_D(const QHostInfo); return d->errorStr; } @@ -597,6 +741,7 @@ QString QHostInfo::errorString() const */ void QHostInfo::setErrorString(const QString &str) { + Q_D(QHostInfo); d->errorStr = str; } @@ -631,14 +776,32 @@ QString QHostInfo::localHostName() \sa hostName() */ +// ### Qt 6 merge with function below int QHostInfo::lookupHostImpl(const QString &name, const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj) { + return QHostInfoPrivate::lookupHostImpl(name, receiver, slotObj, nullptr); +} +/* + Called by the various lookupHost overloads to perform the lookup. + + Signals either the functor encapuslated in the \a slotObj in the context + of \a receiver, or the \a member slot of the \a receiver. + + \a receiver might be the nullptr, but only if a \a slotObj is provided. +*/ +int QHostInfoPrivate::lookupHostImpl(const QString &name, + const QObject *receiver, + QtPrivate::QSlotObjectBase *slotObj, + const char *member) +{ #if defined QHOSTINFO_DEBUG - qDebug("QHostInfo::lookupHost(\"%s\", %p, %p)", - name.toLatin1().constData(), receiver, slotObj); + qDebug("QHostInfoPrivate::lookupHostImpl(\"%s\", %p, %p, %s)", + name.toLatin1().constData(), receiver, slotObj, member ? member + 1 : 0); #endif + Q_ASSERT(!member != !slotObj); // one of these must be set, but not both + Q_ASSERT(receiver || slotObj); if (!QAbstractEventDispatcher::instance(QThread::currentThread())) { qWarning("QHostInfo::lookupHost() called with no event dispatcher"); @@ -653,8 +816,13 @@ int QHostInfo::lookupHostImpl(const QString &name, QHostInfo hostInfo(id); hostInfo.setError(QHostInfo::HostNotFound); hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given")); + QHostInfoResult result(receiver, slotObj); + if (receiver && member) + QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), + receiver, member, Qt::QueuedConnection); result.postResultsReady(hostInfo); + return id; } @@ -669,23 +837,24 @@ int QHostInfo::lookupHostImpl(const QString &name, if (valid) { info.setLookupId(id); QHostInfoResult result(receiver, slotObj); + if (receiver && member) + QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), + receiver, member, Qt::QueuedConnection); result.postResultsReady(info); return id; } } // cache is not enabled or it was not in the cache, do normal lookup - QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id, receiver, slotObj); + QHostInfoRunnable *runnable = new QHostInfoRunnable(name, id, receiver, slotObj); + if (receiver && member) + QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), + receiver, member, Qt::QueuedConnection); manager->scheduleLookup(runnable); } return id; } -QHostInfoRunnable::QHostInfoRunnable(const QString &hn, int i) : toBeLookedUp(hn), id(i) -{ - setAutoDelete(true); -} - QHostInfoRunnable::QHostInfoRunnable(const QString &hn, int i, const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj) : toBeLookedUp(hn), id(i), resultEmitter(receiver, slotObj) @@ -697,11 +866,10 @@ QHostInfoRunnable::QHostInfoRunnable(const QString &hn, int i, const QObject *re void QHostInfoRunnable::run() { QHostInfoLookupManager *manager = theHostInfoLookupManager(); + const auto sg = qScopeGuard([&] { manager->lookupFinished(this); }); // check aborted - if (manager->wasAborted(id)) { - manager->lookupFinished(this); + if (manager->wasAborted(id)) return; - } QHostInfo hostInfo; @@ -723,10 +891,8 @@ void QHostInfoRunnable::run() } // check aborted again - if (manager->wasAborted(id)) { - manager->lookupFinished(this); + if (manager->wasAborted(id)) return; - } // signal emission hostInfo.setLookupId(id); @@ -750,16 +916,15 @@ void QHostInfoRunnable::run() } #endif - manager->lookupFinished(this); - // thread goes back to QThreadPool } -QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false) +QHostInfoLookupManager::QHostInfoLookupManager() : wasDeleted(false) { - moveToThread(QCoreApplicationPrivate::mainThread()); #if QT_CONFIG(thread) - connect(QCoreApplication::instance(), SIGNAL(destroyed()), SLOT(waitForThreadPoolDone()), Qt::DirectConnection); + QObject::connect(QCoreApplication::instance(), &QObject::destroyed, + &threadPool, [&](QObject *) { threadPool.waitForDone(); }, + Qt::DirectConnection); threadPool.setMaxThreadCount(20); // do up to 20 DNS lookups in parallel #endif } @@ -794,10 +959,9 @@ void QHostInfoLookupManager::clear() cache.clear(); } -void QHostInfoLookupManager::work() +// assumes mutex is locked by caller +void QHostInfoLookupManager::rescheduleWithMutexHeld() { - QMutexLocker locker(&mutex); - if (wasDeleted) return; @@ -816,7 +980,7 @@ void QHostInfoLookupManager::work() #if QT_CONFIG(thread) auto isAlreadyRunning = [this](QHostInfoRunnable *lookup) { - return any_of(currentLookups.cbegin(), currentLookups.cend(), ToBeLookedUpEquals(lookup->toBeLookedUp)); + return std::any_of(currentLookups.cbegin(), currentLookups.cend(), ToBeLookedUpEquals(lookup->toBeLookedUp)); }; // Transfer any postponed lookups that aren't currently running to the scheduled list, keeping already-running lookups: @@ -862,7 +1026,7 @@ void QHostInfoLookupManager::scheduleLookup(QHostInfoRunnable *r) return; scheduledLookups.enqueue(r); - work(); + rescheduleWithMutexHeld(); } // called by QHostInfo @@ -918,7 +1082,7 @@ void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r) currentLookups.removeOne(r); #endif finishedLookups.append(r); - work(); + rescheduleWithMutexHeld(); } // This function returns immediately when we had a result in the cache, else it will later emit a signal @@ -928,7 +1092,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *id = -1; // check cache - QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager(); + QHostInfoLookupManager* manager = theHostInfoLookupManager(); if (manager && manager->cache.isEnabled()) { QHostInfo info = manager->cache.get(name, valid); if (*valid) { @@ -937,7 +1101,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char } // was not in cache, trigger lookup - *id = QHostInfo::lookupHost(name, receiver, member); + *id = QHostInfoPrivate::lookupHostImpl(name, receiver, nullptr, member); // return empty response, valid==false return QHostInfo(); @@ -945,7 +1109,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char void qt_qhostinfo_clear_cache() { - QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager(); + QHostInfoLookupManager* manager = theHostInfoLookupManager(); if (manager) { manager->clear(); } @@ -954,7 +1118,7 @@ void qt_qhostinfo_clear_cache() #ifdef QT_BUILD_INTERNAL void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e) { - QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager(); + QHostInfoLookupManager* manager = theHostInfoLookupManager(); if (manager) { manager->cache.setEnabled(e); } @@ -962,7 +1126,7 @@ void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e) void qt_qhostinfo_cache_inject(const QString &hostname, const QHostInfo &resolution) { - QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager(); + QHostInfoLookupManager* manager = theHostInfoLookupManager(); if (!manager || !manager->cache.isEnabled()) return; @@ -1018,9 +1182,4 @@ void QHostInfoCache::clear() cache.clear(); } -QAbstractHostInfoLookupManager* QAbstractHostInfoLookupManager::globalInstance() -{ - return theHostInfoLookupManager(); -} - QT_END_NAMESPACE diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h index 49871ad470..cda286b423 100644 --- a/src/network/kernel/qhostinfo.h +++ b/src/network/kernel/qhostinfo.h @@ -62,11 +62,12 @@ public: explicit QHostInfo(int lookupId = -1); QHostInfo(const QHostInfo &d); + QHostInfo(QHostInfo &&other) noexcept : d_ptr(qExchange(other.d_ptr, nullptr)) {} QHostInfo &operator=(const QHostInfo &d); - QHostInfo &operator=(QHostInfo &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QHostInfo &operator=(QHostInfo &&other) noexcept { swap(other); return *this; } ~QHostInfo(); - void swap(QHostInfo &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QHostInfo &other) noexcept { qSwap(d_ptr, other.d_ptr); } QString hostName() const; void setHostName(const QString &name); @@ -147,7 +148,8 @@ public: #endif // Q_QDOC private: - QScopedPointer<QHostInfoPrivate> d; + QHostInfoPrivate *d_ptr; + Q_DECLARE_PRIVATE(QHostInfo) static int lookupHostImpl(const QString &name, const QObject *receiver, diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h index fa6529bdfd..1798ceab0a 100644 --- a/src/network/kernel/qhostinfo_p.h +++ b/src/network/kernel/qhostinfo_p.h @@ -81,69 +81,50 @@ QT_BEGIN_NAMESPACE class QHostInfoResult : public QObject { Q_OBJECT - - QPointer<const QObject> receiver = nullptr; - QtPrivate::QSlotObjectBase *slotObj = nullptr; - const bool withContextObject = false; - public: - QHostInfoResult() = default; - QHostInfoResult(const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj) : - receiver(receiver), - slotObj(slotObj), - withContextObject(slotObj && receiver) + QHostInfoResult(const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj) + : receiver(receiver), slotObj(slotObj), + withContextObject(slotObj && receiver) { - connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, - &QObject::deleteLater); - if (slotObj && receiver) + if (receiver) moveToThread(receiver->thread()); } void postResultsReady(const QHostInfo &info); -public Q_SLOTS: - inline void emitResultsReady(const QHostInfo &info) - { - if (slotObj) { - // we either didn't have a context object, or it's still alive - if (!withContextObject || receiver) { - QHostInfo copy = info; - void *args[2] = { 0, reinterpret_cast<void *>(©) }; - slotObj->call(const_cast<QObject*>(receiver.data()), args); - } - slotObj->destroyIfLastRef(); - } else { - emit resultsReady(info); - } - } +Q_SIGNALS: + void resultsReady(const QHostInfo &info); protected: - bool event(QEvent *event) override + bool event(QEvent *event) override; + +private: + QHostInfoResult(const QHostInfoResult *other) + : receiver(other->receiver), slotObj(other->slotObj), + withContextObject(other->withContextObject) { - if (event->type() == QEvent::MetaCall) { - auto metaCallEvent = static_cast<QMetaCallEvent *>(event); - auto args = metaCallEvent->args(); - auto hostInfo = reinterpret_cast<QHostInfo *>(args[1]); - emitResultsReady(*hostInfo); - deleteLater(); - return true; - } - return QObject::event(event); + // cleanup if the application terminates before results are delivered + connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, + this, &QObject::deleteLater); + // maintain thread affinity + moveToThread(other->thread()); } -Q_SIGNALS: - void resultsReady(const QHostInfo &info); + QPointer<const QObject> receiver = nullptr; + QtPrivate::QSlotObjectBase *slotObj = nullptr; + const bool withContextObject = false; }; -// needs to be QObject because fromName calls tr() -class QHostInfoAgent : public QObject +class QHostInfoAgent { - Q_OBJECT public: static QHostInfo fromName(const QString &hostName); #ifndef QT_NO_BEARERMANAGEMENT static QHostInfo fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession); #endif +private: + static QHostInfo lookup(const QString &hostName); + static QHostInfo reverseLookup(const QHostAddress &address); }; class QHostInfoPrivate @@ -159,6 +140,10 @@ public: //not a public API yet static QHostInfo fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession); #endif + static int lookupHostImpl(const QString &name, + const QObject *receiver, + QtPrivate::QSlotObjectBase *slotObj, + const char *member); QHostInfo::HostInfoError err; QString errorStr; @@ -203,7 +188,6 @@ private: class QHostInfoRunnable : public QRunnable { public: - QHostInfoRunnable(const QString &hn, int i); QHostInfoRunnable(const QString &hn, int i, const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj); void run() override; @@ -214,31 +198,13 @@ public: }; -class QAbstractHostInfoLookupManager : public QObject +class QHostInfoLookupManager { - Q_OBJECT - -public: - ~QAbstractHostInfoLookupManager() {} - virtual void clear() = 0; - - QHostInfoCache cache; - -protected: - QAbstractHostInfoLookupManager() {} - static QAbstractHostInfoLookupManager* globalInstance(); - -}; - -class QHostInfoLookupManager : public QAbstractHostInfoLookupManager -{ - Q_OBJECT public: QHostInfoLookupManager(); ~QHostInfoLookupManager(); - void clear() override; - void work(); + void clear(); // called from QHostInfo void scheduleLookup(QHostInfoRunnable *r); @@ -248,6 +214,8 @@ public: void lookupFinished(QHostInfoRunnable *r); bool wasAborted(int id); + QHostInfoCache cache; + friend class QHostInfoRunnable; protected: #if QT_CONFIG(thread) @@ -265,10 +233,8 @@ protected: bool wasDeleted; -private slots: -#if QT_CONFIG(thread) - void waitForThreadPoolDone() { threadPool.waitForDone(); } -#endif +private: + void rescheduleWithMutexHeld(); }; QT_END_NAMESPACE diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index e4810d68ee..78a05f8407 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -72,17 +72,6 @@ QT_BEGIN_NAMESPACE -// Almost always the same. If not, specify in qplatformdefs.h. -#if !defined(QT_SOCKOPTLEN_T) -# define QT_SOCKOPTLEN_T QT_SOCKLEN_T -#endif - -// HP-UXi has a bug in getaddrinfo(3) that makes it thread-unsafe -// with this flag. So disable it in that platform. -#if defined(AI_ADDRCONFIG) && !defined(Q_OS_HPUX) -# define Q_ADDRCONFIG AI_ADDRCONFIG -#endif - enum LibResolvFeature { NeedResInit, NeedResNInit @@ -197,132 +186,10 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) local_res_init(); QHostAddress address; - if (address.setAddress(hostName)) { - // Reverse lookup - sockaddr_in sa4; - sockaddr_in6 sa6; - sockaddr *sa = 0; - QT_SOCKLEN_T saSize = 0; - if (address.protocol() == QAbstractSocket::IPv4Protocol) { - sa = (sockaddr *)&sa4; - saSize = sizeof(sa4); - memset(&sa4, 0, sizeof(sa4)); - sa4.sin_family = AF_INET; - sa4.sin_addr.s_addr = htonl(address.toIPv4Address()); - } - else { - sa = (sockaddr *)&sa6; - saSize = sizeof(sa6); - memset(&sa6, 0, sizeof(sa6)); - sa6.sin6_family = AF_INET6; - memcpy(sa6.sin6_addr.s6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr.s6_addr)); - } - - char hbuf[NI_MAXHOST]; - if (sa && getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) == 0) - results.setHostName(QString::fromLatin1(hbuf)); - - if (results.hostName().isEmpty()) - results.setHostName(address.toString()); - results.setAddresses(QList<QHostAddress>() << address); - return results; - } - - // IDN support - QByteArray aceHostname = QUrl::toAce(hostName); - results.setHostName(hostName); - if (aceHostname.isEmpty()) { - results.setError(QHostInfo::HostNotFound); - results.setErrorString(hostName.isEmpty() ? - QCoreApplication::translate("QHostInfoAgent", "No host name given") : - QCoreApplication::translate("QHostInfoAgent", "Invalid hostname")); - return results; - } - - // Call getaddrinfo, and place all IPv4 addresses at the start and - // the IPv6 addresses at the end of the address list in results. - addrinfo *res = 0; - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; -#ifdef Q_ADDRCONFIG - hints.ai_flags = Q_ADDRCONFIG; -#endif + if (address.setAddress(hostName)) + return reverseLookup(address); - int result = getaddrinfo(aceHostname.constData(), 0, &hints, &res); -# ifdef Q_ADDRCONFIG - if (result == EAI_BADFLAGS) { - // if the lookup failed with AI_ADDRCONFIG set, try again without it - hints.ai_flags = 0; - result = getaddrinfo(aceHostname.constData(), 0, &hints, &res); - } -# endif - - if (result == 0) { - addrinfo *node = res; - QList<QHostAddress> addresses; - while (node) { -#ifdef QHOSTINFO_DEBUG - qDebug() << "getaddrinfo node: flags:" << node->ai_flags << "family:" << node->ai_family << "ai_socktype:" << node->ai_socktype << "ai_protocol:" << node->ai_protocol << "ai_addrlen:" << node->ai_addrlen; -#endif - if (node->ai_family == AF_INET) { - QHostAddress addr; - addr.setAddress(ntohl(((sockaddr_in *) node->ai_addr)->sin_addr.s_addr)); - if (!addresses.contains(addr)) - addresses.append(addr); - } - else if (node->ai_family == AF_INET6) { - QHostAddress addr; - sockaddr_in6 *sa6 = (sockaddr_in6 *) node->ai_addr; - addr.setAddress(sa6->sin6_addr.s6_addr); - if (sa6->sin6_scope_id) - addr.setScopeId(QString::number(sa6->sin6_scope_id)); - if (!addresses.contains(addr)) - addresses.append(addr); - } - node = node->ai_next; - } - if (addresses.isEmpty() && node == 0) { - // Reached the end of the list, but no addresses were found; this - // means the list contains one or more unknown address types. - results.setError(QHostInfo::UnknownError); - results.setErrorString(tr("Unknown address type")); - } - - results.setAddresses(addresses); - freeaddrinfo(res); - } else if (result == EAI_NONAME - || result == EAI_FAIL -#ifdef EAI_NODATA - // EAI_NODATA is deprecated in RFC 3493 - || result == EAI_NODATA -#endif - ) { - results.setError(QHostInfo::HostNotFound); - results.setErrorString(tr("Host not found")); - } else { - results.setError(QHostInfo::UnknownError); - results.setErrorString(QString::fromLocal8Bit(gai_strerror(result))); - } - - -#if defined(QHOSTINFO_DEBUG) - if (results.error() != QHostInfo::NoError) { - qDebug("QHostInfoAgent::fromName(): error #%d %s", - h_errno, results.errorString().toLatin1().constData()); - } else { - QString tmp; - QList<QHostAddress> addresses = results.addresses(); - for (int i = 0; i < addresses.count(); ++i) { - if (i != 0) tmp += ", "; - tmp += addresses.at(i).toString(); - } - qDebug("QHostInfoAgent::fromName(): found %i entries for \"%s\": {%s}", - addresses.count(), hostName.toLatin1().constData(), - tmp.toLatin1().constData()); - } -#endif - return results; + return lookup(hostName); } QString QHostInfo::localDomainName() diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp index c51e9968f8..0b5cc98970 100644 --- a/src/network/kernel/qhostinfo_win.cpp +++ b/src/network/kernel/qhostinfo_win.cpp @@ -51,27 +51,10 @@ QT_BEGIN_NAMESPACE //#define QHOSTINFO_DEBUG //### -#define QT_SOCKLEN_T int #ifndef NI_MAXHOST // already defined to 1025 in ws2tcpip.h? #define NI_MAXHOST 1024 #endif -static void translateWSAError(int error, QHostInfo *results) -{ - switch (error) { - case WSAHOST_NOT_FOUND: //authoritative not found - case WSATRY_AGAIN: //non authoritative not found - case WSANO_DATA: //valid name, no associated address - results->setError(QHostInfo::HostNotFound); - results->setErrorString(QHostInfoAgent::tr("Host not found")); - return; - default: - results->setError(QHostInfo::UnknownError); - results->setErrorString(QHostInfoAgent::tr("Unknown error (%1)").arg(error)); - return; - } -} - QHostInfo QHostInfoAgent::fromName(const QString &hostName) { QSysInfo::machineHostName(); // this initializes ws2_32.dll @@ -79,98 +62,15 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) QHostInfo results; #if defined(QHOSTINFO_DEBUG) - qDebug("QHostInfoAgent::fromName(): looking up \"%s\" (IPv6 support is %s)", - hostName.toLatin1().constData(), - (getaddrinfo && freeaddrinfo) ? "enabled" : "disabled"); + qDebug("QHostInfoAgent::fromName(%s) looking up...", + hostName.toLatin1().constData()); #endif QHostAddress address; - if (address.setAddress(hostName)) { - // Reverse lookup - sockaddr_in sa4; - sockaddr_in6 sa6; - sockaddr *sa; - QT_SOCKLEN_T saSize; - if (address.protocol() == QAbstractSocket::IPv4Protocol) { - sa = reinterpret_cast<sockaddr *>(&sa4); - saSize = sizeof(sa4); - memset(&sa4, 0, sizeof(sa4)); - sa4.sin_family = AF_INET; - sa4.sin_addr.s_addr = htonl(address.toIPv4Address()); - } else { - sa = reinterpret_cast<sockaddr *>(&sa6); - saSize = sizeof(sa6); - memset(&sa6, 0, sizeof(sa6)); - sa6.sin6_family = AF_INET6; - memcpy(&sa6.sin6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr)); - } - - char hbuf[NI_MAXHOST]; - if (getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) == 0) - results.setHostName(QString::fromLatin1(hbuf)); - - if (results.hostName().isEmpty()) - results.setHostName(address.toString()); - results.setAddresses(QList<QHostAddress>() << address); - return results; - } + if (address.setAddress(hostName)) + return reverseLookup(address); - // IDN support - QByteArray aceHostname = QUrl::toAce(hostName); - results.setHostName(hostName); - if (aceHostname.isEmpty()) { - results.setError(QHostInfo::HostNotFound); - results.setErrorString(hostName.isEmpty() ? tr("No host name given") : tr("Invalid hostname")); - return results; - } - - addrinfo *res; - int err = getaddrinfo(aceHostname.constData(), 0, 0, &res); - if (err == 0) { - QList<QHostAddress> addresses; - for (addrinfo *p = res; p != 0; p = p->ai_next) { - switch (p->ai_family) { - case AF_INET: { - QHostAddress addr; - addr.setAddress(ntohl(reinterpret_cast<sockaddr_in *>(p->ai_addr)->sin_addr.s_addr)); - if (!addresses.contains(addr)) - addresses.append(addr); - } - break; - case AF_INET6: { - QHostAddress addr; - addr.setAddress(reinterpret_cast<const sockaddr_in6 *>(p->ai_addr)->sin6_addr.s6_addr); - if (!addresses.contains(addr)) - addresses.append(addr); - } - break; - default: - results.setError(QHostInfo::UnknownError); - results.setErrorString(tr("Unknown address type")); - } - } - results.setAddresses(addresses); - freeaddrinfo(res); - } else { - translateWSAError(WSAGetLastError(), &results); - } - -#if defined(QHOSTINFO_DEBUG) - if (results.error() != QHostInfo::NoError) { - qDebug("QHostInfoAgent::run(): error (%s)", - results.errorString().toLatin1().constData()); - } else { - QString tmp; - QList<QHostAddress> addresses = results.addresses(); - for (int i = 0; i < addresses.count(); ++i) { - if (i != 0) tmp += ", "; - tmp += addresses.at(i).toString(); - } - qDebug("QHostInfoAgent::run(): found %i entries: {%s}", - addresses.count(), tmp.toLatin1().constData()); - } -#endif - return results; + return lookup(hostName); } // QString QHostInfo::localDomainName() defined in qnetworkinterface_win.cpp diff --git a/src/network/kernel/qnetconmonitor_darwin.mm b/src/network/kernel/qnetconmonitor_darwin.mm new file mode 100644 index 0000000000..f6daf9ed50 --- /dev/null +++ b/src/network/kernel/qnetconmonitor_darwin.mm @@ -0,0 +1,419 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qnativesocketengine_p.h" +#include "private/qnetconmonitor_p.h" + +#include "private/qobject_p.h" + +#include <SystemConfiguration/SystemConfiguration.h> +#include <CoreFoundation/CoreFoundation.h> + +#include <netinet/in.h> + +#include <cstring> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor"); + +namespace { + +class ReachabilityDispatchQueue +{ +public: + ReachabilityDispatchQueue() + { + queue = dispatch_queue_create("qt-network-reachability-queue", nullptr); + if (!queue) + qCWarning(lcNetMon, "Failed to create a dispatch queue for reachability probes"); + } + + ~ReachabilityDispatchQueue() + { + if (queue) + dispatch_release(queue); + } + + dispatch_queue_t data() const + { + return queue; + } + +private: + dispatch_queue_t queue = nullptr; + + Q_DISABLE_COPY_MOVE(ReachabilityDispatchQueue) +}; + +dispatch_queue_t qt_reachability_queue() +{ + static const ReachabilityDispatchQueue reachabilityQueue; + return reachabilityQueue.data(); +} + +qt_sockaddr qt_hostaddress_to_sockaddr(const QHostAddress &src) +{ + if (src.isNull()) + return {}; + + qt_sockaddr dst; + if (src.protocol() == QAbstractSocket::IPv4Protocol) { + dst.a4 = sockaddr_in{}; + dst.a4.sin_family = AF_INET; + dst.a4.sin_addr.s_addr = htonl(src.toIPv4Address()); + dst.a4.sin_len = sizeof(sockaddr_in); + } else if (src.protocol() == QAbstractSocket::IPv6Protocol) { + dst.a6 = sockaddr_in6{}; + dst.a6.sin6_family = AF_INET6; + dst.a6.sin6_len = sizeof(sockaddr_in6); + const Q_IPV6ADDR ipv6 = src.toIPv6Address(); + std::memcpy(&dst.a6.sin6_addr, &ipv6, sizeof ipv6); + } else { + Q_UNREACHABLE(); + } + + return dst; +} + +} // unnamed namespace + +class QNetworkConnectionMonitorPrivate : public QObjectPrivate +{ +public: + SCNetworkReachabilityRef probe = nullptr; + SCNetworkReachabilityFlags state = kSCNetworkReachabilityFlagsIsLocalAddress; + bool scheduled = false; + + void updateState(SCNetworkReachabilityFlags newState); + void reset(); + bool isReachable() const; + + static void probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info); + + Q_DECLARE_PUBLIC(QNetworkConnectionMonitor) +}; + +void QNetworkConnectionMonitorPrivate::updateState(SCNetworkReachabilityFlags newState) +{ + // To be executed only on the reachability queue. + Q_Q(QNetworkConnectionMonitor); + + // NETMONTODO: for now, 'online' for us means kSCNetworkReachabilityFlagsReachable + // is set. There are more possible flags that require more tests/some special + // setup. So in future this part and related can change/be extended. + const bool wasReachable = isReachable(); + state = newState; + if (wasReachable != isReachable()) + emit q->reachabilityChanged(isReachable()); +} + +void QNetworkConnectionMonitorPrivate::reset() +{ + if (probe) { + CFRelease(probe); + probe = nullptr; + } + + state = kSCNetworkReachabilityFlagsIsLocalAddress; + scheduled = false; +} + +bool QNetworkConnectionMonitorPrivate::isReachable() const +{ + return !!(state & kSCNetworkReachabilityFlagsReachable); +} + +void QNetworkConnectionMonitorPrivate::probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info) +{ + // To be executed only on the reachability queue. + Q_UNUSED(probe); + + auto monitorPrivate = static_cast<QNetworkConnectionMonitorPrivate *>(info); + Q_ASSERT(monitorPrivate); + monitorPrivate->updateState(flags); +} + +QNetworkConnectionMonitor::QNetworkConnectionMonitor() + : QObject(*new QNetworkConnectionMonitorPrivate) +{ +} + +QNetworkConnectionMonitor::QNetworkConnectionMonitor(const QHostAddress &local, const QHostAddress &remote) + : QObject(*new QNetworkConnectionMonitorPrivate) +{ + setTargets(local, remote); +} + +QNetworkConnectionMonitor::~QNetworkConnectionMonitor() +{ + Q_D(QNetworkConnectionMonitor); + + stopMonitoring(); + d->reset(); +} + +bool QNetworkConnectionMonitor::setTargets(const QHostAddress &local, const QHostAddress &remote) +{ + Q_D(QNetworkConnectionMonitor); + + if (isMonitoring()) { + qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first"); + return false; + } + + if (local.isNull()) { + qCWarning(lcNetMon, "Invalid (null) local address, cannot create a reachability target"); + return false; + } + + // Clear the old target if needed: + d->reset(); + + qt_sockaddr client = qt_hostaddress_to_sockaddr(local); + if (remote.isNull()) { + // That's a special case our QNetworkStatusMonitor is using (AnyIpv4/6 address to check an overall status). + d->probe = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, reinterpret_cast<sockaddr *>(&client)); + } else { + qt_sockaddr target = qt_hostaddress_to_sockaddr(remote); + d->probe = SCNetworkReachabilityCreateWithAddressPair(kCFAllocatorDefault, + reinterpret_cast<sockaddr *>(&client), + reinterpret_cast<sockaddr *>(&target)); + } + + if (d->probe) { + // Let's read the initial state so that callback coming later can + // see a difference. Ignore errors though. + SCNetworkReachabilityGetFlags(d->probe, &d->state); + }else { + qCWarning(lcNetMon, "Failed to create network reachability probe"); + return false; + } + + return true; +} + +bool QNetworkConnectionMonitor::startMonitoring() +{ + Q_D(QNetworkConnectionMonitor); + + if (isMonitoring()) { + qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first"); + return false; + } + + if (!d->probe) { + qCWarning(lcNetMon, "Can not start monitoring, set targets first"); + return false; + } + + auto queue = qt_reachability_queue(); + if (!queue) { + qWarning(lcNetMon, "Failed to create a dispatch queue to schedule a probe on"); + return false; + } + + SCNetworkReachabilityContext context = {}; + context.info = d; + if (!SCNetworkReachabilitySetCallback(d->probe, QNetworkConnectionMonitorPrivate::probeCallback, &context)) { + qWarning(lcNetMon, "Failed to set a reachability callback"); + return false; + } + + + if (!SCNetworkReachabilitySetDispatchQueue(d->probe, queue)) { + qWarning(lcNetMon, "Failed to schedule a reachability callback on a queue"); + return false; + } + + return d->scheduled = true; +} + +bool QNetworkConnectionMonitor::isMonitoring() const +{ + Q_D(const QNetworkConnectionMonitor); + + return d->scheduled; +} + +void QNetworkConnectionMonitor::stopMonitoring() +{ + Q_D(QNetworkConnectionMonitor); + + if (d->scheduled) { + Q_ASSERT(d->probe); + SCNetworkReachabilitySetDispatchQueue(d->probe, nullptr); + SCNetworkReachabilitySetCallback(d->probe, nullptr, nullptr); + d->scheduled = false; + } +} + +bool QNetworkConnectionMonitor::isReachable() +{ + Q_D(QNetworkConnectionMonitor); + + if (isMonitoring()) { + qCWarning(lcNetMon, "Calling isReachable() is unsafe after the monitoring started"); + return false; + } + + if (!d->probe) { + qCWarning(lcNetMon, "Reachability is unknown, set the target first"); + return false; + } + + return d->isReachable(); +} + +class QNetworkStatusMonitorPrivate : public QObjectPrivate +{ +public: + QNetworkConnectionMonitor ipv4Probe; + bool isOnlineIpv4 = false; + QNetworkConnectionMonitor ipv6Probe; + bool isOnlineIpv6 = false; +}; + +QNetworkStatusMonitor::QNetworkStatusMonitor() + : QObject(*new QNetworkStatusMonitorPrivate) +{ + Q_D(QNetworkStatusMonitor); + + if (d->ipv4Probe.setTargets(QHostAddress::AnyIPv4, {})) { + // We manage to create SCNetworkReachabilityRef for IPv4, let's + // read the last known state then! + d->isOnlineIpv4 = d->ipv4Probe.isReachable(); + } + + if (d->ipv6Probe.setTargets(QHostAddress::AnyIPv6, {})) { + // We manage to create SCNetworkReachability ref for IPv6, let's + // read the last known state then! + d->isOnlineIpv6 = d->ipv6Probe.isReachable(); + } + + + connect(&d->ipv4Probe, &QNetworkConnectionMonitor::reachabilityChanged, this, + &QNetworkStatusMonitor::reachabilityChanged, Qt::QueuedConnection); + connect(&d->ipv6Probe, &QNetworkConnectionMonitor::reachabilityChanged, this, + &QNetworkStatusMonitor::reachabilityChanged, Qt::QueuedConnection); +} + +QNetworkStatusMonitor::~QNetworkStatusMonitor() +{ + Q_D(QNetworkStatusMonitor); + + d->ipv4Probe.disconnect(); + d->ipv4Probe.stopMonitoring(); + d->ipv6Probe.disconnect(); + d->ipv6Probe.stopMonitoring(); +} + +bool QNetworkStatusMonitor::start() +{ + Q_D(QNetworkStatusMonitor); + + if (isMonitoring()) { + qCWarning(lcNetMon, "Network status monitor is already active"); + return true; + } + + d->ipv4Probe.startMonitoring(); + d->ipv6Probe.startMonitoring(); + + return isMonitoring(); +} + +void QNetworkStatusMonitor::stop() +{ + Q_D(QNetworkStatusMonitor); + + if (d->ipv4Probe.isMonitoring()) + d->ipv4Probe.stopMonitoring(); + if (d->ipv6Probe.isMonitoring()) + d->ipv6Probe.stopMonitoring(); +} + +bool QNetworkStatusMonitor::isMonitoring() const +{ + Q_D(const QNetworkStatusMonitor); + + return d->ipv4Probe.isMonitoring() || d->ipv6Probe.isMonitoring(); +} + +bool QNetworkStatusMonitor::isNetworkAccessible() +{ + // This function is to be executed on the thread that created + // and uses 'this'. + Q_D(QNetworkStatusMonitor); + + return d->isOnlineIpv4 || d->isOnlineIpv6; +} + +bool QNetworkStatusMonitor::isEnabled() +{ + return true; +} + +void QNetworkStatusMonitor::reachabilityChanged(bool online) +{ + // This function is executed on the thread that created/uses 'this', + // not on the reachability queue. + Q_D(QNetworkStatusMonitor); + + auto probe = qobject_cast<QNetworkConnectionMonitor *>(sender()); + if (!probe) + return; + + const bool isIpv4 = probe == &d->ipv4Probe; + bool &probeOnline = isIpv4 ? d->isOnlineIpv4 : d->isOnlineIpv6; + bool otherOnline = isIpv4 ? d->isOnlineIpv6 : d->isOnlineIpv4; + + if (probeOnline == online) { + // We knew this already? + return; + } + + probeOnline = online; + if (!otherOnline) { + // We either just lost or got a network access. + emit onlineStateChanged(probeOnline); + } +} + +QT_END_NAMESPACE diff --git a/src/network/kernel/qnetconmonitor_p.h b/src/network/kernel/qnetconmonitor_p.h new file mode 100644 index 0000000000..282bac5081 --- /dev/null +++ b/src/network/kernel/qnetconmonitor_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETCONMONITOR_P_H +#define QNETCONMONITOR_P_H + +#include <private/qtnetworkglobal_p.h> + +#include <QtCore/qloggingcategory.h> +#include <QtNetwork/qhostaddress.h> +#include <QtCore/qglobal.h> +#include <QtCore/qobject.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QNetworkConnectionMonitorPrivate; +class Q_AUTOTEST_EXPORT QNetworkConnectionMonitor : public QObject +{ + Q_OBJECT + +public: + QNetworkConnectionMonitor(); + QNetworkConnectionMonitor(const QHostAddress &local, const QHostAddress &remote = {}); + ~QNetworkConnectionMonitor(); + + bool setTargets(const QHostAddress &local, const QHostAddress &remote); + bool isReachable(); + + // Important: on Darwin you should not call isReachable() after + // startMonitoring(), you have to listen to reachabilityChanged() + // signal instead. + bool startMonitoring(); + bool isMonitoring() const; + void stopMonitoring(); + +Q_SIGNALS: + // Important: connect to this using QueuedConnection. On Darwin + // callback is coming on a special dispatch queue. + void reachabilityChanged(bool isOnline); + +private: + Q_DECLARE_PRIVATE(QNetworkConnectionMonitor) + Q_DISABLE_COPY_MOVE(QNetworkConnectionMonitor) +}; + +class QNetworkStatusMonitorPrivate; +class Q_AUTOTEST_EXPORT QNetworkStatusMonitor : public QObject +{ + Q_OBJECT + +public: + QNetworkStatusMonitor(); + ~QNetworkStatusMonitor(); + + bool isNetworkAccessible(); + + bool start(); + void stop(); + bool isMonitoring() const; + + static bool isEnabled(); + +Q_SIGNALS: + // Unlike QNetworkConnectionMonitor, this can be connected to directly. + void onlineStateChanged(bool isOnline); + +private slots: + void reachabilityChanged(bool isOnline); + +private: + Q_DECLARE_PRIVATE(QNetworkStatusMonitor) + Q_DISABLE_COPY_MOVE(QNetworkStatusMonitor) +}; + +Q_DECLARE_LOGGING_CATEGORY(lcNetMon) + +QT_END_NAMESPACE + +#endif // QNETCONMONITOR_P_H diff --git a/src/network/kernel/qnetconmonitor_stub.cpp b/src/network/kernel/qnetconmonitor_stub.cpp new file mode 100644 index 0000000000..1ad4e9ba5a --- /dev/null +++ b/src/network/kernel/qnetconmonitor_stub.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetconmonitor_p.h" + +#include "private/qobject_p.h" + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor"); + +// Note: this 'stub' version is never enabled (see QNetworkStatusMonitor::isEnabled below) +// and thus should never affect QNAM in any unusuall way. Having this 'stub' version is similar +// to building Qt with bearer management configured out. + +class QNetworkConnectionMonitorPrivate : public QObjectPrivate +{ +}; + +QNetworkConnectionMonitor::QNetworkConnectionMonitor() + : QObject(*new QNetworkConnectionMonitorPrivate) +{ +} + +QNetworkConnectionMonitor::QNetworkConnectionMonitor(const QHostAddress &local, const QHostAddress &remote) + : QObject(*new QNetworkConnectionMonitorPrivate) +{ + Q_UNUSED(local) + Q_UNUSED(remote) +} + +QNetworkConnectionMonitor::~QNetworkConnectionMonitor() +{ +} + +bool QNetworkConnectionMonitor::setTargets(const QHostAddress &local, const QHostAddress &remote) +{ + Q_UNUSED(local) + Q_UNUSED(remote) + + return false; +} + +bool QNetworkConnectionMonitor::startMonitoring() +{ + return false; +} + +bool QNetworkConnectionMonitor::isMonitoring() const +{ + return false; +} + +void QNetworkConnectionMonitor::stopMonitoring() +{ +} + +bool QNetworkConnectionMonitor::isReachable() +{ + return false; +} + +class QNetworkStatusMonitorPrivate : public QObjectPrivate +{ +}; + +QNetworkStatusMonitor::QNetworkStatusMonitor() + : QObject(*new QNetworkStatusMonitorPrivate) +{ +} + +QNetworkStatusMonitor::~QNetworkStatusMonitor() +{ +} + +bool QNetworkStatusMonitor::start() +{ + return false; +} + +void QNetworkStatusMonitor::stop() +{ +} + +bool QNetworkStatusMonitor::isMonitoring() const +{ + return false; +} + +bool QNetworkStatusMonitor::isNetworkAccessible() +{ + return false; +} + +bool QNetworkStatusMonitor::isEnabled() +{ + return false; +} + +void QNetworkStatusMonitor::reachabilityChanged(bool online) +{ + Q_UNUSED(online) +} + +QT_END_NAMESPACE diff --git a/src/network/kernel/qnetconmonitor_win.cpp b/src/network/kernel/qnetconmonitor_win.cpp new file mode 100644 index 0000000000..1566e7f914 --- /dev/null +++ b/src/network/kernel/qnetconmonitor_win.cpp @@ -0,0 +1,712 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetconmonitor_p.h" + +#include "private/qobject_p.h" + +#include <QtCore/quuid.h> +#include <QtCore/qmetaobject.h> + +#include <QtNetwork/qnetworkinterface.h> + +#include <objbase.h> +#include <netlistmgr.h> +#include <wrl/client.h> +#include <wrl/wrappers/corewrappers.h> +#include <comdef.h> +#include <iphlpapi.h> + +#include <algorithm> + +using namespace Microsoft::WRL; + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor"); + +namespace { +QString errorStringFromHResult(HRESULT hr) +{ + _com_error error(hr); + return QString::fromWCharArray(error.ErrorMessage()); +} + +template<typename T> +bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject) +{ + if (riid == __uuidof(T)) { + *ppvObject = static_cast<T *>(from); + from->AddRef(); + return true; + } + return false; +} + +QNetworkInterface getInterfaceFromHostAddress(const QHostAddress &local) +{ + QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces(); + auto it = std::find_if( + interfaces.cbegin(), interfaces.cend(), [&local](const QNetworkInterface &iface) { + const auto &entries = iface.addressEntries(); + return std::any_of(entries.cbegin(), entries.cend(), + [&local](const QNetworkAddressEntry &entry) { + return entry.ip().isEqual(local, + QHostAddress::TolerantConversion); + }); + }); + if (it == interfaces.cend()) { + qCWarning(lcNetMon, "Could not find the interface for the local address."); + return {}; + } + return *it; +} +} // anonymous namespace + +class QNetworkConnectionEvents : public INetworkConnectionEvents +{ +public: + QNetworkConnectionEvents(QNetworkConnectionMonitorPrivate *monitor); + virtual ~QNetworkConnectionEvents(); + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; + + ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; } + ULONG STDMETHODCALLTYPE Release() override + { + if (--ref == 0) { + delete this; + return 0; + } + return ref; + } + + HRESULT STDMETHODCALLTYPE + NetworkConnectionConnectivityChanged(GUID connectionId, NLM_CONNECTIVITY connectivity) override; + HRESULT STDMETHODCALLTYPE NetworkConnectionPropertyChanged( + GUID connectionId, NLM_CONNECTION_PROPERTY_CHANGE flags) override; + + Q_REQUIRED_RESULT + bool setTarget(const QNetworkInterface &iface); + Q_REQUIRED_RESULT + bool startMonitoring(); + Q_REQUIRED_RESULT + bool stopMonitoring(); + +private: + ComPtr<INetworkConnection> getNetworkConnectionFromAdapterGuid(QUuid guid); + + QUuid currentConnectionId{}; + + ComPtr<INetworkListManager> networkListManager; + ComPtr<IConnectionPoint> connectionPoint; + + QNetworkConnectionMonitorPrivate *monitor = nullptr; + + QAtomicInteger<ULONG> ref = 0; + DWORD cookie = 0; +}; + +class QNetworkConnectionMonitorPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QNetworkConnectionMonitor); + +public: + QNetworkConnectionMonitorPrivate(); + ~QNetworkConnectionMonitorPrivate(); + + Q_REQUIRED_RESULT + bool setTargets(const QHostAddress &local, const QHostAddress &remote); + Q_REQUIRED_RESULT + bool startMonitoring(); + void stopMonitoring(); + + void setConnectivity(NLM_CONNECTIVITY newConnectivity); + +private: + ComPtr<QNetworkConnectionEvents> connectionEvents; + // We can assume we have access to internet/subnet when this class is created because + // connection has already been established to the peer: + NLM_CONNECTIVITY connectivity = + NLM_CONNECTIVITY(NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET + | NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV6_SUBNET); + + bool sameSubnet = false; + bool monitoring = false; + bool comInitFailed = false; + bool remoteIsIPv6 = false; +}; + +QNetworkConnectionEvents::QNetworkConnectionEvents(QNetworkConnectionMonitorPrivate *monitor) + : monitor(monitor) +{ + auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER, + IID_INetworkListManager, &networkListManager); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Could not get a NetworkListManager instance:" + << errorStringFromHResult(hr); + return; + } + + ComPtr<IConnectionPointContainer> connectionPointContainer; + hr = networkListManager.As(&connectionPointContainer); + if (SUCCEEDED(hr)) { + hr = connectionPointContainer->FindConnectionPoint(IID_INetworkConnectionEvents, + &connectionPoint); + } + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to get connection point for network events:" + << errorStringFromHResult(hr); + } +} + +QNetworkConnectionEvents::~QNetworkConnectionEvents() +{ + Q_ASSERT(ref == 0); +} + +ComPtr<INetworkConnection> QNetworkConnectionEvents::getNetworkConnectionFromAdapterGuid(QUuid guid) +{ + ComPtr<IEnumNetworkConnections> connections; + auto hr = networkListManager->GetNetworkConnections(connections.GetAddressOf()); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to enumerate network connections:" + << errorStringFromHResult(hr); + return nullptr; + } + ComPtr<INetworkConnection> connection = nullptr; + do { + hr = connections->Next(1, connection.GetAddressOf(), nullptr); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to get next network connection in enumeration:" + << errorStringFromHResult(hr); + break; + } + if (connection) { + GUID adapterId; + hr = connection->GetAdapterId(&adapterId); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to get adapter ID from network connection:" + << errorStringFromHResult(hr); + continue; + } + if (guid == adapterId) + return connection; + } + } while (connection); + return nullptr; +} + +HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::QueryInterface(REFIID riid, void **ppvObject) +{ + if (!ppvObject) + return E_INVALIDARG; + + return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject) + || QueryInterfaceImpl<INetworkConnectionEvents>(this, riid, ppvObject) + ? S_OK + : E_NOINTERFACE; +} + +HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::NetworkConnectionConnectivityChanged( + GUID connectionId, NLM_CONNECTIVITY newConnectivity) +{ + // This function is run on a different thread than 'monitor' is created on, so we need to run + // it on that thread + QMetaObject::invokeMethod(monitor->q_ptr, + [this, connectionId, newConnectivity, monitor = this->monitor]() { + if (connectionId == currentConnectionId) + monitor->setConnectivity(newConnectivity); + }, + Qt::QueuedConnection); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::NetworkConnectionPropertyChanged( + GUID connectionId, NLM_CONNECTION_PROPERTY_CHANGE flags) +{ + Q_UNUSED(connectionId); + Q_UNUSED(flags); + return E_NOTIMPL; +} + +bool QNetworkConnectionEvents::setTarget(const QNetworkInterface &iface) +{ + // Unset this in case it's already set to something + currentConnectionId = QUuid{}; + + NET_LUID luid; + if (ConvertInterfaceIndexToLuid(iface.index(), &luid) != NO_ERROR) { + qCWarning(lcNetMon, "Could not get the LUID for the interface."); + return false; + } + GUID guid; + if (ConvertInterfaceLuidToGuid(&luid, &guid) != NO_ERROR) { + qCWarning(lcNetMon, "Could not get the GUID for the interface."); + return false; + } + ComPtr<INetworkConnection> connection = getNetworkConnectionFromAdapterGuid(guid); + if (!connection) { + qCWarning(lcNetMon, "Could not get the INetworkConnection instance for the adapter GUID."); + return false; + } + auto hr = connection->GetConnectionId(&guid); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to get the connection's GUID:" << errorStringFromHResult(hr); + return false; + } + currentConnectionId = guid; + + return true; +} + +bool QNetworkConnectionEvents::startMonitoring() +{ + if (currentConnectionId.isNull()) { + qCWarning(lcNetMon, "Can not start monitoring, set targets first"); + return false; + } + if (!connectionPoint) { + qCWarning(lcNetMon, + "We don't have the connection point, cannot start listening to events!"); + return false; + } + + auto hr = connectionPoint->Advise(this, &cookie); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to subscribe to network connectivity events:" + << errorStringFromHResult(hr); + return false; + } + return true; +} + +bool QNetworkConnectionEvents::stopMonitoring() +{ + auto hr = connectionPoint->Unadvise(cookie); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to unsubscribe from network connection events:" + << errorStringFromHResult(hr); + return false; + } + cookie = 0; + currentConnectionId = QUuid{}; + return true; +} + +QNetworkConnectionMonitorPrivate::QNetworkConnectionMonitorPrivate() +{ + auto hr = CoInitialize(nullptr); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr); + comInitFailed = true; + return; + } + + connectionEvents = new QNetworkConnectionEvents(this); +} + +QNetworkConnectionMonitorPrivate::~QNetworkConnectionMonitorPrivate() +{ + if (comInitFailed) + return; + if (monitoring) + stopMonitoring(); + connectionEvents.Reset(); + CoUninitialize(); +} + +bool QNetworkConnectionMonitorPrivate::setTargets(const QHostAddress &local, + const QHostAddress &remote) +{ + if (comInitFailed) + return false; + + QNetworkInterface iface = getInterfaceFromHostAddress(local); + if (!iface.isValid()) + return false; + const auto &addressEntries = iface.addressEntries(); + auto it = std::find_if( + addressEntries.cbegin(), addressEntries.cend(), + [&local](const QNetworkAddressEntry &entry) { return entry.ip() == local; }); + if (Q_UNLIKELY(it == addressEntries.cend())) { + qCWarning(lcNetMon, "The address entry we were working with disappeared"); + return false; + } + sameSubnet = remote.isInSubnet(local, it->prefixLength()); + remoteIsIPv6 = remote.protocol() == QAbstractSocket::IPv6Protocol; + + return connectionEvents->setTarget(iface); +} + +void QNetworkConnectionMonitorPrivate::setConnectivity(NLM_CONNECTIVITY newConnectivity) +{ + Q_Q(QNetworkConnectionMonitor); + const bool reachable = q->isReachable(); + connectivity = newConnectivity; + const bool newReachable = q->isReachable(); + if (reachable != newReachable) + emit q->reachabilityChanged(newReachable); +} + +bool QNetworkConnectionMonitorPrivate::startMonitoring() +{ + Q_ASSERT(connectionEvents); + Q_ASSERT(!monitoring); + if (connectionEvents->startMonitoring()) + monitoring = true; + return monitoring; +} + +void QNetworkConnectionMonitorPrivate::stopMonitoring() +{ + Q_ASSERT(connectionEvents); + Q_ASSERT(monitoring); + if (connectionEvents->stopMonitoring()) + monitoring = false; +} + +QNetworkConnectionMonitor::QNetworkConnectionMonitor() + : QObject(*new QNetworkConnectionMonitorPrivate) +{ +} + +QNetworkConnectionMonitor::QNetworkConnectionMonitor(const QHostAddress &local, + const QHostAddress &remote) + : QObject(*new QNetworkConnectionMonitorPrivate) +{ + setTargets(local, remote); +} + +QNetworkConnectionMonitor::~QNetworkConnectionMonitor() = default; + +bool QNetworkConnectionMonitor::setTargets(const QHostAddress &local, const QHostAddress &remote) +{ + if (isMonitoring()) { + qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first"); + return false; + } + if (local.isNull()) { + qCWarning(lcNetMon, "Invalid (null) local address, cannot create a reachability target"); + return false; + } + // Silently return false for loopback addresses instead of printing warnings later + if (remote.isLoopback()) + return false; + + return d_func()->setTargets(local, remote); +} + +bool QNetworkConnectionMonitor::startMonitoring() +{ + Q_D(QNetworkConnectionMonitor); + if (isMonitoring()) { + qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first"); + return false; + } + return d->startMonitoring(); +} + +bool QNetworkConnectionMonitor::isMonitoring() const +{ + return d_func()->monitoring; +} + +void QNetworkConnectionMonitor::stopMonitoring() +{ + Q_D(QNetworkConnectionMonitor); + if (!isMonitoring()) { + qCWarning(lcNetMon, "stopMonitoring was called when not monitoring!"); + return; + } + d->stopMonitoring(); +} + +bool QNetworkConnectionMonitor::isReachable() +{ + Q_D(QNetworkConnectionMonitor); + NLM_CONNECTIVITY required = d->sameSubnet + ? (d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_SUBNET : NLM_CONNECTIVITY_IPV4_SUBNET) + : (d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_INTERNET : NLM_CONNECTIVITY_IPV4_INTERNET); + return d_func()->connectivity & required; +} + +class QNetworkListManagerEvents : public INetworkListManagerEvents +{ +public: + QNetworkListManagerEvents(QNetworkStatusMonitorPrivate *monitor); + virtual ~QNetworkListManagerEvents(); + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; + + ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; } + ULONG STDMETHODCALLTYPE Release() override + { + if (--ref == 0) { + delete this; + return 0; + } + return ref; + } + + HRESULT STDMETHODCALLTYPE ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) override; + + Q_REQUIRED_RESULT + bool start(); + Q_REQUIRED_RESULT + bool stop(); + +private: + ComPtr<INetworkListManager> networkListManager = nullptr; + ComPtr<IConnectionPoint> connectionPoint = nullptr; + + QNetworkStatusMonitorPrivate *monitor = nullptr; + + QAtomicInteger<ULONG> ref = 0; + DWORD cookie = 0; +}; + +class QNetworkStatusMonitorPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QNetworkStatusMonitor); + +public: + QNetworkStatusMonitorPrivate(); + ~QNetworkStatusMonitorPrivate(); + + Q_REQUIRED_RESULT + bool start(); + void stop(); + + void setConnectivity(NLM_CONNECTIVITY newConnectivity); + +private: + friend class QNetworkListManagerEvents; + + ComPtr<QNetworkListManagerEvents> managerEvents; + NLM_CONNECTIVITY connectivity = NLM_CONNECTIVITY_DISCONNECTED; + + bool monitoring = false; + bool comInitFailed = false; +}; + +QNetworkListManagerEvents::QNetworkListManagerEvents(QNetworkStatusMonitorPrivate *monitor) + : monitor(monitor) +{ + auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER, + IID_INetworkListManager, &networkListManager); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Could not get a NetworkListManager instance:" + << errorStringFromHResult(hr); + return; + } + + // Set initial connectivity + hr = networkListManager->GetConnectivity(&monitor->connectivity); + if (FAILED(hr)) + qCWarning(lcNetMon) << "Could not get connectivity:" << errorStringFromHResult(hr); + + ComPtr<IConnectionPointContainer> connectionPointContainer; + hr = networkListManager.As(&connectionPointContainer); + if (SUCCEEDED(hr)) { + hr = connectionPointContainer->FindConnectionPoint(IID_INetworkListManagerEvents, + &connectionPoint); + } + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to get connection point for network list manager events:" + << errorStringFromHResult(hr); + } +} + +QNetworkListManagerEvents::~QNetworkListManagerEvents() +{ + Q_ASSERT(ref == 0); +} + +HRESULT STDMETHODCALLTYPE QNetworkListManagerEvents::QueryInterface(REFIID riid, void **ppvObject) +{ + if (!ppvObject) + return E_INVALIDARG; + + return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject) + || QueryInterfaceImpl<INetworkListManagerEvents>(this, riid, ppvObject) + ? S_OK + : E_NOINTERFACE; +} + +HRESULT STDMETHODCALLTYPE +QNetworkListManagerEvents::ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) +{ + // This function is run on a different thread than 'monitor' is created on, so we need to run + // it on that thread + QMetaObject::invokeMethod(monitor->q_ptr, + [newConnectivity, monitor = this->monitor]() { + monitor->setConnectivity(newConnectivity); + }, + Qt::QueuedConnection); + return S_OK; +} + +bool QNetworkListManagerEvents::start() +{ + if (!connectionPoint) { + qCWarning(lcNetMon, "Initialization failed, can't start!"); + return false; + } + auto hr = connectionPoint->Advise(this, &cookie); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to subscribe to network connectivity events:" + << errorStringFromHResult(hr); + return false; + } + return true; +} + +bool QNetworkListManagerEvents::stop() +{ + Q_ASSERT(connectionPoint); + auto hr = connectionPoint->Unadvise(cookie); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to unsubscribe from network connectivity events:" + << errorStringFromHResult(hr); + return false; + } + cookie = 0; + return true; +} + +QNetworkStatusMonitorPrivate::QNetworkStatusMonitorPrivate() +{ + auto hr = CoInitialize(nullptr); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr); + comInitFailed = true; + return; + } + managerEvents = new QNetworkListManagerEvents(this); +} + +QNetworkStatusMonitorPrivate::~QNetworkStatusMonitorPrivate() +{ + if (comInitFailed) + return; + if (monitoring) + stop(); + managerEvents.Reset(); + CoUninitialize(); +} + +void QNetworkStatusMonitorPrivate::setConnectivity(NLM_CONNECTIVITY newConnectivity) +{ + Q_Q(QNetworkStatusMonitor); + + const bool oldAccessibility = q->isNetworkAccessible(); + connectivity = newConnectivity; + const bool accessibility = q->isNetworkAccessible(); + if (oldAccessibility != accessibility) + emit q->onlineStateChanged(accessibility); +} + +bool QNetworkStatusMonitorPrivate::start() +{ + if (comInitFailed) + return false; + Q_ASSERT(managerEvents); + Q_ASSERT(!monitoring); + if (managerEvents->start()) + monitoring = true; + return monitoring; +} + +void QNetworkStatusMonitorPrivate::stop() +{ + Q_ASSERT(managerEvents); + Q_ASSERT(monitoring); + if (managerEvents->stop()) + monitoring = false; +} + +QNetworkStatusMonitor::QNetworkStatusMonitor() : QObject(*new QNetworkStatusMonitorPrivate) {} + +QNetworkStatusMonitor::~QNetworkStatusMonitor() {} + +bool QNetworkStatusMonitor::start() +{ + if (isMonitoring()) { + qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first"); + return false; + } + + return d_func()->start(); +} + +void QNetworkStatusMonitor::stop() +{ + if (!isMonitoring()) { + qCWarning(lcNetMon, "stopMonitoring was called when not monitoring!"); + return; + } + + d_func()->stop(); +} + +bool QNetworkStatusMonitor::isMonitoring() const +{ + return d_func()->monitoring; +} + +bool QNetworkStatusMonitor::isNetworkAccessible() +{ + return d_func()->connectivity + & (NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET + | NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV6_SUBNET); +} + +bool QNetworkStatusMonitor::isEnabled() +{ + return true; +} + +void QNetworkStatusMonitor::reachabilityChanged(bool online) +{ + Q_UNUSED(online); + Q_UNREACHABLE(); +} + +QT_END_NAMESPACE diff --git a/src/network/kernel/qnetworkdatagram.h b/src/network/kernel/qnetworkdatagram.h index 1acb44a1e0..70958fea42 100644 --- a/src/network/kernel/qnetworkdatagram.h +++ b/src/network/kernel/qnetworkdatagram.h @@ -61,13 +61,13 @@ public: ~QNetworkDatagram() { if (d) destroy(d); } - QNetworkDatagram(QNetworkDatagram &&other) Q_DECL_NOTHROW + QNetworkDatagram(QNetworkDatagram &&other) noexcept : d(other.d) { other.d = nullptr; } - QNetworkDatagram &operator=(QNetworkDatagram &&other) Q_DECL_NOTHROW + QNetworkDatagram &operator=(QNetworkDatagram &&other) noexcept { swap(other); return *this; } - void swap(QNetworkDatagram &other) Q_DECL_NOTHROW + void swap(QNetworkDatagram &other) noexcept { qSwap(d, other.d); } void clear(); diff --git a/src/network/kernel/qnetworkinterface.h b/src/network/kernel/qnetworkinterface.h index 148fd5e10d..4caedaa38f 100644 --- a/src/network/kernel/qnetworkinterface.h +++ b/src/network/kernel/qnetworkinterface.h @@ -64,13 +64,11 @@ public: QNetworkAddressEntry(); QNetworkAddressEntry(const QNetworkAddressEntry &other); -#ifdef Q_COMPILER_RVALUE_REFS - QNetworkAddressEntry &operator=(QNetworkAddressEntry &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QNetworkAddressEntry &operator=(QNetworkAddressEntry &&other) noexcept { swap(other); return *this; } QNetworkAddressEntry &operator=(const QNetworkAddressEntry &other); ~QNetworkAddressEntry(); - void swap(QNetworkAddressEntry &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QNetworkAddressEntry &other) noexcept { qSwap(d, other.d); } bool operator==(const QNetworkAddressEntry &other) const; inline bool operator!=(const QNetworkAddressEntry &other) const @@ -142,13 +140,11 @@ public: QNetworkInterface(); QNetworkInterface(const QNetworkInterface &other); -#ifdef Q_COMPILER_RVALUE_REFS - QNetworkInterface &operator=(QNetworkInterface &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QNetworkInterface &operator=(QNetworkInterface &&other) noexcept { swap(other); return *this; } QNetworkInterface &operator=(const QNetworkInterface &other); ~QNetworkInterface(); - void swap(QNetworkInterface &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QNetworkInterface &other) noexcept { qSwap(d, other.d); } bool isValid() const; diff --git a/src/network/kernel/qnetworkinterface_p.h b/src/network/kernel/qnetworkinterface_p.h index 87a46b75fa..44e27a7e34 100644 --- a/src/network/kernel/qnetworkinterface_p.h +++ b/src/network/kernel/qnetworkinterface_p.h @@ -82,7 +82,7 @@ public: class QNetworkInterfacePrivate: public QSharedData { public: - QNetworkInterfacePrivate() : index(0), flags(0) + QNetworkInterfacePrivate() : index(0), flags(nullptr) { } ~QNetworkInterfacePrivate() { } diff --git a/src/network/kernel/qnetworkinterface_unix_p.h b/src/network/kernel/qnetworkinterface_unix_p.h index c085194e3c..553af5a303 100644 --- a/src/network/kernel/qnetworkinterface_unix_p.h +++ b/src/network/kernel/qnetworkinterface_unix_p.h @@ -80,7 +80,7 @@ QT_BEGIN_NAMESPACE static QNetworkInterface::InterfaceFlags convertFlags(uint rawFlags) { - QNetworkInterface::InterfaceFlags flags = 0; + QNetworkInterface::InterfaceFlags flags = nullptr; flags |= (rawFlags & IFF_UP) ? QNetworkInterface::IsUp : QNetworkInterface::InterfaceFlag(0); flags |= (rawFlags & IFF_RUNNING) ? QNetworkInterface::IsRunning : QNetworkInterface::InterfaceFlag(0); flags |= (rawFlags & IFF_BROADCAST) ? QNetworkInterface::CanBroadcast : QNetworkInterface::InterfaceFlag(0); diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp index febddfd880..a2a89ed94b 100644 --- a/src/network/kernel/qnetworkproxy.cpp +++ b/src/network/kernel/qnetworkproxy.cpp @@ -254,8 +254,7 @@ class QGlobalNetworkProxy { public: QGlobalNetworkProxy() - : mutex(QMutex::Recursive) - , applicationLevelProxy(0) + : applicationLevelProxy(0) , applicationLevelProxyFactory(0) #if QT_CONFIG(socks5) , socks5SocketEngineHandler(0) @@ -338,7 +337,7 @@ public: QList<QNetworkProxy> proxyForQuery(const QNetworkProxyQuery &query); private: - QMutex mutex; + QRecursiveMutex mutex; QNetworkProxy *applicationLevelProxy; QNetworkProxyFactory *applicationLevelProxyFactory; #if QT_CONFIG(socks5) @@ -483,7 +482,7 @@ public: template<> void QSharedDataPointer<QNetworkProxyPrivate>::detach() { - if (d && d->ref.load() == 1) + if (d && d->ref.loadRelaxed() == 1) return; QNetworkProxyPrivate *x = (d ? new QNetworkProxyPrivate(*d) : new QNetworkProxyPrivate); @@ -927,7 +926,7 @@ public: template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach() { - if (d && d->ref.load() == 1) + if (d && d->ref.loadRelaxed() == 1) return; QNetworkProxyQueryPrivate *x = (d ? new QNetworkProxyQueryPrivate(*d) : new QNetworkProxyQueryPrivate); diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h index 7e3e6906a8..302a2ce6ca 100644 --- a/src/network/kernel/qnetworkproxy.h +++ b/src/network/kernel/qnetworkproxy.h @@ -89,13 +89,11 @@ public: QueryType queryType = TcpServer); #endif QNetworkProxyQuery(const QNetworkProxyQuery &other); -#ifdef Q_COMPILER_RVALUE_REFS - QNetworkProxyQuery &operator=(QNetworkProxyQuery &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QNetworkProxyQuery &operator=(QNetworkProxyQuery &&other) noexcept { swap(other); return *this; } QNetworkProxyQuery &operator=(const QNetworkProxyQuery &other); ~QNetworkProxyQuery(); - void swap(QNetworkProxyQuery &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QNetworkProxyQuery &other) noexcept { qSwap(d, other.d); } bool operator==(const QNetworkProxyQuery &other) const; inline bool operator!=(const QNetworkProxyQuery &other) const @@ -161,13 +159,11 @@ public: QNetworkProxy(ProxyType type, const QString &hostName = QString(), quint16 port = 0, const QString &user = QString(), const QString &password = QString()); QNetworkProxy(const QNetworkProxy &other); -#ifdef Q_COMPILER_RVALUE_REFS - QNetworkProxy &operator=(QNetworkProxy &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QNetworkProxy &operator=(QNetworkProxy &&other) noexcept { swap(other); return *this; } QNetworkProxy &operator=(const QNetworkProxy &other); ~QNetworkProxy(); - void swap(QNetworkProxy &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QNetworkProxy &other) noexcept { qSwap(d, other.d); } bool operator==(const QNetworkProxy &other) const; inline bool operator!=(const QNetworkProxy &other) const diff --git a/src/network/network.pro b/src/network/network.pro index 9082439f1c..d8453e879c 100644 --- a/src/network/network.pro +++ b/src/network/network.pro @@ -26,7 +26,7 @@ qtConfig(bearermanagement) { ANDROID_BUNDLED_JAR_DEPENDENCIES = \ jar/QtAndroidBearer.jar ANDROID_LIB_DEPENDENCIES = \ - plugins/bearer/libqandroidbearer.so + plugins/bearer/libplugins_bearer_qandroidbearer.so MODULE_PLUGIN_TYPES = \ bearer ANDROID_PERMISSIONS += \ diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index 8eebb06a4d..112e7032d6 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -88,7 +88,7 @@ public: static QAbstractSocketEngine *createSocketEngine(QAbstractSocket::SocketType socketType, const QNetworkProxy &, QObject *parent); static QAbstractSocketEngine *createSocketEngine(qintptr socketDescriptor, QObject *parent); - QAbstractSocketEngine(QObject *parent = 0); + QAbstractSocketEngine(QObject *parent = nullptr); enum SocketOption { NonBlockingSocketOption, @@ -155,7 +155,7 @@ public: virtual qint64 pendingDatagramSize() const = 0; #endif // QT_NO_UDPSOCKET - virtual qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header = 0, + virtual qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header = nullptr, PacketHeaderOptions = WantNone) = 0; virtual qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) = 0; virtual qint64 bytesToWrite() const = 0; @@ -163,11 +163,11 @@ public: virtual int option(SocketOption option) const = 0; virtual bool setOption(SocketOption option, int value) = 0; - virtual bool waitForRead(int msecs = 30000, bool *timedOut = 0) = 0; - virtual bool waitForWrite(int msecs = 30000, bool *timedOut = 0) = 0; + virtual bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) = 0; + virtual bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) = 0; virtual bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, - int msecs = 30000, bool *timedOut = 0) = 0; + int msecs = 30000, bool *timedOut = nullptr) = 0; QAbstractSocket::SocketError error() const; QString errorString() const; @@ -202,7 +202,7 @@ public Q_SLOTS: public: void setReceiver(QAbstractSocketEngineReceiver *receiver); protected: - QAbstractSocketEngine(QAbstractSocketEnginePrivate &dd, QObject* parent = 0); + QAbstractSocketEngine(QAbstractSocketEnginePrivate &dd, QObject* parent = nullptr); void setError(QAbstractSocket::SocketError error, const QString &errorString) const; void setState(QAbstractSocket::SocketState state); diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index bfde3870c4..c67b273937 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -524,7 +524,7 @@ void QHttpSocketEngine::slotSocketConnected() //qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1); if (priv && priv->method != QAuthenticatorPrivate::None) { d->credentialsSent = true; - data += "Proxy-Authorization: " + priv->calculateResponse(method, path); + data += "Proxy-Authorization: " + priv->calculateResponse(method, path, d->proxy.hostName()); data += "\r\n"; } data += "\r\n"; diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h index bbcc09eee9..0c2c450c81 100644 --- a/src/network/socket/qhttpsocketengine_p.h +++ b/src/network/socket/qhttpsocketengine_p.h @@ -79,7 +79,7 @@ public: ReadResponseContent, ReadResponseHeader }; - QHttpSocketEngine(QObject *parent = 0); + QHttpSocketEngine(QObject *parent = nullptr); ~QHttpSocketEngine(); bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) override; @@ -126,11 +126,11 @@ public: int option(SocketOption option) const override; bool setOption(SocketOption option, int value) override; - bool waitForRead(int msecs = 30000, bool *timedOut = 0) override; - bool waitForWrite(int msecs = 30000, bool *timedOut = 0) override; + bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override; + bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override; bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, - int msecs = 30000, bool *timedOut = 0) override; + int msecs = 30000, bool *timedOut = nullptr) override; bool isReadNotificationEnabled() const override; void setReadNotificationEnabled(bool enable) override; diff --git a/src/network/socket/qlocalserver_p.h b/src/network/socket/qlocalserver_p.h index 2c073908cb..92616e59ce 100644 --- a/src/network/socket/qlocalserver_p.h +++ b/src/network/socket/qlocalserver_p.h @@ -78,7 +78,7 @@ class QLocalServerPrivate : public QObjectPrivate public: QLocalServerPrivate() : #if !defined(QT_LOCALSOCKET_TCP) && !defined(Q_OS_WIN) - listenSocket(-1), socketNotifier(0), + listenSocket(-1), socketNotifier(nullptr), #endif maxPendingConnections(30), error(QAbstractSocket::UnknownSocketError), socketOptions(QLocalServer::NoOptions) diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index 2292566265..e5f0701d14 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -125,7 +125,7 @@ class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine { Q_OBJECT public: - QNativeSocketEngine(QObject *parent = 0); + QNativeSocketEngine(QObject *parent = nullptr); ~QNativeSocketEngine(); bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) override; @@ -161,7 +161,7 @@ public: qint64 pendingDatagramSize() const override; #endif // QT_NO_UDPSOCKET - qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0, + qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = nullptr, PacketHeaderOptions = WantNone) override; qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) override; qint64 bytesToWrite() const override; @@ -177,11 +177,11 @@ public: int option(SocketOption option) const override; bool setOption(SocketOption option, int value) override; - bool waitForRead(int msecs = 30000, bool *timedOut = 0) override; - bool waitForWrite(int msecs = 30000, bool *timedOut = 0) override; + bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override; + bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override; bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, - int msecs = 30000, bool *timedOut = 0) override; + int msecs = 30000, bool *timedOut = nullptr) override; bool isReadNotificationEnabled() const override; void setReadNotificationEnabled(bool enable) override; diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 24c17124dc..3ca586e247 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -1000,8 +1000,11 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS // parse the ancillary data struct cmsghdr *cmsgptr; + QT_WARNING_PUSH + QT_WARNING_DISABLE_CLANG("-Wsign-compare") for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { + QT_WARNING_POP if (cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_PKTINFO && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(in6_pktinfo))) { in6_pktinfo *info = reinterpret_cast<in6_pktinfo *>(CMSG_DATA(cmsgptr)); diff --git a/src/network/socket/qsctpserver.cpp b/src/network/socket/qsctpserver.cpp index 77cb997192..2aa694b3fd 100644 --- a/src/network/socket/qsctpserver.cpp +++ b/src/network/socket/qsctpserver.cpp @@ -229,13 +229,12 @@ QSctpSocket *QSctpServer::nextPendingDatagramConnection() { Q_D(QSctpServer); - QMutableListIterator<QTcpSocket *> i(d->pendingConnections); - while (i.hasNext()) { - QSctpSocket *socket = qobject_cast<QSctpSocket *>(i.next()); + for (auto it = d->pendingConnections.begin(), end = d->pendingConnections.end(); it != end; ++it) { + QSctpSocket *socket = qobject_cast<QSctpSocket *>(*it); Q_ASSERT(socket); if (socket->isInDatagramMode()) { - i.remove(); + d->pendingConnections.erase(it); Q_ASSERT(d->socketEngine); d->socketEngine->setReadNotificationEnabled(true); return socket; diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 18b3ce9db2..8a030601dc 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -322,8 +322,8 @@ public: protected: void timerEvent(QTimerEvent * event) override; - QMutex mutex; - int sweepTimerId; + QRecursiveMutex mutex; + int sweepTimerId = -1; //socket descriptor, data, timestamp QHash<int, QSocks5BindData *> store; }; @@ -331,8 +331,6 @@ protected: Q_GLOBAL_STATIC(QSocks5BindStore, socks5BindStore) QSocks5BindStore::QSocks5BindStore() - : mutex(QMutex::Recursive) - , sweepTimerId(-1) { QCoreApplication *app = QCoreApplication::instance(); if (app && app->thread() != thread()) @@ -801,9 +799,9 @@ void QSocks5SocketEnginePrivate::sendRequestMethod() QByteArray buf; buf.reserve(270); // big enough for domain name; - buf[0] = S5_VERSION_5; - buf[1] = command; - buf[2] = 0x00; + buf.append(char(S5_VERSION_5)); + buf.append(command); + buf.append('\0'); if (peerName.isEmpty() && !qt_socks5_set_host_address_and_port(address, port, &buf)) { QSOCKS5_DEBUG << "error setting address" << address << " : " << port; //### set error code .... diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h index ef9d771753..c256987e2d 100644 --- a/src/network/socket/qsocks5socketengine_p.h +++ b/src/network/socket/qsocks5socketengine_p.h @@ -65,7 +65,7 @@ class Q_AUTOTEST_EXPORT QSocks5SocketEngine : public QAbstractSocketEngine { Q_OBJECT public: - QSocks5SocketEngine(QObject *parent = 0); + QSocks5SocketEngine(QObject *parent = nullptr); ~QSocks5SocketEngine(); bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) override; @@ -104,7 +104,7 @@ public: qint64 pendingDatagramSize() const override; #endif // QT_NO_UDPSOCKET - qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0, + qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = nullptr, PacketHeaderOptions = WantNone) override; qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) override; qint64 bytesToWrite() const override; @@ -112,11 +112,11 @@ public: int option(SocketOption option) const override; bool setOption(SocketOption option, int value) override; - bool waitForRead(int msecs = 30000, bool *timedOut = 0) override; - bool waitForWrite(int msecs = 30000, bool *timedOut = 0) override; + bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override; + bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override; bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, - int msecs = 30000, bool *timedOut = 0) override; + int msecs = 30000, bool *timedOut = nullptr) override; bool isReadNotificationEnabled() const override; void setReadNotificationEnabled(bool enable) override; diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri index 44ff5b7b39..c3a98ea31a 100644 --- a/src/network/socket/socket.pri +++ b/src/network/socket/socket.pri @@ -58,7 +58,7 @@ unix { msvc: QMAKE_MOC_OPTIONS += -D_WINSOCK_DEPRECATED_NO_WARNINGS win32:!winrt:SOURCES += socket/qnativesocketengine_win.cpp -win32:!winrt:LIBS_PRIVATE += -ladvapi32 +win32:!winrt: QMAKE_USE_PRIVATE += advapi32 winrt { SOURCES += socket/qnativesocketengine_winrt.cpp diff --git a/src/network/ssl/qasn1element_p.h b/src/network/ssl/qasn1element_p.h index 22948e3ca5..020b5aa1af 100644 --- a/src/network/ssl/qasn1element_p.h +++ b/src/network/ssl/qasn1element_p.h @@ -156,10 +156,10 @@ public: static QAsn1Element fromVector(const QVector<QAsn1Element> &items); static QAsn1Element fromObjectId(const QByteArray &id); - bool toBool(bool *ok = 0) const; + bool toBool(bool *ok = nullptr) const; QDateTime toDateTime() const; QMultiMap<QByteArray, QString> toInfo() const; - qint64 toInteger(bool *ok = 0) const; + qint64 toInteger(bool *ok = nullptr) const; QVector<QAsn1Element> toVector() const; QByteArray toObjectId() const; QByteArray toObjectName() const; diff --git a/src/network/ssl/qdtls.cpp b/src/network/ssl/qdtls.cpp index 3185bfa124..a2280a7d10 100644 --- a/src/network/ssl/qdtls.cpp +++ b/src/network/ssl/qdtls.cpp @@ -342,7 +342,7 @@ QT_BEGIN_NAMESPACE QSslConfiguration QDtlsBasePrivate::configuration() const { auto copyPrivate = new QSslConfigurationPrivate(dtlsConfiguration); - copyPrivate->ref.store(0); // the QSslConfiguration constructor refs up + copyPrivate->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up QSslConfiguration copy(copyPrivate); copyPrivate->sessionCipher = sessionCipher; copyPrivate->sessionProtocol = sessionProtocol; diff --git a/src/network/ssl/qdtls_openssl.cpp b/src/network/ssl/qdtls_openssl.cpp index 8be53df24f..d9ddcceb40 100644 --- a/src/network/ssl/qdtls_openssl.cpp +++ b/src/network/ssl/qdtls_openssl.cpp @@ -729,7 +729,7 @@ bool DtlsState::initCtxAndConnection(QDtlsBasePrivate *dtlsBase) // Create a deep copy of our configuration auto configurationCopy = new QSslConfigurationPrivate(dtlsBase->dtlsConfiguration); - configurationCopy->ref.store(0); // the QSslConfiguration constructor refs up + configurationCopy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up // DTLSTODO: check we do not set something DTLS-incompatible there ... TlsContext newContext(QSslContext::sharedFromConfiguration(dtlsBase->mode, diff --git a/src/network/ssl/qocspresponse.cpp b/src/network/ssl/qocspresponse.cpp index d564e817ca..bf27bb768b 100644 --- a/src/network/ssl/qocspresponse.cpp +++ b/src/network/ssl/qocspresponse.cpp @@ -133,7 +133,7 @@ QOcspResponse::QOcspResponse(const QOcspResponse &) = default; Move-constructs a QOcspResponse instance. */ -QOcspResponse::QOcspResponse(QOcspResponse &&) Q_DECL_NOTHROW = default; +QOcspResponse::QOcspResponse(QOcspResponse &&) noexcept = default; /*! \since 5.13 @@ -154,7 +154,7 @@ QOcspResponse &QOcspResponse::operator=(const QOcspResponse &) = default; Move-assigns to this QOcspResponse instance. */ -QOcspResponse &QOcspResponse::operator=(QOcspResponse &&) Q_DECL_NOTHROW = default; +QOcspResponse &QOcspResponse::operator=(QOcspResponse &&) noexcept = default; /*! \fn void QOcspResponse::swap(QOcspResponse &other) @@ -239,7 +239,7 @@ Q_NETWORK_EXPORT bool operator==(const QOcspResponse &lhs, const QOcspResponse & \since 5.13 \relates QHash */ -uint qHash(const QOcspResponse &response, uint seed) +uint qHash(const QOcspResponse &response, uint seed) noexcept { const QOcspResponsePrivate *d = response.d.data(); Q_ASSERT(d); diff --git a/src/network/ssl/qocspresponse.h b/src/network/ssl/qocspresponse.h index 552a088ba5..cf6be5a369 100644 --- a/src/network/ssl/qocspresponse.h +++ b/src/network/ssl/qocspresponse.h @@ -73,7 +73,7 @@ enum class QOcspRevocationReason }; class QOcspResponse; -Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed = 0); +Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed = 0) noexcept; class QOcspResponsePrivate; class Q_NETWORK_EXPORT QOcspResponse @@ -82,11 +82,11 @@ public: QOcspResponse(); QOcspResponse(const QOcspResponse &other); - QOcspResponse(QOcspResponse && other) Q_DECL_NOEXCEPT; + QOcspResponse(QOcspResponse && other) noexcept; ~QOcspResponse(); QOcspResponse &operator = (const QOcspResponse &other); - QOcspResponse &operator = (QOcspResponse &&other) Q_DECL_NOTHROW; + QOcspResponse &operator = (QOcspResponse &&other) noexcept; QOcspCertificateStatus certificateStatus() const; QOcspRevocationReason revocationReason() const; @@ -94,13 +94,13 @@ public: class QSslCertificate responder() const; QSslCertificate subject() const; - void swap(QOcspResponse &other) Q_DECL_NOTHROW { d.swap(other.d); } + void swap(QOcspResponse &other) noexcept { d.swap(other.d); } private: friend class QSslSocketBackendPrivate; friend Q_NETWORK_EXPORT bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs); - friend Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed); + friend Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed) noexcept; QSharedDataPointer<QOcspResponsePrivate> d; }; diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h index 266fcdacb4..69901b526c 100644 --- a/src/network/ssl/qsslcertificate.h +++ b/src/network/ssl/qsslcertificate.h @@ -66,7 +66,7 @@ class QStringList; class QSslCertificate; // qHash is a friend, but we can't use default arguments for friends (§8.3.6.4) -Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed = 0) Q_DECL_NOTHROW; +Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed = 0) noexcept; class QSslCertificatePrivate; class Q_NETWORK_EXPORT QSslCertificate @@ -88,12 +88,10 @@ public: explicit QSslCertificate(const QByteArray &data = QByteArray(), QSsl::EncodingFormat format = QSsl::Pem); QSslCertificate(const QSslCertificate &other); ~QSslCertificate(); -#ifdef Q_COMPILER_RVALUE_REFS - QSslCertificate &operator=(QSslCertificate &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QSslCertificate &operator=(QSslCertificate &&other) noexcept { swap(other); return *this; } QSslCertificate &operator=(const QSslCertificate &other); - void swap(QSslCertificate &other) Q_DECL_NOTHROW + void swap(QSslCertificate &other) noexcept { qSwap(d, other.d); } bool operator==(const QSslCertificate &other) const; @@ -169,7 +167,7 @@ private: friend class QSslCertificatePrivate; friend class QSslSocketBackendPrivate; - friend Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed) Q_DECL_NOTHROW; + friend Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed) noexcept; }; Q_DECLARE_SHARED(QSslCertificate) diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp index 899c8a0d2d..6f1fb26add 100644 --- a/src/network/ssl/qsslcertificate_openssl.cpp +++ b/src/network/ssl/qsslcertificate_openssl.cpp @@ -45,12 +45,19 @@ #include "qsslcertificateextension_p.h" #include <QtCore/qendian.h> +#include <QtCore/qmutex.h> -#if QT_CONFIG(thread) -#include <QtCore/private/qmutexpool_p.h> -#endif QT_BEGIN_NAMESPACE +Q_CONSTEXPR int MutexPoolSize = 17; +static QBasicMutex mutexPool[MutexPoolSize]; +namespace QMutexPool { + static QBasicMutex *globalInstanceGet(const void *addr) + { + return mutexPool + (quintptr(addr) % MutexPoolSize); + } +} + // forward declaration static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name); @@ -65,7 +72,7 @@ bool QSslCertificate::operator==(const QSslCertificate &other) const return false; } -uint qHash(const QSslCertificate &key, uint seed) Q_DECL_NOTHROW +uint qHash(const QSslCertificate &key, uint seed) noexcept { if (X509 * const x509 = key.d->x509) { const EVP_MD *sha1 = q_EVP_sha1(); diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h index 4b331d4c4e..234cd45ceb 100644 --- a/src/network/ssl/qsslcertificate_p.h +++ b/src/network/ssl/qsslcertificate_p.h @@ -87,7 +87,7 @@ class QSslCertificatePrivate { public: QSslCertificatePrivate() - : null(true), x509(0) + : null(true), x509(nullptr) { #ifndef QT_NO_SSL QSslSocketPrivate::ensureInitialized(); diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp index cce59b5ef3..8b5035ad96 100644 --- a/src/network/ssl/qsslcertificate_qt.cpp +++ b/src/network/ssl/qsslcertificate_qt.cpp @@ -64,7 +64,7 @@ bool QSslCertificate::operator==(const QSslCertificate &other) const return d->derData == other.d->derData; } -uint qHash(const QSslCertificate &key, uint seed) Q_DECL_NOTHROW +uint qHash(const QSslCertificate &key, uint seed) noexcept { // DER is the native encoding here, so toDer() is just "return d->derData": return qHash(key.toDer(), seed); diff --git a/src/network/ssl/qsslcertificateextension.h b/src/network/ssl/qsslcertificateextension.h index c2910e1707..7cc8a888be 100644 --- a/src/network/ssl/qsslcertificateextension.h +++ b/src/network/ssl/qsslcertificateextension.h @@ -55,13 +55,11 @@ class Q_NETWORK_EXPORT QSslCertificateExtension public: QSslCertificateExtension(); QSslCertificateExtension(const QSslCertificateExtension &other); -#ifdef Q_COMPILER_RVALUE_REFS - QSslCertificateExtension &operator=(QSslCertificateExtension &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QSslCertificateExtension &operator=(QSslCertificateExtension &&other) noexcept { swap(other); return *this; } QSslCertificateExtension &operator=(const QSslCertificateExtension &other); ~QSslCertificateExtension(); - void swap(QSslCertificateExtension &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QSslCertificateExtension &other) noexcept { qSwap(d, other.d); } QString oid() const; QString name() const; diff --git a/src/network/ssl/qsslcipher.h b/src/network/ssl/qsslcipher.h index c6328e0169..6994f590ae 100644 --- a/src/network/ssl/qsslcipher.h +++ b/src/network/ssl/qsslcipher.h @@ -59,13 +59,11 @@ public: explicit QSslCipher(const QString &name); QSslCipher(const QString &name, QSsl::SslProtocol protocol); QSslCipher(const QSslCipher &other); -#ifdef Q_COMPILER_RVALUE_REFS - QSslCipher &operator=(QSslCipher &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QSslCipher &operator=(QSslCipher &&other) noexcept { swap(other); return *this; } QSslCipher &operator=(const QSslCipher &other); ~QSslCipher(); - void swap(QSslCipher &other) Q_DECL_NOTHROW + void swap(QSslCipher &other) noexcept { qSwap(d, other.d); } bool operator==(const QSslCipher &other) const; diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index 8f53e25a53..c25c2686de 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -85,12 +85,10 @@ public: QSslConfiguration(); QSslConfiguration(const QSslConfiguration &other); ~QSslConfiguration(); -#ifdef Q_COMPILER_RVALUE_REFS - QSslConfiguration &operator=(QSslConfiguration &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QSslConfiguration &operator=(QSslConfiguration &&other) noexcept { swap(other); return *this; } QSslConfiguration &operator=(const QSslConfiguration &other); - void swap(QSslConfiguration &other) Q_DECL_NOTHROW + void swap(QSslConfiguration &other) noexcept { qSwap(d, other.d); } bool operator==(const QSslConfiguration &other) const; diff --git a/src/network/ssl/qsslcontext_openssl11.cpp b/src/network/ssl/qsslcontext_openssl11.cpp index 21a5c779f7..db023b7331 100644 --- a/src/network/ssl/qsslcontext_openssl11.cpp +++ b/src/network/ssl/qsslcontext_openssl11.cpp @@ -193,7 +193,6 @@ init_context: minVersion = TLS1_2_VERSION; maxVersion = 0; break; -#if QT_CONFIG(dtls) case QSsl::DtlsV1_0: minVersion = DTLS1_VERSION; maxVersion = DTLS1_VERSION; @@ -210,7 +209,6 @@ init_context: minVersion = DTLS1_2_VERSION; maxVersion = DTLS_MAX_VERSION; break; -#endif // dtls case QSsl::TlsV1_3OrLater: #ifdef TLS1_3_VERSION minVersion = TLS1_3_VERSION; diff --git a/src/network/ssl/qsslcontext_openssl_p.h b/src/network/ssl/qsslcontext_openssl_p.h index 48beebf134..1fa27279c7 100644 --- a/src/network/ssl/qsslcontext_openssl_p.h +++ b/src/network/ssl/qsslcontext_openssl_p.h @@ -89,7 +89,7 @@ public: #if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) // must be public because we want to use it from an OpenSSL callback struct NPNContext { - NPNContext() : data(0), + NPNContext() : data(nullptr), len(0), status(QSslConfiguration::NextProtocolNegotiationNone) { } diff --git a/src/network/ssl/qssldiffiehellmanparameters.cpp b/src/network/ssl/qssldiffiehellmanparameters.cpp index 65041d4456..7807afaa30 100644 --- a/src/network/ssl/qssldiffiehellmanparameters.cpp +++ b/src/network/ssl/qssldiffiehellmanparameters.cpp @@ -213,7 +213,7 @@ QSslDiffieHellmanParameters &QSslDiffieHellmanParameters::operator=(const QSslDi Setting an empty QSslDiffieHellmanParameters instance on a QSslSocket-based server will disable Diffie-Hellman key exchange. */ -bool QSslDiffieHellmanParameters::isEmpty() const Q_DECL_NOTHROW +bool QSslDiffieHellmanParameters::isEmpty() const noexcept { return d->derData.isNull() && d->error == QSslDiffieHellmanParameters::NoError; } @@ -229,7 +229,7 @@ bool QSslDiffieHellmanParameters::isEmpty() const Q_DECL_NOTHROW \sa error() */ -bool QSslDiffieHellmanParameters::isValid() const Q_DECL_NOTHROW +bool QSslDiffieHellmanParameters::isValid() const noexcept { return d->error == QSslDiffieHellmanParameters::NoError; } @@ -253,7 +253,7 @@ bool QSslDiffieHellmanParameters::isValid() const Q_DECL_NOTHROW Returns the error that caused the QSslDiffieHellmanParameters object to be invalid. */ -QSslDiffieHellmanParameters::Error QSslDiffieHellmanParameters::error() const Q_DECL_NOTHROW +QSslDiffieHellmanParameters::Error QSslDiffieHellmanParameters::error() const noexcept { return d->error; } @@ -262,7 +262,7 @@ QSslDiffieHellmanParameters::Error QSslDiffieHellmanParameters::error() const Q_ Returns a human-readable description of the error that caused the QSslDiffieHellmanParameters object to be invalid. */ -QString QSslDiffieHellmanParameters::errorString() const Q_DECL_NOTHROW +QString QSslDiffieHellmanParameters::errorString() const noexcept { switch (d->error) { case QSslDiffieHellmanParameters::NoError: @@ -283,7 +283,7 @@ QString QSslDiffieHellmanParameters::errorString() const Q_DECL_NOTHROW Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false. */ -bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW +bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept { return lhs.d->derData == rhs.d->derData; } @@ -316,7 +316,7 @@ QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam) Returns an hash value for \a dhparam, using \a seed to seed the calculation. */ -uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) Q_DECL_NOTHROW +uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) noexcept { return qHash(dhparam.d->derData, seed); } diff --git a/src/network/ssl/qssldiffiehellmanparameters.h b/src/network/ssl/qssldiffiehellmanparameters.h index 497d2bebfb..f62a3b8f44 100644 --- a/src/network/ssl/qssldiffiehellmanparameters.h +++ b/src/network/ssl/qssldiffiehellmanparameters.h @@ -56,16 +56,16 @@ class QSslDiffieHellmanParametersPrivate; class QSslDiffieHellmanParameters; // qHash is a friend, but we can't use default arguments for friends (§8.3.6.4) -Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed = 0) Q_DECL_NOTHROW; +Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed = 0) noexcept; #ifndef QT_NO_DEBUG_STREAM class QDebug; Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparams); #endif -Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW; +Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept; -inline bool operator!=(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW +inline bool operator!=(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept { return !operator==(lhs, rhs); } @@ -83,30 +83,30 @@ public: Q_NETWORK_EXPORT QSslDiffieHellmanParameters(); Q_NETWORK_EXPORT QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other); - QSslDiffieHellmanParameters(QSslDiffieHellmanParameters &&other) Q_DECL_NOTHROW : d(other.d) { other.d = nullptr; } + QSslDiffieHellmanParameters(QSslDiffieHellmanParameters &&other) noexcept : d(other.d) { other.d = nullptr; } Q_NETWORK_EXPORT ~QSslDiffieHellmanParameters(); Q_NETWORK_EXPORT QSslDiffieHellmanParameters &operator=(const QSslDiffieHellmanParameters &other); - QSslDiffieHellmanParameters &operator=(QSslDiffieHellmanParameters &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QSslDiffieHellmanParameters &operator=(QSslDiffieHellmanParameters &&other) noexcept { swap(other); return *this; } - void swap(QSslDiffieHellmanParameters &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QSslDiffieHellmanParameters &other) noexcept { qSwap(d, other.d); } Q_NETWORK_EXPORT static QSslDiffieHellmanParameters fromEncoded(const QByteArray &encoded, QSsl::EncodingFormat format = QSsl::Pem); Q_NETWORK_EXPORT static QSslDiffieHellmanParameters fromEncoded(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem); - Q_NETWORK_EXPORT bool isEmpty() const Q_DECL_NOTHROW; - Q_NETWORK_EXPORT bool isValid() const Q_DECL_NOTHROW; - Q_NETWORK_EXPORT Error error() const Q_DECL_NOTHROW; - Q_NETWORK_EXPORT QString errorString() const Q_DECL_NOTHROW; + Q_NETWORK_EXPORT bool isEmpty() const noexcept; + Q_NETWORK_EXPORT bool isValid() const noexcept; + Q_NETWORK_EXPORT Error error() const noexcept; + Q_NETWORK_EXPORT QString errorString() const noexcept; private: QSslDiffieHellmanParametersPrivate *d; friend class QSslContext; - friend Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW; + friend Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept; #ifndef QT_NO_DEBUG_STREAM friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam); #endif - friend Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) Q_DECL_NOTHROW; + friend Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) noexcept; }; Q_DECLARE_SHARED(QSslDiffieHellmanParameters) diff --git a/src/network/ssl/qsslellipticcurve.h b/src/network/ssl/qsslellipticcurve.h index 57dda19bad..28de3a03b4 100644 --- a/src/network/ssl/qsslellipticcurve.h +++ b/src/network/ssl/qsslellipticcurve.h @@ -52,11 +52,11 @@ QT_BEGIN_NAMESPACE class QSslEllipticCurve; // qHash is a friend, but we can't use default arguments for friends (§8.3.6.4) -Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed = 0) Q_DECL_NOTHROW; +Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed = 0) noexcept; class QSslEllipticCurve { public: - Q_DECL_CONSTEXPR QSslEllipticCurve() Q_DECL_NOTHROW + Q_DECL_CONSTEXPR QSslEllipticCurve() noexcept : id(0) { } @@ -67,18 +67,18 @@ public: Q_REQUIRED_RESULT Q_NETWORK_EXPORT QString shortName() const; Q_REQUIRED_RESULT Q_NETWORK_EXPORT QString longName() const; - Q_DECL_CONSTEXPR bool isValid() const Q_DECL_NOTHROW + Q_DECL_CONSTEXPR bool isValid() const noexcept { return id != 0; } - Q_NETWORK_EXPORT bool isTlsNamedCurve() const Q_DECL_NOTHROW; + Q_NETWORK_EXPORT bool isTlsNamedCurve() const noexcept; private: int id; - friend Q_DECL_CONSTEXPR bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) Q_DECL_NOTHROW; - friend Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed) Q_DECL_NOTHROW; + friend Q_DECL_CONSTEXPR bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept; + friend Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed) noexcept; friend class QSslContext; friend class QSslSocketPrivate; @@ -87,13 +87,13 @@ private: Q_DECLARE_TYPEINFO(QSslEllipticCurve, Q_PRIMITIVE_TYPE); -Q_DECL_CONSTEXPR inline uint qHash(QSslEllipticCurve curve, uint seed) Q_DECL_NOTHROW +Q_DECL_CONSTEXPR inline uint qHash(QSslEllipticCurve curve, uint seed) noexcept { return qHash(curve.id, seed); } -Q_DECL_CONSTEXPR inline bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) Q_DECL_NOTHROW +Q_DECL_CONSTEXPR inline bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept { return lhs.id == rhs.id; } -Q_DECL_CONSTEXPR inline bool operator!=(QSslEllipticCurve lhs, QSslEllipticCurve rhs) Q_DECL_NOTHROW +Q_DECL_CONSTEXPR inline bool operator!=(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept { return !operator==(lhs, rhs); } #ifndef QT_NO_DEBUG_STREAM diff --git a/src/network/ssl/qsslellipticcurve_dummy.cpp b/src/network/ssl/qsslellipticcurve_dummy.cpp index 93e081b9e0..1313e06875 100644 --- a/src/network/ssl/qsslellipticcurve_dummy.cpp +++ b/src/network/ssl/qsslellipticcurve_dummy.cpp @@ -63,7 +63,7 @@ QSslEllipticCurve QSslEllipticCurve::fromLongName(const QString &name) return QSslEllipticCurve(); } -bool QSslEllipticCurve::isTlsNamedCurve() const Q_DECL_NOTHROW +bool QSslEllipticCurve::isTlsNamedCurve() const noexcept { return false; } diff --git a/src/network/ssl/qsslellipticcurve_openssl.cpp b/src/network/ssl/qsslellipticcurve_openssl.cpp index 8cd14837f0..b5e38ada53 100644 --- a/src/network/ssl/qsslellipticcurve_openssl.cpp +++ b/src/network/ssl/qsslellipticcurve_openssl.cpp @@ -170,7 +170,7 @@ static const int tlsNamedCurveNIDs[] = { static const size_t tlsNamedCurveNIDCount = sizeof(tlsNamedCurveNIDs) / sizeof(tlsNamedCurveNIDs[0]); -bool QSslEllipticCurve::isTlsNamedCurve() const Q_DECL_NOTHROW +bool QSslEllipticCurve::isTlsNamedCurve() const noexcept { const int * const tlsNamedCurveNIDsEnd = tlsNamedCurveNIDs + tlsNamedCurveNIDCount; return std::find(tlsNamedCurveNIDs, tlsNamedCurveNIDsEnd, id) != tlsNamedCurveNIDsEnd; diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp index 02dd16a58d..cdc018a508 100644 --- a/src/network/ssl/qsslerror.cpp +++ b/src/network/ssl/qsslerror.cpp @@ -86,6 +86,7 @@ \value UnspecifiedError \value NoSslSupport \value CertificateBlacklisted + \value CertificateStatusUnknown \value OcspNoResponseFound \value OcspMalformedRequest \value OcspMalformedResponse @@ -361,11 +362,11 @@ QSslCertificate QSslError::certificate() const \since 5.4 \relates QHash */ -uint qHash(const QSslError &key, uint seed) Q_DECL_NOTHROW +uint qHash(const QSslError &key, uint seed) noexcept { - // 2x boost::hash_combine inlined: - seed ^= qHash(key.error()) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - seed ^= qHash(key.certificate()) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + QtPrivate::QHashCombine hash; + seed = hash(seed, key.error()); + seed = hash(seed, key.certificate()); return seed; } diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h index 513b8afd7f..28eb1a9ea8 100644 --- a/src/network/ssl/qsslerror.h +++ b/src/network/ssl/qsslerror.h @@ -53,6 +53,7 @@ QT_BEGIN_NAMESPACE class QSslErrorPrivate; class Q_NETWORK_EXPORT QSslError { + Q_GADGET public: enum SslError { NoError, @@ -94,6 +95,7 @@ public: OcspStatusUnknown, UnspecifiedError = -1 }; + Q_ENUM(SslError) // RVCT compiler in debug build does not like about default values in const- // So as an workaround we define all constructor overloads here explicitly @@ -103,13 +105,11 @@ public: QSslError(const QSslError &other); - void swap(QSslError &other) Q_DECL_NOTHROW + void swap(QSslError &other) noexcept { qSwap(d, other.d); } ~QSslError(); -#ifdef Q_COMPILER_RVALUE_REFS - QSslError &operator=(QSslError &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QSslError &operator=(QSslError &&other) noexcept { swap(other); return *this; } QSslError &operator=(const QSslError &other); bool operator==(const QSslError &other) const; inline bool operator!=(const QSslError &other) const @@ -124,7 +124,7 @@ private: }; Q_DECLARE_SHARED(QSslError) -Q_NETWORK_EXPORT uint qHash(const QSslError &key, uint seed = 0) Q_DECL_NOTHROW; +Q_NETWORK_EXPORT uint qHash(const QSslError &key, uint seed = 0) noexcept; #ifndef QT_NO_DEBUG_STREAM class QDebug; diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h index 6de02b1e44..74be406539 100644 --- a/src/network/ssl/qsslkey.h +++ b/src/network/ssl/qsslkey.h @@ -71,13 +71,12 @@ public: const QByteArray &passPhrase = QByteArray()); explicit QSslKey(Qt::HANDLE handle, QSsl::KeyType type = QSsl::PrivateKey); QSslKey(const QSslKey &other); -#ifdef Q_COMPILER_RVALUE_REFS - QSslKey &operator=(QSslKey &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QSslKey(QSslKey &&other) noexcept; + QSslKey &operator=(QSslKey &&other) noexcept; QSslKey &operator=(const QSslKey &other); ~QSslKey(); - void swap(QSslKey &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QSslKey &other) noexcept { qSwap(d, other.d); } bool isNull() const; void clear(); diff --git a/src/network/ssl/qsslkey_mac.cpp b/src/network/ssl/qsslkey_mac.cpp index d460cbfdab..814fe1c4bc 100644 --- a/src/network/ssl/qsslkey_mac.cpp +++ b/src/network/ssl/qsslkey_mac.cpp @@ -42,7 +42,9 @@ #include <CommonCrypto/CommonCrypto.h> -QT_USE_NAMESPACE +#include <cstddef> + +QT_BEGIN_NAMESPACE static QByteArray wrapCCCrypt(CCOperation ccOp, QSslKeyPrivate::Cipher cipher, @@ -64,17 +66,23 @@ static QByteArray wrapCCCrypt(CCOperation ccOp, blockSize = kCCBlockSizeRC2; ccAlgorithm = kCCAlgorithmRC2; break; - }; + case QSslKeyPrivate::Aes128Cbc: + case QSslKeyPrivate::Aes192Cbc: + case QSslKeyPrivate::Aes256Cbc: + blockSize = kCCBlockSizeAES128; + ccAlgorithm = kCCAlgorithmAES; + break; + } size_t plainLength = 0; QByteArray plain(data.size() + blockSize, 0); CCCryptorStatus status = CCCrypt( ccOp, ccAlgorithm, kCCOptionPKCS7Padding, - key.constData(), key.size(), + key.constData(), std::size_t(key.size()), iv.constData(), - data.constData(), data.size(), - plain.data(), plain.size(), &plainLength); + data.constData(), std::size_t(data.size()), + plain.data(), std::size_t(plain.size()), &plainLength); if (status == kCCSuccess) - return plain.left(plainLength); + return plain.left(int(plainLength)); return QByteArray(); } @@ -87,3 +95,5 @@ QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const { return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv); } + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp index 99c1a39c73..888058df22 100644 --- a/src/network/ssl/qsslkey_openssl.cpp +++ b/src/network/ssl/qsslkey_openssl.cpp @@ -333,6 +333,15 @@ static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, type = q_EVP_rc2_cbc(); #endif break; + case QSslKeyPrivate::Aes128Cbc: + type = q_EVP_aes_128_cbc(); + break; + case QSslKeyPrivate::Aes192Cbc: + type = q_EVP_aes_192_cbc(); + break; + case QSslKeyPrivate::Aes256Cbc: + type = q_EVP_aes_256_cbc(); + break; } if (type == nullptr) diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp index 5c90719fcd..b0d6c729f9 100644 --- a/src/network/ssl/qsslkey_p.cpp +++ b/src/network/ssl/qsslkey_p.cpp @@ -385,6 +385,24 @@ QSslKey::QSslKey(const QSslKey &other) : d(other.d) { } +QSslKey::QSslKey(QSslKey &&other) noexcept + : d(nullptr) +{ + qSwap(d, other.d); +} + +QSslKey &QSslKey::operator=(QSslKey &&other) noexcept +{ + if (this == &other) + return *this; + + // If no one else is referencing the key data we want to make sure + // before we swap the d-ptr that it is not left in memory. + d.reset(); + qSwap(d, other.d); + return *this; +} + /*! Destroys the QSslKey object. */ diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h index 06403b5479..dd1a31b0e5 100644 --- a/src/network/ssl/qsslkey_p.h +++ b/src/network/ssl/qsslkey_p.h @@ -68,7 +68,7 @@ class QSslKeyPrivate public: inline QSslKeyPrivate() : algorithm(QSsl::Opaque) - , opaque(0) + , opaque(nullptr) { clear(false); } @@ -105,7 +105,10 @@ public: enum Cipher { DesCbc, DesEde3Cbc, - Rc2Cbc + Rc2Cbc, + Aes128Cbc, + Aes192Cbc, + Aes256Cbc }; Q_AUTOTEST_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv); diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp index 5ebd8ac3bd..43969c3d28 100644 --- a/src/network/ssl/qsslkey_qt.cpp +++ b/src/network/ssl/qsslkey_qt.cpp @@ -48,6 +48,8 @@ #include <QtNetwork/qpassworddigestor.h> +#include <cstring> + QT_USE_NAMESPACE static const quint8 bits_table[256] = { @@ -124,6 +126,37 @@ static int numberOfBits(const QByteArray &modulus) return bits; } +static QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv) +{ + // This is somewhat simplified and shortened version of what OpenSSL does. + // See, for example, EVP_BytesToKey for the "algorithm" itself and elsewhere + // in their code for what they pass as arguments to EVP_BytesToKey when + // deriving encryption keys (when reading/writing pems files with encrypted + // keys). + + Q_ASSERT(iv.size() >= 8); + + QCryptographicHash hash(QCryptographicHash::Md5); + + QByteArray data(passPhrase); + data.append(iv.data(), 8); // AKA PKCS5_SALT_LEN in OpenSSL. + + hash.addData(data); + + if (cipher == QSslKeyPrivate::Aes128Cbc) + return hash.result(); + + QByteArray key(hash.result()); + hash.reset(); + hash.addData(key); + hash.addData(data); + + if (cipher == QSslKeyPrivate::Aes192Cbc) + return key.append(hash.result().constData(), 8); + + return key.append(hash.result()); +} + static QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv) { QByteArray key; @@ -145,14 +178,19 @@ static QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &pas case QSslKeyPrivate::Rc2Cbc: key = hash.result(); break; + case QSslKeyPrivate::Aes128Cbc: + case QSslKeyPrivate::Aes192Cbc: + case QSslKeyPrivate::Aes256Cbc: + return deriveAesKey(cipher, passPhrase, iv); } return key; } void QSslKeyPrivate::clear(bool deep) { - Q_UNUSED(deep); isNull = true; + if (deep) + std::memset(derData.data(), 0, derData.size()); derData.clear(); keyLength = -1; } @@ -378,6 +416,12 @@ void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhra cipher = DesEde3Cbc; } else if (dekInfo.first() == "RC2-CBC") { cipher = Rc2Cbc; + } else if (dekInfo.first() == "AES-128-CBC") { + cipher = Aes128Cbc; + } else if (dekInfo.first() == "AES-192-CBC") { + cipher = Aes192Cbc; + } else if (dekInfo.first() == "AES-256-CBC") { + cipher = Aes256Cbc; } else { clear(deepClear); return; @@ -554,6 +598,10 @@ static EncryptionData readPbes2(const QVector<QAsn1Element> &element, const QByt return {}; break; } // @todo(?): case (RC5 , AES) + case QSslKeyPrivate::Cipher::Aes128Cbc: + case QSslKeyPrivate::Cipher::Aes192Cbc: + case QSslKeyPrivate::Cipher::Aes256Cbc: + Q_UNREACHABLE(); } if (Q_LIKELY(keyDerivationAlgorithm == PKCS5_PBKDF2_ENCRYPTION_OID)) { diff --git a/src/network/ssl/qsslkey_schannel.cpp b/src/network/ssl/qsslkey_schannel.cpp index 5694068860..1e21d123f4 100644 --- a/src/network/ssl/qsslkey_schannel.cpp +++ b/src/network/ssl/qsslkey_schannel.cpp @@ -57,6 +57,10 @@ const wchar_t *getName(QSslKeyPrivate::Cipher cipher) return BCRYPT_3DES_ALGORITHM; case QSslKeyPrivate::Cipher::Rc2Cbc: return BCRYPT_RC2_ALGORITHM; + case QSslKeyPrivate::Cipher::Aes128Cbc: + case QSslKeyPrivate::Cipher::Aes192Cbc: + case QSslKeyPrivate::Cipher::Aes256Cbc: + return BCRYPT_AES_ALGORITHM; } Q_UNREACHABLE(); } diff --git a/src/network/ssl/qsslkey_winrt.cpp b/src/network/ssl/qsslkey_winrt.cpp index f2ed813965..69eaaa387f 100644 --- a/src/network/ssl/qsslkey_winrt.cpp +++ b/src/network/ssl/qsslkey_winrt.cpp @@ -83,6 +83,15 @@ struct SslKeyGlobal hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"RC2_CBC").Get(), &keyProviders[QSslKeyPrivate::Rc2Cbc]); Q_ASSERT_SUCCEEDED(hr); + hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"AES_CBC").Get(), + &keyProviders[QSslKeyPrivate::Aes128Cbc]); + Q_ASSERT_SUCCEEDED(hr); + hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"AES_CBC").Get(), + &keyProviders[QSslKeyPrivate::Aes192Cbc]); + Q_ASSERT_SUCCEEDED(hr); + hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"AES_CBC").Get(), + &keyProviders[QSslKeyPrivate::Aes256Cbc]); + Q_ASSERT_SUCCEEDED(hr); hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(), &bufferFactory); diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.h b/src/network/ssl/qsslpresharedkeyauthenticator.h index 423f7731b4..5d714dc34e 100644 --- a/src/network/ssl/qsslpresharedkeyauthenticator.h +++ b/src/network/ssl/qsslpresharedkeyauthenticator.h @@ -59,11 +59,9 @@ public: Q_NETWORK_EXPORT QSslPreSharedKeyAuthenticator(const QSslPreSharedKeyAuthenticator &authenticator); Q_NETWORK_EXPORT QSslPreSharedKeyAuthenticator &operator=(const QSslPreSharedKeyAuthenticator &authenticator); -#ifdef Q_COMPILER_RVALUE_REFS - QSslPreSharedKeyAuthenticator &operator=(QSslPreSharedKeyAuthenticator &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QSslPreSharedKeyAuthenticator &operator=(QSslPreSharedKeyAuthenticator &&other) noexcept { swap(other); return *this; } - void swap(QSslPreSharedKeyAuthenticator &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QSslPreSharedKeyAuthenticator &other) noexcept { qSwap(d, other.d); } Q_NETWORK_EXPORT QByteArray identityHint() const; diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index e164217e4e..e302aa1761 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -761,8 +761,8 @@ qint64 QSslSocket::bytesAvailable() const { Q_D(const QSslSocket); if (d->mode == UnencryptedMode) - return QIODevice::bytesAvailable() + (d->plainSocket ? d->plainSocket->bytesAvailable() : 0); - return QIODevice::bytesAvailable(); + return QAbstractSocket::bytesAvailable() + (d->plainSocket ? d->plainSocket->bytesAvailable() : 0); + return QAbstractSocket::bytesAvailable(); } /*! @@ -818,8 +818,8 @@ bool QSslSocket::canReadLine() const { Q_D(const QSslSocket); if (d->mode == UnencryptedMode) - return QIODevice::canReadLine() || (d->plainSocket && d->plainSocket->canReadLine()); - return QIODevice::canReadLine(); + return QAbstractSocket::canReadLine() || (d->plainSocket && d->plainSocket->canReadLine()); + return QAbstractSocket::canReadLine(); } /*! @@ -849,8 +849,8 @@ bool QSslSocket::atEnd() const { Q_D(const QSslSocket); if (d->mode == UnencryptedMode) - return QIODevice::atEnd() && (!d->plainSocket || d->plainSocket->atEnd()); - return QIODevice::atEnd(); + return QAbstractSocket::atEnd() && (!d->plainSocket || d->plainSocket->atEnd()); + return QAbstractSocket::atEnd(); } /*! @@ -924,7 +924,7 @@ QSslConfiguration QSslSocket::sslConfiguration() const // create a deep copy of our configuration QSslConfigurationPrivate *copy = new QSslConfigurationPrivate(d->configuration); - copy->ref.store(0); // the QSslConfiguration constructor refs up + copy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up copy->sessionCipher = d->sessionCipher(); copy->sessionProtocol = d->sessionProtocol(); @@ -1209,12 +1209,21 @@ void QSslSocket::setPrivateKey(const QSslKey &key) void QSslSocket::setPrivateKey(const QString &fileName, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat format, const QByteArray &passPhrase) { - Q_D(QSslSocket); QFile file(fileName); - if (file.open(QIODevice::ReadOnly)) { - d->configuration.privateKey = QSslKey(file.readAll(), algorithm, - format, QSsl::PrivateKey, passPhrase); + if (!file.open(QIODevice::ReadOnly)) { + qCWarning(lcSsl, "QSslSocket::setPrivateKey: Couldn't open file for reading"); + return; + } + + QSslKey key(file.readAll(), algorithm, format, QSsl::PrivateKey, passPhrase); + if (key.isNull()) { + qCWarning(lcSsl, "QSslSocket::setPrivateKey: " + "The specified file does not contain a valid key"); + return; } + + Q_D(QSslSocket); + d->configuration.privateKey = key; } /*! @@ -1503,7 +1512,7 @@ bool QSslSocket::addDefaultCaCertificates(const QString &path, QSsl::EncodingFor SSL socket's CA certificate database is initialized to the default CA certificate database. - \sa QSslConfiguration::defaultCaCertificates(), addCaCertificates() + \sa QSslConfiguration::caCertificates(), addCaCertificates() */ void QSslSocket::addDefaultCaCertificate(const QSslCertificate &certificate) { @@ -2369,7 +2378,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri if (!global) return; - ptr->ref.store(1); + ptr->ref.storeRelaxed(1); ptr->peerCertificate = global->peerCertificate; ptr->peerCertificateChain = global->peerCertificateChain; ptr->localCertificateChain = global->localCertificateChain; diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp index e290ba79dd..e0e065679d 100644 --- a/src/network/ssl/qsslsocket_mac.cpp +++ b/src/network/ssl/qsslsocket_mac.cpp @@ -215,7 +215,7 @@ void QSecureTransportContext::reset(SSLContextRef newContext) context = newContext; } -Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_securetransport_mutex, (QMutex::Recursive)) +Q_GLOBAL_STATIC(QRecursiveMutex, qt_securetransport_mutex) //#define QSSLSOCKET_DEBUG diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index c0035d23a8..d4bad1b1a5 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -523,11 +523,12 @@ bool QSslSocketBackendPrivate::initSslContext() { Q_Q(QSslSocket); - // If no external context was set (e.g. bei QHttpNetworkConnection) we will create a default context + // If no external context was set (e.g. by QHttpNetworkConnection) we will + // create a default context if (!sslContextPointer) { // create a deep copy of our configuration QSslConfigurationPrivate *configurationCopy = new QSslConfigurationPrivate(configuration); - configurationCopy->ref.store(0); // the QSslConfiguration constructor refs up + configurationCopy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up sslContextPointer = QSslContext::sharedFromConfiguration(mode, configurationCopy, allowRootCertOnDemandLoading); } diff --git a/src/network/ssl/qsslsocket_openssl11.cpp b/src/network/ssl/qsslsocket_openssl11.cpp index b60b8be41f..1d935c5217 100644 --- a/src/network/ssl/qsslsocket_openssl11.cpp +++ b/src/network/ssl/qsslsocket_openssl11.cpp @@ -68,10 +68,11 @@ #include <QtCore/qfile.h> #include <QtCore/qmutex.h> #include <QtCore/qlibrary.h> +#include <QtCore/qoperatingsystemversion.h> QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_opensslInitMutex, (QMutex::Recursive)) +Q_GLOBAL_STATIC(QRecursiveMutex, qt_opensslInitMutex) void QSslSocketPrivate::deinitialize() { @@ -142,13 +143,12 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded() if (!s_loadRootCertsOnDemand) setDefaultCaCertificates(systemCaCertificates()); #ifdef Q_OS_WIN - //Enabled for fetching additional root certs from windows update on windows 6+ + //Enabled for fetching additional root certs from windows update on windows. //This flag is set false by setDefaultCaCertificates() indicating the app uses //its own cert bundle rather than the system one. //Same logic that disables the unix on demand cert loading. //Unlike unix, we do preload the certificates from the cert store. - if ((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) >= QSysInfo::WV_6_0) - s_loadRootCertsOnDemand = true; + s_loadRootCertsOnDemand = true; #endif } diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h index 150617e3d2..0fe0899d4f 100644 --- a/src/network/ssl/qsslsocket_openssl11_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h @@ -82,6 +82,7 @@ Q_AUTOTEST_EXPORT const BIO_METHOD *q_BIO_s_mem(); int q_DSA_bits(DSA *a); int q_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c); +Q_AUTOTEST_EXPORT int q_EVP_PKEY_up_ref(EVP_PKEY *a); 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); diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 74ac852bcd..1fcfdf9f16 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -63,13 +63,11 @@ # include <QtCore/qlibrary.h> #endif #include <QtCore/qmutex.h> -#if QT_CONFIG(thread) -#include <private/qmutexpool_p.h> -#endif #include <QtCore/qdatetime.h> #if defined(Q_OS_UNIX) #include <QtCore/qdir.h> #endif +#include <QtCore/private/qmemory_p.h> #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) #include <link.h> #endif @@ -150,6 +148,7 @@ DEFINEFUNC(BIO *, BIO_new, const BIO_METHOD *a, a, return nullptr, return) DEFINEFUNC(const BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return nullptr, return) DEFINEFUNC2(int, BN_is_word, BIGNUM *a, a, BN_ULONG w, w, return 0, return) DEFINEFUNC(int, EVP_CIPHER_CTX_reset, EVP_CIPHER_CTX *c, c, return 0, return) +DEFINEFUNC(int, EVP_PKEY_up_ref, EVP_PKEY *a, a, return 0, 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) @@ -363,6 +362,11 @@ DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return null #ifndef OPENSSL_NO_RC2 DEFINEFUNC(const EVP_CIPHER *, EVP_rc2_cbc, DUMMYARG, DUMMYARG, return nullptr, return) #endif +#ifndef OPENSSL_NO_AES +DEFINEFUNC(const EVP_CIPHER *, EVP_aes_128_cbc, DUMMYARG, DUMMYARG, return nullptr, return) +DEFINEFUNC(const EVP_CIPHER *, EVP_aes_192_cbc, DUMMYARG, DUMMYARG, return nullptr, return) +DEFINEFUNC(const EVP_CIPHER *, EVP_aes_256_cbc, DUMMYARG, DUMMYARG, return nullptr, return) +#endif DEFINEFUNC(const EVP_MD *, EVP_sha1, DUMMYARG, DUMMYARG, return nullptr, return) DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return) DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return) @@ -371,6 +375,7 @@ DEFINEFUNC2(int, EVP_PKEY_set1_DH, EVP_PKEY *a, a, DH *b, b, return -1, return) #ifndef OPENSSL_NO_EC DEFINEFUNC2(int, EVP_PKEY_set1_EC_KEY, EVP_PKEY *a, a, EC_KEY *b, b, return -1, return) #endif +DEFINEFUNC2(int, EVP_PKEY_cmp, const EVP_PKEY *a, a, const EVP_PKEY *b, b, return -1, return) DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG) DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return nullptr, return) DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return nullptr, return) @@ -592,8 +597,8 @@ DEFINEFUNC2(PKCS12 *, d2i_PKCS12_bio, BIO *bio, bio, PKCS12 **pkcs12, pkcs12, re DEFINEFUNC(void, PKCS12_free, PKCS12 *pkcs12, pkcs12, return, DUMMYARG) #define RESOLVEFUNC(func) \ - if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \ - && !(_q_##func = _q_PTR_##func(libs.second->resolve(#func)))) \ + if (!(_q_##func = _q_PTR_##func(libs.ssl->resolve(#func))) \ + && !(_q_##func = _q_PTR_##func(libs.crypto->resolve(#func)))) \ qsslSocketCannotResolveSymbolWarning(#func); #if !defined QT_LINKED_OPENSSL @@ -692,7 +697,7 @@ static QStringList libraryPathList() // discover paths of already loaded libraries QSet<QString> loadedPaths; dl_iterate_phdr(dlIterateCallback, &loadedPaths); - paths.append(loadedPaths.toList()); + paths.append(loadedPaths.values()); #endif return paths; @@ -729,34 +734,31 @@ static QStringList findAllLibCrypto() # endif #ifdef Q_OS_WIN -static bool tryToLoadOpenSslWin32Library(QLatin1String ssleay32LibName, QLatin1String libeay32LibName, QPair<QSystemLibrary*, QSystemLibrary*> &pair) -{ - pair.first = 0; - pair.second = 0; - QSystemLibrary *ssleay32 = new QSystemLibrary(ssleay32LibName); +struct LoadedOpenSsl { + std::unique_ptr<QSystemLibrary> ssl, crypto; +}; + +static bool tryToLoadOpenSslWin32Library(QLatin1String ssleay32LibName, QLatin1String libeay32LibName, LoadedOpenSsl &result) +{ + auto ssleay32 = qt_make_unique<QSystemLibrary>(ssleay32LibName); if (!ssleay32->load(false)) { - delete ssleay32; return FALSE; } - QSystemLibrary *libeay32 = new QSystemLibrary(libeay32LibName); + auto libeay32 = qt_make_unique<QSystemLibrary>(libeay32LibName); if (!libeay32->load(false)) { - delete ssleay32; - delete libeay32; return FALSE; } - pair.first = ssleay32; - pair.second = libeay32; + result.ssl = std::move(ssleay32); + result.crypto = std::move(libeay32); return TRUE; } -static QPair<QSystemLibrary*, QSystemLibrary*> loadOpenSslWin32() +static LoadedOpenSsl loadOpenSsl() { - QPair<QSystemLibrary*,QSystemLibrary*> pair; - pair.first = 0; - pair.second = 0; + LoadedOpenSsl result; #if QT_CONFIG(opensslv11) // With OpenSSL 1.1 the names have changed to libssl-1_1(-x64) and libcrypto-1_1(-x64), for builds using @@ -769,7 +771,7 @@ static QPair<QSystemLibrary*, QSystemLibrary*> loadOpenSslWin32() #endif // !Q_PROCESSOR_x86_64 tryToLoadOpenSslWin32Library(QLatin1String("libssl-1_1" QT_SSL_SUFFIX), - QLatin1String("libcrypto-1_1" QT_SSL_SUFFIX), pair); + QLatin1String("libcrypto-1_1" QT_SSL_SUFFIX), result); #undef QT_SSL_SUFFIX @@ -778,28 +780,30 @@ static QPair<QSystemLibrary*, QSystemLibrary*> loadOpenSslWin32() // When OpenSSL is built using MSVC then the libraries are named 'ssleay32.dll' and 'libeay32'dll'. // When OpenSSL is built using GCC then different library names are used (depending on the OpenSSL version) // The oldest version of a GCC-based OpenSSL which can be detected by the code below is 0.9.8g (released in 2007) - if (!tryToLoadOpenSslWin32Library(QLatin1String("ssleay32"), QLatin1String("libeay32"), pair)) { - if (!tryToLoadOpenSslWin32Library(QLatin1String("libssl-10"), QLatin1String("libcrypto-10"), pair)) { - if (!tryToLoadOpenSslWin32Library(QLatin1String("libssl-8"), QLatin1String("libcrypto-8"), pair)) { - tryToLoadOpenSslWin32Library(QLatin1String("libssl-7"), QLatin1String("libcrypto-7"), pair); + if (!tryToLoadOpenSslWin32Library(QLatin1String("ssleay32"), QLatin1String("libeay32"), result)) { + if (!tryToLoadOpenSslWin32Library(QLatin1String("libssl-10"), QLatin1String("libcrypto-10"), result)) { + if (!tryToLoadOpenSslWin32Library(QLatin1String("libssl-8"), QLatin1String("libcrypto-8"), result)) { + tryToLoadOpenSslWin32Library(QLatin1String("libssl-7"), QLatin1String("libcrypto-7"), result); } } } #endif // !QT_CONFIG(opensslv11) - return pair; + return result; } #else -static QPair<QLibrary*, QLibrary*> loadOpenSsl() +struct LoadedOpenSsl { + std::unique_ptr<QLibrary> ssl, crypto; +}; + +static LoadedOpenSsl loadOpenSsl() { - QPair<QLibrary*,QLibrary*> pair; + LoadedOpenSsl result = {qt_make_unique<QLibrary>(), qt_make_unique<QLibrary>()}; # if defined(Q_OS_UNIX) - QLibrary *&libssl = pair.first; - QLibrary *&libcrypto = pair.second; - libssl = new QLibrary; - libcrypto = new QLibrary; + QLibrary * const libssl = result.ssl.get(); + QLibrary * const libcrypto = result.crypto.get(); // Try to find the libssl library on the system. // @@ -843,7 +847,7 @@ static QPair<QLibrary*, QLibrary*> loadOpenSsl() libcrypto->setFileNameAndVersion(QLatin1String("crypto"), QLatin1String(SHLIB_VERSION_NUMBER)); if (libcrypto->load() && libssl->load()) { // libssl.so.<SHLIB_VERSION_NUMBER> and libcrypto.so.<SHLIB_VERSION_NUMBER> found - return pair; + return result; } else { libssl->unload(); libcrypto->unload(); @@ -862,7 +866,7 @@ static QPair<QLibrary*, QLibrary*> loadOpenSsl() libssl->setFileNameAndVersion(QLatin1String("ssl"), fallbackSoname); libcrypto->setFileNameAndVersion(QLatin1String("crypto"), fallbackSoname); if (libcrypto->load() && libssl->load()) { - return pair; + return result; } else { libssl->unload(); libcrypto->unload(); @@ -899,7 +903,7 @@ static QPair<QLibrary*, QLibrary*> loadOpenSsl() # endif if (libcrypto->load() && libssl->load()) { // libssl.so.0 and libcrypto.so.0 found - return pair; + return result; } else { libssl->unload(); libcrypto->unload(); @@ -924,7 +928,7 @@ static QPair<QLibrary*, QLibrary*> loadOpenSsl() if (libssl->load()) { // libssl.so.x and libcrypto.so.x found - return pair; + return result; } else { libssl->unload(); } @@ -934,41 +938,33 @@ static QPair<QLibrary*, QLibrary*> loadOpenSsl() } // failed to load anything - delete libssl; - delete libcrypto; - libssl = libcrypto = 0; - return pair; + result = {}; + return result; # else // not implemented for this platform yet - return pair; + return result; # endif } #endif +static QBasicMutex symbolResolveMutex; +static QBasicAtomicInt symbolsResolved = Q_BASIC_ATOMIC_INITIALIZER(false); +static bool triedToResolveSymbols = false; + bool q_resolveOpenSslSymbols() { - static bool symbolsResolved = false; - static bool triedToResolveSymbols = false; -#if QT_CONFIG(thread) -#if QT_CONFIG(opensslv11) - QMutexLocker locker(QMutexPool::globalInstanceGet((void *)&q_OPENSSL_init_ssl)); -#else - QMutexLocker locker(QMutexPool::globalInstanceGet((void *)&q_SSL_library_init)); -#endif -#endif - if (symbolsResolved) + if (symbolsResolved.loadAcquire()) + return true; + QMutexLocker locker(&symbolResolveMutex); + if (symbolsResolved.loadRelaxed()) return true; if (triedToResolveSymbols) return false; triedToResolveSymbols = true; -#ifdef Q_OS_WIN - QPair<QSystemLibrary *, QSystemLibrary *> libs = loadOpenSslWin32(); -#else - QPair<QLibrary *, QLibrary *> libs = loadOpenSsl(); -#endif - if (!libs.first || !libs.second) + LoadedOpenSsl libs = loadOpenSsl(); + if (!libs.ssl || !libs.crypto) // failed to load them return false; @@ -978,6 +974,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(OPENSSL_init_crypto) RESOLVEFUNC(ASN1_STRING_get0_data) RESOLVEFUNC(EVP_CIPHER_CTX_reset) + RESOLVEFUNC(EVP_PKEY_up_ref) RESOLVEFUNC(EVP_PKEY_base_id) RESOLVEFUNC(RSA_bits) RESOLVEFUNC(OPENSSL_sk_new_null) @@ -1016,8 +1013,6 @@ bool q_resolveOpenSslSymbols() if (!_q_OpenSSL_version) { // Apparently, we were built with OpenSSL 1.1 enabled but are now using // a wrong library. - delete libs.first; - delete libs.second; qCWarning(lcSsl, "Incompatible version of OpenSSL"); return false; } @@ -1139,8 +1134,6 @@ bool q_resolveOpenSslSymbols() // OpenSSL 1.1 has deprecated and removed SSLeay. We consider a failure to // resolve this symbol as a failure to resolve symbols. // The right operand of '||' above is ... a bit of paranoia. - delete libs.first; - delete libs.second; qCWarning(lcSsl, "Incompatible version of OpenSSL"); return false; } @@ -1204,6 +1197,11 @@ bool q_resolveOpenSslSymbols() #ifndef OPENSSL_NO_RC2 RESOLVEFUNC(EVP_rc2_cbc) #endif +#ifndef OPENSSL_NO_AES + RESOLVEFUNC(EVP_aes_128_cbc) + RESOLVEFUNC(EVP_aes_192_cbc) + RESOLVEFUNC(EVP_aes_256_cbc) +#endif RESOLVEFUNC(EVP_sha1) RESOLVEFUNC(EVP_PKEY_assign) RESOLVEFUNC(EVP_PKEY_set1_RSA) @@ -1212,6 +1210,7 @@ bool q_resolveOpenSslSymbols() #ifndef OPENSSL_NO_EC RESOLVEFUNC(EVP_PKEY_set1_EC_KEY) #endif + RESOLVEFUNC(EVP_PKEY_cmp) RESOLVEFUNC(EVP_PKEY_free) RESOLVEFUNC(EVP_PKEY_get1_DSA) RESOLVEFUNC(EVP_PKEY_get1_RSA) @@ -1405,9 +1404,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(d2i_PKCS12_bio) RESOLVEFUNC(PKCS12_free) - symbolsResolved = true; - delete libs.first; - delete libs.second; + symbolsResolved.storeRelease(true); return true; } #endif // QT_CONFIG(library) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 7b604b2ab8..69b2b90fbd 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -281,14 +281,20 @@ const EVP_CIPHER *q_EVP_des_ede3_cbc(); #ifndef OPENSSL_NO_RC2 const EVP_CIPHER *q_EVP_rc2_cbc(); #endif +#ifndef OPENSSL_NO_AES +const EVP_CIPHER *q_EVP_aes_128_cbc(); +const EVP_CIPHER *q_EVP_aes_192_cbc(); +const EVP_CIPHER *q_EVP_aes_256_cbc(); +#endif Q_AUTOTEST_EXPORT const EVP_MD *q_EVP_sha1(); int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c); Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b); -int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b); -int q_EVP_PKEY_set1_DH(EVP_PKEY *a, DH *b); +Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b); +Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_DH(EVP_PKEY *a, DH *b); #ifndef OPENSSL_NO_EC -int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b); +Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b); #endif +Q_AUTOTEST_EXPORT int q_EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b); Q_AUTOTEST_EXPORT void q_EVP_PKEY_free(EVP_PKEY *a); RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a); DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a); diff --git a/src/network/ssl/qsslsocket_opensslpre11.cpp b/src/network/ssl/qsslsocket_opensslpre11.cpp index f5aab821ea..2af437f0fa 100644 --- a/src/network/ssl/qsslsocket_opensslpre11.cpp +++ b/src/network/ssl/qsslsocket_opensslpre11.cpp @@ -67,7 +67,6 @@ #include <QtCore/qthread.h> #include <QtCore/qfile.h> #include <QtCore/qmutex.h> -#include <QtCore/qlibrary.h> QT_BEGIN_NAMESPACE diff --git a/src/network/ssl/qsslsocket_qt.cpp b/src/network/ssl/qsslsocket_qt.cpp index b0fb60ea76..9ff9a66c05 100644 --- a/src/network/ssl/qsslsocket_qt.cpp +++ b/src/network/ssl/qsslsocket_qt.cpp @@ -72,6 +72,7 @@ static QAsn1Element _q_PKCS7_data(const QByteArray &data) Some test vectors: http://www.drh-consultancy.demon.co.uk/test.txt + \internal */ static QByteArray _q_PKCS12_keygen(char id, const QByteArray &salt, const QString &passPhrase, int n, int r) { diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp index 339ecf4da2..d7fb080b49 100644 --- a/src/network/ssl/qsslsocket_schannel.cpp +++ b/src/network/ssl/qsslsocket_schannel.cpp @@ -435,7 +435,7 @@ QByteArray createAlpnString(const QByteArrayList &nextAllowedProtocols) bool QSslSocketPrivate::s_loadRootCertsOnDemand = true; bool QSslSocketPrivate::s_loadedCiphersAndCerts = false; -Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_schannel_mutex, (QMutex::Recursive)) +Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex) void QSslSocketPrivate::ensureInitialized() { diff --git a/src/network/ssl/qsslsocket_winrt.cpp b/src/network/ssl/qsslsocket_winrt.cpp index d54ac2ad73..39c1ce55e3 100644 --- a/src/network/ssl/qsslsocket_winrt.cpp +++ b/src/network/ssl/qsslsocket_winrt.cpp @@ -177,6 +177,7 @@ void QSslSocketPrivate::ensureInitialized() long QSslSocketPrivate::sslLibraryVersionNumber() { + // ### Qt 6: Find a proper replacement for the deprecated method below. return QSysInfo::windowsVersion(); } |