diff --git a/common/pb/CMakeLists.txt b/common/pb/CMakeLists.txt index e1f171ef..1f14c00c 100644 --- a/common/pb/CMakeLists.txt +++ b/common/pb/CMakeLists.txt @@ -102,6 +102,7 @@ SET(PROTO_FILES game_event_context.proto game_event.proto game_replay.proto + isl_message.proto moderator_commands.proto move_card_to_zone.proto response_deck_download.proto @@ -133,7 +134,6 @@ SET(PROTO_FILES serverinfo_room.proto serverinfo_user.proto serverinfo_zone.proto - servernetwork_message.proto server_message.proto session_commands.proto session_event.proto diff --git a/common/pb/servernetwork_message.proto b/common/pb/isl_message.proto similarity index 94% rename from common/pb/servernetwork_message.proto rename to common/pb/isl_message.proto index 880a0a1d..790c333e 100644 --- a/common/pb/servernetwork_message.proto +++ b/common/pb/isl_message.proto @@ -4,7 +4,7 @@ import "commands.proto"; import "game_event_container.proto"; import "room_event.proto"; -message ServerNetworkMessage { +message IslMessage { enum MessageType { RESPONSE = 0; SESSION_EVENT = 1; diff --git a/common/server.cpp b/common/server.cpp index cbe73da4..090e0344 100644 --- a/common/server.cpp +++ b/common/server.cpp @@ -25,6 +25,8 @@ #include "pb/event_user_joined.pb.h" #include "pb/event_user_left.pb.h" #include "pb/event_list_rooms.pb.h" +#include "pb/session_event.pb.h" +#include "pb/isl_message.pb.h" #include #include @@ -98,6 +100,9 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString for (int i = 0; i < clients.size(); ++i) if (clients[i]->getAcceptsUserListChanges()) clients[i]->sendProtocolItem(*se); + serverMutex.unlock(); + + sendIslMessage(*se); delete se; return authState; @@ -121,6 +126,7 @@ void Server::removeClient(Server_ProtocolHandler *client) for (int i = 0; i < clients.size(); ++i) if (clients[i]->getAcceptsUserListChanges()) clients[i]->sendProtocolItem(*se); + sendIslMessage(*se); delete se; users.remove(QString::fromStdString(data->name())); @@ -177,3 +183,12 @@ int Server::getGamesCount() const } return result; } + +void Server::sendIslMessage(const SessionEvent &item, int serverId) +{ + IslMessage msg; + msg.set_message_type(IslMessage::SESSION_EVENT); + msg.mutable_session_event()->CopyFrom(item); + + doSendIslMessage(msg, serverId); +} diff --git a/common/server.h b/common/server.h index dd5da567..c9375679 100644 --- a/common/server.h +++ b/common/server.h @@ -11,6 +11,8 @@ class Server_Game; class Server_Room; class Server_ProtocolHandler; class GameReplay; +class IslMessage; +class SessionEvent; enum AuthenticationResult { NotLoggedIn = 0, PasswordRight = 1, UnknownUser = 2, WouldOverwriteOldSession = 3, UserIsBanned = 4 }; @@ -50,6 +52,8 @@ public: virtual bool isInIgnoreList(const QString &whoseList, const QString &who) { return false; } virtual void storeGameInformation(int secondsElapsed, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replays) { } + + void sendIslMessage(const SessionEvent &item, int serverId = -1); protected: void prepareDestroy(); QList clients; @@ -70,6 +74,8 @@ protected: virtual void lockSessionTables() { } virtual void unlockSessionTables() { } virtual bool userSessionExists(const QString &userName) { return false; } + + virtual void doSendIslMessage(const IslMessage &msg, int serverId) { } }; #endif diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index dea8f606..dbe795a1 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -9,7 +9,7 @@ SET(servatrice_SOURCES src/server_logger.cpp src/serversocketinterface.cpp src/serversocketthread.cpp - src/networkserverinterface.cpp + src/isl_interface.cpp ${CMAKE_CURRENT_BINARY_DIR}/version_string.cpp ) SET(servatrice_HEADERS @@ -17,7 +17,7 @@ SET(servatrice_HEADERS src/server_logger.h src/serversocketinterface.h src/serversocketthread.h - src/networkserverinterface.h + src/isl_interface.h ) SET(QT_DONTUSE_QTGUI) diff --git a/servatrice/src/networkserverinterface.cpp b/servatrice/src/isl_interface.cpp similarity index 58% rename from servatrice/src/networkserverinterface.cpp rename to servatrice/src/isl_interface.cpp index a6397913..b667867d 100644 --- a/servatrice/src/networkserverinterface.cpp +++ b/servatrice/src/isl_interface.cpp @@ -1,50 +1,49 @@ -#include "networkserverinterface.h" +#include "isl_interface.h" #include #include "server_logger.h" #include "main.h" #include "server_protocolhandler.h" #include "server_room.h" -#include "pb/servernetwork_message.pb.h" +#include "pb/isl_message.pb.h" #include "pb/event_server_complete_list.pb.h" #include -void NetworkServerInterface::sharedCtor(const QSslCertificate &cert, const QSslKey &privateKey) +void IslInterface::sharedCtor(const QSslCertificate &cert, const QSslKey &privateKey) { socket = new QSslSocket(this); socket->setLocalCertificate(cert); socket->setPrivateKey(privateKey); 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(int _socketDescriptor, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server) +IslInterface::IslInterface(int _socketDescriptor, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server) : QObject(), socketDescriptor(_socketDescriptor), server(_server), messageInProgress(false) { sharedCtor(cert, privateKey); } -NetworkServerInterface::NetworkServerInterface(const QString &_peerHostName, const QString &_peerAddress, int _peerPort, const QSslCertificate &_peerCert, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server) - : QObject(), peerHostName(_peerHostName), peerAddress(_peerAddress), peerPort(_peerPort), peerCert(_peerCert), server(_server), messageInProgress(false) +IslInterface::IslInterface(int _serverId, const QString &_peerHostName, const QString &_peerAddress, int _peerPort, const QSslCertificate &_peerCert, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server) + : QObject(), serverId(_serverId), peerHostName(_peerHostName), peerAddress(_peerAddress), peerPort(_peerPort), peerCert(_peerCert), server(_server), messageInProgress(false) { sharedCtor(cert, privateKey); } -NetworkServerInterface::~NetworkServerInterface() +IslInterface::~IslInterface() { - logger->logMessage("[SN] session ended", this); + logger->logMessage("[ISL] session ended", this); flushOutputBuffer(); } -void NetworkServerInterface::initServer() +void IslInterface::initServer() { socket->setSocketDescriptor(socketDescriptor); - logger->logMessage(QString("[SN] incoming connection: %1").arg(socket->peerAddress().toString())); + logger->logMessage(QString("[ISL] incoming connection: %1").arg(socket->peerAddress().toString())); QList serverList = server->getServerList(); int listIndex = -1; @@ -54,7 +53,7 @@ void NetworkServerInterface::initServer() break; } if (listIndex == -1) { - logger->logMessage(QString("[SN] address %1 unknown, terminating connection").arg(socket->peerAddress().toString())); + logger->logMessage(QString("[ISL] address %1 unknown, terminating connection").arg(socket->peerAddress().toString())); deleteLater(); return; } @@ -63,20 +62,21 @@ void NetworkServerInterface::initServer() if (!socket->waitForEncrypted(5000)) { QList sslErrors(socket->sslErrors()); if (sslErrors.isEmpty()) - qDebug() << "[SN] SSL handshake timeout, terminating connection"; + qDebug() << "[ISL] SSL handshake timeout, terminating connection"; else - qDebug() << "[SN] SSL errors:" << sslErrors; + qDebug() << "[ISL] SSL errors:" << sslErrors; deleteLater(); return; } if (serverList[listIndex].cert == socket->peerCertificate()) - logger->logMessage(QString("[SN] Peer authenticated as " + serverList[listIndex].hostname)); + logger->logMessage(QString("[ISL] Peer authenticated as " + serverList[listIndex].hostname)); else { - logger->logMessage(QString("[SN] Authentication failed, terminating connection")); + logger->logMessage(QString("[ISL] Authentication failed, terminating connection")); deleteLater(); return; } + serverId = serverList[listIndex].id; Event_ServerCompleteList event; event.set_server_id(server->getServerId()); @@ -93,13 +93,20 @@ void NetworkServerInterface::initServer() event.add_room_list()->CopyFrom(room->getInfo(true, true, false)); } - ServerNetworkMessage message; - message.set_message_type(ServerNetworkMessage::SESSION_EVENT); + IslMessage message; + message.set_message_type(IslMessage::SESSION_EVENT); SessionEvent *sessionEvent = message.mutable_session_event(); sessionEvent->GetReflection()->MutableMessage(sessionEvent, event.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(event); - transmitMessage(message); - server->addNetworkServerInterface(this); + server->islLock.lockForWrite(); + if (server->islConnectionExists(serverId)) { + qDebug() << "[ISL] Duplicate connection to #" << serverId << "terminating connection"; + deleteLater(); + } else { + transmitMessage(message); + server->addIslInterface(serverId, this); + } + server->islLock.unlock(); roomIterator.toFront(); while (roomIterator.hasNext()) @@ -107,33 +114,42 @@ void NetworkServerInterface::initServer() server->serverMutex.unlock(); } -void NetworkServerInterface::initClient() +void IslInterface::initClient() { - QList expectedErrors; - expectedErrors.append(QSslError(QSslError::SelfSignedCertificate, peerCert)); - socket->ignoreSslErrors(expectedErrors); + QList expectedErrors; + expectedErrors.append(QSslError(QSslError::SelfSignedCertificate, peerCert)); + socket->ignoreSslErrors(expectedErrors); - qDebug() << "[SN] Connecting to " << peerAddress << ":" << peerPort; + qDebug() << "[ISL] Connecting to #" << serverId << ":" << peerAddress << ":" << peerPort; socket->connectToHostEncrypted(peerAddress, peerPort, peerHostName); if (!socket->waitForConnected(5000)) { - qDebug() << "[SN] Socket error:" << socket->errorString(); + qDebug() << "[ISL] Socket error:" << socket->errorString(); deleteLater(); return; } if (!socket->waitForEncrypted(5000)) { QList sslErrors(socket->sslErrors()); if (sslErrors.isEmpty()) - qDebug() << "[SN] SSL handshake timeout, terminating connection"; + qDebug() << "[ISL] SSL handshake timeout, terminating connection"; else - qDebug() << "[SN] SSL errors:" << sslErrors; + qDebug() << "[ISL] SSL errors:" << sslErrors; deleteLater(); return; } - server->addNetworkServerInterface(this); + + server->islLock.lockForWrite(); + if (server->islConnectionExists(serverId)) { + qDebug() << "[ISL] Duplicate connection to #" << serverId << "terminating connection"; + deleteLater(); + return; + } + + server->addIslInterface(serverId, this); + server->islLock.unlock(); } -void NetworkServerInterface::flushOutputBuffer() +void IslInterface::flushOutputBuffer() { QMutexLocker locker(&outputBufferMutex); if (outputBuffer.isEmpty()) @@ -144,7 +160,7 @@ void NetworkServerInterface::flushOutputBuffer() outputBuffer.clear(); } -void NetworkServerInterface::readClient() +void IslInterface::readClient() { QByteArray data = socket->readAll(); server->incRxBytes(data.size()); @@ -165,7 +181,7 @@ void NetworkServerInterface::readClient() if (inputBuffer.size() < messageLength) return; - ServerNetworkMessage newMessage; + IslMessage newMessage; newMessage.ParseFromArray(inputBuffer.data(), messageLength); inputBuffer.remove(0, messageLength); messageInProgress = false; @@ -174,14 +190,18 @@ void NetworkServerInterface::readClient() } while (!inputBuffer.isEmpty()); } -void NetworkServerInterface::catchSocketError(QAbstractSocket::SocketError socketError) +void IslInterface::catchSocketError(QAbstractSocket::SocketError socketError) { - qDebug() << "[SN] Socket error:" << socketError; + qDebug() << "[ISL] Socket error:" << socketError; + + server->islLock.lockForWrite(); + server->removeIslInterface(serverId); + server->islLock.unlock(); deleteLater(); } -void NetworkServerInterface::transmitMessage(const ServerNetworkMessage &item) +void IslInterface::transmitMessage(const IslMessage &item) { QByteArray buf; unsigned int size = item.ByteSize(); @@ -198,7 +218,7 @@ void NetworkServerInterface::transmitMessage(const ServerNetworkMessage &item) emit outputBufferChanged(); } -void NetworkServerInterface::processMessage(const ServerNetworkMessage &item) +void IslInterface::processMessage(const IslMessage &item) { qDebug() << QString::fromStdString(item.DebugString()); } diff --git a/servatrice/src/networkserverinterface.h b/servatrice/src/isl_interface.h similarity index 52% rename from servatrice/src/networkserverinterface.h rename to servatrice/src/isl_interface.h index 47a4398e..a59377b3 100644 --- a/servatrice/src/networkserverinterface.h +++ b/servatrice/src/isl_interface.h @@ -1,16 +1,16 @@ -#ifndef NETWORKSERVERINTERFACE_H -#define NETWORKSERVERINTERFACE_H +#ifndef ISL_INTERFACE_H +#define ISL_INTERFACE_H #include "servatrice.h" #include -#include #include class Servatrice; class QSslSocket; -class ServerNetworkMessage; +class QSslKey; +class IslMessage; -class NetworkServerInterface : public QObject { +class IslInterface : public QObject { Q_OBJECT private slots: void readClient(); @@ -19,6 +19,7 @@ private slots: signals: void outputBufferChanged(); private: + int serverId; int socketDescriptor; QString peerHostName, peerAddress; int peerPort; @@ -32,17 +33,17 @@ private: bool messageInProgress; int messageLength; - void processMessage(const ServerNetworkMessage &item); + void processMessage(const IslMessage &item); void sharedCtor(const QSslCertificate &cert, const QSslKey &privateKey); public slots: void initServer(); void initClient(); public: - NetworkServerInterface(int socketDescriptor, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server); - NetworkServerInterface(const QString &peerHostName, const QString &peerAddress, int peerPort, const QSslCertificate &peerCert, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server); - ~NetworkServerInterface(); + IslInterface(int socketDescriptor, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server); + IslInterface(int _serverId, const QString &peerHostName, const QString &peerAddress, int peerPort, const QSslCertificate &peerCert, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server); + ~IslInterface(); - void transmitMessage(const ServerNetworkMessage &item); + void transmitMessage(const IslMessage &item); }; #endif diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index dfd9c35f..687e9221 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -25,7 +25,7 @@ #include "server_room.h" #include "serversocketinterface.h" #include "serversocketthread.h" -#include "networkserverinterface.h" +#include "isl_interface.h" #include "server_logger.h" #include "main.h" #include "passwordhasher.h" @@ -48,12 +48,12 @@ void Servatrice_GameServer::incomingConnection(int socketDescriptor) } } -void Servatrice_NetworkServer::incomingConnection(int socketDescriptor) +void Servatrice_IslServer::incomingConnection(int socketDescriptor) { QThread *thread = new QThread; connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - NetworkServerInterface *interface = new NetworkServerInterface(socketDescriptor, cert, privateKey, server); + IslInterface *interface = new IslInterface(socketDescriptor, cert, privateKey, server); interface->moveToThread(thread); connect(interface, SIGNAL(destroyed()), thread, SLOT(quit())); @@ -122,7 +122,7 @@ Servatrice::Servatrice(QSettings *_settings, QObject *parent) maxGamesPerUser = settings->value("security/max_games_per_user").toInt(); try { if (settings->value("servernetwork/active", 0).toInt()) { - qDebug() << "Connecting to server network."; + qDebug() << "Connecting to ISL network."; const QString certFileName = settings->value("servernetwork/ssl_cert").toString(); const QString keyFileName = settings->value("servernetwork/ssl_key").toString(); qDebug() << "Loading certificate..."; @@ -140,15 +140,6 @@ Servatrice::Servatrice(QSettings *_settings, QObject *parent) 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(); @@ -160,7 +151,7 @@ Servatrice::Servatrice(QSettings *_settings, QObject *parent) QThread *thread = new QThread; connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - NetworkServerInterface *interface = new NetworkServerInterface(prop.hostname, prop.address.toString(), prop.controlPort, prop.cert, cert, key, this); + IslInterface *interface = new IslInterface(prop.id, prop.hostname, prop.address.toString(), prop.controlPort, prop.cert, cert, key, this); interface->moveToThread(thread); connect(interface, SIGNAL(destroyed()), thread, SLOT(quit())); @@ -168,6 +159,15 @@ Servatrice::Servatrice(QSettings *_settings, QObject *parent) QMetaObject::invokeMethod(interface, "initClient", Qt::BlockingQueuedConnection); } + const int networkPort = settings->value("servernetwork/port", 14747).toInt(); + qDebug() << "Starting ISL server on port" << networkPort; + + islServer = new Servatrice_IslServer(this, cert, key, this); + if (islServer->listen(QHostAddress::Any, networkPort)) + qDebug() << "ISL server listening."; + else + throw QString("islServer->listen(): Error."); + } } catch (QString error) { qDebug() << "ERROR --" << error; } @@ -556,8 +556,9 @@ int Servatrice::startSession(const QString &userName, const QString &address) return -1; QSqlQuery query; - query.prepare("insert into " + dbPrefix + "_sessions (user_name, ip_address, start_time) values(:user_name, :ip_address, NOW())"); + query.prepare("insert into " + dbPrefix + "_sessions (user_name, id_server, ip_address, start_time) values(:user_name, :id_server, :ip_address, NOW())"); query.bindValue(":user_name", userName); + query.bindValue(":id_server", serverId); query.bindValue(":ip_address", address); if (execSqlQuery(query)) return query.lastInsertId().toInt(); @@ -574,7 +575,7 @@ void Servatrice::endSession(int sessionId) return; QSqlQuery query; - query.exec("lock tables " + dbPrefix + "_sessions read"); + query.exec("lock tables " + dbPrefix + "_sessions write"); query.prepare("update " + dbPrefix + "_sessions set end_time=NOW() where id = :id_session"); query.bindValue(":id_session", sessionId); execSqlQuery(query); @@ -839,13 +840,42 @@ void Servatrice::shutdownTimeout() deleteLater(); } -void Servatrice::addNetworkServerInterface(NetworkServerInterface *interface) +bool Servatrice::islConnectionExists(int serverId) const { - networkServerInterfaces.append(interface); + // Only call with islLock locked at least for reading + + return islInterfaces.contains(serverId); } -void Servatrice::removeNetworkServerInterface(NetworkServerInterface *interface) +void Servatrice::addIslInterface(int serverId, IslInterface *interface) { - // XXX we probably need to delete everything that belonged to it... - networkServerInterfaces.removeAt(networkServerInterfaces.indexOf(interface)); + // Only call with islLock locked for writing + + islInterfaces.insert(serverId, interface); +} + +void Servatrice::removeIslInterface(int serverId) +{ + // Only call with islLock locked for writing + + // XXX we probably need to delete everything that belonged to it... + islInterfaces.remove(serverId); +} + +void Servatrice::doSendIslMessage(const IslMessage &msg, int serverId) +{ + QReadLocker locker(&islLock); + + qDebug() << "hallo"; + if (serverId == -1) { + QMapIterator islIterator(islInterfaces); + while (islIterator.hasNext()) { + qDebug() << "welt"; + islIterator.next().value()->transmitMessage(msg); + } + } else { + IslInterface *interface = islInterfaces.value(serverId); + if (interface) + interface->transmitMessage(msg); + } } diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index e247fc1d..87e466f5 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "server.h" class QSqlDatabase; @@ -35,7 +36,7 @@ class QTimer; class GameReplay; class Servatrice; class ServerSocketInterface; -class NetworkServerInterface; +class IslInterface; class Servatrice_GameServer : public QTcpServer { Q_OBJECT @@ -49,14 +50,14 @@ protected: void incomingConnection(int socketDescriptor); }; -class Servatrice_NetworkServer : public QTcpServer { +class Servatrice_IslServer : public QTcpServer { Q_OBJECT private: Servatrice *server; QSslCertificate cert; QSslKey privateKey; public: - Servatrice_NetworkServer(Servatrice *_server, const QSslCertificate &_cert, const QSslKey &_privateKey, QObject *parent = 0) + Servatrice_IslServer(Servatrice *_server, const QSslCertificate &_cert, const QSslKey &_privateKey, QObject *parent = 0) : QTcpServer(parent), server(_server), cert(_cert), privateKey(_privateKey) { } protected: void incomingConnection(int socketDescriptor); @@ -115,8 +116,10 @@ public: int getUserIdInDB(const QString &name); void storeGameInformation(int secondsElapsed, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replays); - void addNetworkServerInterface(NetworkServerInterface *interface); - void removeNetworkServerInterface(NetworkServerInterface *interface); + bool islConnectionExists(int serverId) const; + void addIslInterface(int serverId, IslInterface *interface); + void removeIslInterface(int serverId); + QReadWriteLock islLock; QList getServerList() const; protected: @@ -129,6 +132,8 @@ protected: void lockSessionTables(); void unlockSessionTables(); bool userSessionExists(const QString &userName); + + void doSendIslMessage(const IslMessage &msg, int serverId); private: enum AuthenticationMethod { AuthenticationNone, AuthenticationSql }; enum DatabaseType { DatabaseNone, DatabaseMySql }; @@ -136,7 +141,7 @@ private: DatabaseType databaseType; QTimer *pingClock, *statusUpdateClock; Servatrice_GameServer *gameServer; - Servatrice_NetworkServer *networkServer; + Servatrice_IslServer *islServer; QString serverName; QString loginMessage; QString dbPrefix; @@ -158,7 +163,7 @@ private: QList serverList; void updateServerList(); - QList networkServerInterfaces; + QMap islInterfaces; }; #endif