summaryrefslogtreecommitdiffstats
path: root/src/server/net
diff options
context:
space:
mode:
authorsr2013-02-04 19:50:31 +0100
committersr2013-02-04 19:50:31 +0100
commit1a5709501f94014d41987b956338bb6424b9f90c (patch)
treed3b93fe8dc406bca56aff147ef5cc4cbf9ed6be0 /src/server/net
parentTest (diff)
downloadpvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.gz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.xz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.zip
Initial commit
Diffstat (limited to 'src/server/net')
-rw-r--r--src/server/net/certmanager.cpp92
-rw-r--r--src/server/net/certmanager.h29
-rw-r--r--src/server/net/client.cpp288
-rw-r--r--src/server/net/client.h104
-rw-r--r--src/server/net/discoverylistener.cpp165
-rw-r--r--src/server/net/discoverylistener.h40
-rw-r--r--src/server/net/listenserver.cpp40
-rw-r--r--src/server/net/listenserver.h30
-rw-r--r--src/server/net/sslserver.cpp113
-rw-r--r--src/server/net/sslserver.h50
10 files changed, 951 insertions, 0 deletions
diff --git a/src/server/net/certmanager.cpp b/src/server/net/certmanager.cpp
new file mode 100644
index 0000000..e661a70
--- /dev/null
+++ b/src/server/net/certmanager.cpp
@@ -0,0 +1,92 @@
+/*
+ # 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/util/CertManager.cpp
+ # - Manage SSL certificates
+ # - provide access by name
+ # -----------------------------------------------------------------------------
+ */
+
+#include "certmanager.h"
+#include "../util/util.h"
+#include <QMap>
+#include <QFileInfo>
+#include <QSettings>
+#include <cstdlib>
+
+namespace CertManager
+{
+static QMap<QString, QSslCertificate> _certs;
+static QMap<QString, QSslKey> _keys;
+
+static void generateFiles(QString& key, QString& cert);
+static bool loadFiles(QString& keyFile, QString& certFile, QSslKey &key, QSslCertificate &cert);
+
+bool getPrivateKeyAndCert(const QString &name, QSslKey &key, QSslCertificate &cert)
+{
+ if (_keys.contains(name))
+ {
+ key = _keys[name];
+ cert = _certs[name];
+ return true;
+ }
+ USER_SETTINGS(settings);
+ QString certFile = settings.fileName().append(".").append(name);
+ QString keyFile = certFile;
+ keyFile.append(".rsa");
+ certFile.append(".crt");
+ //
+ if (!loadFiles(keyFile, certFile, key, cert))
+ {
+ generateFiles(keyFile, certFile);
+ if (!loadFiles(keyFile, certFile, key, cert))
+ return false;
+ }
+ _certs.insert(name, cert);
+ _keys.insert(name, key);
+ return true;
+}
+
+static bool loadFiles(QString& keyFile, QString& certFile, QSslKey &key, QSslCertificate &cert)
+{
+ QFileInfo keyInfo(keyFile);
+ QFileInfo certInfo(certFile);
+ if (keyInfo.exists() && certInfo.exists())
+ { // Both files exist, see if they're valid and return
+ QFile kf(keyFile);
+ kf.open(QFile::ReadOnly);
+ key = QSslKey(&kf, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ QList<QSslCertificate> certlist = QSslCertificate::fromPath(certFile);
+ if (!key.isNull() && !certlist.empty())
+ {
+ cert = certlist.first();
+ if (!cert.isNull())
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void generateFiles(QString& key, QString& cert)
+{
+ char tmp[1000];
+ unlink(key.toLocal8Bit().data());
+ unlink(cert.toLocal8Bit().data());
+ snprintf(tmp, 1000,
+ "openssl req -x509 -nodes -days 3650 -newkey rsa:1024 -subj '/C=DE/ST=BaWue/L=Freiburg/CN=openslx.org' -keyout \"%s\" -out \"%s\"",
+ key.toLocal8Bit().data(), cert.toLocal8Bit().data());
+ system(tmp);
+ snprintf(tmp, 1000, "chmod 0600 \"%s\" \"%s\"", key.toLocal8Bit().data(), cert.toLocal8Bit().data());
+ system(tmp);
+}
+}
diff --git a/src/server/net/certmanager.h b/src/server/net/certmanager.h
new file mode 100644
index 0000000..c69bc23
--- /dev/null
+++ b/src/server/net/certmanager.h
@@ -0,0 +1,29 @@
+/*
+# 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/util/CertManager.cpp
+# - Manage SSL certificates
+# - provide access by name
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef CERTMANAGER_H_
+#define CERTMANAGER_H_
+
+#include <QtNetwork/QSslCertificate>
+#include <QtNetwork/QSslKey>
+
+namespace CertManager
+{
+ bool getPrivateKeyAndCert(const QString &name, QSslKey &key, QSslCertificate &cert);
+}
+
+#endif /* CERTMANAGER_H_ */
diff --git a/src/server/net/client.cpp b/src/server/net/client.cpp
new file mode 100644
index 0000000..ca91e76
--- /dev/null
+++ b/src/server/net/client.cpp
@@ -0,0 +1,288 @@
+/*
+ * client.cpp
+ *
+ * Created on: 18.01.2013
+ * Author: sr
+ */
+
+#include "client.h"
+#include "../util/global.h"
+#include "../../shared/settings.h"
+#include "../../shared/util.h"
+#include <QSslSocket>
+#include <QHostAddress>
+#include <QPixmap>
+#include <cassert>
+
+#define CHALLENGE_LEN 20
+
+ClientId Client::_clientIdCounter = 0;
+
+Client::Client(QSslSocket* socket) :
+ _socket(socket), _authed(0), _desiredProjectionSource(0), _isProjectionSource(false),
+ _currentProjectionSource(0), _vncPort(0), _activeVncClient(false)
+{
+ assert(socket != NULL);
+ _id = ++_clientIdCounter;
+ _pingTimeout = QDateTime::currentMSecsSinceEpoch() + PING_TIMEOUT_MS;
+ _ip = _socket->peerAddress().toString();
+ qDebug("*** Client %s created.", qPrintable(_ip));
+ // Connect important signals
+ connect(_socket, SIGNAL(disconnected()), this, SLOT(onClosed()));
+ connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
+ connect(_socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(onSslErrors(const QList<QSslError> &)));
+ connect(_socket, SIGNAL(readyRead()), this, SLOT(onDataArrival()));
+ // Send challenge
+ _challenge.resize(CHALLENGE_LEN);
+ for (int i = 0; i < CHALLENGE_LEN; ++i)
+ _challenge[i] = qrand() & 0xff;
+ _toClient.reset();
+ _toClient.setField(_ID, _CHALLENGE);
+ _toClient.setField(_CHALLENGE, _challenge);
+ _toClient.writeMessage(_socket);
+ // give client 3 seconds to complete handshake
+ _timerIdAuthTimeout = startTimer(3000);
+}
+
+Client::~Client()
+{
+ if (_socket != NULL)
+ {
+ qDebug("**** DELETE IN DESTRUCTOR");
+ _socket->deleteLater();
+ }
+ qDebug("*** Client %s destroyed.", qPrintable(_ip));
+}
+
+void Client::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() == _timerIdAuthTimeout)
+ {
+ // Client did not send login request within 3 seconds
+ killTimer(_timerIdAuthTimeout);
+ _timerIdAuthTimeout = 0;
+ this->disconnect();
+ }
+}
+
+void Client::sendMessage(NetworkMessage& message)
+{
+ if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState)
+ return;
+ message.writeMessage(_socket);
+ if (!message.writeComplete())
+ {
+ qCritical() << "SendMessage to client " << _name << "@" << _ip << " failed!";
+ }
+}
+
+void Client::requestThumb(const int width, const int height)
+{
+ if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState)
+ {
+ qDebug("requestThumb called in bad state");
+ return;
+ }
+ _toClient.reset();
+ _toClient.setField(_ID, _THUMB);
+ _toClient.setField(_X, QString::number(width));
+ _toClient.setField(_Y, QString::number(height));
+ _toClient.writeMessage(_socket);
+}
+
+void Client::onDataArrival()
+{
+ _pingTimeout = QDateTime::currentMSecsSinceEpoch() + PING_TIMEOUT_MS;
+ //
+ if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState)
+ {
+ qDebug("dataArrival called in bad state");
+ return;
+ }
+
+ //qDebug() << _socket->bytesAvailable() << " bytes to read";
+ bool ret;
+ while (_socket->bytesAvailable())
+ {
+ ret = _fromClient.readMessage(_socket); // let the message read data from socket
+ if (!ret) // error parsing msg, disconnect client!
+ {
+ this->disconnect();
+ return;
+ }
+ if (_fromClient.readComplete()) // message is complete
+ {
+ this->handleMsg();
+ if (_socket == NULL)
+ return;
+ _fromClient.reset();
+ }
+ }
+}
+
+void Client::onSslErrors(const QList<QSslError> & errors)
+{
+ this->disconnect();
+}
+
+void Client::onClosed()
+{
+ this->disconnect();
+}
+
+void Client::onError(QAbstractSocket::SocketError errcode)
+{
+ this->disconnect();
+}
+
+void Client::handleMsg()
+{
+ const QString &id = _fromClient.getFieldString(_ID);
+ if (id.isEmpty())
+ {
+ qDebug("Received message with empty ID field. ignored.");
+ return;
+ }
+ qDebug() << "Received message " << id;
+
+ if (_authed == 2)
+ {
+ // Following messages are only valid of the client is already authenticated
+ if (id == _THUMB)
+ {
+ QPixmap pixmap;
+ if (!pixmap.loadFromData(_fromClient.getFieldBytes("IMG")))
+ {
+ qDebug("Could not decode thumbnail image from client.");
+ return;
+ }
+ emit thumbUpdated(this, pixmap);
+ }
+ else if (id == _VNCSERVER)
+ {
+ // Client tells about startup of vnc server
+ const int port = _fromClient.getFieldString("PORT").toInt();
+ if (port <= 0)
+ {
+ if (_vncPort <= 0)
+ {
+ qDebug() << "Starting VNC server on client" << _name << " (" << _ip << ") failed.";
+ // TODO: Show message on manager
+ }
+ }
+ else
+ {
+ _vncRoPass = _fromClient.getFieldString("ROPASS");
+ _vncRwPass = _fromClient.getFieldString("RWPASS");
+ }
+ _vncPort = port;
+ emit vncServerStateChange(this);
+ }
+ else if (id == _VNCCLIENT)
+ {
+ // Client tells us that it started or stopped displaying a remote screen via VNC
+ _activeVncClient = (_fromClient.getFieldString("ENABLED").toInt() != 0);
+ const ClientId other = (ClientId)_fromClient.getFieldString("CLIENTID").toInt();
+
+ if (!_activeVncClient && other == 0)
+ _desiredProjectionSource = 0;
+
+ emit vncClientStateChange(this);
+
+ if (!_activeVncClient)
+ _currentProjectionSource = 0;
+ else
+ _currentProjectionSource = other;
+ }
+ return;
+ }
+
+ if (_authed == 1) // Not authed yet, only care about login requests
+ {
+ if (id == _LOGIN)
+ {
+ killTimer(_timerIdAuthTimeout);
+ _timerIdAuthTimeout = 0;
+ ClientLogin request;
+ request.accept = true;
+ request.host = _fromClient.getFieldString("HOST");
+ request.name = _fromClient.getFieldString("NAME");
+ request.ip = _socket->peerAddress().toString();
+ qDebug() << "Login request by " << request.name;
+ // emit event, see if request is accepted
+ emit authenticating(this, &request);
+ if (!request.accept)
+ {
+ qDebug("Request denied.");
+ this->disconnect(); // Nope
+ return;
+ }
+ // Accepted
+ _authed = 2;
+ qDebug("valid, step <- 2");
+ _name = request.name;
+ _host = request.host;
+ _toClient.reset();
+ _toClient.setField(_ID, _LOGIN);
+ _toClient.writeMessage(_socket);
+ emit authenticated(this);
+ }
+ return;
+ }
+
+ if (_authed == 0)
+ {
+ // Waiting for challenge reply by client
+ if (id == _CHALLENGE)
+ {
+ QByteArray hash(_fromClient.getFieldBytes(_HASH));
+ QByteArray challenge(_fromClient.getFieldBytes(_CHALLENGE));
+ if (genSha1(&Global::sessionNameArray(), &_challenge) != hash)
+ { // Challenge reply is invalid, drop client
+ _toClient.buildErrorMessage("Challenge reply invalid.");
+ _toClient.writeMessage(_socket);
+ this->deleteLater();
+ return;
+ }
+ // Now answer to challenge by client
+ _toClient.reset();
+ _toClient.setField(_ID, _CHALLENGE);
+ _toClient.setField(_HASH, genSha1(&Global::sessionNameArray(), &challenge));
+ _toClient.writeMessage(_socket);
+ _authed = 1;
+ qDebug("client's challenge reply was valid, step <- 1");
+ }
+ return;
+ }
+
+}
+
+void Client::startVncServer()
+{
+ _vncPort = 0;
+ _toClient.reset();
+ _toClient.setField(_ID, _VNCSERVER);
+ _toClient.setField("ENABLE", QByteArray("1"));
+ sendMessage(_toClient);
+}
+
+void Client::stopVncServer()
+{
+ _toClient.reset();
+ _toClient.setField(_ID, _VNCSERVER);
+ _toClient.setField("ENABLE", QByteArray("0"));
+ sendMessage(_toClient);
+}
+
+void Client::disconnect()
+{
+ if (_socket != NULL)
+ {
+ qDebug("*** Client %s disconnected.", qPrintable(_ip));
+ _socket->blockSignals(true);
+ _socket->abort();
+ _socket->deleteLater();
+ _socket = NULL;
+ this->deleteLater();
+ }
+}
diff --git a/src/server/net/client.h b/src/server/net/client.h
new file mode 100644
index 0000000..8004ebb
--- /dev/null
+++ b/src/server/net/client.h
@@ -0,0 +1,104 @@
+#ifndef CLIENT_H_
+#define CLIENT_H_
+
+#include <QtCore>
+#include <QSslError>
+#include <QAbstractSocket>
+#include "../../shared/networkmessage.h"
+
+class QSslSocket;
+
+struct ClientLogin
+{
+ bool accept;
+ QString name;
+ QString host;
+ QString ip;
+};
+
+typedef int ClientId;
+
+class Client : public QObject
+{
+ Q_OBJECT
+
+private:
+ static ClientId _clientIdCounter;
+
+ QSslSocket *_socket;
+ int _authed; // 0 = challenge sent, awaiting reply 1 = challenge ok, client challenge replied, awaiting login, 2 = ESTABLISHED
+ QString _name;
+ QString _host;
+ QString _ip;
+ QByteArray _challenge;
+ qint64 _pingTimeout;
+ NetworkMessage _toClient, _fromClient;
+ int _timerIdAuthTimeout;
+
+ ClientId _id; // this client's unique id
+
+ // If this client should be projected to from another client, the other client's id is set here. 0 otherwise.
+ // This is not currently used and it is questionable if this makes sense, as it might just be confusing if
+ // several groups students watch different other students.
+ // Also, visualizing such a situation in the GUI in a meaningful way would be hard.
+ ClientId _desiredProjectionSource;
+ // This boolean tells whether this client is currently the VNC broadcast source. This
+ // version only allows "one to all others" setups
+ bool _isProjectionSource;
+
+ ClientId _currentProjectionSource;
+
+ QString _vncRwPass, _vncRoPass;
+ int _vncPort;
+ bool _activeVncClient;
+
+ void handleMsg();
+ void disconnect();
+
+protected:
+ void timerEvent(QTimerEvent* event);
+
+public:
+ explicit Client(QSslSocket* socket);
+ ~Client();
+ void requestThumb(const int width, const int height);
+ void sendMessage(NetworkMessage& message);
+ //void acceptData();
+ const inline bool isAuthed() const { return _authed == 2; }
+ const inline QString& name() const { return _name; }
+ const inline QString& host() const { return _host; }
+ const inline QString& ip() const { return _ip; }
+ // The computer ID (used eg. for saving the frame positions) is currently the IP, but this is an extra method for easier modification later on
+ const inline QString& computerId() const { return _ip; }
+ const inline ClientId id() const { return _id; }
+
+ inline const QString& vncRwPass() const { return _vncRwPass; }
+ inline const QString& vncRoPass() const { return _vncRoPass; }
+ inline const int vncPort() const { return _vncPort; }
+ inline const bool isActiveVncClient() const { return _activeVncClient; }
+ inline const bool isActiveVncServer() const { return _vncPort > 0; }
+ inline const ClientId desiredProjectionSource() const { return _desiredProjectionSource; }
+ inline void setDesiredProjectionSource(ClientId source) { _desiredProjectionSource = source; }
+ inline const bool isProjectionSource() const { return _isProjectionSource; }
+ inline void setProjectionSource(bool enable) { _isProjectionSource = enable; }
+ inline const ClientId currentProjectionSource() const { return _currentProjectionSource; }
+ inline void setCurrentProjectionSource(ClientId source) { _currentProjectionSource = source; }
+ void startVncServer();
+ void stopVncServer();
+
+signals:
+ void authenticating(Client* client, ClientLogin* request);
+ void authenticated(Client* client);
+ void thumbUpdated(Client* client, const QPixmap& thumb);
+ void vncServerStateChange(Client* client);
+ void vncClientStateChange(Client* client);
+
+private slots:
+ void onSslErrors(const QList<QSslError> & errors); // triggered for errors that occur during SSL negotiation
+ void onDataArrival(); // triggered if data is available for reading
+ void onClosed(); // triggered if the socket is closed
+ void onError(QAbstractSocket::SocketError errcode); // triggered if an error occurs on the socket
+
+};
+
+#endif /* CLIENT_H_ */
diff --git a/src/server/net/discoverylistener.cpp b/src/server/net/discoverylistener.cpp
new file mode 100644
index 0000000..051a972
--- /dev/null
+++ b/src/server/net/discoverylistener.cpp
@@ -0,0 +1,165 @@
+/*
+ * discoverylistener.cpp
+ *
+ * Created on: 25.01.2013
+ * Author: sr
+ */
+
+#include "discoverylistener.h"
+#include "certmanager.h"
+#include "../util/global.h"
+#include "../../shared/settings.h"
+#include "../../shared/network.h"
+#include "../../shared/util.h"
+#include <QCryptographicHash>
+#include <QSslCertificate>
+#include <QSslKey>
+
+#define UDPBUFSIZ 9000
+#define SPAM_CUTOFF 50
+#define SPAM_MODERATE_INTERVAL 6787
+#define SPAM_MODERATE_AT_ONCE 100
+
+// static objects
+
+
+// +++ static ++++ hash ip address +++
+
+static quint16 hash(const QHostAddress& host)
+{
+ static quint16 seed1 = 0, seed2 = 0;
+ while (seed1 == 0) // Make sure the algorithm uses different seeds each time the program is
+ { // run to prevent hash collision attacks
+ seed1 = (quint16)(qrand() & 0xffff);
+ seed2 = (quint16)(qrand() & 0xffff);
+ }
+ quint8 data[16], len;
+ if (host.protocol() == QAbstractSocket::IPv4Protocol)
+ {
+ // IPv4
+ quint32 addr = host.toIPv4Address();
+ len = 4;
+ memcpy(data, &addr, len);
+ }
+ else if (QAbstractSocket::IPv6Protocol)
+ {
+ // IPv6
+ len = 16;
+ // Fast version (might break with future qt versions)
+ memcpy(data, host.toIPv6Address().c, len);
+ /* // --- Safe version (but better try to figure out a new fast way if above stops working ;))
+ Q_IPV6ADDR addr = host.toIPv6Address();
+ for (int i = 0; i < len; ++i)
+ data[i] = addr[i];
+ */
+ }
+ else
+ {
+ // Durr?
+ len = 2;
+ data[0] = (quint16)qrand();
+ data[1] = (quint16)qrand();
+ }
+ quint16 result = 0;
+ quint16 mod = seed1;
+ for (quint8 i = 0; i < len; ++i)
+ {
+ result = ((result << 1) + data[i]) ^ mod; // because of the shift this algo is not suitable for len(input) > 8
+ mod += seed2 + data[i];
+ }
+ return result;
+}
+
+// +++++++++++++++++++++++++++++++++++
+
+DiscoveryListener::DiscoveryListener() :
+ _socket(this), _counterResetPos(0)
+{
+ if (!_socket.bind(SERVICE_DISCOVERY_PORT))
+ qFatal("Could not bind to service discovery port %d", (int)SERVICE_DISCOVERY_PORT);
+ connect(&_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
+ for (int i = 0; i < SD_PACKET_TABLE_SIZE; ++i)
+ _packetCounter[i] = 0;
+ startTimer((SPAM_MODERATE_AT_ONCE * SPAM_MODERATE_INTERVAL) / SD_PACKET_TABLE_SIZE + 1);
+}
+
+DiscoveryListener::~DiscoveryListener()
+{
+ // TODO Auto-generated destructor stub
+}
+
+/**
+ * Overrides
+ */
+
+/**
+ * Decrease packet counters
+ */
+void DiscoveryListener::timerEvent(QTimerEvent* event)
+{
+ for (int i = 0; i < SPAM_MODERATE_AT_ONCE; ++i)
+ {
+ if (++_counterResetPos >= SD_PACKET_TABLE_SIZE)
+ _counterResetPos = 0;
+ if (_packetCounter[_counterResetPos] > 10)
+ _packetCounter[_counterResetPos] -= 10;
+ else if (_packetCounter[_counterResetPos] != 0)
+ _packetCounter[_counterResetPos] = 0;
+ }
+}
+
+/**
+ * Slots
+ */
+
+void DiscoveryListener::onReadyRead()
+{
+ char data[UDPBUFSIZ];
+ QHostAddress addr;
+ quint16 port;
+ while (_socket.hasPendingDatagrams())
+ {
+ const qint64 size = _socket.readDatagram(data, UDPBUFSIZ, &addr, &port);
+ if (size <= 0)
+ continue;
+ const quint16 bucket = hash(addr) % SD_PACKET_TABLE_SIZE;
+ if (_packetCounter[bucket] > SPAM_CUTOFF)
+ {
+ qDebug() << "SD: Potential (D)DoS from " << _socket.peerAddress().toString();
+ // emit some signal and pop up a big warning that someone is flooding/ddosing the PVS SD
+ // ... on the other hand, will the user understand? ;)
+ continue;
+ }
+ ++_packetCounter[bucket];
+ _packet.reset();
+ if (!_packet.readMessage(data, (quint32)size))
+ continue;
+ // Valid packet, process it:
+ const QByteArray iplist(_packet.getFieldBytes(_IPLIST));
+ const QByteArray hash(_packet.getFieldBytes(_HASH));
+ const QByteArray salt1(_packet.getFieldBytes(_SALT1));
+ const QByteArray salt2(_packet.getFieldBytes(_SALT2));
+ // For security, the salt has to be at least 16 bytes long
+ if (salt1.size() < 16 || salt2.size() < 16)
+ continue; // To make this more secure, you could remember the last X salts used, and ignore new packets using the same
+ // 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()))
+ continue;
+ // If so, check if the submitted hash seems valid
+ if (genSha1(&Global::sessionNameArray(), &salt1, &iplist) != hash)
+ continue; // did not match local session name
+ qDebug("Got matching discovery request...");
+ QByteArray myiplist(Network::interfaceAddressesToString().toUtf8());
+ QSslKey key;
+ QSslCertificate cert;
+ CertManager::getPrivateKeyAndCert("manager", key, cert);
+ QByteArray certhash(cert.digest(QCryptographicHash::Sha1));
+ // Reply to client
+ _packet.reset();
+ _packet.setField(_HASH, genSha1(&Global::sessionNameArray(), &salt2, &myiplist, &CLIENT_PORT_ARRAY, &certhash));
+ _packet.setField(_IPLIST, myiplist);
+ _packet.setField(_PORT, CLIENT_PORT_ARRAY);
+ _packet.setField(_CERT, certhash);
+ _packet.writeMessage(&_socket, addr, port);
+ }
+}
diff --git a/src/server/net/discoverylistener.h b/src/server/net/discoverylistener.h
new file mode 100644
index 0000000..64d4351
--- /dev/null
+++ b/src/server/net/discoverylistener.h
@@ -0,0 +1,40 @@
+/*
+ * discoverylistener.h
+ *
+ * Created on: 25.01.2013
+ * Author: sr
+ */
+
+#ifndef DISCOVERYLISTENER_H_
+#define DISCOVERYLISTENER_H_
+
+#include <QtCore>
+#include <QUdpSocket>
+#include "../../shared/networkmessage.h"
+
+#define SD_PACKET_TABLE_SIZE 20000
+
+class DiscoveryListener : public QObject
+{
+ Q_OBJECT
+
+private:
+ QUdpSocket _socket;
+ NetworkMessage _packet;
+ int _counterResetPos;
+
+ quint8 _packetCounter[SD_PACKET_TABLE_SIZE]; // count packets per source address to ignore spammers
+
+protected:
+ void timerEvent(QTimerEvent* event);
+
+public:
+ DiscoveryListener();
+ virtual ~DiscoveryListener();
+
+private slots:
+ void onReadyRead();
+
+};
+
+#endif /* DISCOVERYLISTENER_H_ */
diff --git a/src/server/net/listenserver.cpp b/src/server/net/listenserver.cpp
new file mode 100644
index 0000000..706962d
--- /dev/null
+++ b/src/server/net/listenserver.cpp
@@ -0,0 +1,40 @@
+#include "listenserver.h"
+#include "client.h"
+#include <QSslSocket>
+
+#define MAX_CLIENTS 50
+
+ListenServer::ListenServer(quint16 port)
+{
+ if (!_server.listen(QHostAddress::Any, port) || !_server.isListening())
+ qFatal("Cannot bind to TCP port %d (incoming SSL clients)", (int)port);
+ connect(&_server, SIGNAL(newConnection()), this, SLOT(newClientConnection()));
+}
+
+ListenServer::~ListenServer()
+{
+ _server.close();
+ for (int i = 0; i < _clients.size(); ++i)
+ _clients[i]->deleteLater();
+}
+
+/**
+ * Slots
+ */
+
+void ListenServer::newClientConnection()
+{
+ QSslSocket* sock;
+ while ((sock = (QSslSocket*)_server.nextPendingConnection()) != NULL)
+ {
+ if (_clients.size() >= MAX_CLIENTS)
+ {
+ sock->abort();
+ sock->deleteLater();
+ continue;
+ }
+ Client* client = new Client(sock);
+ _clients.append(client); // create new client class and add to list
+ emit newClient(client);
+ }
+}
diff --git a/src/server/net/listenserver.h b/src/server/net/listenserver.h
new file mode 100644
index 0000000..f237703
--- /dev/null
+++ b/src/server/net/listenserver.h
@@ -0,0 +1,30 @@
+#ifndef LISTENSERVER_H_
+#define LISTENSERVER_H_
+
+#include <QtCore>
+#include <QList>
+#include "sslserver.h"
+
+class Client;
+
+class ListenServer : public QObject
+{
+ Q_OBJECT
+
+private:
+ SslServer _server;
+ QList<Client*> _clients;
+
+public:
+ explicit ListenServer(quint16 port);
+ virtual ~ListenServer();
+
+private slots:
+ void newClientConnection();
+
+signals:
+ void newClient(Client* client);
+
+};
+
+#endif /* LISTENSERVER_H_ */
diff --git a/src/server/net/sslserver.cpp b/src/server/net/sslserver.cpp
new file mode 100644
index 0000000..70daea4
--- /dev/null
+++ b/src/server/net/sslserver.cpp
@@ -0,0 +1,113 @@
+/*
+ # 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/SslServer.cpp
+ # - provide QTcpServer-like behaviour for SSL
+ # -----------------------------------------------------------------------------
+ */
+
+#include "sslserver.h"
+#include <QtNetwork/QSslCipher>
+#include <QtNetwork/QSslSocket>
+#include "certmanager.h"
+
+SslServer::SslServer()
+{
+ _tmr = startTimer(5123);
+ //QSslSocket::setDefaultCiphers(QSslSocket::supportedCiphers());
+}
+
+SslServer::~SslServer()
+{
+ killTimer((_tmr));
+}
+
+void SslServer::incomingConnection(int socketDescriptor)
+{
+ QSslSocket *serverSocket = new QSslSocket(NULL);
+ connect(serverSocket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(sslErrors(const QList<QSslError> &)));
+ QSslKey key;
+ QSslCertificate cert;
+ CertManager::getPrivateKeyAndCert("manager", key, cert);
+ serverSocket->setPrivateKey(key);
+ serverSocket->setLocalCertificate(cert);
+ serverSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
+ serverSocket->setProtocol(QSsl::SslV3);
+ //printf("Keylen %d\n", serverSocket->privateKey().length());
+ if (serverSocket->setSocketDescriptor(socketDescriptor))
+ {
+ // Once the connection is successfully encrypted, raise our newConnection event
+ connect(serverSocket, SIGNAL(encrypted()), this, SIGNAL(newConnection()));
+ serverSocket->startServerEncryption();
+ _pending.push_back(serverSocket);
+ }
+ else
+ {
+ serverSocket->deleteLater();
+ }
+}
+
+void SslServer::sslErrors(const QList<QSslError> & errors)
+{
+ //qDebug("FIXME: SSL ERRORS on SERVER: %s", qPrintable(errors.begin()->errorString()));
+}
+
+void SslServer::timerEvent(QTimerEvent* event)
+{
+ // Remove all sockets marked for deletion
+ while (!_delete.isEmpty())
+ {
+ QSslSocket *sock = _delete.takeFirst();
+ sock->blockSignals(true);
+ sock->deleteLater();
+ }
+ _delete = _pending;
+ _pending.clear();
+}
+
+bool SslServer::hasPendingConnections()
+{
+ for (QList<QSslSocket*>::iterator it(_pending.begin()); it != _pending.end(); it++)
+ {
+ qDebug("State: %d - Encrypted: %d", (int)(*it)->state(), (*it)->isEncrypted());
+ if ((*it)->state() == QAbstractSocket::ConnectedState && (*it)->isEncrypted())
+ return true;
+ }
+ return false;
+}
+
+QTcpSocket* SslServer::nextPendingConnection()
+{
+ for (QList<QSslSocket*>::iterator it(_pending.begin()); it != _pending.end(); it++)
+ {
+ if ((*it)->state() == QAbstractSocket::ConnectedState && (*it)->isEncrypted())
+ {
+ QSslSocket *sock = *it;
+ QObject::disconnect(sock, SIGNAL(encrypted()), this, SIGNAL(newConnection()));
+ _pending.removeAll(sock);
+ _delete.removeAll(sock);
+ return sock;
+ }
+ }
+ for (QList<QSslSocket*>::iterator it(_delete.begin()); it != _delete.end(); it++)
+ {
+ if ((*it)->state() == QAbstractSocket::ConnectedState && (*it)->isEncrypted())
+ {
+ QSslSocket *sock = *it;
+ QObject::disconnect(sock, SIGNAL(encrypted()), this, SIGNAL(newConnection()));
+ _pending.removeAll(sock);
+ _delete.removeAll(sock);
+ return sock;
+ }
+ }
+ return NULL;
+}
+
diff --git a/src/server/net/sslserver.h b/src/server/net/sslserver.h
new file mode 100644
index 0000000..400bf6a
--- /dev/null
+++ b/src/server/net/sslserver.h
@@ -0,0 +1,50 @@
+/*
+# 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/SslServer.cpp
+# - provide QTcpServer-like behaviour for SSL
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef SSLSERVER_H_
+#define SSLSERVER_H_
+
+#include <QtCore/QList>
+#include <QtNetwork/QTcpServer>
+#include <QtNetwork/QSslError>
+
+class QSslSocket;
+
+class SslServer : public QTcpServer
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void sslErrors ( const QList<QSslError> & errors );
+
+public:
+ SslServer();
+ virtual ~SslServer();
+
+ bool hasPendingConnections ();
+ // This one has to return a TcpSocket as we're overwriting from the base class
+ // just cast it to QSslSocket later
+ virtual QTcpSocket* nextPendingConnection();
+
+protected:
+ void incomingConnection(int socketDescriptor);
+ void timerEvent (QTimerEvent* event);
+ QList<QSslSocket*> _pending;
+ QList<QSslSocket*> _delete;
+ int _tmr;
+};
+
+#endif /* SSLSERVER_H_ */