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 ***************************************************************************/
23 #include "corenetwork.h"
24 #include "identserver.h"
25 #include "logmessage.h"
27 IdentServer::IdentServer(QObject *parent)
30 connect(&_server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
31 connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
35 bool IdentServer::startListening()
37 uint16_t port = Quassel::optionValue("ident-port").toUShort();
40 if (_v6server.listen(QHostAddress("::1"), port)) {
41 quInfo() << qPrintable(
42 tr("Listening for identd clients on IPv6 %1 port %2")
44 .arg(_v6server.serverPort())
50 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())
61 quError() << qPrintable(tr("Identd could not open any network interfaces to listen on! No identd functionality will be available"));
68 void IdentServer::stopListening(const QString &msg)
70 bool wasListening = false;
72 if (_server.isListening()) {
76 if (_v6server.isListening()) {
83 quInfo() << "No longer listening for identd clients.";
85 quInfo() << qPrintable(msg);
90 void IdentServer::incomingConnection()
92 auto server = qobject_cast<QTcpServer *>(sender());
94 while (server->hasPendingConnections()) {
95 QTcpSocket *socket = server->nextPendingConnection();
96 connect(socket, SIGNAL(readyRead()), this, SLOT(respond()));
97 connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
102 void IdentServer::respond()
104 QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
107 qint64 transactionId = _socketId;
109 if (!socket->canReadLine()) {
113 QByteArray query = socket->readLine();
114 if (query.endsWith("\r\n"))
116 else if (query.endsWith("\n"))
119 QList<QByteArray> split = query.split(',');
121 bool success = false;
123 quint16 localPort = 0;
124 if (!split.empty()) {
125 localPort = split[0].trimmed().toUShort(&success, 10);
128 Request request{socket, localPort, query, transactionId, _requestId++};
130 request.respondError("INVALID-PORT");
132 else if (responseAvailable(request)) {
135 else if (lowestSocketId() < transactionId) {
136 _requestQueue.emplace_back(request);
139 request.respondError("NO-USER");
144 void Request::respondSuccess(const QString &user)
147 QString data = query + " : USERID : Quassel : " + user + "\r\n";
148 socket->write(data.toUtf8());
155 void Request::respondError(const QString &error)
158 QString data = query + " : ERROR : " + error + "\r\n";
159 socket->write(data.toUtf8());
166 bool IdentServer::responseAvailable(Request request) const
168 if (!_connections.contains(request.localPort)) {
172 request.respondSuccess(_connections[request.localPort]);
177 void IdentServer::addSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
178 const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId)
180 Q_UNUSED(localAddress)
181 Q_UNUSED(peerAddress)
184 const CoreNetwork *network = qobject_cast<CoreNetwork *>(sender());
185 _connections[localPort] = network->coreSession()->strictCompliantIdent(identity);;
186 processWaiting(socketId);
190 void IdentServer::removeSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
191 const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId)
194 Q_UNUSED(localAddress)
195 Q_UNUSED(peerAddress)
198 _connections.remove(localPort);
199 processWaiting(socketId);
203 qint64 IdentServer::addWaitingSocket()
205 qint64 newSocketId = _socketId++;
206 _waiting.push_back(newSocketId);
211 qint64 IdentServer::lowestSocketId() const
213 if (_waiting.empty()) {
214 return std::numeric_limits<qint64>::max();
217 return _waiting.front();
221 void IdentServer::removeWaitingSocket(qint64 socketId)
223 _waiting.remove(socketId);
227 void IdentServer::processWaiting(qint64 socketId)
229 removeWaitingSocket(socketId);
231 _requestQueue.remove_if([this, socketId](Request request) {
232 if (socketId < request.transactionId && responseAvailable(request)) {
235 else if (lowestSocketId() < request.transactionId) {
239 request.respondError("NO-USER");
246 bool operator==(const Request &a, const Request &b)
248 return a.requestId == b.requestId;