Я искал StackOverflow, пытаясь найти подобную проблему, но не сталкивался с ней, поэтому я отправляю этот вопрос.
Я пытаюсь написать C ++ HTTPS-клиент с использованием библиотек Microsoft SChannel, и я получаю стохасти c ошибок с частичной передачей сообщений. Эта проблема возникает только при очень длинных загрузках - короткие обычно работают нормально. Большую часть времени код работает правильно - даже для длительных загрузок - но иногда команда recv () изящно истекает, отключая мой сеанс TLS, и в других случаях я получаю неполный последний пакет. Ошибки Stochasti c, по-видимому, являются результатом фрагментов различного размера и блоков шифрования, которые сервер использует для передачи данных. Я знаю, что мне нужно справиться с этим вариантом, но хотя это будет легко решить на незашифрованном HTTP-соединении, проблема с шифрованием вызывает у меня проблемы.
Во-первых, проблема тайм-аута, возникающая примерно в 5% случаев, когда я запрашиваю большие HTTP-запросы (около 10 МБ данных из одного HTTP-запроса GET).
Истекло время ожидания, потому что в последнем блоке я указал больший буфер приема, чем данные, оставшиеся в блокирующем сокете. Очевидное решение этой проблемы - запросить только то количество байтов, которое мне нужно для следующего фрагмента, и именно это я и сделал. Но по какой-то причине сумма, полученная по каждому запросу, меньше, чем та, которую я запрашиваю, и, тем не менее, после расшифровки данных не хватает. Я предполагаю, что это должно быть из-за некоторого сжатия в потоке данных, но я не знаю. В любом случае, если он использует сжатие, я понятия не имею, как преобразовать размер расшифрованного несжатого потока байтов в размер сжатого зашифрованного потока байтов, включая заголовки шифрования и трейлеры, чтобы запросить точное правильное количество байтов. Может ли кто-нибудь помочь мне сделать это?
Альтернативный подход для меня состоит в том, чтобы просто искать два CR + LF подряд, что также будет сигнализировать об окончании ответа HTTPS. Но поскольку данные зашифрованы, я не могу понять, как выглядеть побайтно. Кажется, что DecryptMessage () в SChannel выполняет расшифровку в блоках, а не побайтно. Может ли кто-нибудь на этом форуме дать какой-либо совет о том, как сделать побитовое расшифрование, чтобы я мог найти конец чанкованного вывода?
Вторая проблема заключается в том, что DecryptMessage иногда ошибочно думает, что он завершил дешифрование, прежде чем я достигну фактического конца сообщения. Результирующее поведение I go при следующем HTTP-запросе, и я получаю остаток от предыдущего ответа, где я ожидаю увидеть заголовок нового запроса.
Очевидное решение для этого - проверить содержимое расшифрованного сообщения, чтобы увидеть, действительно ли мы достигли конца, и если нет, попытаться получить больше данных перед отправкой следующего HTTP-запроса. Но когда я делаю это и пытаюсь расшифровать, я получаю сообщение об ошибке расшифровки.
Буду признателен за любые советы / помощь, которые кто-либо может дать по стратегии. Я приложил соответствующие разделы кода для процесса чтения / дешифрования тела HTTP - я не включаю чтение и анализ заголовка, потому что это работает без каких-либо проблем.
do
{
// Note this receives large files OK, but I can't tell when I hit the end of the buffer, and this
// hangs. Need to consider a non-blocking socket?
// numBytesReceived = recv(windowsSocket, (char*)inputBuffer, inputBufSize, 0);
m_ErrorLog << "Next read size expected " << nextReadSize << endl;
numBytesReceived = recv(windowsSocket, (char*)inputBuffer, nextReadSize, 0);
m_ErrorLog << "NumBytesReceived = " << numBytesReceived << endl;
if (m_BinaryBufLen + numBytesReceived > m_BinaryBufAllocatedSize)
::EnlargeBinaryBuffer(m_BinaryBuffer,m_BinaryBufAllocatedSize,m_BinaryBufLen,numBytesReceived+1);
memcpy(m_BinaryBuffer+m_BinaryBufLen,inputBuffer,numBytesReceived);
m_BinaryBufLen += numBytesReceived;
lenStartDecryptedChunk = decryptedBodyLen;
do
{
// Decrypt the received data.
Buffers[0].pvBuffer = m_BinaryBuffer;
Buffers[0].cbBuffer = m_BinaryBufLen;
Buffers[0].BufferType = SECBUFFER_DATA; // Initial Type of the buffer 1
Buffers[1].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 2
Buffers[2].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 3
Buffers[3].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 4
Message.ulVersion = SECBUFFER_VERSION; // Version number
Message.cBuffers = 4; // Number of buffers - must contain four SecBuffer structures.
Message.pBuffers = Buffers; // Pointer to array of buffers
scRet = m_pSSPI->DecryptMessage(phContext, &Message, 0, NULL);
if (scRet == SEC_E_INCOMPLETE_MESSAGE)
break;
if( scRet == SEC_I_CONTEXT_EXPIRED )
{
m_ErrorLog << "Server shut down connection before I finished reading" << endl;
m_ErrorLog << "# of Bytes Requested = " << nextReadSize << endl;
m_ErrorLog << "# of Bytes received = " << numBytesReceived << endl;
m_ErrorLog << "Decrypted data to this point = " << endl;
m_ErrorLog << decryptedBody << endl;
m_ErrorLog << "BinaryData just decrypted: " << endl;
m_ErrorLog << Buffers[0].pvBuffer << endl;
break; // Server signalled end of session
}
if( scRet != SEC_E_OK &&
scRet != SEC_I_RENEGOTIATE &&
scRet != SEC_I_CONTEXT_EXPIRED )
{
DisplaySECError((DWORD)scRet,errmsg);
m_ErrorLog << "CSISPDoc::ReadDecrypt(): " << "Failed to decrypt message--Error=" << errmsg;
if (decryptedBody)
m_ErrorLog << decryptedBody << endl;
return scRet;
}
// Locate data and (optional) extra buffers.
pDataBuffer = NULL;
pExtraBuffer = NULL;
for(i = 1; i < 4; i++)
{
if( pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA )
pDataBuffer = &Buffers[i];
if( pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA )
pExtraBuffer = &Buffers[i];
}
// Display the decrypted data.
if(pDataBuffer)
{
length = pDataBuffer->cbBuffer;
if( length ) // check if last two chars are CR LF
{
buff = (PBYTE)pDataBuffer->pvBuffer; // printf( "n-2= %d, n-1= %d \n", buff[length-2], buff[length-1] );
if (decryptedBodyLen+length+1 > decryptedBodyAllocatedSize)
::EnlargeBuffer(decryptedBody,decryptedBodyAllocatedSize,decryptedBodyLen,length+1);
memcpy_s(decryptedBody+decryptedBodyLen,decryptedBodyAllocatedSize-decryptedBodyLen,buff,length);
decryptedBodyLen += length;
m_ErrorLog << buff << endl;
}
}
// Move any "extra" data to the input buffer -- this has not yet been decrypted.
if(pExtraBuffer)
{
MoveMemory(m_BinaryBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
m_BinaryBufLen = pExtraBuffer->cbBuffer; // printf("inputStrLen= %d \n", inputStrLen);
}
}
while (pExtraBuffer);
if (decryptedBody)
{
if (incompletePacket)
p1 = decryptedBody + lenStartFragmentedPacket;
else
p1 = decryptedBody + lenStartDecryptedChunk;
p2 = p1;
pEndDecryptedBody = decryptedBody+decryptedBodyLen;
if (lastDecryptRes != SEC_E_INCOMPLETE_MESSAGE)
chunkSizeBlock = true;
do
{
while (p2 < pEndDecryptedBody && (*p2 != '\r' || *(p2+1) != '\n'))
p2++;
// if we're here, we probably found the end of the current line. The pattern we are
// reading is chunk length, chunk, chunk length, chunk,...,chunk lenth (==0)
if (*p2 == '\r' && *(p2+1) == '\n') // new line character -- found chunk size
{
if (chunkSizeBlock) // reading the size of the chunk
{
pStartHexNum = SkipWhiteSpace(p1,p2);
pEndHexNum = SkipWhiteSpaceBackwards(p1,p2);
chunkSize = HexCharToInt(pStartHexNum,pEndHexNum);
p2 += 2; // skip past the newline character
chunkSizeBlock = false;
if (!chunkSize) // chunk size of 0 means we're done
{
bulkReadDone = true;
p2 += 2; // skip past the final CR+LF
}
nextReadSize = chunkSize+8; // chunk + CR/LF + next chunk size (4 hex digits) + CR/LF + encryption header/trailer
}
else // copy the actual chunk
{
if (p2-p1 != chunkSize)
{
m_ErrorLog << "Warning: Actual chunk size of " << p2 - p1 << " != stated chunk size = " << chunkSize << endl;
}
else
{
// copy over the actual chunk data //
if (m_HTTPBodyLen + chunkSize > m_HTTPBodyAllocatedSize)
::EnlargeBuffer(m_HTTPBody,m_HTTPBodyAllocatedSize,m_HTTPBodyLen,chunkSize+1);
memcpy_s(m_HTTPBody+m_HTTPBodyLen,m_HTTPBodyAllocatedSize,p1,chunkSize);
m_HTTPBodyLen += chunkSize;
m_HTTPBody[m_HTTPBodyLen] = 0; // null-terminate
p2 += 2; // skip over chunk and end of line characters
chunkSizeBlock = true;
chunkSize = 0;
incompletePacket = false;
lenStartFragmentedPacket = 0;
}
}
p1 = p2; // move to start of next chunk field
}
else // got to end of encrypted body with no CR+LF found --> fragmeneted chunk. So we need to read and decrypt at least one more chunk
{
incompletePacket = true;
lenStartFragmentedPacket = p1-decryptedBody;
}
}
while (p2 < pEndDecryptedBody);
lastDecryptRes = scRet;
}
}
while (scRet == SEC_E_INCOMPLETE_MESSAGE && !bulkReadDone);