1 /***************************************************************************
2 * Copyright (C) 2005-2018 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
24 #include "corenetwork.h"
25 #include "identserver.h"
27 IdentServer::IdentServer(bool strict, QObject *parent) : QObject(parent), _strict(strict), _socketId(0), _requestId(0) {
28 connect(&_server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
29 connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
32 IdentServer::~IdentServer() = default;
34 bool IdentServer::startListening() {
35 uint16_t port = Quassel::optionValue("ident-port").toUShort();
38 if (_v6server.listen(QHostAddress("::1"), port)) {
39 quInfo() << qPrintable(
40 tr("Listening for identd clients on IPv6 %1 port %2")
42 .arg(_v6server.serverPort())
48 if (_server.listen(QHostAddress("127.0.0.1"), port)) {
51 quInfo() << qPrintable(
52 tr("Listening for identd clients on IPv4 %1 port %2")
54 .arg(_server.serverPort())
59 quError() << qPrintable(
60 tr("Identd could not open any network interfaces to listen on! No identd functionality will be available"));
66 void IdentServer::stopListening(const QString &msg) {
67 bool wasListening = false;
68 if (_server.isListening()) {
72 if (_v6server.isListening()) {
78 quInfo() << "No longer listening for identd clients.";
80 quInfo() << qPrintable(msg);
84 void IdentServer::incomingConnection() {
85 auto *server = qobject_cast<QTcpServer *>(sender());
87 while (server->hasPendingConnections()) {
88 QTcpSocket *socket = server->nextPendingConnection();
89 connect(socket, SIGNAL(readyRead()), this, SLOT(respond()));
93 void IdentServer::respond() {
94 QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
97 qint64 transactionId = _socketId;
99 if (!socket->canReadLine()) {
103 QByteArray query = socket->readLine();
104 if (query.endsWith("\r\n"))
106 else if (query.endsWith("\n"))
109 QList<QByteArray> split = query.split(',');
111 bool success = false;
114 if (!split.empty()) {
115 localPort = split[0].trimmed().toUShort(&success, 10);
118 Request request{socket, localPort, query, transactionId, _requestId++};
120 responseUnavailable(request);
121 } else if (!responseAvailable(request)) {
122 if (hasSocketsBelowId(transactionId)) {
123 _requestQueue.emplace_back(request);
125 responseUnavailable(request);
130 bool IdentServer::responseAvailable(Request request) {
133 if (_connections.contains(request.localPort)) {
134 user = _connections[request.localPort];
141 data += request.query + " : USERID : Quassel : " + user + "\r\n";
143 request.socket->write(data.toUtf8());
144 request.socket->flush();
145 request.socket->close();
150 void IdentServer::responseUnavailable(Request request) {
151 QString data = request.query + " : ERROR : NO-USER\r\n";
153 request.socket->write(data.toUtf8());
154 request.socket->flush();
155 request.socket->close();
159 bool IdentServer::addSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
160 const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId) {
161 Q_UNUSED(localAddress)
162 Q_UNUSED(peerAddress)
165 const CoreNetwork *network = qobject_cast<CoreNetwork *>(sender());
166 _connections[localPort] = network->coreSession()->strictCompliantIdent(identity);;
167 processWaiting(socketId);
172 bool IdentServer::removeSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
173 const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId) {
175 Q_UNUSED(localAddress)
176 Q_UNUSED(peerAddress)
179 _connections.remove(localPort);
180 processWaiting(socketId);
184 qint64 IdentServer::addWaitingSocket() {
185 qint64 newSocketId = _socketId++;
186 _waiting.push_back(newSocketId);
190 bool IdentServer::hasSocketsBelowId(qint64 id) {
191 return std::any_of(_waiting.begin(), _waiting.end(), [=](qint64 socketId) {
192 return socketId <= id;
196 void IdentServer::removeWaitingSocket(qint64 socketId) {
197 _waiting.remove(socketId);
200 void IdentServer::processWaiting(qint64 socketId) {
201 qint64 lowestSocketId = std::numeric_limits<qint64 >::max();
202 for (qint64 id : _waiting) {
203 if (id < lowestSocketId) {
207 removeWaitingSocket(socketId);
208 _requestQueue.remove_if([=](Request request) {
209 if (request.transactionId < lowestSocketId) {
210 responseUnavailable(request);
212 } else if (request.transactionId > socketId) {
213 return responseAvailable(request);
220 bool operator==(const Request &a, const Request &b) {
221 return a.requestId == b.requestId;