void SslAsynchIO::sslDataIn()

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);
    }
}