in c/src/tls/openssl.c [2011:2089]
static void encrypt(pn_tls_t *tls) {
assert(tls);
buff_ptr curr_result = current_encrypted_result(tls);
pbuffer_t *pending = next_encrypt_pending(tls);
bool try_shutdown_again = false;
while (true) {
// Insert unencrypted data into BIO.
// This loop must run even if tls->pn_tls_err, in order to extract the error message for the peer.
// OpenSSL maps each write to a separate TLS record.
// The SSL can take 16KB + a bit before blocking.
// TODO: consider allowing application to configure BIO buffer size on encrypt side.
while (pending && !tls->enc_wblocked && tls->can_shutdown) {
size_t n = pending->size - tls->encrypt_pending_offset;
if (n) {
char *bytes = pending->bytes + pending->offset + tls->encrypt_pending_offset;
int wcount = SSL_write(tls->ssl, bytes, n);
if (wcount < (int) n)
tls->enc_wblocked = true;
if (wcount > 0) {
tls->enc_rblocked = false;
tls->encrypt_pending_offset += wcount;
}
}
pending = next_encrypt_pending(tls);
}
// Finished writing?
if (tls->enc_closed && !pending && !tls->ssl_shutdown) {
try_shutdown_again = !try_shutdown(tls); // may need multple tries if BIO buffers need draining
}
// Extract encrypted data from other side of BIO.
while (curr_result && !tls->enc_rblocked) {
pbuffer_t *result = &tls->eresult_buffers[curr_result-1];
size_t n = room(result);
assert(n);
int rcount = BIO_read(tls->bio_net_io, result->bytes + result->offset + result->size, n);
if (rcount < (int) n)
tls->enc_rblocked = true;
if (rcount > 0) {
if (!tls->pn_tls_err)
tls->enc_wblocked = false;
if (result->size == 0) {
// first data inserted: convert from blank type to encrypted type
assert(result->type == buff_eresult_blank);
blank_eresult_pop(tls, curr_result);
encrypted_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->eresult_first_blank;
}
if (try_shutdown_again)
try_shutdown_again = !try_shutdown(tls);
} else if (!BIO_should_retry(tls->bio_net_io)) {
int reason = SSL_get_error( tls->ssl, rcount );
switch (reason) {
case SSL_ERROR_ZERO_RETURN:
// SSL closed cleanly
ssl_log(NULL, PN_LEVEL_TRACE, "SSL connection has closed");
// TODO: replacement for: start_ssl_shutdown(transport); // KAG: not sure - this may not be necessary
tls->dec_closed = true;
tls->ssl_closed = true; // TODO: still true?
break;
default:
tls_fatal(tls, reason, PN_TLS_PROTOCOL_ERR);
}
}
}
// Done if not possible to move any more bytes from input to output bufs
if ((!pending || tls->enc_wblocked || !tls->can_shutdown) // write side
&& (!curr_result || tls->enc_rblocked)) // read side
break;
}
}