identd: Initial draft of integrated ident daemon
[quassel.git] / src / core / identserver.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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.                                           *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #include <logger.h>
22
23 #include "corenetwork.h"
24 #include "identserver.h"
25
26 IdentServer::IdentServer(bool strict, QObject *parent) : QObject(parent), _strict(strict) {
27     connect(&_server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
28     connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
29 }
30
31 IdentServer::~IdentServer() = default;
32
33 bool IdentServer::startListening() {
34     uint16_t port = 10113;
35
36     bool success = false;
37     if (_v6server.listen(QHostAddress("::1"), port)) {
38         quInfo() << qPrintable(
39                 tr("Listening for identd clients on IPv6 %1 port %2")
40                         .arg("::1")
41                         .arg(_v6server.serverPort())
42         );
43
44         success = true;
45     }
46
47     if (_server.listen(QHostAddress("127.0.0.1"), port)) {
48         success = true;
49
50         quInfo() << qPrintable(
51                 tr("Listening for identd clients on IPv4 %1 port %2")
52                         .arg("127.0.0.1")
53                         .arg(_server.serverPort())
54         );
55     }
56
57     if (!success) {
58         quError() << qPrintable(
59                 tr("Identd could not open any network interfaces to listen on! No identd functionality will be available"));
60     }
61
62     return success;
63 }
64
65 void IdentServer::stopListening(const QString &msg) {
66     bool wasListening = false;
67     if (_server.isListening()) {
68         wasListening = true;
69         _server.close();
70     }
71     if (_v6server.isListening()) {
72         wasListening = true;
73         _v6server.close();
74     }
75     if (wasListening) {
76         if (msg.isEmpty())
77             quInfo() << "No longer listening for identd clients.";
78         else
79             quInfo() << qPrintable(msg);
80     }
81 }
82
83 void IdentServer::incomingConnection() {
84     auto *server = qobject_cast<QTcpServer *>(sender());
85     Q_ASSERT(server);
86     while (server->hasPendingConnections()) {
87         QTcpSocket *socket = server->nextPendingConnection();
88         connect(socket, SIGNAL(readyRead()), this, SLOT(respond()));
89     }
90 }
91
92 void IdentServer::respond() {
93     auto *socket = qobject_cast<QTcpSocket *>(sender());
94     Q_ASSERT(socket);
95
96     if (socket->canReadLine()) {
97         QByteArray s = socket->readLine();
98         if (s.endsWith("\r\n"))
99             s.chop(2);
100         else if (s.endsWith("\n"))
101             s.chop(1);
102
103         QList<QByteArray> split = s.split(',');
104
105         bool success = false;
106
107         uint16_t localPort;
108         if (!split.empty()) {
109             localPort = split[0].toUShort(&success, 10);
110         }
111
112         QString user;
113         if (success) {
114             if (_connections.contains(localPort)) {
115                 user = _connections[localPort];
116             } else {
117                 success = false;
118             }
119         }
120
121         QString data;
122         if (success) {
123             data += s + " : USERID : Quassel : " + user + "\r\n";
124         } else {
125             data += s + " : ERROR : NO-USER\r\n";
126         }
127
128         socket->write(data.toUtf8());
129         socket->flush();
130         socket->close();
131         socket->deleteLater();
132     }
133 }
134
135
136 bool IdentServer::addSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
137                             const QHostAddress &peerAddress, quint16 peerPort) {
138     Q_UNUSED(localAddress)
139     Q_UNUSED(peerAddress)
140     Q_UNUSED(peerPort)
141
142     const CoreNetwork *network = qobject_cast<CoreNetwork *>(sender());
143     _connections[localPort] = network->coreSession()->strictCompliantIdent(identity);;
144     return true;
145 }
146
147
148 //! not yet implemented
149 bool IdentServer::removeSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
150                                const QHostAddress &peerAddress, quint16 peerPort) {
151     Q_UNUSED(identity)
152     Q_UNUSED(localAddress)
153     Q_UNUSED(peerAddress)
154     Q_UNUSED(peerPort)
155
156     _connections.remove(localPort);
157     return true;
158 }