identd: Implement blocking request queueing
authorJanne Koschinski <janne@kuschku.de>
Mon, 7 May 2018 23:49:13 +0000 (01:49 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Mon, 18 Jun 2018 19:25:50 +0000 (21:25 +0200)
src/core/corenetwork.cpp
src/core/corenetwork.h
src/core/identserver.cpp
src/core/identserver.h

index 02823a0..575d20c 100644 (file)
@@ -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) {
index d269358..668f314 100644 (file)
@@ -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;
 
index 21ea5ff..08b1b67 100644 (file)
  ***************************************************************************/
 
 #include <logger.h>
+#include <set>
 
 #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<QTcpSocket *>(sender());
+    QTcpSocket *socket = qobject_cast<QTcpSocket *>(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<QByteArray> split = s.split(',');
+        QList<QByteArray> 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<CoreNetwork *>(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<qint64 >::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;
+}
index 5aa8273..4d4aecc 100644 (file)
 
 #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<uint16_t, QString> _connections;
+    std::list<Request> _requestQueue;
+    std::list<qint64> _waiting;
+    qint64 _socketId;
+    qint64 _requestId;
 };