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/connectwindow/connectwindow.cpp | 284 +++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 src/client/connectwindow/connectwindow.cpp (limited to 'src/client/connectwindow/connectwindow.cpp') diff --git a/src/client/connectwindow/connectwindow.cpp b/src/client/connectwindow/connectwindow.cpp new file mode 100644 index 0000000..bc4e9b2 --- /dev/null +++ b/src/client/connectwindow/connectwindow.cpp @@ -0,0 +1,284 @@ +/* + * connectwindow.cpp + * + * Created on: 28.01.2013 + * Author: sr + */ + +#include "connectwindow.h" +#include "../../shared/settings.h" +#include "../../shared/network.h" +#include "../../shared/util.h" + +#include "../net/serverconnection.h" + +#include + +#define UDPBUFSIZ 9000 +#define SALT_LEN 18 + +ConnectWindow::ConnectWindow(QWidget *parent) : + QDialog(parent), _connected(false), _timerDiscover(0), _timerHide(0), _connection(NULL), _state(Idle), + _hashErrorCount(0), _hashSslErrorCount(0), _certErrorCount(0), _ipErrorCount(0), _discoveryInterval(800) +{ + setupUi(this); + // + connect(cmdOK, SIGNAL(clicked()), this, SLOT(onOkClick())); + connect(cmdCancel, SIGNAL(clicked()), this, SLOT(onCancelClick())); + 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() +{ + +} + +void ConnectWindow::setConnected(const bool connected) +{ + _connected = connected; + this->updateState(); + if (_state == Scanning) + { + killTimer(_timerDiscover); + _discoveryInterval = 1000; + _timerDiscover = startTimer(_discoveryInterval); + } +} + +void ConnectWindow::setState(const ConnectionState state) +{ + if (_state != state) + { + _state = state; + this->updateState(); + } +} + +void ConnectWindow::updateState() +{ + txtName->setEnabled(_state == Idle && !_connected); + + if (_connected) + { + cmdOK->setEnabled(true); + cmdOK->setText(tr("&Disconnect")); + lblStatus->setText(tr("Connected.")); + txtName->setEnabled(false); + return; + } + + if (_state != Idle) + cmdOK->setText(tr("&Stop")); + else + cmdOK->setText(tr("&Connect")); + + switch (_state) + { + case Idle: + lblStatus->setText(tr("Ready to connect; please enter session name.")); + break; + case Scanning: + lblStatus->setText(tr("Scanning for session %1.").arg(txtName->text())); + _timerDiscover = startTimer(_discoveryInterval); + break; + case Connecting: + lblStatus->setText(tr("Found session, connecting...")); + break; + case AwaitingChallenge: + lblStatus->setText(tr("Waiting for server challenge...")); + break; + case AwaitingChallengeResponse: + lblStatus->setText(tr("Replied to challenge, sent own...")); + break; + case LoggingIn: + lblStatus->setText(tr("Logging in...")); + break; + case Connected: + lblStatus->setText(tr("Connection established!")); + break; + case InvalidIpList: + case InvalidHash: + case InvalidCert: + case InvalidSslHash: + lblError->setText(tr("Invalid hash: %1; invalid cert: %2; invalid iplist: %3; invalid sslhash: %4") + .arg(_hashErrorCount).arg(_certErrorCount).arg(_ipErrorCount).arg(_hashSslErrorCount)); + break; + } +} + +/** + * Overrides + */ + +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(&_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"); + // End send discovery + } + else if(event->timerId() == _timerHide) + { + killTimer(_timerHide); + _timerHide = 0; + this->hide(); + } + else + // Unknown/Old timer id, kill it + killTimer(event->timerId()); +} + +void ConnectWindow::closeEvent(QCloseEvent *e) +{ + e->ignore(); + this->hide(); +} + +/** + * Slots + */ + +void ConnectWindow::onOkClick() +{ + if (_timerHide) + killTimer(_timerHide); + _timerHide = 0; + if (_timerDiscover) + killTimer(_timerDiscover); + if (_connected || _state != Idle) + { + // Stop or disconnect + _timerDiscover = 0; + emit disconnect(); + this->setState(Idle); + } + else + { + // Connect (scan for session) + _discoveryInterval = 800; + _nameBytes = txtName->text().toUtf8(); + _timerDiscover = startTimer(_discoveryInterval); + _hashErrorCount = _hashSslErrorCount = _certErrorCount = _ipErrorCount = 0; + this->setState(Scanning); + } +} + +void ConnectWindow::onCancelClick() +{ + this->hide(); +} + +void ConnectWindow::onUdpReadyRead() +{ + 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(&_nameBytes, &_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(), _nameBytes, cert); + connect(_connection, SIGNAL(stateChange(ConnectWindow::ConnectionState)), this, SLOT(onConnectionStateChange(ConnectWindow::ConnectionState))); + connect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onConnectionClosed(QObject*))); + } +} + +void ConnectWindow::onConnectionStateChange(ConnectWindow::ConnectionState state) +{ + bool reset = (_state == Scanning); + if (state == InvalidSslHash) + ++_hashSslErrorCount; + this->setState(state); + if (reset) + _state = Scanning; + if (state == Connected) + { + 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); + _connection = NULL; + _timerHide = startTimer(2000); + } +} + +void ConnectWindow::onConnectionClosed(QObject* connection) +{ + _connection = NULL; +} -- cgit v1.2.3-55-g7522