Commit 6e4141e2 authored by William A. Rowe Jr's avatar William A. Rowe Jr
Browse files

  Rename the many flavors of filter_ctx (pRec, fctx etc) to filter_ctx,
  wbio to bio_out, BIO new and free to create and destroy (to match OpenSSL),
  refactor the bio write code to stash errors in outctx->rc,
  fix the blocking read at EOF if we have some data to return,
  and preempt the nonblock read on GETLINE if we have the newline already.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk/modules/ssl@97393 13f79535-47bb-0310-9956-ffa450edef68
parent f20fd165
Loading
Loading
Loading
Loading
+117 −89
Changes for ssl_engine_io.c: 117 added lines, 89 removed lines.
Original line number Diff line number Diff line
@@ -129,19 +129,21 @@
 */

typedef struct {
    SSLFilterRec *frec;
    SSLFilterRec *filter_ctx;
    conn_rec *c;
    apr_bucket_brigade *bb;
    apr_size_t length;
    char buffer[AP_IOBUFSIZE];
    apr_size_t blen;
    apr_status_t rc;
} bio_filter_out_ctx_t;

static bio_filter_out_ctx_t *bio_filter_out_ctx_new(SSLFilterRec *frec, conn_rec *c)
static bio_filter_out_ctx_t *bio_filter_out_ctx_new(SSLFilterRec *filter_ctx,
                                                    conn_rec *c)
{
    bio_filter_out_ctx_t *outctx = apr_palloc(c->pool, sizeof(*outctx));

    outctx->frec = frec;
    outctx->filter_ctx = filter_ctx;
    outctx->c = c;
    outctx->bb = apr_brigade_create(c->pool, c->bucket_alloc);
    outctx->blen = 0;
@@ -156,7 +158,8 @@ static int bio_filter_out_flush(BIO *bio)
    apr_bucket *e;

    if (!(outctx->blen || outctx->length)) {
        return APR_SUCCESS;
        outctx->rc = APR_SUCCESS;
        return 1;
    }

    if (outctx->blen) {
@@ -173,10 +176,12 @@ static int bio_filter_out_flush(BIO *bio)
    e = apr_bucket_flush_create(outctx->bb->bucket_alloc);
    APR_BRIGADE_INSERT_TAIL(outctx->bb, e);

    return ap_pass_brigade(outctx->frec->pOutputFilter->next, outctx->bb);
    outctx->rc = ap_pass_brigade(outctx->filter_ctx->pOutputFilter->next,
                                 outctx->bb);
    return (outctx->rc == APR_SUCCESS) ? 1 : -1;
}

static int bio_filter_new(BIO *bio)
static int bio_filter_create(BIO *bio)
{
    bio->shutdown = 1;
    bio->init = 1;
@@ -186,7 +191,7 @@ static int bio_filter_new(BIO *bio)
    return 1;
}

static int bio_filter_free(BIO *bio)
static int bio_filter_destroy(BIO *bio)
{
    if (bio == NULL) {
        return 0;
@@ -236,7 +241,9 @@ static int bio_filter_out_write(BIO *bio, const char *in, int inl)
        outctx->length += inl;
        APR_BRIGADE_INSERT_TAIL(outctx->bb, bucket);

        bio_filter_out_flush(bio);
        if (bio_filter_out_flush(bio) < 0) {
            return -1;
        }
    }

    return inl;
@@ -279,7 +286,7 @@ static long bio_filter_out_ctrl(BIO *bio, int cmd, long num, void *ptr)
        ret = (long)(outctx->blen + outctx->length);
        break;
      case BIO_CTRL_FLUSH:
        ret = (bio_filter_out_flush(bio) == APR_SUCCESS);
        ret = bio_filter_out_flush(bio);
        break;
      case BIO_CTRL_DUP:
        ret = 1;
@@ -318,8 +325,8 @@ static BIO_METHOD bio_filter_out_method = {
    bio_filter_out_puts,     /* puts is never called */
    bio_filter_out_gets,     /* gets is never called */
    bio_filter_out_ctrl,
    bio_filter_new,
    bio_filter_free,
    bio_filter_create,
    bio_filter_destroy,
#ifdef OPENSSL_VERSION_NUMBER
    NULL /* sslc does not have the callback_ctrl field */
#endif
@@ -332,7 +339,7 @@ typedef struct {

typedef struct {
    SSL *ssl;
    BIO *wbio;
    BIO *bio_out;
    ap_filter_t *f;
    apr_status_t rc;
    ap_input_mode_t mode;
@@ -341,7 +348,7 @@ typedef struct {
    char_buffer_t cbuf;
    apr_pool_t *pool;
    char buffer[AP_IOBUFSIZE];
    SSLFilterRec *frec;
    SSLFilterRec *filter_ctx;
} bio_filter_in_ctx_t;

/*
@@ -480,7 +487,12 @@ static int bio_filter_in_read(BIO *bio, char *in, int inl)
     * the other protocols.
     */
    if ((SSL_version(inctx->ssl) == SSL2_VERSION) || sslconn->is_proxy) {
        bio_filter_out_flush(inctx->wbio);
        if (bio_filter_out_flush(inctx->bio_out) < 0) {
            bio_filter_out_ctx_t *outctx = 
                   (bio_filter_out_ctx_t *)(inctx->bio_out->ptr);
            inctx->rc = outctx->rc;
            return -1;
        }
    }

    BIO_clear_retry_flags(bio);
@@ -549,8 +561,8 @@ static BIO_METHOD bio_filter_in_method = {
    NULL,                       /* puts is never called */
    NULL,                       /* gets is never called */
    NULL,                       /* ctrl is never called */
    bio_filter_new,
    bio_filter_free,
    bio_filter_create,
    bio_filter_destroy,
#ifdef OPENSSL_VERSION_NUMBER
    NULL /* sslc does not have the callback_ctrl field */
#endif
@@ -558,84 +570,85 @@ static BIO_METHOD bio_filter_in_method = {

static const char ssl_io_filter[] = "SSL/TLS Filter";

static int ssl_io_hook_write(SSL *ssl, unsigned char *buf, int len)
static apr_status_t ssl_filter_write(ap_filter_t *f,
                                     const char *data,
                                     apr_size_t len)
{
    int rc;
    SSLFilterRec *filter_ctx = f->ctx;
    bio_filter_out_ctx_t *outctx = 
           (bio_filter_out_ctx_t *)(filter_ctx->pbioWrite->ptr);
    int res;

    if (ssl == NULL) {
        return -1;
    /* write SSL */
    if (filter_ctx->pssl == NULL) {
        return APR_EGENERAL;
    }

    rc = SSL_write(ssl, buf, len);
    res = SSL_write(filter_ctx->pssl, (unsigned char *)data, len);

    if (rc < 0) {
        int ssl_err = SSL_get_error(ssl, rc);
    if (res < 0) {
        int ssl_err = SSL_get_error(filter_ctx->pssl, res);

        if (ssl_err == SSL_ERROR_WANT_WRITE) {
            /*
             * Simulate an EINTR in case OpenSSL wants to write more.
             * If OpenSSL wants to write more, and we were nonblocking,
             * report as an EAGAIN.  Otherwise loop, pushing more
             * data at the network filter.
             *
             * (This is usually the case when the client forces an SSL
             * renegotation which is handled implicitly by OpenSSL.)
             */
            errno = EINTR;
            outctx->rc = APR_EAGAIN;
        }
        else if (ssl_err == SSL_ERROR_SSL) {
        else if (ssl_err == SSL_ERROR_SYSCALL) {
            conn_rec *c = (conn_rec*)SSL_get_app_data(outctx->filter_ctx->pssl);
            ap_log_error(APLOG_MARK, APLOG_ERR, outctx->rc, c->base_server,
                        "SSL filter out error writing data");
        }
        else /* if (ssl_err == SSL_ERROR_SSL) */ {
            /*
             * Log SSL errors
             */
            conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server,
                    "SSL error on writing data");
            conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl);
            ap_log_error(APLOG_MARK, APLOG_ERR, outctx->rc, c->base_server,
                    "SSL library out error writing data");
            ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server);

        }
        /*
         * XXX - Just trying to reflect the behaviour in 
         * openssl_state_machine.c [mod_tls]. TBD
         */
        rc = 0;
        if (outctx->rc == APR_SUCCESS) {
            outctx->rc = APR_EGENERAL;
        }
    return rc;
    }

static apr_status_t ssl_filter_write(ap_filter_t *f,
                                     const char *data,
                                     apr_size_t len)
{
    SSLFilterRec *frec = f->ctx;
    apr_size_t n;

    /* write SSL */
    n = ssl_io_hook_write(frec->pssl, (unsigned char *)data, len);

    if (n != len) {
    else if ((apr_size_t)res != len) {
        conn_rec *c = f->c;
        char *reason = "reason unknown";

        /* XXX: probably a better way to determine this */
        if (SSL_total_renegotiations(frec->pssl)) {
        if (SSL_total_renegotiations(filter_ctx->pssl)) {
            reason = "likely due to failed renegotiation";
        }

        ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server,
        ap_log_error(APLOG_MARK, APLOG_ERR, outctx->rc, c->base_server,
                     "failed to write %d of %d bytes (%s)",
                n > 0 ? len - n : len, len, reason);
                     len - (apr_size_t)res, len, reason);

        return APR_EINVAL;
        outctx->rc = APR_EGENERAL;
    }

    return APR_SUCCESS;
    return outctx->rc;
}

static apr_status_t ssl_io_filter_Output(ap_filter_t *f,
                                         apr_bucket_brigade *bb)
{
    apr_status_t status = APR_SUCCESS;
    SSLFilterRec *frec = f->ctx;
    SSLFilterRec *filter_ctx = f->ctx;

    if (!frec->pssl) {
    if (!filter_ctx->pssl) {
        /* ssl_abort() has been called */
        return ap_pass_brigade(f->next, bb);
    }

    if ((status = ssl_hook_process_connection(frec)) != APR_SUCCESS) {
    if ((status = ssl_hook_process_connection(filter_ctx)) != APR_SUCCESS) {
        return status;
    }

@@ -646,12 +659,16 @@ static apr_status_t ssl_io_filter_Output(ap_filter_t *f,
         * These types do not require translation by OpenSSL.  
         */
        if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) {
            if ((status = bio_filter_out_flush(frec->pbioWrite)) != APR_SUCCESS) {
                return status;
            if (bio_filter_out_flush(filter_ctx->pbioWrite) < 0) {
                bio_filter_out_ctx_t *outctx = 
                       (bio_filter_out_ctx_t *)(filter_ctx->pbioWrite->ptr);
                status = outctx->rc;
                break;
            }

            if (APR_BUCKET_IS_EOS(bucket)) {
                /* By definition, nothing can come after EOS.
                /*
                 * By definition, nothing can come after EOS.
                 * which also means we can pass the rest of this brigade
                 * without creating a new one since it only contains the
                 * EOS bucket.
@@ -712,10 +729,17 @@ static apr_status_t ssl_io_input_read(bio_filter_in_ctx_t *inctx,
        if (*len >= wanted) {
            return APR_SUCCESS;
        }
        if (inctx->mode == AP_MODE_GETLINE) {
            if (memchr(buf, APR_ASCII_LF, *len)) {
                return APR_SUCCESS;
            }
        }
        else {
            /* Down to a nonblock pattern as we have some data already
             */
            inctx->block = APR_NONBLOCK_READ;
        }
    }

    while (1) {

@@ -723,7 +747,7 @@ static apr_status_t ssl_io_input_read(bio_filter_in_ctx_t *inctx,
         * from the stack.  This is where we want to consider all of
         * the blocking and SPECULATIVE semantics
         */
        rc = SSL_read(inctx->frec->pssl, buf + bytes, wanted - bytes);
        rc = SSL_read(inctx->filter_ctx->pssl, buf + bytes, wanted - bytes);

        if (rc > 0) {
            *len += rc;
@@ -747,13 +771,19 @@ static apr_status_t ssl_io_input_read(bio_filter_in_ctx_t *inctx,
                    break;
                }
            }
            else {
                if (*len > 0) {
                    inctx->rc = APR_SUCCESS;
                }
                else {
                    inctx->rc = APR_EOF;
                }
                break;
            }
        }
        else /* (rc < 0) */ {
            int ssl_err = SSL_get_error(inctx->frec->pssl, rc);
            int ssl_err = SSL_get_error(inctx->filter_ctx->pssl, rc);
            conn_rec *c = (conn_rec*)SSL_get_app_data(inctx->filter_ctx->pssl);

            if (ssl_err == SSL_ERROR_WANT_READ) {
                /*
@@ -773,29 +803,27 @@ static apr_status_t ssl_io_input_read(bio_filter_in_ctx_t *inctx,
                    }
                    break; /* non fatal error */
                }
                continue; /* try again */
            }
            else if (ssl_err == SSL_ERROR_SYSCALL) {
                conn_rec *c = (conn_rec *)SSL_get_app_data(inctx->frec->pssl);
                ap_log_error(APLOG_MARK, APLOG_ERR, inctx->rc, c->base_server,
                            "SSL filter error reading data");
                break;
                            "SSL filter in error reading data");
            }
            else /* if (ssl_err == SSL_ERROR_SSL) */ {
                /*
                 * Log SSL errors and any unexpected conditions.
                 */
                conn_rec *c = (conn_rec *)SSL_get_app_data(inctx->frec->pssl);
                ap_log_error(APLOG_MARK, APLOG_ERR, inctx->rc, c->base_server,
                            "SSL library error reading data");
                            "SSL library in error reading data");
                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server);

            }
            if (inctx->rc == APR_SUCCESS) {
                inctx->rc = APR_EGENERAL;
            }
            break;
        }
    }
    }
    return inctx->rc;
}

@@ -868,7 +896,7 @@ static void ssl_io_filter_disable(ap_filter_t *f)
{
    bio_filter_in_ctx_t *inctx = f->ctx;
    inctx->ssl = NULL;
    inctx->frec->pssl = NULL;
    inctx->filter_ctx->pssl = NULL;
}

static apr_status_t ssl_io_filter_error(ap_filter_t *f,
@@ -935,7 +963,7 @@ static apr_status_t ssl_io_filter_Input(ap_filter_t *f,
     * AP_MODE_INIT for protocols that may upgrade the connection
     * rather than have SSLEngine On configured.
     */
    status = ssl_hook_process_connection(inctx->frec);
    status = ssl_hook_process_connection(inctx->filter_ctx);

    if (status != APR_SUCCESS) {
        return ssl_io_filter_error(f, bb, status);
@@ -979,22 +1007,22 @@ static apr_status_t ssl_io_filter_Input(ap_filter_t *f,
    return APR_SUCCESS;
}

static void ssl_io_input_add_filter(SSLFilterRec *frec, conn_rec *c,
static void ssl_io_input_add_filter(SSLFilterRec *filter_ctx, conn_rec *c,
                                    SSL *ssl)
{
    bio_filter_in_ctx_t *inctx;

    inctx = apr_palloc(c->pool, sizeof(*inctx));

    frec->pInputFilter = ap_add_input_filter(ssl_io_filter, inctx, NULL, c);
    filter_ctx->pInputFilter = ap_add_input_filter(ssl_io_filter, inctx, NULL, c);

    frec->pbioRead = BIO_new(&bio_filter_in_method);
    frec->pbioRead->ptr = (void *)inctx;
    filter_ctx->pbioRead = BIO_new(&bio_filter_in_method);
    filter_ctx->pbioRead->ptr = (void *)inctx;

    inctx->frec = frec;
    inctx->filter_ctx = filter_ctx;
    inctx->ssl = ssl;
    inctx->wbio = frec->pbioWrite;
    inctx->f = frec->pInputFilter;
    inctx->bio_out = filter_ctx->pbioWrite;
    inctx->f = filter_ctx->pInputFilter;
    inctx->bb = apr_brigade_create(c->pool, c->bucket_alloc);

    inctx->cbuf.length = 0;
@@ -1005,14 +1033,14 @@ static void ssl_io_input_add_filter(SSLFilterRec *frec, conn_rec *c,
static apr_status_t ssl_io_filter_cleanup (void *data)
{
    apr_status_t ret;
    SSLFilterRec *pRec = (SSLFilterRec *)data;
    SSLFilterRec *filter_ctx = (SSLFilterRec *)data;

    if (!pRec->pssl) {
    if (!filter_ctx->pssl) {
        /* already been shutdown */
        return APR_SUCCESS;
    }

    if ((ret = ssl_hook_CloseConnection(pRec)) != APR_SUCCESS) {
    if ((ret = ssl_hook_CloseConnection(filter_ctx)) != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_ERR, ret, NULL,
                     "Error in ssl_hook_CloseConnection");
    }
@@ -1022,22 +1050,22 @@ static apr_status_t ssl_io_filter_cleanup (void *data)

void ssl_io_filter_init(conn_rec *c, SSL *ssl)
{
    SSLFilterRec *filter;
    SSLFilterRec *filter_ctx;

    filter = apr_palloc(c->pool, sizeof(SSLFilterRec));
    filter_ctx = apr_palloc(c->pool, sizeof(SSLFilterRec));

    filter->pOutputFilter   = ap_add_output_filter(ssl_io_filter,
                                                   filter, NULL, c);
    filter_ctx->pOutputFilter   = ap_add_output_filter(ssl_io_filter,
                                                   filter_ctx, NULL, c);

    filter->pbioWrite       = BIO_new(&bio_filter_out_method);
    filter->pbioWrite->ptr  = (void *)bio_filter_out_ctx_new(filter, c);
    filter_ctx->pbioWrite       = BIO_new(&bio_filter_out_method);
    filter_ctx->pbioWrite->ptr  = (void *)bio_filter_out_ctx_new(filter_ctx, c);

    ssl_io_input_add_filter(filter, c, ssl);
    ssl_io_input_add_filter(filter_ctx, c, ssl);

    SSL_set_bio(ssl, filter->pbioRead, filter->pbioWrite);
    filter->pssl            = ssl;
    SSL_set_bio(ssl, filter_ctx->pbioRead, filter_ctx->pbioWrite);
    filter_ctx->pssl            = ssl;

    apr_pool_cleanup_register(c->pool, (void*)filter,
    apr_pool_cleanup_register(c->pool, (void*)filter_ctx,
                              ssl_io_filter_cleanup, apr_pool_cleanup_null);

    if (c->base_server->loglevel >= APLOG_DEBUG) {