in c/src/tls/openssl.c [2109:2199]
static void decrypt(pn_tls_t *tls) {
assert(tls);
buff_ptr curr_result = current_decrypted_result(tls);
pbuffer_t *pending = next_decrypt_pending(tls);
bool peek_needed = false;
bool decrypt_done = false;
while (!decrypt_done) {
if (tls->pn_tls_err)
return;
// Insert encrypted data into openssl filter chain.
while (pending && !tls->dec_wblocked && !tls->dec_closed) {
size_t n = pending->size - tls->decrypt_pending_offset;
if (n) {
char *bytes = pending->bytes + pending->offset + tls->decrypt_pending_offset;
int wcount = BIO_write(tls->bio_net_io, bytes, n);
if (wcount < (int) n)
tls->dec_wblocked = true;
if (wcount > 0) {
if (!tls->dec_closed)
tls->dec_rblocked = false;
tls->enc_rblocked = false;
tls->decrypt_pending_offset += wcount;
// new write side content may generate decrypted bytes, EOS, error
peek_needed = true;
// tls_trace_callback("tls decrypt: scanned %d bytes", wcount);)
}
}
pending = next_decrypt_pending(tls);
}
// Extract decrypted data from other side of filter chain.
while (curr_result && !tls->dec_rblocked) {
pbuffer_t *result = &tls->dresult_buffers[curr_result-1];
size_t n = room(result);
assert(n);
int rcount = SSL_read(tls->ssl, result->bytes + result->offset + result->size, n);
if (rcount > 0) {
tls->dec_wblocked = false;
peek_needed = true; // May or may not have drained all available decrypted bytes.
if (result->size == 0) {
// first data inserted: convert from blank type to encrypted type
assert(result->type == buff_dresult_blank);
blank_dresult_pop(tls, curr_result);
decrypted_result_add(tls, curr_result);
}
result->size += rcount;
if (room(result) == 0) {
// Tentatively set a blank buffer for future output without popping.
curr_result = tls->dresult_first_blank;
}
} else {
tls->dec_rblocked = true;
peek_needed = false;
tls->dec_rpending = false;
check_error_reason(tls, rcount);
}
}
// Done if not possible to move any more bytes from input to output bufs
if ( (tls->dec_closed || !pending || tls->dec_wblocked) /* write side */ &&
(!curr_result || tls->dec_rblocked) ) /* read side */ {
decrypt_done = true;
if (peek_needed && !tls->pn_tls_err && !tls->dec_closed) {
// Set dec_rpending.
// Make OpenSSL process input to at least first decrypted byte (if any)
char unused;
int pcount = SSL_peek(tls->ssl, &unused, 1);
tls->dec_rpending = (pcount == 1);
if (pcount <= 0) {
check_error_reason(tls, pcount);
}
// Peek may have made more room in buffer (i.e. handshake followed by large
// incomplete application record and dec_wblocked). If we did not process an
// application record, we must have processed at least one non-app record.
// No longer write blocked after peek. PROTON-2736.
if (!tls->dec_rpending && tls->dec_wblocked) {
decrypt_done = false;
tls->dec_wblocked = false;
}
}
}
}
if (!tls->handshake_ok && SSL_do_handshake(tls->ssl) == 1) {
tls->handshake_ok = true;
tls->can_shutdown = true;
}
}