in src/qpid/sys/windows/SslAsynchIO.cpp [301:478]
void SslAsynchIO::sslDataIn(qpid::sys::AsynchIO& a, BufferBase *buff) {
// See SslAsynchIO::unread() for the partner mechanism to maintain
// separation between unencrypted data and leftoverPlaintext.
if (state == ShuttingDown) {
return;
}
if (state != Running) {
negotiateStep(buff);
return;
}
char *extraBytes;
int32_t extraLength;
BufferBase *extraBuff;
// Decrypt one block; if there's legit data, pass it on through.
// However, it's also possible that the peer hasn't supplied enough
// data yet, or the session needs to be renegotiated, or the session
// is ending. Discard empty SSL frames as we encounter them.
while (true) {
SecBuffer recvBuffs[4];
recvBuffs[0].cbBuffer = buff->dataCount;
recvBuffs[0].BufferType = SECBUFFER_DATA;
recvBuffs[0].pvBuffer = &buff->bytes[buff->dataStart];
recvBuffs[1].BufferType = SECBUFFER_EMPTY;
recvBuffs[2].BufferType = SECBUFFER_EMPTY;
recvBuffs[3].BufferType = SECBUFFER_EMPTY;
SecBufferDesc buffDesc;
buffDesc.ulVersion = SECBUFFER_VERSION;
buffDesc.cBuffers = 4;
buffDesc.pBuffers = recvBuffs;
SECURITY_STATUS status = ::DecryptMessage(&ctxtHandle, &buffDesc, 0, NULL);
if (status != SEC_E_OK) {
if (status == SEC_E_INCOMPLETE_MESSAGE) {
// Give the partially filled buffer back and get more data
a.unread(buff);
}
else {
// Don't need this any more...
a.queueReadBuffer(buff);
if (status == SEC_I_RENEGOTIATE) {
state = Redo;
negotiateStep(0);
}
else if (status == SEC_I_CONTEXT_EXPIRED) {
queueWriteClose();
}
else {
throw QPID_WINDOWS_ERROR(status);
}
}
return;
}
// All decrypted and verified... continue with AMQP. The recvBuffs have
// been set up by DecryptMessage to demarcate the SSL header, data, and
// trailer, as well as any extra data left over. Walk through and find
// that info, adjusting the buff data accordingly to reflect only the
// actual decrypted data.
// If there's extra data, copy that out to a new buffer and run through
// this method again.
extraBytes = 0;
extraLength = 0;
for (int i = 0; i < 4; i++) {
switch (recvBuffs[i].BufferType) {
case SECBUFFER_STREAM_HEADER:
buff->dataStart += recvBuffs[i].cbBuffer;
// Fall through - also don't count these bytes as data
case SECBUFFER_STREAM_TRAILER:
buff->dataCount -= recvBuffs[i].cbBuffer;
break;
case SECBUFFER_EXTRA:
extraBytes = (char *) recvBuffs[i].pvBuffer;
extraLength = recvBuffs[i].cbBuffer;
break;
default:
break;
}
}
if (buff->dataCount)
break; // Process decrypted data
else {
if (extraLength) {
// Point past the empty frame and decrypt the next one.
buff->dataStart = extraBytes - buff->bytes;
buff->dataCount = extraLength;
}
else {
// No encrypted data remaining.
a.queueReadBuffer(buff);
return;
}
}
}
// Since we've already taken (possibly) all the available bytes from the
// aio layer, need to be sure that everything that's processable is
// processed before returning back to aio. It could be that any
// leftoverPlaintext data plus new buff data won't fit in one buffer, so
// need to keep going around the input processing loop until either
// all the bytes are gone, or there's less than a full frame remaining
// (so we can count on more bytes being on the way via aio).
do {
BufferBase *temp = 0;
extraBuff = 0;
// See if there was partial data left over from last time. If so, append this new
// data to that and release the current buff back to aio. Assume that
// leftoverPlaintext was squished so the data starts at 0.
if (leftoverPlaintext != 0) {
// There is leftover data; append all the new data that will fit.
int32_t count = buff->dataCount;
if (count) {
if (leftoverPlaintext->dataCount + count > leftoverPlaintext->byteCount)
count = (leftoverPlaintext->byteCount - leftoverPlaintext->dataCount);
::memmove(&leftoverPlaintext->bytes[leftoverPlaintext->dataCount],
&buff->bytes[buff->dataStart], count);
leftoverPlaintext->dataCount += count;
buff->dataCount -= count;
buff->dataStart += count;
// Prepare to pass the buffer up. Beware that the read callback
// may do an unread(), so move the leftoverPlaintext pointer
// out of the way. It also may release the buffer back to aio,
// so in either event, the pointer passed to the callback is not
// valid on return.
temp = leftoverPlaintext;
leftoverPlaintext = 0;
}
else {
// All decrypted data used up, decrypt some more or get more from the aio
if (extraLength) {
buff->dataStart = extraBytes - buff->bytes;
buff->dataCount = extraLength;
sslDataIn(a, buff);
return;
}
else {
a.queueReadBuffer(buff);
return;
}
}
}
else {
// Use buff, but first offload data not yet encrypted
if (extraLength) {
// Very important to get this buffer from the downstream aio.
// The ones constructed from the local getQueuedBuffer() are
// restricted size for encrypting. However, data coming up from
// TCP may have a bunch of SSL segments coalesced and be much
// larger than the maximum single SSL segment.
extraBuff = a.getQueuedBuffer();
if (0 == extraBuff) {
// No leftoverPlaintext, so a spare buffer should be available
throw QPID_WINDOWS_ERROR(WSAENOBUFS);
}
memmove(extraBuff->bytes, extraBytes, extraLength);
extraBuff->dataCount = extraLength;
extraLength = 0;
}
temp = buff;
buff = 0;
}
if (readCallback && temp->dataCount) {
// The callback guard here is to prevent an upcall from deleting
// this out from under us via queueForDeletion().
readCallback(*this, temp);
}
else
a.queueReadBuffer(temp); // What else can we do with this???
} while (buff != 0);
// Ok, the current decrypted data is done. If there was any extra data,
// go back and handle that.
if (extraBuff != 0) {
sslDataIn(a, extraBuff);
}
}