summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp25
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp36
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp17
-rw-r--r--src/network/access/qnetworkreplywasmimpl.cpp6
-rw-r--r--src/network/access/qnetworkrequest.cpp15
-rw-r--r--src/network/access/qnetworkrequest.h3
-rw-r--r--src/network/access/qspdyprotocolhandler.cpp15
-rw-r--r--src/network/access/qspdyprotocolhandler_p.h2
-rw-r--r--src/network/bearer/qsharednetworksession.cpp14
-rw-r--r--src/network/configure.json23
-rw-r--r--src/network/kernel/kernel.pri2
-rw-r--r--src/network/kernel/qhostinfo.cpp163
-rw-r--r--src/network/kernel/qhostinfo.h6
-rw-r--r--src/network/kernel/qhostinfo_p.h50
-rw-r--r--src/network/kernel/qnetconmonitor_darwin.mm2
-rw-r--r--src/network/kernel/qnetconmonitor_p.h6
-rw-r--r--src/network/kernel/qnetconmonitor_stub.cpp2
-rw-r--r--src/network/kernel/qnetconmonitor_win.cpp710
-rw-r--r--src/network/socket/qhttpsocketengine.cpp2
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp3
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp115
-rw-r--r--src/network/ssl/qsslcertificate_openssl.cpp13
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp2
-rw-r--r--src/network/ssl/qsslsocket_openssl11.cpp2
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp128
-rw-r--r--src/network/ssl/qsslsocket_schannel.cpp2
26 files changed, 1101 insertions, 263 deletions
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index 93afcf0ee1..0ece5b7179 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -170,7 +170,6 @@ QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *chan
encoder(HPack::FieldLookupTable::DefaultSize, true)
{
Q_ASSERT(channel && m_connection);
-
continuedFrames.reserve(20);
const ProtocolParameters params(m_connection->http2Parameters());
@@ -328,10 +327,32 @@ bool QHttp2ProtocolHandler::sendRequest()
return false;
}
+ // Process 'fake' (created by QNetworkAccessManager::connectToHostEncrypted())
+ // requests first:
+ auto &requests = m_channel->spdyRequestsToSend;
+ for (auto it = requests.begin(), endIt = requests.end(); it != endIt;) {
+ const auto &pair = *it;
+ const QString scheme(pair.first.url().scheme());
+ if (scheme == QLatin1String("preconnect-http")
+ || scheme == QLatin1String("preconnect-https")) {
+ m_connection->preConnectFinished();
+ emit pair.second->finished();
+ it = requests.erase(it);
+ if (!requests.size()) {
+ // Normally, after a connection was established and H2
+ // was negotiated, we send a client preface. connectToHostEncrypted
+ // though is not meant to send any data, it's just a 'preconnect'.
+ // Thus we return early:
+ return true;
+ }
+ } else {
+ ++it;
+ }
+ }
+
if (!prefaceSent && !sendClientPreface())
return false;
- auto &requests = m_channel->spdyRequestsToSend;
if (!requests.size())
return true;
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index 6fb4710d77..acc551a7c9 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -128,7 +128,8 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &p
QString result;
QUrl copy = url;
QString scheme = copy.scheme();
- bool isEncrypted = scheme == QLatin1String("https");
+ bool isEncrypted = scheme == QLatin1String("https")
+ || scheme == QLatin1String("preconnect-https");
copy.setPort(copy.port(isEncrypted ? 443 : 80));
if (scheme == QLatin1String("preconnect-http")) {
copy.setScheme(QLatin1String("http"));
@@ -296,17 +297,29 @@ void QHttpThreadDelegate::startRequest()
connectionType = QHttpNetworkConnection::ConnectionTypeHTTP2Direct;
}
+ const bool isH2 = httpRequest.isHTTP2Allowed() || httpRequest.isHTTP2Direct();
+ if (isH2) {
+#if QT_CONFIG(ssl)
+ if (ssl) {
+ if (!httpRequest.isHTTP2Direct()) {
+ QList<QByteArray> protocols;
+ protocols << QSslConfiguration::ALPNProtocolHTTP2
+ << QSslConfiguration::NextProtocolHttp1_1;
+ incomingSslConfiguration->setAllowedNextProtocols(protocols);
+ }
+ urlCopy.setScheme(QStringLiteral("h2s"));
+ } else
+#endif // QT_CONFIG(ssl)
+ {
+ urlCopy.setScheme(QStringLiteral("h2"));
+ }
+ }
+
#ifndef QT_NO_SSL
if (ssl && !incomingSslConfiguration.data())
incomingSslConfiguration.reset(new QSslConfiguration);
- if (httpRequest.isHTTP2Allowed() && ssl) {
- // With HTTP2Direct we do not try any protocol negotiation.
- QList<QByteArray> protocols;
- protocols << QSslConfiguration::ALPNProtocolHTTP2
- << QSslConfiguration::NextProtocolHttp1_1;
- incomingSslConfiguration->setAllowedNextProtocols(protocols);
- } else if (httpRequest.isSPDYAllowed() && ssl) {
+ if (!isH2 && httpRequest.isSPDYAllowed() && ssl) {
connectionType = QHttpNetworkConnection::ConnectionTypeSPDY;
urlCopy.setScheme(QStringLiteral("spdy")); // to differentiate SPDY requests from HTTPS requests
QList<QByteArray> nextProtocols;
@@ -323,12 +336,11 @@ void QHttpThreadDelegate::startRequest()
cacheKey = makeCacheKey(urlCopy, &cacheProxy, httpRequest.peerVerifyName());
else
#endif
- cacheKey = makeCacheKey(urlCopy, 0, httpRequest.peerVerifyName());
-
+ cacheKey = makeCacheKey(urlCopy, nullptr, httpRequest.peerVerifyName());
// the http object is actually a QHttpNetworkConnection
httpConnection = static_cast<QNetworkAccessCachedHttpConnection *>(connections.localData()->requestEntryNow(cacheKey));
- if (httpConnection == 0) {
+ if (!httpConnection) {
// no entry in cache; create an object
// the http object is actually a QHttpNetworkConnection
#ifdef QT_NO_BEARERMANAGEMENT
@@ -358,7 +370,7 @@ void QHttpThreadDelegate::startRequest()
connections.localData()->addEntry(cacheKey, httpConnection);
} else {
if (httpRequest.withCredentials()) {
- QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), 0);
+ QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), nullptr);
if (!credential.user.isEmpty() && !credential.password.isEmpty()) {
QAuthenticator auth;
auth.setUser(credential.user);
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index a2996e3533..29192ae7b0 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -493,9 +493,9 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
connect(&d->statusMonitor, SIGNAL(onlineStateChanged(bool)),
SLOT(_q_onlineStateChanged(bool)));
#ifdef QT_NO_BEARERMANAGEMENT
- d->networkAccessible = d->statusMonitor.isNetworkAccesible();
+ d->networkAccessible = d->statusMonitor.isNetworkAccessible();
#else
- d->networkAccessible = d->statusMonitor.isNetworkAccesible() ? Accessible : NotAccessible;
+ 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.
@@ -1236,10 +1236,11 @@ void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quin
if (sslConfiguration != QSslConfiguration::defaultConfiguration())
request.setSslConfiguration(sslConfiguration);
- // There is no way to enable SPDY via a request, so we need to check
- // the ssl configuration whether SPDY is allowed here.
- if (sslConfiguration.allowedNextProtocols().contains(
- QSslConfiguration::NextProtocolSpdy3_0))
+ // There is no way to enable SPDY/HTTP2 via a request, so we need to check
+ // the ssl configuration whether SPDY/HTTP2 is allowed here.
+ if (sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::ALPNProtocolHTTP2))
+ request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
+ else if (sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::NextProtocolSpdy3_0))
request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true);
request.setPeerVerifyName(peerName);
@@ -1565,8 +1566,8 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
#ifndef QT_NO_BEARERMANAGEMENT
// NETMONTODO: network reply impl must be augmented to use the same monitoring
- // capabilities as http network reply impl does.
- if (!isLocalFile && !d->statusMonitor.isEnabled()) {
+ // 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()));
}
diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp
index ee91dc20b3..53784407d8 100644
--- a/src/network/access/qnetworkreplywasmimpl.cpp
+++ b/src/network/access/qnetworkreplywasmimpl.cpp
@@ -135,8 +135,10 @@ static void q_loadCallback(val event)
if (readyState == 4) { // done
reply->setReplyAttributes(xhr["data-handler"].as<quintptr>(), status, statusText);
- if (!responseString.isEmpty())
- reply->dataReceived(responseString.toUtf8(), responseString.size());
+ if (!responseString.isEmpty()) {
+ QByteArray responseStringArray = responseString.toUtf8();
+ reply->dataReceived(responseStringArray, responseStringArray.size());
+ }
}
}
if (status >= 400 && !statusText.isEmpty())
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index ba36c75419..37f64c3f52 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -468,13 +468,24 @@ public:
};
/*!
+ Constructs a QNetworkRequest object with no URL to be requested.
+ Use setUrl() to set one.
+
+ \sa url(), setUrl()
+*/
+QNetworkRequest::QNetworkRequest()
+ : d(new QNetworkRequestPrivate)
+{
+}
+
+/*!
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;
}
@@ -1344,7 +1355,7 @@ QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
{
- return QLocale::c().toString(dt, QStringViewLiteral("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 846ead1592..8ad4ab41c0 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -128,7 +128,8 @@ public:
};
- explicit QNetworkRequest(const QUrl &url = QUrl());
+ QNetworkRequest();
+ explicit QNetworkRequest(const QUrl &url);
QNetworkRequest(const QNetworkRequest &other);
~QNetworkRequest();
QNetworkRequest &operator=(QNetworkRequest &&other) noexcept { swap(other); return *this; }
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/qsharednetworksession.cpp b/src/network/bearer/qsharednetworksession.cpp
index af543f77e3..b3e9892f4b 100644
--- a/src/network/bearer/qsharednetworksession.cpp
+++ b/src/network/bearer/qsharednetworksession.cpp
@@ -64,9 +64,23 @@ struct DeleteLater {
}
};
+template <typename Container>
+static void maybe_prune_expired(Container &c)
+{
+ 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();
+ maybe_prune_expired(m->sessions);
auto &entry = m->sessions[config];
//if already have a session, return it
if (auto p = entry.toStrongRef())
diff --git a/src/network/configure.json b/src/network/configure.json
index 56805da7b2..9652dfd1f7 100644
--- a/src/network/configure.json
+++ b/src/network/configure.json
@@ -208,6 +208,22 @@
"main": ["gss_ctx_id_t ctx;"],
"qmake": "LIBS += -lgssapi_krb5"
}
+ },
+ "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"
+ }
}
},
@@ -397,6 +413,13 @@
"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" ]
}
},
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri
index 0e4cef5e74..a55648dbc7 100644
--- a/src/network/kernel/kernel.pri
+++ b/src/network/kernel/kernel.pri
@@ -77,6 +77,8 @@ macos | ios {
kernel/qnetconmonitor_darwin.mm
LIBS_PRIVATE += -framework SystemConfiguration
+} else:qtConfig(netlistmgr) {
+ SOURCES += kernel/qnetconmonitor_win.cpp
} else {
SOURCES += kernel/qnetconmonitor_stub.cpp
}
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index 25ff873307..487cac6d90 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -47,6 +47,7 @@
#include <qabstracteventdispatcher.h>
#include <qcoreapplication.h>
#include <qmetaobject.h>
+#include <qscopeguard.h>
#include <qstringlist.h>
#include <qthread.h>
#include <qurl.h>
@@ -85,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)
{
@@ -118,11 +112,39 @@ int get_signal_index()
return signal_index + QMetaObjectPrivate::signalOffset(senderMetaObject);
}
-void emit_results_ready(const QHostInfo &hostInfo, const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj)
+}
+
+/*
+ The calling thread is likely the one that executes the lookup via
+ QHostInfoRunnable. Unless we operate with a queued connection already,
+ posts the QHostInfo to a dedicated QHostInfoResult object that lives in
+ the same thread as the user-provided receiver, or (if there is none) in
+ the thread that made the call to lookupHost. That QHostInfoResult object
+ then calls the user code in the correct thread.
+
+ The 'result' object deletes itself (via deleteLater) when the metacall
+ event is received.
+*/
+void QHostInfoResult::postResultsReady(const QHostInfo &info)
{
+ // queued connection will take care of dispatching to right thread
+ if (!slotObj) {
+ emitResultsReady(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());
Q_CHECK_PTR(result);
const int nargs = 2;
auto types = reinterpret_cast<int *>(malloc(nargs * sizeof(int)));
@@ -132,15 +154,13 @@ void emit_results_ready(const QHostInfo &hostInfo, const QObject *receiver,
auto args = reinterpret_cast<void **>(malloc(nargs * sizeof(void *)));
Q_CHECK_PTR(args);
args[0] = 0;
- args[1] = QMetaType::create(types[1], &hostInfo);
+ 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);
}
-}
-
/*!
\class QHostInfo
\brief The QHostInfo class provides static functions for host name lookups.
@@ -328,6 +348,10 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver,
ready, the \a functor is called with a QHostInfo argument. The
QHostInfo object can then be inspected to get the results of the
lookup.
+
+ The \a functor will be run in the thread that makes the call to lookupHost;
+ that thread must have a running Qt event loop.
+
\note There is no guarantee on the order the signals will be emitted
if you start multiple requests with lookupHost().
@@ -398,7 +422,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;
}
@@ -411,7 +435,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;
}
@@ -600,8 +624,9 @@ QHostInfo QHostInfoAgent::lookup(const QString &hostName)
\sa lookupId()
*/
QHostInfo::QHostInfo(int id)
- : d(new QHostInfoPrivate)
+ : d_ptr(new QHostInfoPrivate)
{
+ Q_D(QHostInfo);
d->lookupId = id;
}
@@ -609,17 +634,30 @@ 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))
{
}
/*!
+ Move-constucts 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;
}
@@ -628,6 +666,7 @@ QHostInfo &QHostInfo::operator=(const QHostInfo &other)
*/
QHostInfo::~QHostInfo()
{
+ delete d_ptr;
}
/*!
@@ -642,6 +681,7 @@ QHostInfo::~QHostInfo()
*/
QList<QHostAddress> QHostInfo::addresses() const
{
+ Q_D(const QHostInfo);
return d->addrs;
}
@@ -652,6 +692,7 @@ QList<QHostAddress> QHostInfo::addresses() const
*/
void QHostInfo::setAddresses(const QList<QHostAddress> &addresses)
{
+ Q_D(QHostInfo);
d->addrs = addresses;
}
@@ -662,6 +703,7 @@ void QHostInfo::setAddresses(const QList<QHostAddress> &addresses)
*/
QString QHostInfo::hostName() const
{
+ Q_D(const QHostInfo);
return d->hostName;
}
@@ -672,6 +714,7 @@ QString QHostInfo::hostName() const
*/
void QHostInfo::setHostName(const QString &hostName)
{
+ Q_D(QHostInfo);
d->hostName = hostName;
}
@@ -683,6 +726,7 @@ void QHostInfo::setHostName(const QString &hostName)
*/
QHostInfo::HostInfoError QHostInfo::error() const
{
+ Q_D(const QHostInfo);
return d->err;
}
@@ -693,6 +737,7 @@ QHostInfo::HostInfoError QHostInfo::error() const
*/
void QHostInfo::setError(HostInfoError error)
{
+ Q_D(QHostInfo);
d->err = error;
}
@@ -703,6 +748,7 @@ void QHostInfo::setError(HostInfoError error)
*/
int QHostInfo::lookupId() const
{
+ Q_D(const QHostInfo);
return d->lookupId;
}
@@ -713,6 +759,7 @@ int QHostInfo::lookupId() const
*/
void QHostInfo::setLookupId(int id)
{
+ Q_D(QHostInfo);
d->lookupId = id;
}
@@ -724,6 +771,7 @@ void QHostInfo::setLookupId(int id)
*/
QString QHostInfo::errorString() const
{
+ Q_D(const QHostInfo);
return d->errorStr;
}
@@ -735,6 +783,7 @@ QString QHostInfo::errorString() const
*/
void QHostInfo::setErrorString(const QString &str)
{
+ Q_D(QHostInfo);
d->errorStr = str;
}
@@ -791,7 +840,8 @@ int QHostInfo::lookupHostImpl(const QString &name,
QHostInfo hostInfo(id);
hostInfo.setError(QHostInfo::HostNotFound);
hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given"));
- emit_results_ready(hostInfo, receiver, slotObj);
+ QHostInfoResult result(receiver, slotObj);
+ result.postResultsReady(hostInfo);
return id;
}
@@ -805,7 +855,8 @@ int QHostInfo::lookupHostImpl(const QString &name,
QHostInfo info = manager->cache.get(name, &valid);
if (valid) {
info.setLookupId(id);
- emit_results_ready(info, receiver, slotObj);
+ QHostInfoResult result(receiver, slotObj);
+ result.postResultsReady(info);
return id;
}
}
@@ -833,11 +884,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;
@@ -859,14 +909,12 @@ void QHostInfoRunnable::run()
}
// check aborted again
- if (manager->wasAborted(id)) {
- manager->lookupFinished(this);
+ if (manager->wasAborted(id))
return;
- }
// signal emission
hostInfo.setLookupId(id);
- resultEmitter.emitResultsReady(hostInfo);
+ resultEmitter.postResultsReady(hostInfo);
#if QT_CONFIG(thread)
// now also iterate through the postponed ones
@@ -879,30 +927,31 @@ void QHostInfoRunnable::run()
QHostInfoRunnable* postponed = *it;
// we can now emit
hostInfo.setLookupId(postponed->id);
- postponed->resultEmitter.emitResultsReady(hostInfo);
+ postponed->resultEmitter.postResultsReady(hostInfo);
delete postponed;
}
manager->postponedLookups.erase(partitionBegin, partitionEnd);
}
#endif
- manager->lookupFinished(this);
-
// thread goes back to QThreadPool
}
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
}
QHostInfoLookupManager::~QHostInfoLookupManager()
{
+ QMutexLocker locker(&mutex);
wasDeleted = true;
+ locker.unlock();
// don't qDeleteAll currentLookups, the QThreadPool has ownership
clear();
@@ -928,7 +977,8 @@ void QHostInfoLookupManager::clear()
cache.clear();
}
-void QHostInfoLookupManager::work()
+// assumes mutex is locked by caller
+void QHostInfoLookupManager::rescheduleWithMutexHeld()
{
if (wasDeleted)
return;
@@ -937,8 +987,6 @@ void QHostInfoLookupManager::work()
// - launch new lookups via the thread pool
// - make sure only one lookup per host/IP is in progress
- QMutexLocker locker(&mutex);
-
if (!finishedLookups.isEmpty()) {
// remove ID from aborted if it is in there
for (int i = 0; i < finishedLookups.length(); i++) {
@@ -950,7 +998,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:
@@ -990,22 +1038,23 @@ void QHostInfoLookupManager::work()
// called by QHostInfo
void QHostInfoLookupManager::scheduleLookup(QHostInfoRunnable *r)
{
+ QMutexLocker locker(&this->mutex);
+
if (wasDeleted)
return;
- QMutexLocker locker(&this->mutex);
scheduledLookups.enqueue(r);
- work();
+ rescheduleWithMutexHeld();
}
// called by QHostInfo
void QHostInfoLookupManager::abortLookup(int id)
{
+ QMutexLocker locker(&this->mutex);
+
if (wasDeleted)
return;
- QMutexLocker locker(&this->mutex);
-
#if QT_CONFIG(thread)
// is postponed? delete and return
for (int i = 0; i < postponedLookups.length(); i++) {
@@ -1031,25 +1080,27 @@ void QHostInfoLookupManager::abortLookup(int id)
// called from QHostInfoRunnable
bool QHostInfoLookupManager::wasAborted(int id)
{
+ QMutexLocker locker(&this->mutex);
+
if (wasDeleted)
return true;
- QMutexLocker locker(&this->mutex);
return abortedLookups.contains(id);
}
// called from QHostInfoRunnable
void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r)
{
+ QMutexLocker locker(&this->mutex);
+
if (wasDeleted)
return;
- QMutexLocker locker(&this->mutex);
#if QT_CONFIG(thread)
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
@@ -1059,7 +1110,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) {
@@ -1076,7 +1127,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();
}
@@ -1085,7 +1136,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);
}
@@ -1093,7 +1144,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;
@@ -1106,23 +1157,10 @@ void qt_qhostinfo_cache_inject(const QString &hostname, const QHostInfo &resolut
QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(128)
{
#ifdef QT_QHOSTINFO_CACHE_DISABLED_BY_DEFAULT
- enabled = false;
+ enabled.store(false, std::memory_order_relaxed);
#endif
}
-bool QHostInfoCache::isEnabled()
-{
- return enabled;
-}
-
-// this function is currently only used for the auto tests
-// and not usable by public API
-void QHostInfoCache::setEnabled(bool e)
-{
- enabled = e;
-}
-
-
QHostInfo QHostInfoCache::get(const QString &name, bool *valid)
{
QMutexLocker locker(&this->mutex);
@@ -1162,9 +1200,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 dc31cc08e4..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) noexcept { swap(other); return *this; }
~QHostInfo();
- void swap(QHostInfo &other) noexcept { 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 7df3f5414c..9a4657234e 100644
--- a/src/network/kernel/qhostinfo_p.h
+++ b/src/network/kernel/qhostinfo_p.h
@@ -73,6 +73,7 @@
#include <QNetworkSession>
#include <QSharedPointer>
+#include <atomic>
QT_BEGIN_NAMESPACE
@@ -83,12 +84,14 @@ class QHostInfoResult : public QObject
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)
+ slotObj(slotObj),
+ withContextObject(slotObj && receiver)
{
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this,
&QObject::deleteLater);
@@ -96,10 +99,15 @@ public:
moveToThread(receiver->thread());
}
+ void postResultsReady(const QHostInfo &info);
+
public Q_SLOTS:
inline void emitResultsReady(const QHostInfo &info)
{
if (slotObj) {
+ // we used to have a context object, but it's already destroyed
+ if (withContextObject && !receiver)
+ return;
QHostInfo copy = info;
void *args[2] = { nullptr, reinterpret_cast<void *>(&copy) };
slotObj->call(const_cast<QObject*>(receiver.data()), args);
@@ -177,10 +185,12 @@ public:
void put(const QString &name, const QHostInfo &info);
void clear();
- bool isEnabled();
- void setEnabled(bool e);
+ bool isEnabled() { return enabled.load(std::memory_order_relaxed); }
+ // this function is currently only used for the auto tests
+ // and not usable by public API
+ void setEnabled(bool e) { enabled.store(e, std::memory_order_relaxed); }
private:
- bool enabled;
+ std::atomic<bool> enabled;
struct QHostInfoCacheElement {
QHostInfo info;
QElapsedTimer age;
@@ -205,31 +215,13 @@ public:
};
-class QAbstractHostInfoLookupManager : public QObject
-{
- Q_OBJECT
-
-public:
- ~QAbstractHostInfoLookupManager() {}
- virtual void clear() = 0;
-
- QHostInfoCache cache;
-
-protected:
- QAbstractHostInfoLookupManager() {}
- static QAbstractHostInfoLookupManager* globalInstance();
-
-};
-
-class QHostInfoLookupManager : public QAbstractHostInfoLookupManager
+class QHostInfoLookupManager
{
- Q_OBJECT
public:
QHostInfoLookupManager();
~QHostInfoLookupManager();
- void clear() override;
- void work();
+ void clear();
// called from QHostInfo
void scheduleLookup(QHostInfoRunnable *r);
@@ -239,6 +231,8 @@ public:
void lookupFinished(QHostInfoRunnable *r);
bool wasAborted(int id);
+ QHostInfoCache cache;
+
friend class QHostInfoRunnable;
protected:
#if QT_CONFIG(thread)
@@ -252,14 +246,12 @@ protected:
#if QT_CONFIG(thread)
QThreadPool threadPool;
#endif
- QRecursiveMutex mutex;
+ QMutex mutex;
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/qnetconmonitor_darwin.mm b/src/network/kernel/qnetconmonitor_darwin.mm
index a64cd6e530..f6daf9ed50 100644
--- a/src/network/kernel/qnetconmonitor_darwin.mm
+++ b/src/network/kernel/qnetconmonitor_darwin.mm
@@ -376,7 +376,7 @@ bool QNetworkStatusMonitor::isMonitoring() const
return d->ipv4Probe.isMonitoring() || d->ipv6Probe.isMonitoring();
}
-bool QNetworkStatusMonitor::isNetworkAccesible()
+bool QNetworkStatusMonitor::isNetworkAccessible()
{
// This function is to be executed on the thread that created
// and uses 'this'.
diff --git a/src/network/kernel/qnetconmonitor_p.h b/src/network/kernel/qnetconmonitor_p.h
index 74ee56d422..282bac5081 100644
--- a/src/network/kernel/qnetconmonitor_p.h
+++ b/src/network/kernel/qnetconmonitor_p.h
@@ -61,7 +61,7 @@
QT_BEGIN_NAMESPACE
class QNetworkConnectionMonitorPrivate;
-class QNetworkConnectionMonitor : public QObject
+class Q_AUTOTEST_EXPORT QNetworkConnectionMonitor : public QObject
{
Q_OBJECT
@@ -91,7 +91,7 @@ private:
};
class QNetworkStatusMonitorPrivate;
-class QNetworkStatusMonitor : public QObject
+class Q_AUTOTEST_EXPORT QNetworkStatusMonitor : public QObject
{
Q_OBJECT
@@ -99,7 +99,7 @@ public:
QNetworkStatusMonitor();
~QNetworkStatusMonitor();
- bool isNetworkAccesible();
+ bool isNetworkAccessible();
bool start();
void stop();
diff --git a/src/network/kernel/qnetconmonitor_stub.cpp b/src/network/kernel/qnetconmonitor_stub.cpp
index 7f3a0c44c6..1ad4e9ba5a 100644
--- a/src/network/kernel/qnetconmonitor_stub.cpp
+++ b/src/network/kernel/qnetconmonitor_stub.cpp
@@ -123,7 +123,7 @@ bool QNetworkStatusMonitor::isMonitoring() const
return false;
}
-bool QNetworkStatusMonitor::isNetworkAccesible()
+bool QNetworkStatusMonitor::isNetworkAccessible()
{
return false;
}
diff --git a/src/network/kernel/qnetconmonitor_win.cpp b/src/network/kernel/qnetconmonitor_win.cpp
new file mode 100644
index 0000000000..b543508169
--- /dev/null
+++ b/src/network/kernel/qnetconmonitor_win.cpp
@@ -0,0 +1,710 @@
+/****************************************************************************
+**
+** 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 final : public INetworkConnectionEvents
+{
+public:
+ QNetworkConnectionEvents(QNetworkConnectionMonitorPrivate *monitor);
+ ~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 = 1; // start at 1 for our own initial reference
+ 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;
+ 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 final : public INetworkListManagerEvents
+{
+public:
+ QNetworkListManagerEvents(QNetworkStatusMonitorPrivate *monitor);
+ ~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 = 1; // start at 1 for our own initial reference
+ 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/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp
index 6cae29193d..c67b273937 100644
--- a/src/network/socket/qhttpsocketengine.cpp
+++ b/src/network/socket/qhttpsocketengine.cpp
@@ -627,7 +627,7 @@ void QHttpSocketEngine::slotSocketReadNotification()
// from http spec is also allowed.
if (proxyConnectionHeader.isEmpty())
proxyConnectionHeader = d->reply->headerField("Connection");
- if (proxyConnectionHeader.compare("close", Qt::CaseSensitive) == 0) {
+ if (proxyConnectionHeader.compare("close", Qt::CaseInsensitive) == 0) {
willClose = true;
} else if (proxyConnectionHeader.compare("keep-alive", Qt::CaseInsensitive) == 0) {
willClose = false;
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/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index b429b3c365..9edabd7822 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -623,6 +623,53 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters()
}
+static void setErrorFromWSAError(int error, QNativeSocketEnginePrivate *d)
+{
+ Q_ASSERT(d);
+ switch (error) {
+ case WSAEISCONN:
+ d->socketState = QAbstractSocket::ConnectedState;
+ break;
+ case WSAEHOSTUNREACH:
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::HostUnreachableErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case WSAEADDRNOTAVAIL:
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::AddressNotAvailableErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case WSAEINPROGRESS:
+ d->setError(QAbstractSocket::UnfinishedSocketOperationError, QNativeSocketEnginePrivate::InvalidSocketErrorString);
+ d->socketState = QAbstractSocket::ConnectingState;
+ break;
+ case WSAEADDRINUSE:
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::AddressInuseErrorString);
+ break;
+ case WSAECONNREFUSED:
+ d->setError(QAbstractSocket::ConnectionRefusedError, QNativeSocketEnginePrivate::ConnectionRefusedErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case WSAETIMEDOUT:
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::ConnectionTimeOutErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case WSAEACCES:
+ d->setError(QAbstractSocket::SocketAccessError, QNativeSocketEnginePrivate::AccessErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case WSAENETUNREACH:
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::NetworkUnreachableErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case WSAEINVAL:
+ case WSAEALREADY:
+ d->setError(QAbstractSocket::UnfinishedSocketOperationError, QNativeSocketEnginePrivate::InvalidSocketErrorString);
+ break;
+ default:
+ break;
+ }
+}
+
bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quint16 port)
{
@@ -651,9 +698,6 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin
case WSANOTINITIALISED:
//###
break;
- case WSAEISCONN:
- socketState = QAbstractSocket::ConnectedState;
- break;
case WSAEWOULDBLOCK: {
// If WSAConnect returns WSAEWOULDBLOCK on the second
// connection attempt, we have to check SO_ERROR's
@@ -668,82 +712,33 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin
do {
if (::getsockopt(socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) {
if (value != NOERROR) {
+ WS_ERROR_DEBUG(value);
+ errorDetected = true;
// MSDN says getsockopt with SO_ERROR clears the error, but it's not actually cleared
// and this can affect all subsequent WSAConnect attempts, so clear it now.
const int val = NO_ERROR;
::setsockopt(socketDescriptor, SOL_SOCKET, SO_ERROR, reinterpret_cast<const char*>(&val), sizeof val);
- }
-
- if (value == WSAECONNREFUSED) {
- setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString);
- socketState = QAbstractSocket::UnconnectedState;
- errorDetected = true;
- break;
- }
- if (value == WSAETIMEDOUT) {
- setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString);
- socketState = QAbstractSocket::UnconnectedState;
- errorDetected = true;
- break;
- }
- if (value == WSAEHOSTUNREACH) {
- setError(QAbstractSocket::NetworkError, HostUnreachableErrorString);
- socketState = QAbstractSocket::UnconnectedState;
- errorDetected = true;
- break;
- }
- if (value == WSAEADDRNOTAVAIL) {
- setError(QAbstractSocket::NetworkError, AddressNotAvailableErrorString);
- socketState = QAbstractSocket::UnconnectedState;
- errorDetected = true;
- break;
- }
- if (value == NOERROR) {
+ } else {
// When we get WSAEWOULDBLOCK the outcome was not known, so a
// NOERROR might indicate that the result of the operation
// is still unknown. We try again to increase the chance that we did
// get the correct result.
tryAgain = !tryAgain;
}
+ setErrorFromWSAError(value, this);
}
tries++;
} while (tryAgain && (tries < 2));
if (errorDetected)
break;
+ // fall through to unfinished operation error handling
+ err = WSAEINPROGRESS;
Q_FALLTHROUGH();
}
- case WSAEINPROGRESS:
- setError(QAbstractSocket::UnfinishedSocketOperationError, InvalidSocketErrorString);
- socketState = QAbstractSocket::ConnectingState;
- break;
- case WSAEADDRINUSE:
- setError(QAbstractSocket::NetworkError, AddressInuseErrorString);
- break;
- case WSAECONNREFUSED:
- setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString);
- socketState = QAbstractSocket::UnconnectedState;
- break;
- case WSAETIMEDOUT:
- setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString);
- break;
- case WSAEACCES:
- setError(QAbstractSocket::SocketAccessError, AccessErrorString);
- socketState = QAbstractSocket::UnconnectedState;
- break;
- case WSAEHOSTUNREACH:
- setError(QAbstractSocket::NetworkError, HostUnreachableErrorString);
- socketState = QAbstractSocket::UnconnectedState;
- break;
- case WSAENETUNREACH:
- setError(QAbstractSocket::NetworkError, NetworkUnreachableErrorString);
- socketState = QAbstractSocket::UnconnectedState;
- break;
- case WSAEINVAL:
- case WSAEALREADY:
- setError(QAbstractSocket::UnfinishedSocketOperationError, InvalidSocketErrorString);
- break;
+
default:
+ setErrorFromWSAError(err, this);
break;
}
if (socketState != QAbstractSocket::ConnectedState) {
diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp
index 806c6426e4..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);
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
index 487e975db6..1725937bc2 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_openssl11.cpp b/src/network/ssl/qsslsocket_openssl11.cpp
index cc2d6ea2d9..28be4f2e79 100644
--- a/src/network/ssl/qsslsocket_openssl11.cpp
+++ b/src/network/ssl/qsslsocket_openssl11.cpp
@@ -72,7 +72,7 @@
QT_BEGIN_NAMESPACE
-Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_opensslInitMutex, (QMutex::Recursive))
+Q_GLOBAL_STATIC(QRecursiveMutex, qt_opensslInitMutex)
void QSslSocketPrivate::deinitialize()
{
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index 2eb9763e90..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
@@ -599,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
@@ -736,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
@@ -776,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
@@ -785,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.
//
@@ -850,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();
@@ -869,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();
@@ -885,11 +882,28 @@ static QPair<QLibrary*, QLibrary*> loadOpenSsl()
// macOS's /usr/lib/libssl.dylib, /usr/lib/libcrypto.dylib will be picked up in the third
// attempt, _after_ <bundle>/Contents/Frameworks has been searched.
// iOS does not ship a system libssl.dylib, libcrypto.dylib in the first place.
+# if defined(Q_OS_ANDROID)
+ // OpenSSL 1.1.x must be suffixed otherwise it will use the system libcrypto.so libssl.so which on API-21 are OpenSSL 1.0 not 1.1
+ auto openSSLSuffix = [](const QByteArray &defaultSuffix = {}) {
+ auto suffix = qgetenv("ANDROID_OPENSSL_SUFFIX");
+ if (suffix.isEmpty())
+ return defaultSuffix;
+ return suffix;
+ };
+# if QT_CONFIG(opensslv11)
+ static QString suffix = QString::fromLatin1(openSSLSuffix("_1_1"));
+# else
+ static QString suffix = QString::fromLatin1(openSSLSuffix());
+# endif
+ libssl->setFileNameAndVersion(QLatin1String("ssl") + suffix, -1);
+ libcrypto->setFileNameAndVersion(QLatin1String("crypto") + suffix, -1);
+# else
libssl->setFileNameAndVersion(QLatin1String("ssl"), -1);
libcrypto->setFileNameAndVersion(QLatin1String("crypto"), -1);
+# endif
if (libcrypto->load() && libssl->load()) {
// libssl.so.0 and libcrypto.so.0 found
- return pair;
+ return result;
} else {
libssl->unload();
libcrypto->unload();
@@ -914,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();
}
@@ -924,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;
@@ -1007,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;
}
@@ -1130,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;
}
@@ -1402,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_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp
index 1314b432a4..c254659a33 100644
--- a/src/network/ssl/qsslsocket_schannel.cpp
+++ b/src/network/ssl/qsslsocket_schannel.cpp
@@ -431,7 +431,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()
{