static apr_status_t ssl_decrypt()

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;
}