identd: Initial draft of integrated ident daemon
authorJanne Koschinski <janne@kuschku.de>
Mon, 7 May 2018 22:12:32 +0000 (00:12 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Mon, 18 Jun 2018 19:25:50 +0000 (21:25 +0200)
src/common/main.cpp
src/core/CMakeLists.txt
src/core/core.cpp
src/core/core.h
src/core/corenetwork.cpp
src/core/identserver.cpp [new file with mode: 0644]
src/core/identserver.h [new file with mode: 0644]

index 513bb33..cedd3c3 100644 (file)
@@ -191,6 +191,8 @@ int main(int argc, char **argv)
     cliParser->addSwitch("oidentd", 0, "Enable oidentd integration.  In most cases you should also enable --strict-ident");
     cliParser->addOption("oidentd-conffile", 0, "Set path to oidentd configuration file", "file");
     cliParser->addSwitch("strict-ident", 0, "Use users' quasselcore username as ident reply. Ignores each user's configured ident setting.");
+    cliParser->addSwitch("ident-daemon", 0, "Enable internal ident daemon");
+    cliParser->addOption("ident-port", 'p', "The port quasselcore will listen at for ident requests. Only meaningful with --ident-daemon", "port", "10113");
 #ifdef HAVE_SSL
     cliParser->addSwitch("require-ssl", 0, "Require SSL for remote (non-loopback) client connections");
     cliParser->addOption("ssl-cert", 0, "Specify the path to the SSL Certificate", "path", "configdir/quasselCert.pem");
index be5133b..2097b6c 100644 (file)
@@ -30,6 +30,7 @@ set(SOURCES
     coreusersettings.cpp
     ctcpparser.cpp
     eventstringifier.cpp
+    identserver.cpp
     ircparser.cpp
     netsplit.cpp
     oidentdconfiggenerator.cpp
@@ -40,8 +41,7 @@ set(SOURCES
     storage.cpp
 
     # needed for automoc
-    coreeventmanager.h
-)
+    coreeventmanager.h)
 
 set(LIBS )
 
index cf943b9..1ff4ada 100644 (file)
@@ -221,6 +221,11 @@ bool Core::init()
         if (Quassel::isOptionSet("oidentd")) {
             _oidentdConfigGenerator = new OidentdConfigGenerator(this);
         }
+        
+
+        if (Quassel::isOptionSet("ident-daemon")) {
+            _identServer = new IdentServer(_strictIdentEnabled, this);
+        }
 
         Quassel::registerReloadHandler([]() {
             // Currently, only reloading SSL certificates and the sysident cache is supported
@@ -665,12 +670,16 @@ bool Core::startListening()
     if (!success)
         quError() << qPrintable(tr("Could not open any network interfaces to listen on!"));
 
+    if (_identServer != nullptr) _identServer->startListening();
+
     return success;
 }
 
 
 void Core::stopListening(const QString &reason)
 {
+    if (_identServer != nullptr) _identServer->stopListening(reason);
+
     bool wasListening = false;
     if (_server.isListening()) {
         wasListening = true;
index 01bf477..7da4f22 100644 (file)
@@ -45,6 +45,7 @@
 #include "sessionthread.h"
 #include "storage.h"
 #include "types.h"
+#include "identserver.h"
 
 class CoreAuthHandler;
 class CoreSession;
@@ -692,6 +693,7 @@ public:
     static inline QTimer *syncTimer() { return &instance()->_storageSyncTimer; }
 
     inline OidentdConfigGenerator *oidentdConfigGenerator() const { return _oidentdConfigGenerator; }
+    inline IdentServer *identServer() const { return _identServer; }
 
     static const int AddClientEventId;
 
@@ -804,6 +806,8 @@ private:
 
     QDateTime _startTime;
 
+    IdentServer *_identServer {nullptr};
+    
     bool _initialized{false};
     bool _configured{false};
 
index 7d1dea9..02823a0 100644 (file)
@@ -106,6 +106,11 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session)
         connect(this, SIGNAL(socketInitialized(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->oidentdConfigGenerator(), SLOT(addSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Qt::BlockingQueuedConnection);
         connect(this, SIGNAL(socketDisconnected(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->oidentdConfigGenerator(), SLOT(removeSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)));
     }
+
+    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)));
+    }
 }
 
 
diff --git a/src/core/identserver.cpp b/src/core/identserver.cpp
new file mode 100644 (file)
index 0000000..21ea5ff
--- /dev/null
@@ -0,0 +1,158 @@
+/***************************************************************************
+ *   Copyright (C) 2005-2018 by the Quassel Project                        *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) version 3.                                           *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
+ ***************************************************************************/
+
+#include <logger.h>
+
+#include "corenetwork.h"
+#include "identserver.h"
+
+IdentServer::IdentServer(bool strict, QObject *parent) : QObject(parent), _strict(strict) {
+    connect(&_server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
+    connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
+}
+
+IdentServer::~IdentServer() = default;
+
+bool IdentServer::startListening() {
+    uint16_t port = 10113;
+
+    bool success = false;
+    if (_v6server.listen(QHostAddress("::1"), port)) {
+        quInfo() << qPrintable(
+                tr("Listening for identd clients on IPv6 %1 port %2")
+                        .arg("::1")
+                        .arg(_v6server.serverPort())
+        );
+
+        success = true;
+    }
+
+    if (_server.listen(QHostAddress("127.0.0.1"), port)) {
+        success = true;
+
+        quInfo() << qPrintable(
+                tr("Listening for identd clients on IPv4 %1 port %2")
+                        .arg("127.0.0.1")
+                        .arg(_server.serverPort())
+        );
+    }
+
+    if (!success) {
+        quError() << qPrintable(
+                tr("Identd could not open any network interfaces to listen on! No identd functionality will be available"));
+    }
+
+    return success;
+}
+
+void IdentServer::stopListening(const QString &msg) {
+    bool wasListening = false;
+    if (_server.isListening()) {
+        wasListening = true;
+        _server.close();
+    }
+    if (_v6server.isListening()) {
+        wasListening = true;
+        _v6server.close();
+    }
+    if (wasListening) {
+        if (msg.isEmpty())
+            quInfo() << "No longer listening for identd clients.";
+        else
+            quInfo() << qPrintable(msg);
+    }
+}
+
+void IdentServer::incomingConnection() {
+    auto *server = qobject_cast<QTcpServer *>(sender());
+    Q_ASSERT(server);
+    while (server->hasPendingConnections()) {
+        QTcpSocket *socket = server->nextPendingConnection();
+        connect(socket, SIGNAL(readyRead()), this, SLOT(respond()));
+    }
+}
+
+void IdentServer::respond() {
+    auto *socket = qobject_cast<QTcpSocket *>(sender());
+    Q_ASSERT(socket);
+
+    if (socket->canReadLine()) {
+        QByteArray s = socket->readLine();
+        if (s.endsWith("\r\n"))
+            s.chop(2);
+        else if (s.endsWith("\n"))
+            s.chop(1);
+
+        QList<QByteArray> split = s.split(',');
+
+        bool success = false;
+
+        uint16_t localPort;
+        if (!split.empty()) {
+            localPort = split[0].toUShort(&success, 10);
+        }
+
+        QString user;
+        if (success) {
+            if (_connections.contains(localPort)) {
+                user = _connections[localPort];
+            } else {
+                success = false;
+            }
+        }
+
+        QString data;
+        if (success) {
+            data += s + " : USERID : Quassel : " + user + "\r\n";
+        } else {
+            data += s + " : ERROR : NO-USER\r\n";
+        }
+
+        socket->write(data.toUtf8());
+        socket->flush();
+        socket->close();
+        socket->deleteLater();
+    }
+}
+
+
+bool IdentServer::addSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
+                            const QHostAddress &peerAddress, quint16 peerPort) {
+    Q_UNUSED(localAddress)
+    Q_UNUSED(peerAddress)
+    Q_UNUSED(peerPort)
+
+    const CoreNetwork *network = qobject_cast<CoreNetwork *>(sender());
+    _connections[localPort] = network->coreSession()->strictCompliantIdent(identity);;
+    return true;
+}
+
+
+//! not yet implemented
+bool IdentServer::removeSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
+                               const QHostAddress &peerAddress, quint16 peerPort) {
+    Q_UNUSED(identity)
+    Q_UNUSED(localAddress)
+    Q_UNUSED(peerAddress)
+    Q_UNUSED(peerPort)
+
+    _connections.remove(localPort);
+    return true;
+}
diff --git a/src/core/identserver.h b/src/core/identserver.h
new file mode 100644 (file)
index 0000000..5aa8273
--- /dev/null
@@ -0,0 +1,51 @@
+/***************************************************************************
+ *   Copyright (C) 2005-2018 by the Quassel Project                        *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) version 3.                                           *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
+ ***************************************************************************/
+
+#pragma once
+
+#include <QTcpServer>
+#include <QTcpSocket>
+
+#include "coreidentity.h"
+
+class IdentServer : public QObject {
+Q_OBJECT
+public:
+    IdentServer(bool strict, QObject *parent);
+    ~IdentServer();
+
+    bool startListening();
+    void stopListening(const QString &msg);
+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);
+
+private slots:
+    void incomingConnection();
+
+    void respond();
+
+private:
+    QTcpServer _server, _v6server;
+
+    bool _strict;
+
+    QHash<uint16_t, QString> _connections;
+};