From 41b9d689945e784b160a25d12076600ff4b7ae90 Mon Sep 17 00:00:00 2001 From: Janne Koschinski Date: Tue, 8 May 2018 01:49:13 +0200 Subject: [PATCH] identd: Implement blocking request queueing --- src/core/corenetwork.cpp | 12 ++-- src/core/corenetwork.h | 5 +- src/core/identserver.cpp | 118 +++++++++++++++++++++++++++++---------- src/core/identserver.h | 33 +++++++++-- 4 files changed, 130 insertions(+), 38 deletions(-) diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index 02823a0c..575d20cd 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -108,8 +108,8 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) } if (Quassel::isOptionSet("ident-daemon")) { - connect(this, SIGNAL(socketInitialized(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->identServer(), SLOT(addSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Qt::BlockingQueuedConnection); - connect(this, SIGNAL(socketDisconnected(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->identServer(), SLOT(removeSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16))); + connect(this, SIGNAL(socketInitialized(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16, qint64)), Core::instance()->identServer(), SLOT(addSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16, qint64)), Qt::BlockingQueuedConnection); + connect(this, SIGNAL(socketDisconnected(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16, qint64)), Core::instance()->identServer(), SLOT(removeSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16, qint64))); } } @@ -191,6 +191,10 @@ QByteArray CoreNetwork::userEncode(const QString &userNick, const QString &strin void CoreNetwork::connectToIrc(bool reconnecting) { + if (Core::instance()->identServer()) { + _socketId = Core::instance()->identServer()->addWaitingSocket(); + } + if (!reconnecting && useAutoReconnect() && _autoReconnectCount == 0) { _autoReconnectTimer.setInterval(autoReconnectInterval() * 1000); if (unlimitedReconnectRetries()) @@ -548,7 +552,7 @@ void CoreNetwork::socketInitialized() // Non-SSL connections enter here only once, always emit socketInitialized(...) in these cases // SSL connections call socketInitialized() twice, only emit socketInitialized(...) on the first (not yet encrypted) run if (!server.useSsl || !socket.isEncrypted()) { - emit socketInitialized(identity, localAddress(), localPort(), peerAddress(), peerPort()); + emit socketInitialized(identity, localAddress(), localPort(), peerAddress(), peerPort(), _socketId); } if (server.useSsl && !socket.isEncrypted()) { @@ -617,7 +621,7 @@ void CoreNetwork::socketDisconnected() setConnected(false); emit disconnected(networkId()); - emit socketDisconnected(identityPtr(), localAddress(), localPort(), peerAddress(), peerPort()); + emit socketDisconnected(identityPtr(), localAddress(), localPort(), peerAddress(), peerPort(), _socketId); // Reset disconnect expectations _disconnectExpected = false; if (_quitRequested) { diff --git a/src/core/corenetwork.h b/src/core/corenetwork.h index d2693587..668f3140 100644 --- a/src/core/corenetwork.h +++ b/src/core/corenetwork.h @@ -389,8 +389,8 @@ signals: void sslErrors(const QVariant &errorData); void newEvent(Event *event); - void socketInitialized(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, const QHostAddress &peerAddress, quint16 peerPort); - void socketDisconnected(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, const QHostAddress &peerAddress, quint16 peerPort); + void socketInitialized(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId); + void socketDisconnected(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId); protected: inline virtual IrcChannel *ircChannelFactory(const QString &channelname) { return new CoreIrcChannel(channelname, this); } @@ -453,6 +453,7 @@ private: #else QTcpSocket socket; #endif + qint64 _socketId; CoreUserInputHandler *_userInputHandler; diff --git a/src/core/identserver.cpp b/src/core/identserver.cpp index 21ea5ff6..08b1b677 100644 --- a/src/core/identserver.cpp +++ b/src/core/identserver.cpp @@ -19,11 +19,12 @@ ***************************************************************************/ #include +#include #include "corenetwork.h" #include "identserver.h" -IdentServer::IdentServer(bool strict, QObject *parent) : QObject(parent), _strict(strict) { +IdentServer::IdentServer(bool strict, QObject *parent) : QObject(parent), _strict(strict), _socketId(0), _requestId(0) { connect(&_server, SIGNAL(newConnection()), this, SLOT(incomingConnection())); connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection())); } @@ -90,69 +91,130 @@ void IdentServer::incomingConnection() { } void IdentServer::respond() { - auto *socket = qobject_cast(sender()); + QTcpSocket *socket = qobject_cast(sender()); Q_ASSERT(socket); + qint64 transactionId = _socketId; + if (socket->canReadLine()) { - QByteArray s = socket->readLine(); - if (s.endsWith("\r\n")) - s.chop(2); - else if (s.endsWith("\n")) - s.chop(1); + QByteArray query = socket->readLine(); + if (query.endsWith("\r\n")) + query.chop(2); + else if (query.endsWith("\n")) + query.chop(1); - QList split = s.split(','); + QList split = query.split(','); bool success = false; - uint16_t localPort; + quint16 localPort; if (!split.empty()) { - localPort = split[0].toUShort(&success, 10); + localPort = split[0].trimmed().toUShort(&success, 10); } - QString user; - if (success) { - if (_connections.contains(localPort)) { - user = _connections[localPort]; + Request request{socket, localPort, query, transactionId, _requestId++}; + if (!success) { + responseUnavailable(request); + } else if (!responseAvailable(request)) { + if (hasSocketsBelowId(transactionId)) { + _requestQueue.emplace_back(request); } else { - success = false; + responseUnavailable(request); } } + } +} - QString data; - if (success) { - data += s + " : USERID : Quassel : " + user + "\r\n"; - } else { - data += s + " : ERROR : NO-USER\r\n"; - } +bool IdentServer::responseAvailable(Request request) { + QString user; + bool success = true; + if (_connections.contains(request.localPort)) { + user = _connections[request.localPort]; + } else { + success = false; + } + + QString data; + if (success) { + data += request.query + " : USERID : Quassel : " + user + "\r\n"; - socket->write(data.toUtf8()); - socket->flush(); - socket->close(); - socket->deleteLater(); + request.socket->write(data.toUtf8()); + request.socket->flush(); + request.socket->close(); } + return success; +} + +void IdentServer::responseUnavailable(Request request) { + QString data = request.query + " : ERROR : NO-USER\r\n"; + + request.socket->write(data.toUtf8()); + request.socket->flush(); + request.socket->close(); } bool IdentServer::addSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, - const QHostAddress &peerAddress, quint16 peerPort) { + const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId) { Q_UNUSED(localAddress) Q_UNUSED(peerAddress) Q_UNUSED(peerPort) const CoreNetwork *network = qobject_cast(sender()); _connections[localPort] = network->coreSession()->strictCompliantIdent(identity);; + processWaiting(socketId); return true; } -//! not yet implemented bool IdentServer::removeSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, - const QHostAddress &peerAddress, quint16 peerPort) { + const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId) { Q_UNUSED(identity) Q_UNUSED(localAddress) Q_UNUSED(peerAddress) Q_UNUSED(peerPort) _connections.remove(localPort); + processWaiting(socketId); return true; } + +qint64 IdentServer::addWaitingSocket() { + qint64 newSocketId = _socketId++; + _waiting.push_back(newSocketId); + return newSocketId; +} + +bool IdentServer::hasSocketsBelowId(qint64 id) { + return std::any_of(_waiting.begin(), _waiting.end(), [=](qint64 socketId) { + return socketId <= id; + }); +} + +void IdentServer::removeWaitingSocket(qint64 socketId) { + _waiting.remove(socketId); +} + +void IdentServer::processWaiting(qint64 socketId) { + qint64 lowestSocketId = std::numeric_limits::max(); + for (qint64 id : _waiting) { + if (id < lowestSocketId) { + lowestSocketId = id; + } + } + removeWaitingSocket(socketId); + _requestQueue.remove_if([=](Request request) { + if (request.transactionId < lowestSocketId) { + responseUnavailable(request); + return true; + } else if (request.transactionId > socketId) { + return responseAvailable(request); + } else { + return false; + } + }); +} + +bool operator==(const Request &a, const Request &b) { + return a.requestId == b.requestId; +} diff --git a/src/core/identserver.h b/src/core/identserver.h index 5aa82737..4d4aecc5 100644 --- a/src/core/identserver.h +++ b/src/core/identserver.h @@ -25,27 +25,52 @@ #include "coreidentity.h" +struct Request { + QTcpSocket *socket; + uint16_t localPort; + QString query; + qint64 transactionId; + qint64 requestId; + + friend bool operator==(const Request &a, const Request &b); +}; + class IdentServer : public QObject { Q_OBJECT public: IdentServer(bool strict, QObject *parent); - ~IdentServer(); + ~IdentServer() override; bool startListening(); void stopListening(const QString &msg); + qint64 addWaitingSocket(); public slots: - bool addSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, const QHostAddress &peerAddress, quint16 peerPort); - bool removeSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, const QHostAddress &peerAddress, quint16 peerPort); + bool addSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId); + bool removeSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId); private slots: void incomingConnection(); - void respond(); private: + bool responseAvailable(Request request); + void responseUnavailable(Request request); + + QString sysIdentForIdentity(const CoreIdentity *identity) const; + + bool hasSocketsBelowId(qint64 socketId); + + void processWaiting(qint64 socketId); + + void removeWaitingSocket(qint64 socketId); + QTcpServer _server, _v6server; bool _strict; QHash _connections; + std::list _requestQueue; + std::list _waiting; + qint64 _socketId; + qint64 _requestId; }; -- 2.20.1