static apr_status_t ssl_encrypt()

in buckets/ssl_buckets.c [1236:1401]


static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize,
                                char *buf, apr_size_t *len)
{
    const char *data;
    apr_size_t interim_bufsize;
    serf_ssl_context_t *ctx = baton;
    apr_status_t status;

    if (ctx->fatal_err)
        return ctx->fatal_err;

    serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
              "ssl_encrypt: begin %"APR_SIZE_T_FMT"\n", bufsize);

    if (!ctx->handshake_done) {

        ctx->handshake_done = TRUE;

        status = ssl_handshake(ctx, TRUE);

        if (SERF_BUCKET_READ_ERROR(status)) {
            return status;
        }
    }

    /* Try to read already encrypted but unread data first. */
    status = serf_bucket_read(ctx->encrypt_pending, bufsize, &data, len);
    if (SERF_BUCKET_READ_ERROR(status)) {
        return status;
    }

    /* Aha, we read something.  Return that now. */
    if (*len) {
        memcpy(buf, data, *len);
        if (APR_STATUS_IS_EOF(status)) {
            status = APR_SUCCESS;
        }

        serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
                  "ssl_encrypt: %d %"APR_SIZE_T_FMT" (quick read)\n",
                  status, *len);

        return status;
    }

    /* Oh well, read from our stream now. */
    interim_bufsize = bufsize;
    do {
        apr_size_t interim_len;

        if (!ctx->want_read) {
            struct iovec vecs[SERF__STD_IOV_COUNT];
            int vecs_read;

            status = serf_bucket_read_iovec(ctx->encrypt.stream,
                                            interim_bufsize,
                                            COUNT_OF(vecs), vecs,
                                            &vecs_read);

            if (!SERF_BUCKET_READ_ERROR(status) && vecs_read) {
                char *vecs_data;
                int i, cur, vecs_data_len;
                int ssl_len;

                /* Combine the buffers of the iovec into one buffer, as
                   that is with SSL_write requires. */
                vecs_data_len = 0;
                for (i = 0; i < vecs_read; i++) {
                    vecs_data_len += vecs[i].iov_len;
                }

                vecs_data = serf_bucket_mem_alloc(ctx->allocator,
                                                  vecs_data_len);

                cur = 0;
                for (i = 0; i < vecs_read; i++) {
                    memcpy(vecs_data + cur, vecs[i].iov_base, vecs[i].iov_len);
                    cur += vecs[i].iov_len;
                }

                interim_bufsize -= vecs_data_len;
                interim_len = vecs_data_len;

                serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
                          "ssl_encrypt: bucket read %"APR_SIZE_T_FMT" bytes; "\
                          "status %d\n", interim_len, status);

                /* When an SSL_write() operation has to be repeated because of
                   SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it MUST be
                   repeated with the same arguments.

                   Unless SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER is set...
                   ... which we now do.
                */
                ctx->crypt_status = APR_SUCCESS; /* Clear before calling SSL */
                ssl_len = SSL_write(ctx->ssl, vecs_data, interim_len);

                serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
                          "ssl_encrypt: SSL write: %d\n", ssl_len);

                /* If we failed to write... */
                if (ssl_len <= 0) {

                    /* Ah, bugger. We need to put that data back.
                       Note: use the copy here, we do not own the original iovec
                       data buffer so it will be freed on next read. */
                    serf_bucket_t *vecs_copy =
                        serf_bucket_simple_own_create(vecs_data,
                                                      vecs_data_len,
                                                      ctx->allocator);
                    serf_bucket_aggregate_prepend(ctx->encrypt.stream,
                                                  vecs_copy);

                    status = status_from_ssl_error(ctx, ssl_len, TRUE);
                } else {
                    /* We're done with this data. */
                    serf_bucket_mem_free(ctx->allocator, vecs_data);

                    serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
                              "---\n%.*s\n-(%"APR_SIZE_T_FMT")-\n",
                              (int)interim_len, vecs_data, interim_len);

                }
            }
        }
        else {
            interim_len = 0;
            *len = 0;
            status = ctx->crypt_status;

            if (!status) {
                status = APR_EAGAIN; /* Exit loop */
            }
        }

    } while (!status && interim_bufsize);

    /* Okay, we exhausted our underlying stream. */
    if (!SERF_BUCKET_READ_ERROR(status)) {
        apr_status_t agg_status;
        struct iovec vecs[SERF__STD_IOV_COUNT];
        int vecs_read, i;

        /* We read something! */
        agg_status = serf_bucket_read_iovec(ctx->encrypt_pending, bufsize,
                                            COUNT_OF(vecs), vecs, &vecs_read);
        *len = 0;
        for (i = 0; i < vecs_read; i++) {
            memcpy(buf + *len, vecs[i].iov_base, vecs[i].iov_len);
            *len += vecs[i].iov_len;
        }

        serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
                  "ssl_encrypt read agg: %d %d %d %"APR_SIZE_T_FMT"\n", status, agg_status,
                  ctx->crypt_status, *len);

        if (!agg_status) {
            status = APR_SUCCESS;
        }
    }

    serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
              "ssl_encrypt finished: %d %"APR_SIZE_T_FMT"\n", status, *len);

    return status;
}