summaryrefslogtreecommitdiffstats
path: root/src/network/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/kernel')
-rw-r--r--src/network/kernel/kernel.pri16
-rw-r--r--src/network/kernel/qauthenticator.cpp413
-rw-r--r--src/network/kernel/qauthenticator_p.h17
-rw-r--r--src/network/kernel/qdnslookup.h30
-rw-r--r--src/network/kernel/qdnslookup_p.h2
-rw-r--r--src/network/kernel/qdnslookup_win.cpp1
-rw-r--r--src/network/kernel/qhostaddress.cpp2
-rw-r--r--src/network/kernel/qhostaddress.h11
-rw-r--r--src/network/kernel/qhostinfo.cpp425
-rw-r--r--src/network/kernel/qhostinfo.h8
-rw-r--r--src/network/kernel/qhostinfo_p.h102
-rw-r--r--src/network/kernel/qhostinfo_unix.cpp139
-rw-r--r--src/network/kernel/qhostinfo_win.cpp110
-rw-r--r--src/network/kernel/qnetconmonitor_darwin.mm419
-rw-r--r--src/network/kernel/qnetconmonitor_p.h126
-rw-r--r--src/network/kernel/qnetconmonitor_stub.cpp141
-rw-r--r--src/network/kernel/qnetconmonitor_win.cpp712
-rw-r--r--src/network/kernel/qnetworkdatagram.h6
-rw-r--r--src/network/kernel/qnetworkinterface.h12
-rw-r--r--src/network/kernel/qnetworkinterface_p.h2
-rw-r--r--src/network/kernel/qnetworkinterface_unix_p.h2
-rw-r--r--src/network/kernel/qnetworkproxy.cpp9
-rw-r--r--src/network/kernel/qnetworkproxy.h12
23 files changed, 2070 insertions, 647 deletions
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 *>(&copy) };
- 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