#include #include "../../shared/settings.h" #include "../../shared/network.h" #include "../../shared/util.h" #include "serverdiscovery.h" #include /***************************************************************************//** * Ctor */ ServerDiscovery::ServerDiscovery(QObject *parent) : QObject(parent), _minDiscoveryInterval(500), _maxDiscoveryInterval(5000) { _hashErrorCount = 0; _ipErrorCount = 0; /* Try to get a UDP port for server discovery */ int tries = 10; while (tries-- != 0) { quint16 port = (quint16)(16384 + qrand() % 32768); if (_discoverySocket.bind(QHostAddress::AnyIPv4, 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(_minDiscoveryInterval); _discoveryTimer.setSingleShot(true); // connect(&_discoveryTimer, SIGNAL(timeout()), this, SLOT(doDiscovery())); } /***************************************************************************//** * Dtor */ ServerDiscovery::~ServerDiscovery() { } /***************************************************************************//** * @brief start */ void ServerDiscovery::start(const QByteArray& sessionName, QString mgrIP) { if (!mgrIP.isEmpty()) { _mgrIP.setAddress(mgrIP); } else { _mgrIP = QHostAddress::Null; } //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(_minDiscoveryInterval); _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] = (char)(qrand() & 0xff); _salt2[i] = (char)(qrand() & 0xff); } _packet.reset(); _packet.setField(_HASH, genSha1(&_nameBytes, &salt1, &iplist)); _packet.setField(_SALT1, salt1); _packet.setField(_SALT2, _salt2); _packet.setField(_IPLIST, iplist); // Check if specifig manager IP is given. If not broadcast in whole network. if (_mgrIP != QHostAddress::Null) { qDebug() << "Broadcasting to " << _mgrIP.toString(); if (!_packet.writeMessage(&_discoverySocket, _mgrIP, SERVICE_DISCOVERY_PORT)) qDebug("Failed"); } else { 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() < _maxDiscoveryInterval) _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) //|| clientApp->connection() != NULL) // TODO CHECK continue; _packet.reset(); if (_packet.readMessage(data, (quint32)size) != NM_READ_OK) 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 && _mgrIP != addr) { // 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, (_mgrIP == addr)); // Stop the discovery this->stop(); } }