diff options
author | sr | 2013-02-04 19:50:31 +0100 |
---|---|---|
committer | sr | 2013-02-04 19:50:31 +0100 |
commit | 1a5709501f94014d41987b956338bb6424b9f90c (patch) | |
tree | d3b93fe8dc406bca56aff147ef5cc4cbf9ed6be0 /src/server/net/client.cpp | |
parent | Test (diff) | |
download | pvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.gz pvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.xz pvs2-1a5709501f94014d41987b956338bb6424b9f90c.zip |
Initial commit
Diffstat (limited to 'src/server/net/client.cpp')
-rw-r--r-- | src/server/net/client.cpp | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/src/server/net/client.cpp b/src/server/net/client.cpp new file mode 100644 index 0000000..ca91e76 --- /dev/null +++ b/src/server/net/client.cpp @@ -0,0 +1,288 @@ +/* + * client.cpp + * + * Created on: 18.01.2013 + * Author: sr + */ + +#include "client.h" +#include "../util/global.h" +#include "../../shared/settings.h" +#include "../../shared/util.h" +#include <QSslSocket> +#include <QHostAddress> +#include <QPixmap> +#include <cassert> + +#define CHALLENGE_LEN 20 + +ClientId Client::_clientIdCounter = 0; + +Client::Client(QSslSocket* socket) : + _socket(socket), _authed(0), _desiredProjectionSource(0), _isProjectionSource(false), + _currentProjectionSource(0), _vncPort(0), _activeVncClient(false) +{ + assert(socket != NULL); + _id = ++_clientIdCounter; + _pingTimeout = QDateTime::currentMSecsSinceEpoch() + PING_TIMEOUT_MS; + _ip = _socket->peerAddress().toString(); + qDebug("*** Client %s created.", qPrintable(_ip)); + // Connect important signals + connect(_socket, SIGNAL(disconnected()), this, SLOT(onClosed())); + connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError))); + connect(_socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(onSslErrors(const QList<QSslError> &))); + connect(_socket, SIGNAL(readyRead()), this, SLOT(onDataArrival())); + // Send challenge + _challenge.resize(CHALLENGE_LEN); + for (int i = 0; i < CHALLENGE_LEN; ++i) + _challenge[i] = qrand() & 0xff; + _toClient.reset(); + _toClient.setField(_ID, _CHALLENGE); + _toClient.setField(_CHALLENGE, _challenge); + _toClient.writeMessage(_socket); + // give client 3 seconds to complete handshake + _timerIdAuthTimeout = startTimer(3000); +} + +Client::~Client() +{ + if (_socket != NULL) + { + qDebug("**** DELETE IN DESTRUCTOR"); + _socket->deleteLater(); + } + qDebug("*** Client %s destroyed.", qPrintable(_ip)); +} + +void Client::timerEvent(QTimerEvent* event) +{ + if (event->timerId() == _timerIdAuthTimeout) + { + // Client did not send login request within 3 seconds + killTimer(_timerIdAuthTimeout); + _timerIdAuthTimeout = 0; + this->disconnect(); + } +} + +void Client::sendMessage(NetworkMessage& message) +{ + if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState) + return; + message.writeMessage(_socket); + if (!message.writeComplete()) + { + qCritical() << "SendMessage to client " << _name << "@" << _ip << " failed!"; + } +} + +void Client::requestThumb(const int width, const int height) +{ + if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState) + { + qDebug("requestThumb called in bad state"); + return; + } + _toClient.reset(); + _toClient.setField(_ID, _THUMB); + _toClient.setField(_X, QString::number(width)); + _toClient.setField(_Y, QString::number(height)); + _toClient.writeMessage(_socket); +} + +void Client::onDataArrival() +{ + _pingTimeout = QDateTime::currentMSecsSinceEpoch() + PING_TIMEOUT_MS; + // + if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState) + { + qDebug("dataArrival called in bad state"); + return; + } + + //qDebug() << _socket->bytesAvailable() << " bytes to read"; + bool ret; + while (_socket->bytesAvailable()) + { + ret = _fromClient.readMessage(_socket); // let the message read data from socket + if (!ret) // error parsing msg, disconnect client! + { + this->disconnect(); + return; + } + if (_fromClient.readComplete()) // message is complete + { + this->handleMsg(); + if (_socket == NULL) + return; + _fromClient.reset(); + } + } +} + +void Client::onSslErrors(const QList<QSslError> & errors) +{ + this->disconnect(); +} + +void Client::onClosed() +{ + this->disconnect(); +} + +void Client::onError(QAbstractSocket::SocketError errcode) +{ + this->disconnect(); +} + +void Client::handleMsg() +{ + const QString &id = _fromClient.getFieldString(_ID); + if (id.isEmpty()) + { + qDebug("Received message with empty ID field. ignored."); + return; + } + qDebug() << "Received message " << id; + + if (_authed == 2) + { + // Following messages are only valid of the client is already authenticated + if (id == _THUMB) + { + QPixmap pixmap; + if (!pixmap.loadFromData(_fromClient.getFieldBytes("IMG"))) + { + qDebug("Could not decode thumbnail image from client."); + return; + } + emit thumbUpdated(this, pixmap); + } + else if (id == _VNCSERVER) + { + // Client tells about startup of vnc server + const int port = _fromClient.getFieldString("PORT").toInt(); + if (port <= 0) + { + if (_vncPort <= 0) + { + qDebug() << "Starting VNC server on client" << _name << " (" << _ip << ") failed."; + // TODO: Show message on manager + } + } + else + { + _vncRoPass = _fromClient.getFieldString("ROPASS"); + _vncRwPass = _fromClient.getFieldString("RWPASS"); + } + _vncPort = port; + emit vncServerStateChange(this); + } + else if (id == _VNCCLIENT) + { + // Client tells us that it started or stopped displaying a remote screen via VNC + _activeVncClient = (_fromClient.getFieldString("ENABLED").toInt() != 0); + const ClientId other = (ClientId)_fromClient.getFieldString("CLIENTID").toInt(); + + if (!_activeVncClient && other == 0) + _desiredProjectionSource = 0; + + emit vncClientStateChange(this); + + if (!_activeVncClient) + _currentProjectionSource = 0; + else + _currentProjectionSource = other; + } + return; + } + + if (_authed == 1) // Not authed yet, only care about login requests + { + if (id == _LOGIN) + { + killTimer(_timerIdAuthTimeout); + _timerIdAuthTimeout = 0; + ClientLogin request; + request.accept = true; + request.host = _fromClient.getFieldString("HOST"); + request.name = _fromClient.getFieldString("NAME"); + request.ip = _socket->peerAddress().toString(); + qDebug() << "Login request by " << request.name; + // emit event, see if request is accepted + emit authenticating(this, &request); + if (!request.accept) + { + qDebug("Request denied."); + this->disconnect(); // Nope + return; + } + // Accepted + _authed = 2; + qDebug("valid, step <- 2"); + _name = request.name; + _host = request.host; + _toClient.reset(); + _toClient.setField(_ID, _LOGIN); + _toClient.writeMessage(_socket); + emit authenticated(this); + } + return; + } + + if (_authed == 0) + { + // Waiting for challenge reply by client + if (id == _CHALLENGE) + { + QByteArray hash(_fromClient.getFieldBytes(_HASH)); + QByteArray challenge(_fromClient.getFieldBytes(_CHALLENGE)); + if (genSha1(&Global::sessionNameArray(), &_challenge) != hash) + { // Challenge reply is invalid, drop client + _toClient.buildErrorMessage("Challenge reply invalid."); + _toClient.writeMessage(_socket); + this->deleteLater(); + return; + } + // Now answer to challenge by client + _toClient.reset(); + _toClient.setField(_ID, _CHALLENGE); + _toClient.setField(_HASH, genSha1(&Global::sessionNameArray(), &challenge)); + _toClient.writeMessage(_socket); + _authed = 1; + qDebug("client's challenge reply was valid, step <- 1"); + } + return; + } + +} + +void Client::startVncServer() +{ + _vncPort = 0; + _toClient.reset(); + _toClient.setField(_ID, _VNCSERVER); + _toClient.setField("ENABLE", QByteArray("1")); + sendMessage(_toClient); +} + +void Client::stopVncServer() +{ + _toClient.reset(); + _toClient.setField(_ID, _VNCSERVER); + _toClient.setField("ENABLE", QByteArray("0")); + sendMessage(_toClient); +} + +void Client::disconnect() +{ + if (_socket != NULL) + { + qDebug("*** Client %s disconnected.", qPrintable(_ip)); + _socket->blockSignals(true); + _socket->abort(); + _socket->deleteLater(); + _socket = NULL; + this->deleteLater(); + } +} |