From 1a5709501f94014d41987b956338bb6424b9f90c Mon Sep 17 00:00:00 2001 From: sr Date: Mon, 4 Feb 2013 19:50:31 +0100 Subject: Initial commit --- src/client/net/serverconnection.cpp | 348 ++++++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 src/client/net/serverconnection.cpp (limited to 'src/client/net/serverconnection.cpp') diff --git a/src/client/net/serverconnection.cpp b/src/client/net/serverconnection.cpp new file mode 100644 index 0000000..849f43b --- /dev/null +++ b/src/client/net/serverconnection.cpp @@ -0,0 +1,348 @@ +#include "serverconnection.h" +#include +#include +#include +#include +#include +#include +#include +//#define verbose +#include "../vnc/vncserver.h" + +#include "../../shared/util.h" +#include "../util/platform/blankscreen.h" + +#define CHALLENGE_LEN 20 + +ServerConnection::ServerConnection(const QString& host, const quint16 port, const QByteArray& sessionName, const QByteArray& certHash) : + QObject(NULL), _jpegQuality(80), _authed(0), _sessionName(sessionName), _certHash(certHash) +{ + _socket = new QSslSocket(); + _blank = new BlankScreen(); + connect(_socket, SIGNAL(encrypted()), this, SLOT(sock_connected())); + connect(_socket, SIGNAL(readyRead()), this, SLOT(sock_dataArrival())); + connect(_socket, SIGNAL(disconnected()), this, SLOT(sock_closed())); + connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sock_error(QAbstractSocket::SocketError))); + connect(_socket, + SIGNAL(sslErrors(const QList &)), + this, + SLOT(sslErrors(const QList &)) + ); + qDebug("Connecting to %s on port %d", host.toUtf8().data(), (int)port); + _socket->connectToHostEncrypted(host, port); + _timerId = startTimer(4000); + // Connect the vnc start/stop signal to this class, so we can tell the server about successful vnc server startup + connect(VncServer::instance(), SIGNAL(started(int, QString&, QString&)), this, SLOT(onVncServerStartStop(int, QString&, QString&))); +} + +ServerConnection::~ServerConnection() +{ + this->disconnectFromServer(); + _blank->deleteLater(); + _blank = NULL; +} + +void ServerConnection::sendMessage(NetworkMessage& message) +{ + if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState) + return; + message.writeMessage(_socket); + if (!message.writeComplete()) + { + qCritical() << "SendMessage to server failed!"; + } +} + + +void ServerConnection::disconnectFromServer() +{ + if (_socket == NULL) + return; + qDebug("Closing connection to server"); + VncServer::instance()->stop(); + emit closeVnc(); + _socket->blockSignals(true); + _socket->abort(); + _socket->deleteLater(); + _socket = NULL; + this->deleteLater(); +} + +void ServerConnection::handleMsg() +{ + const QString &id = _fromServer.getFieldString(_ID); + + if (_authed == 0) + { + if (id == _CHALLENGE) + { + // Initial challenge request by server + emit stateChange(ConnectWindow::AwaitingChallengeResponse); + _myChallenge.resize(CHALLENGE_LEN); + for (int i = 0; i < CHALLENGE_LEN; ++i) + _myChallenge[i] = qrand() & 0xff; + QByteArray serverChallenge(_fromServer.getFieldBytes(_CHALLENGE)); + _toServer.reset(); + _toServer.setField(_ID, _CHALLENGE); + _toServer.setField(_HASH, genSha1(&_sessionName, &serverChallenge)); + _toServer.setField(_CHALLENGE, _myChallenge); + _toServer.writeMessage(_socket); + qDebug("Received challenge, replying, sending own challenge step <- 1"); + _authed = 1; + } + return; + } + + if (_authed == 1) + { + if (id == _CHALLENGE) + { + qDebug("Received challenge reply"); + if (_timerId != 0) + { + killTimer(_timerId); + _timerId = 0; + } + // Check challenge response + QByteArray serverhash(_fromServer.getFieldBytes(_HASH)); + if (serverhash != genSha1(&_sessionName, &_myChallenge)) + { + qDebug("invalid. STOP."); + emit stateChange(ConnectWindow::InvalidSslHash); + this->disconnectFromServer(); + return; + } + emit stateChange(ConnectWindow::LoggingIn); + char *user = getlogin(); + if (user == NULL || *user == '\0') + user = getenv("USER"); + if (user == NULL || *user == '\0') + user = getenv("USERNAME"); + if (user == NULL || *user == '\0') + user = (char*)"Hans Affe"; + _toServer.reset(); + _toServer.setField(_ID, _LOGIN); + _toServer.setField("HOST", QHostInfo::localHostName()); + _toServer.setField("NAME", QString(user)); + qDebug("Sending login request!"); + if (_toServer.writeMessage(_socket)) + { + _authed = 2; + qDebug("valid, step <- 2"); + } + else + { + this->disconnectFromServer(); + } + } + return; + } + + if (_authed == 2) + { + if (id == _LOGIN) + { + qDebug("login accepted, step <- 3"); + _authed = 3; + emit stateChange(ConnectWindow::Connected); + } + return; + } + + // message THUMB - server requests screenshot as thumbnail + if (id == _THUMB) + { + int x = _fromServer.getFieldString(_X).toInt(); + int y = _fromServer.getFieldString(_Y).toInt(); + if (x < 32) + x = 32; + else if (x > 400) + x = 400; + if (y < 18) + y = 18; + else if (y > 300) + y = 300; + QPixmap desktop(QPixmap::grabWindow(QApplication::desktop()->winId()).scaled( + x, y, + Qt::KeepAspectRatio, + Qt::SmoothTransformation)); + QByteArray bytes; + QBuffer jpgBuffer(&bytes); + jpgBuffer.open(QIODevice::WriteOnly); + qDebug("JPEG quality is %d", _jpegQuality); + if (desktop.save(&jpgBuffer, "JPG", _jpegQuality)) // writes pixmap into bytes in JPG format + { + // Try to adjust quality so we stay between 3 and 4.5 KB + if (_jpegQuality < 90 && bytes.size() < 3000) + _jpegQuality += 7; + else if (_jpegQuality > 40 && bytes.size() > 4500) + _jpegQuality -= 7; + } + else + { // FALLBACK + bytes.clear(); + QBuffer pngBuffer(&bytes); + pngBuffer.open(QIODevice::WriteOnly); + if (!desktop.save(&pngBuffer, "PNG")) // writes pixmap into bytes in PNG format + { + qDebug("Could not convert screenshot to PNG nor JPG"); + return; // Failed :-( + } + } + _toServer.reset(); + _toServer.setField(_ID, _THUMB); + _toServer.setField(_IMG, bytes); + sendMessage(_toServer); + } // message VNCSERVER - start local vncserver + else if (id == _VNCSERVER) + { + const bool enable = (_fromServer.getFieldString("ENABLE").toInt() != 0); + if (enable) + { + emit closeVnc(); // In case we are watching some other client, stop doing so + VncServer::instance()->start(); + } + else + { + VncServer::instance()->stop(); + } + } + else if (id == _VNCCLIENT) + { + const QString host(_fromServer.getFieldString("HOST")); + const int port = _fromServer.getFieldString("PORT").toInt(); + if (host.isEmpty() || port <= 0) + { + emit closeVnc(); + } + else + { + emit openVnc(host, port, _fromServer.getFieldString("ROPASS"), true, true, _fromServer.getFieldString("CAPTION"), _fromServer.getFieldString("CLIENTID").toInt()); + } + } + else if (id == _LOCK) + { + const bool enable = (_fromServer.getFieldString("ENABLE").toInt() != 0); + if (enable) + _blank->lock(_fromServer.getFieldString("MESSAGE")); + else + _blank->unlock(); + } +} + +/** + * Override + */ + +void ServerConnection::timerEvent(QTimerEvent *event) +{ + if (_timerId == 0) + return; + killTimer(_timerId); + _timerId = 0; + this->disconnectFromServer(); +} + +/** + * Slots + */ + +void ServerConnection::onVncServerStartStop(int port, QString& ropass, QString& rwpass) +{ + _toServer.reset(); + _toServer.setField(_ID, _VNCSERVER); + if (port <= 0) + { + _toServer.setField("PORT", QByteArray("0")); + } + else + { + _toServer.setField("PORT", QString::number(port)); + _toServer.setField("ROPASS", ropass); + _toServer.setField("RWPASS", rwpass); + } + sendMessage(_toServer); +} + +void ServerConnection::onVncViewerStartStop(const bool started, const int clientId) +{ + _toServer.reset(); + _toServer.setField(_ID, _VNCCLIENT); + if (started) + _toServer.setField("ENABLED", QByteArray("1")); + else + _toServer.setField("ENABLED", QByteArray("0")); + _toServer.setField("CLIENTID", QString::number(clientId)); + sendMessage(_toServer); +} + +void ServerConnection::sslErrors(const QList & errors) +{ + for (QList::const_iterator it = errors.begin(); it != errors.end(); it++) + { + QSslError err = *it; + qDebug("Connect SSL: %s", qPrintable(err.errorString())); + if (err.error() == QSslError::HostNameMismatch) + continue; // We don't pay attention to hostnames for validation + if (err.error() == QSslError::SelfSignedCertificate) + continue; // Also, this will always be the case; we check the fingerprint later + if (err.error() == QSslError::CertificateNotYetValid || err.error() == QSslError::CertificateExpired) + continue; + // Some other SSL error - do not ignore + return; + } + _socket->ignoreSslErrors(); +} + +void ServerConnection::sock_dataArrival() +{ + if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState) + { + qDebug("dataArrival called in bad state"); + return; + } + + while (_socket->bytesAvailable()) + { + bool retval; + retval = _fromServer.readMessage(_socket); // let the message read data from socket + if (!retval) // error parsing msg, disconnect client! + { + this->disconnectFromServer(); + return; + } + if (_fromServer.readComplete()) // message is complete + { + this->handleMsg(); + if (_socket == NULL) + return; + _fromServer.reset(); + } + } +} + +void ServerConnection::sock_closed() +{ + // should this be unreliable in some way i suggest using the signal "stateChanged()" instead + // and check if the state changed to unconnected. + qDebug("Socket was closed... oh well.."); + this->disconnectFromServer(); +} + +void ServerConnection::sock_error(QAbstractSocket::SocketError errcode) +{ + qDebug("Connection error: %d", (int)errcode); + this->disconnectFromServer(); +} + +void ServerConnection::sock_connected() +{ + QByteArray cert(_socket->peerCertificate().digest(QCryptographicHash::Sha1)); + if (_certHash != cert) + { + emit stateChange(ConnectWindow::InvalidCert); + this->disconnectFromServer(); + return; + } + emit stateChange(ConnectWindow::AwaitingChallenge); +} -- cgit v1.2.3-55-g7522