Improve identd
[quassel.git] / src / core / identserver.cpp
index 5a48b5b..1335a6c 100644 (file)
@@ -33,19 +33,65 @@ IdentServer::IdentServer(QObject* parent)
 
 bool IdentServer::startListening()
 {
-    uint16_t port = Quassel::optionValue("ident-port").toUShort();
-
     bool success = false;
-    if (_v6server.listen(QHostAddress("::1"), port)) {
-        qInfo() << 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)) {
-        qInfo() << qPrintable(tr("Listening for identd clients on IPv4 %1 port %2").arg("127.0.0.1").arg(_server.serverPort()));
+    uint16_t port = Quassel::optionValue("ident-port").toUShort();
 
-        success = true;
+    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) {
@@ -104,17 +150,22 @@ void IdentServer::respond()
     else if (query.endsWith("\n"))
         query.chop(1);
 
+    qDebug() << "Received identd query" << query << "from" << socket->peerAddress();
+
     QList<QByteArray> 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) {
+    Request request{socket, localPort, remotePort, query, transactionId, _requestId++};
+    if (!successLocalPort || !successRemotePort) {
         request.respondError("INVALID-PORT");
     }
     else if (responseAvailable(request)) {
@@ -131,20 +182,28 @@ void IdentServer::respond()
 void Request::respondSuccess(const QString& user)
 {
     if (socket) {
-        QString data = query + " : USERID : Quassel : " + user + "\r\n";
+        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();
-        socket->close();
+        QTimer::singleShot(DISCONNECTION_TIMEOUT, socket, &QTcpSocket::close);
     }
 }
 
 void Request::respondError(const QString& error)
 {
     if (socket) {
-        QString data = query + " : ERROR : " + error + "\r\n";
+        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();
-        socket->close();
+        QTimer::singleShot(DISCONNECTION_TIMEOUT, socket, &QTcpSocket::close);
     }
 }
 
@@ -171,7 +230,7 @@ void IdentServer::addSocket(const CoreIdentity* identity,
 
     const CoreNetwork* network = qobject_cast<CoreNetwork*>(sender());
     _connections[localPort] = network->coreSession()->strictCompliantIdent(identity);
-    ;
+
     processWaiting(socketId);
 }