diff options
Diffstat (limited to 'src/network')
40 files changed, 1530 insertions, 100 deletions
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp index ee3911c72c..b7e8bb3f72 100644 --- a/src/network/access/qhttpthreaddelegate.cpp +++ b/src/network/access/qhttpthreaddelegate.cpp @@ -60,6 +60,10 @@ static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QNetworkReply::NetworkError code; // we've got an error switch (httpStatusCode) { + case 400: // Bad Request + code = QNetworkReply::ProtocolInvalidOperationError; + break; + case 401: // Authorization required code = QNetworkReply::AuthenticationRequiredError; break; @@ -80,15 +84,34 @@ static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const code = QNetworkReply::ProxyAuthenticationRequiredError; break; + case 409: // Resource Conflict + code = QNetworkReply::ContentConflictError; + break; + + case 410: // Content no longer available + code = QNetworkReply::ContentGoneError; + break; + case 418: // I'm a teapot code = QNetworkReply::ProtocolInvalidOperationError; break; + case 500: // Internal Server Error + code = QNetworkReply::InternalServerError; + break; + + case 501: // Server does not support this functionality + code = QNetworkReply::OperationNotImplementedError; + break; + + case 503: // Service unavailable + code = QNetworkReply::ServiceUnavailableError; + break; default: if (httpStatusCode > 500) { // some kind of server error - code = QNetworkReply::ProtocolUnknownError; + code = QNetworkReply::UnknownServerError; } else if (httpStatusCode >= 400) { // content error we did not handle above code = QNetworkReply::UnknownContentError; diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp index a871c04d56..664bc8282c 100644 --- a/src/network/access/qnetworkcookie.cpp +++ b/src/network/access/qnetworkcookie.cpp @@ -51,6 +51,7 @@ #include "QtCore/qstring.h" #include "QtCore/qstringlist.h" #include "QtCore/qurl.h" +#include "QtNetwork/qhostaddress.h" #include "private/qobject_p.h" QT_BEGIN_NAMESPACE @@ -466,12 +467,19 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const } if (!d->domain.isEmpty()) { result += "; domain="; - QString domainNoDot = d->domain; - if (domainNoDot.startsWith(QLatin1Char('.'))) { + if (d->domain.startsWith(QLatin1Char('.'))) { result += '.'; - domainNoDot = domainNoDot.mid(1); + result += QUrl::toAce(d->domain.mid(1)); + } else { + QHostAddress hostAddr(d->domain); + if (hostAddr.protocol() == QAbstractSocket::IPv6Protocol) { + result += '['; + result += d->domain.toUtf8(); + result += ']'; + } else { + result += QUrl::toAce(d->domain); + } } - result += QUrl::toAce(domainNoDot); } if (!d->path.isEmpty()) { result += "; path="; @@ -1015,14 +1023,20 @@ void QNetworkCookie::normalize(const QUrl &url) d->path = defaultPath; } - if (d->domain.isEmpty()) + if (d->domain.isEmpty()) { d->domain = url.host(); - else if (!d->domain.startsWith(QLatin1Char('.'))) - // Ensure the domain starts with a dot if its field was not empty - // in the HTTP header. There are some servers that forget the - // leading dot and this is actually forbidden according to RFC 2109, - // but all browsers accept it anyway so we do that as well. - d->domain.prepend(QLatin1Char('.')); + } else { + QHostAddress hostAddress(d->domain); + if (hostAddress.protocol() != QAbstractSocket::IPv4Protocol + && hostAddress.protocol() != QAbstractSocket::IPv6Protocol + && !d->domain.startsWith(QLatin1Char('.'))) { + // Ensure the domain starts with a dot if its field was not empty + // in the HTTP header. There are some servers that forget the + // leading dot and this is actually forbidden according to RFC 2109, + // but all browsers accept it anyway so we do that as well. + d->domain.prepend(QLatin1Char('.')); + } + } } #ifndef QT_NO_DEBUG_STREAM diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp index ba6f706f7a..faa8464463 100644 --- a/src/network/access/qnetworkreply.cpp +++ b/src/network/access/qnetworkreply.cpp @@ -173,6 +173,21 @@ QNetworkReplyPrivate::QNetworkReplyPrivate() again, but this failed for example because the upload data could not be read a second time. + \value ContentConflictError the request could not be completed due + to a conflict with the current state of the resource. + + \value ContentGoneError the requested resource is no longer + available at the server. + + \value InternalServerError the server encountered an unexpected + condition which prevented it from fulfilling the request. + + \value OperationNotImplementedError the server does not support the + functionality required to fulfill the request. + + \value ServiceUnavailableError the server is unable to handle the + request at this time. + \value ProtocolUnknownError the Network Access API cannot honor the request because the protocol is not known @@ -191,6 +206,9 @@ QNetworkReplyPrivate::QNetworkReplyPrivate() \value ProtocolFailure a breakdown in protocol was detected (parsing error, invalid or unexpected responses, etc.) + \value UnknownServerError an unknown error related to + the server response was detected + \sa error() */ diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h index a7db2d189c..f11a5e816a 100644 --- a/src/network/access/qnetworkreply.h +++ b/src/network/access/qnetworkreply.h @@ -93,12 +93,20 @@ public: ContentNotFoundError, AuthenticationRequiredError, ContentReSendError, + ContentConflictError, + ContentGoneError, UnknownContentError = 299, // protocol errors ProtocolUnknownError = 301, ProtocolInvalidOperationError, - ProtocolFailure = 399 + ProtocolFailure = 399, + + // Server side errors (401-499) + InternalServerError = 401, + OperationNotImplementedError, + ServiceUnavailableError, + UnknownServerError = 499 }; ~QNetworkReply(); diff --git a/src/network/doc/snippets/network/tcpwait.cpp b/src/network/doc/snippets/network/tcpwait.cpp index fb44e2ded9..e5e4c1ed40 100644 --- a/src/network/doc/snippets/network/tcpwait.cpp +++ b/src/network/doc/snippets/network/tcpwait.cpp @@ -55,13 +55,13 @@ int main(int argv, char **args) char buffer[50]; forever { - numRead = socket.read(buffer, 50); + numRead = socket.read(buffer, 50); - // do whatever with array + // do whatever with array - numReadTotal += numRead; + numReadTotal += numRead; if (numRead == 0 && !socket.waitForReadyRead()) - break; + break; } //! [0] diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri index 97f52fdb6e..9b584be206 100644 --- a/src/network/kernel/kernel.pri +++ b/src/network/kernel/kernel.pri @@ -33,9 +33,17 @@ android { } win32: { - HEADERS += kernel/qnetworkinterface_win_p.h - SOURCES += kernel/qdnslookup_win.cpp kernel/qhostinfo_win.cpp kernel/qnetworkinterface_win.cpp - LIBS_PRIVATE += -ldnsapi + !winrt { + HEADERS += kernel/qnetworkinterface_win_p.h + SOURCES += kernel/qdnslookup_win.cpp \ + kernel/qhostinfo_win.cpp \ + kernel/qnetworkinterface_win.cpp + LIBS_PRIVATE += -ldnsapi + } else { + SOURCES += kernel/qdnslookup_winrt.cpp \ + kernel/qhostinfo_winrt.cpp \ + kernel/qnetworkinterface_winrt.cpp + } } integrity:SOURCES += kernel/qdnslookup_unix.cpp kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index 8c16486878..edbbbf5a75 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -55,9 +55,11 @@ #include <qmutex.h> #include <private/qmutexpool_p.h> #include <rpc.h> +#ifndef Q_OS_WINRT #define SECURITY_WIN32 1 #include <security.h> #endif +#endif //#define NTLMV1_CLIENT @@ -69,7 +71,7 @@ QT_BEGIN_NAMESPACE static QByteArray qNtlmPhase1(); static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data); -#ifdef Q_OS_WIN +#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 @@ -328,7 +330,7 @@ bool QAuthenticator::isNull() const return !d; } -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) class QNtlmWindowsHandles { public: @@ -340,7 +342,7 @@ public: QAuthenticatorPrivate::QAuthenticatorPrivate() : method(None) - #ifdef Q_OS_WIN + #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) , ntlmWindowsHandles(0) #endif , hasFailed(false) @@ -354,7 +356,7 @@ QAuthenticatorPrivate::QAuthenticatorPrivate() QAuthenticatorPrivate::~QAuthenticatorPrivate() { -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) if (ntlmWindowsHandles) delete ntlmWindowsHandles; #endif @@ -485,7 +487,7 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet case QAuthenticatorPrivate::Ntlm: methodString = "NTLM "; if (challenge.isEmpty()) { -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) QByteArray phase1Token; if (user.isEmpty()) // Only pull from system if no user was specified in authenticator phase1Token = qNtlmPhase1_SSPI(this); @@ -502,7 +504,7 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet phase = Phase2; } } else { -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) QByteArray phase3Token; if (ntlmWindowsHandles) phase3Token = qNtlmPhase3_SSPI(this, QByteArray::fromBase64(challenge)); @@ -1475,7 +1477,7 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas return rc; } -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) // See http://davenport.sourceforge.net/ntlm.html // and libcurl http_ntlm.c @@ -1513,7 +1515,6 @@ static bool q_NTLM_SSPI_library_load() return true; } -#ifdef Q_OS_WIN // Phase 1: static QByteArray qNtlmPhase1_SSPI(QAuthenticatorPrivate *ctx) { @@ -1631,8 +1632,6 @@ static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray& return result; } -#endif // Q_OS_WIN - -#endif +#endif // Q_OS_WIN && !Q_OS_WINRT QT_END_NAMESPACE diff --git a/src/network/kernel/qdnslookup.cpp b/src/network/kernel/qdnslookup.cpp index 53675d083b..e776a8eb76 100644 --- a/src/network/kernel/qdnslookup.cpp +++ b/src/network/kernel/qdnslookup.cpp @@ -363,6 +363,25 @@ void QDnsLookup::setType(Type type) } /*! + \property QDnsLookup::nameserver + \brief the nameserver to use for DNS lookup. +*/ + +QHostAddress QDnsLookup::nameserver() const +{ + return d_func()->nameserver; +} + +void QDnsLookup::setNameserver(const QHostAddress &nameserver) +{ + Q_D(QDnsLookup); + if (nameserver != d->nameserver) { + d->nameserver = nameserver; + emit nameserverChanged(nameserver); + } +} + +/*! Returns the list of canonical name records associated with this lookup. */ diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h index 1df21d866e..ffbef61f92 100644 --- a/src/network/kernel/qdnslookup.h +++ b/src/network/kernel/qdnslookup.h @@ -180,6 +180,7 @@ class Q_NETWORK_EXPORT QDnsLookup : public QObject Q_PROPERTY(QString errorString READ errorString NOTIFY finished) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged) + Q_PROPERTY(QHostAddress nameserver READ nameserver WRITE setNameserver NOTIFY nameserverChanged) public: enum Error @@ -209,6 +210,7 @@ public: explicit QDnsLookup(QObject *parent = 0); QDnsLookup(Type type, const QString &name, QObject *parent = 0); + QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent = 0); ~QDnsLookup(); Error error() const; @@ -221,6 +223,9 @@ public: Type type() const; void setType(QDnsLookup::Type); + QHostAddress nameserver() const; + void setNameserver(const QHostAddress &nameserver); + QList<QDnsDomainNameRecord> canonicalNameRecords() const; QList<QDnsHostAddressRecord> hostAddressRecords() const; QList<QDnsMailExchangeRecord> mailExchangeRecords() const; @@ -238,6 +243,7 @@ Q_SIGNALS: void finished(); void nameChanged(const QString &name); void typeChanged(Type type); + void nameserverChanged(const QHostAddress &nameserver); private: Q_DECLARE_PRIVATE(QDnsLookup) diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h index 692b9088fe..4bd3bd7603 100644 --- a/src/network/kernel/qdnslookup_p.h +++ b/src/network/kernel/qdnslookup_p.h @@ -100,6 +100,7 @@ public: bool isFinished; QString name; QDnsLookup::Type type; + QHostAddress nameserver; QDnsLookupReply reply; QDnsLookupRunnable *runnable; diff --git a/src/network/kernel/qdnslookup_winrt.cpp b/src/network/kernel/qdnslookup_winrt.cpp new file mode 100644 index 0000000000..a5d16e4b63 --- /dev/null +++ b/src/network/kernel/qdnslookup_winrt.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdnslookup_p.h" + +#include <qurl.h> + +#include <wrl.h> +#include <windows.foundation.h> +#include <windows.foundation.collections.h> +#include <windows.networking.h> +#include <windows.networking.sockets.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Networking; +using namespace ABI::Windows::Networking::Connectivity; +using namespace ABI::Windows::Networking::Sockets; + +QT_BEGIN_NAMESPACE + +void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply) +{ + // TODO: is there any way to do "proper" dns lookup? + if (requestType != QDnsLookup::A && requestType != QDnsLookup::AAAA + && requestType != QDnsLookup::ANY) { + reply->error = QDnsLookup::InvalidRequestError; + reply->errorString = QLatin1String("WinRT only supports IPv4 and IPv6 requests"); + return; + } + + QString aceHostname = QUrl::fromAce(requestName); + if (aceHostname.isEmpty()) { + reply->error = QDnsLookup::InvalidRequestError; + reply->errorString = requestName.isEmpty() ? tr("No hostname given") : tr("Invalid hostname"); + return; + } + + IHostNameFactory *hostnameFactory; + + HStringReference classId(RuntimeClass_Windows_Networking_HostName); + if (FAILED(GetActivationFactory(classId.Get(), &hostnameFactory))) { + reply->error = QDnsLookup::ResolverError; + reply->errorString = QLatin1String("Could not obtain hostname factory"); + return; + } + IHostName *host; + HStringReference hostNameRef((const wchar_t*)aceHostname.utf16()); + hostnameFactory->CreateHostName(hostNameRef.Get(), &host); + hostnameFactory->Release(); + + IDatagramSocketStatics *datagramSocketStatics; + GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &datagramSocketStatics); + + IAsyncOperation<IVectorView<EndpointPair*> *> *op; + HSTRING proto; + WindowsCreateString(L"0", 1, &proto); + datagramSocketStatics->GetEndpointPairsAsync(host, proto, &op); + datagramSocketStatics->Release(); + host->Release(); + + IVectorView<EndpointPair*> *endpointPairs = 0; + HRESULT hr = op->GetResults(&endpointPairs); + int waitCount = 0; + while (hr == E_ILLEGAL_METHOD_CALL) { + WaitForSingleObjectEx(GetCurrentThread(), 50, FALSE); + hr = op->GetResults(&endpointPairs); + if (++waitCount > 1200) // Wait for 1 minute max + return; + } + op->Release(); + + if (!endpointPairs) + return; + + unsigned int size; + endpointPairs->get_Size(&size); + for (unsigned int i = 0; i < size; ++i) { + IEndpointPair *endpointpair; + endpointPairs->GetAt(i, &endpointpair); + IHostName *remoteHost; + endpointpair->get_RemoteHostName(&remoteHost); + endpointpair->Release(); + HostNameType type; + remoteHost->get_Type(&type); + if (type == HostNameType_Bluetooth || type == HostNameType_DomainName + || (requestType != QDnsLookup::ANY + && ((type == HostNameType_Ipv4 && requestType == QDnsLookup::AAAA) + || (type == HostNameType_Ipv6 && requestType == QDnsLookup::A)))) + continue; + + HSTRING name; + remoteHost->get_CanonicalName(&name); + remoteHost->Release(); + UINT32 length; + PCWSTR rawString = WindowsGetStringRawBuffer(name, &length); + QDnsHostAddressRecord record; + record.d->name = aceHostname; + record.d->value = QHostAddress(QString::fromWCharArray(rawString, length)); + reply->hostAddressRecords.append(record); + } +} + +QT_END_NAMESPACE diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp index 18fd6dee58..0ab72191dc 100644 --- a/src/network/kernel/qhostaddress.cpp +++ b/src/network/kernel/qhostaddress.cpp @@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE // sockaddr_in6 size changed between old and new SDK // Only the new version is the correct one, so always // use this structure. -#if defined(Q_OS_WINCE) +#if defined(Q_OS_WINCE) || defined(Q_OS_WINRT) # if !defined(u_char) # define u_char unsigned char # endif @@ -448,10 +448,12 @@ QHostAddress::QHostAddress(const QString &address) QHostAddress::QHostAddress(const struct sockaddr *sockaddr) : d(new QHostAddressPrivate) { +#ifndef Q_OS_WINRT if (sockaddr->sa_family == AF_INET) setAddress(htonl(((sockaddr_in *)sockaddr)->sin_addr.s_addr)); else if (sockaddr->sa_family == AF_INET6) setAddress(((qt_sockaddr_in6 *)sockaddr)->sin6_addr.qt_s6_addr); +#endif } /*! @@ -604,11 +606,13 @@ bool QHostAddress::setAddress(const QString &address) */ void QHostAddress::setAddress(const struct sockaddr *sockaddr) { +#ifndef Q_OS_WINRT clear(); if (sockaddr->sa_family == AF_INET) setAddress(htonl(((sockaddr_in *)sockaddr)->sin_addr.s_addr)); else if (sockaddr->sa_family == AF_INET6) setAddress(((qt_sockaddr_in6 *)sockaddr)->sin6_addr.qt_s6_addr); +#endif } /*! diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 32b7318335..df8c8b145a 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -259,10 +259,10 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) } else if (result == EAI_NONAME || result == EAI_FAIL #ifdef EAI_NODATA - // EAI_NODATA is deprecated in RFC 3493 - || result == EAI_NODATA + // EAI_NODATA is deprecated in RFC 3493 + || result == EAI_NODATA #endif - ) { + ) { results.setError(QHostInfo::HostNotFound); results.setErrorString(tr("Host not found")); } else { diff --git a/src/network/kernel/qhostinfo_winrt.cpp b/src/network/kernel/qhostinfo_winrt.cpp new file mode 100644 index 0000000000..928c9e4628 --- /dev/null +++ b/src/network/kernel/qhostinfo_winrt.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhostinfo_p.h" + +#include <qurl.h> + +#include <ppltasks.h> +#include <wrl.h> +#include <windows.networking.h> +#include <windows.networking.sockets.h> +#include <windows.networking.connectivity.h> +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Networking; +using namespace ABI::Windows::Networking::Connectivity; +using namespace ABI::Windows::Networking::Sockets; + +QT_BEGIN_NAMESPACE + +//#define QHOSTINFO_DEBUG + +QHostInfo QHostInfoAgent::fromName(const QString &hostName) +{ + QHostInfo results; + + QHostAddress address; + if (address.setAddress(hostName)) { + // Reverse lookup + // TODO: is there a replacement for getnameinfo for winrt? + Q_UNIMPLEMENTED(); + return results; + } + + 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; + } + + IHostNameFactory *hostnameFactory; + + HStringReference classId(RuntimeClass_Windows_Networking_HostName); + if (FAILED(GetActivationFactory(classId.Get(), &hostnameFactory))) + Q_ASSERT(false, "Could not obtain hostname factory."); + + IHostName *host; + HStringReference hostNameRef((const wchar_t*)hostName.utf16()); + hostnameFactory->CreateHostName(hostNameRef.Get(), &host); + hostnameFactory->Release(); + + IDatagramSocketStatics *datagramSocketStatics; + GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &datagramSocketStatics); + + IAsyncOperation<IVectorView<EndpointPair*> *> *op; + HSTRING proto; + WindowsCreateString(L"0", 1, &proto); + datagramSocketStatics->GetEndpointPairsAsync(host, proto, &op); + datagramSocketStatics->Release(); + host->Release(); + + IVectorView<EndpointPair*> *endpointPairs = 0; + HRESULT hr = op->GetResults(&endpointPairs); + int waitCount = 0; + while (hr == E_ILLEGAL_METHOD_CALL) { + WaitForSingleObjectEx(GetCurrentThread(), 50, FALSE); + hr = op->GetResults(&endpointPairs); + if (++waitCount > 1200) // Wait for 1 minute max + return results; + } + op->Release(); + + if (!endpointPairs) + return results; + + unsigned int size; + endpointPairs->get_Size(&size); + QList<QHostAddress> addresses; + for (unsigned int i = 0; i < size; ++i) { + IEndpointPair *endpointpair; + endpointPairs->GetAt(i, &endpointpair); + IHostName *remoteHost; + endpointpair->get_RemoteHostName(&remoteHost); + endpointpair->Release(); + if (!remoteHost) + continue; + HostNameType type; + remoteHost->get_Type(&type); + if (type == HostNameType_DomainName) + continue; + + HSTRING name; + remoteHost->get_CanonicalName(&name); + remoteHost->Release(); + UINT32 length; + PCWSTR rawString = WindowsGetStringRawBuffer(name, &length); + QHostAddress addr; + addr.setAddress(QString::fromWCharArray(rawString, length)); + if (!addresses.contains(addr)) + addresses.append(addr); + } + results.setAddresses(addresses); + + return results; +} + +QString QHostInfo::localHostName() +{ + INetworkInformationStatics *statics; + GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &statics); + + IVectorView<HostName*> *hostNames = 0; + statics->GetHostNames(&hostNames); + statics->Release(); + if (!hostNames) + return QString(); + + unsigned int size; + hostNames->get_Size(&size); + if (size == 0) + return QString(); + + for (unsigned int i = 0; i < size; ++i) { + IHostName *hostName; + hostNames->GetAt(i, &hostName); + HostNameType type; + hostName->get_Type(&type); + if (type != HostNameType_DomainName) + continue; + + HSTRING name; + hostName->get_CanonicalName(&name); + hostName->Release(); + UINT32 length; + PCWSTR rawString = WindowsGetStringRawBuffer(name, &length); + return QString::fromWCharArray(rawString, length); + } + IHostName *firstHost; + hostNames->GetAt(0, &firstHost); + hostNames->Release(); + + HSTRING name; + firstHost->get_CanonicalName(&name); + firstHost->Release(); + UINT32 length; + PCWSTR rawString = WindowsGetStringRawBuffer(name, &length); + return QString::fromWCharArray(rawString, length); +} + +// QString QHostInfo::localDomainName() defined in qnetworkinterface_win.cpp + +QT_END_NAMESPACE diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp index d3c830a66f..d0e2eca1e0 100644 --- a/src/network/kernel/qnetworkinterface_unix.cpp +++ b/src/network/kernel/qnetworkinterface_unix.cpp @@ -228,7 +228,7 @@ static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfa memcpy(req.ifr_name, oldName, qMin<int>(oldName.length() + 1, sizeof(req.ifr_name) - 1)); } else #endif - { + { // use this name anyways iface->name = QString::fromLatin1(req.ifr_name); } diff --git a/src/network/kernel/qnetworkinterface_winrt.cpp b/src/network/kernel/qnetworkinterface_winrt.cpp new file mode 100644 index 0000000000..6a814c85d4 --- /dev/null +++ b/src/network/kernel/qnetworkinterface_winrt.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkinterface.h" +#include "qnetworkinterface_p.h" + +#ifndef QT_NO_NETWORKINTERFACE + +#include <wrl.h> +#include <windows.foundation.h> +#include <windows.foundation.collections.h> +#include <windows.networking.h> +#include <windows.networking.connectivity.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Networking; +using namespace ABI::Windows::Networking::Connectivity; + +#include <qhostinfo.h> + +QT_BEGIN_NAMESPACE + +struct HostNameInfo { + GUID adapterId; + unsigned char prefixLength; + QString address; +}; + +static QList<QNetworkInterfacePrivate *> interfaceListing() +{ + QList<QNetworkInterfacePrivate *> interfaces; + + QList<HostNameInfo> hostList; + + INetworkInformationStatics *hostNameStatics; + GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &hostNameStatics); + + IVectorView<HostName*> *hostNames = 0; + hostNameStatics->GetHostNames(&hostNames); + hostNameStatics->Release(); + if (!hostNames) + return interfaces; + + unsigned int hostNameCount; + hostNames->get_Size(&hostNameCount); + for (unsigned i = 0; i < hostNameCount; ++i) { + HostNameInfo hostInfo; + IHostName *hostName; + hostNames->GetAt(i, &hostName); + + HostNameType type; + hostName->get_Type(&type); + if (type == HostNameType_DomainName) + continue; + + IIPInformation *ipInformation; + hostName->get_IPInformation(&ipInformation); + INetworkAdapter *currentAdapter; + ipInformation->get_NetworkAdapter(¤tAdapter); + + currentAdapter->get_NetworkAdapterId(&hostInfo.adapterId); + currentAdapter->Release(); + + IReference<unsigned char> *prefixLengthReference; + ipInformation->get_PrefixLength(&prefixLengthReference); + ipInformation->Release(); + + prefixLengthReference->get_Value(&hostInfo.prefixLength); + prefixLengthReference->Release(); + + // invalid prefixes + if ((type == HostNameType_Ipv4 && hostInfo.prefixLength > 32) + || (type == HostNameType_Ipv6 && hostInfo.prefixLength > 128)) + continue; + + HSTRING name; + hostName->get_CanonicalName(&name); + hostName->Release(); + UINT32 length; + PCWSTR rawString = WindowsGetStringRawBuffer(name, &length); + hostInfo.address = QString::fromWCharArray(rawString, length); + + hostList << hostInfo; + } + hostNames->Release(); + + INetworkInformationStatics *networkInfoStatics; + GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &networkInfoStatics); + IVectorView<ConnectionProfile *> *connectionProfiles = 0; + networkInfoStatics->GetConnectionProfiles(&connectionProfiles); + networkInfoStatics->Release(); + if (!connectionProfiles) + return interfaces; + + unsigned int size; + connectionProfiles->get_Size(&size); + for (unsigned int i = 0; i < size; ++i) { + QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate; + interfaces << iface; + + IConnectionProfile *profile; + connectionProfiles->GetAt(i, &profile); + + NetworkConnectivityLevel connectivityLevel; + profile->GetNetworkConnectivityLevel(&connectivityLevel); + if (connectivityLevel != NetworkConnectivityLevel_None) + iface->flags = QNetworkInterface::IsUp | QNetworkInterface::IsRunning; + + INetworkAdapter *adapter; + profile->get_NetworkAdapter(&adapter); + profile->Release(); + UINT32 type; + adapter->get_IanaInterfaceType(&type); + if (type == 23) + iface->flags |= QNetworkInterface::IsPointToPoint; + GUID id; + adapter->get_NetworkAdapterId(&id); + adapter->Release(); + OLECHAR adapterName[39]={0}; + StringFromGUID2(id, adapterName, 39); + iface->name = QString::fromWCharArray(adapterName); + + // According to http://stackoverflow.com/questions/12936193/how-unique-is-the-ethernet-network-adapter-id-in-winrt-it-is-derived-from-the-m + // obtaining the MAC address using WinRT API is impossible + // iface->hardwareAddress = ? + + for (int i = 0; i < hostList.length(); ++i) { + const HostNameInfo hostInfo = hostList.at(i); + if (id != hostInfo.adapterId) + continue; + + QNetworkAddressEntry entry; + entry.setIp(QHostAddress(hostInfo.address)); + entry.setPrefixLength(hostInfo.prefixLength); + iface->addressEntries << entry; + + hostList.takeAt(i); + --i; + } + } + connectionProfiles->Release(); + return interfaces; +} + +QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan() +{ + return interfaceListing(); +} + +QString QHostInfo::localDomainName() +{ + return QString(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_NETWORKINTERFACE diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index bebdf728a7..0345537d1c 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -372,9 +372,22 @@ IP_MULTICAST_LOOP (multicast loopback) socket option. \value TypeOfServiceOption This option is not supported on - Windows. This maps to the IP_TOS socket option. + Windows. This maps to the IP_TOS socket option. For possible values, + see table below. - Possible values for the \e{TypeOfServiceOption} are: + \value SendBufferSizeSocketOption Sets the socket send buffer size + in bytes at the OS level. This maps to the SO_SNDBUF socket option. + This option does not affect the QIODevice or QAbstractSocket buffers. + This enum value has been introduced in Qt 5.3. + + \value ReceiveBufferSizeSocketOption Sets the socket receive + buffer size in bytes at the OS level. + This maps to the SO_RCVBUF socket option. + This option does not affect the QIODevice or QAbstractSocket buffers + (see \l{QAbstractSocket::}{setReadBufferSize()}). + This enum value has been introduced in Qt 5.3. + + Possible values for \e{TypeOfServiceOption} are: \table \header \li Value \li Description @@ -735,8 +748,8 @@ bool QAbstractSocketPrivate::canReadNotification() return true; } - if (!hasData && socketEngine) - socketEngine->setReadNotificationEnabled(true); + if (isBuffered && socketEngine) + socketEngine->setReadNotificationEnabled(readBufferMaxSize == 0 || readBufferMaxSize > q->bytesAvailable()); // reset the read socket notifier state if we reentered inside the // readyRead() connected slot. @@ -1904,6 +1917,14 @@ void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, cons case TypeOfServiceOption: d_func()->socketEngine->setOption(QAbstractSocketEngine::TypeOfServiceOption, value.toInt()); break; + + case SendBufferSizeSocketOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::SendBufferSocketOption, value.toInt()); + break; + + case ReceiveBufferSizeSocketOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::ReceiveBufferSocketOption, value.toInt()); + break; } } @@ -1938,6 +1959,14 @@ QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption option) case TypeOfServiceOption: ret = d_func()->socketEngine->option(QAbstractSocketEngine::TypeOfServiceOption); break; + + case SendBufferSizeSocketOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::SendBufferSocketOption); + break; + + case ReceiveBufferSizeSocketOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::ReceiveBufferSocketOption); + break; } if (ret == -1) return QVariant(); diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h index 46114abf73..8b019cf0fb 100644 --- a/src/network/socket/qabstractsocket.h +++ b/src/network/socket/qabstractsocket.h @@ -115,7 +115,9 @@ public: KeepAliveOption, // SO_KEEPALIVE MulticastTtlOption, // IP_MULTICAST_TTL MulticastLoopbackOption, // IP_MULTICAST_LOOPBACK - TypeOfServiceOption //IP_TOS + TypeOfServiceOption, //IP_TOS + SendBufferSizeSocketOption, //SO_SNDBUF + ReceiveBufferSizeSocketOption //SO_RCVBUF }; enum BindFlag { DefaultForPlatform = 0x0, diff --git a/src/network/socket/qabstractsocketengine.cpp b/src/network/socket/qabstractsocketengine.cpp index 1275461d7d..d8abe01241 100644 --- a/src/network/socket/qabstractsocketengine.cpp +++ b/src/network/socket/qabstractsocketengine.cpp @@ -41,7 +41,11 @@ #include "qabstractsocketengine_p.h" +#ifndef Q_OS_WINRT #include "qnativesocketengine_p.h" +#else +#include "qnativesocketengine_winrt_p.h" +#endif #include "qmutex.h" #include "qnetworkproxy.h" diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index 1dec96762c..6a30012562 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -150,7 +150,7 @@ public: virtual bool waitForRead(int msecs = 30000, bool *timedOut = 0) = 0; virtual bool waitForWrite(int msecs = 30000, bool *timedOut = 0) = 0; virtual bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, - bool checkRead, bool checkWrite, + bool checkRead, bool checkWrite, int msecs = 30000, bool *timedOut = 0) = 0; QAbstractSocket::SocketError error() const; diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp index f7f8aab182..791227002d 100644 --- a/src/network/socket/qlocalserver.cpp +++ b/src/network/socket/qlocalserver.cpp @@ -131,7 +131,7 @@ QLocalServer::QLocalServer(QObject *parent) QLocalServer::~QLocalServer() { if (isListening()) - close(); + close(); } /*! diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index 97a9b98c30..fc1afa48c9 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -162,8 +162,8 @@ public: bool waitForRead(int msecs = 30000, bool *timedOut = 0); bool waitForWrite(int msecs = 30000, bool *timedOut = 0); bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, - bool checkRead, bool checkWrite, - int msecs = 30000, bool *timedOut = 0); + bool checkRead, bool checkWrite, + int msecs = 30000, bool *timedOut = 0); bool isReadNotificationEnabled() const; void setReadNotificationEnabled(bool enable); @@ -271,7 +271,7 @@ public: qint64 nativeWrite(const char *data, qint64 length); int nativeSelect(int timeout, bool selectForRead) const; int nativeSelect(int timeout, bool checkRead, bool checkWrite, - bool *selectForRead, bool *selectForWrite) const; + bool *selectForRead, bool *selectForWrite) const; #ifdef Q_OS_WIN void setPortAndAddress(sockaddr_in * sockAddrIPv4, qt_sockaddr_in6 * sockAddrIPv6, quint16 port, const QHostAddress & address, sockaddr ** sockAddrPtr, QT_SOCKLEN_T *sockAddrSize); diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index e076f2b4bf..b6035b5500 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -144,7 +144,7 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc int protocol = (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) ? AF_INET6 : AF_INET; int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM; - int socket = qt_safe_socket(protocol, type, 0); + int socket = qt_safe_socket(protocol, type, 0); if (socket <= 0 && socketProtocol == QAbstractSocket::AnyIPProtocol && errno == EAFNOSUPPORT) { protocol = AF_INET; socket = qt_safe_socket(protocol, type, 0); diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 751ac9b182..b1c9073eb9 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -186,7 +186,7 @@ static inline void qt_socket_getPortAndAddress(SOCKET socketDescriptor, const qt *address = a; } if (port) - WSANtohs(socketDescriptor, sa6->sin6_port, port); + WSANtohs(socketDescriptor, sa6->sin6_port, port); } else if (sa->a.sa_family == AF_INET) { @@ -194,11 +194,11 @@ static inline void qt_socket_getPortAndAddress(SOCKET socketDescriptor, const qt unsigned long addr; WSANtohl(socketDescriptor, sa4->sin_addr.s_addr, &addr); QHostAddress a; - a.setAddress(addr); - if (address) - *address = a; + a.setAddress(addr); + if (address) + *address = a; if (port) - WSANtohs(socketDescriptor, sa4->sin_port, port); + WSANtohs(socketDescriptor, sa4->sin_port, port); } } @@ -276,7 +276,7 @@ QWindowsSockInit::QWindowsSockInit() // IPv6 requires Winsock v2.0 or better. if (WSAStartup(MAKEWORD(2,0), &wsadata) != 0) { - qWarning("QTcpSocketAPI: WinSock v2.0 initialization failed."); + qWarning("QTcpSocketAPI: WinSock v2.0 initialization failed."); } else { version = 0x20; } @@ -940,14 +940,14 @@ int QNativeSocketEnginePrivate::nativeAccept() break; } } else if (acceptedDescriptor != -1 && QAbstractEventDispatcher::instance()) { - // Because of WSAAsyncSelect() WSAAccept returns a non blocking socket - // with the same attributes as the listening socket including the current - // WSAAsyncSelect(). To be able to change the socket to blocking mode the - // WSAAsyncSelect() call must be cancled. - QSocketNotifier n(acceptedDescriptor, QSocketNotifier::Read); - n.setEnabled(true); - n.setEnabled(false); - } + // Because of WSAAsyncSelect() WSAAccept returns a non blocking socket + // with the same attributes as the listening socket including the current + // WSAAsyncSelect(). To be able to change the socket to blocking mode the + // WSAAsyncSelect() call must be cancled. + QSocketNotifier n(acceptedDescriptor, QSocketNotifier::Read); + n.setEnabled(true); + n.setEnabled(false); + } #if defined (QNATIVESOCKETENGINE_DEBUG) qDebug("QNativeSocketEnginePrivate::nativeAccept() == %i", acceptedDescriptor); #endif diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp new file mode 100644 index 0000000000..8550e0b0d1 --- /dev/null +++ b/src/network/socket/qnativesocketengine_winrt.cpp @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnativesocketengine_winrt_p.h" + +#include <qnetworkinterface.h> + +QT_BEGIN_NAMESPACE + +QNativeSocketEngine::QNativeSocketEngine(QObject *parent) + : QAbstractSocketEngine(*new QNativeSocketEnginePrivate(), parent) +{ +} + +QNativeSocketEngine::~QNativeSocketEngine() +{ + close(); +} + +bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(type); + Q_UNUSED(protocol); + return false; +} + +bool QNativeSocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(socketDescriptor); + Q_UNUSED(socketState); + return false; +} + +qintptr QNativeSocketEngine::socketDescriptor() const +{ + Q_UNIMPLEMENTED(); + return -1; +} + +bool QNativeSocketEngine::isValid() const +{ + Q_UNIMPLEMENTED(); + return false; +} + +bool QNativeSocketEngine::connectToHost(const QHostAddress &address, quint16 port) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(address); + Q_UNUSED(port); + return false; +} + +bool QNativeSocketEngine::connectToHostByName(const QString &name, quint16 port) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(name); + Q_UNUSED(port); + return false; +} + +bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(address); + Q_UNUSED(port); + return false; +} + +bool QNativeSocketEngine::listen() +{ + Q_UNIMPLEMENTED(); + return false; +} + +int QNativeSocketEngine::accept() +{ + Q_UNIMPLEMENTED(); + return -1; +} + +void QNativeSocketEngine::close() +{ +} + +bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(groupAddress); + Q_UNUSED(iface); + return false; +} + +bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(groupAddress); + Q_UNUSED(iface); + return false; +} + +QNetworkInterface QNativeSocketEngine::multicastInterface() const +{ + Q_UNIMPLEMENTED(); + return QNetworkInterface(); +} + +bool QNativeSocketEngine::setMulticastInterface(const QNetworkInterface &iface) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(iface); + return false; +} + +qint64 QNativeSocketEngine::bytesAvailable() const +{ + Q_UNIMPLEMENTED(); + return -1; +} + +qint64 QNativeSocketEngine::read(char *data, qint64 maxlen) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(data); + Q_UNUSED(maxlen); + return -1; +} + +qint64 QNativeSocketEngine::write(const char *data, qint64 len) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(data); + Q_UNUSED(len); + return -1; +} + +qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress *addr, quint16 *port) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(data); + Q_UNUSED(maxlen); + Q_UNUSED(addr); + Q_UNUSED(port); + return -1; +} + +qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QHostAddress &addr, quint16 port) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(data); + Q_UNUSED(len); + Q_UNUSED(addr); + Q_UNUSED(port); + return -1; +} + +bool QNativeSocketEngine::hasPendingDatagrams() const +{ + Q_UNIMPLEMENTED(); + return false; +} + +qint64 QNativeSocketEngine::pendingDatagramSize() const +{ + Q_UNIMPLEMENTED(); + return 0; +} + +qint64 QNativeSocketEngine::bytesToWrite() const +{ + Q_UNIMPLEMENTED(); + return 0; +} + +qint64 QNativeSocketEngine::receiveBufferSize() const +{ + Q_UNIMPLEMENTED(); + return 0; +} + +void QNativeSocketEngine::setReceiveBufferSize(qint64 bufferSize) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(bufferSize); +} + +qint64 QNativeSocketEngine::sendBufferSize() const +{ + Q_UNIMPLEMENTED(); + return 0; +} + +void QNativeSocketEngine::setSendBufferSize(qint64 bufferSize) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(bufferSize); +} + +int QNativeSocketEngine::option(QAbstractSocketEngine::SocketOption option) const +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(option); + return -1; +} + +bool QNativeSocketEngine::setOption(QAbstractSocketEngine::SocketOption option, int value) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(option); + Q_UNUSED(value); + return false; +} + +bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(msecs); + Q_UNUSED(timedOut); + return false; +} + +bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(msecs); + Q_UNUSED(timedOut); + return false; +} + +bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, int msecs, bool *timedOut) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(readyToRead); + Q_UNUSED(readyToWrite); + Q_UNUSED(checkRead); + Q_UNUSED(checkWrite); + Q_UNUSED(msecs); + Q_UNUSED(timedOut); + return false; +} + +bool QNativeSocketEngine::isReadNotificationEnabled() const +{ + Q_UNIMPLEMENTED(); + return false; +} + +void QNativeSocketEngine::setReadNotificationEnabled(bool enable) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(enable); +} + +bool QNativeSocketEngine::isWriteNotificationEnabled() const +{ + Q_UNIMPLEMENTED(); + return false; +} + +void QNativeSocketEngine::setWriteNotificationEnabled(bool enable) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(enable); +} + +bool QNativeSocketEngine::isExceptionNotificationEnabled() const +{ + Q_UNIMPLEMENTED(); + return false; +} + +void QNativeSocketEngine::setExceptionNotificationEnabled(bool enable) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(enable); +} + +QNativeSocketEnginePrivate::QNativeSocketEnginePrivate() + : QAbstractSocketEnginePrivate() +{ +} + +QNativeSocketEnginePrivate::~QNativeSocketEnginePrivate() +{ +} + +QT_END_NAMESPACE diff --git a/src/network/socket/qnativesocketengine_winrt_p.h b/src/network/socket/qnativesocketengine_winrt_p.h new file mode 100644 index 0000000000..47ba3ecf91 --- /dev/null +++ b/src/network/socket/qnativesocketengine_winrt_p.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNATIVESOCKETENGINE_WINRT_P_H +#define QNATIVESOCKETENGINE_WINRT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// +#include "QtNetwork/qhostaddress.h" +#include "private/qabstractsocketengine_p.h" +#include <wrl.h> +#include <windows.networking.sockets.h> + +QT_BEGIN_NAMESPACE + +class QNativeSocketEnginePrivate; + +class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine +{ + Q_OBJECT +public: + QNativeSocketEngine(QObject *parent = 0); + ~QNativeSocketEngine(); + + bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol); + bool initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState = QAbstractSocket::ConnectedState); + + qintptr socketDescriptor() const; + + bool isValid() const; + + bool connectToHost(const QHostAddress &address, quint16 port); + bool connectToHostByName(const QString &name, quint16 port); + bool bind(const QHostAddress &address, quint16 port); + bool listen(); + int accept(); + void close(); + +#ifndef QT_NO_NETWORKINTERFACE + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + QNetworkInterface multicastInterface() const; + bool setMulticastInterface(const QNetworkInterface &iface); +#endif + + qint64 bytesAvailable() const; + + qint64 read(char *data, qint64 maxlen); + qint64 write(const char *data, qint64 len); + + qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, + quint16 *port = 0); + qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, + quint16 port); + bool hasPendingDatagrams() const; + qint64 pendingDatagramSize() const; + + qint64 bytesToWrite() const; + + qint64 receiveBufferSize() const; + void setReceiveBufferSize(qint64 bufferSize); + + qint64 sendBufferSize() const; + void setSendBufferSize(qint64 bufferSize); + + int option(SocketOption option) const; + bool setOption(SocketOption option, int value); + + bool waitForRead(int msecs = 30000, bool *timedOut = 0); + bool waitForWrite(int msecs = 30000, bool *timedOut = 0); + bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, + bool checkRead, bool checkWrite, + int msecs = 30000, bool *timedOut = 0); + + bool isReadNotificationEnabled() const; + void setReadNotificationEnabled(bool enable); + bool isWriteNotificationEnabled() const; + void setWriteNotificationEnabled(bool enable); + bool isExceptionNotificationEnabled() const; + void setExceptionNotificationEnabled(bool enable); + +private: + Q_DECLARE_PRIVATE(QNativeSocketEngine) + Q_DISABLE_COPY(QNativeSocketEngine) +}; + +class QNativeSocketEnginePrivate : public QAbstractSocketEnginePrivate +{ + Q_DECLARE_PUBLIC(QNativeSocketEngine) +public: + QNativeSocketEnginePrivate(); + ~QNativeSocketEnginePrivate(); +}; + +QT_END_NAMESPACE + +#endif // QNATIVESOCKETENGINE_WINRT_P_H diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 6818ff6354..b62c4a6bef 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -735,9 +735,10 @@ void QSocks5SocketEnginePrivate::reauthenticate() proxyInfo.setPassword(auth.password()); data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password()); - data->controlSocket->blockSignals(true); - data->controlSocket->abort(); - data->controlSocket->blockSignals(false); + { + const QSignalBlocker blocker(data->controlSocket); + data->controlSocket->abort(); + } data->controlSocket->connectToHost(proxyInfo.hostName(), proxyInfo.port()); } else { // authentication failure diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri index c0c6d750d9..7e3a54e303 100644 --- a/src/network/socket/socket.pri +++ b/src/network/socket/socket.pri @@ -24,8 +24,10 @@ SOURCES += socket/qabstractsocketengine.cpp \ socket/qlocalsocket.cpp \ socket/qlocalserver.cpp -SOURCES += socket/qnativesocketengine.cpp -HEADERS += socket/qnativesocketengine_p.h +!winrt { + SOURCES += socket/qnativesocketengine.cpp + HEADERS += socket/qnativesocketengine_p.h +} unix: { SOURCES += socket/qnativesocketengine_unix.cpp \ @@ -36,11 +38,20 @@ unix: { unix:HEADERS += \ socket/qnet_unix_p.h -win32:SOURCES += socket/qnativesocketengine_win.cpp \ +win32:!winrt:SOURCES += socket/qnativesocketengine_win.cpp \ socket/qlocalsocket_win.cpp \ socket/qlocalserver_win.cpp -win32:!wince*: LIBS_PRIVATE += -ladvapi32 +win32:!wince*:!winrt:LIBS_PRIVATE += -ladvapi32 + +winrt { + SOURCES += socket/qnativesocketengine_winrt.cpp \ + socket/qlocalsocket_tcp.cpp \ + socket/qlocalserver_tcp.cpp + HEADERS += socket/qnativesocketengine_winrt_p.h + + DEFINES += QT_LOCALSOCKET_TCP +} wince*: { SOURCES -= socket/qlocalsocket_win.cpp \ diff --git a/src/network/ssl/qsslcipher.cpp b/src/network/ssl/qsslcipher.cpp index cdb0ed9063..bb5d93e528 100644 --- a/src/network/ssl/qsslcipher.cpp +++ b/src/network/ssl/qsslcipher.cpp @@ -79,6 +79,26 @@ QSslCipher::QSslCipher() /*! Constructs a QSslCipher object for the cipher determined by \a + name. The constructor accepts only supported ciphers (i.e., the + \a name must identify a cipher in the list of ciphers returned by + QSslSocket::supportedCiphers()). + + You can call isNull() after construction to check if \a name + correctly identified a supported cipher. +*/ +QSslCipher::QSslCipher(const QString &name) + : d(new QSslCipherPrivate) +{ + foreach (const QSslCipher &cipher, QSslSocket::supportedCiphers()) { + if (cipher.name() == name) { + *this = cipher; + return; + } + } +} + +/*! + Constructs a QSslCipher object for the cipher determined by \a name and \a protocol. The constructor accepts only supported ciphers (i.e., the \a name and \a protocol must identify a cipher in the list of ciphers returned by diff --git a/src/network/ssl/qsslcipher.h b/src/network/ssl/qsslcipher.h index e351d7949b..4cebffa7ae 100644 --- a/src/network/ssl/qsslcipher.h +++ b/src/network/ssl/qsslcipher.h @@ -57,6 +57,7 @@ class Q_NETWORK_EXPORT QSslCipher { public: QSslCipher(); + QSslCipher(const QString &name); QSslCipher(const QString &name, QSsl::SslProtocol protocol); QSslCipher(const QSslCipher &other); ~QSslCipher(); diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index 4aad7c04c5..1e859ae6e6 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -52,6 +53,9 @@ const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOp |QSsl::SslOptionDisableCompression |QSsl::SslOptionDisableSessionPersistence; +const char QSslConfiguration::NextProtocolSpdy3_0[] = "spdy/3"; +const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1"; + /*! \class QSslConfiguration \brief The QSslConfiguration class holds the configuration and state of an SSL connection @@ -113,6 +117,33 @@ const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOp */ /*! + \enum QSslConfiguration::NextProtocolNegotiationStatus + + Describes the status of the Next Protocol Negotiation (NPN). + + \value NextProtocolNegotiationNone No application protocol + has been negotiated (yet). + + \value NextProtocolNegotiationNegotiated A next protocol + has been negotiated (see nextNegotiatedProtocol()). + + \value NextProtocolNegotiationUnsupported The client and + server could not agree on a common next application protocol. +*/ + +/*! + \variable QSslConfiguration::NextProtocolSpdy3_0 + \brief The value used for negotiating SPDY 3.0 during the Next + Protocol Negotiation. +*/ + +/*! + \variable QSslConfiguration::NextProtocolHttp1_1 + \brief The value used for negotiating HTTP 1.1 during the Next + Protocol Negotiation. +*/ + +/*! Constructs an empty SSL configuration. This configuration contains no valid settings and the state will be empty. isNull() will return true after this constructor is called. @@ -185,7 +216,10 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const d->allowRootCertOnDemandLoading == other.d->allowRootCertOnDemandLoading && d->sslOptions == other.d->sslOptions && d->sslSession == other.d->sslSession && - d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint; + d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint && + d->nextAllowedProtocols == other.d->nextAllowedProtocols && + d->nextNegotiatedProtocol == other.d->nextNegotiatedProtocol && + d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus; } /*! @@ -221,7 +255,10 @@ bool QSslConfiguration::isNull() const d->peerCertificateChain.count() == 0 && d->sslOptions == QSslConfigurationPrivate::defaultSslOptions && d->sslSession.isNull() && - d->sslSessionTicketLifeTimeHint == -1); + d->sslSessionTicketLifeTimeHint == -1 && + d->nextAllowedProtocols.isEmpty() && + d->nextNegotiatedProtocol.isNull() && + d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone); } /*! @@ -653,6 +690,71 @@ int QSslConfiguration::sessionTicketLifeTimeHint() const } /*! + \since 5.3 + + This function returns the protocol negotiated with the server + if the Next Protocol Negotiation (NPN) TLS extension was enabled. + In order for the NPN extension to be enabled, setAllowedNextProtocols() + needs to be called explicitly before connecting to the server. + + If no protocol could be negotiated or the extension was not enabled, + this function returns a QByteArray which is null. + + \sa setAllowedNextProtocols(), nextProtocolNegotiationStatus() + */ +QByteArray QSslConfiguration::nextNegotiatedProtocol() const +{ + return d->nextNegotiatedProtocol; +} + +/*! + \since 5.3 + + This function sets the allowed \a protocols to be negotiated with the + server through the Next Protocol Negotiation (NPN) TLS extension; each + element in \a protocols must define one allowed protocol. + The function must be called explicitly before connecting to send the NPN + extension in the SSL handshake. + Whether or not the negotiation succeeded can be queried through + nextProtocolNegotiationStatus(). + + \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), allowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1 + */ +void QSslConfiguration::setAllowedNextProtocols(QList<QByteArray> protocols) +{ + d->nextAllowedProtocols = protocols; +} + +/*! + \since 5.3 + + This function returns the allowed protocols to be negotiated with the + server through the Next Protocol Negotiation (NPN) TLS extension, as set + by setAllowedNextProtocols(). + + \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1 + */ +QList<QByteArray> QSslConfiguration::allowedNextProtocols() const +{ + return d->nextAllowedProtocols; +} + +/*! + \since 5.3 + + This function returns the status of the Next Protocol Negotiation (NPN). + If the feature has not been enabled through setAllowedNextProtocols(), + this function returns NextProtocolNegotiationNone. + The status will be set before emitting the encrypted() signal. + + \sa setAllowedNextProtocols(), allowedNextProtocols(), nextNegotiatedProtocol(), QSslConfiguration::NextProtocolNegotiationStatus + */ +QSslConfiguration::NextProtocolNegotiationStatus QSslConfiguration::nextProtocolNegotiationStatus() const +{ + return d->nextProtocolNegotiationStatus; +} + +/*! Returns the default SSL configuration to be used in new SSL connections. @@ -663,7 +765,7 @@ int QSslConfiguration::sessionTicketLifeTimeHint() const \li protocol SecureProtocols (meaning either TLS 1.0 or SSL 3 will be used) \li the system's default CA certificate list \li the cipher list equal to the list of the SSL libraries' - supported SSL ciphers + supported SSL ciphers that are 128 bits or more \endlist \sa QSslSocket::supportedCiphers(), setDefaultConfiguration() diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index a48eceb63e..587187ca06 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -131,6 +132,21 @@ public: static QSslConfiguration defaultConfiguration(); static void setDefaultConfiguration(const QSslConfiguration &configuration); + enum NextProtocolNegotiationStatus { + NextProtocolNegotiationNone, + NextProtocolNegotiationNegotiated, + NextProtocolNegotiationUnsupported + }; + + void setAllowedNextProtocols(QList<QByteArray> protocols); + QList<QByteArray> allowedNextProtocols() const; + + QByteArray nextNegotiatedProtocol() const; + NextProtocolNegotiationStatus nextProtocolNegotiationStatus() const; + + static const char NextProtocolSpdy3_0[]; + static const char NextProtocolHttp1_1[]; + private: friend class QSslSocket; friend class QSslConfigurationPrivate; diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h index 71ee8d2bfe..d183c3335c 100644 --- a/src/network/ssl/qsslconfiguration_p.h +++ b/src/network/ssl/qsslconfiguration_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -86,7 +87,8 @@ public: allowRootCertOnDemandLoading(true), peerSessionShared(false), sslOptions(QSslConfigurationPrivate::defaultSslOptions), - sslSessionTicketLifeTimeHint(-1) + sslSessionTicketLifeTimeHint(-1), + nextProtocolNegotiationStatus(QSslConfiguration::NextProtocolNegotiationNone) { } QSslCertificate peerCertificate; @@ -114,6 +116,10 @@ public: QByteArray sslSession; int sslSessionTicketLifeTimeHint; + QList<QByteArray> nextAllowedProtocols; + QByteArray nextNegotiatedProtocol; + QSslConfiguration::NextProtocolNegotiationStatus nextProtocolNegotiationStatus; + // in qsslsocket.cpp: static QSslConfiguration defaultConfiguration(); static void setDefaultConfiguration(const QSslConfiguration &configuration); diff --git a/src/network/ssl/qsslcontext.cpp b/src/network/ssl/qsslcontext.cpp index adf42fb79a..551804ec79 100644 --- a/src/network/ssl/qsslcontext.cpp +++ b/src/network/ssl/qsslcontext.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -263,6 +264,45 @@ init_context: return sslContext; } +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) + +static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) +{ + QSslContext::NPNContext *ctx = reinterpret_cast<QSslContext::NPNContext *>(arg); + + // comment out to debug: +// QList<QByteArray> supportedVersions; +// for (unsigned int i = 0; i < inlen; ) { +// QByteArray version(reinterpret_cast<const char *>(&in[i+1]), in[i]); +// supportedVersions << version; +// i += in[i] + 1; +// } + + int proto = q_SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len); + switch (proto) { + case OPENSSL_NPN_UNSUPPORTED: + ctx->status = QSslConfiguration::NextProtocolNegotiationNone; + break; + case OPENSSL_NPN_NEGOTIATED: + ctx->status = QSslConfiguration::NextProtocolNegotiationNegotiated; + break; + case OPENSSL_NPN_NO_OVERLAP: + ctx->status = QSslConfiguration::NextProtocolNegotiationUnsupported; + break; + default: + qWarning("OpenSSL sent unknown NPN status"); + } + + return SSL_TLSEXT_ERR_OK; +} + +QSslContext::NPNContext QSslContext::npnContext() const +{ + return m_npnContext; +} +#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... + // Needs to be deleted by caller SSL* QSslContext::createSsl() { @@ -283,6 +323,26 @@ SSL* QSslContext::createSsl() session = 0; } } + +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) + QList<QByteArray> protocols = sslConfiguration.d->nextAllowedProtocols; + if (!protocols.isEmpty()) { + m_supportedNPNVersions.clear(); + for (int a = 0; a < protocols.count(); ++a) { + if (protocols.at(a).size() > 255) { + qWarning() << "TLS NPN extension" << protocols.at(a) + << "is too long and will be truncated to 255 characters."; + protocols[a] = protocols.at(a).left(255); + } + m_supportedNPNVersions.append(protocols.at(a).size()).append(protocols.at(a)); + } + m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data()); + m_npnContext.len = m_supportedNPNVersions.count(); + m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone; + q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext); + } +#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... + return ssl; } diff --git a/src/network/ssl/qsslcontext_p.h b/src/network/ssl/qsslcontext_p.h index 2b596798a6..20b27c1ce7 100644 --- a/src/network/ssl/qsslcontext_p.h +++ b/src/network/ssl/qsslcontext_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -72,6 +73,21 @@ public: QByteArray sessionASN1() const; void setSessionASN1(const QByteArray &sessionASN1); int sessionTicketLifeTimeHint() const; + +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) + // must be public because we want to use it from an OpenSSL callback + struct NPNContext { + NPNContext() : data(0), + len(0), + status(QSslConfiguration::NextProtocolNegotiationNone) + { } + unsigned char *data; + unsigned short len; + QSslConfiguration::NextProtocolNegotiationStatus status; + }; + NPNContext npnContext() const; +#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... + protected: QSslContext(); @@ -84,6 +100,10 @@ private: QSslError::SslError errorCode; QString errorStr; QSslConfiguration sslConfiguration; +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) + QByteArray m_supportedNPNVersions; + NPNContext m_npnContext; +#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... }; #endif // QT_NO_SSL diff --git a/src/network/ssl/qsslkey.cpp b/src/network/ssl/qsslkey.cpp index cf62f44855..95eed6e4b3 100644 --- a/src/network/ssl/qsslkey.cpp +++ b/src/network/ssl/qsslkey.cpp @@ -256,7 +256,7 @@ QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm, a valid key. */ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding, - QSsl::KeyType type, const QByteArray &passPhrase) + QSsl::KeyType type, const QByteArray &passPhrase) : d(new QSslKeyPrivate) { QByteArray encoded; diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 38b493a769..6edf4efae0 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -905,6 +906,9 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration) d->configuration.sslOptions = configuration.d->sslOptions; d->configuration.sslSession = configuration.sessionTicket(); d->configuration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint(); + d->configuration.nextAllowedProtocols = configuration.allowedNextProtocols(); + d->configuration.nextNegotiatedProtocol = configuration.nextNegotiatedProtocol(); + d->configuration.nextProtocolNegotiationStatus = configuration.nextProtocolNegotiationStatus(); // if the CA certificates were set explicitly (either via // QSslConfiguration::setCaCertificates() or QSslSocket::setCaCertificates(), @@ -1195,12 +1199,9 @@ void QSslSocket::setCiphers(const QString &ciphers) Q_D(QSslSocket); d->configuration.ciphers.clear(); foreach (const QString &cipherName, ciphers.split(QLatin1String(":"),QString::SkipEmptyParts)) { - for (int i = 0; i < 3; ++i) { - // ### Crude - QSslCipher cipher(cipherName, QSsl::SslProtocol(i)); - if (!cipher.isNull()) - d->configuration.ciphers << cipher; - } + QSslCipher cipher(cipherName); + if (!cipher.isNull()) + d->configuration.ciphers << cipher; } } @@ -1953,6 +1954,7 @@ void QSslSocketPrivate::init() */ QList<QSslCipher> QSslSocketPrivate::defaultCiphers() { + QSslSocketPrivate::ensureInitialized(); QMutexLocker locker(&globalData()->mutex); return globalData()->config->ciphers; } diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 69b9e53884..3421154114 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -596,6 +596,7 @@ void QSslSocketPrivate::resetDefaultCiphers() SSL *mySsl = q_SSL_new(myCtx); QList<QSslCipher> ciphers; + QList<QSslCipher> defaultCiphers; STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(mySsl); for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) { @@ -603,8 +604,11 @@ void QSslSocketPrivate::resetDefaultCiphers() if (cipher->valid) { QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher); if (!ciph.isNull()) { + // Unconditionally exclude ADH ciphers since they offer no MITM protection if (!ciph.name().toLower().startsWith(QLatin1String("adh"))) ciphers << ciph; + if (ciph.usedBits() >= 128) + defaultCiphers << ciph; } } } @@ -614,7 +618,7 @@ void QSslSocketPrivate::resetDefaultCiphers() q_SSL_free(mySsl); setDefaultSupportedCiphers(ciphers); - setDefaultCiphers(ciphers); + setDefaultCiphers(defaultCiphers); } QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() @@ -1482,6 +1486,15 @@ void QSslSocketBackendPrivate::continueHandshake() } } +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) + const unsigned char *proto; + unsigned int proto_len; + q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len); + QByteArray nextProtocol(reinterpret_cast<const char *>(proto), proto_len); + configuration.nextNegotiatedProtocol = nextProtocol; + configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status; +#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... + connectionEncrypted = true; emit q->encrypted(); if (autoStartHandshake && pendingClose) { diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index ddf53f18f4..79bce22b0d 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -346,6 +347,20 @@ DEFINEFUNC(long, SSLeay, void, DUMMYARG, return 0, return) DEFINEFUNC(const char *, SSLeay_version, int a, a, return 0, return) DEFINEFUNC2(int, i2d_SSL_SESSION, SSL_SESSION *in, in, unsigned char **pp, pp, return 0, return) DEFINEFUNC3(SSL_SESSION *, d2i_SSL_SESSION, SSL_SESSION **a, a, const unsigned char **pp, pp, long length, length, return 0, return) +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) +DEFINEFUNC6(int, SSL_select_next_proto, unsigned char **out, out, unsigned char *outlen, outlen, + const unsigned char *in, in, unsigned int inlen, inlen, + const unsigned char *client, client, unsigned int client_len, client_len, + return -1, return) +DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s, + int (*cb) (SSL *ssl, unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, void *arg), cb, + void *arg, arg, return, DUMMYARG) +DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s, + const unsigned char **data, data, unsigned *len, len, return, DUMMYARG) +#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... #define RESOLVEFUNC(func) \ if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \ @@ -815,6 +830,11 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSLeay_version) RESOLVEFUNC(i2d_SSL_SESSION) RESOLVEFUNC(d2i_SSL_SESSION) +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) + RESOLVEFUNC(SSL_select_next_proto) + RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb) + RESOLVEFUNC(SSL_get0_next_proto_negotiated) +#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... symbolsResolved = true; delete libs.first; diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 7e1a1c983c..500fe9493b 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -79,13 +80,13 @@ QT_BEGIN_NAMESPACE // **************** Shared declarations ****************** // ret func(arg) -# define DEFINEFUNC(ret, func, arg, a, err, funcret) \ - typedef ret (*_q_PTR_##func)(arg); \ - static _q_PTR_##func _q_##func = 0; \ - ret q_##func(arg) { \ +# define DEFINEFUNC(ret, func, arg, a, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg) { \ if (Q_UNLIKELY(!_q_##func)) { \ qsslSocketUnresolvedSymbolWarning(#func); \ - err; \ + err; \ } \ funcret _q_##func(a); \ } @@ -180,8 +181,8 @@ QT_BEGIN_NAMESPACE // **************** Static declarations ****************** // ret func(arg) -# define DEFINEFUNC(ret, func, arg, a, err, funcret) \ - ret q_##func(arg) { funcret func(a); } +# define DEFINEFUNC(ret, func, arg, a, err, funcret) \ + ret q_##func(arg) { funcret func(a); } // ret func(arg1, arg2) # define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \ @@ -384,7 +385,7 @@ int q_X509_cmp(X509 *a, X509 *b); #ifdef SSLEAY_MACROS void *q_ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, char *x); #define q_X509_dup(x509) (X509 *)q_ASN1_dup((i2d_of_void *)q_i2d_X509, \ - (d2i_of_void *)q_d2i_X509,(char *)x509) + (d2i_of_void *)q_d2i_X509,(char *)x509) #else X509 *q_X509_dup(X509 *a); #endif @@ -429,22 +430,22 @@ STACK_OF(X509) *q_X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx); #define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp) #define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL) #ifdef SSLEAY_MACROS -int q_i2d_DSAPrivateKey(const DSA *a, unsigned char **pp); -int q_i2d_RSAPrivateKey(const RSA *a, unsigned char **pp); +int q_i2d_DSAPrivateKey(const DSA *a, unsigned char **pp); +int q_i2d_RSAPrivateKey(const RSA *a, unsigned char **pp); RSA *q_d2i_RSAPrivateKey(RSA **a, unsigned char **pp, long length); DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length); -#define q_PEM_read_bio_RSAPrivateKey(bp, x, cb, u) \ +#define q_PEM_read_bio_RSAPrivateKey(bp, x, cb, u) \ (RSA *)q_PEM_ASN1_read_bio( \ (void *(*)(void**, const unsigned char**, long int))q_d2i_RSAPrivateKey, PEM_STRING_RSA, bp, (void **)x, cb, u) -#define q_PEM_read_bio_DSAPrivateKey(bp, x, cb, u) \ +#define q_PEM_read_bio_DSAPrivateKey(bp, x, cb, u) \ (DSA *)q_PEM_ASN1_read_bio( \ (void *(*)(void**, const unsigned char**, long int))q_d2i_DSAPrivateKey, PEM_STRING_DSA, bp, (void **)x, cb, u) -#define q_PEM_write_bio_RSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ - PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_RSAPrivateKey,PEM_STRING_RSA,\ - bp,(char *)x,enc,kstr,klen,cb,u) -#define q_PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ - PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\ - bp,(char *)x,enc,kstr,klen,cb,u) +#define q_PEM_write_bio_RSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_RSAPrivateKey,PEM_STRING_RSA,\ + bp,(char *)x,enc,kstr,klen,cb,u) +#define q_PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\ + bp,(char *)x,enc,kstr,klen,cb,u) #endif #define q_SSL_CTX_set_options(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL) #define q_SSL_CTX_set_mode(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,(op),NULL) @@ -461,9 +462,9 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length); #define q_X509_get_notAfter(x) X509_get_notAfter(x) #define q_X509_get_notBefore(x) X509_get_notBefore(x) #define q_EVP_PKEY_assign_RSA(pkey,rsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\ - (char *)(rsa)) + (char *)(rsa)) #define q_EVP_PKEY_assign_DSA(pkey,dsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\ - (char *)(dsa)) + (char *)(dsa)) #define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_conf() void q_OPENSSL_add_all_algorithms_noconf(); void q_OPENSSL_add_all_algorithms_conf(); @@ -473,6 +474,20 @@ const char *q_SSLeay_version(int type); int q_i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp); SSL_SESSION *q_d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, long length); +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) +int q_SSL_select_next_proto(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + const unsigned char *client, unsigned int client_len); +void q_SSL_CTX_set_next_proto_select_cb(SSL_CTX *s, + int (*cb) (SSL *ssl, unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, void *arg), + void *arg); +void q_SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data, + unsigned *len); +#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... + // Helper function class QDateTime; QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime); |