diff options
author | Liang Qi <liang.qi@theqtcompany.com> | 2015-09-25 14:02:04 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@theqtcompany.com> | 2015-09-25 14:02:04 +0200 |
commit | a1ad9a74ebb3c556c5f70f7e03be68b09598ac53 (patch) | |
tree | 615a96db418219a57a745a5899e39a9ac90744ec /src/network | |
parent | 6d78b7a0c46ea04f4bb771d960e2f7dff1362341 (diff) | |
parent | 462f355e4fb16cc7a1838fa2dda0f763eee58c84 (diff) |
Merge remote-tracking branch 'origin/5.6' into dev
Conflicts:
src/corelib/io/io.pri
src/corelib/io/qdatastream.cpp
src/corelib/io/qdatastream.h
src/network/socket/qabstractsocket.cpp
src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp
src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h
src/widgets/styles/qgtkstyle.cpp
tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-cache/qmimedatabase-cache.pro
tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-xml/qmimedatabase-xml.pro
tests/auto/dbus/qdbusconnection/qdbusconnection.pro
tests/auto/dbus/qdbuspendingcall/tst_qdbuspendingcall.cpp
tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
Change-Id: I347549a024eb5bfa986699e0a11f96cc55c797a7
Diffstat (limited to 'src/network')
41 files changed, 1077 insertions, 1132 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 16ed67fbc0..c2d986ef3d 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -971,7 +971,6 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() for (int i = 0; i < channelCount; ++i) { if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) { channels[i].resendCurrent = false; - channels[i].state = QHttpNetworkConnectionChannel::IdleState; // if this is not possible, error will be emitted and connection terminated if (!channels[i].resetUploadData()) diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 98247b7df0..8980ed7a41 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -58,6 +58,11 @@ QT_BEGIN_NAMESPACE // TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp +// Because in-flight when sending a request, the server might close our connection (because the persistent HTTP +// connection times out) +// We use 3 because we can get a _q_error 3 times depending on the timing: +static const int reconnectAttemptsDefault = 3; + QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel() : socket(0) , ssl(false) @@ -69,7 +74,7 @@ QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel() , resendCurrent(false) , lastStatus(0) , pendingEncrypt(false) - , reconnectAttempts(2) + , reconnectAttempts(reconnectAttemptsDefault) , authMethod(QAuthenticatorPrivate::None) , proxyAuthMethod(QAuthenticatorPrivate::None) , authenticationCredentialsSent(false) @@ -106,19 +111,18 @@ void QHttpNetworkConnectionChannel::init() socket->setProxy(QNetworkProxy::NoProxy); #endif - // We want all signals (except the interactive ones) be connected as QueuedConnection - // because else we're falling into cases where we recurse back into the socket code - // and mess up the state. Always going to the event loop (and expecting that when reading/writing) - // is safer. + // After some back and forth in all the last years, this is now a DirectConnection because otherwise + // the state inside the *Socket classes gets messed up, also in conjunction with the socket notifiers + // which behave slightly differently on Windows vs Linux QObject::connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_bytesWritten(qint64)), - Qt::QueuedConnection); + Qt::DirectConnection); QObject::connect(socket, SIGNAL(connected()), this, SLOT(_q_connected()), - Qt::QueuedConnection); + Qt::DirectConnection); QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(_q_readyRead()), - Qt::QueuedConnection); + Qt::DirectConnection); // The disconnected() and error() signals may already come // while calling connectToHost(). @@ -129,10 +133,10 @@ void QHttpNetworkConnectionChannel::init() qRegisterMetaType<QAbstractSocket::SocketError>(); QObject::connect(socket, SIGNAL(disconnected()), this, SLOT(_q_disconnected()), - Qt::QueuedConnection); + Qt::DirectConnection); QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(_q_error(QAbstractSocket::SocketError)), - Qt::QueuedConnection); + Qt::DirectConnection); #ifndef QT_NO_NETWORKPROXY @@ -147,7 +151,7 @@ void QHttpNetworkConnectionChannel::init() // won't be a sslSocket if encrypt is false QObject::connect(sslSocket, SIGNAL(encrypted()), this, SLOT(_q_encrypted()), - Qt::QueuedConnection); + Qt::DirectConnection); QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(_q_sslErrors(QList<QSslError>)), Qt::DirectConnection); @@ -156,7 +160,7 @@ void QHttpNetworkConnectionChannel::init() Qt::DirectConnection); QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)), this, SLOT(_q_encryptedBytesWritten(qint64)), - Qt::QueuedConnection); + Qt::DirectConnection); if (ignoreAllSslErrors) sslSocket->ignoreSslErrors(); @@ -400,7 +404,7 @@ void QHttpNetworkConnectionChannel::allDone() // reset the reconnection attempts after we receive a complete reply. // in case of failures, each channel will attempt two reconnects before emitting error. - reconnectAttempts = 2; + reconnectAttempts = reconnectAttemptsDefault; // now the channel can be seen as free/idle again, all signal emissions for the reply have been done if (state != QHttpNetworkConnectionChannel::ClosingState) @@ -668,6 +672,15 @@ void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest() QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } +void QHttpNetworkConnectionChannel::resendCurrentRequest() +{ + requeueCurrentlyPipelinedRequests(); + if (reply) + resendCurrent = true; + if (qobject_cast<QHttpNetworkConnection*>(connection)) + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); +} + bool QHttpNetworkConnectionChannel::isSocketBusy() const { return (state & QHttpNetworkConnectionChannel::BusyState); @@ -711,8 +724,8 @@ void QHttpNetworkConnectionChannel::_q_disconnected() return; } - // read the available data before closing - if (isSocketWaiting() || isSocketReading()) { + // read the available data before closing (also done in _q_error for other codepaths) + if ((isSocketWaiting() || isSocketReading()) && socket->bytesAvailable()) { if (reply) { state = QHttpNetworkConnectionChannel::ReadingState; _q_receiveReply(); @@ -724,7 +737,8 @@ void QHttpNetworkConnectionChannel::_q_disconnected() state = QHttpNetworkConnectionChannel::IdleState; requeueCurrentlyPipelinedRequests(); - close(); + + pendingEncrypt = false; } @@ -806,11 +820,19 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket errorCode = QNetworkReply::ConnectionRefusedError; break; case QAbstractSocket::RemoteHostClosedError: - // try to reconnect/resend before sending an error. - // while "Reading" the _q_disconnected() will handle this. - if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) { + // This error for SSL comes twice in a row, first from SSL layer ("The TLS/SSL connection has been closed") then from TCP layer. + // Depending on timing it can also come three times in a row (first time when we try to write into a closing QSslSocket). + // The reconnectAttempts handling catches the cases where we can re-send the request. + if (!reply && state == QHttpNetworkConnectionChannel::IdleState) { + // Not actually an error, it is normal for Keep-Alive connections to close after some time if no request + // is sent on them. No need to error the other replies below. Just bail out here. + // The _q_disconnected will handle the possibly pipelined replies + return; + } else if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) { + // Try to reconnect/resend before sending an error. + // While "Reading" the _q_disconnected() will handle this. if (reconnectAttempts-- > 0) { - closeAndResendCurrentRequest(); + resendCurrentRequest(); return; } else { errorCode = QNetworkReply::RemoteHostClosedError; @@ -821,11 +843,15 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket if (!reply->d_func()->expectContent()) { // No content expected, this is a valid way to have the connection closed by the server + // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState + QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection); return; } if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) { // There was no content-length header and it's not chunked encoding, // so this is a valid way to have the connection closed by the server + // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState + QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection); return; } // ok, we got a disconnect even though we did not expect it @@ -835,24 +861,15 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket // we can ignore the readbuffersize as the data is already // in memory and we will not receive more data on the socket. reply->setReadBufferSize(0); + reply->setDownstreamLimited(false); _q_receiveReply(); -#ifndef QT_NO_SSL - if (ssl) { - // QT_NO_OPENSSL. The QSslSocket can still have encrypted bytes in the plainsocket. - // So we need to check this if the socket is a QSslSocket. When the socket is flushed - // it will force a decrypt of the encrypted data in the plainsocket. - QSslSocket *sslSocket = static_cast<QSslSocket*>(socket); - qint64 beforeFlush = sslSocket->encryptedBytesAvailable(); - while (sslSocket->encryptedBytesAvailable()) { - sslSocket->flush(); - _q_receiveReply(); - qint64 afterFlush = sslSocket->encryptedBytesAvailable(); - if (afterFlush == beforeFlush) - break; - beforeFlush = afterFlush; - } + if (!reply) { + // No more reply assigned after the previous call? Then it had been finished successfully. + requeueCurrentlyPipelinedRequests(); + state = QHttpNetworkConnectionChannel::IdleState; + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + return; } -#endif } errorCode = QNetworkReply::RemoteHostClosedError; @@ -863,7 +880,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket case QAbstractSocket::SocketTimeoutError: // try to reconnect/resend before sending an error. if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) { - closeAndResendCurrentRequest(); + resendCurrentRequest(); return; } errorCode = QNetworkReply::TimeoutError; @@ -877,7 +894,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket case QAbstractSocket::ProxyConnectionClosedError: // try to reconnect/resend before sending an error. if (reconnectAttempts-- > 0) { - closeAndResendCurrentRequest(); + resendCurrentRequest(); return; } errorCode = QNetworkReply::ProxyConnectionClosedError; @@ -885,7 +902,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket case QAbstractSocket::ProxyConnectionTimeoutError: // try to reconnect/resend before sending an error. if (reconnectAttempts-- > 0) { - closeAndResendCurrentRequest(); + resendCurrentRequest(); return; } errorCode = QNetworkReply::ProxyTimeoutError; @@ -933,8 +950,18 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket // send the next request QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection); - if (that) //signal emission triggered event loop - close(); + if (that) { + //signal emission triggered event loop + if (!socket) + state = QHttpNetworkConnectionChannel::IdleState; + else if (socket->state() == QAbstractSocket::UnconnectedState) + state = QHttpNetworkConnectionChannel::IdleState; + else + state = QHttpNetworkConnectionChannel::ClosingState; + + // pendingEncrypt must only be true in between connected and encrypted states + pendingEncrypt = false; + } } #ifndef QT_NO_NETWORKPROXY @@ -958,7 +985,8 @@ void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetwor void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead() { - sendRequest(); + if (reply) + sendRequest(); } #ifndef QT_NO_SSL diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index 16d6c3b40f..37ad6c9b0a 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -169,6 +169,7 @@ public: void handleUnexpectedEOF(); void closeAndResendCurrentRequest(); + void resendCurrentRequest(); bool isSocketBusy() const; bool isSocketWriting() const; diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index 172d4dab5b..dd108ad5c7 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -208,7 +208,7 @@ QByteArray QHttpNetworkReply::readAny() return QByteArray(); // we'll take the last buffer, so schedule another read from http - if (d->downstreamLimited && d->responseData.bufferCount() == 1) + if (d->downstreamLimited && d->responseData.bufferCount() == 1 && !isFinished()) d->connection->d_func()->readMoreLater(this); return d->responseData.read(); } diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp index 55187755bf..a2083158f1 100644 --- a/src/network/access/qhttpprotocolhandler.cpp +++ b/src/network/access/qhttpprotocolhandler.cpp @@ -237,7 +237,12 @@ void QHttpProtocolHandler::_q_readyRead() } if (m_channel->isSocketWaiting() || m_channel->isSocketReading()) { - m_channel->state = QHttpNetworkConnectionChannel::ReadingState; + if (m_socket->bytesAvailable()) { + // We might get a spurious call from readMoreLater() + // call of the QHttpNetworkConnection even while the socket is disconnecting. + // Therefore check if there is actually bytes available before changing the channel state. + m_channel->state = QHttpNetworkConnectionChannel::ReadingState; + } if (m_reply) _q_receiveReply(); } @@ -250,7 +255,6 @@ bool QHttpProtocolHandler::sendRequest() if (!m_reply) { // heh, how should that happen! qWarning() << "QAbstractProtocolHandler::sendRequest() called without QHttpNetworkReply"; - m_channel->state = QHttpNetworkConnectionChannel::IdleState; return false; } diff --git a/src/network/bearer/qnetworkconfigmanager_p.cpp b/src/network/bearer/qnetworkconfigmanager_p.cpp index 78d4970ca1..71e435b771 100644 --- a/src/network/bearer/qnetworkconfigmanager_p.cpp +++ b/src/network/bearer/qnetworkconfigmanager_p.cpp @@ -43,6 +43,10 @@ #include <QtCore/private/qcoreapplication_p.h> #include <QtCore/private/qthread_p.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qglobal.h> + + #ifndef QT_NO_BEARERMANAGEMENT QT_BEGIN_NAMESPACE @@ -375,6 +379,8 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations() updating = false; #ifndef QT_NO_LIBRARY + bool envOK = false; + const int skipGeneric = qgetenv("QT_EXCLUDE_GENERIC_BEARER").toInt(&envOK); QBearerEngine *generic = 0; QFactoryLoader *l = loader(); const PluginKeyMap keyMap = l->keyMap(); @@ -409,8 +415,10 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations() } } - if (generic) - sessionEngines.append(generic); + if (generic) { + if (!envOK || skipGeneric <= 0) + sessionEngines.append(generic); + } #endif // QT_NO_LIBRARY } @@ -466,15 +474,18 @@ QList<QBearerEngine *> QNetworkConfigurationManagerPrivate::engines() const void QNetworkConfigurationManagerPrivate::startPolling() { QMutexLocker locker(&mutex); - - if(!pollTimer) { + if (!pollTimer) { pollTimer = new QTimer(this); - pollTimer->setInterval(10000); + bool ok; + int interval = qgetenv("QT_BEARER_POLL_TIMEOUT").toInt(&ok); + if (!ok) + interval = 10000;//default 10 seconds + pollTimer->setInterval(interval); pollTimer->setSingleShot(true); connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollEngines())); } - if(pollTimer->isActive()) + if (pollTimer->isActive()) return; foreach (QBearerEngine *engine, sessionEngines) { @@ -483,6 +494,7 @@ void QNetworkConfigurationManagerPrivate::startPolling() break; } } + performAsyncConfigurationUpdate(); } void QNetworkConfigurationManagerPrivate::pollEngines() diff --git a/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp b/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp index a7ab8c0977..3bd57f1d44 100644 --- a/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp +++ b/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp @@ -51,7 +51,8 @@ void SslServer::incomingConnection(qintptr socketDescriptor) { QSslSocket *serverSocket = new QSslSocket; if (serverSocket->setSocketDescriptor(socketDescriptor)) { - connect(serverSocket, SIGNAL(encrypted()), this, SLOT(ready())); + addPendingConnection(serverSocket); + connect(serverSocket, &QSslSocket::encrypted, this, &SslServer::ready); serverSocket->startServerEncryption(); } else { delete serverSocket; diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri index e539388b81..435bfd6c27 100644 --- a/src/network/kernel/kernel.pri +++ b/src/network/kernel/kernel.pri @@ -34,11 +34,12 @@ android { win32: { !winrt { - HEADERS += kernel/qnetworkinterface_win_p.h SOURCES += kernel/qdnslookup_win.cpp \ kernel/qhostinfo_win.cpp \ kernel/qnetworkinterface_win.cpp - LIBS_PRIVATE += -ldnsapi + LIBS_PRIVATE += -ldnsapi -liphlpapi + DEFINES += WINVER=0x0600 _WIN32_WINNT=0x0600 + } else { SOURCES += kernel/qdnslookup_winrt.cpp \ kernel/qhostinfo_winrt.cpp \ diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp index 58c0de1f3b..935af04e31 100644 --- a/src/network/kernel/qhostaddress.cpp +++ b/src/network/kernel/qhostaddress.cpp @@ -46,6 +46,9 @@ #ifndef QT_NO_DATASTREAM #include <qdatastream.h> #endif +#ifdef __SSE2__ +# include <private/qsimd_p.h> +#endif #ifdef QT_LINUXBASE # include <arpa/inet.h> @@ -106,7 +109,11 @@ public: QString scopeId; quint32 a; // IPv4 address - Q_IPV6ADDR a6; // IPv6 address + union { + Q_IPV6ADDR a6; // IPv6 address + struct { quint64 c[2]; } a6_64; + struct { quint32 c[4]; } a6_32; + }; QAbstractSocket::NetworkLayerProtocol protocol; bool isParsed; @@ -123,24 +130,17 @@ QHostAddressPrivate::QHostAddressPrivate() void QHostAddressPrivate::setAddress(quint32 a_) { a = a_; + protocol = QAbstractSocket::IPv4Protocol; + isParsed = true; + //create mapped address, except for a_ == 0 (any) - memset(&a6, 0, sizeof(a6)); + a6_64.c[0] = 0; if (a) { - a6[11] = 0xFF; - a6[10] = 0xFF; + a6_32.c[2] = qToBigEndian(0xffff); + a6_32.c[3] = qToBigEndian(a); } else { - a6[11] = 0; - a6[10] = 0; + a6_64.c[1] = 0; } - - int i; - for (i=15; a_ != 0; i--) { - a6[i] = a_ & 0xFF; - a_ >>=8; - } - Q_ASSERT(i >= 11); - protocol = QAbstractSocket::IPv4Protocol; - isParsed = true; } /// parses v4-mapped addresses or the AnyIPv6 address and stores in \a a; @@ -163,21 +163,16 @@ static bool convertToIpv4(quint32& a, const Q_IPV6ADDR &a6) void QHostAddressPrivate::setAddress(const quint8 *a_) { - for (int i = 0; i < 16; i++) - a6[i] = a_[i]; - a = 0; - convertToIpv4(a, a6); protocol = QAbstractSocket::IPv6Protocol; isParsed = true; + memcpy(a6.c, a_, sizeof(a6)); + a = 0; + convertToIpv4(a, a6); } void QHostAddressPrivate::setAddress(const Q_IPV6ADDR &a_) { - a6 = a_; - a = 0; - convertToIpv4(a, a6); - protocol = QAbstractSocket::IPv6Protocol; - isParsed = true; + setAddress(a_.c); } static bool parseIp6(const QString &address, QIPAddressUtils::IPv6Address &addr, QString *scopeId) @@ -193,7 +188,7 @@ static bool parseIp6(const QString &address, QIPAddressUtils::IPv6Address &addr, return QIPAddressUtils::parseIp6(addr, tmp.constBegin(), tmp.constEnd()) == 0; } -bool QHostAddressPrivate::parse() +Q_NEVER_INLINE bool QHostAddressPrivate::parse() { isParsed = true; protocol = QAbstractSocket::UnknownNetworkLayerProtocol; @@ -486,31 +481,35 @@ QHostAddress::QHostAddress(SpecialAddress address) { Q_IPV6ADDR ip6; memset(&ip6, 0, sizeof ip6); + quint32 ip4 = INADDR_ANY; switch (address) { case Null: - break; + return; + case Broadcast: - d->setAddress(quint32(-1)); + ip4 = INADDR_BROADCAST; break; case LocalHost: - d->setAddress(0x7f000001); - break; - case LocalHostIPv6: - ip6[15] = 1; - d->setAddress(ip6); + ip4 = INADDR_LOOPBACK; break; case AnyIPv4: - setAddress(0u); break; + + case LocalHostIPv6: + ip6[15] = 1; + // fall through case AnyIPv6: d->setAddress(ip6); - break; + return; + case Any: - d->clear(); d->protocol = QAbstractSocket::AnyIPProtocol; - break; + return; } + + // common IPv4 part + d->setAddress(ip4); } /*! @@ -775,11 +774,6 @@ QString QHostAddress::toString() const on your host. Link-local addresses ("fe80...") are generated from the MAC address of the local network adaptor, and are not guaranteed to be unique. - \li Site-local: Addresses that are local to the site / private network - (e.g., the company intranet). Site-local addresses ("fec0...") are - usually distributed by the site router, and are not guaranteed to be - unique outside of the local site. - \li Global: For globally routable addresses, such as public servers on the Internet. @@ -790,7 +784,7 @@ QString QHostAddress::toString() const usually the same as the interface name (e.g., "eth0", "en1") or number (e.g., "1", "2"). - \sa setScopeId() + \sa setScopeId(), QNetworkInterface, QNetworkInterface::interfaceFromName */ QString QHostAddress::scopeId() const { @@ -801,8 +795,14 @@ QString QHostAddress::scopeId() const /*! \since 4.1 - Sets the IPv6 scope ID of the address to \a id. If the address - protocol is not IPv6, this function does nothing. + Sets the IPv6 scope ID of the address to \a id. If the address protocol is + not IPv6, this function does nothing. The scope ID may be set as an + interface name (such as "eth0" or "en1") or as an integer representing the + interface index. If \a id is an interface name, QtNetwork will convert to + an interface index using QNetworkInterface::interfaceIndexFromName() before + calling the operating system networking functions. + + \sa scopeId(), QNetworkInterface, QNetworkInterface::interfaceFromName */ void QHostAddress::setScopeId(const QString &id) { @@ -836,34 +836,36 @@ bool QHostAddress::operator==(const QHostAddress &other) const bool QHostAddress::operator ==(SpecialAddress other) const { QT_ENSURE_PARSED(this); + quint32 ip4 = INADDR_ANY; switch (other) { case Null: return d->protocol == QAbstractSocket::UnknownNetworkLayerProtocol; case Broadcast: - return d->protocol == QAbstractSocket::IPv4Protocol && d->a == INADDR_BROADCAST; + ip4 = INADDR_BROADCAST; + break; case LocalHost: - return d->protocol == QAbstractSocket::IPv4Protocol && d->a == INADDR_LOOPBACK; + ip4 = INADDR_LOOPBACK; + break; case Any: return d->protocol == QAbstractSocket::AnyIPProtocol; case AnyIPv4: - return d->protocol == QAbstractSocket::IPv4Protocol && d->a == INADDR_ANY; + break; case LocalHostIPv6: case AnyIPv6: if (d->protocol == QAbstractSocket::IPv6Protocol) { - Q_IPV6ADDR ip6 = { { 0 } }; - ip6[15] = quint8(other == LocalHostIPv6); // 1 for localhost, 0 for any - return memcmp(&d->a6, &ip6, sizeof ip6) == 0; + quint64 second = quint8(other == LocalHostIPv6); // 1 for localhost, 0 for any + return d->a6_64.c[0] == 0 && d->a6_64.c[1] == qToBigEndian(second); } return false; } - Q_UNREACHABLE(); - return false; + // common IPv4 part + return d->protocol == QAbstractSocket::IPv4Protocol && d->a == ip4; } /*! @@ -1085,16 +1087,36 @@ bool QHostAddress::isLoopback() const if ((d->a & 0xFF000000) == 0x7F000000) return true; // v4 range (including IPv6 wrapped IPv4 addresses) if (d->protocol == QAbstractSocket::IPv6Protocol) { - if (d->a6.c[15] != 1) +#ifdef __SSE2__ + const __m128i loopback = _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); + __m128i ipv6 = _mm_loadu_si128((const __m128i *)d->a6.c); + __m128i cmp = _mm_cmpeq_epi8(ipv6, loopback); + return _mm_movemask_epi8(cmp) == 0xffff; +#else + if (d->a6_64.c[0] != 0 || qFromBigEndian(d->a6_64.c[1]) != 1) return false; - for (int i = 0; i < 15; i++) - if (d->a6[i] != 0) - return false; +#endif return true; } return false; } +/*! + \since 5.6 + + Returns \c true if the address is an IPv4 or IPv6 multicast address, \c + false otherwise. +*/ +bool QHostAddress::isMulticast() const +{ + QT_ENSURE_PARSED(this); + if ((d->a & 0xF0000000) == 0xE0000000) + return true; // 224.0.0.0-239.255.255.255 (including v4-mapped IPv6 addresses) + if (d->protocol == QAbstractSocket::IPv6Protocol) + return d->a6.c[0] == 0xff; + return false; +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QHostAddress &address) { @@ -1112,7 +1134,7 @@ uint qHash(const QHostAddress &key, uint seed) { // both lines might throw QT_ENSURE_PARSED(&key); - return qHash(QByteArray::fromRawData(reinterpret_cast<const char *>(key.d->a6.c), 16), seed); + return qHashBits(key.d->a6.c, 16, seed); } #ifndef QT_NO_DATASTREAM diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h index 8478240d28..9e3ee43d04 100644 --- a/src/network/kernel/qhostaddress.h +++ b/src/network/kernel/qhostaddress.h @@ -124,6 +124,7 @@ public: bool isInSubnet(const QPair<QHostAddress, int> &subnet) const; bool isLoopback() const; + bool isMulticast() const; static QPair<QHostAddress, int> parseSubnet(const QString &subnet); @@ -131,6 +132,7 @@ public: protected: QScopedPointer<QHostAddressPrivate> d; }; +Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QHostAddress) inline bool operator ==(QHostAddress::SpecialAddress address1, const QHostAddress &address2) { return address2 == address1; } diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index a2ac9065fd..c6c09542e7 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -415,10 +415,22 @@ void QHostInfo::setErrorString(const QString &str) /*! \fn QString QHostInfo::localHostName() - Returns the host name of this machine. + Returns this machine's host name, if one is configured. Note that hostnames + are not guaranteed to be globally unique, especially if they were + configured automatically. - \sa hostName() + This function does not guarantee the returned host name is a Fully + Qualified Domain Name (FQDN). For that, use fromName() to resolve the + returned name to an FQDN. + + This function returns the same as QSysInfo::machineHostName(). + + \sa hostName(), localDomainName() */ +QString QHostInfo::localHostName() +{ + return QSysInfo::machineHostName(); +} /*! \fn QString QHostInfo::localDomainName() diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 90a6f763f7..266a67771c 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -315,15 +315,6 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) return results; } -QString QHostInfo::localHostName() -{ - char hostName[512]; - if (gethostname(hostName, sizeof(hostName)) == -1) - return QString(); - hostName[sizeof(hostName) - 1] = '\0'; - return QString::fromLocal8Bit(hostName); -} - QString QHostInfo::localDomainName() { #if !defined(Q_OS_VXWORKS) && !defined(Q_OS_ANDROID) diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp index e044728198..da28cd48c1 100644 --- a/src/network/kernel/qhostinfo_win.cpp +++ b/src/network/kernel/qhostinfo_win.cpp @@ -111,7 +111,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) QMutexLocker locker(&qPrivCEMutex); #endif - QWindowsSockInit winSock; + QSysInfo::machineHostName(); // this initializes ws2_32.dll // Load res_init on demand. static QBasicAtomicInt triedResolve = Q_BASIC_ATOMIC_INITIALIZER(false); @@ -136,7 +136,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) // Reverse lookup if (local_getnameinfo) { sockaddr_in sa4; - qt_sockaddr_in6 sa6; + sockaddr_in6 sa6; sockaddr *sa; QT_SOCKLEN_T saSize; if (address.protocol() == QAbstractSocket::IPv4Protocol) { @@ -150,7 +150,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) saSize = sizeof(sa6); memset(&sa6, 0, sizeof(sa6)); sa6.sin6_family = AF_INET6; - memcpy(sa6.sin6_addr.qt_s6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr.qt_s6_addr)); + memcpy(&sa6.sin6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr)); } char hbuf[NI_MAXHOST]; @@ -197,7 +197,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) break; case AF_INET6: { QHostAddress addr; - addr.setAddress(((qt_sockaddr_in6 *) p->ai_addr)->sin6_addr.qt_s6_addr); + addr.setAddress(((sockaddr_in6 *) p->ai_addr)->sin6_addr.s6_addr); if (!addresses.contains(addr)) addresses.append(addr); } @@ -256,17 +256,6 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) return results; } -QString QHostInfo::localHostName() -{ - QWindowsSockInit winSock; - - char hostName[512]; - if (gethostname(hostName, sizeof(hostName)) == -1) - return QString(); - hostName[sizeof(hostName) - 1] = '\0'; - return QString::fromLocal8Bit(hostName); -} - // QString QHostInfo::localDomainName() defined in qnetworkinterface_win.cpp QT_END_NAMESPACE diff --git a/src/network/kernel/qhostinfo_winrt.cpp b/src/network/kernel/qhostinfo_winrt.cpp index 1a97fe0e40..3d2344726b 100644 --- a/src/network/kernel/qhostinfo_winrt.cpp +++ b/src/network/kernel/qhostinfo_winrt.cpp @@ -130,45 +130,6 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) return results; } -QString QHostInfo::localHostName() -{ - ComPtr<INetworkInformationStatics> statics; - GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &statics); - - ComPtr<IVectorView<HostName *>> hostNames; - statics->GetHostNames(&hostNames); - if (!hostNames) - return QString(); - - unsigned int size; - hostNames->get_Size(&size); - if (size == 0) - return QString(); - - for (unsigned int i = 0; i < size; ++i) { - ComPtr<IHostName> hostName; - hostNames->GetAt(i, &hostName); - HostNameType type; - hostName->get_Type(&type); - if (type != HostNameType_DomainName) - continue; - - HString name; - hostName->get_CanonicalName(name.GetAddressOf()); - UINT32 length; - PCWSTR rawString = name.GetRawBuffer(&length); - return QString::fromWCharArray(rawString, length); - } - ComPtr<IHostName> firstHost; - hostNames->GetAt(0, &firstHost); - - HString name; - firstHost->get_CanonicalName(name.GetAddressOf()); - UINT32 length; - PCWSTR rawString = name.GetRawBuffer(&length); - return QString::fromWCharArray(rawString, length); -} - // QString QHostInfo::localDomainName() defined in qnetworkinterface_win.cpp QT_END_NAMESPACE diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp index 2fbbf56e0f..4a527052d1 100644 --- a/src/network/kernel/qnetworkinterface.cpp +++ b/src/network/kernel/qnetworkinterface.cpp @@ -86,9 +86,16 @@ QSharedDataPointer<QNetworkInterfacePrivate> QNetworkInterfaceManager::interface { QList<QSharedDataPointer<QNetworkInterfacePrivate> > interfaceList = allInterfaces(); QList<QSharedDataPointer<QNetworkInterfacePrivate> >::ConstIterator it = interfaceList.constBegin(); - for ( ; it != interfaceList.constEnd(); ++it) - if ((*it)->name == name) + + bool ok; + uint index = name.toUInt(&ok); + + for ( ; it != interfaceList.constEnd(); ++it) { + if (ok && (*it)->index == int(index)) return *it; + else if ((*it)->name == name) + return *it; + } return empty; } @@ -516,6 +523,9 @@ QList<QNetworkAddressEntry> QNetworkInterface::addressEntries() const name. If no such interface exists, this function returns an invalid QNetworkInterface object. + The string \a name may be either an actual interface name (such as "eth0" + or "en1") or an interface index in string form ("1", "2", etc.). + \sa name(), isValid() */ QNetworkInterface QNetworkInterface::interfaceFromName(const QString &name) diff --git a/src/network/kernel/qnetworkinterface.h b/src/network/kernel/qnetworkinterface.h index 31d3b7b128..b3daa3d4a0 100644 --- a/src/network/kernel/qnetworkinterface.h +++ b/src/network/kernel/qnetworkinterface.h @@ -133,6 +133,9 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkInterface &networ QT_END_NAMESPACE +Q_DECLARE_METATYPE(QNetworkAddressEntry) +Q_DECLARE_METATYPE(QNetworkInterface) + #endif // QT_NO_NETWORKINTERFACE #endif diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp index 9c5ba4e799..cc53087024 100644 --- a/src/network/kernel/qnetworkinterface_unix.cpp +++ b/src/network/kernel/qnetworkinterface_unix.cpp @@ -127,14 +127,13 @@ static QNetworkInterface::InterfaceFlags convertFlags(uint rawFlags) #ifdef QT_NO_GETIFADDRS // getifaddrs not available -static const int STORAGEBUFFER_GROWTH = 256; - static QSet<QByteArray> interfaceNames(int socket) { QSet<QByteArray> result; #ifdef QT_NO_IPV6IFNAME QByteArray storageBuffer; struct ifconf interfaceList; + static const int STORAGEBUFFER_GROWTH = 256; forever { // grow the storage buffer @@ -186,9 +185,14 @@ static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfa QNetworkInterfacePrivate *iface = 0; int ifindex = 0; -#ifndef QT_NO_IPV6IFNAME +#if !defined(QT_NO_IPV6IFNAME) || defined(SIOCGIFINDEX) // Get the interface index +# ifdef SIOCGIFINDEX + if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0) + ifindex = req.ifr_ifindex; +# else ifindex = if_nametoindex(req.ifr_name); +# endif // find the interface data QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin(); @@ -214,6 +218,27 @@ static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfa iface = new QNetworkInterfacePrivate; iface->index = ifindex; interfaces << iface; + } + + return iface; +} + +static QList<QNetworkInterfacePrivate *> interfaceListing() +{ + QList<QNetworkInterfacePrivate *> interfaces; + + int socket; + if ((socket = qt_safe_socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == -1) + return interfaces; // error + + QSet<QByteArray> names = interfaceNames(socket); + QSet<QByteArray>::ConstIterator it = names.constBegin(); + for ( ; it != names.constEnd(); ++it) { + ifreq req; + memset(&req, 0, sizeof(ifreq)); + memcpy(req.ifr_name, *it, qMin<int>(it->length() + 1, sizeof(req.ifr_name) - 1)); + + QNetworkInterfacePrivate *iface = findInterface(socket, interfaces, req); #ifdef SIOCGIFNAME // Get the canonical name @@ -242,27 +267,6 @@ static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfa iface->hardwareAddress = iface->makeHwAddress(6, addr); } #endif - } - - return iface; -} - -static QList<QNetworkInterfacePrivate *> interfaceListing() -{ - QList<QNetworkInterfacePrivate *> interfaces; - - int socket; - if ((socket = qt_safe_socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == -1) - return interfaces; // error - - QSet<QByteArray> names = interfaceNames(socket); - QSet<QByteArray>::ConstIterator it = names.constBegin(); - for ( ; it != names.constEnd(); ++it) { - ifreq req; - memset(&req, 0, sizeof(ifreq)); - memcpy(req.ifr_name, *it, qMin<int>(it->length() + 1, sizeof(req.ifr_name) - 1)); - - QNetworkInterfacePrivate *iface = findInterface(socket, interfaces, req); // Get the interface broadcast address QNetworkAddressEntry entry; diff --git a/src/network/kernel/qnetworkinterface_win.cpp b/src/network/kernel/qnetworkinterface_win.cpp index ddeb6c111f..907638f73e 100644 --- a/src/network/kernel/qnetworkinterface_win.cpp +++ b/src/network/kernel/qnetworkinterface_win.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Intel Corporation. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -31,7 +32,7 @@ ** ****************************************************************************/ -#include "qnetworkinterface_win_p.h" +#define WIN32_LEAN_AND_MEAN 1 #include "qnetworkinterface.h" #include "qnetworkinterface_p.h" @@ -41,16 +42,23 @@ #include <qhostinfo.h> #include <qhash.h> #include <qurl.h> -#include <private/qsystemlibrary_p.h> + +// Since we need to include winsock2.h, we need to define WIN32_LEAN_AND_MEAN +// (above) so windows.h won't include winsock.h. +// In addition, we need to include winsock2.h before iphlpapi.h and we need +// to include ws2ipdef.h to work around an MinGW-w64 bug +// (http://sourceforge.net/p/mingw-w64/mailman/message/32935366/) +#include <winsock2.h> +#include <ws2ipdef.h> +#include <iphlpapi.h> +#include <ws2tcpip.h> + +#include <qt_windows.h> QT_BEGIN_NAMESPACE -typedef DWORD (WINAPI *PtrGetAdaptersInfo)(PIP_ADAPTER_INFO, PULONG); -static PtrGetAdaptersInfo ptrGetAdaptersInfo = 0; -typedef ULONG (WINAPI *PtrGetAdaptersAddresses)(ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG); -static PtrGetAdaptersAddresses ptrGetAdaptersAddresses = 0; -typedef DWORD (WINAPI *PtrGetNetworkParams)(PFIXED_INFO, PULONG); -static PtrGetNetworkParams ptrGetNetworkParams = 0; +typedef NETIO_STATUS (WINAPI *PtrConvertInterfaceLuidToName)(const NET_LUID *, PWSTR, SIZE_T); +static PtrConvertInterfaceLuidToName ptrConvertInterfaceLuidToName = 0; static void resolveLibs() { @@ -58,21 +66,17 @@ static void resolveLibs() static bool done = false; if (!done) { - done = true; - - HINSTANCE iphlpapiHnd = QSystemLibrary::load(L"iphlpapi"); - if (iphlpapiHnd == NULL) - return; + HINSTANCE iphlpapiHnd = GetModuleHandle(L"iphlpapi"); + Q_ASSERT(iphlpapiHnd); #if defined(Q_OS_WINCE) - ptrGetAdaptersInfo = (PtrGetAdaptersInfo)GetProcAddress(iphlpapiHnd, L"GetAdaptersInfo"); - ptrGetAdaptersAddresses = (PtrGetAdaptersAddresses)GetProcAddress(iphlpapiHnd, L"GetAdaptersAddresses"); - ptrGetNetworkParams = (PtrGetNetworkParams)GetProcAddress(iphlpapiHnd, L"GetNetworkParams"); + // since Windows Embedded Compact 7 + ptrConvertInterfaceLuidToName = (PtrConvertInterfaceLuidToName)GetProcAddress(iphlpapiHnd, L"ConvertInterfaceLuidToNameW"); #else - ptrGetAdaptersInfo = (PtrGetAdaptersInfo)GetProcAddress(iphlpapiHnd, "GetAdaptersInfo"); - ptrGetAdaptersAddresses = (PtrGetAdaptersAddresses)GetProcAddress(iphlpapiHnd, "GetAdaptersAddresses"); - ptrGetNetworkParams = (PtrGetNetworkParams)GetProcAddress(iphlpapiHnd, "GetNetworkParams"); + // since Windows Vista + ptrConvertInterfaceLuidToName = (PtrConvertInterfaceLuidToName)GetProcAddress(iphlpapiHnd, "ConvertInterfaceLuidToNameW"); #endif + done = true; } } @@ -85,8 +89,8 @@ static QHostAddress addressFromSockaddr(sockaddr *sa) if (sa->sa_family == AF_INET) address.setAddress(htonl(((sockaddr_in *)sa)->sin_addr.s_addr)); else if (sa->sa_family == AF_INET6) { - address.setAddress(((qt_sockaddr_in6 *)sa)->sin6_addr.qt_s6_addr); - int scope = ((qt_sockaddr_in6 *)sa)->sin6_scope_id; + address.setAddress(((sockaddr_in6 *)sa)->sin6_addr.s6_addr); + int scope = ((sockaddr_in6 *)sa)->sin6_scope_id; if (scope) address.setScopeId(QString::number(scope)); } else @@ -103,14 +107,14 @@ static QHash<QHostAddress, QHostAddress> ipv4Netmasks() ULONG bufSize = sizeof staticBuf; QHash<QHostAddress, QHostAddress> ipv4netmasks; - DWORD retval = ptrGetAdaptersInfo(pAdapter, &bufSize); + DWORD retval = GetAdaptersInfo(pAdapter, &bufSize); if (retval == ERROR_BUFFER_OVERFLOW) { // need more memory pAdapter = (IP_ADAPTER_INFO *)malloc(bufSize); if (!pAdapter) return ipv4netmasks; // try again - if (ptrGetAdaptersInfo(pAdapter, &bufSize) != ERROR_SUCCESS) { + if (GetAdaptersInfo(pAdapter, &bufSize) != ERROR_SUCCESS) { free(pAdapter); return ipv4netmasks; } @@ -145,14 +149,14 @@ static QList<QNetworkInterfacePrivate *> interfaceListingWinXP() ULONG flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST; - ULONG retval = ptrGetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize); + ULONG retval = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize); if (retval == ERROR_BUFFER_OVERFLOW) { // need more memory pAdapter = (IP_ADAPTER_ADDRESSES *)malloc(bufSize); if (!pAdapter) return interfaces; // try again - if (ptrGetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize) != ERROR_SUCCESS) { + if (GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize) != ERROR_SUCCESS) { free(pAdapter); return interfaces; } @@ -180,7 +184,16 @@ static QList<QNetworkInterfacePrivate *> interfaceListingWinXP() if (ptr->IfType == IF_TYPE_PPP) iface->flags |= QNetworkInterface::IsPointToPoint; - iface->name = QString::fromLocal8Bit(ptr->AdapterName); + if (ptrConvertInterfaceLuidToName && ptr->Length >= offsetof(IP_ADAPTER_ADDRESSES, Luid)) { + // use ConvertInterfaceLuidToName because that returns a friendlier name, though not + // as friendly as FriendlyName below + WCHAR buf[IF_MAX_STRING_SIZE + 1]; + if (ptrConvertInterfaceLuidToName(&ptr->Luid, buf, sizeof(buf)/sizeof(buf[0])) == NO_ERROR) + iface->name = QString::fromWCharArray(buf); + } + if (iface->name.isEmpty()) + iface->name = QString::fromLocal8Bit(ptr->AdapterName); + iface->friendlyName = QString::fromWCharArray(ptr->FriendlyName); if (ptr->PhysicalAddressLength) iface->hardwareAddress = iface->makeHwAddress(ptr->PhysicalAddressLength, @@ -221,92 +234,25 @@ static QList<QNetworkInterfacePrivate *> interfaceListingWinXP() return interfaces; } -static QList<QNetworkInterfacePrivate *> interfaceListingWin2k() -{ - QList<QNetworkInterfacePrivate *> interfaces; - IP_ADAPTER_INFO staticBuf[2]; // 2 is arbitrary - PIP_ADAPTER_INFO pAdapter = staticBuf; - ULONG bufSize = sizeof staticBuf; - - DWORD retval = ptrGetAdaptersInfo(pAdapter, &bufSize); - if (retval == ERROR_BUFFER_OVERFLOW) { - // need more memory - pAdapter = (IP_ADAPTER_INFO *)malloc(bufSize); - if (!pAdapter) - return interfaces; - // try again - if (ptrGetAdaptersInfo(pAdapter, &bufSize) != ERROR_SUCCESS) { - free(pAdapter); - return interfaces; - } - } else if (retval != ERROR_SUCCESS) { - // error - return interfaces; - } - - // iterate over the list and add the entries to our listing - for (PIP_ADAPTER_INFO ptr = pAdapter; ptr; ptr = ptr->Next) { - QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate; - interfaces << iface; - - iface->index = ptr->Index; - iface->flags = QNetworkInterface::IsUp | QNetworkInterface::IsRunning; - if (ptr->Type == MIB_IF_TYPE_PPP) - iface->flags |= QNetworkInterface::IsPointToPoint; - else - iface->flags |= QNetworkInterface::CanBroadcast; - iface->name = QString::fromLocal8Bit(ptr->AdapterName); - iface->hardwareAddress = QNetworkInterfacePrivate::makeHwAddress(ptr->AddressLength, - ptr->Address); - - for (PIP_ADDR_STRING addr = &ptr->IpAddressList; addr; addr = addr->Next) { - QNetworkAddressEntry entry; - entry.setIp(QHostAddress(QLatin1String(addr->IpAddress.String))); - entry.setNetmask(QHostAddress(QLatin1String(addr->IpMask.String))); - // broadcast address is set on postProcess() - - iface->addressEntries << entry; - } - } - - if (pAdapter != staticBuf) - free(pAdapter); - - return interfaces; -} - -static QList<QNetworkInterfacePrivate *> interfaceListing() -{ - resolveLibs(); - if (ptrGetAdaptersAddresses != NULL) - return interfaceListingWinXP(); - else if (ptrGetAdaptersInfo != NULL) - return interfaceListingWin2k(); - - // failed - return QList<QNetworkInterfacePrivate *>(); -} - QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan() { - return interfaceListing(); + resolveLibs(); + return interfaceListingWinXP(); } QString QHostInfo::localDomainName() { resolveLibs(); - if (ptrGetNetworkParams == NULL) - return QString(); // couldn't resolve FIXED_INFO info, *pinfo; ULONG bufSize = sizeof info; pinfo = &info; - if (ptrGetNetworkParams(pinfo, &bufSize) == ERROR_BUFFER_OVERFLOW) { + if (GetNetworkParams(pinfo, &bufSize) == ERROR_BUFFER_OVERFLOW) { pinfo = (FIXED_INFO *)malloc(bufSize); if (!pinfo) return QString(); // try again - if (ptrGetNetworkParams(pinfo, &bufSize) != ERROR_SUCCESS) { + if (GetNetworkParams(pinfo, &bufSize) != ERROR_SUCCESS) { free(pinfo); return QString(); // error } diff --git a/src/network/kernel/qnetworkinterface_win_p.h b/src/network/kernel/qnetworkinterface_win_p.h deleted file mode 100644 index 88c1945fe6..0000000000 --- a/src/network/kernel/qnetworkinterface_win_p.h +++ /dev/null @@ -1,262 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://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 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QNETWORKINTERFACE_WIN_P_H -#define QNETWORKINTERFACE_WIN_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 <QtCore/qglobal.h> -#include <winsock2.h> -#include <qt_windows.h> -#include <time.h> - -QT_BEGIN_NAMESPACE - -#ifndef GAA_FLAG_INCLUDE_ALL_INTERFACES -# define GAA_FLAG_INCLUDE_ALL_INTERFACES 0x0100 -#endif -#ifndef MAX_ADAPTER_ADDRESS_LENGTH -// definitions from iptypes.h -# define MAX_ADAPTER_DESCRIPTION_LENGTH 128 // arb. -# define MAX_ADAPTER_NAME_LENGTH 256 // arb. -# define MAX_ADAPTER_ADDRESS_LENGTH 8 // arb. -# define DEFAULT_MINIMUM_ENTITIES 32 // arb. -# define MAX_HOSTNAME_LEN 128 // arb. -# define MAX_DOMAIN_NAME_LEN 128 // arb. -# define MAX_SCOPE_ID_LEN 256 // arb. - -# define GAA_FLAG_SKIP_UNICAST 0x0001 -# define GAA_FLAG_SKIP_ANYCAST 0x0002 -# define GAA_FLAG_SKIP_MULTICAST 0x0004 -# define GAA_FLAG_SKIP_DNS_SERVER 0x0008 -# define GAA_FLAG_INCLUDE_PREFIX 0x0010 -# define GAA_FLAG_SKIP_FRIENDLY_NAME 0x0020 - -# define IP_ADAPTER_DDNS_ENABLED 0x01 -# define IP_ADAPTER_REGISTER_ADAPTER_SUFFIX 0x02 -# define IP_ADAPTER_DHCP_ENABLED 0x04 -# define IP_ADAPTER_RECEIVE_ONLY 0x08 -# define IP_ADAPTER_NO_MULTICAST 0x10 -# define IP_ADAPTER_IPV6_OTHER_STATEFUL_CONFIG 0x20 - -# define MIB_IF_TYPE_OTHER 1 -# define MIB_IF_TYPE_ETHERNET 6 -# define MIB_IF_TYPE_TOKENRING 9 -# define MIB_IF_TYPE_FDDI 15 -# define MIB_IF_TYPE_PPP 23 -# define MIB_IF_TYPE_LOOPBACK 24 -# define MIB_IF_TYPE_SLIP 28 - -// definitions from Ipifcons.h -#define IF_TYPE_PPP 23 - -#endif -// copied from qnativesocketengine_win.cpp -struct qt_in6_addr { - u_char qt_s6_addr[16]; -}; -typedef struct { - short sin6_family; /* AF_INET6 */ - u_short sin6_port; /* Transport level port number */ - u_long sin6_flowinfo; /* IPv6 flow information */ - struct qt_in6_addr sin6_addr; /* IPv6 address */ - u_long sin6_scope_id; /* set of interfaces for a scope */ -} qt_sockaddr_in6; - -// copied from MSDN online help -typedef enum { - IpPrefixOriginOther = 0, - IpPrefixOriginManual, - IpPrefixOriginWellKnown, - IpPrefixOriginDhcp, - IpPrefixOriginRouterAdvertisement -} IP_PREFIX_ORIGIN; - -typedef enum { - IpSuffixOriginOther = 0, - IpSuffixOriginManual, - IpSuffixOriginWellKnown, - IpSuffixOriginDhcp, - IpSuffixOriginLinkLayerAddress, - IpSuffixOriginRandom -} IP_SUFFIX_ORIGIN; - -typedef enum { - IpDadStateInvalid = 0, - IpDadStateTentative, - IpDadStateDuplicate, - IpDadStateDeprecated, - IpDadStatePreferred, -} IP_DAD_STATE; - -typedef enum { - IfOperStatusUp = 1, - IfOperStatusDown, - IfOperStatusTesting, - IfOperStatusUnknown, - IfOperStatusDormant, - IfOperStatusNotPresent, - IfOperStatusLowerLayerDown -} IF_OPER_STATUS; - -typedef struct _IP_ADAPTER_UNICAST_ADDRESS { - union { - ULONGLONG Alignment; - struct { - ULONG Length; - DWORD Flags; - }; - }; - struct _IP_ADAPTER_UNICAST_ADDRESS* Next; - SOCKET_ADDRESS Address; - IP_PREFIX_ORIGIN PrefixOrigin; - IP_SUFFIX_ORIGIN SuffixOrigin; - IP_DAD_STATE DadState; - ULONG ValidLifetime; - ULONG PreferredLifetime; - ULONG LeaseLifetime; -} IP_ADAPTER_UNICAST_ADDRESS, *PIP_ADAPTER_UNICAST_ADDRESS; - -typedef struct _IP_ADAPTER_ANYCAST_ADDRESS - IP_ADAPTER_ANYCAST_ADDRESS, *PIP_ADAPTER_ANYCAST_ADDRESS; - -typedef struct _IP_ADAPTER_MULTICAST_ADDRESS - IP_ADAPTER_MULTICAST_ADDRESS, - *PIP_ADAPTER_MULTICAST_ADDRESS; - -typedef struct _IP_ADAPTER_DNS_SERVER_ADDRESS - IP_ADAPTER_DNS_SERVER_ADDRESS, - *PIP_ADAPTER_DNS_SERVER_ADDRESS; - -typedef struct _IP_ADAPTER_PREFIX { - union { - ULONGLONG Alignment; - struct { - ULONG Length; - DWORD Flags; - }; - }; - struct _IP_ADAPTER_PREFIX* Next; - SOCKET_ADDRESS Address; - ULONG PrefixLength; -} IP_ADAPTER_PREFIX, - *PIP_ADAPTER_PREFIX; - -typedef struct _IP_ADAPTER_ADDRESSES { - union { - ULONGLONG Alignment; - struct { - ULONG Length; - DWORD IfIndex; - }; - }; - struct _IP_ADAPTER_ADDRESSES* Next; - PCHAR AdapterName; - PIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress; - PIP_ADAPTER_ANYCAST_ADDRESS FirstAnycastAddress; - PIP_ADAPTER_MULTICAST_ADDRESS FirstMulticastAddress; - PIP_ADAPTER_DNS_SERVER_ADDRESS FirstDnsServerAddress; - PWCHAR DnsSuffix; - PWCHAR Description; - PWCHAR FriendlyName; - BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH]; - DWORD PhysicalAddressLength; - DWORD Flags; - DWORD Mtu; - DWORD IfType; - IF_OPER_STATUS OperStatus; - DWORD Ipv6IfIndex; - DWORD ZoneIndices[16]; - PIP_ADAPTER_PREFIX FirstPrefix; -} IP_ADAPTER_ADDRESSES, - *PIP_ADAPTER_ADDRESSES; - -typedef struct { - char String[4 * 4]; -} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING; - -typedef struct _IP_ADDR_STRING { - struct _IP_ADDR_STRING* Next; - IP_ADDRESS_STRING IpAddress; - IP_MASK_STRING IpMask; - DWORD Context; -} IP_ADDR_STRING, - *PIP_ADDR_STRING; - -typedef struct _IP_ADAPTER_INFO { - struct _IP_ADAPTER_INFO* Next; - DWORD ComboIndex; - char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4]; - char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4]; - UINT AddressLength; - BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH]; - DWORD Index; - UINT Type; - UINT DhcpEnabled; - PIP_ADDR_STRING CurrentIpAddress; - IP_ADDR_STRING IpAddressList; - IP_ADDR_STRING GatewayList; - IP_ADDR_STRING DhcpServer; - BOOL HaveWins; - IP_ADDR_STRING PrimaryWinsServer; - IP_ADDR_STRING SecondaryWinsServer; - time_t LeaseObtained; - time_t LeaseExpires; -} IP_ADAPTER_INFO, - *PIP_ADAPTER_INFO; - -typedef struct { - char HostName[MAX_HOSTNAME_LEN + 4]; - char DomainName[MAX_DOMAIN_NAME_LEN + 4]; - PIP_ADDR_STRING CurrentDnsServer; - IP_ADDR_STRING DnsServerList; - UINT NodeType; - char ScopeId[MAX_SCOPE_ID_LEN + 4]; - UINT EnableRouting; - UINT EnableProxy; - UINT EnableDns; -} FIXED_INFO, *PFIXED_INFO; - -QT_END_NAMESPACE - -#endif diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp index 51400f55bb..05d4c6955a 100644 --- a/src/network/kernel/qnetworkproxy_mac.cpp +++ b/src/network/kernel/qnetworkproxy_mac.cpp @@ -35,6 +35,7 @@ #ifndef QT_NO_NETWORKPROXY +#include <CFNetwork/CFNetwork.h> #include <CoreFoundation/CoreFoundation.h> #include <SystemConfiguration/SystemConfiguration.h> diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 785ea17389..b2426f5c00 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -630,8 +630,8 @@ bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtoc resetSocketLayer(); socketEngine = QAbstractSocketEngine::createSocketEngine(q->socketType(), proxyInUse, q); if (!socketEngine) { - socketError = QAbstractSocket::UnsupportedSocketOperationError; - q->setErrorString(QAbstractSocket::tr("Operation on socket is not supported")); + setError(QAbstractSocket::UnsupportedSocketOperationError, + QAbstractSocket::tr("Operation on socket is not supported")); return false; } #ifndef QT_NO_BEARERMANAGEMENT @@ -644,8 +644,7 @@ bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtoc typeStr.toLatin1().constData(), protocolStr.toLatin1().constData(), socketEngine->errorString().toLatin1().constData()); #endif - socketError = socketEngine->error(); - q->setErrorString(socketEngine->errorString()); + setError(socketEngine->error(), socketEngine->errorString()); return false; } @@ -771,6 +770,7 @@ bool QAbstractSocketPrivate::canReadNotification() void QAbstractSocketPrivate::canCloseNotification() { Q_Q(QAbstractSocket); + // Note that this method is only called on Windows. Other platforms close in the canReadNotification() #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::canCloseNotification()"); @@ -780,7 +780,11 @@ void QAbstractSocketPrivate::canCloseNotification() if (isBuffered) { // Try to read to the buffer, if the read fail we can close the socket. newBytes = buffer.size(); - if (!readFromSocket()) { + qint64 oldReadBufferMaxSize = readBufferMaxSize; + readBufferMaxSize = 0; // temporarily disable max read buffer, we want to empty the OS buffer + bool hadReadFromSocket = readFromSocket(); + readBufferMaxSize = oldReadBufferMaxSize; + if (!hadReadFromSocket) { q->disconnectFromHost(); return; } @@ -880,13 +884,11 @@ bool QAbstractSocketPrivate::writeToSocket() // Attempt to write it all in one chunk. qint64 written = socketEngine->write(ptr, nextSize); if (written < 0) { - socketError = socketEngine->error(); - q->setErrorString(socketEngine->errorString()); #if defined (QABSTRACTSOCKET_DEBUG) qDebug() << "QAbstractSocketPrivate::writeToSocket() write error, aborting." << socketEngine->errorString(); #endif - emit q->error(socketError); + setErrorAndEmit(socketEngine->error(), socketEngine->errorString()); // an unexpected error so close the socket. q->abort(); return false; @@ -1004,8 +1006,7 @@ void QAbstractSocketPrivate::startConnectingByName(const QString &host) } // failed to connect - socketError = socketEngine->error(); - q->setErrorString(socketEngine->errorString()); + setError(socketEngine->error(), socketEngine->errorString()); } state = QAbstractSocket::UnconnectedState; @@ -1064,8 +1065,7 @@ void QAbstractSocketPrivate::_q_startConnecting(const QHostInfo &hostInfo) qDebug("QAbstractSocketPrivate::_q_startConnecting(), host not found"); #endif state = QAbstractSocket::UnconnectedState; - socketError = QAbstractSocket::HostNotFoundError; - q->setErrorString(QAbstractSocket::tr("Host not found")); + setError(QAbstractSocket::HostNotFoundError, QAbstractSocket::tr("Host not found")); emit q->stateChanged(state); emit q->error(QAbstractSocket::HostNotFoundError); return; @@ -1114,11 +1114,10 @@ void QAbstractSocketPrivate::_q_connectToNextAddress() || socketEngine->error() == QAbstractSocket::UnsupportedSocketOperationError #endif ) && socketEngine->state() == QAbstractSocket::ConnectingState) { - socketError = QAbstractSocket::ConnectionRefusedError; - q->setErrorString(QAbstractSocket::tr("Connection refused")); + setError(QAbstractSocket::ConnectionRefusedError, + QAbstractSocket::tr("Connection refused")); } else { - socketError = socketEngine->error(); - q->setErrorString(socketEngine->errorString()); + setError(socketEngine->error(), socketEngine->errorString()); } } else { // socketError = QAbstractSocket::ConnectionRefusedError; @@ -1242,8 +1241,8 @@ void QAbstractSocketPrivate::_q_abortConnectionAttempt() if (addresses.isEmpty()) { state = QAbstractSocket::UnconnectedState; - socketError = QAbstractSocket::SocketTimeoutError; - q->setErrorString(QAbstractSocket::tr("Connection timed out")); + setError(QAbstractSocket::SocketTimeoutError, + QAbstractSocket::tr("Connection timed out")); emit q->stateChanged(state); emit q->error(socketError); } else { @@ -1303,9 +1302,7 @@ bool QAbstractSocketPrivate::readFromSocket() #endif if (!socketEngine->isValid()) { - socketError = socketEngine->error(); - q->setErrorString(socketEngine->errorString()); - emit q->error(socketError); + setErrorAndEmit(socketEngine->error(), socketEngine->errorString()); #if defined(QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::readFromSocket() read failed: %s", q->errorString().toLatin1().constData()); @@ -1390,6 +1387,31 @@ QAbstractSocketEngine* QAbstractSocketPrivate::getSocketEngine(QAbstractSocket * return socket->d_func()->socketEngine; } +/*! + \internal + + Sets the socket error state to \c errorCode and \a errorString. +*/ +void QAbstractSocketPrivate::setError(QAbstractSocket::SocketError errorCode, + const QString &errStr) +{ + socketError = errorCode; + errorString = errStr; +} + +/*! + \internal + + Sets the socket error state to \c errorCode and \a errorString, + and emits the QAbstractSocket::error() signal. +*/ +void QAbstractSocketPrivate::setErrorAndEmit(QAbstractSocket::SocketError errorCode, + const QString &errorString) +{ + Q_Q(QAbstractSocket); + setError(errorCode, errorString); + emit q->error(errorCode); +} /*! \internal @@ -1554,9 +1576,7 @@ bool QAbstractSocketPrivate::bind(const QHostAddress &address, quint16 port, QAb cachedSocketDescriptor = socketEngine->socketDescriptor(); if (!result) { - socketError = socketEngine->error(); - q->setErrorString(socketEngine->errorString()); - emit q->error(socketError); + setErrorAndEmit(socketEngine->error(), socketEngine->errorString()); return false; } @@ -1633,9 +1653,7 @@ void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, if (d->state == ConnectedState || d->state == ConnectingState || d->state == ClosingState || d->state == HostLookupState) { qWarning("QAbstractSocket::connectToHost() called when already looking up or connecting/connected to \"%s\"", qPrintable(hostName)); - d->socketError = QAbstractSocket::OperationError; - setErrorString(QAbstractSocket::tr("Trying to connect while connection is in progress")); - emit error(d->socketError); + d->setErrorAndEmit(OperationError, tr("Trying to connect while connection is in progress")); return; } @@ -1664,9 +1682,8 @@ void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, d->resolveProxy(hostName, port); if (d->proxyInUse.type() == QNetworkProxy::DefaultProxy) { // failed to setup the proxy - d->socketError = QAbstractSocket::UnsupportedSocketOperationError; - setErrorString(QAbstractSocket::tr("Operation on socket is not supported")); - emit error(d->socketError); + d->setErrorAndEmit(UnsupportedSocketOperationError, + tr("Operation on socket is not supported")); return; } #endif @@ -1886,8 +1903,7 @@ bool QAbstractSocket::setSocketDescriptor(qintptr socketDescriptor, SocketState d->buffer.clear(); d->socketEngine = QAbstractSocketEngine::createSocketEngine(socketDescriptor, this); if (!d->socketEngine) { - d->socketError = UnsupportedSocketOperationError; - setErrorString(tr("Operation on socket is not supported")); + d->setError(UnsupportedSocketOperationError, tr("Operation on socket is not supported")); return false; } #ifndef QT_NO_BEARERMANAGEMENT @@ -1896,8 +1912,7 @@ bool QAbstractSocket::setSocketDescriptor(qintptr socketDescriptor, SocketState #endif bool result = d->socketEngine->initialize(socketDescriptor, socketState); if (!result) { - d->socketError = d->socketEngine->error(); - setErrorString(d->socketEngine->errorString()); + d->setError(d->socketEngine->error(), d->socketEngine->errorString()); return false; } @@ -2107,11 +2122,10 @@ bool QAbstractSocket::waitForConnected(int msecs) } if ((timedOut && state() != ConnectedState) || state() == ConnectingState) { - d->socketError = SocketTimeoutError; + d->setError(SocketTimeoutError, tr("Socket operation timed out")); d->state = UnconnectedState; emit stateChanged(d->state); d->resetSocketLayer(); - setErrorString(tr("Socket operation timed out")); } #if defined (QABSTRACTSOCKET_DEBUG) @@ -2170,13 +2184,11 @@ bool QAbstractSocket::waitForReadyRead(int msecs) bool readyToWrite = false; if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, !d->writeBuffer.isEmpty(), qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) { - d->socketError = d->socketEngine->error(); - setErrorString(d->socketEngine->errorString()); #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)", - msecs, d->socketError, errorString().toLatin1().constData()); + msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData()); #endif - emit error(d->socketError); + d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString()); if (d->socketError != SocketTimeoutError) close(); return false; @@ -2242,13 +2254,11 @@ bool QAbstractSocket::waitForBytesWritten(int msecs) bool readyToWrite = false; if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, !d->writeBuffer.isEmpty(), qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) { - d->socketError = d->socketEngine->error(); - setErrorString(d->socketEngine->errorString()); #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocket::waitForBytesWritten(%i) failed (%i, %s)", - msecs, d->socketError, errorString().toLatin1().constData()); + msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData()); #endif - emit error(d->socketError); + d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString()); if (d->socketError != SocketTimeoutError) close(); return false; @@ -2324,13 +2334,11 @@ bool QAbstractSocket::waitForDisconnected(int msecs) if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, state() == ConnectedState, !d->writeBuffer.isEmpty(), qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) { - d->socketError = d->socketEngine->error(); - setErrorString(d->socketEngine->errorString()); #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)", - msecs, d->socketError, errorString().toLatin1().constData()); + msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData()); #endif - emit error(d->socketError); + d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString()); if (d->socketError != SocketTimeoutError) close(); return false; @@ -2461,8 +2469,7 @@ qint64 QAbstractSocket::readData(char *data, qint64 maxSize) // -2 from the engine means no bytes available (EAGAIN) so read more later return 0; } else if (readBytes < 0) { - d->socketError = d->socketEngine->error(); - setErrorString(d->socketEngine->errorString()); + d->setError(d->socketEngine->error(), d->socketEngine->errorString()); d->resetSocketLayer(); d->state = QAbstractSocket::UnconnectedState; } else if (!d->socketEngine->isReadNotificationEnabled()) { @@ -2492,8 +2499,7 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size) { Q_D(QAbstractSocket); if (d->state == QAbstractSocket::UnconnectedState) { - d->socketError = QAbstractSocket::UnknownSocketError; - setErrorString(tr("Socket is not connected")); + d->setError(UnknownSocketError, tr("Socket is not connected")); return -1; } @@ -2501,8 +2507,7 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size) // This code is for the new Unbuffered QTcpSocket use case qint64 written = d->socketEngine->write(data, size); if (written < 0) { - d->socketError = d->socketEngine->error(); - setErrorString(d->socketEngine->errorString()); + d->setError(d->socketEngine->error(), d->socketEngine->errorString()); return written; } else if (written < size) { // Buffer what was not written yet @@ -2516,8 +2521,7 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size) // This is for a QUdpSocket that was connect()ed qint64 written = d->socketEngine->write(data, size); if (written < 0) { - d->socketError = d->socketEngine->error(); - setErrorString(d->socketEngine->errorString()); + d->setError(d->socketEngine->error(), d->socketEngine->errorString()); } else if (!d->writeBuffer.isEmpty()) { d->socketEngine->setWriteNotificationEnabled(true); } diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h index c0662f01b4..ba129b48df 100644 --- a/src/network/socket/qabstractsocket_p.h +++ b/src/network/socket/qabstractsocket_p.h @@ -138,6 +138,9 @@ public: bool writeToSocket(); void emitReadyRead(); + void setError(QAbstractSocket::SocketError errorCode, const QString &errorString); + void setErrorAndEmit(QAbstractSocket::SocketError errorCode, const QString &errorString); + qint64 readBufferMaxSize; QRingBuffer writeBuffer; diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index f2a3149678..d2b5882d18 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Intel Corporation. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -58,6 +59,30 @@ class QNetworkInterface; #endif class QNetworkProxy; +class QIpPacketHeader +{ +public: + QIpPacketHeader(const QHostAddress &dstAddr = QHostAddress(), quint16 port = 0) + : destinationAddress(dstAddr), ifindex(0), hopLimit(-1), destinationPort(port) + {} + + void clear() + { + senderAddress.clear(); + destinationAddress.clear(); + ifindex = 0; + hopLimit = -1; + } + + QHostAddress senderAddress; + QHostAddress destinationAddress; + + uint ifindex; + qint16 hopLimit; + quint16 senderPort; + quint16 destinationPort; +}; + class QAbstractSocketEngineReceiver { public: virtual ~QAbstractSocketEngineReceiver(){} @@ -93,9 +118,21 @@ public: KeepAliveOption, MulticastTtlOption, MulticastLoopbackOption, - TypeOfServiceOption + TypeOfServiceOption, + ReceivePacketInformation, + ReceiveHopLimit }; + enum PacketHeaderOption { + WantNone = 0, + WantDatagramSender, + WantDatagramDestination, + WantDatagramHopLimit, + + WantAll = 0xff + }; + Q_DECLARE_FLAGS(PacketHeaderOptions, PacketHeaderOption) + virtual bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) = 0; virtual bool initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState = QAbstractSocket::ConnectedState) = 0; @@ -126,10 +163,9 @@ public: virtual bool setMulticastInterface(const QNetworkInterface &iface) = 0; #endif // QT_NO_NETWORKINTERFACE - virtual qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, - quint16 *port = 0) = 0; - virtual qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, - quint16 port) = 0; + virtual qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header = 0, + PacketHeaderOptions = WantNone) = 0; + virtual qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) = 0; virtual bool hasPendingDatagrams() const = 0; virtual qint64 pendingDatagramSize() const = 0; #endif // QT_NO_UDPSOCKET @@ -225,6 +261,8 @@ private: friend class QAbstractSocketEngine; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractSocketEngine::PacketHeaderOptions) + QT_END_NAMESPACE #endif // QABSTRACTSOCKETENGINE_P_H diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index 23347ce08b..1a90abd22c 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -271,14 +271,12 @@ bool QHttpSocketEngine::setMulticastInterface(const QNetworkInterface &) } #endif // QT_NO_NETWORKINTERFACE -qint64 QHttpSocketEngine::readDatagram(char *, qint64, QHostAddress *, - quint16 *) +qint64 QHttpSocketEngine::readDatagram(char *, qint64, QIpPacketHeader *, PacketHeaderOptions) { return 0; } -qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QHostAddress &, - quint16) +qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QIpPacketHeader &) { return 0; } diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h index 4d90487679..41c63fe11e 100644 --- a/src/network/socket/qhttpsocketengine_p.h +++ b/src/network/socket/qhttpsocketengine_p.h @@ -105,10 +105,9 @@ public: bool setMulticastInterface(const QNetworkInterface &iface) Q_DECL_OVERRIDE; #endif // QT_NO_NETWORKINTERFACE - qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, - quint16 *port = 0) Q_DECL_OVERRIDE; - qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, - quint16 port) Q_DECL_OVERRIDE; + qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *, + PacketHeaderOptions) Q_DECL_OVERRIDE; + qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE; bool hasPendingDatagrams() const Q_DECL_OVERRIDE; qint64 pendingDatagramSize() const Q_DECL_OVERRIDE; #endif // QT_NO_UDPSOCKET diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index 33bf3af0b5..c11b889220 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -88,6 +88,25 @@ errorString() can be called to determine the cause of the error. */ +/*! + \enum QAbstractSocketEngine::PacketHeaderOption + + Specifies which fields in the IP packet header are desired in the call to + readDatagram(). + + \value WantNone caller isn't interested in the packet metadata + \value WantDatagramSender caller wants the sender address and port number + \value WantDatagramDestination caller wants the packet's destination address and port number + (this option is useful to distinguish multicast packets from unicast) + \value WantDatagramHopLimit caller wants the packet's remaining hop limit or time to live + (this option is useful in IPv4 multicasting, where the TTL is used + to indicate the realm) + \value WantAll this is a catch-all value to indicate the caller is + interested in all the available information + + \sa readDatagram(), QUdpDatagram +*/ + #include "qnativesocketengine_p.h" #include <qabstracteventdispatcher.h> @@ -152,10 +171,6 @@ QT_BEGIN_NAMESPACE /*! \internal Constructs the private class and initializes all data members. - - On Windows, WSAStartup is called "recursively" for every - concurrent QNativeSocketEngine. This is safe, because WSAStartup and - WSACleanup are reference counted. */ QNativeSocketEnginePrivate::QNativeSocketEnginePrivate() : socketDescriptor(-1), @@ -163,6 +178,9 @@ QNativeSocketEnginePrivate::QNativeSocketEnginePrivate() : writeNotifier(0), exceptNotifier(0) { +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + QSysInfo::machineHostName(); // this initializes ws2_32.dll +#endif } /*! \internal @@ -395,13 +413,18 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb return false; } - // Set the broadcasting flag if it's a UDP socket. - if (socketType == QAbstractSocket::UdpSocket - && !setOption(BroadcastSocketOption, 1)) { - d->setError(QAbstractSocket::UnsupportedSocketOperationError, - QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString); - close(); - return false; + if (socketType == QAbstractSocket::UdpSocket) { + // Set the broadcasting flag if it's a UDP socket. + if (!setOption(BroadcastSocketOption, 1)) { + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString); + close(); + return false; + } + + // Set some extra flags that are interesting to us, but accept failure + setOption(ReceivePacketInformation, 1); + setOption(ReceiveHopLimit, 1); } @@ -760,9 +783,8 @@ qint64 QNativeSocketEngine::pendingDatagramSize() const /*! Reads up to \a maxSize bytes of a datagram from the socket, stores it in \a data and returns the number of bytes read. The - address and port of the sender are stored in \a address and \a - port. If either of these pointers is 0, the corresponding value is - discarded. + address, port, and other IP header fields are stored in \a header + according to the request in \a options. To avoid unnecessarily loss of data, call pendingDatagramSize() to determine the size of the pending message before reading it. If \a @@ -772,20 +794,23 @@ qint64 QNativeSocketEngine::pendingDatagramSize() const \sa hasPendingDatagrams() */ -qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxSize, QHostAddress *address, - quint16 *port) +qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxSize, QIpPacketHeader *header, + PacketHeaderOptions options) { Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::readDatagram(), -1); Q_CHECK_TYPE(QNativeSocketEngine::readDatagram(), QAbstractSocket::UdpSocket, -1); - return d->nativeReceiveDatagram(data, maxSize, address, port); + return d->nativeReceiveDatagram(data, maxSize, header, options); } /*! Writes a UDP datagram of size \a size bytes to the socket from - \a data to the address \a host on port \a port, and returns the - number of bytes written, or -1 if an error occurred. + \a data to the destination contained in \a header, and returns the + number of bytes written, or -1 if an error occurred. If \a header + contains other settings like hop limit or source address, this function + will try to pass them to the operating system too, but will not + indicate an error if it could not pass them. Only one datagram is sent, and if there is too much data to fit into a single datagram, the operation will fail and error() @@ -795,18 +820,19 @@ qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxSize, QHostAddres disadvised, as even if they are sent successfully, they are likely to be fragmented before arriving at their destination. - Experience has shown that it is in general safe to send datagrams - no larger than 512 bytes. + Experience has shown that it is in general safe to send IPv4 datagrams + no larger than 512 bytes or IPv6 datagrams no larger than 1280 (the + minimum MTU). \sa readDatagram() */ -qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 size, - const QHostAddress &host, quint16 port) +qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 size, const QIpPacketHeader &header) { Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::writeDatagram(), -1); Q_CHECK_TYPE(QNativeSocketEngine::writeDatagram(), QAbstractSocket::UdpSocket, -1); - return d->nativeSendDatagram(data, size, d->adjustAddressProtocol(host), port); + + return d->nativeSendDatagram(data, size, header); } /*! diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index 24909bf310..0fa1d8f96e 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -51,46 +51,51 @@ # include <netinet/in.h> #else # include <winsock2.h> +# include <ws2tcpip.h> +# include <mswsock.h> #endif QT_BEGIN_NAMESPACE -// Use our own defines and structs which we know are correct -# define QT_SS_MAXSIZE 128 -# define QT_SS_ALIGNSIZE (sizeof(qint64)) -# define QT_SS_PAD1SIZE (QT_SS_ALIGNSIZE - sizeof (short)) -# define QT_SS_PAD2SIZE (QT_SS_MAXSIZE - (sizeof (short) + QT_SS_PAD1SIZE + QT_SS_ALIGNSIZE)) -struct qt_sockaddr_storage { - short ss_family; - char __ss_pad1[QT_SS_PAD1SIZE]; - qint64 __ss_align; - char __ss_pad2[QT_SS_PAD2SIZE]; -}; - #ifdef Q_OS_WIN #define QT_SOCKLEN_T int #define QT_SOCKOPTLEN_T int -#endif -// sockaddr_in6 size changed between old and new SDK -// Only the new version is the correct one, so always -// use this structure. -struct qt_in6_addr { - quint8 qt_s6_addr[16]; -}; -struct qt_sockaddr_in6 { - short sin6_family; /* AF_INET6 */ - quint16 sin6_port; /* Transport level port number */ - quint32 sin6_flowinfo; /* IPv6 flow information */ - struct qt_in6_addr sin6_addr; /* IPv6 address */ - quint32 sin6_scope_id; /* set of interfaces for a scope */ -}; +// The following definitions are copied from the MinGW header mswsock.h which +// was placed in the public domain. The WSASendMsg and WSARecvMsg functions +// were introduced with Windows Vista, so some Win32 headers are lacking them. +// There are no known versions of Windows CE or Embedded that contain them. +#ifndef Q_OS_WINCE +# ifndef WSAID_WSARECVMSG +typedef INT (WINAPI *LPFN_WSARECVMSG)(SOCKET s, LPWSAMSG lpMsg, + LPDWORD lpdwNumberOfBytesRecvd, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); +# define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}} +# endif +# ifndef WSAID_WSASENDMSG +typedef struct { + LPWSAMSG lpMsg; + DWORD dwFlags; + LPDWORD lpNumberOfBytesSent; + LPWSAOVERLAPPED lpOverlapped; + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine; +} WSASENDMSG, *LPWSASENDMSG; + +typedef INT (WSAAPI *LPFN_WSASENDMSG)(SOCKET s, LPWSAMSG lpMsg, DWORD dwFlags, + LPDWORD lpNumberOfBytesSent, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + +# define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}} +# endif +#endif +#endif union qt_sockaddr { sockaddr a; sockaddr_in a4; - qt_sockaddr_in6 a6; - qt_sockaddr_storage storage; + sockaddr_in6 a6; }; class QNativeSocketEnginePrivate; @@ -133,10 +138,9 @@ public: qint64 read(char *data, qint64 maxlen) Q_DECL_OVERRIDE; qint64 write(const char *data, qint64 len) Q_DECL_OVERRIDE; - qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, - quint16 *port = 0) Q_DECL_OVERRIDE; - qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, - quint16 port) Q_DECL_OVERRIDE; + qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0, + PacketHeaderOptions = WantNone) Q_DECL_OVERRIDE; + qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE; bool hasPendingDatagrams() const Q_DECL_OVERRIDE; qint64 pendingDatagramSize() const Q_DECL_OVERRIDE; @@ -173,16 +177,6 @@ private: Q_DISABLE_COPY(QNativeSocketEngine) }; -#ifdef Q_OS_WIN -class QWindowsSockInit -{ -public: - QWindowsSockInit(); - ~QWindowsSockInit(); - int version; -}; -#endif - class QSocketNotifier; class QNativeSocketEnginePrivate : public QAbstractSocketEnginePrivate @@ -196,10 +190,10 @@ public: QSocketNotifier *readNotifier, *writeNotifier, *exceptNotifier; -#ifdef Q_OS_WIN - QWindowsSockInit winSock; -#endif - +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + LPFN_WSASENDMSG sendmsg; + LPFN_WSARECVMSG recvmsg; +# endif enum ErrorString { NonBlockingInitFailedErrorString, BroadcastingInitFailedErrorString, @@ -256,24 +250,48 @@ public: bool nativeHasPendingDatagrams() const; qint64 nativePendingDatagramSize() const; - qint64 nativeReceiveDatagram(char *data, qint64 maxLength, - QHostAddress *address, quint16 *port); - qint64 nativeSendDatagram(const char *data, qint64 length, - const QHostAddress &host, quint16 port); + qint64 nativeReceiveDatagram(char *data, qint64 maxLength, QIpPacketHeader *header, + QAbstractSocketEngine::PacketHeaderOptions options); + qint64 nativeSendDatagram(const char *data, qint64 length, const QIpPacketHeader &header); qint64 nativeRead(char *data, qint64 maxLength); 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; -#ifdef Q_OS_WIN - void setPortAndAddress(sockaddr_in * sockAddrIPv4, qt_sockaddr_in6 * sockAddrIPv6, - quint16 port, const QHostAddress & address, sockaddr ** sockAddrPtr, QT_SOCKLEN_T *sockAddrSize); -#endif void nativeClose(); bool checkProxy(const QHostAddress &address); bool fetchConnectionParameters(); + + static uint scopeIdFromString(const QString &scopeid); + + /*! \internal + Sets \a address and \a port in the \a aa sockaddr structure and the size in \a sockAddrSize. + The address \a is converted to IPv6 if the current socket protocol is also IPv6. + */ + void setPortAndAddress(quint16 port, const QHostAddress &address, qt_sockaddr *aa, QT_SOCKLEN_T *sockAddrSize) + { + if (address.protocol() == QAbstractSocket::IPv6Protocol + || address.protocol() == QAbstractSocket::AnyIPProtocol + || socketProtocol == QAbstractSocket::IPv6Protocol + || socketProtocol == QAbstractSocket::AnyIPProtocol) { + memset(&aa->a6, 0, sizeof(sockaddr_in6)); + aa->a6.sin6_family = AF_INET6; + aa->a6.sin6_scope_id = scopeIdFromString(address.scopeId()); + aa->a6.sin6_port = htons(port); + Q_IPV6ADDR tmp = address.toIPv6Address(); + memcpy(&aa->a6.sin6_addr, &tmp, sizeof(tmp)); + *sockAddrSize = sizeof(sockaddr_in6); + } else { + memset(&aa->a, 0, sizeof(sockaddr_in)); + aa->a4.sin_family = AF_INET; + aa->a4.sin_port = htons(port); + aa->a4.sin_addr.s_addr = htonl(address.toIPv4Address()); + *sockAddrSize = sizeof(sockaddr_in); + } + } + }; QT_END_NAMESPACE diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 5c4e4e885b..0e14c175c5 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -49,6 +49,9 @@ #ifdef QT_LINUXBASE #include <arpa/inet.h> #endif +#ifdef Q_OS_BSD4 +#include <net/if_dl.h> +#endif #if defined QNATIVESOCKETENGINE_DEBUG #include <qstring.h> @@ -128,9 +131,9 @@ static inline void qt_socket_getPortAndAddress(const qt_sockaddr *s, quint16 *po } } -static inline uint makeScopeId(const QHostAddress &addr) +// inline on purpose +inline uint QNativeSocketEnginePrivate::scopeIdFromString(const QString &scopeid) { - QString scopeid = addr.scopeId(); if (scopeid.isEmpty()) return 0; @@ -202,6 +205,32 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, n = IP_TOS; } break; + case QNativeSocketEngine::ReceivePacketInformation: + if (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) { + level = IPPROTO_IPV6; + n = IPV6_RECVPKTINFO; + } else if (socketProtocol == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; +#ifdef IP_PKTINFO + n = IP_PKTINFO; +#elif defined(IP_RECVDSTADDR) + // variant found in QNX and FreeBSD; it will get us only the + // destination address, not the interface; we need IP_RECVIF for that. + n = IP_RECVDSTADDR; +#endif + } + break; + case QNativeSocketEngine::ReceiveHopLimit: + if (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) { + level = IPPROTO_IPV6; + n = IPV6_RECVHOPLIMIT; + } else if (socketProtocol == QAbstractSocket::IPv4Protocol) { +#ifdef IP_RECVTTL // IP_RECVTTL is a non-standard extension supported on some OS + level = IPPROTO_IP; + n = IP_RECVTTL; +#endif + } + break; } } @@ -285,7 +314,7 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co QT_SOCKOPTLEN_T len = sizeof(v); convertToLevelAndOption(opt, socketProtocol, level, n); - if (::getsockopt(socketDescriptor, level, n, (char *) &v, &len) != -1) + if (n != -1 && ::getsockopt(socketDescriptor, level, n, (char *) &v, &len) != -1) return v; return -1; @@ -360,37 +389,11 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16 qDebug() << "QNativeSocketEnginePrivate::nativeConnect() " << socketDescriptor; #endif - struct sockaddr_in sockAddrIPv4; - struct sockaddr *sockAddrPtr = 0; - QT_SOCKLEN_T sockAddrSize = 0; - - struct sockaddr_in6 sockAddrIPv6; - - if (addr.protocol() == QAbstractSocket::IPv6Protocol) { - memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6)); - sockAddrIPv6.sin6_family = AF_INET6; - sockAddrIPv6.sin6_port = htons(port); - sockAddrIPv6.sin6_scope_id = makeScopeId(addr); - - Q_IPV6ADDR ip6 = addr.toIPv6Address(); - memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6)); - - sockAddrSize = sizeof(sockAddrIPv6); - sockAddrPtr = (struct sockaddr *) &sockAddrIPv6; - } else - if (addr.protocol() == QAbstractSocket::IPv4Protocol) { - memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4)); - sockAddrIPv4.sin_family = AF_INET; - sockAddrIPv4.sin_port = htons(port); - sockAddrIPv4.sin_addr.s_addr = htonl(addr.toIPv4Address()); - - sockAddrSize = sizeof(sockAddrIPv4); - sockAddrPtr = (struct sockaddr *) &sockAddrIPv4; - } else { - // unreachable - } + qt_sockaddr aa; + QT_SOCKLEN_T sockAddrSize; + setPortAndAddress(port, addr, &aa, &sockAddrSize); - int connectResult = qt_safe_connect(socketDescriptor, sockAddrPtr, sockAddrSize); + int connectResult = qt_safe_connect(socketDescriptor, &aa.a, sockAddrSize); #if defined (QNATIVESOCKETENGINE_DEBUG) int ecopy = errno; #endif @@ -462,51 +465,28 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16 bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &address, quint16 port) { - struct sockaddr_in sockAddrIPv4; - struct sockaddr *sockAddrPtr = 0; - QT_SOCKLEN_T sockAddrSize = 0; - - - struct sockaddr_in6 sockAddrIPv6; + qt_sockaddr aa; + QT_SOCKLEN_T sockAddrSize; + setPortAndAddress(port, address, &aa, &sockAddrSize); - if (address.protocol() == QAbstractSocket::IPv6Protocol || address.protocol() == QAbstractSocket::AnyIPProtocol) { #ifdef IPV6_V6ONLY + if (aa.a.sa_family == AF_INET6) { int ipv6only = 0; if (address.protocol() == QAbstractSocket::IPv6Protocol) ipv6only = 1; //default value of this socket option varies depending on unix variant (or system configuration on BSD), so always set it explicitly ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) ); + } #endif - memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6)); - sockAddrIPv6.sin6_family = AF_INET6; - sockAddrIPv6.sin6_port = htons(port); - sockAddrIPv6.sin6_scope_id = makeScopeId(address); - - Q_IPV6ADDR tmp = address.toIPv6Address(); - memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &tmp, sizeof(tmp)); - sockAddrSize = sizeof(sockAddrIPv6); - sockAddrPtr = (struct sockaddr *) &sockAddrIPv6; - } else - if (address.protocol() == QAbstractSocket::IPv4Protocol) { - memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4)); - sockAddrIPv4.sin_family = AF_INET; - sockAddrIPv4.sin_port = htons(port); - sockAddrIPv4.sin_addr.s_addr = htonl(address.toIPv4Address()); - sockAddrSize = sizeof(sockAddrIPv4); - sockAddrPtr = (struct sockaddr *) &sockAddrIPv4; - } else { - // unreachable - } - int bindResult = QT_SOCKET_BIND(socketDescriptor, sockAddrPtr, sockAddrSize); + int bindResult = QT_SOCKET_BIND(socketDescriptor, &aa.a, sockAddrSize); if (bindResult < 0 && errno == EAFNOSUPPORT && address.protocol() == QAbstractSocket::AnyIPProtocol) { - memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4)); - sockAddrIPv4.sin_family = AF_INET; - sockAddrIPv4.sin_port = htons(port); - sockAddrIPv4.sin_addr.s_addr = htonl(address.toIPv4Address()); - sockAddrSize = sizeof(sockAddrIPv4); - sockAddrPtr = (struct sockaddr *) &sockAddrIPv4; - bindResult = QT_SOCKET_BIND(socketDescriptor, sockAddrPtr, sockAddrSize); + // retry with v4 + aa.a4.sin_family = AF_INET; + aa.a4.sin_port = htons(port); + aa.a4.sin_addr.s_addr = htonl(address.toIPv4Address()); + sockAddrSize = sizeof(aa.a4); + bindResult = QT_SOCKET_BIND(socketDescriptor, &aa.a, sockAddrSize); } if (bindResult < 0) { @@ -844,68 +824,183 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const return qint64(recvResult); } -qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxSize, - QHostAddress *address, quint16 *port) +qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxSize, QIpPacketHeader *header, + QAbstractSocketEngine::PacketHeaderOptions options) { + // we use quintptr to force the alignment + quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) + sizeof(quintptr) - 1) / sizeof(quintptr)]; + + struct msghdr msg; + struct iovec vec; qt_sockaddr aa; + char c; + memset(&msg, 0, sizeof(msg)); memset(&aa, 0, sizeof(aa)); - QT_SOCKLEN_T sz; - sz = sizeof(aa); - ssize_t recvFromResult = 0; + // we need to receive at least one byte, even if our user isn't interested in it + vec.iov_base = maxSize ? data : &c; + vec.iov_len = maxSize ? maxSize : 1; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + if (options & QAbstractSocketEngine::WantDatagramSender) { + msg.msg_name = &aa; + msg.msg_namelen = sizeof(aa); + } + if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination)) { + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + } + + ssize_t recvResult = 0; do { - char c; - recvFromResult = ::recvfrom(socketDescriptor, maxSize ? data : &c, maxSize ? maxSize : 1, - 0, &aa.a, &sz); - } while (recvFromResult == -1 && errno == EINTR); + recvResult = ::recvmsg(socketDescriptor, &msg, 0); + } while (recvResult == -1 && errno == EINTR); - if (recvFromResult == -1) { + if (recvResult == -1) { setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString); - } else if (port || address) { - qt_socket_getPortAndAddress(&aa, port, address); + if (header) + header->clear(); + } else if (options != QAbstractSocketEngine::WantNone) { + Q_ASSERT(header); + qt_socket_getPortAndAddress(&aa, &header->senderPort, &header->senderAddress); + header->destinationPort = localPort; + + // parse the ancillary data + struct cmsghdr *cmsgptr; + for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; + cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_PKTINFO + && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(in6_pktinfo))) { + in6_pktinfo *info = reinterpret_cast<in6_pktinfo *>(CMSG_DATA(cmsgptr)); + + header->destinationAddress.setAddress(reinterpret_cast<quint8 *>(&info->ipi6_addr)); + header->ifindex = info->ipi6_ifindex; + if (header->ifindex) + header->destinationAddress.setScopeId(QString::number(info->ipi6_ifindex)); + } + +#ifdef IP_PKTINFO + if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_PKTINFO + && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(in_pktinfo))) { + in_pktinfo *info = reinterpret_cast<in_pktinfo *>(CMSG_DATA(cmsgptr)); + + header->destinationAddress.setAddress(ntohl(info->ipi_addr.s_addr)); + header->ifindex = info->ipi_ifindex; + } +#else +# ifdef IP_RECVDSTADDR + if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_RECVDSTADDR + && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(in_addr))) { + in_addr *addr = reinterpret_cast<in_addr *>(CMSG_DATA(cmsgptr)); + + header->destinationAddress.setAddress(ntohl(addr->s_addr)); + } +# endif +# if defined(IP_RECVIF) && defined(Q_OS_BSD4) + if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_RECVIF + && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(sockaddr_dl))) { + sockaddr_dl *sdl = reinterpret_cast<sockaddr_dl *>(CMSG_DATA(cmsgptr)); + + header->ifindex = LLINDEX(sdl); + } +# endif +#endif + + if (cmsgptr->cmsg_len == CMSG_LEN(sizeof(int)) + && ((cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_HOPLIMIT) + || (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_TTL))) { + header->hopLimit = *reinterpret_cast<int *>(CMSG_DATA(cmsgptr)); + } + } } #if defined (QNATIVESOCKETENGINE_DEBUG) qDebug("QNativeSocketEnginePrivate::nativeReceiveDatagram(%p \"%s\", %lli, %s, %i) == %lli", - data, qt_prettyDebug(data, qMin(recvFromResult, ssize_t(16)), recvFromResult).data(), maxSize, + data, qt_prettyDebug(data, qMin(recvResult, ssize_t(16)), recvResult).data(), maxSize, address ? address->toString().toLatin1().constData() : "(nil)", - port ? *port : 0, (qint64) recvFromResult); + port ? *port : 0, (qint64) recvResult); #endif - return qint64(maxSize ? recvFromResult : recvFromResult == -1 ? -1 : 0); + return qint64(maxSize ? recvResult : recvResult == -1 ? -1 : 0); } -qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 len, - const QHostAddress &host, quint16 port) +qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 len, const QIpPacketHeader &header) { - struct sockaddr_in sockAddrIPv4; - struct sockaddr *sockAddrPtr = 0; - QT_SOCKLEN_T sockAddrSize = 0; - - struct sockaddr_in6 sockAddrIPv6; - if (host.protocol() == QAbstractSocket::IPv6Protocol - || socketProtocol == QAbstractSocket::IPv6Protocol - || socketProtocol == QAbstractSocket::AnyIPProtocol) { - memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6)); - sockAddrIPv6.sin6_family = AF_INET6; - sockAddrIPv6.sin6_port = htons(port); - sockAddrIPv6.sin6_scope_id = makeScopeId(host); - - Q_IPV6ADDR tmp = host.toIPv6Address(); - memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &tmp, sizeof(tmp)); - sockAddrSize = sizeof(sockAddrIPv6); - sockAddrPtr = (struct sockaddr *)&sockAddrIPv6; - } else if (host.protocol() == QAbstractSocket::IPv4Protocol) { - memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4)); - sockAddrIPv4.sin_family = AF_INET; - sockAddrIPv4.sin_port = htons(port); - sockAddrIPv4.sin_addr.s_addr = htonl(host.toIPv4Address()); - sockAddrSize = sizeof(sockAddrIPv4); - sockAddrPtr = (struct sockaddr *)&sockAddrIPv4; + // we use quintptr to force the alignment + quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) + sizeof(quintptr) - 1) / sizeof(quintptr)]; + + struct cmsghdr *cmsgptr = reinterpret_cast<struct cmsghdr *>(cbuf); + struct msghdr msg; + struct iovec vec; + qt_sockaddr aa; + + memset(&msg, 0, sizeof(msg)); + memset(&aa, 0, sizeof(aa)); + vec.iov_base = const_cast<char *>(data); + vec.iov_len = len; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_name = &aa.a; + msg.msg_control = &cbuf; + + setPortAndAddress(header.destinationPort, header.destinationAddress, &aa, &msg.msg_namelen); + + if (msg.msg_namelen == sizeof(aa.a6)) { + if (header.hopLimit != -1) { + msg.msg_controllen += CMSG_SPACE(sizeof(int)); + cmsgptr->cmsg_len = CMSG_LEN(sizeof(int)); + cmsgptr->cmsg_level = IPPROTO_IPV6; + cmsgptr->cmsg_type = IPV6_HOPLIMIT; + memcpy(CMSG_DATA(cmsgptr), &header.hopLimit, sizeof(int)); + cmsgptr = reinterpret_cast<cmsghdr *>(reinterpret_cast<char *>(cmsgptr) + CMSG_SPACE(sizeof(int))); + } + if (header.ifindex != 0 || !header.senderAddress.isNull()) { + struct in6_pktinfo *data = reinterpret_cast<in6_pktinfo *>(CMSG_DATA(cmsgptr)); + memset(data, 0, sizeof(*data)); + msg.msg_controllen += CMSG_SPACE(sizeof(*data)); + cmsgptr->cmsg_len = CMSG_LEN(sizeof(*data)); + cmsgptr->cmsg_level = IPPROTO_IPV6; + cmsgptr->cmsg_type = IPV6_PKTINFO; + data->ipi6_ifindex = header.ifindex; + + QIPv6Address tmp = header.senderAddress.toIPv6Address(); + memcpy(&data->ipi6_addr, &tmp, sizeof(tmp)); + cmsgptr = reinterpret_cast<cmsghdr *>(reinterpret_cast<char *>(cmsgptr) + CMSG_SPACE(sizeof(*data))); + } + } else { + if (header.hopLimit != -1) { + msg.msg_controllen += CMSG_SPACE(sizeof(int)); + cmsgptr->cmsg_len = CMSG_LEN(sizeof(int)); + cmsgptr->cmsg_level = IPPROTO_IP; + cmsgptr->cmsg_type = IP_TTL; + memcpy(CMSG_DATA(cmsgptr), &header.hopLimit, sizeof(int)); + cmsgptr = reinterpret_cast<cmsghdr *>(reinterpret_cast<char *>(cmsgptr) + CMSG_SPACE(sizeof(int))); + } + +#if defined(IP_PKTINFO) || defined(IP_SENDSRCADDR) + if (header.ifindex != 0 || !header.senderAddress.isNull()) { +# ifdef IP_PKTINFO + struct in_pktinfo *data = reinterpret_cast<in_pktinfo *>(CMSG_DATA(cmsgptr)); + memset(data, 0, sizeof(*data)); + cmsgptr->cmsg_type = IP_PKTINFO; + data->ipi_ifindex = header.ifindex; + data->ipi_addr.s_addr = htonl(header.senderAddress.toIPv4Address()); +# elif defined(IP_SENDSRCADDR) + struct in_addr *data = reinterpret_cast<in_addr *>(CMSG_DATA(cmsgptr)); + cmsgptr->cmsg_type = IP_SENDSRCADDR; + addr->s_addr = htonl(header.senderAddress.toIPv4Address()); +# endif + cmsgptr->cmsg_level = IPPROTO_IP; + msg.msg_controllen += CMSG_SPACE(sizeof(*data)); + cmsgptr->cmsg_len = CMSG_LEN(sizeof(*data)); + cmsgptr = reinterpret_cast<cmsghdr *>(reinterpret_cast<char *>(cmsgptr) + CMSG_SPACE(sizeof(*data))); + } +#endif } - ssize_t sentBytes = qt_safe_sendto(socketDescriptor, data, len, - 0, sockAddrPtr, sockAddrSize); + if (msg.msg_controllen == 0) + msg.msg_control = 0; + ssize_t sentBytes = qt_safe_sendmsg(socketDescriptor, &msg, 0); if (sentBytes < 0) { switch (errno) { diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 7856db0487..2e905ad610 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -59,6 +59,9 @@ QT_BEGIN_NAMESPACE #ifndef IPV6_V6ONLY #define IPV6_V6ONLY 27 #endif +#ifndef IP_HOPLIMIT +#define IP_HOPLIMIT 21 // Receive packet hop limit. +#endif #if defined(QNATIVESOCKETENGINE_DEBUG) @@ -168,10 +171,10 @@ static QByteArray qt_prettyDebug(const char *data, int len, int maxLength) static inline void qt_socket_getPortAndAddress(SOCKET socketDescriptor, const qt_sockaddr *sa, quint16 *port, QHostAddress *address) { if (sa->a.sa_family == AF_INET6) { - const qt_sockaddr_in6 *sa6 = &sa->a6; + const sockaddr_in6 *sa6 = &sa->a6; Q_IPV6ADDR tmp; for (int i = 0; i < 16; ++i) - tmp.c[i] = sa6->sin6_addr.qt_s6_addr[i]; + tmp.c[i] = sa6->sin6_addr.s6_addr[i]; if (address) { QHostAddress a; a.setAddress(tmp); @@ -252,40 +255,24 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, n = IP_MULTICAST_LOOP; } break; - } -} - -/*! \internal - - Sets the port and address to a sockaddr. Requires that sa point to the IPv6 struct if the address is IPv6. -*/ -void QNativeSocketEnginePrivate::setPortAndAddress(sockaddr_in * sockAddrIPv4, qt_sockaddr_in6 * sockAddrIPv6, - quint16 port, const QHostAddress & address, sockaddr ** sockAddrPtr, QT_SOCKLEN_T *sockAddrSize) -{ - if (address.protocol() == QAbstractSocket::IPv6Protocol - || address.protocol() == QAbstractSocket::AnyIPProtocol - || socketProtocol == QAbstractSocket::IPv6Protocol - || socketProtocol == QAbstractSocket::AnyIPProtocol) { - memset(sockAddrIPv6, 0, sizeof(qt_sockaddr_in6)); - sockAddrIPv6->sin6_family = AF_INET6; - sockAddrIPv6->sin6_scope_id = address.scopeId().toUInt(); - WSAHtons(socketDescriptor, port, &(sockAddrIPv6->sin6_port)); - Q_IPV6ADDR tmp = address.toIPv6Address(); - memcpy(&(sockAddrIPv6->sin6_addr.qt_s6_addr), &tmp, sizeof(tmp)); - *sockAddrSize = sizeof(qt_sockaddr_in6); - *sockAddrPtr = (struct sockaddr *) sockAddrIPv6; - } else - - if (address.protocol() == QAbstractSocket::IPv4Protocol - || address.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) { - memset(sockAddrIPv4, 0, sizeof(sockaddr_in)); - sockAddrIPv4->sin_family = AF_INET; - WSAHtons(socketDescriptor, port, &(sockAddrIPv4->sin_port)); - WSAHtonl(socketDescriptor, address.toIPv4Address(), &(sockAddrIPv4->sin_addr.s_addr)); - *sockAddrSize = sizeof(sockaddr_in); - *sockAddrPtr = (struct sockaddr *) sockAddrIPv4; - } else { - // unreachable + case QNativeSocketEngine::ReceivePacketInformation: + if (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) { + level = IPPROTO_IPV6; + n = IPV6_PKTINFO; + } else if (socketProtocol == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; + n = IP_PKTINFO; + } + break; + case QNativeSocketEngine::ReceiveHopLimit: + if (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) { + level = IPPROTO_IPV6; + n = IPV6_HOPLIMIT; + } else if (socketProtocol == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; + n = IP_HOPLIMIT; + } + break; } } @@ -320,25 +307,6 @@ static inline int qt_socket_getMaxMsgSize(qintptr socketDescriptor) return value; } -QWindowsSockInit::QWindowsSockInit() -: version(0) -{ - //### should we try for 2.2 on all platforms ?? - WSAData wsadata; - - // IPv6 requires Winsock v2.0 or better. - if (WSAStartup(MAKEWORD(2,0), &wsadata) != 0) { - qWarning("QTcpSocketAPI: WinSock v2.0 initialization failed."); - } else { - version = 0x20; - } -} - -QWindowsSockInit::~QWindowsSockInit() -{ - WSACleanup(); -} - // MS Transport Provider IOCTL to control // reporting PORT_UNREACHABLE messages // on UDP sockets via recv/WSARecv/etc. @@ -354,6 +322,12 @@ QWindowsSockInit::~QWindowsSockInit() # define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) #endif +// inline on purpose +inline uint QNativeSocketEnginePrivate::scopeIdFromString(const QString &scopeid) +{ + return scopeid.toUInt(); +} + bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol &socketProtocol) { @@ -439,6 +413,20 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc WS_ERROR_DEBUG(err); } } + + // get the pointer to sendmsg and recvmsg + DWORD bytesReturned; + GUID recvmsgguid = WSAID_WSARECVMSG; + if (WSAIoctl(socketDescriptor, SIO_GET_EXTENSION_FUNCTION_POINTER, + &recvmsgguid, sizeof(recvmsgguid), + &recvmsg, sizeof(recvmsg), &bytesReturned, NULL, NULL) == SOCKET_ERROR) + recvmsg = 0; + + GUID sendmsgguid = WSAID_WSASENDMSG; + if (WSAIoctl(socketDescriptor, SIO_GET_EXTENSION_FUNCTION_POINTER, + &sendmsgguid, sizeof(sendmsgguid), + &sendmsg, sizeof(sendmsg), &bytesReturned, NULL, NULL) == SOCKET_ERROR) + sendmsg = 0; #endif socketDescriptor = socket; @@ -630,12 +618,10 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin qDebug("QNativeSocketEnginePrivate::nativeConnect() to %s :: %i", address.toString().toLatin1().constData(), port); #endif - struct sockaddr_in sockAddrIPv4; - qt_sockaddr_in6 sockAddrIPv6; - struct sockaddr *sockAddrPtr = 0; + qt_sockaddr aa; QT_SOCKLEN_T sockAddrSize = 0; - setPortAndAddress(&sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize); + setPortAndAddress(port, address, &aa, &sockAddrSize); if ((socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) && address.toIPv4Address()) { //IPV6_V6ONLY option must be cleared to connect to a V4 mapped address @@ -646,7 +632,7 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin } forever { - int connectResult = ::WSAConnect(socketDescriptor, sockAddrPtr, sockAddrSize, 0,0,0,0); + int connectResult = ::WSAConnect(socketDescriptor, &aa.a, sockAddrSize, 0,0,0,0); if (connectResult == SOCKET_ERROR) { int err = WSAGetLastError(); WS_ERROR_DEBUG(err); @@ -769,46 +755,36 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &a, quint16 port) { QHostAddress address = a; - DWORD ipv6only = 0; - switch (address.protocol()) { - case QAbstractSocket::IPv6Protocol: - if (address.toIPv6Address()[0] == 0xff) { - // binding to a multicast address - address = QHostAddress(QHostAddress::AnyIPv6); - } - //This is default in current windows versions, it may change in future so set it explicitly - if (QSysInfo::windowsVersion() >= QSysInfo::WV_6_0) { - ipv6only = 1; - ipv6only = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) ); - } - break; - case QAbstractSocket::IPv4Protocol: + if (address.protocol() == QAbstractSocket::IPv4Protocol) { if ((address.toIPv4Address() & 0xffff0000) == 0xefff0000) { // binding to a multicast address address = QHostAddress(QHostAddress::AnyIPv4); } - break; - case QAbstractSocket::AnyIPProtocol: - if (QSysInfo::windowsVersion() >= QSysInfo::WV_6_0) { - ipv6only = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) ); - } else { - address = QHostAddress(QHostAddress::AnyIPv4); //xp/WS2003 and earlier don't support dual stack, so bind to IPv4 - socketProtocol = QAbstractSocket::IPv4Protocol; - } - break; - default: - break; } - struct sockaddr_in sockAddrIPv4; - qt_sockaddr_in6 sockAddrIPv6; - struct sockaddr *sockAddrPtr = 0; + qt_sockaddr aa; QT_SOCKLEN_T sockAddrSize = 0; + setPortAndAddress(port, address, &aa, &sockAddrSize); - setPortAndAddress(&sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize); + if (aa.a.sa_family == AF_INET6) { + // The default may change in future, so set it explicitly + int ipv6only = 0; + if (address.protocol() == QAbstractSocket::IPv6Protocol) + ipv6only = 1; + ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) ); + } - int bindResult = ::bind(socketDescriptor, sockAddrPtr, sockAddrSize); + int bindResult = ::bind(socketDescriptor, &aa.a, sockAddrSize); + if (bindResult == SOCKET_ERROR && WSAGetLastError() == WSAEAFNOSUPPORT + && address.protocol() == QAbstractSocket::AnyIPProtocol) { + // retry with v4 + aa.a4.sin_family = AF_INET; + aa.a4.sin_port = htons(port); + aa.a4.sin_addr.s_addr = htonl(address.toIPv4Address()); + sockAddrSize = sizeof(aa.a4); + bindResult = ::bind(socketDescriptor, &aa.a, sockAddrSize); + } if (bindResult == SOCKET_ERROR) { int err = WSAGetLastError(); WS_ERROR_DEBUG(err); @@ -1215,33 +1191,47 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const return ret; } +#ifdef Q_OS_WINCE +// Windows CE has no support for sendmsg or recvmsg. We set it to null here to simplify the code below. +static int (*const recvmsg)(...) = 0; +static int (*const sendmsg)(...) = 0; +#endif -qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxLength, - QHostAddress *address, quint16 *port) +qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxLength, QIpPacketHeader *header, + QAbstractSocketEngine::PacketHeaderOptions options) { - qint64 ret = 0; - + union { + char cbuf[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo)) + WSA_CMSG_SPACE(sizeof(int))]; + WSACMSGHDR align; // only to ensure alignment + }; + WSAMSG msg; + WSABUF buf; qt_sockaddr aa; + char c; + memset(&msg, 0, sizeof(msg)); memset(&aa, 0, sizeof(aa)); - QT_SOCKLEN_T sz; - sz = sizeof(aa); - WSABUF buf; - buf.buf = data; - buf.len = maxLength; -#if !defined(Q_OS_WINCE) - buf.buf = data; - buf.len = maxLength; -#else - char tmpChar; - buf.buf = data ? data : &tmpChar; - buf.len = maxLength; -#endif + // we need to receive at least one byte, even if our user isn't interested in it + buf.buf = maxLength ? data : &c; + buf.len = maxLength ? maxLength : 1; + msg.lpBuffers = &buf; + msg.dwBufferCount = 1; + msg.name = reinterpret_cast<LPSOCKADDR>(&aa); + msg.namelen = sizeof(aa); + if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination)) { + msg.Control.buf = cbuf; + msg.Control.len = sizeof(cbuf); + } DWORD flags = 0; DWORD bytesRead = 0; - int wsaRet = ::WSARecvFrom(socketDescriptor, &buf, 1, &bytesRead, &flags, &aa.a, &sz,0,0); - if (wsaRet == SOCKET_ERROR) { + qint64 ret; + + if (recvmsg) + ret = recvmsg(socketDescriptor, &msg, &bytesRead, 0,0); + else + ret = ::WSARecvFrom(socketDescriptor, &buf, 1, &bytesRead, &flags, msg.name, &msg.namelen,0,0); + if (ret == SOCKET_ERROR) { int err = WSAGetLastError(); if (err == WSAEMSGSIZE) { // it is ok the buffer was to small if bytesRead is larger than @@ -1251,12 +1241,44 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxL WS_ERROR_DEBUG(err); setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString); ret = -1; + if (header) + header->clear(); } } else { ret = qint64(bytesRead); + if (options & QNativeSocketEngine::WantDatagramSender) + qt_socket_getPortAndAddress(socketDescriptor, &aa, &header->senderPort, &header->senderAddress); } - qt_socket_getPortAndAddress(socketDescriptor, &aa, port, address); + if (ret != -1 && recvmsg) { + // get the ancillary data + WSACMSGHDR *cmsgptr; + for (cmsgptr = WSA_CMSG_FIRSTHDR(&msg); cmsgptr != NULL; + cmsgptr = WSA_CMSG_NXTHDR(&msg, cmsgptr)) { + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_PKTINFO + && cmsgptr->cmsg_len >= WSA_CMSG_LEN(sizeof(in6_pktinfo))) { + in6_pktinfo *info = reinterpret_cast<in6_pktinfo *>(WSA_CMSG_DATA(cmsgptr)); + QHostAddress target(reinterpret_cast<quint8 *>(&info->ipi6_addr)); + if (info->ipi6_ifindex) + target.setScopeId(QString::number(info->ipi6_ifindex)); + } + if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_PKTINFO + && cmsgptr->cmsg_len >= WSA_CMSG_LEN(sizeof(in_pktinfo))) { + in_pktinfo *info = reinterpret_cast<in_pktinfo *>(WSA_CMSG_DATA(cmsgptr)); + u_long addr; + WSANtohl(socketDescriptor, info->ipi_addr.s_addr, &addr); + QHostAddress target(addr); + if (info->ipi_ifindex) + target.setScopeId(QString::number(info->ipi_ifindex)); + } + + if (cmsgptr->cmsg_len == WSA_CMSG_LEN(sizeof(int)) + && ((cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_HOPLIMIT) + || (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_TTL))) { + header->hopLimit = *reinterpret_cast<int *>(WSA_CMSG_DATA(cmsgptr)); + } + } + } #if defined (QNATIVESOCKETENGINE_DEBUG) qDebug("QNativeSocketEnginePrivate::nativeReceiveDatagram(%p \"%s\", %li, %s, %i) == %li", @@ -1270,27 +1292,94 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxL qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 len, - const QHostAddress &address, quint16 port) + const QIpPacketHeader &header) { - qint64 ret = -1; - struct sockaddr_in sockAddrIPv4; - qt_sockaddr_in6 sockAddrIPv6; - struct sockaddr *sockAddrPtr = 0; - QT_SOCKLEN_T sockAddrSize = 0; - - setPortAndAddress(&sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize); - + union { + char cbuf[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo)) + WSA_CMSG_SPACE(sizeof(int))]; + WSACMSGHDR align; // ensures alignment + }; + WSACMSGHDR *cmsgptr = &align; + WSAMSG msg; WSABUF buf; + qt_sockaddr aa; + + memset(&msg, 0, sizeof(msg)); + memset(&aa, 0, sizeof(aa)); #if !defined(Q_OS_WINCE) buf.buf = len ? (char*)data : 0; #else char tmp; buf.buf = len ? (char*)data : &tmp; #endif + msg.lpBuffers = &buf; + msg.dwBufferCount = 1; + msg.name = &aa.a; buf.len = len; + + setPortAndAddress(header.destinationPort, header.destinationAddress, &aa, &msg.namelen); + + if (msg.namelen == sizeof(aa.a6)) { + // sending IPv6 + if (header.hopLimit != -1) { + msg.Control.len += WSA_CMSG_SPACE(sizeof(int)); + cmsgptr->cmsg_len = WSA_CMSG_LEN(sizeof(int)); + cmsgptr->cmsg_level = IPPROTO_IPV6; + cmsgptr->cmsg_type = IPV6_HOPLIMIT; + memcpy(WSA_CMSG_DATA(cmsgptr), &header.hopLimit, sizeof(int)); + cmsgptr = reinterpret_cast<WSACMSGHDR *>(reinterpret_cast<char *>(cmsgptr) + + WSA_CMSG_SPACE(sizeof(int))); + } + if (header.ifindex != 0 || !header.senderAddress.isNull()) { + struct in6_pktinfo *data = reinterpret_cast<in6_pktinfo *>(WSA_CMSG_DATA(cmsgptr)); + memset(data, 0, sizeof(*data)); + msg.Control.len += WSA_CMSG_SPACE(sizeof(*data)); + cmsgptr->cmsg_len = WSA_CMSG_LEN(sizeof(*data)); + cmsgptr->cmsg_level = IPPROTO_IPV6; + cmsgptr->cmsg_type = IPV6_PKTINFO; + data->ipi6_ifindex = header.ifindex; + + Q_IPV6ADDR tmp = header.senderAddress.toIPv6Address(); + memcpy(&data->ipi6_addr, &tmp, sizeof(tmp)); + cmsgptr = reinterpret_cast<WSACMSGHDR *>(reinterpret_cast<char *>(cmsgptr) + + WSA_CMSG_SPACE(sizeof(*data))); + } + } else { + // sending IPv4 + if (header.hopLimit != -1) { + msg.Control.len += WSA_CMSG_SPACE(sizeof(int)); + cmsgptr->cmsg_len = WSA_CMSG_LEN(sizeof(int)); + cmsgptr->cmsg_level = IPPROTO_IP; + cmsgptr->cmsg_type = IP_TTL; + memcpy(WSA_CMSG_DATA(cmsgptr), &header.hopLimit, sizeof(int)); + cmsgptr = reinterpret_cast<WSACMSGHDR *>(reinterpret_cast<char *>(cmsgptr) + + WSA_CMSG_SPACE(sizeof(int))); + } + if (header.ifindex != 0 || !header.senderAddress.isNull()) { + struct in_pktinfo *data = reinterpret_cast<in_pktinfo *>(WSA_CMSG_DATA(cmsgptr)); + memset(data, 0, sizeof(*data)); + msg.Control.len += WSA_CMSG_SPACE(sizeof(*data)); + cmsgptr->cmsg_len = WSA_CMSG_LEN(sizeof(*data)); + cmsgptr->cmsg_level = IPPROTO_IP; + cmsgptr->cmsg_type = IP_PKTINFO; + data->ipi_ifindex = header.ifindex; + WSAHtonl(socketDescriptor, header.senderAddress.toIPv4Address(), &data->ipi_addr.s_addr); + cmsgptr = reinterpret_cast<WSACMSGHDR *>(reinterpret_cast<char *>(cmsgptr) + + WSA_CMSG_SPACE(sizeof(*data))); + } + } + + if (msg.Control.len != 0) + msg.Control.buf = cbuf; + DWORD flags = 0; DWORD bytesSent = 0; - if (::WSASendTo(socketDescriptor, &buf, 1, &bytesSent, flags, sockAddrPtr, sockAddrSize, 0,0) == SOCKET_ERROR) { + qint64 ret = -1; + if (sendmsg) { + ret = sendmsg(socketDescriptor, &msg, flags, &bytesSent, 0,0); + } else { + ret = ::WSASendTo(socketDescriptor, &buf, 1, &bytesSent, flags, msg.name, msg.namelen, 0,0); + } + if (ret == SOCKET_ERROR) { int err = WSAGetLastError(); WS_ERROR_DEBUG(err); switch (err) { diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp index 2d04946601..025e3e5017 100644 --- a/src/network/socket/qnativesocketengine_winrt.cpp +++ b/src/network/socket/qnativesocketengine_winrt.cpp @@ -538,18 +538,19 @@ qint64 QNativeSocketEngine::write(const char *data, qint64 len) return bytesWritten; } -qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress *addr, quint16 *port) +qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header, + PacketHeaderOptions) { Q_D(QNativeSocketEngine); - if (d->socketType != QAbstractSocket::UdpSocket || d->pendingDatagrams.isEmpty()) + if (d->socketType != QAbstractSocket::UdpSocket || d->pendingDatagrams.isEmpty()) { + if (header) + header->clear(); return -1; + } WinRtDatagram datagram = d->pendingDatagrams.takeFirst(); - if (addr) - *addr = datagram.address; - - if (port) - *port = datagram.port; + if (header) + *header = datagram.header; QByteArray readOrigin; // Do not read the whole datagram. Put the rest of it back into the "queue" @@ -564,7 +565,7 @@ qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress return readOrigin.length(); } -qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QHostAddress &addr, quint16 port) +qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) { Q_D(QNativeSocketEngine); if (d->socketType != QAbstractSocket::UdpSocket) @@ -576,13 +577,13 @@ qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QH HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), &hostNameFactory); RETURN_IF_FAILED("Could not obtain hostname factory", return -1); - const QString addressString = addr.toString(); + const QString addressString = header.destinationAddress.toString(); HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(addressString.utf16())); hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost); ComPtr<IAsyncOperation<IOutputStream *>> streamOperation; ComPtr<IOutputStream> stream; - const QString portString = QString::number(port); + const QString portString = QString::number(header.destinationPort); HStringReference portRef(reinterpret_cast<LPCWSTR>(portString.utf16())); hr = d->udpSocket()->GetOutputStreamAsync(remoteHost.Get(), portRef.Get(), &streamOperation); RETURN_IF_FAILED("Failed to get output stream to socket", return -1); @@ -1230,11 +1231,11 @@ HRESULT QNativeSocketEnginePrivate::handleNewDatagram(IDatagramSocket *socket, I remoteHost->get_CanonicalName(remoteHostString.GetAddressOf()); RETURN_OK_IF_FAILED("Could not obtain remote host's canonical name"); returnAddress.setAddress(qt_QStringFromHString(remoteHostString)); - datagram.address = returnAddress; + datagram.header.senderAddress = returnAddress; HString remotePort; hr = args->get_RemotePort(remotePort.GetAddressOf()); RETURN_OK_IF_FAILED("Could not obtain remote port"); - datagram.port = qt_QStringFromHString(remotePort).toInt(); + datagram.header.senderPort = qt_QStringFromHString(remotePort).toInt(); ComPtr<IDataReader> reader; hr = args->GetDataReader(&reader); diff --git a/src/network/socket/qnativesocketengine_winrt_p.h b/src/network/socket/qnativesocketengine_winrt_p.h index eb032bc977..4286ff6373 100644 --- a/src/network/socket/qnativesocketengine_winrt_p.h +++ b/src/network/socket/qnativesocketengine_winrt_p.h @@ -58,8 +58,7 @@ class QNativeSocketEnginePrivate; struct WinRtDatagram { QByteArray data; - int port; - QHostAddress address; + QIpPacketHeader header; }; class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine @@ -97,10 +96,8 @@ public: 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); + qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *, PacketHeaderOptions); + qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header); bool hasPendingDatagrams() const; qint64 pendingDatagramSize() const; diff --git a/src/network/socket/qnet_unix_p.h b/src/network/socket/qnet_unix_p.h index a5a87fc7c1..9626c53711 100644 --- a/src/network/socket/qnet_unix_p.h +++ b/src/network/socket/qnet_unix_p.h @@ -173,8 +173,7 @@ static inline in_addr_t qt_safe_inet_addr(const char *cp) #endif } -// VxWorks' headers do not specify any const modifiers -static inline int qt_safe_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *to, QT_SOCKLEN_T tolen) +static inline int qt_safe_sendmsg(int sockfd, const struct msghdr *msg, int flags) { #ifdef MSG_NOSIGNAL flags |= MSG_NOSIGNAL; @@ -183,11 +182,7 @@ static inline int qt_safe_sendto(int sockfd, const void *buf, size_t len, int fl #endif int ret; -#ifdef Q_OS_VXWORKS - EINTR_LOOP(ret, ::sendto(sockfd, (char *) buf, len, flags, (struct sockaddr *) to, tolen)); -#else - EINTR_LOOP(ret, ::sendto(sockfd, buf, len, flags, to, tolen)); -#endif + EINTR_LOOP(ret, ::sendmsg(sockfd, msg, flags)); return ret; } @@ -199,24 +194,6 @@ static inline int qt_safe_recvmsg(int sockfd, struct msghdr *msg, int flags) return ret; } -// VxWorks' headers do not specify any const modifiers -static inline int qt_safe_sendmsg(int sockfd, const struct msghdr *msg, int flags) -{ -#ifdef MSG_NOSIGNAL - flags |= MSG_NOSIGNAL; -#else - qt_ignore_sigpipe(); -#endif - - int ret; -#ifdef Q_OS_VXWORKS - EINTR_LOOP(ret, ::sendmsg(sockfd, (struct msghdr *) msg, flags); -#else - EINTR_LOOP(ret, ::sendmsg(sockfd, msg, flags)); -#endif - return ret; -} - QT_END_NAMESPACE #endif // QNET_UNIX_P_H diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index a6eafd2601..26543883cc 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -1383,7 +1383,7 @@ bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port) #endif dummy.setProxy(QNetworkProxy::NoProxy); if (!dummy.bind() - || writeDatagram(0,0, d->data->controlSocket->localAddress(), dummy.localPort()) != 0 + || writeDatagram(0,0, QIpPacketHeader(d->data->controlSocket->localAddress(), dummy.localPort())) != 0 || !dummy.waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed())) || dummy.readDatagram(0,0, &d->localAddress, &d->localPort) != 0) { QSOCKS5_DEBUG << "udp actual address and port lookup failed"; @@ -1555,7 +1555,7 @@ qint64 QSocks5SocketEngine::write(const char *data, qint64 len) #ifndef QT_NO_UDPSOCKET } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) { // send to connected address - return writeDatagram(data, len, d->peerAddress, d->peerPort); + return writeDatagram(data, len, QIpPacketHeader(d->peerAddress, d->peerPort)); #endif } //### set an error ??? @@ -1594,8 +1594,7 @@ bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &) } #endif // QT_NO_NETWORKINTERFACE -qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress *addr, - quint16 *port) +qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header, PacketHeaderOptions) { Q_D(QSocks5SocketEngine); @@ -1607,15 +1606,12 @@ qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress QSocks5RevivedDatagram datagram = d->udpData->pendingDatagrams.dequeue(); int copyLen = qMin<int>(maxlen, datagram.data.size()); memcpy(data, datagram.data.constData(), copyLen); - if (addr) - *addr = datagram.address; - if (port) - *port = datagram.port; + header->senderAddress = datagram.address; + header->senderPort = datagram.port; return copyLen; } -qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QHostAddress &address, - quint16 port) +qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) { Q_D(QSocks5SocketEngine); @@ -1634,7 +1630,7 @@ qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QH outBuf[0] = 0x00; outBuf[1] = 0x00; outBuf[2] = 0x00; - if (!qt_socks5_set_host_address_and_port(address, port, &outBuf)) { + if (!qt_socks5_set_host_address_and_port(header.destinationAddress, header.destinationPort, &outBuf)) { } outBuf += QByteArray(data, len); QSOCKS5_DEBUG << "sending" << dump(outBuf); diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h index c97b0e89ce..de20e0ef0e 100644 --- a/src/network/socket/qsocks5socketengine_p.h +++ b/src/network/socket/qsocks5socketengine_p.h @@ -93,10 +93,9 @@ public: bool setMulticastInterface(const QNetworkInterface &iface) Q_DECL_OVERRIDE; #endif // QT_NO_NETWORKINTERFACE - qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, - quint16 *port = 0) Q_DECL_OVERRIDE; - qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, - quint16 port) Q_DECL_OVERRIDE; + qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0, + PacketHeaderOptions = WantNone) Q_DECL_OVERRIDE; + qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE; bool hasPendingDatagrams() const Q_DECL_OVERRIDE; qint64 pendingDatagramSize() const Q_DECL_OVERRIDE; #endif // QT_NO_UDPSOCKET diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp index 914c14877e..bf75666548 100644 --- a/src/network/socket/qtcpserver.cpp +++ b/src/network/socket/qtcpserver.cpp @@ -559,6 +559,10 @@ QTcpSocket *QTcpServer::nextPendingConnection() may not be usable with native socket functions, and should only be used with QTcpSocket::setSocketDescriptor(). + \note If another socket is created in the reimplementation + of this method, it needs to be added to the Pending Connections mechanism + by calling addPendingConnection(). + \note If you want to handle an incoming connection as a new QTcpSocket object in another thread you have to pass the socketDescriptor to the other thread and create the QTcpSocket object there and diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index 87686f94e1..a46e6ade04 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -333,15 +333,13 @@ qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddre if (state() == UnconnectedState) bind(); - qint64 sent = d->socketEngine->writeDatagram(data, size, address, port); + qint64 sent = d->socketEngine->writeDatagram(data, size, QIpPacketHeader(address, port)); d->cachedSocketDescriptor = d->socketEngine->socketDescriptor(); if (sent >= 0) { emit bytesWritten(sent); } else { - d->socketError = d->socketEngine->error(); - setErrorString(d->socketEngine->errorString()); - emit error(d->socketError); + d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString()); } return sent; } @@ -379,13 +377,23 @@ qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *addres qDebug("QUdpSocket::readDatagram(%p, %llu, %p, %p)", data, maxSize, address, port); #endif QT_CHECK_BOUND("QUdpSocket::readDatagram()", -1); - qint64 readBytes = d->socketEngine->readDatagram(data, maxSize, address, port); - d_func()->socketEngine->setReadNotificationEnabled(true); - if (readBytes < 0) { - d->socketError = d->socketEngine->error(); - setErrorString(d->socketEngine->errorString()); - emit error(d->socketError); + + qint64 readBytes; + if (address || port) { + QIpPacketHeader header; + readBytes = d->socketEngine->readDatagram(data, maxSize, &header, + QAbstractSocketEngine::WantDatagramSender); + if (address) + *address = header.senderAddress; + if (port) + *port = header.senderPort; + } else { + readBytes = d->socketEngine->readDatagram(data, maxSize); } + + d_func()->socketEngine->setReadNotificationEnabled(true); + if (readBytes < 0) + d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString()); return readBytes; } #endif // QT_NO_UDPSOCKET diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index c15045b180..b62cfd2fde 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -499,8 +499,7 @@ bool QSslSocket::setSocketDescriptor(qintptr socketDescriptor, SocketState state d->createPlainSocket(openMode); bool retVal = d->plainSocket->setSocketDescriptor(socketDescriptor, state, openMode); d->cachedSocketDescriptor = d->plainSocket->socketDescriptor(); - setSocketError(d->plainSocket->error()); - setErrorString(d->plainSocket->errorString()); + d->setError(d->plainSocket->error(), d->plainSocket->errorString()); setSocketState(state); setOpenMode(openMode); setLocalPort(d->plainSocket->localPort()); @@ -1532,8 +1531,7 @@ bool QSslSocket::waitForConnected(int msecs) bool retVal = d->plainSocket->waitForConnected(msecs); if (!retVal) { setSocketState(d->plainSocket->state()); - setSocketError(d->plainSocket->error()); - setErrorString(d->plainSocket->errorString()); + d->setError(d->plainSocket->error(), d->plainSocket->errorString()); } return retVal; } @@ -1688,8 +1686,7 @@ bool QSslSocket::waitForDisconnected(int msecs) bool retVal = d->plainSocket->waitForDisconnected(qt_subtract_from_timeout(msecs, stopWatch.elapsed())); if (!retVal) { setSocketState(d->plainSocket->state()); - setSocketError(d->plainSocket->error()); - setErrorString(d->plainSocket->errorString()); + d->setError(d->plainSocket->error(), d->plainSocket->errorString()); } return retVal; } @@ -2402,15 +2399,22 @@ void QSslSocketPrivate::_q_stateChangedSlot(QAbstractSocket::SocketState state) */ void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error) { - Q_Q(QSslSocket); + Q_UNUSED(error) #ifdef QSSLSOCKET_DEBUG + Q_Q(QSslSocket); qCDebug(lcSsl) << "QSslSocket::_q_errorSlot(" << error << ')'; qCDebug(lcSsl) << "\tstate =" << q->state(); qCDebug(lcSsl) << "\terrorString =" << q->errorString(); #endif - q->setSocketError(plainSocket->error()); - q->setErrorString(plainSocket->errorString()); - emit q->error(error); + // this moves encrypted bytes from plain socket into our buffer + if (plainSocket->bytesAvailable()) { + qint64 tmpReadBufferMaxSize = readBufferMaxSize; + readBufferMaxSize = 0; // reset temporarily so the plain sockets completely drained drained + transmit(); + readBufferMaxSize = tmpReadBufferMaxSize; + } + + setErrorAndEmit(plainSocket->error(), plainSocket->errorString()); } /*! @@ -2475,7 +2479,6 @@ void QSslSocketPrivate::_q_flushReadBuffer() */ void QSslSocketPrivate::_q_resumeImplementation() { - Q_Q(QSslSocket); if (plainSocket) plainSocket->resume(); paused = false; @@ -2483,9 +2486,7 @@ void QSslSocketPrivate::_q_resumeImplementation() if (verifyErrorsHaveBeenIgnored()) { continueHandshake(); } else { - q->setErrorString(sslErrors.first().errorString()); - q->setSocketError(QAbstractSocket::SslHandshakeFailedError); - emit q->error(QAbstractSocket::SslHandshakeFailedError); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.first().errorString()); plainSocket->disconnectFromHost(); return; } diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp index b83e56c29e..06c707f271 100644 --- a/src/network/ssl/qsslsocket_mac.cpp +++ b/src/network/ssl/qsslsocket_mac.cpp @@ -51,6 +51,10 @@ #include <algorithm> #include <cstddef> +#ifdef Q_OS_OSX +#include <CoreServices/CoreServices.h> +#endif + QT_BEGIN_NAMESPACE Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_securetransport_mutex, (QMutex::Recursive)) @@ -318,7 +322,7 @@ void QSslSocketBackendPrivate::startClientEncryption() // Error description/code were set, 'error' emitted // by initSslContext, but OpenSSL socket also sets error // emits a signal twice, so ... - setError("Unable to init SSL Context", QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, "Unable to init SSL Context"); return; } @@ -331,7 +335,7 @@ void QSslSocketBackendPrivate::startServerEncryption() // Error description/code were set, 'error' emitted // by initSslContext, but OpenSSL socket also sets error // emits a signal twice, so ... - setError("Unable to init SSL Context", QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, "Unable to init SSL Context"); return; } @@ -360,8 +364,8 @@ void QSslSocketBackendPrivate::transmit() qCDebug(lcSsl) << plainSocket << "SSLWrite returned" << err; #endif if (err != noErr && err != errSSLWouldBlock) { - setError(QStringLiteral("SSLWrite failed: %1").arg(err), - QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QStringLiteral("SSLWrite failed: %1").arg(err)); break; } @@ -395,12 +399,12 @@ void QSslSocketBackendPrivate::transmit() #endif if (err == errSSLClosedGraceful) { shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves - setError(QSslSocket::tr("The TLS/SSL connection has been closed"), - QAbstractSocket::RemoteHostClosedError); + setErrorAndEmit(QAbstractSocket::RemoteHostClosedError, + QSslSocket::tr("The TLS/SSL connection has been closed")); break; } else if (err != noErr && err != errSSLWouldBlock) { - setError(QStringLiteral("SSLRead failed: %1").arg(err), - QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QStringLiteral("SSLRead failed: %1").arg(err)); break; } @@ -658,7 +662,7 @@ bool QSslSocketBackendPrivate::initSslContext() context = SSLCreateContext(Q_NULLPTR, side, kSSLStreamType); if (!context) { - setError("SSLCreateContext failed", QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, "SSLCreateContext failed"); return false; } @@ -666,8 +670,8 @@ bool QSslSocketBackendPrivate::initSslContext() reinterpret_cast<SSLWriteFunc>(&_q_SSLWrite)); if (err != noErr) { destroySslContext(); - setError(QStringLiteral("SSLSetIOFuncs failed: %1").arg(err), - QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QStringLiteral("SSLSetIOFuncs failed: %1").arg(err)); return false; } @@ -679,14 +683,14 @@ bool QSslSocketBackendPrivate::initSslContext() QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError; if (!setSessionCertificate(errorDescription, errorCode)) { destroySslContext(); - setError(errorDescription, errorCode); + setErrorAndEmit(errorCode, errorDescription); return false; } } if (!setSessionProtocol()) { destroySslContext(); - setError("Failed to set protocol version", QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, "Failed to set protocol version"); return false; } @@ -698,8 +702,8 @@ bool QSslSocketBackendPrivate::initSslContext() const OSStatus err = SSLSetEnableCertVerify(context, false); if (err != noErr) { destroySslContext(); - setError(QStringLiteral("SSLSetEnableCertVerify failed: %1").arg(err), - QSslSocket::SslInternalError); + setErrorAndEmit(QSslSocket::SslInternalError, + QStringLiteral("SSLSetEnableCertVerify failed: %1").arg(err)); return false; } } @@ -720,8 +724,8 @@ bool QSslSocketBackendPrivate::initSslContext() if (err != noErr) { destroySslContext(); - setError(QStringLiteral("SSLSetSessionOption failed: %1").arg(err), - QSslSocket::SslInternalError); + setErrorAndEmit(QSslSocket::SslInternalError, + QStringLiteral("SSLSetSessionOption failed: %1").arg(err)); return false; } // @@ -737,8 +741,8 @@ bool QSslSocketBackendPrivate::initSslContext() if (err != noErr) { destroySslContext(); - setError(QStringLiteral("failed to set SSL context option in server mode: %1").arg(err), - QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QStringLiteral("failed to set SSL context option in server mode: %1").arg(err)); return false; } } @@ -981,8 +985,8 @@ bool QSslSocketBackendPrivate::verifyPeerTrust() // !trust - SSLCopyPeerTrust can return noErr but null trust. if (err != noErr || !trust) { if (!canIgnoreVerify) { - setError(QStringLiteral("Failed to obtain peer trust: %1").arg(err), - QAbstractSocket::SslHandshakeFailedError); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, + QStringLiteral("Failed to obtain peer trust: %1").arg(err)); plainSocket->disconnectFromHost(); return false; } else { @@ -1005,8 +1009,8 @@ bool QSslSocketBackendPrivate::verifyPeerTrust() if (err != noErr) { // We can not ignore this, it's not even about trust verification // probably ... - setError(QStringLiteral("SecTrustEvaluate failed: %1").arg(err), - QAbstractSocket::SslHandshakeFailedError); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, + QStringLiteral("SecTrustEvaluate failed: %1").arg(err)); plainSocket->disconnectFromHost(); return false; } @@ -1124,8 +1128,8 @@ bool QSslSocketBackendPrivate::checkSslErrors() pauseSocketNotifiers(q); paused = true; } else { - setError(sslErrors.first().errorString(), - QAbstractSocket::SslHandshakeFailedError); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, + sslErrors.first().errorString()); plainSocket->disconnectFromHost(); } return false; @@ -1162,7 +1166,7 @@ bool QSslSocketBackendPrivate::startHandshake() // setSessionCertificate does not fail if we have no certificate. // Failure means a real error (invalid certificate, no private key, etc). if (!setSessionCertificate(errorDescription, errorCode)) { - setError(errorDescription, errorCode); + setErrorAndEmit(errorCode, errorDescription); return false; } else { // We try to resume a handshake, even if have no @@ -1177,8 +1181,8 @@ bool QSslSocketBackendPrivate::startHandshake() return startHandshake(); } - setError(QStringLiteral("SSLHandshake failed: %1").arg(err), - QAbstractSocket::SslHandshakeFailedError); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, + QStringLiteral("SSLHandshake failed: %1").arg(err)); plainSocket->disconnectFromHost(); return false; } @@ -1192,8 +1196,8 @@ bool QSslSocketBackendPrivate::startHandshake() // check protocol version ourselves, as Secure Transport does not enforce // the requested min / max versions. if (!verifySessionProtocol()) { - setError("Protocol version mismatch", - QAbstractSocket::SslHandshakeFailedError); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, + "Protocol version mismatch"); plainSocket->disconnectFromHost(); return false; } @@ -1206,16 +1210,6 @@ bool QSslSocketBackendPrivate::startHandshake() } } -void QSslSocketBackendPrivate::setError(const QString &errorString, - QAbstractSocket::SocketError errorCode) -{ - Q_Q(QSslSocket); - - q->setErrorString(errorString); - q->setSocketError(errorCode); - emit q->error(errorCode); -} - /* PKCS12 helpers. */ diff --git a/src/network/ssl/qsslsocket_mac_p.h b/src/network/ssl/qsslsocket_mac_p.h index 868b816957..414c155882 100644 --- a/src/network/ssl/qsslsocket_mac_p.h +++ b/src/network/ssl/qsslsocket_mac_p.h @@ -101,13 +101,6 @@ private: bool checkSslErrors(); bool startHandshake(); - // Aux. function, sets: - //1) socket error code, - //2) error string (description) - //3) emits a signal. - void setError(const QString &errorString, - QAbstractSocket::SocketError errorCode); - mutable QCFType<SSLContextRef> context; Q_DISABLE_COPY(QSslSocketBackendPrivate); diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 1634d65eb6..0a4c68d7c0 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -364,9 +364,7 @@ bool QSslSocketBackendPrivate::initSslContext() } if (sslContextPointer->error() != QSslError::NoError) { - q->setErrorString(sslContextPointer->errorString()); - q->setSocketError(QAbstractSocket::SslInvalidUserDataError); - emit q->error(QAbstractSocket::SslInvalidUserDataError); + setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, sslContextPointer->errorString()); sslContextPointer.clear(); // deletes the QSslContext return false; } @@ -374,9 +372,8 @@ bool QSslSocketBackendPrivate::initSslContext() // Create and initialize SSL session if (!(ssl = sslContextPointer->createSsl())) { // ### Bad error code - q->setErrorString(QSslSocket::tr("Error creating SSL session, %1").arg(getErrorsFromOpenSsl())); - q->setSocketError(QAbstractSocket::SslInternalError); - emit q->error(QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QSslSocket::tr("Error creating SSL session, %1").arg(getErrorsFromOpenSsl())); return false; } @@ -405,9 +402,8 @@ bool QSslSocketBackendPrivate::initSslContext() readBio = q_BIO_new(q_BIO_s_mem()); writeBio = q_BIO_new(q_BIO_s_mem()); if (!readBio || !writeBio) { - q->setErrorString(QSslSocket::tr("Error creating SSL session: %1").arg(getErrorsFromOpenSsl())); - q->setSocketError(QAbstractSocket::SslInternalError); - emit q->error(QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QSslSocket::tr("Error creating SSL session: %1").arg(getErrorsFromOpenSsl())); return false; } @@ -808,11 +804,9 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() void QSslSocketBackendPrivate::startClientEncryption() { - Q_Q(QSslSocket); if (!initSslContext()) { - q->setErrorString(QSslSocket::tr("Unable to init SSL Context: %1").arg(getErrorsFromOpenSsl())); - q->setSocketError(QAbstractSocket::SslInternalError); - emit q->error(QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QSslSocket::tr("Unable to init SSL Context: %1").arg(getErrorsFromOpenSsl())); return; } @@ -824,11 +818,9 @@ void QSslSocketBackendPrivate::startClientEncryption() void QSslSocketBackendPrivate::startServerEncryption() { - Q_Q(QSslSocket); if (!initSslContext()) { - q->setErrorString(QSslSocket::tr("Unable to init SSL Context: %1").arg(getErrorsFromOpenSsl())); - q->setSocketError(QAbstractSocket::SslInternalError); - emit q->error(QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QSslSocket::tr("Unable to init SSL Context: %1").arg(getErrorsFromOpenSsl())); return; } @@ -874,9 +866,9 @@ void QSslSocketBackendPrivate::transmit() break; } else { // ### Better error handling. - q->setErrorString(QSslSocket::tr("Unable to write data: %1").arg(getErrorsFromOpenSsl())); - q->setSocketError(QAbstractSocket::SslInternalError); - emit q->error(QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QSslSocket::tr("Unable to write data: %1").arg( + getErrorsFromOpenSsl())); return; } } @@ -918,9 +910,7 @@ void QSslSocketBackendPrivate::transmit() #endif if (actualWritten < 0) { //plain socket write fails if it was in the pending close state. - q->setErrorString(plainSocket->errorString()); - q->setSocketError(plainSocket->error()); - emit q->error(plainSocket->error()); + setErrorAndEmit(plainSocket->error(), plainSocket->errorString()); return; } transmitting = true; @@ -946,9 +936,9 @@ void QSslSocketBackendPrivate::transmit() plainSocket->read(data.data(), writtenToBio); } else { // ### Better error handling. - q->setErrorString(QSslSocket::tr("Unable to decrypt data: %1").arg(getErrorsFromOpenSsl())); - q->setSocketError(QAbstractSocket::SslInternalError); - emit q->error(QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QSslSocket::tr("Unable to decrypt data: %1").arg( + getErrorsFromOpenSsl())); return; } @@ -1022,17 +1012,15 @@ void QSslSocketBackendPrivate::transmit() qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: remote disconnect"; #endif shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves - q->setErrorString(QSslSocket::tr("The TLS/SSL connection has been closed")); - q->setSocketError(QAbstractSocket::RemoteHostClosedError); - emit q->error(QAbstractSocket::RemoteHostClosedError); + setErrorAndEmit(QAbstractSocket::RemoteHostClosedError, + QSslSocket::tr("The TLS/SSL connection has been closed")); return; case SSL_ERROR_SYSCALL: // some IO error case SSL_ERROR_SSL: // error in the SSL library // we do not know exactly what the error is, nor whether we can recover from it, // so just return to prevent an endless loop in the outer "while" statement - q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl())); - q->setSocketError(QAbstractSocket::SslInternalError); - emit q->error(QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl())); return; default: // SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: can only happen with a @@ -1040,9 +1028,8 @@ void QSslSocketBackendPrivate::transmit() // SSL_ERROR_WANT_X509_LOOKUP: can only happen with a // SSL_CTX_set_client_cert_cb(), which we do not call. // So this default case should never be triggered. - q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl())); - q->setSocketError(QAbstractSocket::SslInternalError); - emit q->error(QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl())); break; } } while (ssl && readBytes > 0); @@ -1134,12 +1121,12 @@ bool QSslSocketBackendPrivate::startHandshake() // The handshake is not yet complete. break; default: - q->setErrorString(QSslSocket::tr("Error during SSL handshake: %1").arg(getErrorsFromOpenSsl())); - q->setSocketError(QAbstractSocket::SslHandshakeFailedError); + QString errorString + = QSslSocket::tr("Error during SSL handshake: %1").arg(getErrorsFromOpenSsl()); #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "QSslSocketBackendPrivate::startHandshake: error!" << q->errorString(); + qCDebug(lcSsl) << "QSslSocketBackendPrivate::startHandshake: error!" << errorString; #endif - emit q->error(QAbstractSocket::SslHandshakeFailedError); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, errorString); q->abort(); } return false; @@ -1290,9 +1277,7 @@ bool QSslSocketBackendPrivate::checkSslErrors() pauseSocketNotifiers(q); paused = true; } else { - q->setErrorString(sslErrors.first().errorString()); - q->setSocketError(QAbstractSocket::SslHandshakeFailedError); - emit q->error(QAbstractSocket::SslHandshakeFailedError); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.first().errorString()); plainSocket->disconnectFromHost(); } return false; @@ -1513,6 +1498,13 @@ void QSslSocketBackendPrivate::disconnected() { if (plainSocket->bytesAvailable() <= 0) destroySslContext(); + else { + // Move all bytes into the plain buffer + qint64 tmpReadBufferMaxSize = readBufferMaxSize; + readBufferMaxSize = 0; // reset temporarily so the plain socket buffer is completely drained + transmit(); + readBufferMaxSize = tmpReadBufferMaxSize; + } //if there is still buffered data in the plain socket, don't destroy the ssl context yet. //it will be destroyed when the socket is deleted. } diff --git a/src/network/ssl/qsslsocket_winrt.cpp b/src/network/ssl/qsslsocket_winrt.cpp index aba3ea8170..b03f234d49 100644 --- a/src/network/ssl/qsslsocket_winrt.cpp +++ b/src/network/ssl/qsslsocket_winrt.cpp @@ -251,9 +251,8 @@ void QSslSocketBackendPrivate::startClientEncryption() case QSsl::TlsV1_2OrLater: // TlsV1_0OrLater, TlsV1_1OrLater and TlsV1_2OrLater are disabled on WinRT // because there is no good way to map them to the native API. - q->setErrorString(QStringLiteral("unsupported protocol")); - q->setSocketError(QAbstractSocket::SslInvalidUserDataError); - emit q->error(QAbstractSocket::SslInvalidUserDataError); + setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, + QStringLiteral("unsupported protocol")); return; default: protectionLevel = SocketProtectionLevel_Tls12; // default to highest @@ -347,13 +346,10 @@ QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const void QSslSocketBackendPrivate::continueHandshake() { - Q_Q(QSslSocket); - IStreamSocket *socket = reinterpret_cast<IStreamSocket *>(plainSocket->socketDescriptor()); if (qintptr(socket) == -1) { - q->setErrorString(QStringLiteral("At attempt was made to continue the handshake on an invalid socket.")); - q->setSocketError(QAbstractSocket::SslInternalError); - emit q->error(QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QStringLiteral("At attempt was made to continue the handshake on an invalid socket.")); return; } @@ -372,9 +368,7 @@ void QSslSocketBackendPrivate::continueHandshake() Q_ASSERT_SUCCEEDED(hr); } if (FAILED(hr)) { - q->setErrorString(qt_error_string(hr)); - q->setSocketError(QAbstractSocket::SslInvalidUserDataError); - emit q->error(QAbstractSocket::SslInvalidUserDataError); + setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, qt_error_string(hr)); return; } @@ -440,10 +434,8 @@ void QSslSocketBackendPrivate::continueHandshake() ComPtr<IAsyncAction> op; hr = socket->UpgradeToSslAsync(protectionLevel, hostName.Get(), &op); if (FAILED(hr)) { - q->setErrorString(QSslSocket::tr("Error creating SSL session: %1") - .arg(qt_error_string(hr))); - q->setSocketError(QAbstractSocket::SslInternalError); - emit q->error(QAbstractSocket::SslInternalError); + setErrorAndEmit(QAbstractSocket::SslInternalError, + QSslSocket::tr("Error creating SSL session: %1").arg(qt_error_string(hr))); return; } @@ -467,9 +459,7 @@ HRESULT QSslSocketBackendPrivate::onSslUpgrade(IAsyncAction *action, AsyncStatus QSet<QSslError> errors; switch (hr) { case SEC_E_INVALID_TOKEN: // Occurs when the server doesn't support the requested protocol - q->setErrorString(qt_error_string(hr)); - q->setSocketError(QAbstractSocket::SslHandshakeFailedError); - emit q->error(QAbstractSocket::SslHandshakeFailedError); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, qt_error_string(hr)); q->disconnectFromHost(); return S_OK; default: @@ -628,9 +618,7 @@ HRESULT QSslSocketBackendPrivate::onSslUpgrade(IAsyncAction *action, AsyncStatus if (!sslErrors.isEmpty()) { emit q->sslErrors(sslErrors); - q->setErrorString(sslErrors.first().errorString()); - q->setSocketError(QAbstractSocket::SslHandshakeFailedError); - emit q->error(QAbstractSocket::SslHandshakeFailedError); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.first().errorString()); // Disconnect if there are any non-ignorable errors foreach (const QSslError &error, sslErrors) { |