From afc69ca4ffb0e11d9d06b8ddd31ee40963f86f17 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Wed, 28 May 2014 00:37:28 +0200 Subject: Outsource serverDiscovery. --- src/client/net/serverdiscovery.cpp | 181 +++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 src/client/net/serverdiscovery.cpp (limited to 'src/client/net/serverdiscovery.cpp') 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 +#include "../../shared/settings.h" +#include "../../shared/network.h" +#include "../../shared/util.h" +#include "serverdiscovery.h" +#include + + +/***************************************************************************//** + * 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(); + } +} -- cgit v1.2.3-55-g7522