Home

An SSL server example

This example demonstrates how to use QtSSLSocket to create a simple secure SSL server.


sslserver.h:

#ifndef SSLSERVER_H
#define SSLSERVER_H
#include <qserversocket.h>
class QtSSLSocket;

class SSLServerConnection : public QObject
{
    Q_OBJECT

public:
    SSLServerConnection(unsigned short int socket, QObject *parent = 0, const char *name = 0);
    ~SSLServerConnection();

public slots:
    void acceptedClient();
    void readData();
    void connectionClosed();
    void error(int);

private:
    unsigned int readBytes;
    unsigned int writtenBytes;

    QtSSLSocket *sslsocket;
};

class SSLServer : public QServerSocket
{
    Q_OBJECT

public:
    SSLServer(short unsigned int port, QObject *parent = 0, const char *name = 0);

    void newConnection(int socket);
};

#endif


sslserver.cpp:

#include "sslserver.h"
#include <qdir.h>
#include <qfileinfo.h>
#include <qptrlist.h>
#include <qdatetime.h>

#include <qtsslsocket.h>
#include <qserversocket.h>

SSLServer::SSLServer(short unsigned int port, QObject *parent, const char *name)
    : QServerSocket(port, /* backlog = */ 1, parent, name)
{
}

void SSLServer::newConnection(int socket)
{
    // As soon as a client connects, pass its incoming socket id to a
    // SSLServerConnection child. This child is deleted after the
    // connection is closed (see the connectionClosed() slot).
    new SSLServerConnection(socket, this);
}

SSLServerConnection::SSLServerConnection(unsigned short int socket,
                                         QObject *parent, const char *name)
    : QObject(parent, name)
{
    // Create an SSL socket and make its QSocket use our accepted
    // socket, then give it the path to our certificate & private key
    // file. For notes on this file, please check the provided
    // "server.txt".
    sslsocket = new QtSSLSocket(QtSSLSocket::Server, 0, this);
    sslsocket->socket()->setSocket(socket);
    sslsocket->setPathToCertificate("sslserver.pem");
    sslsocket->setPathToPrivateKey("sslserver.pem");

    // Notice the platform dependency here; the location of the CA
    // certificate bundle is specific to the OS.
    sslsocket->setPathToCACertDir("/etc/ssl/certs");

    // Connect the SSL socket's signals to our slots.
    connect(sslsocket, SIGNAL(accepted()), SLOT(acceptedClient()));
    connect(sslsocket, SIGNAL(connectionClosed()), SLOT(connectionClosed()));
    connect(sslsocket, SIGNAL(delayedCloseFinished()), SLOT(connectionClosed()));
    connect(sslsocket, SIGNAL(readyRead()), SLOT(readData()));
    connect(sslsocket, SIGNAL(error(int)), SLOT(error(int)));

    // Call sslAccepted(). After this, when the SSL socket emits
    // accepted(), we are ready to go. We ignore the return value of
    // this function, because it will always fail the first time we
    // call it.
    sslsocket->sslAccept();
}

SSLServerConnection::~SSLServerConnection()
{
    // Report that the connection has closed.
    qDebug("Connection closed.");
}

void SSLServerConnection::acceptedClient()
{
    // Provide feedback to the user about incoming connections. This
    // slot is only called if the connection was established, so all
    // communication is now encrypted.
    qDebug("Accepted new client from %s",
           sslsocket->socket()->peerAddress().toString().latin1());

    // Print a simple DOS-like prompt. Write this to the SSL socket.
    // The SSL socket encrypts the data, and sends it to the client.
    QCString s = "Welcome to Fake-DOS 2.11\r\nC:\\>";
    sslsocket->writeBlock(s, s.length());
}

void SSLServerConnection::readData()
{
    // First, read all incoming data from the client. The SSL socket
    // has already decrypted it for us. We assume that the client uses
    // a plain text protocol, so we convert the data to a QString.
    QString incoming(sslsocket->readAll());

    // This server accepts only the commands "EXIT" and "DIR",
    // although case insensitive. All other commands are rejected with
    // "bad command or file name". Write response back to the client
    // through the SSL socket.
    QString command = incoming.upper().stripWhiteSpace();
    if (command == "EXIT") {
        QCString s = "system halted\r\n";
        sslsocket->writeBlock(s, s.length());
        sslsocket->close();
    } else if (command == "DIR") {
        QDir cwd(".");
        const QFileInfoList *cwdlist = cwd.entryInfoList();
        if (!cwdlist) {
            QCString s = "unable to list directory contents\r\nC:\\>";
            sslsocket->writeBlock(s, s.length());
        } else {
            QCString s = " Volume in drive C has no label.\r\n";
            s += " Volume Serial Number is C564-1226\r\n\r\n";
            s += " Directory of C:\\\r\n\r\n";
            QPtrListIterator<QFileInfo> it(*cwdlist);

            int nfiles = 0;
            int ndirs = 0;
            int tildes = 0;
            while (it.current()) {
                QFileInfo *f = *it;
                QDate d = f->created().date();
                QTime t = f->created().time();
                QString line;

                bool dots = f->fileName() == "." || f->fileName() == "..";
                QString fname = dots ? QString("") : f->baseName().upper();
                QString lname = dots ? f->fileName() : f->extension().upper().left(3);

                if (fname.length() > 8) {
                    QString tmp;
                    tmp.sprintf("~%i", ++tildes);
                    fname = fname.left(8 - tmp.length()) + tmp;
                }

                if (f->isDir()) {
                    line.sprintf("%8s %3s <DIR>         %02i-%02i-%02i  %2i:%02i%c\r\n",
                                 fname.latin1(), lname.latin1(), d.day(), d.month(),
                                 (d.year() - 1900) % 100,
                                 t.hour() % 12, t.minute(), t.hour() > 12 ? 'p' : 'a');
                } else {
                    line.sprintf("%8s %3s       %7u %02i-%02i-%02i  %2i:%02i%c\r\n",
                                 fname.latin1(), lname.latin1(), f->size(), d.day(), d.month(),
                                 (d.year() - 1900) % 100,  t.hour() % 12, t.minute(),
                                 t.hour() > 12 ? 'p' : 'a');
                }

                s += line.local8Bit();
                if ((*it)->isDir())
                    ++ndirs;
                else
                    ++nfiles;
                ++it;
            }

            QString line;
            line.sprintf("%16i File(s)\r\n", nfiles);
            s += line.local8Bit();
            line.sprintf("%16i Dir(s)\r\n", ndirs);
            s += line.local8Bit();
            s += "C:\\>";
            sslsocket->writeBlock(s, s.length());
        }

    } else {
        QCString s = "bad command or file name\r\nC:\\>";
        sslsocket->writeBlock(s, s.length());
    }
}

void SSLServerConnection::connectionClosed()
{
    // Although the socket may be closing, we must not delete it until
    // the delayed close is done.
    if (sslsocket->socket()->state() == QSocket::Closing) {
        connect(sslsocket->socket(), SIGNAL(delayedCloseFinished()), SLOT(deleteLater()));
    } else {
        deleteLater();
        return;
    }

    qDebug("Connection closed.");
}

void SSLServerConnection::error(int)
{
    // The SSL socket conveniently provides human readable error
    // messages through the errorString() call. Note that sometimes
    // the errors come directly from the underlying SSL library, and
    // the quality of the text may vary.
    qDebug("Error: %s", (const char *)sslsocket->errorString().local8Bit());
}


main.cpp:

#include <qapplication.h>
#include <stdlib.h>
#include <qfileinfo.h>

#include "sslserver.h"

/*
    An SSL server that is started from the console.

    This example demonstrates the use of the QtSSLSocket class in a
    server application.
 */
int main(int argc, char *argv[])
{
    QFileInfo cert("sslserver.pem");
    if (!cert.exists()) {
        qDebug("Note: This server requires the file sslserver.pem to exist, "
               "and to contain the SSL private key and certificate for "
               "this server, encoded in PEM format. Please read "
               "server.txt for more information.");
        return 1;
    }

    if (argc < 2) {
        qDebug("usage: %s <port>", argv[0]);
        qDebug("A simple SSL server.");
        return 1;
    }

    QApplication app(argc, argv, false);

    int port = atoi(argv[1]);

    SSLServer sserver(port);

    qDebug("Listening on port %i. Please press Ctrl-C to exit.", port);

    return app.exec();
}


server.pro:

TEMPLATE = app
INCLUDEPATH += .
CONFIG += console

include(../../src/qtsslsocket.pri)

HEADERS += sslserver.h
SOURCES += main.cpp sslserver.cpp


Copyright © 2003-2006 TrolltechTrademarks
Qt Solutions