/* # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg # # This program is free software distributed under the GPL version 2. # See http://openslx.org/COPYING # # If you have any feedback please consult http://openslx.org/feedback and # send your suggestions, praise, or complaints to feedback@openslx.org # # General information about OpenSLX can be found at http://openslx.org/ # ----------------------------------------------------------------------------- # src/net/pvsServiceDiscovery.cpp # - handle console broadcasts, tell client to connect on match # ----------------------------------------------------------------------------- */ #include #include "src/pvs.h" #include "pvsServiceDiscovery.h" #include "pvsDiscoveredServer.h" #include "src/setup.h" #include "src/util/serviceDiscoveryUtil.h" #include PVSServiceDiscovery::PVSServiceDiscovery(PVS* client) { assert(client); bool ret = _sock.bind(SD_PORT_CLIENT); if (!ret) { qDebug("Could not open SERVICE DISCOVERY port"); exit(1); } connect(&_sock, SIGNAL(readyRead()), this, SLOT(sock_dataArrival())); _currentServer = 0; _last = QDateTime::currentDateTime(); _client = client; _timerId = startTimer(10000); } PVSServiceDiscovery::~PVSServiceDiscovery() { killTimer(_timerId); } void PVSServiceDiscovery::connectToSession(QString name, QString passwd) { _sessionName = name; _sessionPasswd = passwd; if (name.length() == 0) return; for (tServerList::iterator it = _servers.begin(); it != _servers.end(); it++) { PVSDiscoveredServer *ds = *it; if (ds->isValid() && ds->getName() == name) { _client->connectToHost(ds, _sessionPasswd); } } } void PVSServiceDiscovery::sock_dataArrival() { int len; while ((len = _sock.pendingDatagramSize()) > -1) { if (len == 0) continue; char *data = new char[len]; QHostAddress host; len = _sock.readDatagram(data, len, &host); SdFields fields = parseSdFields((unsigned char*)data, len); QDateTime now = QDateTime::currentDateTime(); if (fields.contains("hsh") && fields.contains("prt") && fields.contains("aut")) { if (fields["aut"] == "SHA1") { this->handleDiscovery( host, atoi(fields["prt"].toUtf8().data()), QByteArray::fromBase64(fields["hsh"].toAscii()) ); } } /* // DEBUG ONLY: connect to any host without matching the session name if (_last.secsTo(now) > 9 && fields.contains("prt")) { _last = now; int port = atoi(fields["prt"].toUtf8().data()); _client->connectToHost(host, QByteArray(), port); } // ^^^^^^^^^^ */ } } void PVSServiceDiscovery::handleDiscovery(QHostAddress host, int port, QByteArray hash) { int numhosts = 0; ///< while iterating we count how many entries have the same host for (tServerList::iterator it = _servers.begin(); it != _servers.end(); it++) { if ((**it).hasHost(host)) { ConsoleLog writeNetwork(host.toString() + " == " + (**it).getHost().toString()); if (++numhosts >= 5) return; // ddos through faked service broadcasts? ignore... if ((**it).hasFingerprint(hash) && (**it).getPort() == port) // known entry... { if ((*it)->isValid() && (*it)->getName() == _sessionName && _sessionName.length() > 0) { ConsoleLog writeNetwork(QString("Connecting to ").append(_sessionName)); _client->connectToHost((*it), _sessionPasswd); } (**it).update(port); return; } } } if (_servers.length() >= 30) return; // !? QString oname = sha1ToReadable(hash); QString name = oname; int dup = 0; while (nameExists(name)) { name = "(" + QString::number(++dup) + ") " + oname; } PVSDiscoveredServer *ds = new PVSDiscoveredServer(this, host, port, hash, name); connect(ds, SIGNAL(validated(PVSDiscoveredServer*)), this, SLOT(sendServerToGui(PVSDiscoveredServer*))); _servers.push_back(ds); setTimerInterval(); } bool PVSServiceDiscovery::nameExists(QString name) { for (tServerList::iterator it = _servers.begin(); it != _servers.end(); it++) { if ((**it).getName() == name) return true; } return false; } QStringList PVSServiceDiscovery::getAllServers() { QStringList hosts; for (tServerList::iterator it = _servers.begin(); it != _servers.end(); it++) { if ((**it).isValid()) hosts.append((**it).getName()); } return hosts; } void PVSServiceDiscovery::sendServerToGui(PVSDiscoveredServer* ds) { _client->guiAddHost(ds->getName()); } void PVSServiceDiscovery::timerEvent(QTimerEvent *event) { if (_servers.size() == 0) return; if (_currentServer >= _servers.size()) _currentServer = 0; PVSDiscoveredServer *ds = _servers.at(_currentServer); if (ds->getAge() >= SB_INTERVAL*2 + 1) // Entry too old? { disconnect(ds, SIGNAL(validated(PVSDiscoveredServer*)), this, SLOT(sendServerToGui(PVSDiscoveredServer*))); _client->guiDelHost(ds->getName()); delete ds; _servers.removeAt(_currentServer); // Clean it up setTimerInterval(); } else { ds->validateCertificate(); } ++_currentServer; } void PVSServiceDiscovery::setTimerInterval() { killTimer(_timerId); _timerId = startTimer(10000 / (_servers.size() + 1)); }