diff options
Diffstat (limited to 'src/network/kernel')
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 *>(©) }; - 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 |