diff options
author | Sebastian | 2010-05-12 19:42:27 +0200 |
---|---|---|
committer | Sebastian | 2010-05-12 19:42:27 +0200 |
commit | ce3329047d378a14006ce74ec273ac59e3375303 (patch) | |
tree | 782430f270b4c7aca1b35d5b7813518e3797c555 /src/net | |
download | pvs-ce3329047d378a14006ce74ec273ac59e3375303.tar.gz pvs-ce3329047d378a14006ce74ec273ac59e3375303.tar.xz pvs-ce3329047d378a14006ce74ec273ac59e3375303.zip |
initial import of latest svn version
Diffstat (limited to 'src/net')
-rw-r--r-- | src/net/SslServer.cpp | 130 | ||||
-rw-r--r-- | src/net/SslServer.h | 50 | ||||
-rw-r--r-- | src/net/pvsClientConnection.cpp | 178 | ||||
-rw-r--r-- | src/net/pvsClientConnection.h | 67 | ||||
-rw-r--r-- | src/net/pvsDiscoveredServer.cpp | 152 | ||||
-rw-r--r-- | src/net/pvsDiscoveredServer.h | 63 | ||||
-rw-r--r-- | src/net/pvsListenServer.cpp | 308 | ||||
-rw-r--r-- | src/net/pvsListenServer.h | 106 | ||||
-rw-r--r-- | src/net/pvsLocalhostCommunicator.cpp | 104 | ||||
-rw-r--r-- | src/net/pvsLocalhostCommunicator.h | 51 | ||||
-rw-r--r-- | src/net/pvsMsg.cpp | 207 | ||||
-rw-r--r-- | src/net/pvsMsg.h | 73 | ||||
-rw-r--r-- | src/net/pvsServerConnection.cpp | 243 | ||||
-rw-r--r-- | src/net/pvsServerConnection.h | 99 | ||||
-rw-r--r-- | src/net/pvsServiceBroadcast.cpp | 51 | ||||
-rw-r--r-- | src/net/pvsServiceBroadcast.h | 46 | ||||
-rw-r--r-- | src/net/pvsServiceDiscovery.cpp | 181 | ||||
-rw-r--r-- | src/net/pvsServiceDiscovery.h | 57 |
18 files changed, 2166 insertions, 0 deletions
diff --git a/src/net/SslServer.cpp b/src/net/SslServer.cpp new file mode 100644 index 0000000..e353e0a --- /dev/null +++ b/src/net/SslServer.cpp @@ -0,0 +1,130 @@ +/* +# 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 "src/util/CertManager.h" + + +SslServer::SslServer() +{ + _tmr = startTimer(7000); + //QSslSocket::setDefaultCiphers(QSslSocket::supportedCiphers()); +} + +SslServer::~SslServer() +{ + killTimer((_tmr)); +} + +void SslServer::incomingConnection(int socketDescriptor) +{ + QSslSocket *serverSocket = new QSslSocket(this); + connect(serverSocket, + SIGNAL(sslErrors(const QList<QSslError> &)), + this, + SLOT(sslErrors(const QList<QSslError> &)) + ); + serverSocket->setPrivateKey(CertManager::getPrivateKey("manager")); + serverSocket->setLocalCertificate(CertManager::getCertificate("manager")); + 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 out newConnection event + connect(serverSocket, SIGNAL(encrypted()), this, SIGNAL(newConnection())); + serverSocket->startServerEncryption(); + _pending.push_back(serverSocket); + } + else + { + delete serverSocket; + } +} + +void SslServer :: sslErrors ( const QList<QSslError> & errors ) +{ + printf("FIXME: SSL ERRORS on SERVER: %s\n", errors.begin()->errorString().toUtf8().data()); +} + +void SslServer::timerEvent (QTimerEvent* event) +{ + // Remove all sockets marked for deletion + for (QList<QSslSocket*>::iterator it = _delete.begin(); it != _delete.end(); it++) + { + (*it)->deleteLater(); + } + _delete.clear(); + // Mark all sockets for deletion where the ssl handshake did not happen after connecting + /* + bool flag; + do + { + flag = false; + for (QList<QSslSocket*>::iterator it = _pending.begin(); it != _pending.end(); it++) + { + if ((*it)->state() != QAbstractSocket::ConnectedState || !(*it)->isEncrypted()) + { + _delete.push_back(*it); + _pending.removeAll(*it); + flag = true; + break; + } + + } + } + while (flag); + */ + _delete.append(_pending); + _pending.clear(); +} + +bool SslServer::hasPendingConnections() +{ + for (QList<QSslSocket*>::iterator it = _pending.begin(); it != _pending.end(); it++) + { + printf("State: %d - Encrypted: %d\n", (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()) + { + _pending.removeAll(*it); + _delete.removeAll(*it); + return *it; + } + } + for (QList<QSslSocket*>::iterator it = _delete.begin(); it != _delete.end(); it++) + { + if ((*it)->state() == QAbstractSocket::ConnectedState && (*it)->isEncrypted()) + { + _pending.removeAll(*it); + _delete.removeAll(*it); + return *it; + } + } + return NULL; +} + + diff --git a/src/net/SslServer.h b/src/net/SslServer.h new file mode 100644 index 0000000..b1efeea --- /dev/null +++ b/src/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(); + + virtual 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: + virtual void incomingConnection(int socketDescriptor); + void timerEvent (QTimerEvent* event); + QList<QSslSocket*> _pending; + QList<QSslSocket*> _delete; + int _tmr; +}; + +#endif /* SSLSERVER_H_ */ diff --git a/src/net/pvsClientConnection.cpp b/src/net/pvsClientConnection.cpp new file mode 100644 index 0000000..dff40d5 --- /dev/null +++ b/src/net/pvsClientConnection.cpp @@ -0,0 +1,178 @@ +/* +# Copyright (c) 2009,2010 - 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/ +# ----------------------------------------------------------------------------- +# pvsClientConnection.cpp +# - ???. +# ----------------------------------------------------------------------------- +*/ + +#include "pvsClientConnection.h" +#include "pvsListenServer.h" +#include "src/util/consoleLogger.h" + +#include <cassert> +#include <QtNetwork/QHostAddress> + + +PVSClientConnection::PVSClientConnection(PVSListenServer* server, QSslSocket* sock) +{ + assert(sock); + assert(server); + _socket = sock; + _incomplete = NULL; + _toldDisconnect = false; + _server = server; + _lastData = time(NULL); + sock->setParent(this); // for automatic deletion + connect(_socket, SIGNAL(readyRead()), this, SLOT(sock_dataArrival())); + connect(_socket, SIGNAL(disconnected()), this, SLOT(sock_closed())); + connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sock_error(QAbstractSocket::SocketError))); +} + +PVSClientConnection::~PVSClientConnection() +{ + this->closeConnection(); + if (_incomplete != NULL) delete _incomplete; + if (_socket != NULL) + { + disconnect(_socket, SIGNAL(readyRead()), this, SLOT(sock_dataArrival())); + disconnect(_socket, SIGNAL(disconnected()), this, SLOT(sock_closed())); + disconnect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sock_error(QAbstractSocket::SocketError))); + _socket->abort(); + } +} + +void PVSClientConnection::closeConnection() +{ + if (_toldDisconnect) return; // we already got served + if (_socket == NULL || _socket->state() == QAbstractSocket::UnconnectedState) + { + // should not happen but handled anyway + _toldDisconnect = true; + _server->disconnectClient(this); + return; + } + if (_socket->state() == QAbstractSocket::ClosingState) return; // sock_close() slot should eventually be called, nothing to do + if (_socket->state() == QAbstractSocket::ConnectedState) // this is what should be the case normally + { + _socket->disconnectFromHost(); // graceful shutdown of socket, sock_close() will be called once complete + return; + } + // otherwise the socket is in some unknown state, so do a harsh shutdown + _socket->abort(); + _toldDisconnect = true; + _server->disconnectClient(this); +} + +void PVSClientConnection::sock_dataArrival() +{ + if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState) + { + ConsoleLog writeError("dataArrival called in bad state"); + return; + } + + _lastData = time(NULL); + while (_socket->bytesAvailable()) + { + int retval = 0; + do + { + if (_incomplete == NULL) _incomplete = new PVSMsg(); // we need a pvsmsg object + retval = _incomplete->readMessage(_socket); // let the message read data from socket + if (retval == -1) // error parsing msg, disconnect client! + { + this->closeConnection(); + return; + } + if (retval == 1) // message is complete + { + _server->handleClientMsg(_id, *_incomplete); + delete _incomplete; // ...and delete... + _incomplete = NULL; // ...so the next msg can be parsed + } + } while (retval == 1); + } +} + +void PVSClientConnection::sock_closed() +{ + // should this be unreliable in some way i suggest using the signal "stateChanged()" instead + // and check if the state changed to unconnected. + if (_toldDisconnect) return; + _toldDisconnect = true; + _server->disconnectClient(this); +} + +void PVSClientConnection::sock_error(QAbstractSocket::SocketError errcode) +{ + this->closeConnection(); +} + +/* +void PVSClientConnection::push_back_receive(PVSMsg newMsg) +{ + _recQueue.push_back(newMsg); +} +*/ + +bool PVSClientConnection::push_back_send(PVSMsg newMsg) +{ + if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState) return false; + newMsg.setSndID(_id); + int len; + char data[2000]; + char *tmp = data; + if (!newMsg.getBinaryData(tmp, len)) + { + printf("Message empty. Ignored.\n"); + return false; + } + QByteArray ba; + ba.append(tmp, len); + int ret = (int)_socket->write(ba); + //printf("Sent %d of %d bytes.\n", ret, len); + if (ret == -1) + { + this->closeConnection(); + return false; + } + return true; +} + +QSslSocket* PVSClientConnection::getSocket() +{ + return _socket; +} + +void PVSClientConnection::setID(unsigned int id) +{ + _id = id; + this->push_back_send(PVSMsg(PVSLOGIN, "ID", int2String(_id))); +} + +void PVSClientConnection::ping() +{ + // still needs flag/timer to check for a ping timeout (and a reply from the client) + this->push_back_send(PVSMsg(PVSCOMMAND, "PING", "HELLO?", _id)); +} + +QString PVSClientConnection::getAddress() +{ + if (_socket == NULL) return QString(); + return _socket->peerAddress().toString(); +} + +QString PVSClientConnection::getNameUser() +{ + return getUserName(); +} diff --git a/src/net/pvsClientConnection.h b/src/net/pvsClientConnection.h new file mode 100644 index 0000000..cfbf98e --- /dev/null +++ b/src/net/pvsClientConnection.h @@ -0,0 +1,67 @@ +/** + * PVSClientConnection + * + * holds and maintains a Connection made from/to a PVSServerConnection (or derivate) + * PVSClientConnections are created by the PVSListenServer if a PVSCLientConnection is accepted + * + */ + +#include "pvsMsg.h" + +#include <QtNetwork/QSslSocket> + + +#ifndef _PVSCLIENTCONNECTION_H_ +#define _PVSCLIENTCONNECTION_H_ + +class PVSListenServer; + +class PVSClientConnection : public QObject +{ + Q_OBJECT + +public: + //C'Tor + PVSClientConnection(PVSListenServer* server, QSslSocket* sock = NULL); + // D'Tor + ~PVSClientConnection(); + + void closeConnection(); + bool isConnected() + { + return _socket != NULL && _socket->state() == QAbstractSocket::ConnectedState; + } + time_t lastData() + { + return _lastData; + } + + //void push_back_receive(PVSMsg newMsg); + bool push_back_send(PVSMsg newMsg); + QSslSocket* getSocket(); + + int getID() { return _id; } + void setID(unsigned int id); + void setServerID(unsigned int id) { _serverId = id; } + + QString getAddress(); + void setUsername(QString username); + QString getNameUser(); + void ping(); + +private Q_SLOTS: + void sock_dataArrival(); + void sock_closed(); + void sock_error(QAbstractSocket::SocketError errcode); + +private: + QSslSocket *_socket; + int _id; + unsigned int _serverId; + PVSMsg *_incomplete; + PVSListenServer *_server; + bool _toldDisconnect; + time_t _lastData; +}; + +#endif diff --git a/src/net/pvsDiscoveredServer.cpp b/src/net/pvsDiscoveredServer.cpp new file mode 100644 index 0000000..dddb5a0 --- /dev/null +++ b/src/net/pvsDiscoveredServer.cpp @@ -0,0 +1,152 @@ +/* +# 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/pvsDiscoveredServer.cpp +# - represents an entry in the list of available servers +# - handles some extra things like validating the identity of the remote host +# ----------------------------------------------------------------------------- +*/ + +#include "pvsDiscoveredServer.h" +#include "src/util/serviceDiscoveryUtil.h" +#include <QtNetwork/QSslKey> + +PVSDiscoveredServer::PVSDiscoveredServer(QObject* parent, QHostAddress host, int port, QByteArray fingerprint, QString name) + : QObject(parent) +{ + _validated = false; + _socket = NULL; + _host = host; + _port = port; + _fingerprint = fingerprint; + _name = name; + _lastUpdate = QDateTime::currentDateTime(); + _lastCheck = QDateTime::fromTime_t(1000000); + this->validateCertificate(); +} + +PVSDiscoveredServer::~PVSDiscoveredServer() { + delete _socket; +} + +void PVSDiscoveredServer::ssl_Error( const QList<QSslError> & errors ) +{ + for (QList<QSslError>::const_iterator it = errors.begin(); it != errors.end(); it++) + { + QSslError err = *it; + if (err.error() == QSslError::HostNameMismatch) continue; // We don't pay attention to hostnames for validation + if (err.error() == QSslError::SelfSignedCertificate) { + continue; // Also, this will always be the case; we check the fingerprint later + } + //ConsoleLog writeNetwork(err.errorString().toUtf8().data()); + //ConsoleLog writeNetwork("***** SSL ERROR, ABORTING *****"); + printf("Unhandled SSL Error in SD: %s\n", err.errorString().toUtf8().data()); + return; + } + _socket->ignoreSslErrors(); +} + +void PVSDiscoveredServer::sock_dataArrival() +{ + if (_socket == NULL) return; + + char charbuff[2000]; + while (_socket->bytesAvailable()) + { + _socket->read(charbuff, 2000); + } +} + +void PVSDiscoveredServer::sock_connected() +{ + QByteArray cert = _socket->peerCertificate().digest(QCryptographicHash::Sha1); + if (_socket->peerCertificate().isNull()) + { + printf("**** WARNING - PEER CERTIFICATE IS NULL ****\n"); + } + else + { + printf("%s\n", _socket->peerCertificate().subjectInfo(QSslCertificate::Organization).toUtf8().data()); + printf("%s\n", _socket->peerCertificate().subjectInfo(QSslCertificate::CommonName).toUtf8().data()); + printf("%s\n", _socket->peerCertificate().subjectInfo(QSslCertificate::LocalityName).toUtf8().data()); + printf("%s\n", _socket->peerCertificate().subjectInfo(QSslCertificate::OrganizationalUnitName ).toUtf8().data()); + printf("%s\n", _socket->peerCertificate().subjectInfo(QSslCertificate::CountryName ).toUtf8().data()); + printf("%s\n", _socket->peerCertificate().subjectInfo(QSslCertificate::StateOrProvinceName ).toUtf8().data()); + } + if (cert == _fingerprint && !_validated) + { + _validated = true; + emit validated(this); + printf("Validated certificate of %s :)\n", _socket->peerAddress().toString().toUtf8().data()); + } + else + { + printf("Certificate of %s is invalid :(\n", _socket->peerAddress().toString().toUtf8().data()); + QByteArray is, should; + is = cert.toBase64(); + should = _fingerprint.toBase64(); + printf("Is %s and should be %s\n", is.data(), should.data()); + } + _socket->disconnectFromHost(); +} + +void PVSDiscoveredServer::validateCertificate() +{ + if (_validated) return; // Nothing to do, this one is legit + QDateTime now = QDateTime::currentDateTime(); + if (_lastCheck.secsTo(now) < 30) return; // Too soon! + if (_host.isNull() || _port < 1 || _port > 65535) return; // Invalid + if (_socket != NULL) { + disconnect(_socket, SIGNAL(connected()), this, SLOT(sock_connected())); + disconnect(_socket, SIGNAL(readyRead()), this, SLOT(sock_dataArrival())); + disconnect(_socket, SIGNAL(sslErrors(const QList<QSslError> &)), + this, SLOT(ssl_Error(const QList<QSslError> &)) + ); + _socket->abort(); + _socket->deleteLater(); + } + _socket = new QSslSocket(this); + _socket->setProtocol(QSsl::SslV3); + _socket->setPeerVerifyMode(QSslSocket::VerifyPeer); + connect(_socket, SIGNAL(encrypted()), this, SLOT(sock_connected())); + connect(_socket, SIGNAL(readyRead()), this, SLOT(sock_dataArrival())); + connect(_socket, SIGNAL(sslErrors(const QList<QSslError> &)), + this, SLOT(ssl_Error(const QList<QSslError> &)) + ); + _socket->connectToHostEncrypted(_host.toString(), _port); + _lastCheck = now; +} + +bool PVSDiscoveredServer::hasFingerprint(QByteArray &fingerprint) +{ + return _fingerprint == fingerprint; +} + +bool PVSDiscoveredServer::hasHost(QHostAddress &host) +{ + return _host == host; +} + +void PVSDiscoveredServer::update(int port) +{ + _port = port; + _lastUpdate = QDateTime::currentDateTime(); +} + +int PVSDiscoveredServer::getAge() +{ + return _lastUpdate.secsTo(QDateTime::currentDateTime()); +} + +QString PVSDiscoveredServer::getName() +{ + return _name; +} diff --git a/src/net/pvsDiscoveredServer.h b/src/net/pvsDiscoveredServer.h new file mode 100644 index 0000000..d9f0c01 --- /dev/null +++ b/src/net/pvsDiscoveredServer.h @@ -0,0 +1,63 @@ +/* +# 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/pvsDiscoveredServer.h +# - represents an entry in the list of available servers +# - handles some extra things like validating the identity of the remote host +# ----------------------------------------------------------------------------- +*/ + +#ifndef PVSDISCOVEREDSERVER_H_ +#define PVSDISCOVEREDSERVER_H_ + +#include <QtNetwork/QSslSocket> +#include <QtNetwork/QHostAddress> +#include <QtCore/QDateTime> + +class PVSDiscoveredServer; + +class PVSDiscoveredServer : public QObject { + Q_OBJECT + +private: + QHostAddress _host; + int _port; + QByteArray _fingerprint; + bool _validated; + QSslSocket *_socket; + QDateTime _lastCheck; + QDateTime _lastUpdate; + QString _name; + +public: + PVSDiscoveredServer(QObject* parent, QHostAddress host, int port, QByteArray fingerprint, QString name); + virtual ~PVSDiscoveredServer(); + bool isValid() { return _validated; } + bool hasFingerprint(QByteArray &fingerprint); + bool hasHost(QHostAddress &host); + void update(int port); + int getAge(); + QString getName(); + QHostAddress getHost() { return _host; } + QByteArray getFingerprint() { return _fingerprint; } + int getPort() { return _port; } + void validateCertificate(); + +private Q_SLOTS: + void ssl_Error( const QList<QSslError> & errors ); + void sock_dataArrival(); + void sock_connected(); + +Q_SIGNALS: + void validated(PVSDiscoveredServer*); +}; + +#endif /* PVSDISCOVEREDSERVER_H_ */ diff --git a/src/net/pvsListenServer.cpp b/src/net/pvsListenServer.cpp new file mode 100644 index 0000000..1dac9c0 --- /dev/null +++ b/src/net/pvsListenServer.cpp @@ -0,0 +1,308 @@ +/* + # Copyright (c) 2009,2010 - 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/pvsListenServer.cpp + # - handling of clients (server side) + # - accepting new connections etc. + # ----------------------------------------------------------------------------- + */ + +#include "pvsListenServer.h" +#include "pvsMsg.h" +#include "pvsClientConnection.h" +#include "src/util/consoleLogger.h" +#include <QtNetwork/QSslSocket> +#include "SslServer.h" +#include <cassert> +//#define verbose + +// Create listener +PVSListenServer::PVSListenServer(int port, int clients) +{ + _id = generateID(); + _listenSocket = NULL; + _timer = 0; + if (clients < 1) + _clientsMax = 100; + else + _clientsMax = clients; + _port = port; + init(); + +} + +PVSListenServer::~PVSListenServer() +{ + if (_listenSocket != NULL) + { + disconnect(_listenSocket, SIGNAL(newConnection()), this, SLOT( + server_connectionRequest())); + _listenSocket->deleteLater(); + } +} + +PVSClientConnection* PVSListenServer::getConnectionFromId(int id) +{ + if (!(_clients.empty())) + { + // Check for ping timeout + for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it + != _clients.end(); it++) + { + PVSClientConnection *client = *it; + if (client) + if (client->getID() == id) + return client; + } + } + return NULL; +} + +void PVSListenServer::sendToAll(PVSMsg msg) +{ + if (!(_clients.empty())) + { + // Check for ping timeout + for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it + != _clients.end(); it++) + { + PVSClientConnection *client = *it; + if (client) + client->push_back_send(msg); + } + } +} + +// listen on a specific port +bool PVSListenServer::startListen(int port) +{ + if (_listenSocket != NULL) + { + ConsoleLog writeError( + QString("Server already listening on port :").append( + int2String(_port))); + if (shutdown()) + { + ConsoleLog writeError(QString("Server shutdown failed!")); + return false; + } + } + _port = port; + return init(); +} + +// stop listening and free socket descriptor +bool PVSListenServer::shutdown() +{ + if (_timer != 0) + killTimer( _timer); + _timer = 0; + if (_listenSocket != NULL) + { + disconnect(_listenSocket, SIGNAL(newConnection()), this, SLOT( + server_connectionRequest())); + _listenSocket->close(); + _listenSocket->deleteLater(); + } + _listenSocket = NULL; + _fresh = true; + return false; +} + +// timer event, 5 second interval +void PVSListenServer::timerEvent(QTimerEvent *event) +{ + if (!(_clients.empty())) + { + // Check for ping timeout + time_t refval = time(NULL) - 10; + for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it + != _clients.end(); it++) + { + PVSClientConnection *client = *it; + client->ping(); + if (!client->isConnected()) + { // disconnected + if (disconnectClient(client)) + break; // list was modified, iterator not valid anymore + } + if (client->lastData() < refval) + { // ping timeout + printf("Ping timeout for client %s\n", client->getNameUser().toLocal8Bit().data()); + if (disconnectClient(client)) + break; // list was modified, iterator not valid anymore + } + } + } +} + +// remove a client +bool PVSListenServer::disconnectClient(PVSClientConnection* delinquent) +{ + assert(delinquent); + delinquent->deleteLater(); + if (!(_clients.empty())) + { + for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it + != _clients.end(); it++) + { + if ((*it) == delinquent) + { + (*it)->closeConnection(); + _clients.remove((*it)); + onClientDisconnected(delinquent); + return true; + } + } + } + return false; +} + +// All sorts of events: + +void PVSListenServer::onConnectionRemoved(PVSClientConnection* delinquent) +{ + +} + +void PVSListenServer::onClientConnected(PVSClientConnection* connected) +{ + connected->setServerID(_id); + connected->setID(generateID()); +} + +void PVSListenServer::onClientDisconnected(PVSClientConnection* disconnected) +{ + // +} + +// Get client class for a given socket +PVSClientConnection* PVSListenServer::getConnectionFromSocket(QSslSocket* sock) +{ + for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it + != _clients.end(); it++) + { + if ((*it)->getSocket() == sock) // match! + return (*it); + } + return NULL; +} + +// generate a random (unique) id +unsigned int PVSListenServer::generateID() +{ + int random_integer; + do + { + random_integer = getRandom(0, 0); + } + while (getConnectionFromID(random_integer)); + + return (unsigned int) random_integer; +} + +// Get client class for a given id +PVSClientConnection* PVSListenServer::getConnectionFromID(int id) +{ + for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it + != _clients.end(); it++) + { + if ((*it)->getID() == id || id == _id) + { + return (*it); + } + } + return NULL; +} + +// Initialize listening socket +bool PVSListenServer::init() +{ + if (_listenSocket != NULL) + shutdown(); + + if (_port > 0) + { + // make a socket, bind it, and listen on it: + _listenSocket = new SslServer; + QHostAddress addr(QHostAddress::Any); + _listenSocket->listen(addr, _port); + + if (!_listenSocket->isListening()) + { + ConsoleLog writeNetwork( + QString("Error listening on Port ").append(int2String( + _port))); + return false; + } + ConsoleLog writeNetwork( + QString("Server is listening. Socket: ").append(int2String( + (int) _listenSocket))); + connect(_listenSocket, SIGNAL(newConnection()), this, SLOT( + server_connectionRequest())); + + _fresh = true; + if (_timer != 0) + killTimer( _timer); + _timer = startTimer(5000); + return true; + } + return false; +} + +void PVSListenServer::server_connectionRequest() +{ + //printf("ConnReq\n"); + if (_listenSocket == NULL) + return; // o.O + while (_listenSocket->hasPendingConnections()) + { + QSslSocket* sock = (QSslSocket*)_listenSocket->nextPendingConnection(); + //printf("Got one: %d: %s\n", (int)sock->isEncrypted(), sock->peerAddress().toString().toUtf8().data()); + if (_clients.size() >= _clientsMax) + { + sock->abort(); + sock->deleteLater(); + continue; + } + PVSClientConnection* client = new PVSClientConnection(this, sock); + _clients.push_back(client); // create new client class and add to list + onClientConnected(client); // trigger event + _fresh = false; + } +} + +void PVSListenServer::handleClientMsg(unsigned int clientID, PVSMsg msg) +{ + printf("Got Message for client %ud: [%c][%s][%s]\n", clientID, + (char) msg.getType(), msg.getIdent().toUtf8().data(), + msg.getMessage().toUtf8().data()); + msg.setSndID(clientID); + if (msg.getType() == PVSCOMMAND) + _commandDispatcher.fire(msg.getIdent(), msg); + else if (msg.getType() == PVSMESSAGE) + _chatDispatcher.fire(msg.getIdent(), msg); + else if (msg.getType() == PVSLOGIN) + _loginDispatcher.fire(msg.getIdent(), msg); + else + ConsoleLog + writeNetwork( + QString( + "Could not dispatch Message of unknown Type.\n\t Message was: ").append( + msg.getIdent().append(QString(":").append( + msg.getMessage())))); +} + +bool PVSListenServer::isListening() +{ + return _listenSocket != NULL && _listenSocket->isListening(); +} diff --git a/src/net/pvsListenServer.h b/src/net/pvsListenServer.h new file mode 100644 index 0000000..ab021c7 --- /dev/null +++ b/src/net/pvsListenServer.h @@ -0,0 +1,106 @@ +#ifndef _PVSLISTENSERVER_H_ +#define _PVSLISTENSERVER_H_ +#include <list> +#include <src/util/dispatcher.h> +#include <src/util/consoleLogger.h> +#include <QtNetwork/QSslSocket> +//#include <QtNetwork/QTcpServer> + +class SslServer; + +class PVSClientConnection; +class PVSMsg; + +class PVSListenServer : public QObject +{ + Q_OBJECT + +private: + + // internal control functions + + std::list<PVSClientConnection*> _clients; + + + int _numCon; + bool _fresh; + unsigned int _clientsMax; + int _port; + + //QTcpServer *_listenSocket; + SslServer *_listenSocket; + + int _id; + EventIdentDispatcher<PVSMsg> _loginDispatcher; + EventIdentDispatcher<PVSMsg> _commandDispatcher; + EventIdentDispatcher<PVSMsg> _chatDispatcher; + int _timer; + + // obvious init method is obvious + bool init(); + unsigned int generateID(); + +protected: + void timerEvent(QTimerEvent *event); + +public: + + +// typedef void (A::*function)(void); + // C'Tor + explicit PVSListenServer(int port = 0, int clients = 0); + virtual ~PVSListenServer(); + + PVSClientConnection* getConnectionFromId(int id); + void sendToAll(PVSMsg msg); + // control methods + bool startListen(int port); + bool shutdown(); + bool isListening(); + bool disconnectClient(PVSClientConnection* delinquent); + void onConnectionRemoved(PVSClientConnection* delinquent); + + std::list<PVSClientConnection*>* getClientListPtr() + { + return &_clients; + } + // methods which can/should be overwritten by derivates + virtual void onClientConnected(PVSClientConnection* connected); // you can hook up additional services on client connection here + virtual void onClientDisconnected(PVSClientConnection* disconnected); // deletes the client, but you can hook up stuff here too + + void handleClientMsg(unsigned int clientID, PVSMsg msg); ///< called by a clientconnection if a msg was received + + + template<class T> void addLoginHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + printf("Listener got added to LoginHandler\n"); + _loginDispatcher.addListener(ident, who, func); + }; + template<class T> void removeLoginHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + _loginDispatcher.removeListener(ident, who, func); + }; + template<class T> void addChatHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + _chatDispatcher.addListener(ident, who, func); + }; + template<class T> void removeChatHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + _chatDispatcher.removeListener(ident, who, func); + }; + template<class T> void addCommandHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + _commandDispatcher.addListener(ident, who, func); + }; + template<class T> void removeCommandHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + _commandDispatcher.removeListener(ident, who, func); + }; + PVSClientConnection* getConnectionFromSocket(QSslSocket* sock); + PVSClientConnection* getConnectionFromID(int id); + +private Q_SLOTS: + void server_connectionRequest(); +}; + +#endif diff --git a/src/net/pvsLocalhostCommunicator.cpp b/src/net/pvsLocalhostCommunicator.cpp new file mode 100644 index 0000000..199b39c --- /dev/null +++ b/src/net/pvsLocalhostCommunicator.cpp @@ -0,0 +1,104 @@ +#include "pvsLocalhostCommunicator.h" +#include "src/util/consoleLogger.h" +#include "src/net/pvsMsg.h" + +static QHostAddress localhost(QHostAddress::LocalHost); +#define localport 14913 + +PVSLocalhostCommunicator::PVSLocalhostCommunicator(QString path) +{ + _isServer = _isRunning = false; + _sock = NULL; +} + +void PVSLocalhostCommunicator::run() +{ + if (_isRunning) + return; + + // First try to run as server + _sock = new QUdpSocket(); + // We'll bind only to localhost, so nobody else can send commands (from remote hosts) + // Otherwise we had to check the remote addr when receiving a datagram, but this + // wouldn't be as secure since you can easily fake it + _sock->bind(localhost, localport); // QUdpSocket::DontShareAddress? would need root + connect(_sock, SIGNAL(readyRead()), this, SLOT(sock_dataArrival())); + + if (_sock->state() == QAbstractSocket::BoundState) + { + // OK, we are the first daemon to run + _isServer = true; + _isRunning = true; + return; + } + if (_sock->bind()) + { + _isRunning = true; + } +} + +void PVSLocalhostCommunicator::stop() +{ + _isRunning = false; + _sock->deleteLater(); + _sock = NULL; +} + +void PVSLocalhostCommunicator::sock_dataArrival() +{ + if (_sock == NULL) return; + + qint64 dsize; + while ((dsize = _sock->pendingDatagramSize()) > -1) // returns -1 if no more datagrams are pending + { + if (dsize < 4) // anything shorter than 4 bytes is not a valid PVSMsg anyways, so take a shortcut + { + char x; + _sock->readDatagram(&x, 1, NULL, NULL); + continue; // discard the packet and see if there is more + } + + PVSMsg receiver; + receiver.readMessage(_sock, true); + + if (receiver.isMsgComplete()) + { + // we received a message, so fire away + ConsoleLog writeNetwork(QString("Received UDS command: ").append(receiver.getIdent())); + _daemonDispatcher.fire(receiver.getIdent(), receiver); + } // If the msg is not complete we have to discard it, since UDP + // is packet oriented and we could not say for sure if the next one + // would be the continuation of this msg. + // Usually on the loopback interface it is safe to send datagrams + // up to 8kb, so this is not be an issue anyway. + } +} + +void PVSLocalhostCommunicator::sendCommand(QString ident, QString cmd) +{ + if (_isRunning && !_isServer && _sock != NULL) + { + PVSMsg sender(PVSDAEMON, ident, cmd); + char *data; + int len; + sender.getBinaryData(data, len); + QByteArray qba(data, len); + printf("Sending %d bytes to daemon...\n", qba.length()); + qint64 result = _sock->writeDatagram(qba, localhost, localport); + _sock->waitForBytesWritten(100); + + // simply bail out on an error or disconnect or whatever + if (result != len) + { + if (result == -1) + { + printf("Error sending PVSMsg to daemon: %s\n", _sock->errorString().toUtf8().data()); + } + else + { + printf("Sent PVSMsg was incomplete.\n"); + } + stop(); + } + } +} diff --git a/src/net/pvsLocalhostCommunicator.h b/src/net/pvsLocalhostCommunicator.h new file mode 100644 index 0000000..425cc37 --- /dev/null +++ b/src/net/pvsLocalhostCommunicator.h @@ -0,0 +1,51 @@ +/// test documentation +/// whatever +//! and one more + + + +#ifndef _PVSLocalhostCommunicator_H_ +#define _PVSLocalhostCommunicator_H_ +#include <src/util/dispatcher.h> +#include <QtNetwork/QUdpSocket> + + +class PVSMsg; + +class PVSLocalhostCommunicator : public QObject +{ + Q_OBJECT +public: + PVSLocalhostCommunicator(QString path); + void run(); + bool server() + { + return _isServer; + }; + bool running() + { + return _isRunning; + }; + void stop(); + void sendCommand(QString ident, QString cmd); + EventIdentDispatcher<PVSMsg>* getDispatcher() + { + return &_daemonDispatcher; + }; + +private Q_SLOTS: + void sock_dataArrival(); + +private: + + bool _isServer, _isRunning; + QUdpSocket* _sock; + + + + EventIdentDispatcher<PVSMsg> _daemonDispatcher; //!< Dispatcher for Daemon Commands +}; + +typedef PVSLocalhostCommunicator PVSUDSC; + +#endif diff --git a/src/net/pvsMsg.cpp b/src/net/pvsMsg.cpp new file mode 100644 index 0000000..b56c979 --- /dev/null +++ b/src/net/pvsMsg.cpp @@ -0,0 +1,207 @@ +/* + # Copyright (c) 2010 - 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/pvsMsg.cpp + # - ??? . + # ----------------------------------------------------------------------------- + */ + +#include "pvsMsg.h" +#include <cstring> +#include <QtNetwork/QUdpSocket> +#include <QtNetwork/QHostAddress> + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif + +static unsigned char PVS_ID = 'P'; + +// Several constructors for the PVSMsg class... +PVSMsg::PVSMsg() +{ + _msgType = PVSMESSAGE; + _buffer = NULL; + _buffLen = 0; + _buffFill = 0; +} + +PVSMsg::PVSMsg(PVSMsgType type, QString ident, QString msg, int recID) +{ + this->_recID = recID; + + if (ident.size()) + _msgIdent = ident; + else + _msgIdent = QString("invalid"); + + _msgType = type; + _msgString = msg; + _buffer = NULL; + _buffLen = 0; + _buffFill = 0; +} + +PVSMsg::PVSMsg(PVSMsg const& copyMe) +{ + _msgType = copyMe._msgType; + _msgString = copyMe._msgString; + _msgIdent = copyMe._msgIdent; + _remoteIp = copyMe._remoteIp; + + _buffLen = copyMe._buffLen; + _buffFill = copyMe._buffFill; + if (_buffLen > 0) + { + _buffer = new char[_buffLen]; + memcpy(_buffer, copyMe._buffer, _buffLen); + } + else + _buffer = NULL; + _sndID = copyMe._sndID; + _recID = copyMe._recID; +} + +// Destructor... +PVSMsg::~PVSMsg() +{ + if (_buffer) + delete[] _buffer; +} + +int PVSMsg::readMessage(QAbstractSocket* sock, bool udp) +{ + if (_buffFill > 0 && _buffLen <= _buffFill) + return 1; + qint64 ret = 0; + if (_buffFill < 4) // message header is not complete yet + { + if (_buffer == NULL) + { + _remoteIp = sock->peerAddress().toString(); + if (udp) + _buffer = new char[1000]; + else + _buffer = new char[4]; + } + // Read as many bytes as needed to complete the 4 byte header + if (udp) + ret = ((QUdpSocket*)sock)->readDatagram(_buffer, 1000); + else + ret = sock->read(_buffer + _buffFill, 4 - _buffFill); + if (ret == -1) return -1; + _buffFill += (int)ret; + if (_buffFill == 0) + return 0; + if (_buffer[0] != PVS_ID) + { + // check if the id byte is correct + // otherwise either the message parsing is broken + // or someone is sending garbage + return -1; + } + if (_buffFill >= 4) + { + // header complete, allocate buffer + _buffLen = 4 + (unsigned char)_buffer[2] + (unsigned char)_buffer[3]; + char *nb = new char[_buffLen + 1]; // +1 for \0 + memcpy(nb, _buffer, _buffFill); + delete[] _buffer; + _buffer = nb; + } + } + if (_buffFill >= 4) + { + // got header, assemble rest of msg + if (!udp) + { + ret = sock->read(_buffer + _buffFill, _buffLen - _buffFill); // read remaining bytes + if (ret == -1) return -1; + _buffFill += (int)ret; + } + if (_buffFill >= _buffLen) + { + // message is complete + _buffer[_buffLen] = '\0'; // first, terminate with nullchar + _msgString = QString::fromUtf8(_buffer + 4 + (unsigned char)_buffer[2]); // get message + _buffer[4 + (unsigned char)_buffer[2]] = '\0'; + _msgIdent = QString::fromUtf8(_buffer + 4); // get ident + _msgType = (PVSMsgType)_buffer[1]; // get msg type + return 1; + } + } + return 0; +} + +void PVSMsg::setMessage(QString text) +{ + _msgString = text; +} + +void PVSMsg::setIdent(QString ident) +{ + _msgIdent = ident; +} + +QString PVSMsg::getMessage() +{ + return _msgString; +} + +QString PVSMsg::getIdent() +{ + return _msgIdent; +} + +bool PVSMsg::getBinaryData(char*& data, int& dataLen) +{ + if (_msgIdent.size() == 0) + return false; + this->makeSndBuff(); + data = (char*) _buffer; + dataLen = _buffLen; + return true; +} + +void PVSMsg::setSndID(int id) +{ + _sndID = id; +} + +void PVSMsg::setRecID(int id) +{ + _recID = id; +} + +// Create a send buffer with the necessary size +bool PVSMsg::makeSndBuff() +{ + QByteArray uIdent = _msgIdent.toUtf8(); + QByteArray uString = _msgString.toUtf8(); + uIdent.truncate(255); + uString.truncate(255); + _buffLen = 4 + uIdent.size() + uString.size(); + if (_buffer != NULL) + delete[] _buffer; + unsigned char *tmpBuffer = new unsigned char[_buffLen + 1]; + + tmpBuffer[0] = PVS_ID; + tmpBuffer[1] = (unsigned char) _msgType; + tmpBuffer[2] = (unsigned char) uIdent.size(); + tmpBuffer[3] = (unsigned char) uString.size(); + memcpy(tmpBuffer + 4, uIdent.data(), uIdent.size()); + memcpy(tmpBuffer + 4 + uIdent.size(), uString.data(), uString.size()); + //printf("[%c] '%s' - '%s'\n", (char)_msgType, _msgIdent.toUtf8().data(), _msgString.toUtf8().data()); + tmpBuffer[_buffLen] = '\0'; + //printf("Binary: '%s'\n", _buffer); + _buffer = (char*)tmpBuffer; + return true; +} diff --git a/src/net/pvsMsg.h b/src/net/pvsMsg.h new file mode 100644 index 0000000..6fbcd99 --- /dev/null +++ b/src/net/pvsMsg.h @@ -0,0 +1,73 @@ +/* + * pvsMsg.h + * + * Created on: 08.01.2010 + * Author: sr + */ + +#ifndef PVSMSG_H_ +#define PVSMSG_H_ + + +#include <QtCore/QString> + +enum PVSMsgType +{ + PVSCOMMAND = 'C', + PVSMESSAGE = 'M', + PVSLOGIN = 'L', + PVSDAEMON = 'D', + PVSUNKNOWN = 'U' +}; + +class PVSServerConnection; +class PVSListenServer; +class PVSClientConnection; +class QAbstractSocket; + +class PVSMsg +{ +public: + friend class PVSServerConnection; + friend class PVSListenServer; + friend class PVSClientConnection; + + PVSMsg(); + PVSMsg(PVSMsgType type, QString _ident, QString msg, int recID = 0); + PVSMsg(PVSMsg const& copyMe); + ~PVSMsg(); + + int readMessage(QAbstractSocket* sock, bool udp = false); ///< -1 = error, 0 = incomplete, 1 = complete + unsigned int getLength() + { + return _msgString.size(); + }; + void setIdent(QString ident); + QString getIdent(); + void setMessage(QString text); + QString getMessage(); + PVSMsgType getType() { return _msgType; } + bool getBinaryData(char*& data, int& dataLen); ///< get binary representation of this PVSmsg + + unsigned int getRecID() { return _recID; } + int getSndID() { return _sndID; } + QString getRemoteIp() { return _remoteIp; } + bool isMsgComplete() { return _buffFill > 0 && _buffFill == _buffLen; } + +private: + void setSndID(int id); + void setRecID(int id); + bool makeSndBuff(); + PVSMsgType _msgType; ///< type of message (command, message, login, daemon) + QString _msgIdent; ///< message ident + QString _msgString; ///< message string + char *_buffer; ///< receive buffer + int _buffLen; ///< size of complete message in bytes + int _buffFill; ///< actual number of bytes in buffer + + int _sndID; + int _recID; + QString _remoteIp; +}; + +#endif /* PVSMSG_H_ */ diff --git a/src/net/pvsServerConnection.cpp b/src/net/pvsServerConnection.cpp new file mode 100644 index 0000000..04a1ef6 --- /dev/null +++ b/src/net/pvsServerConnection.cpp @@ -0,0 +1,243 @@ +#include "pvsServerConnection.h" +#include "src/util/consoleLogger.h" +#include "src/util/util.h" +#include "pvsMsg.h" +#include "src/pvs.h" +#include <QtNetwork/QHostAddress> +#include "src/core/pvsChatClient.h" +#include "src/util/serviceDiscoveryUtil.h" +#include "src/net/pvsDiscoveredServer.h" +//#define verbose + +PVSServerConnection::PVSServerConnection(PVS *parent) : QObject(parent) +{ + _client = parent; + _incomplete = NULL; + _socket = NULL; + _timerId = 0; + _timer = 0; + _wasConnected = false; + loadCommands(); +} + +inline void PVSServerConnection::timerStop() +{ + if (_timer == 0) return; + killTimer(_timer); + _timer = 0; +} + +bool PVSServerConnection::connectToServer(PVSDiscoveredServer* server, QString passwd) +{ + QHostAddress host = server->getHost(); + if (_socket != NULL && _socket->state() == QAbstractSocket::ConnectedState + && _socket->peerAddress() == host) return false; + int port = server->getPort(); + if (port < 1 || port > 65535) port = SERVER_PORT_INT; + ConsoleLog writeNetwork("Connecting to host:"); + ConsoleLog writeNetwork(host.toString().toUtf8().data()); + timerStop(); + if (_socket != NULL) + { + _socket->blockSignals(true); + _socket->abort(); + _socket->deleteLater(); + } + _name = server->getName(); + _expectedFingerprint = server->getFingerprint(); + _passwd = passwd; + _socket = new QSslSocket(this); + connect(_socket, SIGNAL(encrypted()), this, SLOT(sock_connected())); + connect(_socket, SIGNAL(readyRead()), this, SLOT(sock_dataArrival())); + connect(_socket, SIGNAL(disconnected()), this, SLOT(sock_closed())); + connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sock_error(QAbstractSocket::SocketError))); + char tex[2000]; + snprintf(tex, 2000, "Connecting to %s on port %d", host.toString().toUtf8().data(), (int)port); + ConsoleLog writeNetwork(tex); + connect(_socket, + SIGNAL(sslErrors(const QList<QSslError> &)), + this, + SLOT(sslErrors(const QList<QSslError> &)) + ); + _socket->connectToHostEncrypted(host.toString(), port); + if (_timer == 0) _timer = startTimer(10000); + + return true; // we really don't know yet if it will succeed since its async +} + +void PVSServerConnection::sslErrors ( const QList<QSslError> & errors ) +{ + for (QList<QSslError>::const_iterator it = errors.begin(); it != errors.end(); it++) + { + QSslError err = *it; + printf("Connect SSL: %s\n", err.errorString().toUtf8().data()); + if (err.error() == QSslError::HostNameMismatch) continue; // We don't pay attention to hostnames for validation + if (err.error() == QSslError::SelfSignedCertificate) continue; // Also, this will always be the case; we check the fingerprint later + ConsoleLog writeNetwork(err.errorString().toUtf8().data()); + ConsoleLog writeNetwork("***** SSL ERROR, ABORTING *****"); + return; + } + _socket->ignoreSslErrors(); +} + +void PVSServerConnection::disconnectFromServer() +{ + if (_socket == NULL) return; + _socket->disconnectFromHost(); + handleDisconnectInternal(); +} + +void PVSServerConnection::timerEvent(QTimerEvent *event) +{ + if (_socket != NULL && _socket->state() == QAbstractSocket::ConnectedState) return; + // TODO: Check for ping timeout +} + +void PVSServerConnection::sendMessage(PVSMsg newMessage) +{ + if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState) return; + char *data; + int len; + newMessage.getBinaryData(data, len); + QByteArray qba(data, len); + _socket->write(qba); +} + +void PVSServerConnection::ping() +{ + sendMessage(PVSMsg(PVSCOMMAND, "PING", "U THERE?"));; +} + +QString PVSServerConnection::getServerName() +{ + if (!isConnected()) return QString(); + return _name; +} + +void PVSServerConnection::loadCommands() +{ + addLoginHandler("ID", this, &PVSServerConnection::onID); + addCommandHandler("PING", this, &PVSServerConnection::onPing); +} + +void PVSServerConnection::onID(PVSMsg idmsg) +{ + _id = (unsigned int)string2Int(idmsg.getMessage()); +} + +void PVSServerConnection::onPing(PVSMsg pingmsg) +{ + if (pingmsg.getMessage().at(pingmsg.getMessage().size()-1) == '?') + { + sendMessage(PVSMsg(PVSCOMMAND, "PING", "HI!")); + } +} + +void PVSServerConnection::handleClientMsg(PVSMsg receiver) +{ + // FIXME: @SimonR, this line cuase problems with pvs in daemon mode and dbus + //qDebug("Got Message for this client: [%c][%s][%s]\n", (char)receiver.getType(), receiver.getIdent().toUtf8().data(), receiver.getMessage().toUtf8().data()); + if (receiver.getType() == PVSCOMMAND) + { + _commandDispatcher.fire(receiver.getIdent(), receiver); +// ConsoleLog writeNetwork(QString("Received a command and handled it.").append(receiver.getIdent().append(QString(":").append(receiver.getMessage())))); + } + else if (receiver.getType() == PVSMESSAGE) + { + _chatDispatcher.fire(receiver.getIdent(), receiver); + ConsoleLog writeNetwork(QString("Received a chat message and handled it.").append(receiver.getIdent().append(QString(":").append(receiver.getMessage())))); + } + else if (receiver.getType() == PVSLOGIN) + { + _loginDispatcher.fire(receiver.getIdent(), receiver); +// ConsoleLog writeNetwork(QString("Received a login and handled it. Ident: ").append(receiver.getIdent().append(QString(":")).append(receiver.getMessage()))); + } + else if (receiver.getType() == PVSUNKNOWN) + { + ConsoleLog writeNetwork(QString("Received an unknown type. Ident : ").append(receiver.getIdent().append(QString(" : ").append(receiver.getMessage())))); + } +} + +void PVSServerConnection::sock_dataArrival() +{ + if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState) + { + ConsoleLog writeError("dataArrival called in bad state"); + return; + } + + while (_socket->bytesAvailable()) + { + int retval = 0; + do + { + if (_incomplete == NULL) _incomplete = new PVSMsg(); // we need a pvsmsg object + retval = _incomplete->readMessage(_socket); // let the message read data from socket + if (retval == -1) // error parsing msg, disconnect client! + { + this->disconnectFromServer(); + return; + } + if (retval == 1) // message is complete + { + this->handleClientMsg(*_incomplete); + delete _incomplete; // ...and delete... + _incomplete = NULL; // ...so the next msg can be parsed + } + } while (retval == 1); + } +} + +void PVSServerConnection::sock_closed() +{ + // should this be unreliable in some way i suggest using the signal "stateChanged()" instead + // and check if the state changed to unconnected. + ConsoleLog writeNetwork("Socket was closed... oh well.."); + handleDisconnectInternal(); +} + +void PVSServerConnection::sock_error(QAbstractSocket::SocketError errcode) +{ + char txt[204]; + snprintf(txt, 200, "Connection error: %d", (int)errcode); + ConsoleLog writeNetwork(txt); + handleDisconnectInternal(); +} + +// Send to server username and loginname. +void PVSServerConnection::sock_connected() +{ + QByteArray cert = _socket->peerCertificate().digest(QCryptographicHash::Sha1); + if (_expectedFingerprint != cert) + { // Nich mit mir, Freundchen! + disconnectFromServer(); + // Maybe you want to inform the user that there was a certificate mismatch + // and that it is very possible that the CIA is trying to eavesdrop on the connection + return; + } + ConsoleLog writeNetwork("Connected!"); + _wasConnected = true; + sendMessage( + PVSMsg(PVSLOGIN, "USERNAME", getFullUsername()+","+getenv("USER"))); + sendMessage( + PVSMsg(PVSLOGIN, "PASSWORD", _passwd)); + ConsoleLog writeNetwork("Sent Username and password."); + + _client->onConnected(_name); +} + +void PVSServerConnection::handleDisconnectInternal() +{ + _socket->blockSignals(true); + _socket->abort(); + _socket->deleteLater(); + _socket = NULL; + _name = QString(); + _client->securityUnlock(); + timerStop(); + if (_wasConnected) + { + _wasConnected = false; + _client->onDisconnected(); + } +} diff --git a/src/net/pvsServerConnection.h b/src/net/pvsServerConnection.h new file mode 100644 index 0000000..0669d88 --- /dev/null +++ b/src/net/pvsServerConnection.h @@ -0,0 +1,99 @@ +/** + * PVSServerConnection + * + * holds and maintains a Connection to a PVSListenServer (or derivate) + * + */ + + + +#ifndef _PVSSERVERCONNECTION_H_ +#define _PVSSERVERCONNECTION_H_ + +#include "src/util/dispatcher.h" +#include <QtNetwork/QSslSocket> + +class PVSMsg; +class PVS; +class PVSDiscoveredServer; + +class PVSServerConnection : public QObject +{ + Q_OBJECT +public: + PVSServerConnection(PVS *parent); + ~PVSServerConnection() {} + bool connectToServer(PVSDiscoveredServer* server, QString passwd = QString()); + void disconnectFromServer(); + bool isConnected() + { + return _socket != NULL && _socket->state() == QAbstractSocket::ConnectedState; + } + + void sendMessage(PVSMsg newMessage); + + void ping(); + QString getServerName(); + + virtual void loadCommands(); + void onID(PVSMsg idmsg); + void onPing(PVSMsg pingmsg); + template<class T> void addLoginHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + _loginDispatcher.addListener(ident, who, func); + }; + template<class T> void removeLoginHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + _loginDispatcher.removeListener(ident, who, func); + }; + template<class T> void addChatHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + _chatDispatcher.addListener(ident, who, func); + }; + template<class T> void removeChatHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + _chatDispatcher.removeListener(ident, who, func); + }; + template<class T> void addCommandHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + _commandDispatcher.addListener(ident, who, func); + }; + template<class T> void removeCommandHandler(QString ident, T* who, void (T :: *func)(PVSMsg)) + { + _commandDispatcher.removeListener(ident, who, func); + }; + +protected: + void timerEvent(QTimerEvent *event); + +private Q_SLOTS: + void sslErrors(const QList<QSslError> & errors); // triggered for errors that occur during SSL negotiation + void sock_dataArrival(); // triggered if data is available for reading + void sock_closed(); // triggered if the socket is closed + void sock_error(QAbstractSocket::SocketError errcode); // triggered if an error occurs on the socket + void sock_connected(); // triggered if the connection is established and ready to use + +private: + + inline void timerStop(); + void handleClientMsg(PVSMsg receiver); + void handleDisconnectInternal(); + + EventIdentDispatcher<PVSMsg> _commandDispatcher; + EventIdentDispatcher<PVSMsg> _chatDispatcher; + EventIdentDispatcher<PVSMsg> _loginDispatcher; + PVSMsg *_incomplete; + QSslSocket *_socket; + unsigned int _id; + int _timerId; + //QString _host; + //quint16 _port; + int _timer; + PVS *_client; + bool _wasConnected; + QByteArray _expectedFingerprint; + QString _name; + QString _passwd; +}; + +#endif diff --git a/src/net/pvsServiceBroadcast.cpp b/src/net/pvsServiceBroadcast.cpp new file mode 100644 index 0000000..cf15b5b --- /dev/null +++ b/src/net/pvsServiceBroadcast.cpp @@ -0,0 +1,51 @@ +/*
+# 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/pvsServiceBroadcast.cpp
+# - broadcast sessionname
+# -----------------------------------------------------------------------------
+*/
+/********** COMPILE-TIME SETTINGS ***************/
+
+#include "pvsServiceBroadcast.h"
+#include "src/setup.h"
+#include <cassert>
+#include "src/util/serviceDiscoveryUtil.h"
+
+PVSServiceBroadcast::PVSServiceBroadcast()
+{
+ _announce = NULL;
+ _timer = 0;
+ _broadcaster.bind(SD_PORT_CONSOLE);
+ _everyone = QHostAddress(QHostAddress::Broadcast);
+}
+
+PVSServiceBroadcast::~PVSServiceBroadcast()
+{
+ if (_announce != NULL) delete _announce;
+ if (_timer != 0) this->killTimer(_timer);
+}
+
+void PVSServiceBroadcast::setFingerprint(QByteArray sha1)
+{
+ if (_announce != NULL) _announce->clear();
+ else _announce = new QByteArray();
+ appendSdField(_announce, "hsh", QString(sha1.toBase64()));
+ appendSdField(_announce, "prt", SERVER_PORT); // TODO: maybe this has to come from somewhere else if configurable
+ appendSdField(_announce, "aut", "SHA1");
+ if (_timer == 0) _timer = this->startTimer(SB_INTERVAL * 1000);
+}
+
+void PVSServiceBroadcast::timerEvent(QTimerEvent *event)
+{
+ if (_announce == NULL) return;
+ _broadcaster.writeDatagram(*_announce, _everyone, SD_PORT_CLIENT);
+}
diff --git a/src/net/pvsServiceBroadcast.h b/src/net/pvsServiceBroadcast.h new file mode 100644 index 0000000..cf3f63d --- /dev/null +++ b/src/net/pvsServiceBroadcast.h @@ -0,0 +1,46 @@ +/*
+# 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/pvsServiceBroadcast.cpp
+# - broadcast sessionname, port, auth/encryption
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef PVSSERVICEBROADCAST_H_
+#define PVSSERVICEBROADCAST_H_
+
+#include <QtCore/QString>
+#include <QtCore/QObject>
+#include <QtNetwork/QUdpSocket>
+#include "src/net/pvsMsg.h"
+
+class PVSMsg;
+
+class PVSServiceBroadcast : public QObject
+{
+ Q_OBJECT
+
+public:
+ PVSServiceBroadcast();
+ virtual ~PVSServiceBroadcast();
+ void setFingerprint(QByteArray sha1);
+
+protected:
+ void timerEvent(QTimerEvent *event);
+
+private:
+ QByteArray *_announce;
+ QUdpSocket _broadcaster;
+ QHostAddress _everyone;
+ int _timer;
+};
+
+#endif /* PVSSERVICEBROADCAST_H_ */
diff --git a/src/net/pvsServiceDiscovery.cpp b/src/net/pvsServiceDiscovery.cpp new file mode 100644 index 0000000..0a99802 --- /dev/null +++ b/src/net/pvsServiceDiscovery.cpp @@ -0,0 +1,181 @@ +/* +# 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) + { + printf("Could not open SERVICE DISCOVERY port\n"); + 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)); +} diff --git a/src/net/pvsServiceDiscovery.h b/src/net/pvsServiceDiscovery.h new file mode 100644 index 0000000..08a38b8 --- /dev/null +++ b/src/net/pvsServiceDiscovery.h @@ -0,0 +1,57 @@ +/* +# 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 +# ----------------------------------------------------------------------------- +*/ + +#ifndef PVSSERVICEDISCOVERY_H_ +#define PVSSERVICEDISCOVERY_H_ + +#include <QtNetwork/QUdpSocket> +#include <QtCore/QDateTime> + +class PVS; +class PVSDiscoveredServer; +typedef QList<PVSDiscoveredServer*> tServerList; + +class PVSServiceDiscovery : public QObject +{ + Q_OBJECT +public: + PVSServiceDiscovery(PVS* client); + virtual ~PVSServiceDiscovery(); + void connectToSession(QString name, QString passwd); + bool nameExists(QString name); + QStringList getAllServers(); + +protected: + void timerEvent(QTimerEvent *event); + +private Q_SLOTS: + void sock_dataArrival(); + void sendServerToGui(PVSDiscoveredServer*); + +private: + void handleDiscovery(QHostAddress host, int port, QByteArray hash); + void setTimerInterval(); + QUdpSocket _sock; + QString _sessionName; + QString _sessionPasswd; + PVS *_client; + QDateTime _last; ///< to protect us from being abused as a ddos zombie + tServerList _servers; + int _currentServer; + int _timerId; +}; + +#endif /* PVSSERVICEDISCOVERY_H_ */ |