summaryrefslogblamecommitdiffstats
path: root/src/net/pvsServiceDiscovery.cpp
blob: dd6b500b9db786e180842bd544e6eb6a26a8bafb (plain) (tree)































                                                                               
                                                        



















































































































































                                                                                                                   
/*
# 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));
}