ssl: Use QSslSocket directly to avoid redundant qobject_casts
[quassel.git] / src / core / coreauthhandler.cpp
index d8f81ab..b289520 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2019 by the Quassel Project                        *
+ *   Copyright (C) 2005-2020 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
 
 #include "coreauthhandler.h"
 
-#ifdef HAVE_SSL
-#    include <QSslSocket>
-#endif
+#include <QtEndian>
+
+#include <QSslSocket>
 
 #include "core.h"
 
-CoreAuthHandler::CoreAuthHandler(QTcpSocket* socket, QObject* parent)
+CoreAuthHandler::CoreAuthHandler(QSslSocket* socket, QObject* parent)
     : AuthHandler(parent)
     , _peer(nullptr)
+    , _metricsServer(Core::instance()->metricsServer())
+    , _proxyReceived(false)
+    , _proxyLine({})
+    , _useProxyLine(false)
     , _magicReceived(false)
     , _legacy(false)
     , _clientRegistered(false)
@@ -42,13 +46,37 @@ CoreAuthHandler::CoreAuthHandler(QTcpSocket* socket, QObject* parent)
 
 void CoreAuthHandler::onReadyRead()
 {
-    if (socket()->bytesAvailable() < 4)
-        return;
-
     // once we have selected a peer, we certainly don't want to read more data!
     if (_peer)
         return;
 
+    if (!_proxyReceived) {
+        quint32 magic;
+        socket()->peek((char*) &magic, 4);
+        magic = qFromBigEndian<quint32>(magic);
+
+        if (magic == Protocol::proxyMagic) {
+            if (!socket()->canReadLine()) {
+                return;
+            }
+            QByteArray line = socket()->readLine(108);
+            _proxyLine = ProxyLine::parseProxyLine(line);
+            if (_proxyLine.protocol != QAbstractSocket::UnknownNetworkLayerProtocol) {
+                QList<QString> subnets = Quassel::optionValue("proxy-cidr").split(",");
+                for (const QString& subnet : subnets) {
+                    if (socket()->peerAddress().isInSubnet(QHostAddress::parseSubnet(subnet))) {
+                        _useProxyLine = true;
+                        break;
+                    }
+                }
+            }
+        }
+        _proxyReceived = true;
+    }
+
+    if (socket()->bytesAvailable() < 4)
+        return;
+
     if (!_magicReceived) {
         quint32 magic;
         socket()->peek((char*)&magic, 4);
@@ -98,7 +126,7 @@ void CoreAuthHandler::onReadyRead()
 
             RemotePeer* peer = PeerFactory::createPeer(_supportedProtos, this, socket(), level, this);
             if (!peer) {
-                qWarning() << "Received invalid handshake data from client" << socket()->peerAddress().toString();
+                qWarning() << "Received invalid handshake data from client" << hostAddress().toString();
                 close();
                 return;
             }
@@ -127,6 +155,9 @@ void CoreAuthHandler::setPeer(RemotePeer* peer)
     qDebug().nospace() << "Using " << qPrintable(peer->protocolName()) << "...";
 
     _peer = peer;
+    if (_proxyLine.protocol != QAbstractSocket::UnknownNetworkLayerProtocol) {
+        _peer->setProxyLine(_proxyLine);
+    }
     disconnect(socket(), &QIODevice::readyRead, this, &CoreAuthHandler::onReadyRead);
 }
 
@@ -145,7 +176,7 @@ void CoreAuthHandler::onProtocolVersionMismatch(int actual, int expected)
 bool CoreAuthHandler::checkClientRegistered()
 {
     if (!_clientRegistered) {
-        qWarning() << qPrintable(tr("Client")) << qPrintable(socket()->peerAddress().toString())
+        qWarning() << qPrintable(tr("Client")) << qPrintable(hostAddress().toString())
                    << qPrintable(tr("did not send a registration message before trying to login, rejecting."));
         _peer->dispatch(
             Protocol::ClientDenied(tr("<b>Client not initialized!</b><br>You need to send a registration message before trying to login.")));
@@ -164,7 +195,7 @@ void CoreAuthHandler::handle(const Protocol::RegisterClient& msg)
         useSsl = _connectionFeatures & Protocol::Encryption;
 
     if (Quassel::isOptionSet("require-ssl") && !useSsl && !_peer->isLocal()) {
-        qInfo() << qPrintable(tr("SSL required but non-SSL connection attempt from %1").arg(socket()->peerAddress().toString()));
+        qInfo() << qPrintable(tr("SSL required but non-SSL connection attempt from %1").arg(hostAddress().toString()));
         _peer->dispatch(Protocol::ClientDenied(tr("<b>SSL is required!</b><br>You need to use SSL in order to connect to this core.")));
         _peer->close();
         return;
@@ -219,7 +250,7 @@ void CoreAuthHandler::handle(const Protocol::Login& msg)
         return;
 
     if (!Core::isConfigured()) {
-        qWarning() << qPrintable(tr("Client")) << qPrintable(socket()->peerAddress().toString())
+        qWarning() << qPrintable(tr("Client")) << qPrintable(hostAddress().toString())
                    << qPrintable(tr("attempted to login before the core was configured, rejecting."));
         _peer->dispatch(Protocol::ClientDenied(
             tr("<b>Attempted to login before core was configured!</b><br>The core must be configured before attempting to login.")));
@@ -244,15 +275,21 @@ void CoreAuthHandler::handle(const Protocol::Login& msg)
     }
 
     if (uid == 0) {
-        qInfo() << qPrintable(tr("Invalid login attempt from %1 as \"%2\"").arg(socket()->peerAddress().toString(), msg.user));
+        qInfo() << qPrintable(tr("Invalid login attempt from %1 as \"%2\"").arg(hostAddress().toString(), msg.user));
         _peer->dispatch(Protocol::LoginFailed(tr(
             "<b>Invalid username or password!</b><br>The username/password combination you supplied could not be found in the database.")));
+        if (_metricsServer) {
+            _metricsServer->addLoginAttempt(msg.user, false);
+        }
         return;
     }
     _peer->dispatch(Protocol::LoginSuccess());
+    if (_metricsServer) {
+        _metricsServer->addLoginAttempt(uid, true);
+    }
 
     qInfo() << qPrintable(tr("Client %1 initialized and authenticated successfully as \"%2\" (UserId: %3).")
-                          .arg(socket()->peerAddress().toString(), msg.user, QString::number(uid.toInt())));
+                              .arg(_peer->address(), msg.user, QString::number(uid.toInt())));
 
     const auto& clientFeatures = _peer->features();
     auto unsupported = clientFeatures.toStringList(false);
@@ -275,26 +312,35 @@ void CoreAuthHandler::handle(const Protocol::Login& msg)
     emit handshakeComplete(_peer, uid);
 }
 
+QHostAddress CoreAuthHandler::hostAddress() const
+{
+    if (_useProxyLine) {
+        return _proxyLine.sourceHost;
+    }
+    else if (socket()) {
+        return socket()->peerAddress();
+    }
+
+    return {};
+}
+
+bool CoreAuthHandler::isLocal() const
+{
+    return hostAddress() == QHostAddress::LocalHost ||
+           hostAddress() == QHostAddress::LocalHostIPv6;
+}
+
 /*** SSL Stuff ***/
 
 void CoreAuthHandler::startSsl()
 {
-#ifdef HAVE_SSL
-    auto* sslSocket = qobject_cast<QSslSocket*>(socket());
-    Q_ASSERT(sslSocket);
-
     qDebug() << qPrintable(tr("Starting encryption for Client:")) << _peer->description();
-    connect(sslSocket, selectOverload<const QList<QSslError>&>(&QSslSocket::sslErrors), this, &CoreAuthHandler::onSslErrors);
-    sslSocket->flush();  // ensure that the write cache is flushed before we switch to ssl (bug 682)
-    sslSocket->startServerEncryption();
-#endif /* HAVE_SSL */
+    connect(socket(), selectOverload<const QList<QSslError>&>(&QSslSocket::sslErrors), this, &CoreAuthHandler::onSslErrors);
+    socket()->flush();  // ensure that the write cache is flushed before we switch to ssl (bug 682)
+    socket()->startServerEncryption();
 }
 
-#ifdef HAVE_SSL
 void CoreAuthHandler::onSslErrors()
 {
-    auto* sslSocket = qobject_cast<QSslSocket*>(socket());
-    Q_ASSERT(sslSocket);
-    sslSocket->ignoreSslErrors();
+    socket()->ignoreSslErrors();
 }
-#endif