/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "qnativesocketengine_winrt_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifndef QT_NO_SSL #include #endif #include #include #include #include #include #include #include using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Collections; using namespace ABI::Windows::Storage::Streams; using namespace ABI::Windows::Networking; using namespace ABI::Windows::Networking::Connectivity; using namespace ABI::Windows::Networking::Sockets; #if _MSC_VER >= 1900 using namespace ABI::Windows::Security::EnterpriseData; #endif typedef ITypedEventHandler ClientConnectedHandler; typedef ITypedEventHandler DatagramReceivedHandler; typedef IAsyncOperationWithProgressCompletedHandler SocketReadCompletedHandler; typedef IAsyncOperationWithProgressCompletedHandler SocketWriteCompletedHandler; typedef IAsyncOperationWithProgress IAsyncBufferOperation; QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcNetworkSocket, "qt.network.socket"); Q_LOGGING_CATEGORY(lcNetworkSocketVerbose, "qt.network.socket.verbose"); #if _MSC_VER >= 1900 static HRESULT qt_winrt_try_create_thread_network_context(QString host, ComPtr &context) { HRESULT hr; ComPtr protectionPolicyManager; hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &protectionPolicyManager); RETURN_HR_IF_FAILED("Could not access ProtectionPolicyManager statics."); ComPtr hostNameFactory; hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), &hostNameFactory); RETURN_HR_IF_FAILED("Could not access HostName factory."); ComPtr hostName; HStringReference hostRef(reinterpret_cast(host.utf16()), host.length()); hr = hostNameFactory->CreateHostName(hostRef.Get(), &hostName); RETURN_HR_IF_FAILED("Could not create hostname."); ComPtr> op; hr = protectionPolicyManager->GetPrimaryManagedIdentityForNetworkEndpointAsync(hostName.Get(), &op); RETURN_HR_IF_FAILED("Could not get identity operation."); HSTRING hIdentity; hr = QWinRTFunctions::await(op, &hIdentity); RETURN_HR_IF_FAILED("Could not wait for identity operation."); // Implies there is no need for a network context for this address if (hIdentity == nullptr) return S_OK; hr = protectionPolicyManager->CreateCurrentThreadNetworkContext(hIdentity, &context); RETURN_HR_IF_FAILED("Could not create thread network context"); return S_OK; } #endif // _MSC_VER >= 1900 typedef QHash TcpSocketHash; struct SocketHandler { SocketHandler() : socketCount(0) {} qintptr socketCount; TcpSocketHash pendingTcpSockets; }; Q_GLOBAL_STATIC(SocketHandler, gSocketHandler) struct SocketGlobal { SocketGlobal() { HRESULT hr; hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory); Q_ASSERT_SUCCEEDED(hr); } ComPtr bufferFactory; }; Q_GLOBAL_STATIC(SocketGlobal, g) #define READ_BUFFER_SIZE 65536 static inline QString qt_QStringFromHString(const HString &string) { UINT32 length; PCWSTR rawString = string.GetRawBuffer(&length); return QString::fromWCharArray(rawString, length); } class SocketEngineWorker : public QObject { Q_OBJECT public: SocketEngineWorker(QNativeSocketEnginePrivate *engine) : enginePrivate(engine) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << engine; } ~SocketEngineWorker() { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; if (Q_UNLIKELY(initialReadOp)) { qCDebug(lcNetworkSocket) << Q_FUNC_INFO << "Closing initial read operation"; ComPtr info; HRESULT hr = initialReadOp.As(&info); Q_ASSERT_SUCCEEDED(hr); if (info) { hr = info->Cancel(); Q_ASSERT_SUCCEEDED(hr); hr = info->Close(); Q_ASSERT_SUCCEEDED(hr); } } if (readOp) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Closing read operation"; ComPtr info; HRESULT hr = readOp.As(&info); Q_ASSERT_SUCCEEDED(hr); if (info) { hr = info->Cancel(); Q_ASSERT_SUCCEEDED(hr); hr = info->Close(); Q_ASSERT_SUCCEEDED(hr); } } if (connectOp) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Closing connect operation"; ComPtr info; HRESULT hr = connectOp.As(&info); Q_ASSERT_SUCCEEDED(hr); if (info) { hr = info->Cancel(); Q_ASSERT_SUCCEEDED(hr); hr = info->Close(); Q_ASSERT_SUCCEEDED(hr); } } } signals: void connectOpFinished(bool success, QAbstractSocket::SocketError error, WinRTSocketEngine::ErrorString errorString); void newDataReceived(); void socketErrorOccured(QAbstractSocket::SocketError error); public: void startReading() { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; ComPtr buffer; HRESULT hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &buffer); Q_ASSERT_SUCCEEDED(hr); ComPtr stream; hr = tcpSocket->get_InputStream(&stream); Q_ASSERT_SUCCEEDED(hr); hr = stream->ReadAsync(buffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, initialReadOp.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); enginePrivate->socketState = QAbstractSocket::ConnectedState; hr = initialReadOp->put_Completed(Callback(this, &SocketEngineWorker::onReadyRead).Get()); Q_ASSERT_SUCCEEDED(hr); } HRESULT onConnectOpFinished(IAsyncAction *action, AsyncStatus) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; HRESULT hr = action->GetResults(); if (FAILED(hr)) { if (hr == HRESULT_FROM_WIN32(WSAETIMEDOUT)) { emit connectOpFinished(false, QAbstractSocket::NetworkError, WinRTSocketEngine::ConnectionTimeOutErrorString); return S_OK; } else if (hr == HRESULT_FROM_WIN32(WSAEHOSTUNREACH)) { emit connectOpFinished(false, QAbstractSocket::HostNotFoundError, WinRTSocketEngine::HostUnreachableErrorString); return S_OK; } else if (hr == HRESULT_FROM_WIN32(WSAECONNREFUSED)) { emit connectOpFinished(false, QAbstractSocket::ConnectionRefusedError, WinRTSocketEngine::ConnectionRefusedErrorString); return S_OK; } else { emit connectOpFinished(false, QAbstractSocket::UnknownSocketError, WinRTSocketEngine::UnknownSocketErrorString); return S_OK; } } // The callback might be triggered several times if we do not cancel/reset it here if (connectOp) { ComPtr info; hr = connectOp.As(&info); Q_ASSERT_SUCCEEDED(hr); if (info) { hr = info->Cancel(); Q_ASSERT_SUCCEEDED(hr); hr = info->Close(); Q_ASSERT_SUCCEEDED(hr); } hr = connectOp.Reset(); Q_ASSERT_SUCCEEDED(hr); } emit connectOpFinished(true, QAbstractSocket::UnknownSocketError, WinRTSocketEngine::UnknownSocketErrorString); return S_OK; } HRESULT OnNewDatagramReceived(IDatagramSocket *, IDatagramSocketMessageReceivedEventArgs *args) { qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO; WinRtDatagram datagram; QHostAddress returnAddress; ComPtr remoteHost; HRESULT hr = args->get_RemoteAddress(&remoteHost); RETURN_OK_IF_FAILED("Could not obtain remote host"); HString remoteHostString; hr = remoteHost->get_CanonicalName(remoteHostString.GetAddressOf()); RETURN_OK_IF_FAILED("Could not obtain remote host's canonical name"); returnAddress.setAddress(qt_QStringFromHString(remoteHostString)); datagram.header.senderAddress = returnAddress; HString remotePort; hr = args->get_RemotePort(remotePort.GetAddressOf()); RETURN_OK_IF_FAILED("Could not obtain remote port"); datagram.header.senderPort = qt_QStringFromHString(remotePort).toInt(); ComPtr reader; hr = args->GetDataReader(&reader); RETURN_OK_IF_FAILED("Could not obtain data reader"); quint32 length; hr = reader->get_UnconsumedBufferLength(&length); RETURN_OK_IF_FAILED("Could not obtain unconsumed buffer length"); datagram.data.resize(length); hr = reader->ReadBytes(length, reinterpret_cast(datagram.data.data())); RETURN_OK_IF_FAILED("Could not read datagram"); QMutexLocker locker(&mutex); // Notify the engine about new datagrams being present at the next event loop iteration if (emitDataReceived) emit newDataReceived(); pendingDatagrams << datagram; return S_OK; } HRESULT onReadyRead(IAsyncBufferOperation *asyncInfo, AsyncStatus status) { qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO; if (asyncInfo == initialReadOp.Get()) { initialReadOp.Reset(); } else if (asyncInfo == readOp.Get()) { readOp.Reset(); } else { Q_ASSERT(false); } // A read in UnconnectedState will close the socket and return -1 and thus tell the caller, // that the connection was closed. The socket cannot be closed here, as the subsequent read // might fail then. if (status == Error || status == Canceled) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Remote host closed"; emit socketErrorOccured(QAbstractSocket::RemoteHostClosedError); return S_OK; } ComPtr buffer; HRESULT hr = asyncInfo->GetResults(&buffer); if (FAILED(hr)) { qErrnoWarning(hr, "Failed to get read results buffer"); emit socketErrorOccured(QAbstractSocket::UnknownSocketError); return S_OK; } UINT32 bufferLength; hr = buffer->get_Length(&bufferLength); if (FAILED(hr)) { qErrnoWarning(hr, "Failed to get buffer length"); emit socketErrorOccured(QAbstractSocket::UnknownSocketError); return S_OK; } // A zero sized buffer length signals, that the remote host closed the connection. The socket // cannot be closed though, as the following read might have socket descriptor -1 and thus and // the closing of the socket won't be communicated to the caller. So only the error is set. The // actual socket close happens inside of read. if (!bufferLength) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Remote host closed"; emit socketErrorOccured(QAbstractSocket::RemoteHostClosedError); return S_OK; } ComPtr byteArrayAccess; hr = buffer.As(&byteArrayAccess); if (FAILED(hr)) { qErrnoWarning(hr, "Failed to get cast buffer"); emit socketErrorOccured(QAbstractSocket::UnknownSocketError); return S_OK; } byte *data; hr = byteArrayAccess->Buffer(&data); if (FAILED(hr)) { qErrnoWarning(hr, "Failed to access buffer data"); emit socketErrorOccured(QAbstractSocket::UnknownSocketError); return S_OK; } QByteArray newData(reinterpret_cast(data), qint64(bufferLength)); QMutexLocker readLocker(&mutex); emit newDataReceived(); pendingData.append(newData); readLocker.unlock(); hr = QEventDispatcherWinRT::runOnXamlThread([buffer, this]() { UINT32 readBufferLength; ComPtr stream; HRESULT hr = tcpSocket->get_InputStream(&stream); if (FAILED(hr)) { qErrnoWarning(hr, "Failed to obtain input stream"); emit socketErrorOccured(QAbstractSocket::UnknownSocketError); return S_OK; } // Reuse the stream buffer hr = buffer->get_Capacity(&readBufferLength); if (FAILED(hr)) { qErrnoWarning(hr, "Failed to get buffer capacity"); emit socketErrorOccured(QAbstractSocket::UnknownSocketError); return S_OK; } hr = buffer->put_Length(0); if (FAILED(hr)) { qErrnoWarning(hr, "Failed to set buffer length"); emit socketErrorOccured(QAbstractSocket::UnknownSocketError); return S_OK; } hr = stream->ReadAsync(buffer.Get(), readBufferLength, InputStreamOptions_Partial, &readOp); if (FAILED(hr)) { qErrnoWarning(hr, "onReadyRead(): Could not read into socket stream buffer."); emit socketErrorOccured(QAbstractSocket::UnknownSocketError); return S_OK; } hr = readOp->put_Completed(Callback(this, &SocketEngineWorker::onReadyRead).Get()); if (FAILED(hr)) { qErrnoWarning(hr, "onReadyRead(): Failed to set socket read callback."); emit socketErrorOccured(QAbstractSocket::UnknownSocketError); return S_OK; } return S_OK; }); Q_ASSERT_SUCCEEDED(hr); return S_OK; } void setTcpSocket(ComPtr socket) { tcpSocket = socket; } private: friend class QNativeSocketEngine; ComPtr tcpSocket; QList pendingDatagrams; bool emitDataReceived = true; QByteArray pendingData; // Protects pendingData/pendingDatagrams which are accessed from native callbacks QMutex mutex; ComPtr connectOp; ComPtr> initialReadOp; ComPtr> readOp; QNativeSocketEnginePrivate *enginePrivate; }; static QByteArray socketDescription(const QAbstractSocketEngine *s) { QByteArray result; if (const QObject *o = s->parent()) { const QString name = o->objectName(); if (!name.isEmpty()) { result += '"'; result += name.toLocal8Bit(); result += "\"/"; } result += o->metaObject()->className(); } return result; } // Common constructs #define Q_CHECK_VALID_SOCKETLAYER(function, returnValue) do { \ if (!isValid()) { \ qWarning(""#function" was called on an uninitialized socket device"); \ return returnValue; \ } } while (0) #define Q_CHECK_INVALID_SOCKETLAYER(function, returnValue) do { \ if (isValid()) { \ qWarning(""#function" was called on an already initialized socket device"); \ return returnValue; \ } } while (0) #define Q_CHECK_STATE(function, checkState, returnValue) do { \ if (d->socketState != (checkState)) { \ qWarning(""#function" was not called in "#checkState); \ return (returnValue); \ } } while (0) #define Q_CHECK_NOT_STATE(function, checkState, returnValue) do { \ if (d->socketState == (checkState)) { \ qWarning(""#function" was called in "#checkState); \ return (returnValue); \ } } while (0) #define Q_CHECK_STATES(function, state1, state2, returnValue) do { \ if (d->socketState != (state1) && d->socketState != (state2)) { \ qWarning(""#function" was called" \ " not in "#state1" or "#state2); \ return (returnValue); \ } } while (0) #define Q_CHECK_STATES3(function, state1, state2, state3, returnValue) do { \ if (d->socketState != (state1) && d->socketState != (state2) && d->socketState != (state3)) { \ qWarning(""#function" was called" \ " not in "#state1", "#state2" or "#state3); \ return (returnValue); \ } } while (0) #define Q_CHECK_TYPE(function, type, returnValue) do { \ if (d->socketType != (type)) { \ qWarning(#function" was called by a" \ " socket other than "#type""); \ return (returnValue); \ } } while (0) #define Q_TR(a) QT_TRANSLATE_NOOP(QNativeSocketEngine, a) template static AsyncStatus opStatus(const ComPtr &op) { ComPtr info; HRESULT hr = op.As(&info); Q_ASSERT_SUCCEEDED(hr); AsyncStatus status; hr = info->get_Status(&status); Q_ASSERT_SUCCEEDED(hr); return status; } static qint64 writeIOStream(ComPtr stream, const char *data, qint64 len) { qCDebug(lcNetworkSocket) << Q_FUNC_INFO << data << len; ComPtr buffer; HRESULT hr = g->bufferFactory->Create(len, &buffer); Q_ASSERT_SUCCEEDED(hr); hr = buffer->put_Length(len); Q_ASSERT_SUCCEEDED(hr); ComPtr byteArrayAccess; hr = buffer.As(&byteArrayAccess); Q_ASSERT_SUCCEEDED(hr); byte *bytes; hr = byteArrayAccess->Buffer(&bytes); Q_ASSERT_SUCCEEDED(hr); memcpy(bytes, data, len); ComPtr> op; hr = stream->WriteAsync(buffer.Get(), &op); RETURN_IF_FAILED("Failed to write to stream", return -1); UINT32 bytesWritten; hr = QWinRTFunctions::await(op, &bytesWritten); RETURN_IF_FAILED("Failed to write to stream", return -1); return bytesWritten; } QNativeSocketEngine::QNativeSocketEngine(QObject *parent) : QAbstractSocketEngine(*new QNativeSocketEnginePrivate(), parent) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << parent; qRegisterMetaType(); qRegisterMetaType(); Q_D(QNativeSocketEngine); #ifndef QT_NO_SSL if (parent) d->sslSocket = qobject_cast(parent->parent()); #endif connect(this, &QNativeSocketEngine::connectionReady, this, &QNativeSocketEngine::connectionNotification, Qt::QueuedConnection); connect(this, &QNativeSocketEngine::readReady, this, &QNativeSocketEngine::processReadReady, Qt::QueuedConnection); connect(this, &QNativeSocketEngine::writeReady, this, &QNativeSocketEngine::writeNotification, Qt::QueuedConnection); connect(d->worker, &SocketEngineWorker::connectOpFinished, this, &QNativeSocketEngine::handleConnectOpFinished, Qt::QueuedConnection); connect(d->worker, &SocketEngineWorker::newDataReceived, this, &QNativeSocketEngine::handleNewData, Qt::QueuedConnection); connect(d->worker, &SocketEngineWorker::socketErrorOccured, this, &QNativeSocketEngine::handleTcpError, Qt::QueuedConnection); } QNativeSocketEngine::~QNativeSocketEngine() { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; close(); } bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << type << protocol; Q_D(QNativeSocketEngine); if (isValid()) close(); // Create the socket if (!d->createNewSocket(type, protocol)) return false; if (type == QAbstractSocket::UdpSocket) { // Set the broadcasting flag if it's a UDP socket. if (!setOption(BroadcastSocketOption, 1)) { d->setError(QAbstractSocket::UnsupportedSocketOperationError, WinRTSocketEngine::BroadcastingInitFailedErrorString); close(); return false; } // Set some extra flags that are interesting to us, but accept failure setOption(ReceivePacketInformation, 1); setOption(ReceiveHopLimit, 1); } // Make sure we receive out-of-band data if (type == QAbstractSocket::TcpSocket && !setOption(ReceiveOutOfBandData, 1)) { qWarning("QNativeSocketEngine::initialize unable to inline out-of-band data"); } d->socketType = type; d->socketProtocol = protocol; return true; } bool QNativeSocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << socketDescriptor << socketState; Q_D(QNativeSocketEngine); if (isValid()) close(); // Currently, only TCP sockets are initialized this way. IStreamSocket *socket = gSocketHandler->pendingTcpSockets.take(socketDescriptor); d->socketDescriptor = qintptr(socket); d->socketType = QAbstractSocket::TcpSocket; if (!d->socketDescriptor || !d->fetchConnectionParameters()) { d->setError(QAbstractSocket::UnsupportedSocketOperationError, WinRTSocketEngine::InvalidSocketErrorString); d->socketDescriptor = -1; return false; } // Start processing incoming data if (d->socketType == QAbstractSocket::TcpSocket) { HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([d, socket, this]() { d->worker->setTcpSocket(socket); d->worker->startReading(); return S_OK; }); if (FAILED(hr)) return false; } else { d->socketState = socketState; } return true; } qintptr QNativeSocketEngine::socketDescriptor() const { Q_D(const QNativeSocketEngine); return d->socketDescriptor; } bool QNativeSocketEngine::isValid() const { Q_D(const QNativeSocketEngine); return d->socketDescriptor != -1; } bool QNativeSocketEngine::connectToHost(const QHostAddress &address, quint16 port) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << address << port; Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::connectToHost(), false); Q_CHECK_STATES3(QNativeSocketEngine::connectToHost(), QAbstractSocket::BoundState, QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false); const QString addressString = address.toString(); return connectToHostByName(addressString, port); } bool QNativeSocketEngine::connectToHostByName(const QString &name, quint16 port) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << name << port; Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::connectToHostByName(), false); Q_CHECK_STATES3(QNativeSocketEngine::connectToHostByName(), QAbstractSocket::BoundState, QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false); HRESULT hr; #if _MSC_VER >= 1900 ComPtr networkContext; if (!qEnvironmentVariableIsEmpty("QT_WINRT_USE_THREAD_NETWORK_CONTEXT")) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Creating network context"; hr = qt_winrt_try_create_thread_network_context(name, networkContext); if (FAILED(hr)) { setError(QAbstractSocket::ConnectionRefusedError, QLatin1String("Could not create thread network context.")); d->socketState = QAbstractSocket::ConnectedState; return true; } } #endif // _MSC_VER >= 1900 HStringReference hostNameRef(reinterpret_cast(name.utf16())); ComPtr hostNameFactory; hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), &hostNameFactory); Q_ASSERT_SUCCEEDED(hr); ComPtr remoteHost; hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost); RETURN_FALSE_IF_FAILED("QNativeSocketEngine::connectToHostByName: Could not create hostname."); const QString portString = QString::number(port); HStringReference portReference(reinterpret_cast(portString.utf16())); if (d->socketType == QAbstractSocket::TcpSocket) hr = d->tcpSocket()->ConnectAsync(remoteHost.Get(), portReference.Get(), &d->worker->connectOp); else if (d->socketType == QAbstractSocket::UdpSocket) hr = d->udpSocket()->ConnectAsync(remoteHost.Get(), portReference.Get(), &d->worker->connectOp); if (hr == E_ACCESSDENIED) { qErrnoWarning(hr, "QNativeSocketEngine::connectToHostByName: Unable to connect to host (%s:%hu/%s). " "Please check your manifest capabilities.", qPrintable(name), port, socketDescription(this).constData()); return false; } Q_ASSERT_SUCCEEDED(hr); #if _MSC_VER >= 1900 if (networkContext != nullptr) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Closing network context"; ComPtr networkContextCloser; hr = networkContext.As(&networkContextCloser); Q_ASSERT_SUCCEEDED(hr); hr = networkContextCloser->Close(); Q_ASSERT_SUCCEEDED(hr); } #endif // _MSC_VER >= 1900 d->socketState = QAbstractSocket::ConnectingState; QEventDispatcherWinRT::runOnXamlThread([d, &hr]() { hr = d->worker->connectOp->put_Completed(Callback( d->worker, &SocketEngineWorker::onConnectOpFinished).Get()); RETURN_OK_IF_FAILED("connectToHostByName: Could not register \"connectOp\" callback"); return S_OK; }); if (FAILED(hr)) return false; return d->socketState == QAbstractSocket::ConnectedState; } bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << address << port; Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::bind(), false); Q_CHECK_STATE(QNativeSocketEngine::bind(), QAbstractSocket::UnconnectedState, false); 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_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_OK_IF_FAILED("QNativeSocketEngine::bind: Could not create hostname."); } QString portQString = port ? QString::number(port) : QString(); HStringReference portString(reinterpret_cast(portQString.utf16())); ComPtr op; if (d->socketType == QAbstractSocket::TcpSocket) { if (!d->tcpListener) { hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocketListener).Get(), &d->tcpListener); RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not create tcp listener"); } hr = d->tcpListener->add_ConnectionReceived( Callback(d, &QNativeSocketEnginePrivate::handleClientConnection).Get(), &d->connectionToken); 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); } 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()); d->setError(QAbstractSocket::SocketAccessError, WinRTSocketEngine::AccessErrorString); d->socketState = QAbstractSocket::UnconnectedState; specificErrorSet = true; return S_OK; } RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Unable to bind socket"); hr = QWinRTFunctions::await(op); if (hr == 0x80072741) { // The requested address is not valid in its context d->setError(QAbstractSocket::SocketAddressNotAvailableError, WinRTSocketEngine::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, WinRTSocketEngine::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; }); if (FAILED(hr)) { if (!specificErrorSet) { d->setError(QAbstractSocket::UnknownSocketError, WinRTSocketEngine::UnknownSocketErrorString); d->socketState = QAbstractSocket::UnconnectedState; } return false; } d->socketState = QAbstractSocket::BoundState; return d->fetchConnectionParameters(); } bool QNativeSocketEngine::listen() { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::listen(), false); Q_CHECK_STATE(QNativeSocketEngine::listen(), QAbstractSocket::BoundState, false); #if QT_CONFIG(sctp) Q_CHECK_TYPES(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, QAbstractSocket::SctpSocket, false); #else Q_CHECK_TYPE(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, false); #endif if (d->tcpListener && d->socketDescriptor != -1) { d->socketState = QAbstractSocket::ListeningState; return true; } return false; } int QNativeSocketEngine::accept() { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::accept(), -1); Q_CHECK_STATE(QNativeSocketEngine::accept(), QAbstractSocket::ListeningState, -1); #if QT_CONFIG(sctp) Q_CHECK_TYPES(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket, QAbstractSocket::SctpSocket, -1); #else Q_CHECK_TYPE(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket, -1); #endif if (d->socketDescriptor == -1 || d->pendingConnections.isEmpty()) { d->setError(QAbstractSocket::TemporaryError, WinRTSocketEngine::TemporaryErrorString); return -1; } if (d->socketType == QAbstractSocket::TcpSocket) { IStreamSocket *socket = d->pendingConnections.takeFirst(); SocketHandler *handler = gSocketHandler(); handler->pendingTcpSockets.insert(++handler->socketCount, socket); return handler->socketCount; } return -1; } void QNativeSocketEngine::close() { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; Q_D(QNativeSocketEngine); if (d->closingDown) return; if (d->pendingReadNotification) { // We use QPointer here to see if this QNativeSocketEngine was deleted as a result of // finishing and cleaning up a network request when calling "processReadReady". QPointer alive(this); processReadReady(); if (alive.isNull()) return; } d->closingDown = true; d->notifyOnRead = false; d->notifyOnWrite = false; d->notifyOnException = false; d->emitReadReady = false; HRESULT hr; if (d->socketType == QAbstractSocket::TcpSocket) { hr = QEventDispatcherWinRT::runOnXamlThread([d]() { HRESULT hr; // To close the connection properly (not with a hard reset) all pending read operation have to // be finished or cancelled. The API isn't available on Windows 8.1 though. ComPtr socket3; hr = d->tcpSocket()->QueryInterface(IID_PPV_ARGS(&socket3)); Q_ASSERT_SUCCEEDED(hr); ComPtr action; hr = socket3->CancelIOAsync(&action); Q_ASSERT_SUCCEEDED(hr); hr = QWinRTFunctions::await(action, QWinRTFunctions::YieldThread, 5000); // If there is no pending IO (no read established before) the function will fail with // "function was called at an unexpected time" which is fine. // Timeout is fine as well. The result will be the socket being hard reset instead of // being closed gracefully if (hr != E_ILLEGAL_METHOD_CALL && hr != ERROR_TIMEOUT) Q_ASSERT_SUCCEEDED(hr); return S_OK; }); Q_ASSERT_SUCCEEDED(hr); } if (d->socketDescriptor != -1) { ComPtr socket; if (d->socketType == QAbstractSocket::TcpSocket) { hr = d->tcpSocket()->QueryInterface(IID_PPV_ARGS(&socket)); Q_ASSERT_SUCCEEDED(hr); hr = d->tcpSocket()->Release(); Q_ASSERT_SUCCEEDED(hr); } else if (d->socketType == QAbstractSocket::UdpSocket) { hr = d->udpSocket()->QueryInterface(IID_PPV_ARGS(&socket)); Q_ASSERT_SUCCEEDED(hr); hr = d->udpSocket()->Release(); Q_ASSERT_SUCCEEDED(hr); } if (socket) { hr = socket->Close(); Q_ASSERT_SUCCEEDED(hr); } d->socketDescriptor = -1; } d->socketState = QAbstractSocket::UnconnectedState; d->hasSetSocketError = false; d->localPort = 0; d->localAddress.clear(); d->peerPort = 0; d->peerAddress.clear(); d->inboundStreamCount = d->outboundStreamCount = 0; } bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface) { Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::joinMulticastGroup(), false); Q_CHECK_STATE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false); Q_CHECK_TYPE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false); qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << groupAddress << iface; Q_UNIMPLEMENTED(); return false; } bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface) { Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::leaveMulticastGroup(), false); Q_CHECK_STATE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false); Q_CHECK_TYPE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false); qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << groupAddress << iface; Q_UNIMPLEMENTED(); return false; } QNetworkInterface QNativeSocketEngine::multicastInterface() const { Q_D(const QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::multicastInterface(), QNetworkInterface()); Q_CHECK_TYPE(QNativeSocketEngine::multicastInterface(), QAbstractSocket::UdpSocket, QNetworkInterface()); Q_UNIMPLEMENTED(); return QNetworkInterface(); } bool QNativeSocketEngine::setMulticastInterface(const QNetworkInterface &iface) { Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setMulticastInterface(), false); Q_CHECK_TYPE(QNativeSocketEngine::setMulticastInterface(), QAbstractSocket::UdpSocket, false); qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << iface; Q_UNIMPLEMENTED(); return false; } qint64 QNativeSocketEngine::bytesAvailable() const { Q_D(const QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::bytesAvailable(), -1); Q_CHECK_NOT_STATE(QNativeSocketEngine::bytesAvailable(), QAbstractSocket::UnconnectedState, -1); if (d->socketType != QAbstractSocket::TcpSocket) return -1; QMutexLocker locker(&d->worker->mutex); const qint64 bytesAvailable = d->worker->pendingData.length(); qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << bytesAvailable; return bytesAvailable; } qint64 QNativeSocketEngine::read(char *data, qint64 maxlen) { qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << maxlen; Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::read(), -1); Q_CHECK_STATES(QNativeSocketEngine::read(), QAbstractSocket::ConnectedState, QAbstractSocket::BoundState, -1); if (d->socketType != QAbstractSocket::TcpSocket) return -1; // There will be a read notification when the socket was closed by the remote host. If that // happens and there isn't anything left in the buffer, we have to return -1 in order to signal // the closing of the socket. QMutexLocker mutexLocker(&d->worker->mutex); if (d->worker->pendingData.isEmpty() && d->socketState != QAbstractSocket::ConnectedState) { close(); return -1; } QByteArray readData; const int copyLength = qMin(maxlen, qint64(d->worker->pendingData.length())); if (maxlen >= d->worker->pendingData.length()) { qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Reading full buffer"; readData = d->worker->pendingData; d->worker->pendingData.clear(); d->emitReadReady = true; } else { qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Reading part of the buffer (" << copyLength << "of" << d->worker->pendingData.length() << "bytes"; readData = d->worker->pendingData.left(maxlen); d->worker->pendingData.remove(0, maxlen); if (d->notifyOnRead) { d->pendingReadNotification = true; emit readReady(); } } mutexLocker.unlock(); memcpy(data, readData, copyLength); qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Read" << copyLength << "bytes"; return copyLength; } qint64 QNativeSocketEngine::write(const char *data, qint64 len) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << data << len; Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::write(), -1); Q_CHECK_STATE(QNativeSocketEngine::write(), QAbstractSocket::ConnectedState, -1); HRESULT hr = E_FAIL; ComPtr stream; if (d->socketType == QAbstractSocket::TcpSocket) hr = d->tcpSocket()->get_OutputStream(&stream); else if (d->socketType == QAbstractSocket::UdpSocket) hr = d->udpSocket()->get_OutputStream(&stream); Q_ASSERT_SUCCEEDED(hr); qint64 bytesWritten = writeIOStream(stream, data, len); if (bytesWritten < 0) d->setError(QAbstractSocket::SocketAccessError, WinRTSocketEngine::AccessErrorString); else if (bytesWritten > 0 && d->notifyOnWrite) emit writeReady(); return bytesWritten; } qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header, PacketHeaderOptions) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << maxlen; #ifndef QT_NO_UDPSOCKET Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::readDatagram(), -1); Q_CHECK_STATES(QNativeSocketEngine::readDatagram(), QAbstractSocket::BoundState, QAbstractSocket::ConnectedState, -1); QMutexLocker locker(&d->worker->mutex); if (d->socketType != QAbstractSocket::UdpSocket || d->worker->pendingDatagrams.isEmpty()) { if (header) header->clear(); return -1; } WinRtDatagram datagram = d->worker->pendingDatagrams.takeFirst(); if (header) *header = datagram.header; QByteArray readOrigin; if (maxlen < datagram.data.length()) readOrigin = datagram.data.left(maxlen); else readOrigin = datagram.data; if (d->worker->pendingDatagrams.isEmpty()) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "That's all folks"; d->worker->emitDataReceived = true; d->emitReadReady = true; } locker.unlock(); memcpy(data, readOrigin, qMin(maxlen, qint64(datagram.data.length()))); return readOrigin.length(); #else Q_UNUSED(data) Q_UNUSED(maxlen) Q_UNUSED(header) return -1; #endif // QT_NO_UDPSOCKET } qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << data << len; #ifndef QT_NO_UDPSOCKET Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::writeDatagram(), -1); Q_CHECK_STATES(QNativeSocketEngine::writeDatagram(), QAbstractSocket::BoundState, QAbstractSocket::ConnectedState, -1); if (d->socketType != QAbstractSocket::UdpSocket) return -1; ComPtr remoteHost; ComPtr hostNameFactory; HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), &hostNameFactory); Q_ASSERT_SUCCEEDED(hr); const QString addressString = header.destinationAddress.toString(); HStringReference hostNameRef(reinterpret_cast(addressString.utf16())); hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost); RETURN_IF_FAILED("QNativeSocketEngine::writeDatagram: Could not create hostname.", return -1); ComPtr> streamOperation; ComPtr stream; const QString portString = QString::number(header.destinationPort); HStringReference portRef(reinterpret_cast(portString.utf16())); hr = d->udpSocket()->GetOutputStreamAsync(remoteHost.Get(), portRef.Get(), &streamOperation); Q_ASSERT_SUCCEEDED(hr); hr = QWinRTFunctions::await(streamOperation, stream.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); return writeIOStream(stream, data, len); #else Q_UNUSED(data) Q_UNUSED(len) Q_UNUSED(header) return -1; #endif // QT_NO_UDPSOCKET } bool QNativeSocketEngine::hasPendingDatagrams() const { Q_D(const QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::hasPendingDatagrams(), false); Q_CHECK_NOT_STATE(QNativeSocketEngine::hasPendingDatagrams(), QAbstractSocket::UnconnectedState, false); Q_CHECK_TYPE(QNativeSocketEngine::hasPendingDatagrams(), QAbstractSocket::UdpSocket, false); QMutexLocker locker(&d->worker->mutex); return d->worker->pendingDatagrams.length() > 0; } qint64 QNativeSocketEngine::pendingDatagramSize() const { Q_D(const QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::pendingDatagramSize(), -1); Q_CHECK_TYPE(QNativeSocketEngine::pendingDatagramSize(), QAbstractSocket::UdpSocket, -1); QMutexLocker locker(&d->worker->mutex); if (d->worker->pendingDatagrams.isEmpty()) return -1; return d->worker->pendingDatagrams.at(0).data.length(); } qint64 QNativeSocketEngine::bytesToWrite() const { return 0; } qint64 QNativeSocketEngine::receiveBufferSize() const { Q_D(const QNativeSocketEngine); return d->option(QAbstractSocketEngine::ReceiveBufferSocketOption); } void QNativeSocketEngine::setReceiveBufferSize(qint64 bufferSize) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << bufferSize; Q_D(QNativeSocketEngine); d->setOption(QAbstractSocketEngine::ReceiveBufferSocketOption, bufferSize); } qint64 QNativeSocketEngine::sendBufferSize() const { Q_D(const QNativeSocketEngine); return d->option(QAbstractSocketEngine::SendBufferSocketOption); } void QNativeSocketEngine::setSendBufferSize(qint64 bufferSize) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << bufferSize; Q_D(QNativeSocketEngine); d->setOption(QAbstractSocketEngine::SendBufferSocketOption, bufferSize); } int QNativeSocketEngine::option(QAbstractSocketEngine::SocketOption option) const { Q_D(const QNativeSocketEngine); return d->option(option); } bool QNativeSocketEngine::setOption(QAbstractSocketEngine::SocketOption option, int value) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << option << value; Q_D(QNativeSocketEngine); return d->setOption(option, value); } bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << msecs; Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForRead(), false); Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForRead(), QAbstractSocket::UnconnectedState, false); if (timedOut) *timedOut = false; QElapsedTimer timer; timer.start(); while (msecs > timer.elapsed()) { // Servers with active connections are ready for reading if (!d->currentConnections.isEmpty()) return true; // If we are a client, we are ready to read if our buffer has data QMutexLocker locker(&d->worker->mutex); if (!d->worker->pendingData.isEmpty()) return true; // Nothing to do, wait for more events d->eventLoop.processEvents(); } d->setError(QAbstractSocket::SocketTimeoutError, WinRTSocketEngine::TimeOutErrorString); if (timedOut) *timedOut = true; return false; } bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << msecs; Q_UNUSED(timedOut); Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false); Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForWrite(), QAbstractSocket::UnconnectedState, false); if (d->socketState == QAbstractSocket::ConnectingState) { HRESULT hr = QWinRTFunctions::await(d->worker->connectOp, QWinRTFunctions::ProcessMainThreadEvents); if (SUCCEEDED(hr)) { handleConnectOpFinished(true, QAbstractSocket::UnknownSocketError, WinRTSocketEngine::UnknownSocketErrorString); return true; } } return false; } bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, int msecs, bool *timedOut) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << checkRead << checkWrite << msecs; Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForReadOrWrite(), false); Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForReadOrWrite(), QAbstractSocket::UnconnectedState, false); Q_UNUSED(readyToRead); Q_UNUSED(readyToWrite); Q_UNUSED(timedOut); return false; } bool QNativeSocketEngine::isReadNotificationEnabled() const { Q_D(const QNativeSocketEngine); return d->notifyOnRead; } void QNativeSocketEngine::setReadNotificationEnabled(bool enable) { qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << enable; Q_D(QNativeSocketEngine); d->notifyOnRead = enable; } bool QNativeSocketEngine::isWriteNotificationEnabled() const { Q_D(const QNativeSocketEngine); return d->notifyOnWrite; } void QNativeSocketEngine::setWriteNotificationEnabled(bool enable) { qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << enable; Q_D(QNativeSocketEngine); d->notifyOnWrite = enable; if (enable && d->socketState == QAbstractSocket::ConnectedState) { if (bytesToWrite()) return; // will be emitted as a result of bytes written writeNotification(); } } bool QNativeSocketEngine::isExceptionNotificationEnabled() const { Q_D(const QNativeSocketEngine); return d->notifyOnException; } void QNativeSocketEngine::setExceptionNotificationEnabled(bool enable) { qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << enable; Q_D(QNativeSocketEngine); d->notifyOnException = enable; } void QNativeSocketEngine::establishRead() { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; Q_D(QNativeSocketEngine); HRESULT hr; hr = QEventDispatcherWinRT::runOnXamlThread([d]() { d->worker->setTcpSocket(d->tcpSocket()); d->worker->startReading(); return S_OK; }); Q_ASSERT_SUCCEEDED(hr); } void QNativeSocketEngine::handleConnectOpFinished(bool success, QAbstractSocket::SocketError error, WinRTSocketEngine::ErrorString errorString) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << success << error << errorString; Q_D(QNativeSocketEngine); disconnect(d->worker, &SocketEngineWorker::connectOpFinished, this, &QNativeSocketEngine::handleConnectOpFinished); if (!success) { d->setError(error, errorString); d->socketState = QAbstractSocket::UnconnectedState; close(); return; } d->socketState = QAbstractSocket::ConnectedState; d->fetchConnectionParameters(); emit connectionReady(); if (d->socketType != QAbstractSocket::TcpSocket) return; #ifndef QT_NO_SSL // Delay the reader so that the SSL socket can upgrade if (d->sslSocket) QObject::connect(qobject_cast(d->sslSocket), &QSslSocket::encrypted, this, &QNativeSocketEngine::establishRead); else #endif establishRead(); } void QNativeSocketEngine::handleNewData() { qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO; Q_D(QNativeSocketEngine); if (d->notifyOnRead && d->emitReadReady) { if (d->socketType == QAbstractSocket::UdpSocket && !d->worker->emitDataReceived) return; qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Emitting readReady"; d->pendingReadNotification = true; emit readReady(); d->worker->emitDataReceived = false; d->emitReadReady = false; } } void QNativeSocketEngine::handleTcpError(QAbstractSocket::SocketError error) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << error; Q_D(QNativeSocketEngine); WinRTSocketEngine::ErrorString errorString; switch (error) { case QAbstractSocket::RemoteHostClosedError: errorString = WinRTSocketEngine::RemoteHostClosedErrorString; break; default: errorString = WinRTSocketEngine::UnknownSocketErrorString; } d->setError(error, errorString); close(); } void QNativeSocketEngine::processReadReady() { Q_D(QNativeSocketEngine); if (d->closingDown) return; d->pendingReadNotification = false; readNotification(); } bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol &socketProtocol) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << socketType << socketProtocol; Q_UNUSED(socketProtocol); HRESULT hr; switch (socketType) { case QAbstractSocket::TcpSocket: { ComPtr socket; hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &socket); 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); RETURN_FALSE_IF_FAILED("createNewSocket: Could not create socket instance"); socketDescriptor = qintptr(socket.Detach()); QEventDispatcherWinRT::runOnXamlThread([&hr, this]() { hr = udpSocket()->add_MessageReceived(Callback(worker, &SocketEngineWorker::OnNewDatagramReceived).Get(), &connectionToken); if (FAILED(hr)) { qErrnoWarning(hr, "createNewSocket: Could not add \"message received\" callback"); return hr; } return S_OK; }); if (FAILED(hr)) return false; break; } default: qWarning("Invalid socket type"); return false; } this->socketType = socketType; // Make the socket nonblocking. if (!setOption(QAbstractSocketEngine::NonBlockingSocketOption, 1)) { setError(QAbstractSocket::UnsupportedSocketOperationError, WinRTSocketEngine::NonBlockingInitFailedErrorString); q_func()->close(); return false; } return true; } QNativeSocketEnginePrivate::QNativeSocketEnginePrivate() : QAbstractSocketEnginePrivate() , notifyOnRead(true) , notifyOnWrite(true) , notifyOnException(false) , closingDown(false) , socketDescriptor(-1) , worker(new SocketEngineWorker(this)) , sslSocket(nullptr) , connectionToken( { -1 } ) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; } QNativeSocketEnginePrivate::~QNativeSocketEnginePrivate() { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; if (socketDescriptor == -1 || connectionToken.value == -1) return; HRESULT hr; if (socketType == QAbstractSocket::UdpSocket) hr = udpSocket()->remove_MessageReceived(connectionToken); else if (socketType == QAbstractSocket::TcpSocket) hr = tcpListener->remove_ConnectionReceived(connectionToken); Q_ASSERT_SUCCEEDED(hr); worker->deleteLater(); } void QNativeSocketEnginePrivate::setError(QAbstractSocket::SocketError error, WinRTSocketEngine::ErrorString errorString) const { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << error << errorString; if (hasSetSocketError) { // Only set socket errors once for one engine; expect the // socket to recreate its engine after an error. Note: There's // one exception: SocketError(11) bypasses this as it's purely // a temporary internal error condition. // Another exception is the way the waitFor*() functions set // an error when a timeout occurs. After the call to setError() // they reset the hasSetSocketError to false return; } if (error != QAbstractSocket::SocketError(11)) hasSetSocketError = true; socketError = error; switch (errorString) { case WinRTSocketEngine::NonBlockingInitFailedErrorString: socketErrorString = QNativeSocketEngine::tr("Unable to initialize non-blocking socket"); break; case WinRTSocketEngine::BroadcastingInitFailedErrorString: socketErrorString = QNativeSocketEngine::tr("Unable to initialize broadcast socket"); break; // should not happen anymore case WinRTSocketEngine::NoIpV6ErrorString: socketErrorString = QNativeSocketEngine::tr("Attempt to use IPv6 socket on a platform with no IPv6 support"); break; case WinRTSocketEngine::RemoteHostClosedErrorString: socketErrorString = QNativeSocketEngine::tr("The remote host closed the connection"); break; case WinRTSocketEngine::TimeOutErrorString: socketErrorString = QNativeSocketEngine::tr("Network operation timed out"); break; case WinRTSocketEngine::ResourceErrorString: socketErrorString = QNativeSocketEngine::tr("Out of resources"); break; case WinRTSocketEngine::OperationUnsupportedErrorString: socketErrorString = QNativeSocketEngine::tr("Unsupported socket operation"); break; case WinRTSocketEngine::ProtocolUnsupportedErrorString: socketErrorString = QNativeSocketEngine::tr("Protocol type not supported"); break; case WinRTSocketEngine::InvalidSocketErrorString: socketErrorString = QNativeSocketEngine::tr("Invalid socket descriptor"); break; case WinRTSocketEngine::HostUnreachableErrorString: socketErrorString = QNativeSocketEngine::tr("Host unreachable"); break; case WinRTSocketEngine::NetworkUnreachableErrorString: socketErrorString = QNativeSocketEngine::tr("Network unreachable"); break; case WinRTSocketEngine::AccessErrorString: socketErrorString = QNativeSocketEngine::tr("Permission denied"); break; case WinRTSocketEngine::ConnectionTimeOutErrorString: socketErrorString = QNativeSocketEngine::tr("Connection timed out"); break; case WinRTSocketEngine::ConnectionRefusedErrorString: socketErrorString = QNativeSocketEngine::tr("Connection refused"); break; case WinRTSocketEngine::AddressInuseErrorString: socketErrorString = QNativeSocketEngine::tr("The bound address is already in use"); break; case WinRTSocketEngine::AddressNotAvailableErrorString: socketErrorString = QNativeSocketEngine::tr("The address is not available"); break; case WinRTSocketEngine::AddressProtectedErrorString: socketErrorString = QNativeSocketEngine::tr("The address is protected"); break; case WinRTSocketEngine::DatagramTooLargeErrorString: socketErrorString = QNativeSocketEngine::tr("Datagram was too large to send"); break; case WinRTSocketEngine::SendDatagramErrorString: socketErrorString = QNativeSocketEngine::tr("Unable to send a message"); break; case WinRTSocketEngine::ReceiveDatagramErrorString: socketErrorString = QNativeSocketEngine::tr("Unable to receive a message"); break; case WinRTSocketEngine::WriteErrorString: socketErrorString = QNativeSocketEngine::tr("Unable to write"); break; case WinRTSocketEngine::ReadErrorString: socketErrorString = QNativeSocketEngine::tr("Network error"); break; case WinRTSocketEngine::PortInuseErrorString: socketErrorString = QNativeSocketEngine::tr("Another socket is already listening on the same port"); break; case WinRTSocketEngine::NotSocketErrorString: socketErrorString = QNativeSocketEngine::tr("Operation on non-socket"); break; case WinRTSocketEngine::InvalidProxyTypeString: socketErrorString = QNativeSocketEngine::tr("The proxy type is invalid for this operation"); break; case WinRTSocketEngine::TemporaryErrorString: socketErrorString = QNativeSocketEngine::tr("Temporary error"); break; case WinRTSocketEngine::UnknownSocketErrorString: socketErrorString = QNativeSocketEngine::tr("Unknown error"); break; } } int QNativeSocketEnginePrivate::option(QAbstractSocketEngine::SocketOption opt) const { ComPtr control; if (socketType == QAbstractSocket::TcpSocket) { if (FAILED(tcpSocket()->get_Control(&control))) { qWarning("QNativeSocketEnginePrivate::option: Could not obtain socket control"); return -1; } } switch (opt) { case QAbstractSocketEngine::NonBlockingSocketOption: case QAbstractSocketEngine::BroadcastSocketOption: case QAbstractSocketEngine::ReceiveOutOfBandData: return 1; case QAbstractSocketEngine::SendBufferSocketOption: if (socketType == QAbstractSocket::UdpSocket) return -1; UINT32 bufferSize; if (FAILED(control->get_OutboundBufferSizeInBytes(&bufferSize))) { qWarning("Could not obtain OutboundBufferSizeInBytes information vom socket control"); return -1; } return bufferSize; case QAbstractSocketEngine::LowDelayOption: if (socketType == QAbstractSocket::UdpSocket) return -1; boolean noDelay; if (FAILED(control->get_NoDelay(&noDelay))) { qWarning("Could not obtain NoDelay information from socket control"); return -1; } return noDelay; case QAbstractSocketEngine::KeepAliveOption: if (socketType == QAbstractSocket::UdpSocket) return -1; boolean keepAlive; if (FAILED(control->get_KeepAlive(&keepAlive))) { qWarning("Could not obtain KeepAlive information from socket control"); return -1; } return keepAlive; case QAbstractSocketEngine::ReceiveBufferSocketOption: case QAbstractSocketEngine::AddressReusable: case QAbstractSocketEngine::BindExclusively: case QAbstractSocketEngine::MulticastTtlOption: case QAbstractSocketEngine::MulticastLoopbackOption: case QAbstractSocketEngine::TypeOfServiceOption: case QAbstractSocketEngine::MaxStreamsSocketOption: case QAbstractSocketEngine::PathMtuInformation: default: return -1; } return -1; } bool QNativeSocketEnginePrivate::setOption(QAbstractSocketEngine::SocketOption opt, int v) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << opt << v; ComPtr control; if (socketType == QAbstractSocket::TcpSocket) { if (FAILED(tcpSocket()->get_Control(&control))) { qWarning("QNativeSocketEnginePrivate::setOption: Could not obtain socket control"); return false; } } switch (opt) { case QAbstractSocketEngine::NonBlockingSocketOption: case QAbstractSocketEngine::BroadcastSocketOption: case QAbstractSocketEngine::ReceiveOutOfBandData: return v != 0; case QAbstractSocketEngine::SendBufferSocketOption: if (socketType == QAbstractSocket::UdpSocket) return false; if (FAILED(control->put_OutboundBufferSizeInBytes(v))) { qWarning("Could not set OutboundBufferSizeInBytes"); return false; } return true; case QAbstractSocketEngine::LowDelayOption: { if (socketType == QAbstractSocket::UdpSocket) return false; boolean noDelay = v; if (FAILED(control->put_NoDelay(noDelay))) { qWarning("Could not obtain NoDelay information from socket control"); return false; } return true; } case QAbstractSocketEngine::KeepAliveOption: { if (socketType == QAbstractSocket::UdpSocket || socketState != QAbstractSocket::UnconnectedState) return false; boolean keepAlive = v; if (FAILED(control->put_KeepAlive(keepAlive))) { qWarning("Could not set KeepAlive value"); return false; } return true; } case QAbstractSocketEngine::ReceiveBufferSocketOption: case QAbstractSocketEngine::AddressReusable: case QAbstractSocketEngine::BindExclusively: case QAbstractSocketEngine::MulticastTtlOption: case QAbstractSocketEngine::MulticastLoopbackOption: case QAbstractSocketEngine::TypeOfServiceOption: case QAbstractSocketEngine::MaxStreamsSocketOption: case QAbstractSocketEngine::PathMtuInformation: default: return false; } return false; } bool QNativeSocketEnginePrivate::fetchConnectionParameters() { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; localPort = 0; localAddress.clear(); peerPort = 0; peerAddress.clear(); inboundStreamCount = outboundStreamCount = 0; HRESULT hr; if (socketType == QAbstractSocket::TcpSocket) { ComPtr hostName; HString tmpHString; ComPtr info; hr = tcpSocket()->get_Information(&info); Q_ASSERT_SUCCEEDED(hr); hr = info->get_LocalAddress(&hostName); Q_ASSERT_SUCCEEDED(hr); if (hostName) { hr = hostName->get_CanonicalName(tmpHString.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); localAddress.setAddress(qt_QStringFromHString(tmpHString)); hr = info->get_LocalPort(tmpHString.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); localPort = qt_QStringFromHString(tmpHString).toInt(); } if (!localPort && tcpListener) { ComPtr listenerInfo = 0; hr = tcpListener->get_Information(&listenerInfo); Q_ASSERT_SUCCEEDED(hr); hr = listenerInfo->get_LocalPort(tmpHString.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); localPort = qt_QStringFromHString(tmpHString).toInt(); localAddress = QHostAddress::Any; } info->get_RemoteAddress(&hostName); if (hostName) { hr = hostName->get_CanonicalName(tmpHString.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); peerAddress.setAddress(qt_QStringFromHString(tmpHString)); hr = info->get_RemotePort(tmpHString.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); peerPort = qt_QStringFromHString(tmpHString).toInt(); inboundStreamCount = outboundStreamCount = 1; } } else if (socketType == QAbstractSocket::UdpSocket) { ComPtr hostName; HString tmpHString; ComPtr info; hr = udpSocket()->get_Information(&info); Q_ASSERT_SUCCEEDED(hr); hr = info->get_LocalAddress(&hostName); Q_ASSERT_SUCCEEDED(hr); if (hostName) { hr = hostName->get_CanonicalName(tmpHString.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); localAddress.setAddress(qt_QStringFromHString(tmpHString)); hr = info->get_LocalPort(tmpHString.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); localPort = qt_QStringFromHString(tmpHString).toInt(); } hr = info->get_RemoteAddress(&hostName); Q_ASSERT_SUCCEEDED(hr); if (hostName) { hr = hostName->get_CanonicalName(tmpHString.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); peerAddress.setAddress(qt_QStringFromHString(tmpHString)); hr = info->get_RemotePort(tmpHString.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); peerPort = qt_QStringFromHString(tmpHString).toInt(); inboundStreamCount = outboundStreamCount = 1; } } return true; } HRESULT QNativeSocketEnginePrivate::handleClientConnection(IStreamSocketListener *listener, IStreamSocketListenerConnectionReceivedEventArgs *args) { qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO; Q_Q(QNativeSocketEngine); Q_UNUSED(listener) IStreamSocket *socket; args->get_Socket(&socket); pendingConnections.append(socket); emit q->connectionReady(); if (notifyOnRead) emit q->readReady(); return S_OK; } QT_END_NAMESPACE #include "qnativesocketengine_winrt.moc"