PDA

View Full Version : QSslSocket server



tpf80
6th May 2009, 03:57
Hi, I have been experimenting with the Simple Chat example at Here (http://wiki.qtcentre.org/index.php?title=Simple_Chat) and have decided as an exercise to learn more about SSL sockets, to convert it into an encrypted simple chat. So far I am stuck at a compile error in the server part:


error: invalid conversion from ‘QTcpSocket*’ to ‘QSslSocket*’

This is caused by the following code:

QSslSocket* connection = nextPendingConnection();

So it seems as if nextPendingConnection() returns a QTcpSocket, and that I need to reimplement in order to make it work for the SSL connections, however I have no idea where to begin with this or even if I am going in the right direction. It looks like there isn't a "QTcpServer" equivalent for SSL.

mcosta
6th May 2009, 08:10
From QSslSocket documentation you read


An example of using the delayed SSL handshake to secure an existing connection is the case where an SSL server secures an incoming connection. Suppose you create an SSL server class as a subclass of QTcpServer. You would override QTcpServer::incomingConnection() with something like the example below, which first constructs an instance of QSslSocket and then calls setSocketDescriptor() to set the new socket's descriptor to the existing one passed in. It then initiates the SSL handshake by calling startServerEncryption().

void SslServer::incomingConnection(int socketDescriptor)
{
QSslSocket *serverSocket = new QSslSocket;
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
connect(serverSocket, SIGNAL(encrypted()), this, SLOT(ready()));
serverSocket->startServerEncryption();
} else {
delete serverSocket;
}
}

tpf80
6th May 2009, 21:57
Yeah.. I read this very piece of code from the manual before I posted.

I am just not quite sure how the functionality of incomingConnection() relates to the functionality of nextPendingConnection() such that I can rework that part of the program. I will take a look at the Qt source for these functions later on and see if I cant see what is different about the suggested incomingConnection() in the manual for SSL, and the original non-ssl are, which should give me a clue as to what to do about incommingConnection() hopefully.

tpf80
7th May 2009, 04:10
Ok I think I am close, but just need a nudge in the right direction. Heres the modified code for the client and server so far:

Client:


SimpleChatClient::SimpleChatClient(QWidget* parent, Qt::WFlags flags) : QWidget(parent, flags) {


QVBoxLayout* main = new QVBoxLayout(this);

QGridLayout* top = new QGridLayout;

QHBoxLayout* bottom = new QHBoxLayout;



QLabel* label = new QLabel("Server:", this);

server = new QLineEdit(this);

port = new QSpinBox(this);

conn = new QPushButton("Connect", this);

port->setRange(1, 32767);

port->setValue(DEFAULT_PORT);

server->setText("localhost");

top->addWidget(label, 0, 0);

top->addWidget(server, 0, 1);

top->addWidget(port, 0, 2);



label = new QLabel("Nick:", this);

nick = new QLineEdit(this);

nick->setText("Anonymous");

top->addWidget(label, 1, 0);

top->addWidget(nick, 1, 1);

top->addWidget(conn, 1, 2);



chat = new QTextEdit(this);

chat->setReadOnly(true);



label = new QLabel("Message:", this);

message = new QLineEdit(this);

send = new QPushButton("Send", this);

send->setDefault(true);

bottom->addWidget(label);

bottom->addWidget(message);

bottom->addWidget(send);



main->addLayout(top);

main->addWidget(chat);

main->addLayout(bottom);

setLayout(main);



buffer = new QBuffer(this);

socket = new QSslSocket(this);

buffer->open(QIODevice::ReadWrite);



connect(message, SIGNAL(returnPressed()), SLOT(sendMessage()));

connect(send, SIGNAL(clicked()), SLOT(sendMessage()));

connect(conn, SIGNAL(clicked()), SLOT(toggleConnection()));



connect(socket, SIGNAL(connected()), SLOT(setConnected()));

connect(socket, SIGNAL(disconnected()), SLOT(setDisconnected()));

connect(socket, SIGNAL(readyRead()), SLOT(receiveMessage()));

connect(socket, SIGNAL(encrypted()), this, SLOT(connectionReady()));


setDisconnected();

}

void SimpleChatClient::connectionReady() {
qDebug() << "Connection is encrypted! ";
}



SimpleChatClient::~SimpleChatClient() {

buffer->close();

}



void SimpleChatClient::setConnected() {
qDebug() << "Connected! ";

port->setEnabled(false);

server->setEnabled(false);

nick->setEnabled(true);

message->setEnabled(true);

chat->setEnabled(true);

chat->clear();

send->setEnabled(true);

conn->setText("Disconnect");

}



void SimpleChatClient::setDisconnected() {
qDebug() << "Disconnected! ";

port->setEnabled(true);

server->setEnabled(true);

nick->setEnabled(false);

message->setEnabled(false);

chat->setEnabled(false);

send->setEnabled(false);

conn->setText("Connect");

}



void SimpleChatClient::toggleConnection() {

if (socket->state() == QAbstractSocket::UnconnectedState) {
qDebug() << "Connecting! ";

socket->connectToHostEncrypted(server->text(), port->value());

} else {
qDebug() << "Disconnecting! ";

socket->disconnectFromHost();

}

}



void SimpleChatClient::sendMessage() {

// "<nick> message\n"

socket->write("<" + nick->text().toLatin1() + "> " + message->text().toLatin1() + "\n");

message->clear();

}



void SimpleChatClient::receiveMessage() {

// missing some checks for returns values for the sake of simplicity

qint64 bytes = buffer->write(socket->readAll());

// go back as many bytes as we just wrote so that it can be read

buffer->seek(buffer->pos() - bytes);

// read only full lines, line by line

while (buffer->canReadLine()) {

QString line = buffer->readLine();

chat->append(line.simplified());

}

}

Server:


SimpleChatServer::SimpleChatServer(QObject* parent) : QTcpServer(parent) {

connect(this, SIGNAL(newConnection()), this, SLOT(addConnection()));

}



SimpleChatServer::~SimpleChatServer() {


}

////////////////////////////////////////////////////////////////////////////////////////////////



QSslSocket *SimpleChatServer::nextPendingConnection() {

if (pendingConnections.isEmpty())

return 0;



return pendingConnections.takeFirst();

}

void SimpleChatServer::incomingConnection(int socketDescriptor) {
qDebug() << "New Connection! ";
QSslSocket *serverSocket = new QSslSocket;
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
qDebug() << "Socket Descriptor Set! ";
connect(serverSocket, SIGNAL(encrypted()), this, SLOT(connectionReady()));
serverSocket->startServerEncryption();
pendingConnections.append(serverSocket);
} else {
qDebug() << "Socket Descriptor Not Set! ";
delete serverSocket;
}
}

void SimpleChatServer::connectionReady() {
qDebug() << "Connection is encrypted! ";
}

////////////////////////////////////////////////////////////////////////////////////////////////



void SimpleChatServer::addConnection() {
qDebug() << "Connection added to list! ";

QSslSocket* connection = nextPendingConnection();

connections.append(connection);

QBuffer* buffer = new QBuffer(this);

buffer->open(QIODevice::ReadWrite);

buffers.insert(connection, buffer);

connect(connection, SIGNAL(disconnected()), SLOT(removeConnection()));

connect(connection, SIGNAL(readyRead()), SLOT(receiveMessage()));

}



void SimpleChatServer::removeConnection() {
qDebug() << "Disconnected! ";

QSslSocket* socket = static_cast<QSslSocket*>(sender());

QBuffer* buffer = buffers.take(socket);

buffer->close();

buffer->deleteLater();

connections.removeAll(socket);

socket->deleteLater();

}



void SimpleChatServer::receiveMessage() {

QSslSocket* socket = static_cast<QSslSocket*>(sender());

QBuffer* buffer = buffers.value(socket);



// missing some checks for returns values for the sake of simplicity
qDebug() << "Reading From: " << socket;

qint64 bytes = buffer->write(socket->readAll());

//now we have a buffer containing the data incomming from the socket

// go back as many bytes as we just wrote so that it can be read

buffer->seek(buffer->pos() - bytes);




// read only full lines, line by line

while (buffer->canReadLine()) {


QByteArray line = buffer->readLine();

//this list holds the sockets currently connected:

foreach (QSslSocket* connection, connections) {
qDebug() << "Writing to: " << connection;

connection->write(line);


}

}

}


On the console what shows for the client when I try to connect is:



Connecting!
Connected!
Disconnected!


On the server at the same time it shows:



New Connection!
Socket Descriptor Set!
Connection added to list!
Disconnected!


So it seems that both the client and server are communicating, but I notice that neither of them are firing the encrypted() signal, and they immediately disconnect. What could I be missing?