From 0719d4c6edea18e27101131cd3f2cd612135cb38 Mon Sep 17 00:00:00 2001 From: Max-Wilhelm Bruker Date: Sun, 4 Mar 2012 23:15:39 +0100 Subject: [PATCH] client-side SSL works, thread initialization needs fixing --- servatrice/src/networkserverinterface.cpp | 40 ++++++- servatrice/src/networkserverinterface.h | 2 + servatrice/src/networkserverthread.cpp | 54 ++++++++-- servatrice/src/networkserverthread.h | 10 ++ servatrice/src/servatrice.cpp | 121 +++++++++++++--------- 5 files changed, 164 insertions(+), 63 deletions(-) diff --git a/servatrice/src/networkserverinterface.cpp b/servatrice/src/networkserverinterface.cpp index 7a17568b..be3ea229 100644 --- a/servatrice/src/networkserverinterface.cpp +++ b/servatrice/src/networkserverinterface.cpp @@ -9,14 +9,17 @@ #include "pb/event_server_complete_list.pb.h" #include -NetworkServerInterface::NetworkServerInterface(Servatrice *_server, QSslSocket *_socket) - : QObject(), server(_server), socket(_socket), messageInProgress(false) +void NetworkServerInterface::sharedCtor() { connect(socket, SIGNAL(readyRead()), this, SLOT(readClient())); connect(socket, SIGNAL(disconnected()), this, SLOT(deleteLater())); connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(catchSocketError(QAbstractSocket::SocketError))); connect(this, SIGNAL(outputBufferChanged()), this, SLOT(flushOutputBuffer()), Qt::QueuedConnection); - +} + +NetworkServerInterface::NetworkServerInterface(Servatrice *_server, QSslSocket *_socket) + : QObject(), server(_server), socket(_socket), messageInProgress(false) +{ Event_ServerCompleteList event; event.set_server_id(server->getServerId()); @@ -46,6 +49,29 @@ NetworkServerInterface::NetworkServerInterface(Servatrice *_server, QSslSocket * server->serverMutex.unlock(); } +NetworkServerInterface::NetworkServerInterface(const QString &peerHostName, const QString &peerAddress, int peerPort, Servatrice *_server, QSslSocket *_socket) + : QObject(), server(_server), socket(_socket), messageInProgress(false) +{ + sharedCtor(); + + socket->connectToHostEncrypted(peerAddress, peerPort, peerHostName); + if (!socket->waitForConnected(5000)) { + qDebug() << "[SN] Socket error:" << socket->errorString(); + deleteLater(); + return; + } + if (!socket->waitForEncrypted(5000)) { + QList sslErrors(socket->sslErrors()); + if (sslErrors.isEmpty()) + qDebug() << "[SN] SSL handshake timeout, terminating connection"; + else + qDebug() << "[SN] SSL errors:" << sslErrors; + deleteLater(); + return; + } + server->addNetworkServerInterface(this); +} + NetworkServerInterface::~NetworkServerInterface() { logger->logMessage("[SN] session ended", this); @@ -56,6 +82,7 @@ NetworkServerInterface::~NetworkServerInterface() void NetworkServerInterface::flushOutputBuffer() { QMutexLocker locker(&outputBufferMutex); + qDebug("FLUSH"); if (outputBuffer.isEmpty()) return; server->incTxBytes(outputBuffer.size()); @@ -96,7 +123,7 @@ void NetworkServerInterface::readClient() void NetworkServerInterface::catchSocketError(QAbstractSocket::SocketError socketError) { - qDebug() << "Socket error:" << socketError; + qDebug() << "[SN] Socket error:" << socketError; deleteLater(); } @@ -112,11 +139,14 @@ void NetworkServerInterface::transmitMessage(const ServerNetworkMessage &item) buf.data()[1] = (unsigned char) (size >> 16); buf.data()[0] = (unsigned char) (size >> 24); - QMutexLocker locker(&outputBufferMutex); + outputBufferMutex.lock(); outputBuffer.append(buf); + outputBufferMutex.unlock(); + qDebug("TRANSMIT"); emit outputBufferChanged(); } void NetworkServerInterface::processMessage(const ServerNetworkMessage &item) { + qDebug() << QString::fromStdString(item.DebugString()); } diff --git a/servatrice/src/networkserverinterface.h b/servatrice/src/networkserverinterface.h index d713a0d2..80f08c15 100644 --- a/servatrice/src/networkserverinterface.h +++ b/servatrice/src/networkserverinterface.h @@ -25,8 +25,10 @@ private: int messageLength; void processMessage(const ServerNetworkMessage &item); + void sharedCtor(); public: NetworkServerInterface(Servatrice *_server, QSslSocket *_socket); + NetworkServerInterface(const QString &peerHostName, const QString &peerAddress, int peerPort, Servatrice *_server, QSslSocket *_socket); ~NetworkServerInterface(); void transmitMessage(const ServerNetworkMessage &item); diff --git a/servatrice/src/networkserverthread.cpp b/servatrice/src/networkserverthread.cpp index 05287e87..4c3f24cd 100644 --- a/servatrice/src/networkserverthread.cpp +++ b/servatrice/src/networkserverthread.cpp @@ -4,9 +4,16 @@ #include "servatrice.h" #include "main.h" #include +#include NetworkServerThread::NetworkServerThread(int _socketDescriptor, Servatrice *_server, const QSslCertificate &_cert, const QSslKey &_privateKey, QObject *parent) - : QThread(parent), server(_server), socketDescriptor(_socketDescriptor), cert(_cert), privateKey(_privateKey) + : QThread(parent), server(_server), socketDescriptor(_socketDescriptor), cert(_cert), privateKey(_privateKey), connectionType(ServerType) +{ + connect(this, SIGNAL(finished()), this, SLOT(deleteLater())); +} + +NetworkServerThread::NetworkServerThread(const QString &_hostName, const QString &_address, int _port, const QSslCertificate &_peerCert, Servatrice *_server, const QSslCertificate &_cert, const QSslKey &_privateKey, QObject *parent) + : QThread(parent), server(_server), cert(_cert), privateKey(_privateKey), peerHostName(_hostName), peerAddress(_address), peerPort(_port), peerCert(_peerCert), connectionType(ClientType) { connect(this, SIGNAL(finished()), this, SLOT(deleteLater())); } @@ -22,10 +29,38 @@ NetworkServerThread::~NetworkServerThread() void NetworkServerThread::run() { socket = new QSslSocket; - socket->setSocketDescriptor(socketDescriptor); socket->setLocalCertificate(cert); socket->setPrivateKey(privateKey); + if (connectionType == ClientType) + try { + initClient(); + } catch (bool) { + usleep(100000); + initWaitCondition.wakeAll(); + return; + } + else + try { + initServer(); + } catch (bool) { + return; + } + + interface = new NetworkServerInterface(server, socket); + connect(interface, SIGNAL(destroyed()), this, SLOT(deleteLater())); + + if (connectionType == ClientType) { + usleep(100000); + initWaitCondition.wakeAll(); + } + + exec(); +} + +void NetworkServerThread::initServer() +{ + socket->setSocketDescriptor(socketDescriptor); logger->logMessage(QString("[SN] incoming connection: %1").arg(socket->peerAddress().toString())); QList serverList = server->getServerList(); @@ -52,9 +87,14 @@ void NetworkServerThread::run() logger->logMessage(QString("[SN] Authentication failed, terminating connection")); return; } - - interface = new NetworkServerInterface(server, socket); - connect(interface, SIGNAL(destroyed()), this, SLOT(deleteLater())); - - exec(); +} + +void NetworkServerThread::initClient() +{ + qDebug() << "[SN] Connecting to " << peerAddress << ":" << peerPort; + + QList expectedErrors; + expectedErrors.append(QSslError(QSslError::SelfSignedCertificate, peerCert)); + socket->ignoreSslErrors(expectedErrors); + } diff --git a/servatrice/src/networkserverthread.h b/servatrice/src/networkserverthread.h index e9cef265..925e58f3 100644 --- a/servatrice/src/networkserverthread.h +++ b/servatrice/src/networkserverthread.h @@ -4,6 +4,7 @@ #include #include #include +#include class Servatrice; class NetworkServerInterface; @@ -16,11 +17,20 @@ private: NetworkServerInterface *interface; QSslCertificate cert; QSslKey privateKey; + QString peerHostName, peerAddress; + int peerPort; + QSslCertificate peerCert; int socketDescriptor; QSslSocket *socket; + enum ConnectionType { ClientType, ServerType } connectionType; + + void initClient(); + void initServer(); public: NetworkServerThread(int _socketDescriptor, Servatrice *_server, const QSslCertificate &_cert, const QSslKey &_privateKey, QObject *parent = 0); + NetworkServerThread(const QString &_hostName, const QString &_address, int _port, const QSslCertificate &peerCert, Servatrice *_server, const QSslCertificate &_cert, const QSslKey &_privateKey, QObject *parent = 0); ~NetworkServerThread(); + QWaitCondition initWaitCondition; protected: void run(); }; diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index cd73f7a2..6b8095bb 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -57,28 +57,8 @@ void Servatrice_NetworkServer::incomingConnection(int socketDescriptor) Servatrice::Servatrice(QSettings *_settings, QObject *parent) : Server(parent), dbMutex(QMutex::Recursive), settings(_settings), uptime(0), shutdownTimer(0) { - pingClock = new QTimer(this); - connect(pingClock, SIGNAL(timeout()), this, SIGNAL(pingClockTimeout())); - pingClock->start(1000); - serverName = settings->value("server/name").toString(); serverId = settings->value("server/id", 0).toInt(); - int statusUpdateTime = settings->value("server/statusupdate").toInt(); - statusUpdateClock = new QTimer(this); - connect(statusUpdateClock, SIGNAL(timeout()), this, SLOT(statusUpdate())); - if (statusUpdateTime != 0) { - qDebug() << "Starting status update clock, interval " << statusUpdateTime << " ms"; - statusUpdateClock->start(statusUpdateTime); - } - - threaded = settings->value("server/threaded", false).toInt(); - gameServer = new Servatrice_GameServer(this, threaded, this); - const int gamePort = settings->value("server/port", 4747).toInt(); - qDebug() << "Starting server on port" << gamePort; - if (gameServer->listen(QHostAddress::Any, gamePort)) - qDebug() << "Server listening."; - else - qDebug() << "gameServer->listen(): Error."; const QString authenticationMethodStr = settings->value("authentication/method").toString(); if (authenticationMethodStr == "sql") @@ -98,37 +78,6 @@ Servatrice::Servatrice(QSettings *_settings, QObject *parent) updateServerList(); clearSessionTables(); - try { if (settings->value("servernetwork/active", 0).toInt()) { - qDebug() << "Connecting to server network."; - const QString certFileName = settings->value("servernetwork/ssl_cert").toString(); - const QString keyFileName = settings->value("servernetwork/ssl_key").toString(); - qDebug() << "Loading certificate..."; - QFile certFile(certFileName); - if (!certFile.open(QIODevice::ReadOnly)) - throw QString("Error opening certificate file: %1").arg(certFileName); - QSslCertificate cert(&certFile); - if (!cert.isValid()) - throw(QString("Invalid certificate.")); - qDebug() << "Loading private key..."; - QFile keyFile(keyFileName); - if (!keyFile.open(QIODevice::ReadOnly)) - throw QString("Error opening private key file: %1").arg(keyFileName); - QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); - if (key.isNull()) - throw QString("Invalid private key."); - - const int networkPort = settings->value("servernetwork/port", 14747).toInt(); - qDebug() << "Starting network server on port" << networkPort; - - networkServer = new Servatrice_NetworkServer(this, cert, key, this); - if (networkServer->listen(QHostAddress::Any, networkPort)) - qDebug() << "Network server listening."; - else - throw QString("networkServer->listen(): Error."); - } } catch (QString error) { - qDebug() << "ERROR --" << error; - } - int size = settings->beginReadArray("rooms"); for (int i = 0; i < size; ++i) { settings->setArrayIndex(i); @@ -164,6 +113,76 @@ Servatrice::Servatrice(QSettings *_settings, QObject *parent) maxMessageCountPerInterval = settings->value("security/max_message_count_per_interval").toInt(); maxMessageSizePerInterval = settings->value("security/max_message_size_per_interval").toInt(); maxGamesPerUser = settings->value("security/max_games_per_user").toInt(); + + try { if (settings->value("servernetwork/active", 0).toInt()) { + qDebug() << "Connecting to server network."; + const QString certFileName = settings->value("servernetwork/ssl_cert").toString(); + const QString keyFileName = settings->value("servernetwork/ssl_key").toString(); + qDebug() << "Loading certificate..."; + QFile certFile(certFileName); + if (!certFile.open(QIODevice::ReadOnly)) + throw QString("Error opening certificate file: %1").arg(certFileName); + QSslCertificate cert(&certFile); + if (!cert.isValid()) + throw(QString("Invalid certificate.")); + qDebug() << "Loading private key..."; + QFile keyFile(keyFileName); + if (!keyFile.open(QIODevice::ReadOnly)) + throw QString("Error opening private key file: %1").arg(keyFileName); + QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + if (key.isNull()) + throw QString("Invalid private key."); + + const int networkPort = settings->value("servernetwork/port", 14747).toInt(); + qDebug() << "Starting network server on port" << networkPort; + + networkServer = new Servatrice_NetworkServer(this, cert, key, this); + if (networkServer->listen(QHostAddress::Any, networkPort)) + qDebug() << "Network server listening."; + else + throw QString("networkServer->listen(): Error."); + + QMutableListIterator serverIterator(serverList); + while (serverIterator.hasNext()) { + const ServerProperties &prop = serverIterator.next(); + if (prop.cert == cert) { + serverIterator.remove(); + continue; + } + + NetworkServerThread *thread = new NetworkServerThread(prop.hostname, prop.address.toString(), prop.controlPort, prop.cert, this, cert, key); + thread->start(); + + QMutex initMutex; + initMutex.lock(); + thread->initWaitCondition.wait(&initMutex); + } + + } } catch (QString error) { + qDebug() << "ERROR --" << error; + } + + pingClock = new QTimer(this); + connect(pingClock, SIGNAL(timeout()), this, SIGNAL(pingClockTimeout())); + pingClock->start(1000); + + int statusUpdateTime = settings->value("server/statusupdate").toInt(); + statusUpdateClock = new QTimer(this); + connect(statusUpdateClock, SIGNAL(timeout()), this, SLOT(statusUpdate())); + if (statusUpdateTime != 0) { + qDebug() << "Starting status update clock, interval " << statusUpdateTime << " ms"; + statusUpdateClock->start(statusUpdateTime); + } + + threaded = settings->value("server/threaded", false).toInt(); + gameServer = new Servatrice_GameServer(this, threaded, this); + const int gamePort = settings->value("server/port", 4747).toInt(); + qDebug() << "Starting server on port" << gamePort; + if (gameServer->listen(QHostAddress::Any, gamePort)) + qDebug() << "Server listening."; + else + qDebug() << "gameServer->listen(): Error."; + } Servatrice::~Servatrice()