in c/src/ssl/openssl.c [1142:1290]
static ssize_t process_input_ssl( pn_transport_t *transport, unsigned int layer, const char *input_data, size_t available)
{
pni_ssl_t *ssl = transport->ssl;
if (ssl->ssl == NULL) {
if (init_ssl_socket(transport, ssl, NULL)) return PN_EOS;
transport->present_layers |= LAYER_SSL;
}
ssl_log( transport, PN_LEVEL_TRACE, "process_input_ssl( data size=%zu )",available );
ssize_t consumed = 0;
bool work_pending;
bool shutdown_input = (available == 0); // caller is closed
do {
work_pending = false;
ERR_clear_error();
// Write to network bio as much as possible, consuming bytes/available
if (available > 0) {
int written = BIO_write( ssl->bio_net_io, input_data, available );
if (written > 0) {
input_data += written;
available -= written;
consumed += written;
ssl->read_blocked = false;
work_pending = (available > 0);
ssl_log( transport, PN_LEVEL_TRACE, "Wrote %d bytes to BIO Layer, %" PN_ZU " left over", written, available );
}
} else if (shutdown_input) {
// lower layer (caller) has closed. Close the WRITE side of the BIO. This will cause
// an EOF to be passed to SSL once all pending inbound data has been consumed.
ssl_log( transport, PN_LEVEL_TRACE, "Lower layer closed - shutting down BIO write side");
(void)BIO_shutdown_wr( ssl->bio_net_io );
shutdown_input = false;
}
// Read all available data from the SSL socket
if (!ssl->ssl_closed && ssl->in_count < ssl->in_size) {
int read = BIO_read( ssl->bio_ssl, &ssl->inbuf[ssl->in_count], ssl->in_size - ssl->in_count );
if (read > 0) {
ssl_log( transport, PN_LEVEL_TRACE, "Read %d bytes from SSL socket for app", read );
ssl_log_clear_data(transport, &ssl->inbuf[ssl->in_count], read );
ssl->in_count += read;
work_pending = true;
} else {
if (!BIO_should_retry(ssl->bio_ssl)) {
int reason = SSL_get_error( ssl->ssl, read );
switch (reason) {
case SSL_ERROR_ZERO_RETURN:
// SSL closed cleanly
ssl_log(transport, PN_LEVEL_TRACE, "SSL connection has closed");
start_ssl_shutdown(transport); // KAG: not sure - this may not be necessary
ssl->ssl_closed = true;
break;
default:
// unexpected error
return (ssize_t)ssl_failed(transport, reason);
}
} else {
if (BIO_should_write( ssl->bio_ssl )) {
ssl->write_blocked = true;
ssl_log(transport, PN_LEVEL_TRACE, "Detected write-blocked");
}
if (BIO_should_read( ssl->bio_ssl )) {
ssl->read_blocked = true;
ssl_log(transport, PN_LEVEL_TRACE, "Detected read-blocked");
}
}
}
}
// write incoming data to app layer
if (!ssl->app_input_closed) {
if (ssl->in_count > 0 || ssl->ssl_closed) { /* if ssl_closed, send 0 count */
ssize_t consumed = transport->io_layers[layer+1]->process_input(transport, layer+1, ssl->inbuf, ssl->in_count);
if (consumed > 0) {
ssl->in_count -= consumed;
if (ssl->in_count)
memmove( ssl->inbuf, ssl->inbuf + consumed, ssl->in_count );
work_pending = true;
ssl_log( transport, PN_LEVEL_TRACE, "Application consumed %d bytes from peer", (int) consumed );
} else if (consumed < 0) {
ssl_log(transport, PN_LEVEL_TRACE, "Application layer closed its input, error=%d (discarding %d bytes)",
(int) consumed, (int)ssl->in_count);
ssl->in_count = 0; // discard any pending input
ssl->app_input_closed = consumed;
if (ssl->app_output_closed && ssl->out_count == 0) {
// both sides of app closed, and no more app output pending:
start_ssl_shutdown(transport);
}
} else {
// app did not consume any bytes, must be waiting for a full frame
if (ssl->in_count == ssl->in_size) {
// but the buffer is full, not enough room for a full frame.
// can we grow the buffer?
uint32_t max_frame = pn_transport_get_max_frame(transport);
if (!max_frame) max_frame = ssl->in_size * 2; // no limit
if (ssl->in_size < max_frame) {
// no max frame limit - grow it.
size_t newsize = pn_min(max_frame, ssl->in_size * 2);
char *newbuf = (char *)realloc( ssl->inbuf, newsize );
if (newbuf) {
ssl->in_size = newsize;
ssl->inbuf = newbuf;
work_pending = true; // can we get more input?
}
} else {
// can't gather any more input, but app needs more?
// This is a bug - since SSL can buffer up to max-frame,
// the application _must_ have enough data to process. If
// this is an oversized frame, the app _must_ handle it
// by returning an error code to SSL.
ssl_log(transport, PN_LEVEL_ERROR, "Error: application unable to consume input.");
}
}
}
}
}
} while (work_pending);
//_log(ssl, "ssl_closed=%d in_count=%d app_input_closed=%d app_output_closed=%d",
// ssl->ssl_closed, ssl->in_count, ssl->app_input_closed, ssl->app_output_closed );
// PROTON-82: Instead, close the input side as soon as we've completed enough of the SSL
// shutdown handshake to send the close_notify. We're not requiring the response, as
// some implementations never reply.
// ---
// tell transport our input side is closed if the SSL socket cannot be read from any
// longer, AND any pending input has been written up to the application (or the
// application is closed)
//if (ssl->ssl_closed && ssl->app_input_closed) {
// consumed = ssl->app_input_closed;
//}
if (ssl->app_input_closed && (SSL_get_shutdown(ssl->ssl) & SSL_SENT_SHUTDOWN) ) {
consumed = ssl->app_input_closed;
if (transport->io_layers[layer]==&ssl_output_closed_layer) {
transport->io_layers[layer] = &ssl_closed_layer;
} else {
transport->io_layers[layer] = &ssl_input_closed_layer;
}
}
ssl_log(transport, PN_LEVEL_TRACE, "process_input_ssl() returning %d", (int) consumed);
return consumed;
}