/* # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg # # This program is free software distributed under the GPL version 2. # See http://openslx.org/COPYING # # If you have any feedback please consult http://openslx.org/feedback and # send your suggestions, praise, or complaints to feedback@openslx.org # # General information about OpenSLX can be found at http://openslx.org/ # ----------------------------------------------------------------------------- # src/net/SslServer.cpp # - provide QTcpServer-like behaviour for SSL # ----------------------------------------------------------------------------- */ #include "sslserver.h" #include #include #include "certmanager.h" #include SslServer::SslServer() : QTcpServer(nullptr) { _tmr = startTimer(5123); } SslServer::~SslServer() { killTimer(_tmr); } /** * Handle incomming connection. * @param socketDescriptor */ void SslServer::incomingConnection(qintptr socketDescriptor) { static int certFails = 0; QSslKey key; QSslCertificate cert; if (!CertManager::getPrivateKeyAndCert("manager", key, cert)) { if (++certFails > 5) { CertManager::fatal(); } ::close((int)socketDescriptor); return; } QSslSocket *serverSocket = new QSslSocket(nullptr); connect(serverSocket, SIGNAL(sslErrors(const QList &)), this, SLOT(sslErrors(const QList &))); connect(serverSocket, SIGNAL(disconnected()), this, SLOT(sock_closed())); connect(serverSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sock_error(QAbstractSocket::SocketError))); serverSocket->setPrivateKey(key); serverSocket->setLocalCertificate(cert); serverSocket->setPeerVerifyMode(QSslSocket::VerifyNone); serverSocket->setProtocol(QSsl::SecureProtocols); if (serverSocket->setSocketDescriptor(socketDescriptor)) { // Once the connection is successfully encrypted, raise our newConnection event connect(serverSocket, &QSslSocket::encrypted, [=]() { disconnect(serverSocket, SIGNAL(sslErrors(const QList &)), this, SLOT(sslErrors(const QList &))); disconnect(serverSocket, SIGNAL(disconnected()), this, SLOT(sock_closed())); disconnect(serverSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sock_error(QAbstractSocket::SocketError))); }); connect(serverSocket, SIGNAL(encrypted()), this, SIGNAL(newConnection())); serverSocket->startServerEncryption(); _pending.push_back(serverSocket); } else { qDebug() << "Failed to setSocketDescriptor on new SSL Socket"; serverSocket->deleteLater(); } } void SslServer::sslErrors(const QList& /* errors */) { /* qDebug() << "Client caused sslErrors before connection:"; for (QList::const_iterator it = errors.begin(); it != errors.end(); it++) { qDebug() << it->errorString(); } */ } void SslServer::sock_closed() { qDebug() << "Client closed connection before SSL handshake completed."; sender()->deleteLater(); } void SslServer::sock_error(QAbstractSocket::SocketError err) { qDebug() << "Client error before SSL handshake completed: " << err; sender()->deleteLater(); } void SslServer::timerEvent(QTimerEvent* /* event */ ) { // Remove all sockets marked for deletion while (!_delete.isEmpty()) { QSslSocket *sock = _delete.takeFirst(); sock->blockSignals(true); sock->deleteLater(); } _delete = _pending; _pending.clear(); } bool SslServer::hasPendingConnections() const { for (QList::const_iterator it(_pending.begin()); it != _pending.end(); it++) { qDebug("State: %d - Encrypted: %d", (int)(*it)->state(), (*it)->isEncrypted()); if ((*it)->state() == QAbstractSocket::ConnectedState && (*it)->isEncrypted()) return true; } return false; } QTcpSocket* SslServer::nextPendingConnection() { for (QList::iterator it(_pending.begin()); it != _pending.end(); it++) { if ((*it)->state() == QAbstractSocket::ConnectedState && (*it)->isEncrypted()) { QSslSocket *sock = *it; QObject::disconnect(sock, SIGNAL(encrypted()), this, SIGNAL(newConnection())); _pending.removeAll(sock); _delete.removeAll(sock); return sock; } } for (QList::iterator it(_delete.begin()); it != _delete.end(); it++) { if ((*it)->state() == QAbstractSocket::ConnectedState && (*it)->isEncrypted()) { QSslSocket *sock = *it; QObject::disconnect(sock, SIGNAL(encrypted()), this, SIGNAL(newConnection())); _pending.removeAll(sock); _delete.removeAll(sock); return sock; } } return nullptr; }