diff options
author | Manuel Schneider | 2014-05-28 00:37:28 +0200 |
---|---|---|
committer | Manuel Schneider | 2014-05-28 00:37:28 +0200 |
commit | afc69ca4ffb0e11d9d06b8ddd31ee40963f86f17 (patch) | |
tree | f7284ba33e98d2bb6e099f248c7b127282efa99f /src/client | |
parent | Remove TODO. User get visual feedback via cam icon. (diff) | |
download | pvs2-afc69ca4ffb0e11d9d06b8ddd31ee40963f86f17.tar.gz pvs2-afc69ca4ffb0e11d9d06b8ddd31ee40963f86f17.tar.xz pvs2-afc69ca4ffb0e11d9d06b8ddd31ee40963f86f17.zip |
Outsource serverDiscovery.
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/connectwindow/connectwindow.cpp | 233 | ||||
-rw-r--r-- | src/client/connectwindow/connectwindow.h | 53 | ||||
-rw-r--r-- | src/client/net/serverdiscovery.cpp | 181 | ||||
-rw-r--r-- | src/client/net/serverdiscovery.h | 51 | ||||
-rw-r--r-- | src/client/toolbar/toolbar.cpp | 4 |
5 files changed, 324 insertions, 198 deletions
diff --git a/src/client/connectwindow/connectwindow.cpp b/src/client/connectwindow/connectwindow.cpp index c8f7efd..34087ad 100644 --- a/src/client/connectwindow/connectwindow.cpp +++ b/src/client/connectwindow/connectwindow.cpp @@ -16,16 +16,19 @@ #define UDPBUFSIZ 9000 #define SALT_LEN 18 -/** +/***************************************************************************//** * Initialize Connection Window. * @param parent */ -ConnectWindow::ConnectWindow(QWidget *parent) : - QWidget(parent), _ui(new Ui::ConnectWindow), _connected(false), - _timerDiscover(0), _timerHide(0), _connection(NULL), _state(Idle), - _hashErrorCount(0), _hashSslErrorCount(0), _certErrorCount(0), - _ipErrorCount(0), _discoveryInterval(800) -{ +ConnectWindow::ConnectWindow(QWidget *parent) : QWidget(parent) +{ + _ui = new Ui::ConnectWindow; + _connected = false; + _timerHide = 0; + _connection = NULL; + _state = Idle; + _hashSslErrorCount = 0; + // Initialize the GUI _ui->setupUi(this); @@ -39,53 +42,21 @@ ConnectWindow::ConnectWindow(QWidget *parent) : connect(_ui->btn_connection, SIGNAL(clicked()), this, SLOT(onBtnConnection())); connect(_ui->btn_hide, SIGNAL(clicked()), this, SLOT(onBtnHide())); - int tries = 10; - while (tries-- != 0) - { - const quint16 port = (quint16)(qrand() % 10000) + 10000; - if (_discoverySocket.bind(QHostAddress::Any, port)) - break; - if (tries == 0) - qFatal("Could not bind to any UDP port for server discovery."); - } - connect(&_discoverySocket, SIGNAL(readyRead()), this, SLOT(onUdpReadyRead())); - this->setState(Idle); -} - -ConnectWindow::~ConnectWindow(){} + // React on discovery signal + connect(&_serverDiscovery, SIGNAL(serverDetected(QString,quint16,QByteArray,QByteArray)), + this, SLOT(onServerDetected(QString,quint16,QByteArray,QByteArray))); -/** - * Set Client as Connected (true) or Disconnected (false). - * After settings updateState() is called. - * @param connected - */ -void ConnectWindow::setConnected(const bool connected) -{ - _connected = connected; this->updateState(); - if (_state == Scanning) - { - killTimer(_timerDiscover); - _discoveryInterval = 1000; - _timerDiscover = startTimer(_discoveryInterval); - } } -/** - * Set current state of Client. - * After setting state updateState() is called. - * @param state +/***************************************************************************//** + * @brief ConnectWindow::~ConnectWindow */ -void ConnectWindow::setState(const ConnectionState state) -{ - if (_state != state) - { - _state = state; - this->updateState(); - } -} +ConnectWindow::~ConnectWindow(){} + -/** + +/***************************************************************************//** * Handle changes in state and update window. * Also changing TextLabel which shows the user what the program is doing. */ @@ -115,7 +86,6 @@ void ConnectWindow::updateState() break; case Scanning: _ui->lblStatus->setText(tr("Scanning for session %1.").arg(_ui->lineEditName->text())); - _timerDiscover = startTimer(_discoveryInterval); break; case Connecting: _ui->lblStatus->setText(tr("Found session, connecting...")); @@ -132,12 +102,11 @@ void ConnectWindow::updateState() case Connected: _ui->lblStatus->setText(tr("Connection established!")); break; - case InvalidIpList: - case InvalidHash: case InvalidCert: + _ui->lblStatus->setText(tr("Invalid certificate.")); + break; case InvalidSslHash: - _ui->lblStatus->setText(tr("Invalid hash: %1; invalid cert: %2; invalid iplist: %3; invalid sslhash: %4") - .arg(_hashErrorCount).arg(_certErrorCount).arg(_ipErrorCount).arg(_hashSslErrorCount)); + _ui->lblStatus->setText(tr("Invalid Ssl hash: %1.").arg(_hashSslErrorCount)); break; } } @@ -146,57 +115,13 @@ void ConnectWindow::updateState() * Overrides */ -/** +/***************************************************************************//** * Called when a Qt timer fires; used for server discovery and * auto-hiding the connect dialog. */ void ConnectWindow::timerEvent(QTimerEvent* event) { - if (event->timerId() == _timerDiscover) - { - killTimer(_timerDiscover); - if (_connected || _state != Scanning) // Not scanning, bail out - return; - if (_discoveryInterval < 30000) - _discoveryInterval += 100; - _timerDiscover = startTimer(_discoveryInterval); - // Don't send packet if we're trying to connect - if (_connection != NULL) - return; - // Send discovery - _packet.reset(); - QByteArray iplist(Network::interfaceAddressesToString().toUtf8()); - QByteArray salt1(SALT_LEN, 0); - if (_salt2.size() < SALT_LEN) - _salt2.resize(SALT_LEN); - for (int i = 0; i < SALT_LEN; ++i) - { - salt1[i] = qrand() & 0xff; - _salt2[i] = qrand() & 0xff; - } - _packet.reset(); - _packet.setField(_HASH, genSha1(&_sessionNameBytes, &salt1, &iplist)); - _packet.setField(_SALT1, salt1); - _packet.setField(_SALT2, _salt2); - _packet.setField(_IPLIST, iplist); - foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces()) - { - foreach (QNetworkAddressEntry entry, interface.addressEntries()) - { - if (!entry.broadcast().isNull() && entry.ip() != QHostAddress::LocalHost && entry.ip() != QHostAddress::LocalHostIPv6) - { - qDebug() << "Broadcasting to " << entry.broadcast().toString(); - if (!_packet.writeMessage(&_discoverySocket, entry.broadcast(), SERVICE_DISCOVERY_PORT)) - qDebug("FAILED"); - } - } - } - qDebug("Broadcasting to 255.255.255.255"); - if (!_packet.writeMessage(&_discoverySocket, QHostAddress::Broadcast, SERVICE_DISCOVERY_PORT)) - qDebug("FAILED"); - // End send discovery - } - else if(event->timerId() == _timerHide) + if(event->timerId() == _timerHide) { killTimer(_timerHide); _timerHide = 0; @@ -204,11 +129,11 @@ void ConnectWindow::timerEvent(QTimerEvent* event) _ui->stackedWidget->setCurrentIndex(0); } else - // Unknown/Old timer id, kill it + // Unknown/Old timer id, kill it ??? PALM -> FACE killTimer(event->timerId()); } -/** +/***************************************************************************//** * Handle incoming closeEvent and hide window. * @param e */ @@ -218,7 +143,7 @@ void ConnectWindow::closeEvent(QCloseEvent *e) this->hide(); } -/** +/***************************************************************************//** * Gives the keyboard input focus to the input line. * @param event */ @@ -227,7 +152,7 @@ void ConnectWindow::showEvent(QShowEvent* event) _ui->lineEditName->setFocus(); } -/** +/***************************************************************************//** * Public function connect to session. * Check if currently connected to server, * if not --> connect to given sessionName. @@ -237,19 +162,16 @@ void ConnectWindow::connectToSession(const QByteArray sessionName) { if (_connected || _state != Idle) return; - - _discoveryInterval = 800; - _sessionNameBytes = sessionName; - _timerDiscover = startTimer(_discoveryInterval); - _hashErrorCount = _hashSslErrorCount = _certErrorCount = _ipErrorCount = 0; - this->setState(Scanning); + _state = Scanning; + this->updateState(); + _serverDiscovery.start(_ui->lineEditName->text().toUtf8()); } /* * Slots */ -/** +/***************************************************************************//** * Handle click on Connect/Disconnect button. * If already connected --> Stop/disconnect. * Else scanning for given sessionId. @@ -258,17 +180,19 @@ void ConnectWindow::onBtnConnection() { if (_timerHide){ killTimer(_timerHide); + _timerHide = 0; _ui->stackedWidget->setCurrentIndex(0); } - _timerHide = 0; - if (_timerDiscover) - killTimer(_timerDiscover); + + if (_serverDiscovery.isActive()) + _serverDiscovery.stop(); + if (_connected || _state != Idle) { // Stop or disconnect - _timerDiscover = 0; emit disconnect(); - this->setState(Idle); + _state = Idle; + this->updateState(); } else { @@ -277,7 +201,7 @@ void ConnectWindow::onBtnConnection() } } -/** +/***************************************************************************//** * Handle click on Cancel/Hide Button. * Just hide the window. */ @@ -286,53 +210,24 @@ void ConnectWindow::onBtnHide() this->hide(); } -/** - * Handle incoming service discovery packets. + +/***************************************************************************//** + * @brief ConnectWindow::onServerDetected + * @param host + * @param port + * @param sessionName + * @param certHash */ -void ConnectWindow::onUdpReadyRead() +void ConnectWindow::onServerDetected(const QString& host, const quint16 port, const QByteArray& sessionName, const QByteArray& certHash) { - char data[UDPBUFSIZ]; - QHostAddress addr; - quint16 port; - while (_discoverySocket.hasPendingDatagrams()) - { - const qint64 size = _discoverySocket.readDatagram(data, UDPBUFSIZ, &addr, &port); - if (size <= 0 || _connection != NULL) - continue; - - _packet.reset(); - if (!_packet.readMessage(data, (quint32)size)) - continue; - // Valid packet, process it: - const QByteArray hash(_packet.getFieldBytes(_HASH)); - const QByteArray iplist(_packet.getFieldBytes(_IPLIST)); - const QByteArray port(_packet.getFieldBytes(_PORT)); - const QByteArray cert(_packet.getFieldBytes(_CERT)); - // Check if the source IP of the packet matches any of the addresses given in the IP list - if (!Network::isAddressInList(QString::fromUtf8(iplist), addr.toString())) - { - ++_ipErrorCount; - this->setState(InvalidIpList); - this->setState(Scanning); - continue; - } - // If so, check if the submitted hash seems valid - if (genSha1(&_sessionNameBytes, &_salt2, &iplist, &port, &cert) != hash) - { - // did not match local session name, or other data was spoofed - ++_hashErrorCount; - this->setState(InvalidHash); - this->setState(Scanning); - continue; - } - // Otherwise it's a valid reply, try to connect - _connection = new ServerConnection(addr.toString(), (quint16)QString::fromUtf8(port).toInt(), _sessionNameBytes, cert); - connect(_connection, SIGNAL(stateChange(ConnectWindow::ConnectionState)), this, SLOT(onConnectionStateChange(ConnectWindow::ConnectionState))); - connect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onConnectionClosed(QObject*))); - } + _connection = new ServerConnection(host, port, sessionName, certHash); + connect(_connection, SIGNAL(stateChange(ConnectWindow::ConnectionState)), this, SLOT(onConnectionStateChange(ConnectWindow::ConnectionState))); + connect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onConnectionClosed(QObject*))); + connect(_connection, SIGNAL(disconnected()), this, SLOT(onConnectionDisconnected())); } -/** + +/***************************************************************************//** * Handle connection state changes and update member variables describing state. * @param state */ @@ -341,7 +236,10 @@ void ConnectWindow::onConnectionStateChange(ConnectWindow::ConnectionState state bool reset = (_state == Scanning); if (state == InvalidSslHash) ++_hashSslErrorCount; - this->setState(state); + + _state = state; + this->updateState(); + if (reset) _state = Scanning; if (state == Connected) @@ -349,12 +247,16 @@ void ConnectWindow::onConnectionStateChange(ConnectWindow::ConnectionState state QObject::disconnect(_connection, SIGNAL(stateChange(ConnectWindow::ConnectionState)), this, SLOT(onConnectionStateChange(ConnectWindow::ConnectionState))); QObject::disconnect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onConnectionClosed(QObject*))); emit connected(_connection); + + _connected = true; + this->updateState(); + _connection = NULL; _timerHide = startTimer(2000); } } -/** +/***************************************************************************//** * If connection is closed set _connection = NULL. * @param connection */ @@ -362,3 +264,12 @@ void ConnectWindow::onConnectionClosed(QObject* connection) { _connection = NULL; } + +/***************************************************************************//** + * @brief ConnectWindow::onConnectionDisconnected + */ +void ConnectWindow::onConnectionDisconnected() +{ + _connected = false; + this->updateState(); +} diff --git a/src/client/connectwindow/connectwindow.h b/src/client/connectwindow/connectwindow.h index 9319e45..39eac86 100644 --- a/src/client/connectwindow/connectwindow.h +++ b/src/client/connectwindow/connectwindow.h @@ -20,6 +20,7 @@ #include <QUdpSocket> #include <QSslSocket> #include "../../shared/networkmessage.h" +#include "../net/serverdiscovery.h" namespace Ui{ class ConnectWindow; @@ -38,54 +39,37 @@ class ConnectWindow : public QWidget Q_OBJECT public: - enum ConnectionState - { + enum ConnectionState { Idle, Scanning, Connecting, AwaitingChallenge, AwaitingChallengeResponse, LoggingIn, - InvalidIpList, - InvalidHash, // Hash of UDP reply invalid InvalidSslHash, // Hash of challenge inside SSL connection invalid InvalidCert, Connected }; -private: - Ui::ConnectWindow *_ui; - - bool _connected; - int _timerDiscover, _timerHide; - ServerConnection *_connection; - ConnectionState _state; - int _hashErrorCount, _hashSslErrorCount, _certErrorCount, _ipErrorCount; - int _discoveryInterval; - - QByteArray _sessionNameBytes; - QByteArray _salt2; - QUdpSocket _discoverySocket; - NetworkMessage _packet; - - void setState(const ConnectionState state); - void updateState(); - -public: explicit ConnectWindow(QWidget *parent = NULL); virtual ~ConnectWindow(); - void setConnected(const bool connected); void connectToSession(const QByteArray sessionName); +private: + Ui::ConnectWindow *_ui; + ServerConnection *_connection; + int _hashSslErrorCount; + ServerDiscovery _serverDiscovery; + ConnectionState _state; + QByteArray _sessionNameBytes; + NetworkMessage _packet; + bool _connected; + int _timerHide; + + void updateState(); + protected: - /* - void enterEvent(QEvent *e); - void leaveEvent(QEvent *e); - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - */ void timerEvent(QTimerEvent* event); void closeEvent(QCloseEvent *e); void showEvent(QShowEvent* event); @@ -93,15 +77,16 @@ protected: protected slots: void onBtnConnection(); void onBtnHide(); - void onUdpReadyRead(); + void onConnectionStateChange(ConnectWindow::ConnectionState state); void onConnectionClosed(QObject* connection); - //void onSsl + void onConnectionDisconnected(); + // void onUdpReadyRead(); + void onServerDetected(const QString& host, const quint16 port, const QByteArray& sessionName, const QByteArray& certHash); signals: void disconnect(); void connected(ServerConnection* connection); - }; #endif diff --git a/src/client/net/serverdiscovery.cpp b/src/client/net/serverdiscovery.cpp new file mode 100644 index 0000000..9f1991c --- /dev/null +++ b/src/client/net/serverdiscovery.cpp @@ -0,0 +1,181 @@ + +#include <QNetworkInterface> +#include "../../shared/settings.h" +#include "../../shared/network.h" +#include "../../shared/util.h" +#include "serverdiscovery.h" +#include <assert.h> + + +/***************************************************************************//** + * Ctor + */ +ServerDiscovery::ServerDiscovery(QObject *parent) : QObject(parent) +{ + _hashErrorCount = 0; + _ipErrorCount = 0; + + /* Try to get a UDP port for server discovery */ + int tries = 10; + while (tries-- != 0) + { + const quint16 port = (quint16)(qrand() % 10000) + 10000; + if (_discoverySocket.bind(QHostAddress::Any, port)) + break; + if (tries == 0) + qFatal("Could not bind to any UDP port for server discovery."); + } + // Handle incoming messages + connect(&_discoverySocket, SIGNAL(readyRead()), this, SLOT(onUdpReadyRead())); + + /* Setup the discovery timer */ + _discoveryTimer.setInterval(500); + _discoveryTimer.setSingleShot(true); + // + connect(&_discoveryTimer, SIGNAL(timeout()), this, SLOT(doDiscovery())); +} + +/***************************************************************************//** + * Dtor + */ +ServerDiscovery::~ServerDiscovery() +{ +} + +/***************************************************************************//** + * @brief start + */ +void ServerDiscovery::start(const QByteArray& sessionName) +{ + assert(!this->isActive()); + + // Set the session which is searched + _nameBytes = sessionName; + + // Enable signal emittance + this->blockSignals(false); + + // Reset the error counters + _hashErrorCount = _ipErrorCount = 0; + + // reset anbd start the discovery timer + _discoveryTimer.setInterval(500); + _discoveryTimer.start(); +} + +/***************************************************************************//** + * @brief stop + */ +void ServerDiscovery::stop() +{ + assert(this->isActive()); + + //Bock further signal emittance + this->blockSignals(true); + _discoveryTimer.stop(); +} + +/******************************************************************************* + * SLOTS + ***************************************************************************//** + * @brief ConnectWindow::doDiscovery + */ +void ServerDiscovery::doDiscovery() +{ + // Send discovery + _packet.reset(); + QByteArray iplist(Network::interfaceAddressesToString().toUtf8()); + // qDebug + QByteArray salt1(SALT_LEN, 0); + if (_salt2.size() < SALT_LEN) + _salt2.resize(SALT_LEN); + for (int i = 0; i < SALT_LEN; ++i) { + salt1[i] = qrand() & 0xff; + _salt2[i] = qrand() & 0xff; + } + _packet.reset(); + _packet.setField(_HASH, genSha1(&_nameBytes, &salt1, &iplist)); + _packet.setField(_SALT1, salt1); + _packet.setField(_SALT2, _salt2); + _packet.setField(_IPLIST, iplist); + foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces()) + { + foreach (QNetworkAddressEntry entry, interface.addressEntries()) + { + if (!entry.broadcast().isNull() && entry.ip() != QHostAddress::LocalHost && entry.ip() != QHostAddress::LocalHostIPv6) + { + qDebug() << "Broadcasting to " << entry.broadcast().toString(); + if (!_packet.writeMessage(&_discoverySocket, entry.broadcast(), SERVICE_DISCOVERY_PORT)) + qDebug("FAILED"); + } + } + } + qDebug("Broadcasting to 255.255.255.255"); + if (!_packet.writeMessage(&_discoverySocket, QHostAddress::Broadcast, SERVICE_DISCOVERY_PORT)) + qDebug("FAILED"); + + // Start the timer again with a larger interval + if (_discoveryTimer.interval() < 5000) + _discoveryTimer.setInterval(_discoveryTimer.interval() * 2); + _discoveryTimer.start(); +} + + +/***************************************************************************//** + * Handle incoming service discovery packets. + */ +void ServerDiscovery::onUdpReadyRead() +{ + char data[UDPBUFSIZ]; + QHostAddress addr; + quint16 port; + while (_discoverySocket.hasPendingDatagrams()) + { + // Discard any packets if discovery is stopped + if (!this->isActive()){ + _discoverySocket.readDatagram(NULL, 0); + continue; + } + + const qint64 size = _discoverySocket.readDatagram(data, UDPBUFSIZ, &addr, &port); + if (size <= 0) //|| _connection != NULL) // TODO CHECK + continue; + + _packet.reset(); + if (!_packet.readMessage(data, (quint32)size)) + continue; + + // Valid packet, process it: + const QByteArray hash(_packet.getFieldBytes(_HASH)); + const QByteArray iplist(_packet.getFieldBytes(_IPLIST)); + const QByteArray port(_packet.getFieldBytes(_PORT)); + const QByteArray cert(_packet.getFieldBytes(_CERT)); + + // Check if the source IP of the packet matches any of the addresses given in the IP list + if (!Network::isAddressInList(QString::fromUtf8(iplist), addr.toString())) + { + ++_ipErrorCount; + emit error(ErrorType::InvalidIpList, _hashErrorCount); + continue; + } + + // If so, check if the submitted hash seems valid + if (genSha1(&_nameBytes, &_salt2, &iplist, &port, &cert) != hash) + { + // did not match local session name, or other data was spoofed + ++_hashErrorCount; + emit error(ErrorType::InvalidHash, _ipErrorCount); + continue; + } + + /* Otherwise it's a valid reply */ + qDebug() << "Server detected:" + << addr.toString() + ":" + QString::fromUtf8(port) + "/" + _nameBytes; + + // Tell that a server hs been found + emit serverDetected(addr.toString(), (quint16)QString::fromUtf8(port).toInt(), _nameBytes, cert); + + // Stop the discovery + this->stop(); + } +} diff --git a/src/client/net/serverdiscovery.h b/src/client/net/serverdiscovery.h new file mode 100644 index 0000000..093b841 --- /dev/null +++ b/src/client/net/serverdiscovery.h @@ -0,0 +1,51 @@ +#ifndef SERVERDISCOVERY_H +#define SERVERDISCOVERY_H + +#include <QObject> +#include <QUdpSocket> +#include <QTimer> +#include "../../shared/networkmessage.h" + +class ServerDiscovery : public QObject +{ + Q_OBJECT + + public: + enum class ErrorType{ + InvalidIpList, + InvalidHash + }; + + explicit ServerDiscovery(QObject *parent = 0); + ~ServerDiscovery(); + + void start(const QByteArray& sessionName); + void stop(); + inline bool isActive(){ return _discoveryTimer.isActive(); } + + private: + QTimer _discoveryTimer; + int _hashErrorCount; + int _ipErrorCount; + QByteArray _nameBytes; + QByteArray _salt2; + QUdpSocket _discoverySocket; + NetworkMessage _packet; + + static const int UDPBUFSIZ = 9000; + static const int SALT_LEN = 18; + +public: + signals: + void serverDetected(const QString& host, const quint16 port, const QByteArray& sessionName, const QByteArray& certHash); + void error(ErrorType e, int count); + + public slots: + + private slots: + void doDiscovery(); + void onUdpReadyRead(); + +}; + +#endif // SERVERDISCOVERY_H diff --git a/src/client/toolbar/toolbar.cpp b/src/client/toolbar/toolbar.cpp index 88d4b2a..9bc5998 100644 --- a/src/client/toolbar/toolbar.cpp +++ b/src/client/toolbar/toolbar.cpp @@ -174,8 +174,7 @@ void Toolbar::onVncServerIsRunning(int port) */ void Toolbar::onDisconnected() { - _connectWindow->setConnected(false); - if (_connection != NULL) + if (_connection != NULL) _connection->blockSignals(true); _connection = NULL; _ui->lblStatus->setStyleSheet("color:red"); @@ -205,7 +204,6 @@ void Toolbar::onConnected(ServerConnection* connection) _vnc, SLOT(open(const QString&, int, const QString&, bool, bool, const QString&, const int))); connect(_connection, SIGNAL(closeVnc()), _vnc, SLOT(close())); connect(_vnc, SIGNAL(running(const bool, const int)), _connection, SLOT(onVncViewerStartStop(const bool, const int))); - _connectWindow->setConnected(true); } /***************************************************************************//** |