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()?


  • Lifetime Qt Champion

    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.k

    Code:
    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.


  • Lifetime Qt Champion

    Nice ! Thank you :)


  • Qt Champions 2016

    @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.


  • Qt Champions 2016

    @Mythiclese
    Well that really is the good spirit :)



  • That's why I love open source. You can always roll your own.


  • Qt Champions 2016

    @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.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.