void ZStreamR_Boundary::Imp_Read(void* oDest, size_t iCount, size_t* oCountRead) { if (not fBuffer) { fStreamSource.Read(oDest, iCount, oCountRead); } else { const size_t boundarySize = fBoundary.size(); uint8* localDest = reinterpret_cast<uint8*>(oDest); while (iCount) { if (fDataEnd > fDataStart) { size_t countToMove = min(fDataEnd - fDataStart, iCount); sMemCopy(localDest, fBuffer + fDataStart, countToMove); fDataStart += countToMove; localDest += countToMove; iCount -= countToMove; } else { if (fHitBoundary) break; // Shuffle existing stuff to beginning of buffer. sMemMove(fBuffer, fBuffer + fDataEnd, boundarySize - fDataEnd); // Top up the tail. size_t countRead; fStreamSource.Read(fBuffer + boundarySize - fDataEnd, fDataEnd, &countRead); if (countRead < fDataEnd) { // The source stream has gone empty without our having already seen the // boundary. Shuffle the data we do have so the last byte aligns with // the end of the buffer. sMemMove(fBuffer + fDataEnd - countRead, fBuffer, boundarySize - (fDataEnd - countRead)); // The first returnable byte is at fDataStart, and the last is at // the end of the buffer. fDataStart = fDataEnd - countRead; fDataEnd = boundarySize; if (countRead == 0) { // We read nothing at all, so fDataStart will equal fDataEnd, // which will be the end of the buffer. And we should bail. break; } } else { // Do Boyer-Moore search on the full buffer. int xx; for (xx = boundarySize - 1; xx >= 0 && fBuffer[xx] == fBoundary[xx]; --xx) {} if (xx < 0) { // We found the boundary. fHitBoundary = true; fDataStart = boundarySize; fDataEnd = boundarySize; } else { // We didn't find the boundary. The shift distance is precisely the number // of bytes at the start of the buffer that *cannot* match the boundary, // which are bytes that can be returned as our output. fDataStart = 0; fDataEnd = fDistance[fBuffer[boundarySize - 1]]; } } } } if (oCountRead) *oCountRead = localDest - reinterpret_cast<uint8*>(oDest); } }
void ZStreamRWCon_SSL_Win::Imp_Read(void* oDest, size_t iCount, size_t* oCountRead) { ZAcqMtx acq(fMtx_R); char* localDest = static_cast<char*>(oDest); while (iCount) { if (fBufferPlain.size()) { // We've got some data to return. size_t countToRead = min(iCount, fBufferPlain.size()); deque<char>::iterator begin = fBufferPlain.begin(); deque<char>::iterator end = begin + countToRead; std::copy(begin, end, static_cast<char*>(localDest)); fBufferPlain.erase(begin, end); localDest += countToRead; iCount -= countToRead; } if (localDest != oDest) { // We've somehow managed to read some data, just above or down // below, and so can bail from the loop. break; } if (not fReceiveOpen) break; // We pass four buffers. inSB[0] references the available encrypted // data. inSB[1] through inSB[3] are marked as being empty, and may // be modified by DecryptMessage. SecBuffer inSB[4]; inSB[0].cbBuffer = fBufferEnc.size(); inSB[0].pvBuffer = ZUtil_STL::sFirstOrNil(fBufferEnc); inSB[0].BufferType = SECBUFFER_DATA; inSB[1].BufferType = SECBUFFER_EMPTY; inSB[2].BufferType = SECBUFFER_EMPTY; inSB[3].BufferType = SECBUFFER_EMPTY; SecBufferDesc inSBD; inSBD.cBuffers = 4; inSBD.pBuffers = inSB; inSBD.ulVersion = SECBUFFER_VERSION; SECURITY_STATUS scRet = spPSFT->DecryptMessage(&fCtxtHandle, &inSBD, 0, nullptr); if (scRet == SEC_E_INCOMPLETE_MESSAGE || (FAILED(scRet) && fBufferEnc.empty())) { // fBufferEnc holds an incomplete chunk, DecryptMessage needs // more data before it can do the decrypt. if (not spReadMore(fBufferEnc, fStreamR)) { // We couldn't read any more encrypted data. Mark // our receive as being closed. fReceiveOpen = false; } continue; } if (scRet == SEC_I_CONTEXT_EXPIRED) { // SSL-level disconnect has been sent by the other side. fReceiveOpen = false; continue; } if (scRet == SEC_I_RENEGOTIATE) { // We need to re-handshake. if (not this->pHandshake()) { fReceiveOpen = false; fSendOpen = false; } continue; } if (FAILED(scRet)) { // We failed for some other reason. fReceiveOpen = false; continue; } // If DecryptMessage did any work, inSB[0] through inSB[2] will now reference // the header, decrypted data and trailer. inSB[3] will indicate how many // bytes at the end of fBufferEnc were unused by this decrypt, and so // must be preserved for subsequent use. // This assignment of information to buffers is only something I've determined // by inspection, so for safety we walk through them to find the two we care // about -- the decrypted data and any unused encrypted data. SecBuffer* decrypted = nullptr; SecBuffer* encrypted = nullptr; // Pickup any decrypted data for (size_t x = 0; x < 4; ++x) { if (inSB[x].BufferType == SECBUFFER_DATA && ! decrypted) decrypted = &inSB[x]; if (inSB[x].BufferType == SECBUFFER_EXTRA && ! encrypted) encrypted = &inSB[x]; } // The decryption happens in-place, ie in fBufferEnc. Therefore // we must copy out the decrypted data before munging fBufferEnc to // reference only the unused data. if (decrypted) { // Copy some decrypted data to our destination. const size_t countToCopy = std::min(iCount, size_t(decrypted->cbBuffer)); sMemCopy(localDest, decrypted->pvBuffer, countToCopy); localDest += countToCopy; iCount -= countToCopy; // Anything remaining we put in fBufferPlain, which must be // empty otherwise we wouldn't have got to this point. const char* data = static_cast<const char*>(decrypted->pvBuffer); fBufferPlain.insert(fBufferPlain.begin(), data + countToCopy, data + decrypted->cbBuffer); } if (encrypted) { // There is some unused data, move it to the front of fBufferEnc, // and resize fBufferEnc to reference only that data. sMemMove(&fBufferEnc[0], encrypted->pvBuffer, encrypted->cbBuffer); fBufferEnc.resize(encrypted->cbBuffer); } else { // There was no unused data. fBufferEnc.clear(); } } if (oCountRead) *oCountRead = localDest - static_cast<char*>(oDest); }