Example #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;
}
Example #2
0
static void send_queue_push_buffer(GQueue *queue, liBuffer *buf, gsize start, gsize len) {
	send_item *i;
	if (!buf || !len) return;
	g_assert(start+len <= buf->used);

	li_buffer_acquire(buf);
	i = g_slice_new0(send_item);
	i->buf = buf;
	i->pos = start;
	i->len = len;

	g_queue_push_tail(queue, i);
}
Example #3
0
liMemcachedRequest* li_memcached_set(liMemcachedCon *con, GString *key, guint32 flags, li_tstamp ttl, liBuffer *data, liMemcachedCB callback, gpointer cb_data, GError **err) {
	int_request* req;

	if (!li_memcached_is_key_valid(key)) {
		g_set_error(err, LI_MEMCACHED_ERROR, LI_MEMCACHED_BAD_KEY, "Invalid key: '%s'", key->str);
		return NULL;
	}

	if (-1 == con->fd) memcached_connect(con);
	if (-1 == con->fd) {
		if (NULL == con->err) {
			g_set_error(err, LI_MEMCACHED_ERROR, LI_MEMCACHED_DISABLED, "Not connected");
		} else if (err) {
			*err = g_error_copy(con->err);
		}
		return NULL;
	}

	req = g_slice_new0(int_request);
	req->req.callback = callback;
	req->req.cb_data = cb_data;

	req->type = REQ_SET;
	req->key = g_string_new_len(GSTR_LEN(key));
	req->flags = flags;
	req->ttl = ttl;
	if (NULL != data) {
		li_buffer_acquire(data);
		req->data = data;
	}

	if (!push_request(con, req, err)) {
		free_request(con, req);
		return NULL;
	}

	return &req->req;
}
Example #4
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);
}
Example #5
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;
}
Example #6
0
/* steal up to length bytes from in and put them into out, return number of bytes stolen */
goffset li_chunkqueue_steal_len(liChunkQueue *out, liChunkQueue *in, goffset length) {
	liChunk *c, *cnew;
	GList* l;
	goffset bytes = 0, meminbytes = 0, memoutbytes = 0;
	goffset we_have;

	while ( (NULL != (c = li_chunkqueue_first_chunk(in))) && length > 0 ) {
		we_have = li_chunk_length(c);
		if (!we_have) { /* remove empty chunks */
			if (c->type == STRING_CHUNK) meminbytes -= c->data.str->len;
			else if (c->type == MEM_CHUNK) meminbytes -= c->mem->len;
			else if (c->type == BUFFER_CHUNK) meminbytes -= c->data.buffer.length;
			chunk_free(in, c);
			continue;
		}
		if (we_have <= length) { /* move complete chunk */
			l = g_queue_pop_head_link(&in->queue);
			g_queue_push_tail_link(&out->queue, l);
			bytes += we_have;
			if (c->type == STRING_CHUNK) {
				meminbytes -= c->data.str->len;
				memoutbytes += c->data.str->len;
			} else if (c->type == MEM_CHUNK) {
				meminbytes -= c->mem->len;
				memoutbytes += c->mem->len;
			} else if (c->type == BUFFER_CHUNK) {
				meminbytes -= c->data.buffer.length;
				memoutbytes += c->data.buffer.length;
			}
			length -= we_have;
		} else { /* copy first part of a chunk */
			cnew = chunk_new();
			switch (c->type) {
			case UNUSED_CHUNK: /* impossible, has length 0 */
				/* remove "empty" chunks */
				chunk_free(in, c);
				chunk_free(NULL, cnew);
				continue;
			case STRING_CHUNK: /* change type to MEM_CHUNK, as we copy it anyway */
				cnew->type = MEM_CHUNK;
				cnew->mem = g_byte_array_sized_new(length);
				g_byte_array_append(cnew->mem, (guint8*) c->data.str->str + c->offset, length);
				memoutbytes += length;
				break;
			case MEM_CHUNK:
				cnew->type = MEM_CHUNK;
				cnew->mem = g_byte_array_sized_new(length);
				g_byte_array_append(cnew->mem, (guint8*) c->mem->data + c->offset, length);
				memoutbytes += length;
				break;
			case FILE_CHUNK:
				cnew->type = FILE_CHUNK;
				li_chunkfile_acquire(c->data.file.file);
				cnew->data.file.file = c->data.file.file;
				cnew->data.file.start = c->data.file.start + c->offset;
				cnew->data.file.length = length;
				break;
			case BUFFER_CHUNK:
				cnew->type = BUFFER_CHUNK;
				li_buffer_acquire(c->data.buffer.buffer);
				cnew->data.buffer.buffer = c->data.buffer.buffer;
				cnew->data.buffer.offset = c->data.buffer.offset + c->offset;
				cnew->data.buffer.length = length;
				memoutbytes += length;
				break;
			}
			c->offset += length;
			bytes += length;
			length = 0;
			g_queue_push_tail_link(&out->queue, &cnew->cq_link);
		}
	}

	in->bytes_out += bytes;
	in->length -= bytes;
	out->bytes_in += bytes;
	out->length += bytes;
	cqlimit_update(out, memoutbytes);
	cqlimit_update(in, meminbytes);

	return bytes;
}