From 0f6ededb157b3739b95c470f8dbf7b8bbb5a61cc Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 3 Jun 2016 10:39:46 +0200 Subject: WinRT: Proper error handling in socket engine's "bind" runOnXamlThread should always return S_OK (causes exceptions otherwise) so we need another way of error reporting to the outside (hr reference). A failed hr is reported to the outside (of the lambda) and interpreted as an unknown error. Specific error cases like the given address not being a local address or the given address being in use already are handled inside the lambda. The specificErrorSet variable is necessary in these cases so that the error is not overwritten by unknownError. Change-Id: I198d66fe97726d5127bf31e50c7eff3363d5259c Reviewed-by: Maurice Kalinowski --- src/network/socket/qnativesocketengine_winrt.cpp | 47 +++++++++++++++++++----- 1 file changed, 37 insertions(+), 10 deletions(-) (limited to 'src/network') diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp index 3a4ef4b400..b926583c46 100644 --- a/src/network/socket/qnativesocketengine_winrt.cpp +++ b/src/network/socket/qnativesocketengine_winrt.cpp @@ -342,19 +342,21 @@ bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port) { Q_D(QNativeSocketEngine); HRESULT hr; - hr = QEventDispatcherWinRT::runOnXamlThread([address, d, port, this]() { - HRESULT hr; + // runOnXamlThread may only return S_OK (will assert otherwise) so no need to check its result. + // hr is set inside the lambda though. If an error occurred hr will point that out. + bool specificErrorSet = false; + QEventDispatcherWinRT::runOnXamlThread([address, d, &hr, port, &specificErrorSet, this]() { ComPtr hostAddress; if (address != QHostAddress::Any && address != QHostAddress::AnyIPv4 && address != QHostAddress::AnyIPv6) { ComPtr hostNameFactory; hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), &hostNameFactory); - RETURN_HR_IF_FAILED("QNativeSocketEngine::bind: Could not obtain hostname factory"); + RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not obtain hostname factory"); const QString addressString = address.toString(); HStringReference addressRef(reinterpret_cast(addressString.utf16())); hr = hostNameFactory->CreateHostName(addressRef.Get(), &hostAddress); - RETURN_HR_IF_FAILED("QNativeSocketEngine::bind: Could not create hostname."); + RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not create hostname."); } QString portQString = port ? QString::number(port) : QString(); @@ -365,13 +367,13 @@ bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port) if (!d->tcpListener) { hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocketListener).Get(), &d->tcpListener); - RETURN_HR_IF_FAILED("QNativeSocketEngine::bind: Could not create tcp listener"); + RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not create tcp listener"); } hr = d->tcpListener->add_ConnectionReceived( Callback(d, &QNativeSocketEnginePrivate::handleClientConnection).Get(), &d->connectionToken); - RETURN_HR_IF_FAILED("QNativeSocketEngine::bind: Could not register client connection callback"); + RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not register client connection callback"); hr = d->tcpListener->BindEndpointAsync(hostAddress.Get(), portString.Get(), &op); } else if (d->socketType == QAbstractSocket::UdpSocket) { hr = d->udpSocket()->BindEndpointAsync(hostAddress.Get(), portString.Get(), &op); @@ -379,15 +381,40 @@ bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port) if (hr == E_ACCESSDENIED) { qErrnoWarning(hr, "Unable to bind socket (%s:%hu/%s). Please check your manifest capabilities.", qPrintable(address.toString()), port, socketDescription(this).constData()); - return hr; + d->setError(QAbstractSocket::SocketAccessError, + QNativeSocketEnginePrivate::AccessErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + specificErrorSet = true; + return S_OK; } - RETURN_HR_IF_FAILED("QNativeSocketEngine::bind: Unable to bind socket"); + RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Unable to bind socket"); hr = QWinRTFunctions::await(op); - RETURN_HR_IF_FAILED("QNativeSocketEngine::bind: Could not wait for bind to finish"); + if (hr == 0x80072741) { // The requested address is not valid in its context + d->setError(QAbstractSocket::SocketAddressNotAvailableError, + QNativeSocketEnginePrivate::AddressNotAvailableErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + specificErrorSet = true; + return S_OK; + // Only one usage of each socket address (protocol/network address/port) is normally permitted + } else if (hr == 0x80072740) { + d->setError(QAbstractSocket::AddressInUseError, + QNativeSocketEnginePrivate::AddressInuseErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + specificErrorSet = true; + return S_OK; + } + RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not wait for bind to finish"); return S_OK; }); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr)) { + if (!specificErrorSet) { + d->setError(QAbstractSocket::UnknownSocketError, + QNativeSocketEnginePrivate::UnknownSocketErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + } + return false; + } d->socketState = QAbstractSocket::BoundState; return d->fetchConnectionParameters(); -- cgit v1.2.3 From c111abe0605c95b50eb4362df2a68953f3e0da1e Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 3 Jun 2016 10:58:51 +0200 Subject: WinRT: fixed error reporting of QNativeSocketEngine::initialize If runOnXamlThread returns anything but S_OK an exception is thrown which might cause the application to terminate. So we give the lambda a reference to hr and check that reference instead of runOnXamlThread's return value for errors. Change-Id: I1188ea720c63f6fdf43400f2f3ff928b72afc58e Reviewed-by: Maurice Kalinowski --- src/network/socket/qnativesocketengine_winrt.cpp | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'src/network') diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp index b926583c46..4547a0cbe4 100644 --- a/src/network/socket/qnativesocketengine_winrt.cpp +++ b/src/network/socket/qnativesocketengine_winrt.cpp @@ -253,31 +253,23 @@ bool QNativeSocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket:: // Start processing incoming data if (d->socketType == QAbstractSocket::TcpSocket) { HRESULT hr; - hr = QEventDispatcherWinRT::runOnXamlThread([d, socket, this]() { + QEventDispatcherWinRT::runOnXamlThread([d, &hr, socket, this]() { ComPtr buffer; HRESULT hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &buffer); - RETURN_HR_IF_FAILED("initialize(): Could not create buffer"); - + RETURN_OK_IF_FAILED("initialize(): Could not create buffer"); ComPtr stream; hr = socket->get_InputStream(&stream); - RETURN_HR_IF_FAILED("initialize(): Could not obtain input stream"); + RETURN_OK_IF_FAILED("initialize(): Could not obtain input stream"); hr = stream->ReadAsync(buffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, d->readOp.GetAddressOf()); - if (FAILED(hr)) { - qErrnoWarning(hr, "initialize(): Failed to read from the socket buffer (%s).", + RETURN_OK_IF_FAILED_WITH_ARGS("initialize(): Failed to read from the socket buffer (%s).", socketDescription(this).constData()); - return E_FAIL; - } hr = d->readOp->put_Completed(Callback(d, &QNativeSocketEnginePrivate::handleReadyRead).Get()); - if (FAILED(hr)) { - qErrnoWarning(hr, "initialize(): Failed to set socket read callback (%s).", + RETURN_OK_IF_FAILED_WITH_ARGS("initialize(): Failed to set socket read callback (%s).", socketDescription(this).constData()); - return E_FAIL; - } return S_OK; }); - if (hr == E_FAIL) + if (FAILED(hr)) return false; - Q_ASSERT_SUCCEEDED(hr); } d->socketState = socketState; -- cgit v1.2.3 From 6fa74764d91e876162e2a992a4087aaf4082c001 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 3 Jun 2016 12:07:46 +0200 Subject: WinRT: Avoid asserts in socket function with bool return values There is no need for the functions to assert. By returning false they show that something went wrong and the error will be handled gracefully. Change-Id: Ib026adf5c6fb23b5e6b5598533caec3b3669220c Reviewed-by: Maurice Kalinowski --- src/network/socket/qnativesocketengine_winrt.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'src/network') diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp index 4547a0cbe4..58f0668854 100644 --- a/src/network/socket/qnativesocketengine_winrt.cpp +++ b/src/network/socket/qnativesocketengine_winrt.cpp @@ -321,11 +321,14 @@ bool QNativeSocketEngine::connectToHostByName(const QString &name, quint16 port) Q_ASSERT_SUCCEEDED(hr); d->socketState = QAbstractSocket::ConnectingState; - hr = QEventDispatcherWinRT::runOnXamlThread([d]() { - return d->connectOp->put_Completed(Callback( + QEventDispatcherWinRT::runOnXamlThread([d, &hr]() { + hr = d->connectOp->put_Completed(Callback( d, &QNativeSocketEnginePrivate::handleConnectOpFinished).Get()); + RETURN_OK_IF_FAILED("connectToHostByName: Could not register \"connectOp\" callback"); + return S_OK; }); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr)) + return false; return d->socketState == QAbstractSocket::ConnectedState; } @@ -865,20 +868,22 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc case QAbstractSocket::TcpSocket: { ComPtr socket; hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &socket); - Q_ASSERT_SUCCEEDED(hr); + RETURN_FALSE_IF_FAILED("createNewSocket: Could not create socket instance"); socketDescriptor = qintptr(socket.Detach()); break; } case QAbstractSocket::UdpSocket: { ComPtr socket; hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &socket); - Q_ASSERT_SUCCEEDED(hr); + RETURN_FALSE_IF_FAILED("createNewSocket: Could not create socket instance"); socketDescriptor = qintptr(socket.Detach()); - hr = QEventDispatcherWinRT::runOnXamlThread([this]() { - HRESULT hr = udpSocket()->add_MessageReceived(Callback(this, &QNativeSocketEnginePrivate::handleNewDatagram).Get(), &connectionToken); - return hr; + QEventDispatcherWinRT::runOnXamlThread([&hr, this]() { + hr = udpSocket()->add_MessageReceived(Callback(this, &QNativeSocketEnginePrivate::handleNewDatagram).Get(), &connectionToken); + RETURN_OK_IF_FAILED("createNewSocket: Could not add \"message received\" callback") + return S_OK; }); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr)) + return false; break; } default: -- cgit v1.2.3 From 3394d97edfd6d1f5f4311868212831bb3ae03dfb Mon Sep 17 00:00:00 2001 From: Maurice Kalinowski Date: Thu, 9 Jun 2016 10:22:58 +0200 Subject: winrt: Add privateNetworkClientServer to default capabilities Contrary to our initial assumption, this should be added by default to allow full functionality during development by default. Developers need to decide which capabilities to use during the publishing step already, hence improve DX by adding this. Task-number: QTBUG-50847 Change-Id: I36e0214f7bcf8610d31851eea172aba3944cfd99 Reviewed-by: Oliver Wolff Reviewed-by: Friedemann Kleint --- src/network/network.pro | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/network') diff --git a/src/network/network.pro b/src/network/network.pro index fed14616af..ae81132b52 100644 --- a/src/network/network.pro +++ b/src/network/network.pro @@ -34,7 +34,8 @@ ANDROID_PERMISSIONS += \ MODULE_WINRT_CAPABILITIES = \ internetClient \ - internetClientServer + internetClientServer \ + privateNetworkClientServer MODULE_PLUGIN_TYPES = \ bearer -- cgit v1.2.3 From 23173c725ce8b1c5b03498c9fa2bb334d40ec6f4 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Thu, 9 Jun 2016 10:36:50 +0200 Subject: QSslSocket (OpenSSL) - handle abort/close on sslErrors emitted If a user's code, attached to sslErrors signal, calls abort/close or disconnectFromHost but our SSL socket was configured not to verify a peer, no need to continue handshake after calling checkSslErrors (and finally crashing on invalid 'ssl' pointer). Task-number: QTBUG-53906 Change-Id: I7f185511d278f9d6f16e7d6c5ba424707141459c Reviewed-by: Edward Welbourne Reviewed-by: Timur Pocheptsov --- src/network/ssl/qsslsocket_openssl.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/network') diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 57cedf2c22..82644c155c 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1240,6 +1240,11 @@ bool QSslSocketBackendPrivate::startHandshake() #endif if (!checkSslErrors()) return false; + // A slot, attached to sslErrors signal can call + // abort/close/disconnetFromHost/etc; no need to + // continue handshake then. + if (q->state() != QAbstractSocket::ConnectedState) + return false; } else { sslErrors.clear(); } -- cgit v1.2.3