diff --git a/.gitmodules b/.gitmodules index 39d96f8..af6b6e0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = QuasarAppLib url = https://github.com/QuasarApp/QuasarAppLib.git +[submodule "SnakeServer/Qt-Secret"] + path = SnakeServer/Qt-Secret + url = git@github.com:QuasarApp/Qt-Secret.git diff --git a/SnakeServer/ClientProtocol/ClientProtocol.pri b/SnakeServer/ClientProtocol/ClientProtocol.pri index 82ddd65..dead322 100644 --- a/SnakeServer/ClientProtocol/ClientProtocol.pri +++ b/SnakeServer/ClientProtocol/ClientProtocol.pri @@ -18,6 +18,7 @@ CONFIG(release, debug|release): { LIBS += -L$$CLIENTPROTOCOL_LIB_OUTPUT_DIR -lClientProtocol include($$PWD/ClientProtocolIncludes.pri) +include($$PWD/../Qt-Secret/src/Qt-Secret.pri) diff --git a/SnakeServer/ClientProtocol/ClientProtocol.pro b/SnakeServer/ClientProtocol/ClientProtocol.pro index d8c2e8a..f0df27f 100644 --- a/SnakeServer/ClientProtocol/ClientProtocol.pro +++ b/SnakeServer/ClientProtocol/ClientProtocol.pro @@ -39,12 +39,14 @@ CONFIG(release, debug|release): { include($$PWD/../../QuasarAppLib/QuasarLib.pri) include($$PWD/../../SnakeUtils/SnakeUtils.pri) +include($$PWD/../Qt-Secret/src/Qt-Secret.pri) SOURCES += \ Objects/basenetworkobject.cpp \ Objects/map.cpp \ Objects/objdata.cpp \ + Objects/pubkey.cpp \ Objects/snake.cpp \ clientprotocol.cpp \ client.cpp \ @@ -53,6 +55,7 @@ SOURCES += \ Objects/login.cpp \ networkclasssize.cpp \ Objects/player.cpp \ + rsakeyspool.cpp \ server.cpp \ factorynetobjects.cpp \ connectioninfo.cpp \ @@ -63,6 +66,7 @@ HEADERS += \ Objects/basenetworkobject.h \ Objects/map.h \ Objects/objdata.h \ + Objects/pubkey.h \ Objects/snake.h \ clientprotocol.h \ clientprotocol_global.h \ @@ -72,6 +76,7 @@ HEADERS += \ networkclasssize.h \ client.h \ Objects/player.h \ + rsakeyspool.h \ server.h \ cp.h \ config.h \ diff --git a/SnakeServer/ClientProtocol/Objects/pubkey.cpp b/SnakeServer/ClientProtocol/Objects/pubkey.cpp new file mode 100644 index 0000000..264b83a --- /dev/null +++ b/SnakeServer/ClientProtocol/Objects/pubkey.cpp @@ -0,0 +1,58 @@ +#include "pubkey.h" +#include "clientprotocol.h" + +namespace ClientProtocol { + +PubKey::PubKey() { + _class = static_cast(Command::PubKey); +} + +QRSAEncryption::Rsa PubKey::getTypeKey() const { + return typeKey; +} + +void PubKey::setTypeKey(const QRSAEncryption::Rsa &value) { + typeKey = value; +} + +QByteArray PubKey::getKey() const { + return key; +} + +void PubKey::setKey(const QByteArray &value) { + key = value; +} + +BaseNetworkObject *PubKey::create() const { + return new PubKey(); +} + +NetworkClassSize PubKey::classSize() const { + return BaseNetworkObject::classSize() + + QRSAEncryption::getBytesSize(typeKey) + + static_cast(sizeof (int)) + + getTypeSize(typeKey); +} + +QDataStream &PubKey::writeToStream(QDataStream &stream) const { + BaseNetworkObject::writeToStream(stream); + stream << int(typeKey); + stream << key; + return stream; +} + +QDataStream &PubKey::readFromStream(QDataStream &stream) { + BaseNetworkObject::readFromStream(stream); + int _typeKey; + stream >> _typeKey; + typeKey = static_cast(_typeKey); + stream >> key; + return stream; +} + +bool PubKey::isValid() const { + return static_cast(key.size()) == QRSAEncryption::getBytesSize(typeKey) + && BaseNetworkObject::isValid(); +} + +} diff --git a/SnakeServer/ClientProtocol/Objects/pubkey.h b/SnakeServer/ClientProtocol/Objects/pubkey.h new file mode 100644 index 0000000..9504959 --- /dev/null +++ b/SnakeServer/ClientProtocol/Objects/pubkey.h @@ -0,0 +1,30 @@ +#ifndef PUBKEY_H +#define PUBKEY_H +#include + +#include "basenetworkobject.h" + +namespace ClientProtocol { + +class PubKey: public BaseNetworkObject +{ +private: + QRSAEncryption::Rsa typeKey; + QByteArray key; + +public: + PubKey(); + + BaseNetworkObject *create() const override; + NetworkClassSize classSize() const override; + QDataStream &writeToStream(QDataStream &stream) const override; + QDataStream &readFromStream(QDataStream &stream) override; + bool isValid() const override; + + QRSAEncryption::Rsa getTypeKey() const; + void setTypeKey(const QRSAEncryption::Rsa &value); + QByteArray getKey() const; + void setKey(const QByteArray &value); +}; +} +#endif // PUBKEY_H diff --git a/SnakeServer/ClientProtocol/clientprotocol.cpp b/SnakeServer/ClientProtocol/clientprotocol.cpp index 85c5894..ec5f55d 100644 --- a/SnakeServer/ClientProtocol/clientprotocol.cpp +++ b/SnakeServer/ClientProtocol/clientprotocol.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #define DEFAULT_GAME_PORT 7777 @@ -183,6 +184,12 @@ bool initClientProtockol() { static_cast(Command::Map))) { return false; } + + if (!FactoryNetObjects::regType( + static_cast(Command::PubKey))) { + return false; + } + return true; } diff --git a/SnakeServer/ClientProtocol/clientprotocol.h b/SnakeServer/ClientProtocol/clientprotocol.h index 61133c9..9f1207a 100644 --- a/SnakeServer/ClientProtocol/clientprotocol.h +++ b/SnakeServer/ClientProtocol/clientprotocol.h @@ -28,7 +28,8 @@ enum class Command: quint8 { GetItem = 0x06, Player = 0x07, Snake = 0x08, - Map = 0x09 + Map = 0x09, + PubKey = 0x0a }; bool isValidSize(quint8 type, unsigned int size); diff --git a/SnakeServer/ClientProtocol/connectioninfo.cpp b/SnakeServer/ClientProtocol/connectioninfo.cpp index 4c17728..8b85384 100644 --- a/SnakeServer/ClientProtocol/connectioninfo.cpp +++ b/SnakeServer/ClientProtocol/connectioninfo.cpp @@ -1,14 +1,67 @@ #include "connectioninfo.h" +#include -QString ConnectionInfo::getAddres() const -{ - return addres; +namespace ClientProtocol { + + +int Connectioninfo::getKarma() const { + return karma; } -ConnectionInfo::ConnectionInfo(const QString &addrrss) { - addres = addrrss; +void Connectioninfo::setKarma(int value) { + karma = value; } -bool ConnectionInfo::operator ==(const ConnectionInfo &otherInfo) const { - return addres == otherInfo.addres; +RSAKeyPair Connectioninfo::getRSAKey() const { + return RSAKey; +} + +void Connectioninfo::setRSAKey(const RSAKeyPair &value) { + RSAKey = value; +} + +QTcpSocket *Connectioninfo::getSct() const { + return sct; +} + +void Connectioninfo::setSct(QTcpSocket *value) { + sct = value; +} + +void Connectioninfo::disconnect() { + if (sct) { + sct->close(); + delete sct; + sct = nullptr; + } +} + +void Connectioninfo::ban() { + karma = BANED_KARMA; + disconnect(); +} + +bool Connectioninfo::isBaned() const { + return karma < 1; +} + +void Connectioninfo::unBan() { + karma = RESTORE_KARMA; +} + +bool Connectioninfo::isValid() const { + return sct && !RSAKey.pub.isEmpty() && !RSAKey.priv.isEmpty(); +} + +Connectioninfo::Connectioninfo(QTcpSocket *tcp, int kar, RSAKeyPair keys) { + sct = tcp; + karma = kar; + RSAKey = keys; +} + +Connectioninfo::~Connectioninfo() { + if (sct) { + sct->deleteLater(); + } +} } diff --git a/SnakeServer/ClientProtocol/connectioninfo.h b/SnakeServer/ClientProtocol/connectioninfo.h index 1600cda..0ddfef9 100644 --- a/SnakeServer/ClientProtocol/connectioninfo.h +++ b/SnakeServer/ClientProtocol/connectioninfo.h @@ -1,17 +1,43 @@ #ifndef CONNECTIONINFO_H #define CONNECTIONINFO_H -#include +#include "rsakeyspool.h" + + +class QTcpSocket; +namespace ClientProtocol { + +#define NOT_VALID_CARMA 0xFF +#define DEFAULT_KARMA 100 +#define RESTORE_KARMA 20 +#define BANED_KARMA 0 + +class Connectioninfo { + + QTcpSocket *sct = nullptr; + int karma = DEFAULT_KARMA; + RSAKeyPair RSAKey; -class ConnectionInfo -{ -private: - QString addres = ""; public: - ConnectionInfo(const QString & addrrss); - bool operator == (const ConnectionInfo &otherInfo) const; + void disconnect(); - QString getAddres() const; + void ban(); + bool isBaned() const; + void unBan(); + + bool isValid() const; + + Connectioninfo(QTcpSocket * tcp = nullptr, + int kar = NOT_VALID_CARMA, + RSAKeyPair keys = RSAKeyPair()); + ~Connectioninfo(); + + int getKarma() const; + void setKarma(int value); + RSAKeyPair getRSAKey() const; + void setRSAKey(const RSAKeyPair &value); + QTcpSocket *getSct() const; + void setSct(QTcpSocket *value); }; - +} #endif // CONNECTIONINFO_H diff --git a/SnakeServer/ClientProtocol/rsakeyspool.cpp b/SnakeServer/ClientProtocol/rsakeyspool.cpp new file mode 100644 index 0000000..8367cf8 --- /dev/null +++ b/SnakeServer/ClientProtocol/rsakeyspool.cpp @@ -0,0 +1,27 @@ +#include "rsakeyspool.h" +#include + +namespace ClientProtocol { + +bool RSAKeysPool::take(QRSAEncryption::Rsa rsa, RSAKeyPair& res) { + QMutexLocker locker(&multithread); + + if (pool.value(rsa).isEmpty()) { + emit sigKeyTaked(); + return false; + } + + res = pool.value(rsa).begin().value(); + return true; +} + +void RSAKeysPool::addNewKey(QRSAEncryption::Rsa rsa, const RSAKeyPair& key) { + QMutexLocker locker(&multithread); + pool[rsa].insert(static_cast(time(nullptr)), key); +} + +int RSAKeysPool::size(QRSAEncryption::Rsa rsa) const { + return pool.value(rsa).size(); +} + +} diff --git a/SnakeServer/ClientProtocol/rsakeyspool.h b/SnakeServer/ClientProtocol/rsakeyspool.h new file mode 100644 index 0000000..9890194 --- /dev/null +++ b/SnakeServer/ClientProtocol/rsakeyspool.h @@ -0,0 +1,35 @@ +#ifndef RSAKEYSPOOL_H +#define RSAKEYSPOOL_H +#include +#include +#include +#include +#include + +namespace ClientProtocol { + +struct RSAKeyPair { + QByteArray pub; + QByteArray priv; +}; + +typedef QMap KeysPool; +typedef QHash PoolData; +class RSAKeysPool: public QObject { + Q_OBJECT +private: + PoolData pool; + + QMutex multithread; + +public: + bool take(QRSAEncryption::Rsa rsa, RSAKeyPair &res); + void addNewKey(QRSAEncryption::Rsa rsa, const RSAKeyPair& key); + int size(QRSAEncryption::Rsa rsa) const; +signals: + + void sigKeyTaked(); +}; + +} +#endif // RSAKEYSPOOL_H diff --git a/SnakeServer/ClientProtocol/server.cpp b/SnakeServer/ClientProtocol/server.cpp index 8b05bdc..25f59c0 100644 --- a/SnakeServer/ClientProtocol/server.cpp +++ b/SnakeServer/ClientProtocol/server.cpp @@ -2,6 +2,7 @@ #include "quasarapp.h" #include #include +#include #include "clientprotocol.h" namespace ClientProtocol { @@ -72,29 +73,45 @@ bool Server::sendPackage(Package &pkg, QTcpSocket * target) { } void Server::ban(quint32 target) { - _connections[target].karma = BANED_KARMA; - if (_connections[target].sct) { - _connections[target].sct->close(); - } + _connections[target].ban(); } void Server::unBan(quint32 target) { - _connections[target].karma = RESTORE_KARMA; + _connections[target].unBan(); } -void Server::registerSocket(QTcpSocket *socket) { +bool Server::registerSocket(QTcpSocket *socket) { auto address = socket->peerAddress().toIPv4Address(); - _connections[address].karma = DEFAULT_KARMA; - _connections[address].sct = socket; + if (_pool) { + QuasarAppUtils::Params::verboseLog("key pool is not inited", QuasarAppUtils::Error); + return false; + } + + RSAKeyPair pair; + + if (!_pool->take(QRSAEncryption::RSA_128, pair)) { + QuasarAppUtils::Params::verboseLog("key pool is empty", QuasarAppUtils::Error); + return false; + } + + _connections[address] = Connectioninfo(socket, DEFAULT_KARMA, + pair); connect(socket, &QTcpSocket::readyRead, this, &Server::avelableBytes); connect(socket, &QTcpSocket::disconnected, this, &Server::handleDisconected); + if (!sendPubKey(socket, pair.pub)) { + QuasarAppUtils::Params::verboseLog("not sendet pub key to client" + "generate new key!", QuasarAppUtils::Error); + return false; + } + + return true; } bool Server::changeKarma(quint32 addresss, int diff) { - auto objKarma = _connections.value(addresss).karma; + auto objKarma = _connections.value(addresss).getKarma(); if (objKarma >= NOT_VALID_CARMA) { return false; @@ -104,28 +121,21 @@ bool Server::changeKarma(quint32 addresss, int diff) { return false; } - _connections[addresss].karma += diff; + _connections[addresss].setKarma(objKarma + diff); return true; } bool Server::isBaned(const QTcpSocket * adr) const { - return _connections.value(adr->peerAddress().toIPv4Address()).karma < 1; -} - -void Server::saveKarma() const { - -} - -bool Server::loadKarma() { - return false; + return _connections.value(adr->peerAddress().toIPv4Address()).isBaned(); } int Server::connectionsCount() const { int count = 0; for (auto i : _connections) { - if (i.sct) { - if (!i.sct->isValid()) { - // log about warning + if (i.getSct()) { + if (!i.getSct()->isValid()) { + QuasarAppUtils::Params::verboseLog("connection count, findet not valid socket", + QuasarAppUtils::Warning); } count++; @@ -134,6 +144,28 @@ int Server::connectionsCount() const { return count; } +bool Server::sendPubKey(QTcpSocket * target, const QByteArray &pubKey) { + + Package pcg; + + PubKey pubkey; + + pubkey.setKey(pubKey); + pubkey.setTypeKey(QRSAEncryption::RSA_128); + pubkey.setId(0); + + if (!pubkey.isValid()) { + return false; + } + + if (!(pcg.create(&pubkey, Type::Responke))) { + return false; + }; + + return sendPackage(pcg, target); + +} + void Server::avelableBytes() { auto client = dynamic_cast(sender()); @@ -167,8 +199,7 @@ void Server::handleDisconected() { unsigned int address = _sender->peerAddress().toIPv4Address(); if (_connections.contains(address)) { - _connections[address].sct = nullptr; - _sender->deleteLater(); + _connections[address].disconnect(); } else { QuasarAppUtils::Params::verboseLog("system error in void Server::handleDisconected()" " address not valid", @@ -195,9 +226,10 @@ void Server::handleIncommingConnection() { } } -Server::Server(QObject *ptr) : +Server::Server(RSAKeysPool *pool, QObject *ptr) : QTcpServer (ptr) { + _pool = pool; connect(this, &Server::newConnection, this, &Server::handleIncommingConnection); } @@ -234,7 +266,7 @@ void Server::badRequest(quint32 address) { QuasarAppUtils::Error); }; - if (!sendPackage(pcg, client.sct)) { + if (!sendPackage(pcg, client.getSct())) { return; } } @@ -258,7 +290,7 @@ QString Server::connectionState() const { QStringList Server::baned() const { QStringList list = {}; for (auto i = _connections.begin(); i != _connections.end(); ++i) { - if (i.value().karma <= 0) { + if (i.value().isBaned()) { list.push_back(QHostAddress(i.key()).toString()); } } @@ -266,15 +298,5 @@ QStringList Server::baned() const { return list; } -Connectioninfo::Connectioninfo(QTcpSocket *tcp, int kar) { - sct = tcp; - karma = kar; -} - -Connectioninfo::~Connectioninfo() { - if (sct) { - sct->deleteLater(); - } -} } diff --git a/SnakeServer/ClientProtocol/server.h b/SnakeServer/ClientProtocol/server.h index 30d6fa3..35d7416 100644 --- a/SnakeServer/ClientProtocol/server.h +++ b/SnakeServer/ClientProtocol/server.h @@ -4,28 +4,15 @@ #include "clientprotocol.h" #include +#include "rsakeyspool.h" +#include "connectioninfo.h" namespace ClientProtocol { -#define NOT_VALID_CARMA 0xFF -#define DEFAULT_KARMA 100 -#define RESTORE_KARMA 20 -#define BANED_KARMA 0 - #define CRITICAL_ERROOR -50 #define LOGICK_ERROOR -20 #define REQUEST_ERROR -5 -struct Connectioninfo { - - QTcpSocket *sct = nullptr; - int karma; - - Connectioninfo(QTcpSocket * tcp = nullptr, - int kar = NOT_VALID_CARMA); - ~Connectioninfo(); - -}; class CLIENTPROTOCOLSHARED_EXPORT Server : public QTcpServer { @@ -33,17 +20,17 @@ class CLIENTPROTOCOLSHARED_EXPORT Server : public QTcpServer private: Package _downloadPackage; QHash _connections; -// QHash _karma; + RSAKeysPool * _pool = nullptr; bool parsePackage(const Package &pkg, QTcpSocket * sender); bool sendPackage(Package &pkg, QTcpSocket * target); - void registerSocket(QTcpSocket *socket); + bool registerSocket(QTcpSocket *socket); bool changeKarma(quint32 addresss, int diff); inline bool isBaned(const QTcpSocket *) const; - void saveKarma() const; - bool loadKarma(); int connectionsCount() const; + bool sendPubKey(QTcpSocket *target, const QByteArray &pubKey); + private slots: void avelableBytes(); @@ -51,7 +38,7 @@ private slots: void handleIncommingConnection(); public: - explicit Server(QObject * ptr = nullptr); + explicit Server(RSAKeysPool * pool, QObject * ptr = nullptr); ~Server() override; bool run(const QString& ip, unsigned short port); void stop(bool reset = false); diff --git a/SnakeServer/Qt-Secret b/SnakeServer/Qt-Secret new file mode 160000 index 0000000..2f334e8 --- /dev/null +++ b/SnakeServer/Qt-Secret @@ -0,0 +1 @@ +Subproject commit 2f334e8d7b3507d66daac0349694402df7c6b6a2 diff --git a/SnakeServer/Server/Server.pro b/SnakeServer/Server/Server.pro index d128b31..1c324b7 100644 --- a/SnakeServer/Server/Server.pro +++ b/SnakeServer/Server/Server.pro @@ -25,6 +25,7 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ item.cpp \ + keysreactor.cpp \ mainserver.cpp \ playerdbdata.cpp \ sqldbcache.cpp \ @@ -41,6 +42,7 @@ CONFIG(release, debug|release): { HEADERS += \ item.h \ + keysreactor.h \ mainserver.h \ playerdbdata.h \ server_global.h \ diff --git a/SnakeServer/Server/keysreactor.cpp b/SnakeServer/Server/keysreactor.cpp new file mode 100644 index 0000000..188c755 --- /dev/null +++ b/SnakeServer/Server/keysreactor.cpp @@ -0,0 +1,60 @@ +#include "keysreactor.h" + +ClientProtocol::RSAKeysPool *KeysReactor::getPool() { + return &_pool; +} + +int KeysReactor::getPoolSize() const { + return _poolSize; +} + +void KeysReactor::setPoolSize(int value) { + if (_poolSize != value) { + _poolSize = value; + handleGenerateNewKeys(); + } +} + +void KeysReactor::handleGenerateNewKeys() { + + + auto generatorFunc = [this] (QRSAEncryption::Rsa rsa) { + + if (_mutexs[rsa]) { + return; + } + + _mutexs[rsa] = true; + + QByteArray pub, priv; + + while (_poolSize > _pool.size(rsa)) { + _generator.generatePairKey(pub, priv, rsa); + _pool.addNewKey(rsa, {pub, priv}); + } + + _mutexs[rsa] = false; + + return; + }; + + QtConcurrent::run(generatorFunc, QRSAEncryption::RSA_64); + QtConcurrent::run(generatorFunc, QRSAEncryption::RSA_128); + +} + +KeysReactor::KeysReactor(QObject * ptr): + QObject (ptr) { + + _mutexs[QRSAEncryption::RSA_64] = false; + _mutexs[QRSAEncryption::RSA_128] = false; + + handleGenerateNewKeys(); + + connect(&_pool, &ClientProtocol::RSAKeysPool::sigKeyTaked, + this, &KeysReactor::handleGenerateNewKeys); +} + +KeysReactor::~KeysReactor() { + +} diff --git a/SnakeServer/Server/keysreactor.h b/SnakeServer/Server/keysreactor.h new file mode 100644 index 0000000..095d986 --- /dev/null +++ b/SnakeServer/Server/keysreactor.h @@ -0,0 +1,33 @@ +#ifndef KEYSREACTOR_H +#define KEYSREACTOR_H + +#include +#include +#include + +#define DEFAULT_KEYPOOL_SIZE 10 + +class KeysReactor: public QObject +{ + Q_OBJECT +private: + ClientProtocol::RSAKeysPool _pool; + QRSAEncryption _generator; + int _poolSize = DEFAULT_KEYPOOL_SIZE; + QHash _mutexs; + +private slots: + void handleGenerateNewKeys(); + +public: + KeysReactor(QObject *ptr = nullptr); + + ~KeysReactor() override; + + ClientProtocol::RSAKeysPool* getPool(); + int getPoolSize() const; + void setPoolSize(int value); + +}; + +#endif // KEYSREACTOR_H diff --git a/SnakeServer/Server/mainserver.cpp b/SnakeServer/Server/mainserver.cpp index 5960a51..8201c8b 100644 --- a/SnakeServer/Server/mainserver.cpp +++ b/SnakeServer/Server/mainserver.cpp @@ -1,3 +1,4 @@ +#include "keysreactor.h" #include "mainserver.h" #include "sqldbcache.h" #include @@ -136,7 +137,10 @@ void MainServer::handleTerminalRequest(QVariantMap obj) { MainServer::MainServer(QObject *ptr): QObject (ptr) { - _serverDaemon = new ClientProtocol::Server(this); + + _keyReactor = new KeysReactor(); + + _serverDaemon = new ClientProtocol::Server(_keyReactor->getPool(), this); _terminalPort = new ServerProtocol::Server(this); _db = new SqlDBCache(); diff --git a/SnakeServer/Server/mainserver.h b/SnakeServer/Server/mainserver.h index 84ada9d..0631650 100644 --- a/SnakeServer/Server/mainserver.h +++ b/SnakeServer/Server/mainserver.h @@ -13,7 +13,7 @@ namespace ClientProtocol { class Server; class BaseNetworkObject; } - +class KeysReactor; class SqlDBCache; class SERVERSHARED_EXPORT MainServer: public QObject @@ -23,7 +23,7 @@ private: ServerProtocol::Server *_terminalPort = nullptr; ClientProtocol::Server *_serverDaemon= nullptr; SqlDBCache* _db = nullptr; - + KeysReactor *_keyReactor = nullptr; bool payItem(int player, int idItem); bool sellItem(int player, int idItem); diff --git a/SnakeServer/SnakeServer.pro b/SnakeServer/SnakeServer.pro index 988b190..bdc7db7 100644 --- a/SnakeServer/SnakeServer.pro +++ b/SnakeServer/SnakeServer.pro @@ -4,6 +4,7 @@ CONFIG += ordered SUBDIRS += \ ServerProtocol \ + Qt-Secret \ ClientProtocol \ Terminal \ Server \