From 9451580d19875b23ec52af64585496efb7268e0f Mon Sep 17 00:00:00 2001 From: Janne Koschinski Date: Tue, 8 May 2018 00:12:32 +0200 Subject: [PATCH] identd: Initial draft of integrated ident daemon --- src/common/main.cpp | 2 + src/core/CMakeLists.txt | 4 +- src/core/core.cpp | 9 +++ src/core/core.h | 4 + src/core/corenetwork.cpp | 5 ++ src/core/identserver.cpp | 158 +++++++++++++++++++++++++++++++++++++++ src/core/identserver.h | 51 +++++++++++++ 7 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 src/core/identserver.cpp create mode 100644 src/core/identserver.h diff --git a/src/common/main.cpp b/src/common/main.cpp index 513bb339..cedd3c35 100644 --- a/src/common/main.cpp +++ b/src/common/main.cpp @@ -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"); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index be5133b9..2097b6c1 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 ) diff --git a/src/core/core.cpp b/src/core/core.cpp index cf943b98..1ff4adab 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -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; diff --git a/src/core/core.h b/src/core/core.h index 01bf477f..7da4f227 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -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}; diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index 7d1dea91..02823a0c 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -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 index 00000000..21ea5ff6 --- /dev/null +++ b/src/core/identserver.cpp @@ -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 + +#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(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(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 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(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 index 00000000..5aa82737 --- /dev/null +++ b/src/core/identserver.h @@ -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 +#include + +#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 _connections; +}; -- 2.20.1