예제 #1
0
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;
}
예제 #2
0
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);
}
예제 #3
0
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;
}