int pn_dispatch_frame(pn_dispatcher_t *disp, pn_frame_t frame) { if (frame.size == 0) { // ignore null frames if (disp->trace & PN_TRACE_FRM) pn_transport_logf(disp->transport, "%u <- (EMPTY FRAME)\n", frame.channel); return 0; } ssize_t dsize = pn_data_decode(disp->args, frame.payload, frame.size); if (dsize < 0) { pn_string_format(disp->scratch, "Error decoding frame: %s %s\n", pn_code(dsize), pn_error_text(pn_data_error(disp->args))); pn_quote(disp->scratch, frame.payload, frame.size); pn_transport_log(disp->transport, pn_string_get(disp->scratch)); return dsize; } disp->channel = frame.channel; // XXX: assuming numeric uint64_t lcode; bool scanned; int e = pn_data_scan(disp->args, "D?L.", &scanned, &lcode); if (e) { pn_transport_log(disp->transport, "Scan error"); return e; } if (!scanned) { pn_transport_log(disp->transport, "Error dispatching frame"); return PN_ERR; } uint8_t code = lcode; disp->code = code; disp->size = frame.size - dsize; if (disp->size) disp->payload = frame.payload + dsize; pn_do_trace(disp, disp->channel, IN, disp->args, disp->payload, disp->size); pn_action_t *action = disp->actions[code]; int err = action(disp); disp->channel = 0; disp->code = 0; pn_data_clear(disp->args); disp->size = 0; disp->payload = NULL; return err; }
int pn_ssl_domain_allow_unsecured_client(pn_ssl_domain_t *domain) { if (!domain) return -1; if (domain->mode != PN_SSL_MODE_SERVER) { pn_transport_log(NULL, "Cannot permit unsecured clients - not a server."); return -1; } domain->allow_unsecured = true; return 0; }
int pn_post_frame(pn_dispatcher_t *disp, uint16_t ch, const char *fmt, ...) { va_list ap; va_start(ap, fmt); pn_data_clear(disp->output_args); int err = pn_data_vfill(disp->output_args, fmt, ap); va_end(ap); if (err) { pn_transport_logf(disp->transport, "error posting frame: %s, %s: %s", fmt, pn_code(err), pn_error_text(pn_data_error(disp->output_args))); return PN_ERR; } pn_do_trace(disp, ch, OUT, disp->output_args, disp->output_payload, disp->output_size); encode_performatives: pn_buffer_clear( disp->frame ); pn_bytes_t buf = pn_buffer_bytes( disp->frame ); buf.size = pn_buffer_available( disp->frame ); ssize_t wr = pn_data_encode( disp->output_args, buf.start, buf.size ); if (wr < 0) { if (wr == PN_OVERFLOW) { pn_buffer_ensure( disp->frame, pn_buffer_available( disp->frame ) * 2 ); goto encode_performatives; } pn_transport_logf(disp->transport, "error posting frame: %s", pn_code(wr)); return PN_ERR; } pn_frame_t frame = {disp->frame_type}; frame.channel = ch; frame.payload = buf.start; frame.size = wr; size_t n; while (!(n = pn_write_frame(disp->output + disp->available, disp->capacity - disp->available, frame))) { disp->capacity *= 2; disp->output = (char *) realloc(disp->output, disp->capacity); } disp->output_frames_ct += 1; if (disp->trace & PN_TRACE_RAW) { pn_string_set(disp->scratch, "RAW: \""); pn_quote(disp->scratch, disp->output + disp->available, n); pn_string_addf(disp->scratch, "\""); pn_transport_log(disp->transport, pn_string_get(disp->scratch)); } disp->available += n; return 0; }
static void pn_do_trace(pn_dispatcher_t *disp, uint16_t ch, pn_dir_t dir, pn_data_t *args, const char *payload, size_t size) { if (disp->trace & PN_TRACE_FRM) { pn_string_format(disp->scratch, "%u %s ", ch, dir == OUT ? "->" : "<-"); pn_inspect(args, disp->scratch); if (size) { char buf[1024]; int e = pn_quote_data(buf, 1024, payload, size); pn_string_addf(disp->scratch, " (%" PN_ZU ") \"%s\"%s", size, buf, e == PN_OVERFLOW ? "... (truncated)" : ""); } pn_transport_log(disp->transport, pn_string_get(disp->scratch)); } }
// 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; }
static int init_ssl_socket(pn_transport_t* transport, pni_ssl_t *ssl) { if (ssl->ssl) return 0; if (!ssl->domain) return -1; ssl->ssl = SSL_new(ssl->domain->ctx); if (!ssl->ssl) { pn_transport_logf(transport, "SSL socket setup failure." ); return -1; } // store backpointer to pn_transport_t in SSL object: SSL_set_ex_data(ssl->ssl, ssl_ex_data_index, transport); #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME if (ssl->peer_hostname && ssl->domain->mode == PN_SSL_MODE_CLIENT) { SSL_set_tlsext_host_name(ssl->ssl, ssl->peer_hostname); } #endif // restore session, if available if (ssl->session_id) { pn_ssl_session_t *ssn = ssn_cache_find( ssl->domain, ssl->session_id ); if (ssn) { ssl_log( transport, "Restoring previous session id=%s", ssn->id ); int rc = SSL_set_session( ssl->ssl, ssn->session ); if (rc != 1) { ssl_log( transport, "Session restore failed, id=%s", ssn->id ); } LL_REMOVE( ssl->domain, ssn_cache, ssn ); ssl_session_free( ssn ); } } // now layer a BIO over the SSL socket ssl->bio_ssl = BIO_new(BIO_f_ssl()); if (!ssl->bio_ssl) { pn_transport_log(transport, "BIO setup failure." ); return -1; } (void)BIO_set_ssl(ssl->bio_ssl, ssl->ssl, BIO_NOCLOSE); // create the "lower" BIO "pipe", and attach it below the SSL layer if (!BIO_new_bio_pair(&ssl->bio_ssl_io, 0, &ssl->bio_net_io, 0)) { pn_transport_log(transport, "BIO setup failure." ); return -1; } SSL_set_bio(ssl->ssl, ssl->bio_ssl_io, ssl->bio_ssl_io); if (ssl->domain->mode == PN_SSL_MODE_SERVER) { SSL_set_accept_state(ssl->ssl); BIO_set_ssl_mode(ssl->bio_ssl, 0); // server mode ssl_log( transport, "Server SSL socket created." ); } else { // client mode SSL_set_connect_state(ssl->ssl); BIO_set_ssl_mode(ssl->bio_ssl, 1); // client mode ssl_log( transport, "Client SSL socket created." ); } ssl->subject = NULL; return 0; }
int pn_post_transfer_frame(pn_dispatcher_t *disp, uint16_t ch, uint32_t handle, pn_sequence_t id, const pn_bytes_t *tag, uint32_t message_format, bool settled, bool more, pn_sequence_t frame_limit) { bool more_flag = more; int framecount = 0; // create preformatives, assuming 'more' flag need not change compute_performatives: pn_data_clear(disp->output_args); int err = pn_data_fill(disp->output_args, "DL[IIzIoo]", TRANSFER, handle, id, tag->size, tag->start, message_format, settled, more_flag); if (err) { pn_transport_logf(disp->transport, "error posting transfer frame: %s: %s", pn_code(err), pn_error_text(pn_data_error(disp->output_args))); return PN_ERR; } do { // send as many frames as possible without changing the 'more' flag... encode_performatives: pn_buffer_clear( disp->frame ); pn_bytes_t buf = pn_buffer_bytes( disp->frame ); buf.size = pn_buffer_available( disp->frame ); ssize_t wr = pn_data_encode(disp->output_args, buf.start, buf.size); if (wr < 0) { if (wr == PN_OVERFLOW) { pn_buffer_ensure( disp->frame, pn_buffer_available( disp->frame ) * 2 ); goto encode_performatives; } pn_transport_logf(disp->transport, "error posting frame: %s", pn_code(wr)); return PN_ERR; } buf.size = wr; // check if we need to break up the outbound frame size_t available = disp->output_size; if (disp->remote_max_frame) { if ((available + buf.size) > disp->remote_max_frame - 8) { available = disp->remote_max_frame - 8 - buf.size; if (more_flag == false) { more_flag = true; goto compute_performatives; // deal with flag change } } else if (more_flag == true && more == false) { // caller has no more, and this is the last frame more_flag = false; goto compute_performatives; } } if (pn_buffer_available( disp->frame ) < (available + buf.size)) { // not enough room for payload - try again... pn_buffer_ensure( disp->frame, available + buf.size ); goto encode_performatives; } pn_do_trace(disp, ch, OUT, disp->output_args, disp->output_payload, available); memmove( buf.start + buf.size, disp->output_payload, available); disp->output_payload += available; disp->output_size -= available; buf.size += available; pn_frame_t frame = {disp->frame_type}; frame.channel = ch; frame.payload = buf.start; frame.size = buf.size; size_t n; while (!(n = pn_write_frame(disp->output + disp->available, disp->capacity - disp->available, frame))) { disp->capacity *= 2; disp->output = (char *) realloc(disp->output, disp->capacity); } disp->output_frames_ct += 1; framecount++; if (disp->trace & PN_TRACE_RAW) { pn_string_set(disp->scratch, "RAW: \""); pn_quote(disp->scratch, disp->output + disp->available, n); pn_string_addf(disp->scratch, "\""); pn_transport_log(disp->transport, pn_string_get(disp->scratch)); } disp->available += n; } while (disp->output_size > 0 && framecount < frame_limit); disp->output_payload = NULL; return framecount; }