X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fidentserver.cpp;h=1335a6c0147a2e73238e01bf9a2925acd1966c6f;hp=6d23a6cacb2472d87fb60ff150cf913c61791ace;hb=8bdfd0d16857c508f543298ad98f6262127f2886;hpb=a5ee609b02cb8d7ebb43b0034a6d50838c426f0b diff --git a/src/core/identserver.cpp b/src/core/identserver.cpp index 6d23a6ca..1335a6c0 100644 --- a/src/core/identserver.cpp +++ b/src/core/identserver.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2018 by the Quassel Project * + * Copyright (C) 2005-2019 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -18,53 +18,93 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include -#include - -#include "corenetwork.h" #include "identserver.h" -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())); -} +#include -IdentServer::~IdentServer() = default; +#include "corenetwork.h" -bool IdentServer::startListening() { - uint16_t port = Quassel::optionValue("ident-port").toUShort(); +IdentServer::IdentServer(QObject* parent) + : QObject(parent) +{ + connect(&_server, &QTcpServer::newConnection, this, &IdentServer::incomingConnection); + connect(&_v6server, &QTcpServer::newConnection, this, &IdentServer::incomingConnection); +} +bool IdentServer::startListening() +{ 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; + uint16_t port = Quassel::optionValue("ident-port").toUShort(); - quInfo() << qPrintable( - tr("Listening for identd clients on IPv4 %1 port %2") - .arg("127.0.0.1") - .arg(_server.serverPort()) - ); + const QString listen = Quassel::optionValue("ident-listen"); + const QStringList listen_list = listen.split(",", QString::SkipEmptyParts); + for (const QString &listen_term : listen_list) { // TODO: handle multiple interfaces for same TCP version gracefully + QHostAddress addr; + if (!addr.setAddress(listen_term)) { + qCritical() << qPrintable( + tr("Invalid listen address %1") + .arg(listen_term) + ); + } + else { + switch (addr.protocol()) { + case QAbstractSocket::IPv6Protocol: + if (_v6server.listen(addr, port)) { + qInfo() << qPrintable( + tr("Listening for identd requests on IPv6 %1 port %2") + .arg(addr.toString()) + .arg(_v6server.serverPort()) + ); + success = true; + } + else + qWarning() << qPrintable( + tr("Could not open IPv6 interface %1:%2: %3") + .arg(addr.toString()) + .arg(port) + .arg(_v6server.errorString())); + break; + case QAbstractSocket::IPv4Protocol: + if (_server.listen(addr, port)) { + qInfo() << qPrintable( + tr("Listening for identd requests on IPv4 %1 port %2") + .arg(addr.toString()) + .arg(_server.serverPort()) + ); + success = true; + } + else { + // if v6 succeeded on Any, the port will be already in use - don't display the error then + if (!success || _server.serverError() != QAbstractSocket::AddressInUseError) + qWarning() << qPrintable( + tr("Could not open IPv4 interface %1:%2: %3") + .arg(addr.toString()) + .arg(port) + .arg(_server.errorString())); + } + break; + default: + qCritical() << qPrintable( + tr("Invalid listen address %1, unknown network protocol") + .arg(listen_term) + ); + break; + } + } } if (!success) { - quError() << qPrintable( - tr("Identd could not open any network interfaces to listen on! No identd functionality will be available")); + qWarning() << 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) { +void IdentServer::stopListening(const QString& msg) +{ bool wasListening = false; + if (_server.isListening()) { wasListening = true; _server.close(); @@ -73,25 +113,29 @@ void IdentServer::stopListening(const QString &msg) { wasListening = true; _v6server.close(); } + if (wasListening) { if (msg.isEmpty()) - quInfo() << "No longer listening for identd clients."; + qInfo() << "No longer listening for identd clients."; else - quInfo() << qPrintable(msg); + qInfo() << qPrintable(msg); } } -void IdentServer::incomingConnection() { - auto *server = qobject_cast(sender()); +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())); + QTcpSocket* socket = server->nextPendingConnection(); + connect(socket, &QIODevice::readyRead, this, &IdentServer::respond); + connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater); } } -void IdentServer::respond() { - QTcpSocket *socket = qobject_cast(sender()); +void IdentServer::respond() +{ + auto* socket = qobject_cast(sender()); Q_ASSERT(socket); qint64 transactionId = _socketId; @@ -106,66 +150,97 @@ void IdentServer::respond() { else if (query.endsWith("\n")) query.chop(1); + qDebug() << "Received identd query" << query << "from" << socket->peerAddress(); + QList split = query.split(','); - bool success = false; + bool successLocalPort = false; + bool successRemotePort = false; quint16 localPort = 0; - if (!split.empty()) { - localPort = split[0].trimmed().toUShort(&success, 10); + quint16 remotePort = 0; + if (split.length() == 2) { + localPort = split[0].trimmed().toUShort(&successLocalPort, 10); + remotePort = split[1].trimmed().toUShort(&successRemotePort, 10); } - Request request{socket, localPort, query, transactionId, _requestId++}; - if (!success) { - responseUnavailable(request); - } else if (responseAvailable(request)) { + Request request{socket, localPort, remotePort, query, transactionId, _requestId++}; + if (!successLocalPort || !successRemotePort) { + request.respondError("INVALID-PORT"); + } + else if (responseAvailable(request)) { // success - } else if (hasSocketsBelowId(transactionId)) { + } + else if (lowestSocketId() < transactionId) { _requestQueue.emplace_back(request); - } else { - responseUnavailable(request); + } + else { + request.respondError("NO-USER"); } } -bool IdentServer::responseAvailable(Request request) { - if (!_connections.contains(request.localPort)) { - return false; +void Request::respondSuccess(const QString& user) +{ + if (socket) { + QString data = QString("%1, %2 : USERID : Quassel : %3\r\n") + .arg(QString::number(localPort)) + .arg(QString::number(remotePort)) + .arg(user); + qDebug() << "answering identd request from" << socket->peerAddress() << "with" << data; + socket->write(data.toUtf8()); + socket->flush(); + QTimer::singleShot(DISCONNECTION_TIMEOUT, socket, &QTcpSocket::close); } +} - QString user = _connections[request.localPort]; - QString data = request.query + " : USERID : Quassel : " + user + "\r\n"; - - request.socket->write(data.toUtf8()); - request.socket->flush(); - request.socket->close(); - - return true; +void Request::respondError(const QString& error) +{ + if (socket) { + QString data = QString("%1, %2 : ERROR : %3\r\n") + .arg(QString::number(localPort)) + .arg(QString::number(remotePort)) + .arg(error); + qDebug() << "answering identd request from" << socket->peerAddress() << "with" << data; + socket->write(data.toUtf8()); + socket->flush(); + QTimer::singleShot(DISCONNECTION_TIMEOUT, socket, &QTcpSocket::close); + } } -void IdentServer::responseUnavailable(Request request) { - QString data = request.query + " : ERROR : NO-USER\r\n"; +bool IdentServer::responseAvailable(Request request) const +{ + if (!_connections.contains(request.localPort)) { + return false; + } - request.socket->write(data.toUtf8()); - request.socket->flush(); - request.socket->close(); + request.respondSuccess(_connections[request.localPort]); + return true; } - -bool IdentServer::addSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, - const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId) { +void IdentServer::addSocket(const CoreIdentity* identity, + const QHostAddress& localAddress, + quint16 localPort, + 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);; + const CoreNetwork* network = qobject_cast(sender()); + _connections[localPort] = network->coreSession()->strictCompliantIdent(identity); + processWaiting(socketId); - return true; } - -bool IdentServer::removeSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort, - const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId) { +void IdentServer::removeSocket(const CoreIdentity* identity, + const QHostAddress& localAddress, + quint16 localPort, + const QHostAddress& peerAddress, + quint16 peerPort, + qint64 socketId) +{ Q_UNUSED(identity) Q_UNUSED(localAddress) Q_UNUSED(peerAddress) @@ -173,45 +248,48 @@ bool IdentServer::removeSocket(const CoreIdentity *identity, const QHostAddress _connections.remove(localPort); processWaiting(socketId); - return true; } -qint64 IdentServer::addWaitingSocket() { +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; - }); +qint64 IdentServer::lowestSocketId() const +{ + if (_waiting.empty()) { + return std::numeric_limits::max(); + } + + return _waiting.front(); } -void IdentServer::removeWaitingSocket(qint64 socketId) { +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; - } - } +void IdentServer::processWaiting(qint64 socketId) +{ removeWaitingSocket(socketId); - _requestQueue.remove_if([=](Request request) { - if (request.transactionId < lowestSocketId) { - responseUnavailable(request); + + _requestQueue.remove_if([this, socketId](Request request) { + if (socketId < request.transactionId && responseAvailable(request)) { return true; - } else if (request.transactionId > socketId) { - return responseAvailable(request); - } else { + } + else if (lowestSocketId() < request.transactionId) { return false; } + else { + request.respondError("NO-USER"); + return true; + } }); } -bool operator==(const Request &a, const Request &b) { +bool operator==(const Request& a, const Request& b) +{ return a.requestId == b.requestId; }