/* Returns the amount written. */ static int bio_bucket_write(BIO *bio, const char *in, int inl) { serf_ssl_context_t *ctx = bio->ptr; serf_bucket_t *tmp; #ifdef SSL_VERBOSE printf("bio_bucket_write called for %d bytes\n", inl); #endif if (ctx->encrypt.status == SERF_ERROR_WAIT_CONN && !BIO_should_read(ctx->bio)) { #ifdef SSL_VERBOSE printf("bio_bucket_write waiting: (%d %d %d)\n", BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), BIO_get_retry_flags(ctx->bio)); #endif /* Falling back... */ ctx->encrypt.exhausted_reset = 1; BIO_clear_retry_flags(bio); } tmp = serf_bucket_simple_copy_create(in, inl, ctx->encrypt.pending->allocator); serf_bucket_aggregate_append(ctx->encrypt.pending, tmp); return inl; }
int BIO_zero_copy_get_read_buf_done(BIO* bio, size_t bytes_read) { struct bio_bio_st* b; struct bio_bio_st* peer_b; size_t max_available; size_t dummy_read_offset; uint8_t* dummy_read_buf; assert(BIO_get_retry_flags(bio) == 0); if (!bio->init) { OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED); return 0; } b = bio->ptr; if (!b || !b->peer) { OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD); return 0; } peer_b = b->peer->ptr; if (!peer_b || !peer_b->peer || peer_b->peer->ptr != b) { OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD); return 0; } if (!peer_b->zero_copy_read_lock) { OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT); return 0; } max_available = bio_zero_copy_get_read_buf(peer_b, &dummy_read_buf, &dummy_read_offset); if (bytes_read > max_available) { OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT); return 0; } peer_b->len -= bytes_read; assert(peer_b->len >= 0); assert(peer_b->offset + bytes_read <= peer_b->size); /* Move read offset. If zero_copy_write_lock == 1 we must advance the * offset even if buffer becomes empty, to make sure * write_offset = (offset + len) mod size does not change. */ if (peer_b->offset + bytes_read == peer_b->size || (!peer_b->zero_copy_write_lock && peer_b->len == 0)) { peer_b->offset = 0; } else { peer_b->offset += bytes_read; } bio->num_read += bytes_read; peer_b->zero_copy_read_lock = 0; return 1; }
/* Returns the amount read. */ static int bio_bucket_read(BIO *bio, char *in, int inlen) { serf_ssl_context_t *ctx = bio->ptr; const char *data; apr_status_t status; apr_size_t len; #ifdef SSL_VERBOSE printf("bio_bucket_read called for %d bytes\n", inlen); #endif if (ctx->encrypt.status == SERF_ERROR_WAIT_CONN && BIO_should_read(ctx->bio)) { #ifdef SSL_VERBOSE printf("bio_bucket_read waiting: (%d %d %d)\n", BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), BIO_get_retry_flags(ctx->bio)); #endif /* Falling back... */ ctx->encrypt.exhausted_reset = 1; BIO_clear_retry_flags(bio); } status = serf_bucket_read(ctx->decrypt.pending, inlen, &data, &len); ctx->decrypt.status = status; #ifdef SSL_VERBOSE printf("bio_bucket_read received %d bytes (%d)\n", len, status); #endif if (!SERF_BUCKET_READ_ERROR(status)) { /* Oh suck. */ if (len) { memcpy(in, data, len); return len; } if (APR_STATUS_IS_EOF(status)) { BIO_set_retry_read(bio); return -1; } } return -1; }
void BIO_copy_next_retry(BIO *bio) { BIO_clear_retry_flags(bio); BIO_set_flags(bio, BIO_get_retry_flags(bio->next_bio)); bio->retry_reason = bio->next_bio->retry_reason; }
void BIO_copy_next_retry(BIO *b) { BIO_set_flags(b,BIO_get_retry_flags(b->next_bio)); b->retry_reason=b->next_bio->retry_reason; }
/* This function reads a decrypted stream and returns an encrypted stream. */ 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; #ifdef SSL_VERBOSE printf("ssl_encrypt: begin %d\n", bufsize); #endif /* 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; } #ifdef SSL_VERBOSE printf("ssl_encrypt: %d %d %d (quick read)\n", status, *len, BIO_get_retry_flags(ctx->bio)); #endif return status; } if (BIO_should_retry(ctx->bio) && BIO_should_write(ctx->bio)) { #ifdef SSL_VERBOSE printf("ssl_encrypt: %d %d %d (should write exit)\n", status, *len, BIO_get_retry_flags(ctx->bio)); #endif return APR_EAGAIN; } /* If we were previously blocked, unblock ourselves now. */ if (BIO_should_read(ctx->bio)) { #ifdef SSL_VERBOSE printf("ssl_encrypt: reset %d %d (%d %d %d)\n", status, ctx->encrypt.status, BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), BIO_get_retry_flags(ctx->bio)); #endif ctx->encrypt.status = APR_SUCCESS; ctx->encrypt.exhausted_reset = 0; } /* Oh well, read from our stream now. */ interim_bufsize = bufsize; do { apr_size_t interim_len; if (!ctx->encrypt.status) { struct iovec vecs[64]; int vecs_read; status = serf_bucket_read_iovec(ctx->encrypt.stream, interim_bufsize, 64, 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; #ifdef SSL_VERBOSE printf("ssl_encrypt: bucket read %d bytes; status %d\n", interim_len, status); printf("---\n%s\n-(%d)-\n", vecs_data, interim_len); #endif /* Stash our status away. */ ctx->encrypt.status = status; ssl_len = SSL_write(ctx->ssl, vecs_data, interim_len); #ifdef SSL_VERBOSE printf("ssl_encrypt: SSL write: %d\n", ssl_len); #endif /* We're done. */ serf_bucket_mem_free(ctx->allocator, vecs_data); /* If we failed to write... */ if (ssl_len < 0) { int ssl_err; /* Ah, bugger. We need to put that data back. */ serf_bucket_aggregate_prepend_iovec(ctx->encrypt.stream, vecs, vecs_read); ssl_err = SSL_get_error(ctx->ssl, ssl_len); #ifdef SSL_VERBOSE printf("ssl_encrypt: SSL write error: %d\n", ssl_err); #endif if (ssl_err == SSL_ERROR_SYSCALL) { status = ctx->encrypt.status; if (SERF_BUCKET_READ_ERROR(status)) { return status; } } else { /* Oh, no. */ if (ssl_err == SSL_ERROR_WANT_READ) { status = SERF_ERROR_WAIT_CONN; } else { status = APR_EGENERAL; } } #ifdef SSL_VERBOSE printf("ssl_encrypt: SSL write error: %d %d\n", status, *len); #endif } } } else { interim_len = 0; *len = 0; status = ctx->encrypt.status; } } while (!status && interim_bufsize); /* Okay, we exhausted our underlying stream. */ if (!SERF_BUCKET_READ_ERROR(status)) { apr_status_t agg_status; struct iovec vecs[64]; int vecs_read, i; /* We read something! */ agg_status = serf_bucket_read_iovec(ctx->encrypt.pending, bufsize, 64, 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; } #ifdef SSL_VERBOSE printf("ssl_encrypt read agg: %d %d %d %d\n", status, agg_status, ctx->encrypt.status, *len); #endif if (!agg_status) { status = agg_status; } } if (status == SERF_ERROR_WAIT_CONN && BIO_should_retry(ctx->bio) && BIO_should_read(ctx->bio)) { ctx->encrypt.exhausted = ctx->encrypt.status; ctx->encrypt.status = SERF_ERROR_WAIT_CONN; } #ifdef SSL_VERBOSE printf("ssl_encrypt finished: %d %d (%d %d %d)\n", status, *len, BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), BIO_get_retry_flags(ctx->bio)); #endif return status; }
/* This function reads an encrypted stream and returns the decrypted stream. */ 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_size_t priv_len; apr_status_t status; const char *data; int ssl_len; #ifdef SSL_VERBOSE printf("ssl_decrypt: begin %d\n", bufsize); #endif /* Is there some data waiting to be read? */ ssl_len = SSL_read(ctx->ssl, buf, bufsize); if (ssl_len > 0) { #ifdef SSL_VERBOSE printf("ssl_decrypt: %d bytes (%d); status: %d; flags: %d\n", ssl_len, bufsize, ctx->decrypt.status, BIO_get_retry_flags(ctx->bio)); #endif *len = ssl_len; return APR_SUCCESS; } status = serf_bucket_read(ctx->decrypt.stream, bufsize, &data, &priv_len); if (!SERF_BUCKET_READ_ERROR(status) && priv_len) { serf_bucket_t *tmp; #ifdef SSL_VERBOSE printf("ssl_decrypt: read %d bytes (%d); status: %d\n", priv_len, bufsize, status); #endif tmp = serf_bucket_simple_copy_create(data, priv_len, ctx->decrypt.pending->allocator); serf_bucket_aggregate_append(ctx->decrypt.pending, tmp); ssl_len = SSL_read(ctx->ssl, buf, bufsize); if (ssl_len < 0) { int ssl_err; ssl_err = SSL_get_error(ctx->ssl, ssl_len); switch (ssl_err) { case SSL_ERROR_SYSCALL: *len = 0; status = ctx->decrypt.status; break; case SSL_ERROR_WANT_READ: *len = 0; status = APR_EAGAIN; break; case SSL_ERROR_SSL: *len = 0; status = ctx->pending_err ? ctx->pending_err : APR_EGENERAL; ctx->pending_err = 0; break; default: *len = 0; status = APR_EGENERAL; break; } } else { *len = ssl_len; #ifdef SSL_VERBOSE printf("---\n%s\n-(%d)-\n", buf, *len); #endif } } else { *len = 0; } #ifdef SSL_VERBOSE printf("ssl_decrypt: %d %d %d\n", status, *len, BIO_get_retry_flags(ctx->bio)); #endif return status; }