in buckets/ssl_buckets.c [1118:1232]
static apr_status_t ssl_decrypt(void *baton, apr_size_t bufsize,
char *buf, apr_size_t *len)
{
serf_ssl_context_t *ctx = baton;
apr_status_t status;
int ssl_len;
if (ctx->fatal_err)
return ctx->fatal_err;
if (!ctx->handshake_done) {
ctx->handshake_done = TRUE;
status = ssl_handshake(ctx, FALSE);
if (SERF_BUCKET_READ_ERROR(status)) {
*len = 0;
return status;
}
}
serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
"ssl_decrypt: begin %" APR_SIZE_T_FMT "\n", bufsize);
ctx->want_read = FALSE; /* Reading now */
ctx->crypt_status = APR_SUCCESS; /* Clear before calling SSL */
/* When an SSL_read() operation has to be repeated because of
SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be repeated
with the same arguments.
Luckily we can assume that we are called from the databuffer
implementation */
/* Is there some data waiting to be read? */
ssl_len = SSL_read(ctx->ssl, buf, bufsize);
if (ssl_len < 0) {
*len = 0;
status = status_from_ssl_error(ctx, ssl_len, FALSE);
} else if (ssl_len == 0) {
/* The server shut down the connection. */
int ssl_err, shutdown;
*len = 0;
/* Check for SSL_RECEIVED_SHUTDOWN */
shutdown = SSL_get_shutdown(ctx->ssl);
/* Check for SSL_ERROR_ZERO_RETURN */
ssl_err = SSL_get_error(ctx->ssl, ssl_len);
if (shutdown == SSL_RECEIVED_SHUTDOWN &&
ssl_err == SSL_ERROR_ZERO_RETURN) {
/* The server closed the SSL session. While this doesn't
necessary mean the connection is closed, let's close
it here anyway.
We can optimize this later. */
serf__log(LOGLVL_ERROR, LOGCOMP_SSL, __FILE__, ctx->config,
"ssl_decrypt: SSL read error: server"
" shut down connection!\n");
status = APR_EOF;
} else {
/* A fatal error occurred. */
ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
log_ssl_error(ctx);
}
} else {
*len = ssl_len;
status = ctx->crypt_status;
serf__log(LOGLVL_DEBUG, LOGCOMP_SSLMSG, __FILE__, ctx->config,
"---\n%.*s\n-(%"APR_SIZE_T_FMT")-\n", (int)*len, buf, *len);
}
if (!ctx->handshake_finished
&& !SERF_BUCKET_READ_ERROR(status)) {
apr_status_t s = APR_SUCCESS;
/* Once we got through the initial handshake, we should have received
the ALPN information if there is such information. */
ctx->handshake_finished = SSL_is_init_finished(ctx->ssl)
#ifdef SERF_HAVE_OSSL_HANDSHAKE_STATE
|| (SSL_get_state(ctx->ssl) == TLS_ST_OK);
#elif defined(SSL_CB_HANDSHAKE_DONE)
|| (SSL_state(ctx->ssl)
& SSL_CB_HANDSHAKE_DONE);
#else
#error "neither TLS_ST_OK nor SSL_CB_HANDSHAKE_DONE is available"
#endif
/* Call the protocol callback as soon as possible as this triggers
pipelining data for the selected protocol. */
if (ctx->protocol_callback) {
const char *protocol = ssl_get_selected_protocol(ctx);
/* When ctx->init_finished is TRUE protocol will never be NULL,
reporting the final result if not already handled */
if (protocol) {
s = ctx->protocol_callback(ctx->protocol_userdata, protocol);
ctx->protocol_callback = NULL;
}
}
if (SERF_BUCKET_READ_ERROR(s)) {
serf__log(LOGLVL_ERROR, LOGCOMP_SSL, __FILE__, ctx->config,
"ssl_decrypt: negotiation reported: %d\n", status);
status = s;
}
}
serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
"ssl_decrypt: %d %"APR_SIZE_T_FMT"\n", status, *len);
return status;
}