예제 #1
0
파일: buffers.c 프로젝트: gnutls/gnutls
/* This is a send function for the gnutls handshake 
 * protocol. Just makes sure that all data have been sent.
 *
 */
int
_gnutls_handshake_io_cache_int(gnutls_session_t session,
			       gnutls_handshake_description_t htype,
			       mbuffer_st * bufel)
{
	mbuffer_head_st *send_buffer;

	if (IS_DTLS(session)) {
		bufel->handshake_sequence =
		    session->internals.dtls.hsk_write_seq - 1;
	}

	send_buffer = &session->internals.handshake_send_buffer;

	/* ensure that our epoch does not get garbage collected
	 * before we send all queued messages with it */
	bufel->epoch =
	    (uint16_t) _gnutls_epoch_refcount_inc(session,
						  EPOCH_WRITE_CURRENT);
	bufel->htype = htype;
	if (bufel->htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC)
		bufel->type = GNUTLS_CHANGE_CIPHER_SPEC;
	else
		bufel->type = GNUTLS_HANDSHAKE;

	_mbuffer_enqueue(send_buffer, bufel);

	_gnutls_write_log
	    ("HWRITE: enqueued [%s] %d. Total %d bytes.\n",
	     _gnutls_handshake2str(bufel->htype), (int) bufel->msg.size,
	     (int) send_buffer->byte_length);

	return 0;
}
예제 #2
0
int _mbuffer_linearize(mbuffer_head_st * buf)
{
	mbuffer_st *bufel, *cur;
	gnutls_datum_t msg;
	size_t pos = 0;

	if (buf->length <= 1) {
		/* Nothing to do */
		return 0;
	}
	
	bufel = _mbuffer_alloc(buf->byte_length);
	if (!bufel) {
		gnutls_assert();
		return GNUTLS_E_MEMORY_ERROR;
	}

	for (cur = _mbuffer_head_get_first(buf, &msg);
	     msg.data != NULL; cur = _mbuffer_head_get_next(cur, &msg)) {
		memcpy(&bufel->msg.data[pos], msg.data, msg.size);
		bufel->msg.size += msg.size;
		pos += msg.size;
	}

	_mbuffer_head_clear(buf);
	_mbuffer_enqueue(buf, bufel);

	return 0;
}
예제 #3
0
/* Buffers received packets of type APPLICATION DATA and
 * HANDSHAKE DATA.
 */
int
_gnutls_record_buffer_put (gnutls_session_t session,
  content_type_t type, uint64* seq, mbuffer_st* bufel)
{

  bufel->type = type;
  memcpy(&bufel->record_sequence, seq, sizeof(*seq));

  _mbuffer_enqueue(&session->internals.record_buffer, bufel);
  _gnutls_buffers_log ("BUF[REC]: Inserted %d bytes of Data(%d)\n",
                       (int) bufel->msg.size, (int) type);

  return 0;
}
예제 #4
0
/* This function is like write. But it does not return -1 on error.
 * It does return gnutls_errno instead.
 *
 * This function takes full responsibility of freeing msg->data.
 *
 * In case of E_AGAIN and E_INTERRUPTED errors, you must call
 * gnutls_write_flush(), until it returns ok (0).
 *
 * We need to push exactly the data in msg->size, since we cannot send
 * less data. In TLS the peer must receive the whole packet in order
 * to decrypt and verify the integrity.
 *
 */
ssize_t
_gnutls_io_write_buffered (gnutls_session_t session,
                           mbuffer_st * bufel, unsigned int mflag)
{
  mbuffer_head_st *const send_buffer = &session->internals.record_send_buffer;

  _mbuffer_enqueue (send_buffer, bufel);

  _gnutls_write_log
    ("WRITE: enqueued %d bytes for %p. Total %d bytes.\n",
     (int) bufel->msg.size, session->internals.transport_recv_ptr,
     (int) send_buffer->byte_length);

  if (mflag == MBUFFER_FLUSH)
    return _gnutls_io_write_flush (session);
  else
    return bufel->msg.size;
}
예제 #5
0
파일: buffers.c 프로젝트: gnutls/gnutls
/* 
 * @ms: a pointer to the number of milliseconds to wait for data. Use zero or NULL for indefinite.
 *
 * This function is like recv(with MSG_PEEK). But it does not return -1 on error.
 * It does return gnutls_errno instead.
 * This function reads data from the socket and keeps them in a buffer, of up to
 * max_record_recv_size. 
 *
 * This is not a general purpose function. It returns EXACTLY the data requested,
 * which are stored in a local (in the session) buffer.
 *
 * If the @ms parameter is non zero then this function will return before
 * the given amount of milliseconds or return GNUTLS_E_TIMEDOUT.
 *
 */
ssize_t
_gnutls_io_read_buffered(gnutls_session_t session, size_t total,
			 content_type_t recv_type, unsigned int *ms)
{
	ssize_t ret;
	size_t min;
	mbuffer_st *bufel = NULL;
	size_t recvdata, readsize;

	if (total > max_record_recv_size(session) || total == 0) {
		gnutls_assert();
		return GNUTLS_E_RECORD_OVERFLOW;
	}

	/* calculate the actual size, ie. get the minimum of the
	 * buffered data and the requested data.
	 */
	min =
	    MIN(session->internals.record_recv_buffer.byte_length, total);
	if (min > 0) {
		/* if we have enough buffered data
		 * then just return them.
		 */
		if (min == total) {
			return min;
		}
	}

	/* min is over zero. recvdata is the data we must
	 * receive in order to return the requested data.
	 */
	recvdata = total - min;
	readsize = recvdata;

	/* Check if the previously read data plus the new data to
	 * receive are longer than the maximum receive buffer size.
	 */
	if ((session->internals.record_recv_buffer.byte_length +
	     recvdata) > max_record_recv_size(session)) {
		gnutls_assert();	/* internal error */
		return GNUTLS_E_INVALID_REQUEST;
	}

	/* READ DATA
	 */
	if (readsize > 0) {
		ret =
		    _gnutls_read(session, &bufel, readsize,
				 session->internals.pull_func, ms);

		/* return immediately if we got an interrupt or eagain
		 * error.
		 */
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}

		if (ret == 0)	/* EOF */
			return gnutls_assert_val(0);

		/* copy fresh data to our buffer.
		 */
		_gnutls_read_log
		    ("RB: Have %d bytes into buffer. Adding %d bytes.\n",
		     (int) session->internals.record_recv_buffer.
		     byte_length, (int) ret);
		_gnutls_read_log("RB: Requested %d bytes\n", (int) total);

		_mbuffer_enqueue(&session->internals.record_recv_buffer,
				 bufel);

		if (IS_DTLS(session))
			ret =
			    MIN(total,
				session->internals.record_recv_buffer.
				byte_length);
		else
			ret =
			    session->internals.record_recv_buffer.
			    byte_length;

		if ((ret > 0) && ((size_t) ret < total))	/* Short Read */
			return gnutls_assert_val(GNUTLS_E_AGAIN);
		else
			return ret;
	} else
		return gnutls_assert_val(0);
}
예제 #6
0
파일: buffers.c 프로젝트: gnutls/gnutls
/* This is a receive function for the gnutls handshake 
 * protocol. Makes sure that we have received all data.
 *
 * htype is the next handshake packet expected.
 */
int _gnutls_parse_record_buffered_msgs(gnutls_session_t session)
{
	gnutls_datum_t msg;
	mbuffer_st *bufel = NULL, *prev = NULL;
	int ret;
	size_t data_size;
	handshake_buffer_st *recv_buf =
	    session->internals.handshake_recv_buffer;

	bufel =
	    _mbuffer_head_get_first(&session->internals.record_buffer,
				    &msg);
	if (bufel == NULL)
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;

	if (!IS_DTLS(session)) {
		ssize_t append, header_size;

		do {
			if (bufel->type != GNUTLS_HANDSHAKE)
				return
				    gnutls_assert_val
				    (GNUTLS_E_UNEXPECTED_PACKET);

			if (unlikely
			    (session->internals.handshake_recv_buffer_size == 0 &&
			     msg.size < HANDSHAKE_HEADER_SIZE(session) &&
			     session->internals.handshake_header_recv_buffer.byte_length <
			     HANDSHAKE_HEADER_SIZE(session) - msg.size)) {
				bufel = _mbuffer_head_pop_first(&session->internals.record_buffer);
				_mbuffer_enqueue(&session->internals.handshake_header_recv_buffer,
						 bufel);
				break;
			} else if (session->internals.handshake_recv_buffer_size >
				   0 && recv_buf[0].length > recv_buf[0].data.length) {
				/* this is the rest of a previous message */
				append = MIN(msg.size,
					     recv_buf[0].length -
					     recv_buf[0].data.length);

				ret =
				    _gnutls_buffer_append_data(&recv_buf
							       [0].data,
							       msg.data,
							       append);
				if (ret < 0)
					return gnutls_assert_val(ret);

				_mbuffer_head_remove_bytes(&session->
							   internals.
							   record_buffer,
							   append);
			} else {	/* received new message */
				if (unlikely
				    (session->internals.
				     handshake_header_recv_buffer.length > 0)) {
					bufel = _mbuffer_head_pop_first(&session->internals.
									record_buffer);
					_mbuffer_enqueue(&session->internals.
							 handshake_header_recv_buffer,
							 bufel);
					ret = _mbuffer_linearize_align16(&session->internals.
									 handshake_header_recv_buffer,
									 get_total_headers(session));
					if (ret < 0)
						return gnutls_assert_val(ret);
					bufel = _mbuffer_head_pop_first(&session->internals.
									handshake_header_recv_buffer);
					_mbuffer_head_push_first(&session->internals.
								 record_buffer,
								 bufel);
				}

				ret =
				    parse_handshake_header(session, bufel,
							   &recv_buf[0]);
				if (ret < 0)
					return gnutls_assert_val(ret);

				header_size = ret;
				session->internals.
				    handshake_recv_buffer_size = 1;

				_mbuffer_set_uhead_size(bufel,
							header_size);

				data_size =
				    MIN(recv_buf[0].length,
					_mbuffer_get_udata_size(bufel));
				ret =
				    _gnutls_buffer_append_data(&recv_buf
							       [0].data,
							       _mbuffer_get_udata_ptr
							       (bufel),
							       data_size);
				if (ret < 0)
					return gnutls_assert_val(ret);
				_mbuffer_set_uhead_size(bufel, 0);
				_mbuffer_head_remove_bytes(&session->
							   internals.
							   record_buffer,
							   data_size +
							   header_size);
			}

			/* if packet is complete then return it
			 */
			if (recv_buf[0].length == recv_buf[0].data.length) {
				return 0;
			}
			bufel =
			    _mbuffer_head_get_first(&session->internals.
						    record_buffer, &msg);
		}
		while (bufel != NULL);

		/* if we are here it means that the received packets were not
		 * enough to complete the handshake packet.
		 */
		return gnutls_assert_val(GNUTLS_E_AGAIN);
	} else {		/* DTLS */

		handshake_buffer_st tmp;

		do {
			/* we now 
			 * 0. parse headers
			 * 1. insert to handshake_recv_buffer
			 * 2. sort handshake_recv_buffer on sequence numbers
			 * 3. return first packet if completed or GNUTLS_E_AGAIN.
			 */
			do {
				if (bufel->type != GNUTLS_HANDSHAKE) {
					gnutls_assert();
					goto next;	/* ignore packet */
				}

				_gnutls_handshake_buffer_init(&tmp);

				ret =
				    parse_handshake_header(session, bufel,
							   &tmp);
				if (ret < 0) {
					gnutls_assert();
					_gnutls_audit_log(session,
							  "Invalid handshake packet headers. Discarding.\n");
					break;
				}

				_mbuffer_consume(&session->internals.
						 record_buffer, bufel,
						 ret);

				data_size =
				    MIN(tmp.length,
					tmp.end_offset - tmp.start_offset +
					1);

				ret =
				    _gnutls_buffer_append_data(&tmp.data,
							       _mbuffer_get_udata_ptr
							       (bufel),
							       data_size);
				if (ret < 0)
					return gnutls_assert_val(ret);

				_mbuffer_consume(&session->internals.
						 record_buffer, bufel,
						 data_size);

				ret =
				    merge_handshake_packet(session, &tmp);
				if (ret < 0)
					return gnutls_assert_val(ret);

			}
			while (_mbuffer_get_udata_size(bufel) > 0);

			prev = bufel;
			bufel =
			    _mbuffer_dequeue(&session->internals.
					     record_buffer, bufel);

			_mbuffer_xfree(&prev);
			continue;

		      next:
			bufel = _mbuffer_head_get_next(bufel, NULL);
		}
		while (bufel != NULL);

		/* sort in descending order */
		if (session->internals.handshake_recv_buffer_size > 1)
			qsort(recv_buf,
			      session->internals.
			      handshake_recv_buffer_size,
			      sizeof(recv_buf[0]), handshake_compare);

		while (session->internals.handshake_recv_buffer_size > 0 &&
		       recv_buf[LAST_ELEMENT].sequence <
		       session->internals.dtls.hsk_read_seq) {
			_gnutls_audit_log(session,
					  "Discarded replayed handshake packet with sequence %d\n",
					  recv_buf[LAST_ELEMENT].sequence);
			_gnutls_handshake_buffer_clear(&recv_buf
						       [LAST_ELEMENT]);
			session->internals.handshake_recv_buffer_size--;
		}

		return 0;
	}
}