in c/src/ssl/schannel.cpp [1627:1766]
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;
ssl_log( transport, PN_LEVEL_TRACE, "process_input_ssl( data size=%zu )",available );
ssize_t consumed = 0;
ssize_t forwarded = 0;
bool new_app_input;
if (available == 0) {
// No more inbound network data
read_closed(transport, layer, 0);
return 0;
}
do {
if (ssl->sc_input_shutdown) {
// TLS protocol shutdown detected on input, so we are done.
read_closed(transport, layer, 0);
return PN_EOS;
}
// sc_inbuf should be ready for new or additional network encrypted bytes.
// i.e. no straggling decrypted bytes pending.
assert(ssl->in_data_count == 0 && ssl->decrypting);
new_app_input = false;
size_t count;
if (ssl->state != RUNNING) {
count = _pni_min(ssl->sc_in_size - ssl->sc_in_count, available);
} else {
// look for TLS record boundaries
if (ssl->sc_in_count < 5) {
ssl->sc_in_incomplete = true;
size_t hc = _pni_min(available, 5 - ssl->sc_in_count);
memmove(ssl->sc_inbuf + ssl->sc_in_count, input_data, hc);
ssl->sc_in_count += hc;
input_data += hc;
available -= hc;
consumed += hc;
if (ssl->sc_in_count < 5 || available == 0)
break;
}
// Top up sc_inbuf from network input_data hoping for a complete TLS Record
// We try to guess the length as an optimization, but let SChannel
// ultimately decide if there is spoofing going on.
unsigned char low = (unsigned char) ssl->sc_inbuf[4];
unsigned char high = (unsigned char) ssl->sc_inbuf[3];
size_t rec_len = high * 256 + low + 5;
if (rec_len < 5 || rec_len == ssl->sc_in_count || rec_len > ssl->sc_in_size)
rec_len = ssl->sc_in_size;
count = _pni_min(rec_len - ssl->sc_in_count, available);
}
if (count > 0) {
memmove(ssl->sc_inbuf + ssl->sc_in_count, input_data, count);
ssl->sc_in_count += count;
input_data += count;
available -= count;
consumed += count;
ssl->sc_in_incomplete = false;
}
// Try to decrypt another TLS Record.
if (ssl->sc_in_count > 0 && ssl->state <= SHUTTING_DOWN) {
if (ssl->state == NEGOTIATING) {
ssl_handshake(transport);
} else {
if (ssl_decrypt(transport)) {
// Ignore TLS Record with 0 length data (does not mean EOS)
if (ssl->in_data_size > 0) {
new_app_input = true;
app_inbytes_add(transport);
} else {
assert(ssl->decrypting == false);
rewind_sc_inbuf(ssl);
}
}
ssl_log(transport, PN_LEVEL_TRACE, "Next decryption, %zu left over", available);
}
}
if (ssl->state == SHUTTING_DOWN) {
if (ssl->network_out_pending == 0 && !ssl->queued_shutdown) {
start_ssl_shutdown(transport);
}
} else if (ssl->state == SSL_CLOSED) {
return PN_EOS;
}
// Consume or discard the decrypted bytes
if (new_app_input && (ssl->state == RUNNING || ssl->state == SHUTTING_DOWN)) {
// present app_inbytes to io_next only if it has new content
while (ssl->app_inbytes.size > 0) {
if (!ssl->app_input_closed) {
ssize_t count = transport->io_layers[layer+1]->process_input(transport, layer+1, ssl->app_inbytes.start, ssl->app_inbytes.size);
if (count > 0) {
forwarded += count;
// advance() can increase app_inbytes.size if double buffered
app_inbytes_advance(transport, count);
ssl_log(transport, PN_LEVEL_TRACE, "Application consumed %d bytes from peer", (int) count);
} else if (count == 0) {
size_t old_size = ssl->app_inbytes.size;
app_inbytes_advance(transport, 0);
if (ssl->app_inbytes.size == old_size) {
break; // no additional contiguous decrypted data available, get more network data
}
} else {
// count < 0
ssl_log(transport, PN_LEVEL_WARNING, "Application layer closed its input, error=%d (discarding %d bytes)",
(int) count, (int)ssl->app_inbytes.size);
app_inbytes_advance(transport, ssl->app_inbytes.size); // discard
read_closed(transport, layer, count);
}
} else {
ssl_log(transport, PN_LEVEL_WARNING, "Input closed discard %d bytes",
(int)ssl->app_inbytes.size);
app_inbytes_advance(transport, ssl->app_inbytes.size); // discard
}
}
}
} while (available || (ssl->sc_in_count && !ssl->sc_in_incomplete));
if (ssl->state >= SHUTTING_DOWN) {
if (ssl->app_input_closed || ssl->sc_input_shutdown) {
// Next layer doesn't want more bytes, or it can't process without more data than it has seen so far
// but the ssl stream has ended
consumed = ssl->app_input_closed ? ssl->app_input_closed : PN_EOS;
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, forwarded %d", (int) consumed, (int) forwarded);
return consumed;
}