/*
# 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 <QtCore/QHash>
#include "src/pvs.h"
#include "pvsServiceDiscovery.h"
#include "pvsDiscoveredServer.h"
#include "src/setup.h"
#include "src/util/serviceDiscoveryUtil.h"
#include <cassert>
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));
}