diff options
author | sr | 2013-02-04 19:50:31 +0100 |
---|---|---|
committer | sr | 2013-02-04 19:50:31 +0100 |
commit | 1a5709501f94014d41987b956338bb6424b9f90c (patch) | |
tree | d3b93fe8dc406bca56aff147ef5cc4cbf9ed6be0 /src/server/net | |
parent | Test (diff) | |
download | pvs2-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.cpp | 92 | ||||
-rw-r--r-- | src/server/net/certmanager.h | 29 | ||||
-rw-r--r-- | src/server/net/client.cpp | 288 | ||||
-rw-r--r-- | src/server/net/client.h | 104 | ||||
-rw-r--r-- | src/server/net/discoverylistener.cpp | 165 | ||||
-rw-r--r-- | src/server/net/discoverylistener.h | 40 | ||||
-rw-r--r-- | src/server/net/listenserver.cpp | 40 | ||||
-rw-r--r-- | src/server/net/listenserver.h | 30 | ||||
-rw-r--r-- | src/server/net/sslserver.cpp | 113 | ||||
-rw-r--r-- | src/server/net/sslserver.h | 50 |
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_ */ |