Use Schannel's incomplete data guesstimation feature

It tells us how many bytes we will need before the call succeeds. It's
not accurate but will reduce the amount of calls to their slow functions

Change-Id: I82393d5acd68b84c6e6f3377ba40bb1d5c51ca8a
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Mårten Nordheim 2019-09-07 02:22:21 +02:00
parent 69a43c6c3e
commit 559b563d71
2 changed files with 33 additions and 0 deletions

View File

@ -467,6 +467,17 @@ void retainExtraData(QByteArray &buffer, const SecBuffer &secBuffer)
buffer.resize(secBuffer.cbBuffer);
}
qint64 checkIncompleteData(const SecBuffer &secBuffer)
{
if (secBuffer.BufferType == SECBUFFER_MISSING) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl, "Need %lu more bytes.", secBuffer.cbBuffer);
#endif
return secBuffer.cbBuffer;
}
return 0;
}
} // anonymous namespace
bool QSslSocketPrivate::s_loadRootCertsOnDemand = true;
@ -810,6 +821,10 @@ bool QSslSocketBackendPrivate::acceptContext()
Q_ASSERT(mode == QSslSocket::SslServerMode);
ULONG contextReq = getContextRequirements();
if (missingData > plainSocket->bytesAvailable())
return true;
missingData = 0;
readToBuffer(intermediateBuffer, plainSocket);
if (intermediateBuffer.isEmpty())
return true; // definitely need more data..
@ -866,6 +881,7 @@ bool QSslSocketBackendPrivate::acceptContext()
if (status == SEC_E_INCOMPLETE_MESSAGE) {
// Need more data
missingData = checkIncompleteData(outBuffers[0]);
return true;
}
@ -905,6 +921,10 @@ bool QSslSocketBackendPrivate::performHandshake()
qCDebug(lcSsl, "intermediateBuffer size: %d", intermediateBuffer.size());
#endif
if (missingData > plainSocket->bytesAvailable())
return true;
missingData = 0;
readToBuffer(intermediateBuffer, plainSocket);
if (intermediateBuffer.isEmpty())
return true; // no data, will fail
@ -990,6 +1010,7 @@ bool QSslSocketBackendPrivate::performHandshake()
return true;
case SEC_E_INCOMPLETE_MESSAGE:
// Simply incomplete, wait for more data
missingData = checkIncompleteData(outBuffers[0]);
return true;
case SEC_E_ALGORITHM_MISMATCH:
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
@ -1192,6 +1213,8 @@ void QSslSocketBackendPrivate::reset()
connectionEncrypted = false;
shutdown = false;
renegotiating = false;
missingData = 0;
}
void QSslSocketBackendPrivate::startClientEncryption()
@ -1288,6 +1311,14 @@ void QSslSocketBackendPrivate::transmit()
int totalRead = 0;
bool hadIncompleteData = false;
while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) {
if (missingData > plainSocket->bytesAvailable()) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl, "We're still missing %lld bytes, will check later.", missingData);
#endif
break;
}
missingData = 0;
const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket);
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl, "Read %lld encrypted bytes from the socket", bytesRead);
@ -1341,6 +1372,7 @@ void QSslSocketBackendPrivate::transmit()
}
if (status == SEC_E_INCOMPLETE_MESSAGE) {
missingData = checkIncompleteData(dataBuffer[0]);
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl, "We didn't have enough data to decrypt anything, will try again!");
#endif

View File

@ -145,6 +145,7 @@ private:
const CERT_CONTEXT *localCertContext = nullptr;
ULONG contextAttributes = 0;
qint64 missingData = 0;
bool renegotiating = false;
};