QSslSocket Server Side SNI Support
-
I'm trying to write an SSL server program that can support SNI. I first set startTransaction(), and then read the incoming client handshake request, and pull the SNI. Then set rollbackTransaction(), and set setPrivateKey, setLocalCertificate, and startServerEncryption. However, the connection never gets encryption enabled. When I check bytesAvailable() after rollbackTransaction() for my QSslSocket, it comes back with the right amount of bytes. However, when I check in void QSslSocket::startServerEncryption() for d->plainSocket->bytesAvailable(), it says zero bytes available. Also, checking in void QSslSocketBackendPrivate::startServerEncryption() for plainSocket->bytesAvailable(), gives zero bytes available as well. Why the difference in bytesAvailable()?
-
Hi and welcome to devnet,
You should add which version of Qt your are using as well as platform and OpenSSL version.
Can you also show the code that you are currently using ?
-
Sorry about that.
Qt: 5.8.0
Platform: Arch Linux (latest updates)
OpenSSL: 1.0.2.kCode:
main.cpp#include <QCoreApplication> #include "sniserver.h" int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); app.setObjectName("CoreApp"); SniServer *sniServer(new SniServer); sniServer->setObjectName("sniserverd"); sniServer->moveToThread(sniServer); sniServer->start(); return app.exec(); }
sslserver.h
#ifndef SSLSERVER_H #define SSLSERVER_H #include <QTcpServer> class SslServer : public QTcpServer { Q_OBJECT public: explicit SslServer(QObject *parent = 0); virtual ~SslServer(); protected: virtual void incomingConnection(qintptr socketDescriptor); private slots: void sslSocketReady(); }; #endif // SSLSERVER_H
sslserver.cpp
#include <QSslSocket> #include "sslserver.h" SslServer::SslServer(QObject *parent) : QTcpServer(parent) { } SslServer::~SslServer() { } void SslServer::incomingConnection(qintptr socketDescriptor) { QSslSocket *sslSocket = new QSslSocket; sslSocket->setSocketDescriptor(socketDescriptor); sslSocket->setProtocol(QSsl::TlsV1_2OrLater); QObject::connect(sslSocket, &QSslSocket::encrypted, this, &SslServer::sslSocketReady); addPendingConnection(sslSocket); } void SslServer::sslSocketReady() { qDebug("Ssl Socket is encrypted"); }
sniserver.h
#ifndef SNISERVER_H #define SNISERVER_H #include <QThread> class SslServer; class SniServer : public QThread { Q_OBJECT public: explicit SniServer(QObject *parent = 0); virtual ~SniServer(); protected: void run(); private: SniServer(const SniServer&); SniServer& operator=(const SniServer&); SslServer *m_sslServer; private slots: void disconnectedHttpsClient(); void newSslConnection(); void readClientHello(); void readInputHttps(); }; #endif // SNISERVER_H
sniserver.cpp
#include <QSslSocket> #include <QSslCertificate> #include "utils.h" #include "sslserver.h" #include "httpsclient.h" #include "sniserver.h" SniServer::SniServer(QObject *parent) : QThread(parent), m_sslServer(new SslServer(this)) { } SniServer::~SniServer() { m_sslServer->close(); } void SniServer::run() { m_sslServer->setObjectName("SNIServer::m_sslServer"); QObject::connect(m_sslServer, &SslServer::newConnection, this, &SniServer::newSslConnection); if(m_sslServer->listen(QHostAddress::Any, 443)){ qWarning(" Notice:;\tThe SNI Https server is running on port 443"); }else{ qCritical() << "Unable to start the Http server:" << m_sslServer->errorString(); } exec(); } void SniServer::newSslConnection() { HttpsClient *httpsClient = new HttpsClient(qobject_cast<QSslSocket *>(m_sslServer->nextPendingConnection()), this); httpsClient->setObjectName("SniServer::httpsClient"); QObject::connect(httpsClient, &HttpsClient::readyRead, this, &SniServer::readClientHello); QObject::connect(httpsClient, &HttpsClient::disconnected, this, &SniServer::disconnectedHttpsClient); } void SniServer::disconnectedHttpsClient() { HttpsClient *tmpClient(qobject_cast<HttpsClient *>(sender())); tmpClient->deleteLater(); tmpClient = 0; } void SniServer::readClientHello() { HttpsClient *tmpClient(qobject_cast<HttpsClient *>(sender())); tmpClient->resetInactivityTimer(); QObject::disconnect(tmpClient, &HttpsClient::readyRead, this, &SniServer::readClientHello); tmpClient->m_sslSocket->startTransaction(); QByteArray incomingRequest(tmpClient->m_sslSocket->readAll()); tmpClient->m_sslSocket->rollbackTransaction(); qDebug() << "SniServer::readInputHttps:" << incomingRequest.toHex(); tmpClient->m_sslSocket->setPrivateKey("/opt/server/server.domainname.org.key.pem"); tmpClient->m_sslSocket->setLocalCertificate(QSslCertificate(Utils().readFile("/opt/server/server.domainname.org.cert.pem"))); tmpClient->m_sslSocket->startServerEncryption(); QObject::connect(tmpClient, &HttpsClient::readyRead, this, &SniServer::readInputHttps); tmpClient = 0; } void SniServer::readInputHttps() { HttpsClient *tmpClient(qobject_cast<HttpsClient *>(sender())); tmpClient->resetInactivityTimer(); QByteArray incomingRequest(tmpClient->m_sslSocket->readAll()); qDebug() << "SniServer::readInputHttps:" << incomingRequest.toHex(); tmpClient = 0; }
As you can see, a pretty straight forward implementation.
-
I decided to add Server Side SNI support to QSslSocket.
-
Nice ! Thank you :)
-
@Mythiclese
Thank you for making Qt better! -
Well, it is self-serving after all, lol. I need this feature, so might as well share it.
-
@Mythiclese
Well that really is the good spirit :) -
That's why I love open source. You can always roll your own.
-
@Mythiclese
Indeed. The freedom is real. So its also great you give something back. Helping to keep this freedom. -
Aye, I try to give back when I can.