static ssize_t stream_pushv(gnutls_transport_ptr_t trans, const li_iovec_t * iov, int iovcnt) { const ssize_t blocksize = 16*1024; /* 16k */ liGnuTLSFilter *f = (liGnuTLSFilter*) trans; liChunkQueue *cq; int i; liBuffer *buf; gboolean cq_buf_append; ssize_t written = 0; errno = ECONNRESET; if (NULL == f || NULL == f->crypt_source.out) return -1; cq = f->crypt_source.out; if (cq->is_closed) return -1; buf = f->raw_out_buffer; cq_buf_append = (buf != NULL && buf == li_chunkqueue_get_last_buffer(cq, 1024)); for (i = 0; i < iovcnt; ++i) { const char *data = iov[i].iov_base; size_t len = iov[i].iov_len; while (len > 0) { size_t bufsize, do_write; if (NULL == buf) buf = li_buffer_new(blocksize); bufsize = buf->alloc_size - buf->used; do_write = (bufsize > len) ? len : bufsize; memcpy(buf->addr + buf->used, data, do_write); len -= do_write; data += do_write; if (cq_buf_append) { /* also updates buf->used */ li_chunkqueue_update_last_buffer_size(cq, do_write); } else { gsize offset = buf->used; buf->used += do_write; li_buffer_acquire(buf); li_chunkqueue_append_buffer2(cq, buf, offset, do_write); cq_buf_append = TRUE; } if (buf->used == buf->alloc_size) { li_buffer_release(buf); buf = NULL; cq_buf_append = FALSE; } written += do_write; } } if (NULL != buf && buf->alloc_size - buf->used < 1024) { li_buffer_release(buf); f->raw_out_buffer = buf = NULL; } else { f->raw_out_buffer = buf; } li_stream_notify_later(&f->crypt_source); errno = 0; return written; }
static void do_gnutls_read(liGnuTLSFilter *f) { const ssize_t blocksize = 16*1024; /* 16k */ off_t max_read = 4 * blocksize; /* 64k */ ssize_t r; off_t len = 0; liChunkQueue *cq = f->plain_source.out; f_acquire(f); if (NULL != f->session && !f->initial_handshaked_finished && !do_gnutls_handshake(f, FALSE)) goto out; if (NULL == f->session) { f_abort_gnutls(f); goto out; } do { liBuffer *buf; gboolean cq_buf_append; buf = li_chunkqueue_get_last_buffer(cq, 1024); cq_buf_append = (buf != NULL); if (buf != NULL) { /* use last buffer as raw_in_buffer; they should be the same anyway */ if (G_UNLIKELY(buf != f->raw_in_buffer)) { li_buffer_acquire(buf); li_buffer_release(f->raw_in_buffer); f->raw_in_buffer = buf; } } else { buf = f->raw_in_buffer; if (buf != NULL && buf->alloc_size - buf->used < 1024) { /* release *buffer */ li_buffer_release(buf); f->raw_in_buffer = buf = NULL; } if (buf == NULL) { f->raw_in_buffer = buf = li_buffer_new(blocksize); } } LI_FORCE_ASSERT(f->raw_in_buffer == buf); r = gnutls_record_recv(f->session, buf->addr + buf->used, buf->alloc_size - buf->used); if (r < 0) { do_handle_error(f, "gnutls_record_recv", r, FALSE); goto out; } else if (r == 0) { /* clean shutdown? */ f->plain_source.out->is_closed = TRUE; f->plain_drain.out->is_closed = TRUE; f->crypt_source.out->is_closed = TRUE; f->crypt_drain.out->is_closed = TRUE; li_stream_disconnect(&f->crypt_drain); /* io -> crypt in */ li_stream_disconnect_dest(&f->crypt_source); /* crypt out -> io */ li_stream_disconnect(&f->crypt_source); /* plain in -> crypt out */ f_close_gnutls(f); goto out; } if (cq_buf_append) { li_chunkqueue_update_last_buffer_size(cq, r); } else { gsize offset; li_buffer_acquire(buf); offset = buf->used; buf->used += r; li_chunkqueue_append_buffer2(cq, buf, offset, r); } if (buf->alloc_size - buf->used < 1024) { /* release *buffer */ li_buffer_release(buf); f->raw_in_buffer = buf = NULL; } len += r; } while (len < max_read); out: f_release(f); }
liNetworkStatus li_network_read(liVRequest *vr, int fd, liChunkQueue *cq, liBuffer **buffer) { const ssize_t blocksize = 16*1024; /* 16k */ off_t max_read = 16 * blocksize; /* 256k */ ssize_t r; off_t len = 0; if (cq->limit && cq->limit->limit > 0) { if (max_read > cq->limit->limit - cq->limit->current) { max_read = cq->limit->limit - cq->limit->current; if (max_read <= 0) { max_read = 0; /* we still have to read something */ VR_ERROR(vr, "%s", "li_network_read: fd should be disabled as chunkqueue is already full"); } } } do { liBuffer *buf = NULL; gboolean cq_buf_append; buf = li_chunkqueue_get_last_buffer(cq, 1024); cq_buf_append = (buf != NULL); if (NULL != buffer) { if (buf != NULL) { /* use last buffer as *buffer; they should be the same anyway */ if (G_UNLIKELY(buf != *buffer)) { li_buffer_acquire(buf); li_buffer_release(*buffer); *buffer = buf; } } else { buf = *buffer; if (buf != NULL) { /* if *buffer is the only reference, we can reset the buffer */ if (g_atomic_int_get(&buf->refcount) == 1) { buf->used = 0; } if (buf->alloc_size - buf->used < 1024) { /* release *buffer */ li_buffer_release(buf); *buffer = buf = NULL; } } if (buf == NULL) { *buffer = buf = li_buffer_new(blocksize); } } assert(*buffer == buf); } else { if (buf == NULL) { buf = li_buffer_new(blocksize); } } if (-1 == (r = li_net_read(fd, buf->addr + buf->used, buf->alloc_size - buf->used))) { if (buffer == NULL && !cq_buf_append) li_buffer_release(buf); switch (errno) { case EAGAIN: #if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif return LI_NETWORK_STATUS_WAIT_FOR_EVENT; case ECONNRESET: case ETIMEDOUT: return LI_NETWORK_STATUS_CONNECTION_CLOSE; default: VR_ERROR(vr, "oops, read from fd=%d failed: %s", fd, g_strerror(errno) ); return LI_NETWORK_STATUS_FATAL_ERROR; } } else if (0 == r) { if (buffer == NULL && !cq_buf_append) li_buffer_release(buf); return LI_NETWORK_STATUS_CONNECTION_CLOSE; } if (cq_buf_append) { li_chunkqueue_update_last_buffer_size(cq, r); } else { gsize offset; if (buffer != NULL) li_buffer_acquire(buf); offset = buf->used; buf->used += r; li_chunkqueue_append_buffer2(cq, buf, offset, r); } if (NULL != buffer) { if (buf->alloc_size - buf->used < 1024) { /* release *buffer */ li_buffer_release(buf); *buffer = buf = NULL; } } len += r; } while (r == blocksize && len < max_read); return LI_NETWORK_STATUS_SUCCESS; }