PDA

View Full Version : QSslSocket and decryption buffer



kalpa
24th April 2009, 20:03
Hello all.

Just now found strange situation in Qt4.5.
on server side

QSslSocketBackendPrivate::transmit: encrypted 15798 bytes
QSslSocketBackendPrivate::transmit: wrote 15845 encrypted bytes to the socket

on client

QSslSocketBackendPrivate::transmit: read 15845 encrypted bytes from the socket
QSslSocketBackendPrivate::transmit: decrypted 4096 bytes

debug information.

So bytes available returns wrong value and protocol become broken.

Does it normal?

wysota
24th April 2009, 22:36
You mean you can't decrypt the data properly?

kalpa
25th April 2009, 05:39
I mean that QSslSocket on one side read, encrypt and send all data, but on another side QSslSocket read all data from socket correct but decrypt and return only 4096 bytes (As you can see).
If I try to send <4096 of data it's ok.
Is this a feature of QSslSocket or just a trick and I must send data throw socket by small fragments?

wysota
25th April 2009, 06:58
Neither. I'd say you have to start reading the data at all. The difference probably comes from a fact that the rest apart the 4 kilobytes is a SSL handshake or you simply filled the input buffer on the receiving side. If you read the data, more should come.

kalpa
25th April 2009, 07:32
Client code is


QByteArray KSocket::receiveData(qint64 size)
{
__PRFUNC__<<"waiting for"<<size<<bytesAvailable();
qint64 offset=0;
QByteArray returnBytes;
QByteArray tempBytes;


if (bytesAvailable()>=size)
{
tempBytes=read(size);
offset+=tempBytes.size();
returnBytes+=tempBytes;
}
else
{
while (bytesAvailable() < size-offset)
{
__PRFUNC__<<"Bytes Available"<<bytesAvailable()<<"Need"<<size-offset;
__PRFUNC__<<"Has Encrypted bytes"<<encryptedBytesAvailable();
if (state()==QAbstractSocket::UnconnectedState)
return 0;
if(bytesAvailable())
{
tempBytes=read(size-offset);
offset+=tempBytes.size();
returnBytes+=tempBytes;
}
else
if (!waitForReadyRead(5000))
if (state()==QAbstractSocket::UnconnectedState)
return returnBytes;
}
}
return returnBytes;
}


Debug Trace is

QSslSocket::_q_readyReadSlot() - 15845 bytes available
QSslSocketBackendPrivate::transmit: read 15845 encrypted bytes from the socket
QSslSocketBackendPrivate::transmit: decrypted 4096 bytes
"Kalpa::CoreModule::readPacket"
"Kalpa::CoreModule::readPacket" 4096 QAbstractSocket::ConnectedState
"Kalpa::CoreModule::readPacket"
"Kalpa::KSocket::receiveData" waiting for 8 packet
"Kalpa::KSocket::receiveData" 4096
QSslSocket::readData( 0x66e3e8 , 16384 ) == 4096
"Kalpa::KSocket::receiveData" Normal return 8
"Kalpa::KSocket::receiveData" waiting for 15790 4088
"Kalpa::KSocket::receiveData" 4088 Need 15790
"Kalpa::KSocket::receiveData" Has Encrypted bytes 0
QSslSocket::readData( 0x7fffa5d9a658 , 8 ) == 0
"Kalpa::KSocket::receiveData" 0 Need 11702
"Kalpa::KSocket::receiveData" Has Encrypted bytes 0
QSslSocket::_q_errorSlot( QAbstractSocket::SocketTimeoutError )
state = QAbstractSocket::ConnectedState
errorString = "Unknown error"
"Kalpa::KSocket::receiveData" 0 Need 11702
"Kalpa::KSocket::receiveData" Has Encrypted bytes 0
QSslSocket::_q_errorSlot( QAbstractSocket::SocketTimeoutError )
state = QAbstractSocket::ConnectedState
errorString = "Network operation timed out"
"Kalpa::KSocket::receiveData" 0 Need 11702
"Kalpa::KSocket::receiveData" Has Encrypted bytes 0
QSslSocket::_q_errorSlot( QAbstractSocket::SocketTimeoutError )
state = QAbstractSocket::ConnectedState
errorString = "Network operation timed out"
"Kalpa::KSocket::receiveData" 0 Need 11702
"Kalpa::KSocket::receiveData" Has Encrypted bytes 0


I try to read more than 4K data, but QSslSocket decrypts only 4K

Trying to investigate situation in isolation form.

kalpa
27th April 2009, 16:46
The situation I described before is reproducible with QSslSocket and ReadyRead signal.

just pooling in loop working normal

for (;;)
{
mSocket.waitForReadyRead(3000);

qint64 size=0;
qint64 offset=0;
QByteArray data;
QByteArray tempData;

mSocket.read((char*)&size, sizeof(size));


if (mSocket.bytesAvailable()>=size)
data=mSocket.read(size);
else
{
while (offset<size)
{
QCoreApplication::instance()->processEvents();
mSocket.waitForReadyRead(300);
QCoreApplication::instance()->processEvents();
if (mSocket.state()==QAbstractSocket::UnconnectedStat e)
{
qDebug()<<"Connection close";
::exit(0);
}
tempData=mSocket.read(size-offset);
offset+=tempData.size();
data+=tempData;
qDebug()<<"Read"<<offset<<"of"<<size<<"Bytes available"<<mSocket.bytesAvailable();
}
}
qDebug()<<"Read"<<data.size()<<"bytes";
}

But if you'll attach this procedure as signal for ReadyRead slot

void KClientSession::readData()
{
qDebug()<<"void KClientSession::readData()";

/* for (;;)
{
mSocket.waitForReadyRead(3000);
*/
qint64 size=0;
qint64 offset=0;
QByteArray data;
QByteArray tempData;

mSocket.read((char*)&size, sizeof(size));


if (mSocket.bytesAvailable()>=size)
data=mSocket.read(size);
else
{
while (offset<size)
{
QCoreApplication::instance()->processEvents();
mSocket.waitForReadyRead(300);
QCoreApplication::instance()->processEvents();
if (mSocket.state()==QAbstractSocket::UnconnectedStat e)
{
qDebug()<<"Connection close";
::exit(0);
}
tempData=mSocket.read(size-offset);
offset+=tempData.size();
data+=tempData;
qDebug()<<"Read"<<offset<<"of"<<size<<"Bytes available"<<mSocket.bytesAvailable();
}
}
qDebug()<<"Read"<<data.size()<<"bytes";
// }
}

You have got a problem. QSslSocket now not works properly. As you can see QSslSocket don't decrypt all data but only 4096 bytes.


QSslSocket::_q_readyReadSlot() - 21759 bytes available
QSslSocketBackendPrivate::transmit: read 21759 encrypted bytes from the socket
QSslSocketBackendPrivate::transmit: decrypted 4096 bytes
void KClientSession::readData()
QSslSocket::readData( 0x685f58 , 16384 ) == 4096
QSslSocket::_q_errorSlot( QAbstractSocket::SocketTimeoutError )


I believe that's it's a trick I don't know about because it's so stupid to polling socket in loop via waitForReadyRead in Client-side GUI application.

The situation is reproducible on packets more than 4K.

Anybody can help me?

Thanks a lot.

wysota
27th April 2009, 19:04
What is the point of calling processEvents() before and after waitForReadyRead()?

Can you provide a minimal compilable example reproducing the problem?

kalpa
28th April 2009, 14:51
Of course. Full example in attachment.

Clilent code (with signal-slot processing in decryption problem)


void TestClient::connectTo(const QString &host, int port)
{
mSocket.connectToHostEncrypted(host, port);
mSocket.ignoreSslErrors();

if (!mSocket.waitForEncrypted(50000))
return ;

qDebug()<<"********** encrypted ************"<<mSocket.isEncrypted();

connect(&mSocket, SIGNAL(readyRead()), this, SLOT(readData()));
// readData();
}

void TestClient::readData()
{
qDebug()<<"*******************************************"<<mSocket.bytesAvailable();


while (mSocket.bytesAvailable()>0)
{
qint64 size=0;
qint64 offset=0;
QByteArray data;
QByteArray tempData;
int i;

mSocket.read((char*)&size, sizeof(size));
mSocket.read((char*)&i, sizeof(i));

if (mSocket.bytesAvailable()>=size)
{
qDebug()<<"*** Read all";
data=mSocket.read(size);
}
else
{
while (offset<size)
{
qDebug()<<"*** Read parts";
mSocket.waitForReadyRead(3000);
if (mSocket.state()==QAbstractSocket::UnconnectedStat e)
{
qDebug()<<"Connection close";
::exit(0);
}
tempData=mSocket.read(size-offset);
offset+=tempData.size();
data+=tempData;
}
}

qDebug()<<"packet"<<i<<"Read"<<offset<<"of"<<size<<"Bytes available"<<mSocket.bytesAvailable();
if (mSocket.bytesAvailable()>0)
{
qDebug()<<"We have some bytes"<<mSocket.bytesAvailable();
continue;
}

mSocket.waitForReadyRead(7000);

}

qDebug()<<"I hope no data. Exit from ReadData";
}


Server code

TestServer::TestServer()
: QObject()
{
}

void TestServer::init(int port)
{
mServerSocket=new TServer;
connect(mServerSocket, SIGNAL(newConnection(int)), this, SLOT(incomingConnection(int)));

mServerSocket->setMaxPendingConnections(10);
if (!mServerSocket->listen(QHostAddress::Any, port))
{
qDebug()<<tr("Port is busy");
::exit(0);
}
}

void TestServer::incomingConnection(int handle)
{
mSocket=new QSslSocket(this);
mSocket->setLocalCertificate("./server.crt");
mSocket->setPrivateKey("./server.pem");
mSocket->ignoreSslErrors();

connect(this, SIGNAL(connectionOK()), this, SLOT(mproc()), Qt::QueuedConnection);

if ( mSocket->setSocketDescriptor( handle ) )
{
mSocket->startServerEncryption();
if (!mSocket->waitForEncrypted())
{
qDebug()<<"Error ssl";
::exit(0);
}
}
else
::exit(0);


qDebug()<<"Is Server side encrypted"<<mSocket->isEncrypted();

emit connectionOK();
qDebug()<<"#################################";
}

void TestServer::mproc()
{
qDebug()<<"void TestServer::mproc()";


for (int i=0; i<5; i++)
{
// Simple test packet
QString TestData;
qint64 size=0;
qint64 wrote=0;

// packet preparing
for (int a=0; a<900000; a++)
TestData+=QUuid::createUuid().toString();

size=TestData.toAscii().size();

mSocket->write((const char*)&size, sizeof(size));
mSocket->write((const char*)&i, sizeof(i));
wrote=mSocket->write(TestData.toAscii());

qDebug()<<"packet"<<i<<"wrote"<<wrote<<"bytes";
mSocket->flush();


// sleep(2);
}
}



Where I am wrong? Please correct me. Where is the trick?
Now I have no ideas how to use QSslSocket in Signal-Slot Gui application.