diff options
author | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2019-07-02 14:28:58 +0200 |
---|---|---|
committer | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2019-07-11 10:46:23 +0200 |
commit | cc32226e647854a02ecd0775787bb08b85e2a014 (patch) | |
tree | 3a50004830c421d52f8c0138254f3e1c4a66791f /src/network/socket | |
parent | 2d597c0e1a32dda3f4d2f1c941b8a0d2a4be4735 (diff) |
Windows: handle errors correctly when connecting to unreachable host
The previous code handled only some error codes, in a very inefficient
way, for some code paths. This change standardizes error handling using
a helper function that maps winsock WSAE* codes to Qt error codes.
The test for connecting to unreachable hosts or ports is now more
generic, and enabled on Windows, where it passes in local tests,
but dependency on network configuration still makes it fragile,
so ignoring some failures without completely skipping the test.
[ChangeLog][Network][Windows] Correctly emit errors when trying to
reach unreachable hosts or services
Change-Id: Icaca3e6fef88621d683f6d6fa3016212847de4ea
Fixes: QTBUG-42567
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src/network/socket')
-rw-r--r-- | src/network/socket/qnativesocketengine_win.cpp | 115 |
1 files changed, 55 insertions, 60 deletions
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 986b0b4861..897f9e24bb 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -623,6 +623,53 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters() } +static void setErrorFromWSAError(int error, QNativeSocketEnginePrivate *d) +{ + Q_ASSERT(d); + switch (error) { + case WSAEISCONN: + d->socketState = QAbstractSocket::ConnectedState; + break; + case WSAEHOSTUNREACH: + d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::HostUnreachableErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + break; + case WSAEADDRNOTAVAIL: + d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::AddressNotAvailableErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + break; + case WSAEINPROGRESS: + d->setError(QAbstractSocket::UnfinishedSocketOperationError, QNativeSocketEnginePrivate::InvalidSocketErrorString); + d->socketState = QAbstractSocket::ConnectingState; + break; + case WSAEADDRINUSE: + d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::AddressInuseErrorString); + break; + case WSAECONNREFUSED: + d->setError(QAbstractSocket::ConnectionRefusedError, QNativeSocketEnginePrivate::ConnectionRefusedErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + break; + case WSAETIMEDOUT: + d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::ConnectionTimeOutErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + break; + case WSAEACCES: + d->setError(QAbstractSocket::SocketAccessError, QNativeSocketEnginePrivate::AccessErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + break; + case WSAENETUNREACH: + d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::NetworkUnreachableErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + break; + case WSAEINVAL: + case WSAEALREADY: + d->setError(QAbstractSocket::UnfinishedSocketOperationError, QNativeSocketEnginePrivate::InvalidSocketErrorString); + break; + default: + break; + } +} + bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quint16 port) { @@ -651,9 +698,6 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin case WSANOTINITIALISED: //### break; - case WSAEISCONN: - socketState = QAbstractSocket::ConnectedState; - break; case WSAEWOULDBLOCK: { // If WSAConnect returns WSAEWOULDBLOCK on the second // connection attempt, we have to check SO_ERROR's @@ -668,82 +712,33 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin do { if (::getsockopt(socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) { if (value != NOERROR) { + WS_ERROR_DEBUG(value); + errorDetected = true; // MSDN says getsockopt with SO_ERROR clears the error, but it's not actually cleared // and this can affect all subsequent WSAConnect attempts, so clear it now. const int val = NO_ERROR; ::setsockopt(socketDescriptor, SOL_SOCKET, SO_ERROR, reinterpret_cast<const char*>(&val), sizeof val); - } - - if (value == WSAECONNREFUSED) { - setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString); - socketState = QAbstractSocket::UnconnectedState; - errorDetected = true; - break; - } - if (value == WSAETIMEDOUT) { - setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString); - socketState = QAbstractSocket::UnconnectedState; - errorDetected = true; - break; - } - if (value == WSAEHOSTUNREACH) { - setError(QAbstractSocket::NetworkError, HostUnreachableErrorString); - socketState = QAbstractSocket::UnconnectedState; - errorDetected = true; - break; - } - if (value == WSAEADDRNOTAVAIL) { - setError(QAbstractSocket::NetworkError, AddressNotAvailableErrorString); - socketState = QAbstractSocket::UnconnectedState; - errorDetected = true; - break; - } - if (value == NOERROR) { + } else { // When we get WSAEWOULDBLOCK the outcome was not known, so a // NOERROR might indicate that the result of the operation // is still unknown. We try again to increase the chance that we did // get the correct result. tryAgain = !tryAgain; } + setErrorFromWSAError(value, this); } tries++; } while (tryAgain && (tries < 2)); if (errorDetected) break; + // fall through to unfinished operation error handling + err = WSAEINPROGRESS; Q_FALLTHROUGH(); } - case WSAEINPROGRESS: - setError(QAbstractSocket::UnfinishedSocketOperationError, InvalidSocketErrorString); - socketState = QAbstractSocket::ConnectingState; - break; - case WSAEADDRINUSE: - setError(QAbstractSocket::NetworkError, AddressInuseErrorString); - break; - case WSAECONNREFUSED: - setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString); - socketState = QAbstractSocket::UnconnectedState; - break; - case WSAETIMEDOUT: - setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString); - break; - case WSAEACCES: - setError(QAbstractSocket::SocketAccessError, AccessErrorString); - socketState = QAbstractSocket::UnconnectedState; - break; - case WSAEHOSTUNREACH: - setError(QAbstractSocket::NetworkError, HostUnreachableErrorString); - socketState = QAbstractSocket::UnconnectedState; - break; - case WSAENETUNREACH: - setError(QAbstractSocket::NetworkError, NetworkUnreachableErrorString); - socketState = QAbstractSocket::UnconnectedState; - break; - case WSAEINVAL: - case WSAEALREADY: - setError(QAbstractSocket::UnfinishedSocketOperationError, InvalidSocketErrorString); - break; + default: + setErrorFromWSAError(err, this); break; } if (socketState != QAbstractSocket::ConnectedState) { |