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 ***************************************************************************/
21 #include "identserver.h"
25 #include "corenetwork.h"
27 IdentServer::IdentServer(QObject* parent)
30 connect(&_server, &QTcpServer::newConnection, this, &IdentServer::incomingConnection);
31 connect(&_v6server, &QTcpServer::newConnection, this, &IdentServer::incomingConnection);
34 bool IdentServer::startListening()
36 uint16_t port = Quassel::optionValue("ident-port").toUShort();
39 if (_v6server.listen(QHostAddress("::1"), port)) {
40 qInfo() << qPrintable(tr("Listening for identd clients on IPv6 %1 port %2").arg("::1").arg(_v6server.serverPort()));
45 if (_server.listen(QHostAddress("127.0.0.1"), port)) {
46 qInfo() << qPrintable(tr("Listening for identd clients on IPv4 %1 port %2").arg("127.0.0.1").arg(_server.serverPort()));
52 qWarning() << qPrintable(tr("Identd could not open any network interfaces to listen on! No identd functionality will be available"));
58 void IdentServer::stopListening(const QString& msg)
60 bool wasListening = false;
62 if (_server.isListening()) {
66 if (_v6server.isListening()) {
73 qInfo() << "No longer listening for identd clients.";
75 qInfo() << qPrintable(msg);
79 void IdentServer::incomingConnection()
81 auto server = qobject_cast<QTcpServer*>(sender());
83 while (server->hasPendingConnections()) {
84 QTcpSocket* socket = server->nextPendingConnection();
85 connect(socket, &QIODevice::readyRead, this, &IdentServer::respond);
86 connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
90 void IdentServer::respond()
92 auto* socket = qobject_cast<QTcpSocket*>(sender());
95 qint64 transactionId = _socketId;
97 if (!socket->canReadLine()) {
101 QByteArray query = socket->readLine();
102 if (query.endsWith("\r\n"))
104 else if (query.endsWith("\n"))
107 QList<QByteArray> split = query.split(',');
109 bool success = false;
111 quint16 localPort = 0;
112 if (!split.empty()) {
113 localPort = split[0].trimmed().toUShort(&success, 10);
116 Request request{socket, localPort, query, transactionId, _requestId++};
118 request.respondError("INVALID-PORT");
120 else if (responseAvailable(request)) {
123 else if (lowestSocketId() < transactionId) {
124 _requestQueue.emplace_back(request);
127 request.respondError("NO-USER");
131 void Request::respondSuccess(const QString& user)
134 QString data = query + " : USERID : Quassel : " + user + "\r\n";
135 socket->write(data.toUtf8());
141 void Request::respondError(const QString& error)
144 QString data = query + " : ERROR : " + error + "\r\n";
145 socket->write(data.toUtf8());
151 bool IdentServer::responseAvailable(Request request) const
153 if (!_connections.contains(request.localPort)) {
157 request.respondSuccess(_connections[request.localPort]);
161 void IdentServer::addSocket(const CoreIdentity* identity,
162 const QHostAddress& localAddress,
164 const QHostAddress& peerAddress,
168 Q_UNUSED(localAddress)
169 Q_UNUSED(peerAddress)
172 const CoreNetwork* network = qobject_cast<CoreNetwork*>(sender());
173 _connections[localPort] = network->coreSession()->strictCompliantIdent(identity);
175 processWaiting(socketId);
178 void IdentServer::removeSocket(const CoreIdentity* identity,
179 const QHostAddress& localAddress,
181 const QHostAddress& peerAddress,
186 Q_UNUSED(localAddress)
187 Q_UNUSED(peerAddress)
190 _connections.remove(localPort);
191 processWaiting(socketId);
194 qint64 IdentServer::addWaitingSocket()
196 qint64 newSocketId = _socketId++;
197 _waiting.push_back(newSocketId);
201 qint64 IdentServer::lowestSocketId() const
203 if (_waiting.empty()) {
204 return std::numeric_limits<qint64>::max();
207 return _waiting.front();
210 void IdentServer::removeWaitingSocket(qint64 socketId)
212 _waiting.remove(socketId);
215 void IdentServer::processWaiting(qint64 socketId)
217 removeWaitingSocket(socketId);
219 _requestQueue.remove_if([this, socketId](Request request) {
220 if (socketId < request.transactionId && responseAvailable(request)) {
223 else if (lowestSocketId() < request.transactionId) {
227 request.respondError("NO-USER");
233 bool operator==(const Request& a, const Request& b)
235 return a.requestId == b.requestId;