pn_ssl_t *pn_ssl(pn_transport_t *transport) { if (!transport) return NULL; if (transport->ssl) return (pn_ssl_t *) transport; pni_ssl_t *ssl = (pni_ssl_t *) calloc(1, sizeof(pni_ssl_t)); if (!ssl) return NULL; ssl->out_size = APP_BUF_SIZE; uint32_t max_frame = pn_transport_get_max_frame(transport); ssl->in_size = max_frame ? max_frame : APP_BUF_SIZE; ssl->outbuf = (char *)malloc(ssl->out_size); if (!ssl->outbuf) { free(ssl); return NULL; } ssl->inbuf = (char *)malloc(ssl->in_size); if (!ssl->inbuf) { free(ssl->outbuf); free(ssl); return NULL; } transport->ssl = ssl; // Set up hostname from any bound connection if (transport->connection) { if (pn_string_size(transport->connection->hostname)) { pn_ssl_set_peer_hostname((pn_ssl_t *) transport, pn_string_get(transport->connection->hostname)); } } return (pn_ssl_t *) transport; }
pn_ssl_t *pn_ssl(pn_transport_t *transport) { if (!transport) return NULL; if (transport->ssl) return transport->ssl; pn_ssl_t *ssl = (pn_ssl_t *) calloc(1, sizeof(pn_ssl_t)); if (!ssl) return NULL; ssl->out_size = APP_BUF_SIZE; uint32_t max_frame = pn_transport_get_max_frame(transport); ssl->in_size = max_frame ? max_frame : APP_BUF_SIZE; ssl->outbuf = (char *)malloc(ssl->out_size); if (!ssl->outbuf) { free(ssl); return NULL; } ssl->inbuf = (char *)malloc(ssl->in_size); if (!ssl->inbuf) { free(ssl->outbuf); free(ssl); return NULL; } ssl->transport = transport; transport->ssl = ssl; ssl->io_layer = &transport->io_layers[PN_IO_SSL]; ssl->io_layer->context = ssl; ssl->io_layer->process_input = pn_io_layer_input_passthru; ssl->io_layer->process_output = pn_io_layer_output_passthru; ssl->io_layer->process_tick = pn_io_layer_tick_passthru; ssl->io_layer->buffered_output = buffered_output; ssl->io_layer->buffered_input = buffered_input; ssl->trace = (transport->disp) ? transport->disp->trace : PN_TRACE_OFF; return ssl; }
// take data from the network, and pass it into SSL. Attempt to read decrypted data from // SSL socket and pass it to the application. 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 && init_ssl_socket(transport, ssl)) return PN_EOS; ssl_log( transport, "process_input_ssl( data size=%d )",available ); ssize_t consumed = 0; bool work_pending; bool shutdown_input = (available == 0); // caller is closed do { work_pending = false; // 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, "Wrote %d bytes to BIO Layer, %d 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, "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, "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, "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); } } else { if (BIO_should_write( ssl->bio_ssl )) { ssl->write_blocked = true; ssl_log(transport, "Detected write-blocked"); } if (BIO_should_read( ssl->bio_ssl )) { ssl->read_blocked = true; ssl_log(transport, "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, "Application consumed %d bytes from peer", (int) consumed ); } else if (consumed < 0) { ssl_log(transport, "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. pn_transport_log(transport, "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, "process_input_ssl() returning %d", (int) consumed); return consumed; }