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