X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fwebsockets%2Fqwebsocket_p.cpp;h=bff060f4ff689518d9dabf1ecc2b1e7740b1a6d8;hb=45eadd663ff22df46fadf03c45db7e058e71e476;hp=646e24f67d745c0bfe9e767fa803a9fa91ca08c6;hpb=6295e6bd35e49c9d5c91922ac564a2d1c81687c6;p=contrib%2Fqtwebsockets.git diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp index 646e24f..bff060f 100644 --- a/src/websockets/qwebsocket_p.cpp +++ b/src/websockets/qwebsocket_p.cpp @@ -44,6 +44,7 @@ #include "qwebsocketprotocol_p.h" #include "qwebsockethandshakerequest_p.h" #include "qwebsockethandshakeresponse_p.h" +#include "qdefaultmaskgenerator_p.h" #include #include @@ -87,11 +88,11 @@ QWebSocketConfiguration::QWebSocketConfiguration() : \internal */ QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::Version version, - QWebSocket *pWebSocket, QObject *parent) : - QObject(parent), + QWebSocket *pWebSocket) : + QObjectPrivate(), q_ptr(pWebSocket), m_pSocket(), - m_errorString(), + m_errorString(QWebSocket::tr("Unknown error")), m_version(version), m_resourceName(), m_requestUrl(), @@ -109,17 +110,18 @@ QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol:: m_closeReason(), m_pingTimer(), m_dataProcessor(), - m_configuration() + m_configuration(), + m_pMaskGenerator(&m_defaultMaskGenerator), + m_defaultMaskGenerator() { - init(); } /*! \internal */ QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version, - QWebSocket *pWebSocket, QObject *parent) : - QObject(parent), + QWebSocket *pWebSocket) : + QObjectPrivate(), q_ptr(pWebSocket), m_pSocket(pTcpSocket), m_errorString(pTcpSocket->errorString()), @@ -140,10 +142,10 @@ QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol: m_closeReason(), m_pingTimer(), m_dataProcessor(), - m_configuration() + m_configuration(), + m_pMaskGenerator(&m_defaultMaskGenerator), + m_defaultMaskGenerator() { - init(); - makeConnections(m_pSocket.data()); } /*! @@ -152,8 +154,13 @@ QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol: void QWebSocketPrivate::init() { Q_ASSERT(q_ptr); - //TODO: need a better randomizer - qsrand(static_cast(QDateTime::currentMSecsSinceEpoch())); + Q_ASSERT(m_pMaskGenerator); + + m_pMaskGenerator->seed(); + + if (m_pSocket) { + makeConnections(m_pSocket.data()); + } } /*! @@ -164,7 +171,7 @@ QWebSocketPrivate::~QWebSocketPrivate() if (!m_pSocket) return; if (state() == QAbstractSocket::ConnectedState) - close(QWebSocketProtocol::CloseCodeGoingAway, tr("Connection closed")); + close(QWebSocketProtocol::CloseCodeGoingAway, QWebSocket::tr("Connection closed")); releaseConnections(m_pSocket.data()); } @@ -182,7 +189,7 @@ void QWebSocketPrivate::abort() */ QAbstractSocket::SocketError QWebSocketPrivate::error() const { - QAbstractSocket::SocketError err = QAbstractSocket::OperationError; + QAbstractSocket::SocketError err = QAbstractSocket::UnknownSocketError; if (Q_LIKELY(m_pSocket)) err = m_pSocket->error(); return err; @@ -329,8 +336,14 @@ void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString r void QWebSocketPrivate::open(const QUrl &url, bool mask) { //just delete the old socket for the moment; - //later, we can add more 'intelligent' handling by looking at the url + //later, we can add more 'intelligent' handling by looking at the URL //m_pSocket.reset(); + Q_Q(QWebSocket); + if (!url.isValid() || url.toString().contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("Invalid URL.")); + Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); + return; + } QTcpSocket *pTcpSocket = m_pSocket.take(); if (pTcpSocket) { releaseConnections(pTcpSocket); @@ -338,14 +351,18 @@ void QWebSocketPrivate::open(const QUrl &url, bool mask) } //if (m_url != url) if (Q_LIKELY(!m_pSocket)) { - Q_Q(QWebSocket); - m_dataProcessor.clear(); m_isClosingHandshakeReceived = false; m_isClosingHandshakeSent = false; setRequestUrl(url); QString resourceName = url.path(); + if (resourceName.contains(QStringLiteral("\r\n"))) { + setRequestUrl(QUrl()); //clear requestUrl + setErrorString(QWebSocket::tr("Invalid resource name.")); + Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); + return; + } if (!url.query().isEmpty()) { if (!resourceName.endsWith(QChar::fromLatin1('?'))) { resourceName.append(QChar::fromLatin1('?')); @@ -360,11 +377,12 @@ void QWebSocketPrivate::open(const QUrl &url, bool mask) #ifndef QT_NO_SSL if (url.scheme() == QStringLiteral("wss")) { if (!QSslSocket::supportsSsl()) { - const QString message = tr("SSL Sockets are not supported on this platform."); + const QString message = + QWebSocket::tr("SSL Sockets are not supported on this platform."); setErrorString(message); Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError); } else { - QSslSocket *sslSocket = new QSslSocket(this); + QSslSocket *sslSocket = new QSslSocket; m_pSocket.reset(sslSocket); if (Q_LIKELY(m_pSocket)) { m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1); @@ -373,8 +391,12 @@ void QWebSocketPrivate::open(const QUrl &url, bool mask) m_pSocket->setPauseMode(m_pauseMode); makeConnections(m_pSocket.data()); - connect(sslSocket, &QSslSocket::encryptedBytesWritten, q, - &QWebSocket::bytesWritten); + QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten, q, + &QWebSocket::bytesWritten); + typedef void (QSslSocket:: *sslErrorSignalType)(const QList &); + QObject::connect(sslSocket, + static_cast(&QSslSocket::sslErrors), + q, &QWebSocket::sslErrors); setSocketState(QAbstractSocket::ConnectingState); sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration); @@ -387,7 +409,7 @@ void QWebSocketPrivate::open(const QUrl &url, bool mask) #endif sslSocket->connectToHostEncrypted(url.host(), url.port(443)); } else { - const QString message = tr("Out of memory."); + const QString message = QWebSocket::tr("Out of memory."); setErrorString(message); Q_EMIT q->error(QAbstractSocket::SocketResourceError); } @@ -395,7 +417,7 @@ void QWebSocketPrivate::open(const QUrl &url, bool mask) } else #endif if (url.scheme() == QStringLiteral("ws")) { - m_pSocket.reset(new QTcpSocket(this)); + m_pSocket.reset(new QTcpSocket); if (Q_LIKELY(m_pSocket)) { m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1); m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1); @@ -403,20 +425,21 @@ void QWebSocketPrivate::open(const QUrl &url, bool mask) m_pSocket->setPauseMode(m_pauseMode); makeConnections(m_pSocket.data()); - connect(m_pSocket.data(), &QAbstractSocket::bytesWritten, q, - &QWebSocket::bytesWritten); + QObject::connect(m_pSocket.data(), &QAbstractSocket::bytesWritten, q, + &QWebSocket::bytesWritten); setSocketState(QAbstractSocket::ConnectingState); #ifndef QT_NO_NETWORKPROXY m_pSocket->setProxy(m_configuration.m_proxy); #endif m_pSocket->connectToHost(url.host(), url.port(80)); } else { - const QString message = tr("Out of memory."); + const QString message = QWebSocket::tr("Out of memory."); setErrorString(message); Q_EMIT q->error(QAbstractSocket::SocketResourceError); } } else { - const QString message = tr("Unsupported websockets scheme: %1").arg(url.scheme()); + const QString message = + QWebSocket::tr("Unsupported websockets scheme: %1").arg(url.scheme()); setErrorString(message); Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError); } @@ -515,40 +538,40 @@ void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket) //pass through signals typedef void (QAbstractSocket:: *ASErrorSignal)(QAbstractSocket::SocketError); typedef void (QWebSocket:: *WSErrorSignal)(QAbstractSocket::SocketError); - connect(pTcpSocket, - static_cast(&QAbstractSocket::error), - q, static_cast(&QWebSocket::error)); - connect(pTcpSocket, &QAbstractSocket::proxyAuthenticationRequired, q, - &QWebSocket::proxyAuthenticationRequired); - connect(pTcpSocket, &QAbstractSocket::readChannelFinished, q, - &QWebSocket::readChannelFinished); - connect(pTcpSocket, &QAbstractSocket::aboutToClose, q, &QWebSocket::aboutToClose); + QObject::connect(pTcpSocket, + static_cast(&QAbstractSocket::error), + q, static_cast(&QWebSocket::error)); + QObject::connect(pTcpSocket, &QAbstractSocket::proxyAuthenticationRequired, q, + &QWebSocket::proxyAuthenticationRequired); + QObject::connect(pTcpSocket, &QAbstractSocket::readChannelFinished, q, + &QWebSocket::readChannelFinished); + QObject::connect(pTcpSocket, &QAbstractSocket::aboutToClose, q, &QWebSocket::aboutToClose); //catch signals - connect(pTcpSocket, &QAbstractSocket::stateChanged, this, - &QWebSocketPrivate::processStateChanged); + QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::stateChanged, this, + &QWebSocketPrivate::processStateChanged); //!!!important to use a QueuedConnection here; //with QTcpSocket there is no problem, but with QSslSocket the processing hangs - connect(pTcpSocket, &QAbstractSocket::readyRead, this, - &QWebSocketPrivate::processData, Qt::QueuedConnection); + QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::readyRead, this, + &QWebSocketPrivate::processData, Qt::QueuedConnection); } - connect(&m_dataProcessor, &QWebSocketDataProcessor::textFrameReceived, q, - &QWebSocket::textFrameReceived); - connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryFrameReceived, q, - &QWebSocket::binaryFrameReceived); - connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryMessageReceived, q, - &QWebSocket::binaryMessageReceived); - connect(&m_dataProcessor, &QWebSocketDataProcessor::textMessageReceived, q, - &QWebSocket::textMessageReceived); - connect(&m_dataProcessor, &QWebSocketDataProcessor::errorEncountered, this, - &QWebSocketPrivate::close); - connect(&m_dataProcessor, &QWebSocketDataProcessor::pingReceived, this, - &QWebSocketPrivate::processPing); - connect(&m_dataProcessor, &QWebSocketDataProcessor::pongReceived, this, - &QWebSocketPrivate::processPong); - connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this, - &QWebSocketPrivate::processClose); + QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textFrameReceived, q, + &QWebSocket::textFrameReceived); + QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryFrameReceived, q, + &QWebSocket::binaryFrameReceived); + QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryMessageReceived, q, + &QWebSocket::binaryMessageReceived); + QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textMessageReceived, q, + &QWebSocket::textMessageReceived); + QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::errorEncountered, this, + &QWebSocketPrivate::close); + QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pingReceived, this, + &QWebSocketPrivate::processPing); + QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pongReceived, this, + &QWebSocketPrivate::processPong); + QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this, + &QWebSocketPrivate::processClose); } /*! @@ -557,8 +580,8 @@ void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket) void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket) { if (Q_LIKELY(pTcpSocket)) - disconnect(pTcpSocket); - disconnect(&m_dataProcessor); + pTcpSocket->disconnect(pTcpSocket); + m_dataProcessor.disconnect(); } /*! @@ -678,7 +701,7 @@ QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode, qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary) { qint64 payloadWritten = 0; - if (Q_UNLIKELY(!m_pSocket)) + if (Q_UNLIKELY(!m_pSocket) || (state() != QAbstractSocket::ConnectedState)) return payloadWritten; Q_Q(QWebSocket); @@ -727,7 +750,7 @@ qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary) payloadWritten += written; } else { m_pSocket->flush(); - setErrorString(tr("Error writing bytes to socket: %1.") + setErrorString(QWebSocket::tr("Error writing bytes to socket: %1.") .arg(m_pSocket->errorString())); Q_EMIT q->error(QAbstractSocket::NetworkError); break; @@ -737,27 +760,19 @@ qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary) bytesLeft -= size; } if (Q_UNLIKELY(payloadWritten != data.size())) { - setErrorString(tr("Bytes written %1 != %2.").arg(payloadWritten).arg(data.size())); + setErrorString(QWebSocket::tr("Bytes written %1 != %2.") + .arg(payloadWritten).arg(data.size())); Q_EMIT q->error(QAbstractSocket::NetworkError); } return payloadWritten; } /*! - * \internal - */ -quint32 QWebSocketPrivate::generateRandomNumber() const -{ - //TODO: need a better randomizer - return quint32((double(qrand()) / RAND_MAX) * std::numeric_limits::max()); -} - -/*! \internal */ quint32 QWebSocketPrivate::generateMaskingKey() const { - return generateRandomNumber(); + return m_pMaskGenerator->nextMask(); } /*! @@ -768,7 +783,7 @@ QByteArray QWebSocketPrivate::generateKey() const QByteArray key; for (int i = 0; i < 4; ++i) { - const quint32 tmp = generateRandomNumber(); + const quint32 tmp = m_pMaskGenerator->nextMask(); key.append(static_cast(static_cast(&tmp)), sizeof(quint32)); } @@ -861,14 +876,16 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) } } if (Q_UNLIKELY(!ok)) { - errorDescription = tr("Invalid statusline in response: %1.").arg(statusLine); + errorDescription = QWebSocket::tr("Invalid statusline in response: %1.").arg(statusLine); } else { QString headerLine = readLine(pSocket); QMap headers; while (!headerLine.isEmpty()) { const QStringList headerField = headerLine.split(QStringLiteral(": "), QString::SkipEmptyParts); - headers.insertMulti(headerField[0], headerField[1]); + if (headerField.size() == 2) { + headers.insertMulti(headerField[0], headerField[1]); + } headerLine = readLine(pSocket); } @@ -898,11 +915,11 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) ok = (accept == acceptKey); if (!ok) errorDescription = - tr("Accept-Key received from server %1 does not match the client key %2.") + QWebSocket::tr("Accept-Key received from server %1 does not match the client key %2.") .arg(acceptKey).arg(accept); } else { errorDescription = - tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.") + QWebSocket::tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.") .arg(statusLine); } } else if (httpStatusCode == 400) { @@ -914,21 +931,20 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) //if needed to switch protocol version, then we are finished here //because we cannot handle other protocols than the RFC one (v13) errorDescription = - tr("Handshake: Server requests a version that we don't support: %1.") + QWebSocket::tr("Handshake: Server requests a version that we don't support: %1.") .arg(versions.join(QStringLiteral(", "))); ok = false; } else { //we tried v13, but something different went wrong errorDescription = - tr("QWebSocketPrivate::processHandshake: Unknown error condition " \ - "encountered. Aborting connection."); + QWebSocket::tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection."); ok = false; } } } else { errorDescription = - tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).") - .arg(httpStatusCode).arg(httpStatusMessage); + QWebSocket::tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).") + .arg(httpStatusCode).arg(httpStatusMessage); ok = false; } @@ -964,6 +980,11 @@ void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketS QString(), QString(), m_key); + if (handshake.isEmpty()) { + m_pSocket->abort(); + Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); + return; + } m_pSocket->write(handshake.toLatin1()); } break; @@ -1053,6 +1074,31 @@ QString QWebSocketPrivate::createHandShakeRequest(QString resourceName, QByteArray key) { QStringList handshakeRequest; + if (resourceName.contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("The resource name contains newlines. " \ + "Possible attack detected.")); + return QString(); + } + if (host.contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("The hostname contains newlines. " \ + "Possible attack detected.")); + return QString(); + } + if (origin.contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("The origin contains newlines. " \ + "Possible attack detected.")); + return QString(); + } + if (extensions.contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("The extensions attribute contains newlines. " \ + "Possible attack detected.")); + return QString(); + } + if (protocols.contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("The protocols attribute contains newlines. " \ + "Possible attack detected.")); + return QString(); + } handshakeRequest << QStringLiteral("GET ") % resourceName % QStringLiteral(" HTTP/1.1") << QStringLiteral("Host: ") % host << @@ -1186,6 +1232,26 @@ void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy) /*! \internal */ +void QWebSocketPrivate::setMaskGenerator(const QMaskGenerator *maskGenerator) +{ + if (!maskGenerator) + m_pMaskGenerator = &m_defaultMaskGenerator; + else if (maskGenerator != m_pMaskGenerator) + m_pMaskGenerator = const_cast(maskGenerator); +} + +/*! + \internal + */ +const QMaskGenerator *QWebSocketPrivate::maskGenerator() const +{ + Q_ASSERT(m_pMaskGenerator); + return m_pMaskGenerator; +} + +/*! + \internal + */ qint64 QWebSocketPrivate::readBufferSize() const { return m_readBufferSize; @@ -1225,7 +1291,8 @@ void QWebSocketPrivate::setReadBufferSize(qint64 size) */ bool QWebSocketPrivate::isValid() const { - return (m_pSocket && m_pSocket->isValid()); + return (m_pSocket && m_pSocket->isValid() && + (m_socketState == QAbstractSocket::ConnectedState)); } QT_END_NAMESPACE