NOEXPORT int ocsp_request(CLI *c, X509_STORE_CTX *callback_ctx,
        OCSP_CERTID *cert_id, char *url) {
    int ocsp_status=V_OCSP_CERTSTATUS_UNKNOWN;
    int response_status;
    int reason;
    int ctx_err=X509_V_ERR_APPLICATION_VERIFICATION;
    OCSP_REQUEST *request=NULL;
    OCSP_RESPONSE *response=NULL;
    OCSP_BASICRESP *basic_response=NULL;
    ASN1_GENERALIZEDTIME *revoked_at=NULL,
        *this_update=NULL, *next_update=NULL;

    /* build request */
    request=OCSP_REQUEST_new();
    if(!request) {
        sslerror("OCSP: OCSP_REQUEST_new");
        goto cleanup;
    }
    if(!OCSP_request_add0_id(request, OCSP_CERTID_dup(cert_id))) {
        sslerror("OCSP: OCSP_request_add0_id");
        goto cleanup;
    }
    if(c->opt->option.nonce)
        OCSP_request_add1_nonce(request, NULL, -1);

    /* send the request and get a response */
    response=ocsp_get_response(c, request, url);
    if(!response)
        goto cleanup;
    response_status=OCSP_response_status(response);
    if(response_status!=OCSP_RESPONSE_STATUS_SUCCESSFUL) {
        s_log(LOG_ERR, "OCSP: Responder error: %d: %s",
            response_status, OCSP_response_status_str(response_status));
        goto cleanup;
    }

    /* verify the response */
    basic_response=OCSP_response_get1_basic(response);
    if(!basic_response) {
        sslerror("OCSP: OCSP_response_get1_basic");
        goto cleanup;
    }
    if(c->opt->option.nonce && OCSP_check_nonce(request, basic_response)<=0) {
        s_log(LOG_ERR, "OCSP: Invalid or unsupported nonce");
        goto cleanup;
    }
    if(OCSP_basic_verify(basic_response,
            X509_STORE_CTX_get0_chain(callback_ctx),
            SSL_CTX_get_cert_store(c->opt->ctx), c->opt->ocsp_flags)<=0) {
        sslerror("OCSP: OCSP_basic_verify");
        goto cleanup;
    }
    if(!OCSP_resp_find_status(basic_response, cert_id, &ocsp_status, &reason,
            &revoked_at, &this_update, &next_update)) {
        sslerror("OCSP: OCSP_resp_find_status");
        goto cleanup;
    }
    s_log(LOG_INFO, "OCSP: Status: %s", OCSP_cert_status_str(ocsp_status));
    log_time(LOG_INFO, "OCSP: This update", this_update);
    log_time(LOG_INFO, "OCSP: Next update", next_update);
    /* check if the response is valid for at least one minute */
    if(!OCSP_check_validity(this_update, next_update, 60, -1)) {
        sslerror("OCSP: OCSP_check_validity");
        ocsp_status=V_OCSP_CERTSTATUS_UNKNOWN;
        goto cleanup;
    }
    switch(ocsp_status) {
    case V_OCSP_CERTSTATUS_GOOD:
        s_log(LOG_NOTICE, "OCSP: Certificate accepted");
        break;
    case V_OCSP_CERTSTATUS_REVOKED:
        if(reason==-1)
            s_log(LOG_ERR, "OCSP: Certificate revoked");
        else
            s_log(LOG_ERR, "OCSP: Certificate revoked: %d: %s",
                reason, OCSP_crl_reason_str(reason));
        log_time(LOG_NOTICE, "OCSP: Revoked at", revoked_at);
        ctx_err=X509_V_ERR_CERT_REVOKED;
        break;
    case V_OCSP_CERTSTATUS_UNKNOWN:
        s_log(LOG_WARNING, "OCSP: Unknown verification status");
    }
cleanup:
    if(request)
        OCSP_REQUEST_free(request);
    if(response)
        OCSP_RESPONSE_free(response);
    if(basic_response)
        OCSP_BASICRESP_free(basic_response);
    if(ocsp_status!=V_OCSP_CERTSTATUS_GOOD)
        X509_STORE_CTX_set_error(callback_ctx, ctx_err);
    return ocsp_status;
}