Blame lib/dtls.c

Packit Service 4684c1
/*
Packit Service 4684c1
 * Copyright (C) 2009-2012 Free Software Foundation, Inc.
Packit Service 4684c1
 * Copyright (C) 2013 Nikos Mavrogiannopoulos
Packit Service 4684c1
 *
Packit Service 4684c1
 * Authors: Jonathan Bastien-Filiatrault
Packit Service 4684c1
 *	  Nikos Mavrogiannopoulos
Packit Service 4684c1
 *
Packit Service 4684c1
 * This file is part of GNUTLS.
Packit Service 4684c1
 *
Packit Service 4684c1
 * The GNUTLS library is free software; you can redistribute it and/or
Packit Service 4684c1
 * modify it under the terms of the GNU Lesser General Public License
Packit Service 4684c1
 * as published by the Free Software Foundation; either version 2.1 of
Packit Service 4684c1
 * the License, or (at your option) any later version.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This library is distributed in the hope that it will be useful, but
Packit Service 4684c1
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 4684c1
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 4684c1
 * Lesser General Public License for more details.
Packit Service 4684c1
 *
Packit Service 4684c1
 * You should have received a copy of the GNU Lesser General Public License
Packit Service 4684c1
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
Packit Service 4684c1
 *
Packit Service 4684c1
 */
Packit Service 4684c1
Packit Service 4684c1
/* Functions that relate to DTLS retransmission and reassembly.
Packit Service 4684c1
 */
Packit Service 4684c1
Packit Service 4684c1
#include "gnutls_int.h"
Packit Service 4684c1
#include "errors.h"
Packit Service 4684c1
#include "debug.h"
Packit Service 4684c1
#include "dtls.h"
Packit Service 4684c1
#include "record.h"
Packit Service 4684c1
#include <mbuffers.h>
Packit Service 4684c1
#include <buffers.h>
Packit Service 4684c1
#include <constate.h>
Packit Service 4684c1
#include <state.h>
Packit Service 4684c1
#include <gnutls/dtls.h>
Packit Service 4684c1
#include <algorithms.h>
Packit Service 4684c1
Packit Service 4684c1
void _dtls_async_timer_delete(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	if (session->internals.dtls.async_term != 0) {
Packit Service 4684c1
		_gnutls_dtls_log
Packit Service 4684c1
		    ("DTLS[%p]: Deinitializing previous handshake state.\n",
Packit Service 4684c1
		     session);
Packit Service 4684c1
		session->internals.dtls.async_term = 0;	/* turn off "timer" */
Packit Service 4684c1
Packit Service 4684c1
		_dtls_reset_hsk_state(session);
Packit Service 4684c1
		_gnutls_handshake_io_buffer_clear(session);
Packit Service 4684c1
		_gnutls_epoch_gc(session);
Packit Service 4684c1
	}
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* This function fragments and transmits a previously buffered
Packit Service 4684c1
 * outgoing message. It accepts mtu_data which is a buffer to
Packit Service 4684c1
 * be reused (should be set to NULL initially).
Packit Service 4684c1
 */
Packit Service 4684c1
static inline int
Packit Service 4684c1
transmit_message(gnutls_session_t session,
Packit Service 4684c1
		 mbuffer_st * bufel, uint8_t ** buf)
Packit Service 4684c1
{
Packit Service 4684c1
	uint8_t *data, *mtu_data;
Packit Service 4684c1
	int ret = 0;
Packit Service 4684c1
	unsigned int offset, frag_len, data_size;
Packit Service 4684c1
	unsigned int mtu =
Packit Service 4684c1
	    gnutls_dtls_get_data_mtu(session);
Packit Service 4684c1
Packit Service 4684c1
	if (session->security_parameters.max_record_send_size < mtu)
Packit Service 4684c1
		mtu = session->security_parameters.max_record_send_size;
Packit Service 4684c1
Packit Service 4684c1
	mtu -= DTLS_HANDSHAKE_HEADER_SIZE;
Packit Service 4684c1
Packit Service 4684c1
	if (bufel->type == GNUTLS_CHANGE_CIPHER_SPEC) {
Packit Service 4684c1
		_gnutls_dtls_log
Packit Service 4684c1
		    ("DTLS[%p]: Sending Packet[%u] fragment %s(%d), mtu %u\n",
Packit Service 4684c1
		     session, bufel->handshake_sequence,
Packit Service 4684c1
		     _gnutls_handshake2str(bufel->htype), bufel->htype, mtu);
Packit Service 4684c1
Packit Service 4684c1
		return _gnutls_send_int(session, bufel->type, -1,
Packit Service 4684c1
					bufel->epoch,
Packit Service 4684c1
					_mbuffer_get_uhead_ptr(bufel),
Packit Service 4684c1
					_mbuffer_get_uhead_size(bufel), 0);
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	if (*buf == NULL)
Packit Service 4684c1
		*buf = gnutls_malloc(mtu + DTLS_HANDSHAKE_HEADER_SIZE);
Packit Service 4684c1
	if (*buf == NULL)
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
Packit Service 4684c1
Packit Service 4684c1
	mtu_data = *buf;
Packit Service 4684c1
Packit Service 4684c1
	data = _mbuffer_get_udata_ptr(bufel);
Packit Service 4684c1
	data_size = _mbuffer_get_udata_size(bufel);
Packit Service 4684c1
Packit Service 4684c1
	/* Write fixed headers
Packit Service 4684c1
	 */
Packit Service 4684c1
Packit Service 4684c1
	/* Handshake type */
Packit Service 4684c1
	mtu_data[0] = (uint8_t) bufel->htype;
Packit Service 4684c1
Packit Service 4684c1
	/* Total length */
Packit Service 4684c1
	_gnutls_write_uint24(data_size, &mtu_data[1]);
Packit Service 4684c1
Packit Service 4684c1
	/* Handshake sequence */
Packit Service 4684c1
	_gnutls_write_uint16(bufel->handshake_sequence, &mtu_data[4]);
Packit Service 4684c1
Packit Service 4684c1
	/* Chop up and send handshake message into mtu-size pieces. */
Packit Service 4684c1
	for (offset = 0; offset <= data_size; offset += mtu) {
Packit Service 4684c1
		/* Calculate fragment length */
Packit Service 4684c1
		if (offset + mtu > data_size)
Packit Service 4684c1
			frag_len = data_size - offset;
Packit Service 4684c1
		else
Packit Service 4684c1
			frag_len = mtu;
Packit Service 4684c1
Packit Service 4684c1
		/* we normally allow fragments of zero length, to allow
Packit Service 4684c1
		 * the packets which have zero size. On the others don't
Packit Service 4684c1
		 * send such fragments */
Packit Service 4684c1
		if (frag_len == 0 && data_size > 0) {
Packit Service 4684c1
			ret = 0;
Packit Service 4684c1
			break;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		/* Fragment offset */
Packit Service 4684c1
		_gnutls_write_uint24(offset, &mtu_data[6]);
Packit Service 4684c1
Packit Service 4684c1
		/* Fragment length */
Packit Service 4684c1
		_gnutls_write_uint24(frag_len, &mtu_data[9]);
Packit Service 4684c1
Packit Service 4684c1
		memcpy(&mtu_data[DTLS_HANDSHAKE_HEADER_SIZE],
Packit Service 4684c1
		       data + offset, frag_len);
Packit Service 4684c1
Packit Service 4684c1
		_gnutls_dtls_log
Packit Service 4684c1
		    ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with "
Packit Service 4684c1
		     "length: %u, offset: %u, fragment length: %u, mtu: %u\n",
Packit Service 4684c1
		     session, bufel->handshake_sequence,
Packit Service 4684c1
		     _gnutls_handshake2str(bufel->htype), bufel->htype,
Packit Service 4684c1
		     data_size, offset, frag_len, mtu);
Packit Service 4684c1
Packit Service 4684c1
		ret = _gnutls_send_int(session, bufel->type, bufel->htype,
Packit Service 4684c1
				       bufel->epoch, mtu_data,
Packit Service 4684c1
				       DTLS_HANDSHAKE_HEADER_SIZE +
Packit Service 4684c1
				       frag_len, 0);
Packit Service 4684c1
		if (ret < 0) {
Packit Service 4684c1
			gnutls_assert();
Packit Service 4684c1
			break;
Packit Service 4684c1
		}
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	return ret;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static int drop_usage_count(gnutls_session_t session,
Packit Service 4684c1
			    mbuffer_head_st * const send_buffer)
Packit Service 4684c1
{
Packit Service 4684c1
	int ret;
Packit Service 4684c1
	mbuffer_st *cur;
Packit Service 4684c1
Packit Service 4684c1
	for (cur = send_buffer->head; cur != NULL; cur = cur->next) {
Packit Service 4684c1
		ret = _gnutls_epoch_refcount_dec(session, cur->epoch);
Packit Service 4684c1
		if (ret < 0)
Packit Service 4684c1
			return gnutls_assert_val(ret);
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
/* Checks whether the received packet contains a handshake
Packit Service 4684c1
 * packet with sequence higher that the previously received.
Packit Service 4684c1
 * It must be called only when an actual packet has been
Packit Service 4684c1
 * received.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: 0 if expected, negative value otherwise.
Packit Service 4684c1
 */
Packit Service 4684c1
static int is_next_hpacket_expected(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	int ret;
Packit Service 4684c1
Packit Service 4684c1
	/* htype is arbitrary */
Packit Service 4684c1
	ret =
Packit Service 4684c1
	    _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE,
Packit Service 4684c1
				    GNUTLS_HANDSHAKE_FINISHED, 0);
Packit Service 4684c1
	if (ret < 0)
Packit Service 4684c1
		return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
	ret = _gnutls_parse_record_buffered_msgs(session);
Packit Service 4684c1
	if (ret < 0)
Packit Service 4684c1
		return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
	if (session->internals.handshake_recv_buffer_size > 0)
Packit Service 4684c1
		return 0;
Packit Service 4684c1
	else
Packit Service 4684c1
		return
Packit Service 4684c1
		    gnutls_assert_val
Packit Service 4684c1
		    (GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
void _dtls_reset_hsk_state(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	session->internals.dtls.flight_init = 0;
Packit Service 4684c1
	drop_usage_count(session,
Packit Service 4684c1
			 &session->internals.handshake_send_buffer);
Packit Service 4684c1
	_mbuffer_head_clear(&session->internals.handshake_send_buffer);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
#define UPDATE_TIMER { \
Packit Service 4684c1
      session->internals.dtls.actual_retrans_timeout_ms *= 2; \
Packit Service 4684c1
      session->internals.dtls.actual_retrans_timeout_ms %= MAX_DTLS_TIMEOUT; \
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
#define RESET_TIMER \
Packit Service 4684c1
      session->internals.dtls.actual_retrans_timeout_ms = session->internals.dtls.retrans_timeout_ms
Packit Service 4684c1
Packit Service 4684c1
#define TIMER_WINDOW session->internals.dtls.actual_retrans_timeout_ms
Packit Service 4684c1
Packit Service 4684c1
/* This function transmits the flight that has been previously
Packit Service 4684c1
 * buffered.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function is called from the handshake layer and calls the
Packit Service 4684c1
 * record layer.
Packit Service 4684c1
 */
Packit Service 4684c1
int _dtls_transmit(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	int ret;
Packit Service 4684c1
	uint8_t *buf = NULL;
Packit Service 4684c1
	unsigned int timeout;
Packit Service 4684c1
Packit Service 4684c1
	/* PREPARING -> SENDING state transition */
Packit Service 4684c1
	mbuffer_head_st *const send_buffer =
Packit Service 4684c1
	    &session->internals.handshake_send_buffer;
Packit Service 4684c1
	mbuffer_st *cur;
Packit Service 4684c1
	gnutls_handshake_description_t last_type = 0;
Packit Service 4684c1
	unsigned int diff;
Packit Service 4684c1
	struct timespec now;
Packit Service 4684c1
Packit Service 4684c1
	gnutls_gettime(&now;;
Packit Service 4684c1
Packit Service 4684c1
	/* If we have already sent a flight and we are operating in a 
Packit Service 4684c1
	 * non blocking way, check if it is time to retransmit or just
Packit Service 4684c1
	 * return.
Packit Service 4684c1
	 */
Packit Service 4684c1
	if (session->internals.dtls.flight_init != 0
Packit Service 4684c1
	    && (session->internals.flags & GNUTLS_NONBLOCK)) {
Packit Service 4684c1
		/* just in case previous run was interrupted */
Packit Service 4684c1
		ret = _gnutls_io_write_flush(session);
Packit Service 4684c1
		if (ret < 0) {
Packit Service 4684c1
			gnutls_assert();
Packit Service 4684c1
			goto cleanup;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		if (session->internals.dtls.last_flight == 0
Packit Service 4684c1
		    || !_dtls_is_async(session)) {
Packit Service 4684c1
			/* check for ACK */
Packit Service 4684c1
			ret = _gnutls_io_check_recv(session, 0);
Packit Service 4684c1
			if (ret == GNUTLS_E_TIMEDOUT) {
Packit Service 4684c1
				/* if no retransmission is required yet just return 
Packit Service 4684c1
				 */
Packit Service 4684c1
				if (timespec_sub_ms
Packit Service 4684c1
				    (&now,
Packit Service 4684c1
				     &session->internals.dtls.
Packit Service 4684c1
				     last_retransmit) < TIMER_WINDOW) {
Packit Service 4684c1
					gnutls_assert();
Packit Service 4684c1
					goto nb_timeout;
Packit Service 4684c1
				}
Packit Service 4684c1
			} else {	/* received something */
Packit Service 4684c1
Packit Service 4684c1
				if (ret == 0) {
Packit Service 4684c1
					ret =
Packit Service 4684c1
					    is_next_hpacket_expected
Packit Service 4684c1
					    (session);
Packit Service 4684c1
					if (ret == GNUTLS_E_AGAIN
Packit Service 4684c1
					    || ret == GNUTLS_E_INTERRUPTED)
Packit Service 4684c1
						goto nb_timeout;
Packit Service 4684c1
					if (ret < 0
Packit Service 4684c1
					    && ret !=
Packit Service 4684c1
					    GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
Packit Service 4684c1
					{
Packit Service 4684c1
						gnutls_assert();
Packit Service 4684c1
						goto cleanup;
Packit Service 4684c1
					}
Packit Service 4684c1
					if (ret == 0)
Packit Service 4684c1
						goto end_flight;
Packit Service 4684c1
					/* if ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET retransmit */
Packit Service 4684c1
				} else
Packit Service 4684c1
					goto nb_timeout;
Packit Service 4684c1
			}
Packit Service 4684c1
		}
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	do {
Packit Service 4684c1
		timeout = TIMER_WINDOW;
Packit Service 4684c1
Packit Service 4684c1
		diff =
Packit Service 4684c1
		    timespec_sub_ms(&now,
Packit Service 4684c1
				    &session->internals.handshake_start_time);
Packit Service 4684c1
		if (diff >= session->internals.handshake_timeout_ms) {
Packit Service 4684c1
			_gnutls_dtls_log("Session timeout: %u ms\n", diff);
Packit Service 4684c1
			ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
Packit Service 4684c1
			goto end_flight;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		diff =
Packit Service 4684c1
		    timespec_sub_ms(&now,
Packit Service 4684c1
				    &session->internals.dtls.
Packit Service 4684c1
				    last_retransmit);
Packit Service 4684c1
		if (session->internals.dtls.flight_init == 0
Packit Service 4684c1
		    || diff >= TIMER_WINDOW) {
Packit Service 4684c1
			_gnutls_dtls_log
Packit Service 4684c1
			    ("DTLS[%p]: %sStart of flight transmission.\n",
Packit Service 4684c1
			     session,
Packit Service 4684c1
			     (session->internals.dtls.flight_init ==
Packit Service 4684c1
			      0) ? "" : "re-");
Packit Service 4684c1
			for (cur = send_buffer->head; cur != NULL;
Packit Service 4684c1
			     cur = cur->next) {
Packit Service 4684c1
				ret = transmit_message(session, cur, &buf;;
Packit Service 4684c1
				if (ret < 0) {
Packit Service 4684c1
					gnutls_assert();
Packit Service 4684c1
					goto end_flight;
Packit Service 4684c1
				}
Packit Service 4684c1
Packit Service 4684c1
				last_type = cur->htype;
Packit Service 4684c1
			}
Packit Service 4684c1
			gnutls_gettime(&session->internals.dtls.last_retransmit);
Packit Service 4684c1
Packit Service 4684c1
			if (session->internals.dtls.flight_init == 0) {
Packit Service 4684c1
				session->internals.dtls.flight_init = 1;
Packit Service 4684c1
				RESET_TIMER;
Packit Service 4684c1
				timeout = TIMER_WINDOW;
Packit Service 4684c1
Packit Service 4684c1
				if (last_type == GNUTLS_HANDSHAKE_FINISHED) {
Packit Service 4684c1
					/* On the last flight we cannot ensure retransmission
Packit Service 4684c1
					 * from here. _dtls_wait_and_retransmit() is being called
Packit Service 4684c1
					 * by handshake.
Packit Service 4684c1
					 */
Packit Service 4684c1
					session->internals.dtls.
Packit Service 4684c1
					    last_flight = 1;
Packit Service 4684c1
				} else
Packit Service 4684c1
					session->internals.dtls.
Packit Service 4684c1
					    last_flight = 0;
Packit Service 4684c1
			} else {
Packit Service 4684c1
				UPDATE_TIMER;
Packit Service 4684c1
			}
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		ret = _gnutls_io_write_flush(session);
Packit Service 4684c1
		if (ret < 0) {
Packit Service 4684c1
			ret = gnutls_assert_val(ret);
Packit Service 4684c1
			goto cleanup;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		/* last message in handshake -> no ack */
Packit Service 4684c1
		if (session->internals.dtls.last_flight != 0) {
Packit Service 4684c1
			/* we don't wait here. We just return 0 and
Packit Service 4684c1
			 * if a retransmission occurs because peer didn't receive it
Packit Service 4684c1
			 * we rely on the record or handshake
Packit Service 4684c1
			 * layer calling this function again.
Packit Service 4684c1
			 */
Packit Service 4684c1
			ret = 0;
Packit Service 4684c1
			goto cleanup;
Packit Service 4684c1
		} else {	/* all other messages -> implicit ack (receive of next flight) */
Packit Service 4684c1
Packit Service 4684c1
			if (!(session->internals.flags & GNUTLS_NONBLOCK))
Packit Service 4684c1
				ret =
Packit Service 4684c1
				    _gnutls_io_check_recv(session,
Packit Service 4684c1
							  timeout);
Packit Service 4684c1
			else {
Packit Service 4684c1
				ret = _gnutls_io_check_recv(session, 0);
Packit Service 4684c1
				if (ret == GNUTLS_E_TIMEDOUT) {
Packit Service 4684c1
					goto nb_timeout;
Packit Service 4684c1
				}
Packit Service 4684c1
			}
Packit Service 4684c1
Packit Service 4684c1
			if (ret == 0) {
Packit Service 4684c1
				ret = is_next_hpacket_expected(session);
Packit Service 4684c1
				if (ret == GNUTLS_E_AGAIN
Packit Service 4684c1
				    || ret == GNUTLS_E_INTERRUPTED)
Packit Service 4684c1
					goto nb_timeout;
Packit Service 4684c1
Packit Service 4684c1
				if (ret ==
Packit Service 4684c1
				    GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET) {
Packit Service 4684c1
					ret = GNUTLS_E_TIMEDOUT;
Packit Service 4684c1
					goto keep_up;
Packit Service 4684c1
				}
Packit Service 4684c1
				if (ret < 0) {
Packit Service 4684c1
					gnutls_assert();
Packit Service 4684c1
					goto cleanup;
Packit Service 4684c1
				}
Packit Service 4684c1
				goto end_flight;
Packit Service 4684c1
			}
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
	      keep_up:
Packit Service 4684c1
		gnutls_gettime(&now;;
Packit Service 4684c1
	} while (ret == GNUTLS_E_TIMEDOUT);
Packit Service 4684c1
Packit Service 4684c1
	if (ret < 0) {
Packit Service 4684c1
		ret = gnutls_assert_val(ret);
Packit Service 4684c1
		goto end_flight;
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	ret = 0;
Packit Service 4684c1
Packit Service 4684c1
      end_flight:
Packit Service 4684c1
	_gnutls_dtls_log("DTLS[%p]: End of flight transmission.\n",
Packit Service 4684c1
			 session);
Packit Service 4684c1
	_dtls_reset_hsk_state(session);
Packit Service 4684c1
Packit Service 4684c1
      cleanup:
Packit Service 4684c1
	if (buf != NULL)
Packit Service 4684c1
		gnutls_free(buf);
Packit Service 4684c1
Packit Service 4684c1
	/* SENDING -> WAITING state transition */
Packit Service 4684c1
	return ret;
Packit Service 4684c1
Packit Service 4684c1
      nb_timeout:
Packit Service 4684c1
	if (buf != NULL)
Packit Service 4684c1
		gnutls_free(buf);
Packit Service 4684c1
Packit Service 4684c1
	RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Waits for the last flight or retransmits
Packit Service 4684c1
 * the previous on timeout. Returns 0 on success.
Packit Service 4684c1
 */
Packit Service 4684c1
int _dtls_wait_and_retransmit(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	int ret;
Packit Service 4684c1
Packit Service 4684c1
	if (!(session->internals.flags & GNUTLS_NONBLOCK))
Packit Service 4684c1
		ret = _gnutls_io_check_recv(session, TIMER_WINDOW);
Packit Service 4684c1
	else
Packit Service 4684c1
		ret = _gnutls_io_check_recv(session, 0);
Packit Service 4684c1
Packit Service 4684c1
	if (ret == GNUTLS_E_TIMEDOUT) {
Packit Service 4684c1
		ret = _dtls_retransmit(session);
Packit Service 4684c1
		if (ret == 0) {
Packit Service 4684c1
			RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, 0);
Packit Service 4684c1
		} else
Packit Service 4684c1
			return gnutls_assert_val(ret);
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	RESET_TIMER;
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_dtls_set_timeouts:
Packit Service 4684c1
 * @session: is a #gnutls_session_t type.
Packit Service 4684c1
 * @retrans_timeout: The time at which a retransmission will occur in milliseconds
Packit Service 4684c1
 * @total_timeout: The time at which the connection will be aborted, in milliseconds.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function will set the timeouts required for the DTLS handshake
Packit Service 4684c1
 * protocol. The retransmission timeout is the time after which a
Packit Service 4684c1
 * message from the peer is not received, the previous messages will
Packit Service 4684c1
 * be retransmitted. The total timeout is the time after which the
Packit Service 4684c1
 * handshake will be aborted with %GNUTLS_E_TIMEDOUT.
Packit Service 4684c1
 *
Packit Service 4684c1
 * The DTLS protocol recommends the values of 1 sec and 60 seconds
Packit Service 4684c1
 * respectively, and these are the default values.
Packit Service 4684c1
 *
Packit Service 4684c1
 * To disable retransmissions set a @retrans_timeout larger than the @total_timeout.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.0
Packit Service 4684c1
 **/
Packit Service 4684c1
void gnutls_dtls_set_timeouts(gnutls_session_t session,
Packit Service 4684c1
			      unsigned int retrans_timeout,
Packit Service 4684c1
			      unsigned int total_timeout)
Packit Service 4684c1
{
Packit Service 4684c1
	if (total_timeout == GNUTLS_INDEFINITE_TIMEOUT)
Packit Service 4684c1
		session->internals.handshake_timeout_ms = 0;
Packit Service 4684c1
	else
Packit Service 4684c1
		session->internals.handshake_timeout_ms = total_timeout;
Packit Service 4684c1
Packit Service 4684c1
	session->internals.dtls.retrans_timeout_ms = retrans_timeout;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_dtls_set_mtu:
Packit Service 4684c1
 * @session: is a #gnutls_session_t type.
Packit Service 4684c1
 * @mtu: The maximum transfer unit of the transport
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function will set the maximum transfer unit of the transport
Packit Service 4684c1
 * that DTLS packets are sent over. Note that this should exclude
Packit Service 4684c1
 * the IP (or IPv6) and UDP headers. So for DTLS over IPv6 on an
Packit Service 4684c1
 * Ethernet device with MTU 1500, the DTLS MTU set with this function
Packit Service 4684c1
 * would be 1500 - 40 (IPV6 header) - 8 (UDP header) = 1452.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.0
Packit Service 4684c1
 **/
Packit Service 4684c1
void gnutls_dtls_set_mtu(gnutls_session_t session, unsigned int mtu)
Packit Service 4684c1
{
Packit Service 4684c1
	session->internals.dtls.mtu = MIN(mtu, DEFAULT_MAX_RECORD_SIZE);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* when max is non-zero this function will return the maximum
Packit Service 4684c1
 * overhead that this ciphersuite may introduce, e.g., the maximum
Packit Service 4684c1
 * amount of padding required */
Packit Service 4684c1
unsigned _gnutls_record_overhead(const version_entry_st *ver,
Packit Service 4684c1
				 const cipher_entry_st *cipher,
Packit Service 4684c1
				 const mac_entry_st *mac,
Packit Service 4684c1
				 unsigned max)
Packit Service 4684c1
{
Packit Service 4684c1
	int total = 0;
Packit Service 4684c1
	int ret;
Packit Service 4684c1
	int hash_len = 0;
Packit Service 4684c1
Packit Service 4684c1
	if (unlikely(cipher == NULL))
Packit Service 4684c1
		return 0;
Packit Service 4684c1
Packit Service 4684c1
	/* 1 octet content type in the unencrypted content */
Packit Service 4684c1
	if (ver->tls13_sem)
Packit Service 4684c1
		total++;
Packit Service 4684c1
Packit Service 4684c1
	if (mac->id == GNUTLS_MAC_AEAD) {
Packit Service 4684c1
		if (!ver->tls13_sem)
Packit Service 4684c1
			total += _gnutls_cipher_get_explicit_iv_size(cipher);
Packit Service 4684c1
Packit Service 4684c1
		total += _gnutls_cipher_get_tag_size(cipher);
Packit Service 4684c1
	} else {
Packit Service 4684c1
		/* STREAM + BLOCK have a MAC appended */
Packit Service 4684c1
		ret = _gnutls_mac_get_algo_len(mac);
Packit Service 4684c1
		if (unlikely(ret < 0))
Packit Service 4684c1
			return 0;
Packit Service 4684c1
Packit Service 4684c1
		hash_len = ret;
Packit Service 4684c1
		total += hash_len;
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	/* Block ciphers have padding + IV */
Packit Service 4684c1
	if (_gnutls_cipher_type(cipher) == CIPHER_BLOCK) {
Packit Service 4684c1
		int exp_iv;
Packit Service 4684c1
Packit Service 4684c1
		exp_iv = _gnutls_cipher_get_explicit_iv_size(cipher);
Packit Service 4684c1
Packit Service 4684c1
		if (max)
Packit Service 4684c1
			total += 2*exp_iv; /* block == iv size */
Packit Service 4684c1
		else
Packit Service 4684c1
			total += exp_iv + 1;
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	return total;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_est_record_overhead_size:
Packit Service 4684c1
 * @version: is a #gnutls_protocol_t value
Packit Service 4684c1
 * @cipher: is a #gnutls_cipher_algorithm_t value
Packit Service 4684c1
 * @mac: is a #gnutls_mac_algorithm_t value
Packit Service 4684c1
 * @comp: is a #gnutls_compression_method_t value (ignored)
Packit Service 4684c1
 * @flags: must be zero
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function will return the set size in bytes of the overhead
Packit Service 4684c1
 * due to TLS (or DTLS) per record.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Note that this function may provide inacurate values when TLS
Packit Service 4684c1
 * extensions that modify the record format are negotiated. In these
Packit Service 4684c1
 * cases a more accurate value can be obtained using gnutls_record_overhead_size() 
Packit Service 4684c1
 * after a completed handshake.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.2.2
Packit Service 4684c1
 **/
Packit Service 4684c1
size_t gnutls_est_record_overhead_size(gnutls_protocol_t version,
Packit Service 4684c1
				       gnutls_cipher_algorithm_t cipher,
Packit Service 4684c1
				       gnutls_mac_algorithm_t mac,
Packit Service 4684c1
				       gnutls_compression_method_t comp,
Packit Service 4684c1
				       unsigned int flags)
Packit Service 4684c1
{
Packit Service 4684c1
	const cipher_entry_st *c;
Packit Service 4684c1
	const mac_entry_st *m;
Packit Service 4684c1
	const version_entry_st *v;
Packit Service 4684c1
	size_t total = 0;
Packit Service 4684c1
Packit Service 4684c1
	c = cipher_to_entry(cipher);
Packit Service 4684c1
	if (c == NULL)
Packit Service 4684c1
		return 0;
Packit Service 4684c1
Packit Service 4684c1
	m = mac_to_entry(mac);
Packit Service 4684c1
	if (m == NULL)
Packit Service 4684c1
		return 0;
Packit Service 4684c1
Packit Service 4684c1
	v = version_to_entry(version);
Packit Service 4684c1
	if (v == NULL)
Packit Service 4684c1
		return 0;
Packit Service 4684c1
Packit Service 4684c1
	if (v->transport == GNUTLS_STREAM)
Packit Service 4684c1
		total = TLS_RECORD_HEADER_SIZE;
Packit Service 4684c1
	else
Packit Service 4684c1
		total = DTLS_RECORD_HEADER_SIZE;
Packit Service 4684c1
Packit Service 4684c1
	total += _gnutls_record_overhead(v, c, m, 1);
Packit Service 4684c1
Packit Service 4684c1
	return total;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* returns overhead imposed by the record layer (encryption/compression)
Packit Service 4684c1
 * etc. It does not include the record layer headers, since the caller
Packit Service 4684c1
 * needs to cope with rounding to multiples of blocksize, and the header
Packit Service 4684c1
 * is outside that.
Packit Service 4684c1
 *
Packit Service 4684c1
 * blocksize: will contain the block size when padding may be required or 1
Packit Service 4684c1
 *
Packit Service 4684c1
 * It may return a negative error code on error.
Packit Service 4684c1
 */
Packit Service 4684c1
static int record_overhead_rt(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	record_parameters_st *params;
Packit Service 4684c1
	int ret;
Packit Service 4684c1
Packit Service 4684c1
	if (session->internals.initial_negotiation_completed == 0)
Packit Service 4684c1
		return GNUTLS_E_INVALID_REQUEST;
Packit Service 4684c1
	ret = _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT, &params);
Packit Service 4684c1
	if (ret < 0)
Packit Service 4684c1
		return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
	return _gnutls_record_overhead(get_version(session), params->cipher, params->mac, 1);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_record_overhead_size:
Packit Service 4684c1
 * @session: is #gnutls_session_t
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function will return the size in bytes of the overhead
Packit Service 4684c1
 * due to TLS (or DTLS) per record. On certain occasions
Packit Service 4684c1
 * (e.g., CBC ciphers) the returned value is the maximum
Packit Service 4684c1
 * possible overhead.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.2.2
Packit Service 4684c1
 **/
Packit Service 4684c1
size_t gnutls_record_overhead_size(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	const version_entry_st *v = get_version(session);
Packit Service 4684c1
	int ret;
Packit Service 4684c1
	size_t total;
Packit Service 4684c1
Packit Service 4684c1
	if (v->transport == GNUTLS_STREAM)
Packit Service 4684c1
		total = TLS_RECORD_HEADER_SIZE;
Packit Service 4684c1
	else
Packit Service 4684c1
		total = DTLS_RECORD_HEADER_SIZE;
Packit Service 4684c1
Packit Service 4684c1
	ret = record_overhead_rt(session);
Packit Service 4684c1
	if (ret >= 0)
Packit Service 4684c1
		total += ret;
Packit Service 4684c1
Packit Service 4684c1
	return total;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_dtls_get_data_mtu:
Packit Service 4684c1
 * @session: is a #gnutls_session_t type.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function will return the actual maximum transfer unit for
Packit Service 4684c1
 * application data. I.e. DTLS headers are subtracted from the
Packit Service 4684c1
 * actual MTU which is set using gnutls_dtls_set_mtu().
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: the maximum allowed transfer unit.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.0
Packit Service 4684c1
 **/
Packit Service 4684c1
unsigned int gnutls_dtls_get_data_mtu(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	int mtu = session->internals.dtls.mtu;
Packit Service 4684c1
	record_parameters_st *params;
Packit Service 4684c1
	int ret, k, hash_size, block;
Packit Service 4684c1
Packit Service 4684c1
	mtu -= RECORD_HEADER_SIZE(session);
Packit Service 4684c1
Packit Service 4684c1
	if (session->internals.initial_negotiation_completed == 0)
Packit Service 4684c1
		return mtu;
Packit Service 4684c1
Packit Service 4684c1
	ret = _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT, &params);
Packit Service 4684c1
	if (ret < 0)
Packit Service 4684c1
		return mtu;
Packit Service 4684c1
Packit Service 4684c1
	if (params->cipher->type == CIPHER_AEAD || params->cipher->type == CIPHER_STREAM)
Packit Service 4684c1
		return mtu-_gnutls_record_overhead(get_version(session), params->cipher, params->mac, 0);
Packit Service 4684c1
Packit Service 4684c1
	/* CIPHER_BLOCK: in CBC ciphers guess the data MTU as it depends on residues
Packit Service 4684c1
	 */
Packit Service 4684c1
	hash_size = _gnutls_mac_get_algo_len(params->mac);
Packit Service 4684c1
	block = _gnutls_cipher_get_explicit_iv_size(params->cipher);
Packit Service 4684c1
	assert(_gnutls_cipher_get_block_size(params->cipher) == block);
Packit Service 4684c1
Packit Service 4684c1
	if (params->etm) {
Packit Service 4684c1
		/* the maximum data mtu satisfies:
Packit Service 4684c1
		 * data mtu (mod block) = block-1
Packit Service 4684c1
		 * or data mtu = (k+1)*(block) - 1
Packit Service 4684c1
	         *
Packit Service 4684c1
		 * and data mtu + block + hash size + 1 = link_mtu
Packit Service 4684c1
		 *     (k+2) * (block) + hash size = link_mtu
Packit Service 4684c1
		 *
Packit Service 4684c1
		 *     We try to find k, and thus data mtu
Packit Service 4684c1
		 */
Packit Service 4684c1
		k = ((mtu-hash_size)/block) - 2;
Packit Service 4684c1
Packit Service 4684c1
		return (k+1)*block - 1;
Packit Service 4684c1
	} else {
Packit Service 4684c1
		/* the maximum data mtu satisfies:
Packit Service 4684c1
		 * data mtu + hash size (mod block) = block-1
Packit Service 4684c1
		 * or data mtu = (k+1)*(block) - hash size - 1
Packit Service 4684c1
		 *
Packit Service 4684c1
		 * and data mtu + block + hash size + 1 = link_mtu
Packit Service 4684c1
		 *     (k+2) * (block) = link_mtu
Packit Service 4684c1
		 *
Packit Service 4684c1
		 *     We try to find k, and thus data mtu
Packit Service 4684c1
		 */
Packit Service 4684c1
		k = ((mtu)/block) - 2;
Packit Service 4684c1
Packit Service 4684c1
		return (k+1)*block - hash_size - 1;
Packit Service 4684c1
	}
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_dtls_set_data_mtu:
Packit Service 4684c1
 * @session: is a #gnutls_session_t type.
Packit Service 4684c1
 * @mtu: The maximum unencrypted transfer unit of the session
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function will set the maximum size of the *unencrypted* records
Packit Service 4684c1
 * which will be sent over a DTLS session. It is equivalent to calculating
Packit Service 4684c1
 * the DTLS packet overhead with the current encryption parameters, and
Packit Service 4684c1
 * calling gnutls_dtls_set_mtu() with that value. In particular, this means
Packit Service 4684c1
 * that you may need to call this function again after any negotiation or
Packit Service 4684c1
 * renegotiation, in order to ensure that the MTU is still sufficient to
Packit Service 4684c1
 * account for the new protocol overhead.
Packit Service 4684c1
 *
Packit Service 4684c1
 * In most cases you only need to call gnutls_dtls_set_mtu() with
Packit Service 4684c1
 * the maximum MTU of your transport layer.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: %GNUTLS_E_SUCCESS (0) on success, or a negative error code.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.1
Packit Service 4684c1
 **/
Packit Service 4684c1
int gnutls_dtls_set_data_mtu(gnutls_session_t session, unsigned int mtu)
Packit Service 4684c1
{
Packit Service 4684c1
	int overhead;
Packit Service 4684c1
Packit Service 4684c1
	overhead = record_overhead_rt(session);
Packit Service 4684c1
Packit Service 4684c1
	/* You can't call this until the session is actually running */
Packit Service 4684c1
	if (overhead < 0)
Packit Service 4684c1
		return GNUTLS_E_INVALID_SESSION;
Packit Service 4684c1
Packit Service 4684c1
	/* Add the overhead inside the encrypted part */
Packit Service 4684c1
	mtu += overhead;
Packit Service 4684c1
Packit Service 4684c1
	/* Add the *unencrypted header size */
Packit Service 4684c1
	mtu += RECORD_HEADER_SIZE(session);
Packit Service 4684c1
Packit Service 4684c1
	gnutls_dtls_set_mtu(session, mtu);
Packit Service 4684c1
	return GNUTLS_E_SUCCESS;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_dtls_get_mtu:
Packit Service 4684c1
 * @session: is a #gnutls_session_t type.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function will return the MTU size as set with
Packit Service 4684c1
 * gnutls_dtls_set_mtu(). This is not the actual MTU
Packit Service 4684c1
 * of data you can transmit. Use gnutls_dtls_get_data_mtu()
Packit Service 4684c1
 * for that reason.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: the set maximum transfer unit.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.0
Packit Service 4684c1
 **/
Packit Service 4684c1
unsigned int gnutls_dtls_get_mtu(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	return session->internals.dtls.mtu;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_dtls_get_timeout:
Packit Service 4684c1
 * @session: is a #gnutls_session_t type.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function will return the milliseconds remaining
Packit Service 4684c1
 * for a retransmission of the previously sent handshake
Packit Service 4684c1
 * message. This function is useful when DTLS is used in
Packit Service 4684c1
 * non-blocking mode, to estimate when to call gnutls_handshake()
Packit Service 4684c1
 * if no packets have been received.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: the remaining time in milliseconds.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.0
Packit Service 4684c1
 **/
Packit Service 4684c1
unsigned int gnutls_dtls_get_timeout(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	struct timespec now;
Packit Service 4684c1
	unsigned int diff;
Packit Service 4684c1
Packit Service 4684c1
	gnutls_gettime(&now;;
Packit Service 4684c1
Packit Service 4684c1
	diff =
Packit Service 4684c1
	    timespec_sub_ms(&now,
Packit Service 4684c1
			    &session->internals.dtls.last_retransmit);
Packit Service 4684c1
	if (diff >= TIMER_WINDOW)
Packit Service 4684c1
		return 0;
Packit Service 4684c1
	else
Packit Service 4684c1
		return TIMER_WINDOW - diff;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
#define COOKIE_SIZE 16
Packit Service 4684c1
#define COOKIE_MAC_SIZE 16
Packit Service 4684c1
Packit Service 4684c1
/*   MAC
Packit Service 4684c1
 * 16 bytes
Packit Service 4684c1
 *
Packit Service 4684c1
 * total 19 bytes
Packit Service 4684c1
 */
Packit Service 4684c1
Packit Service 4684c1
#define C_HASH GNUTLS_MAC_SHA1
Packit Service 4684c1
#define C_HASH_SIZE 20
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_dtls_cookie_send:
Packit Service 4684c1
 * @key: is a random key to be used at cookie generation
Packit Service 4684c1
 * @client_data: contains data identifying the client (i.e. address)
Packit Service 4684c1
 * @client_data_size: The size of client's data
Packit Service 4684c1
 * @prestate: The previous cookie returned by gnutls_dtls_cookie_verify()
Packit Service 4684c1
 * @ptr: A transport pointer to be used by @push_func
Packit Service 4684c1
 * @push_func: A function that will be used to reply
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function can be used to prevent denial of service
Packit Service 4684c1
 * attacks to a DTLS server by requiring the client to
Packit Service 4684c1
 * reply using a cookie sent by this function. That way
Packit Service 4684c1
 * it can be ensured that a client we allocated resources
Packit Service 4684c1
 * for (i.e. #gnutls_session_t) is the one that the 
Packit Service 4684c1
 * original incoming packet was originated from.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function must be called at the first incoming packet,
Packit Service 4684c1
 * prior to allocating any resources and must be succeeded
Packit Service 4684c1
 * by gnutls_dtls_cookie_verify().
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: the number of bytes sent, or a negative error code.  
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.0
Packit Service 4684c1
 **/
Packit Service 4684c1
int gnutls_dtls_cookie_send(gnutls_datum_t * key, void *client_data,
Packit Service 4684c1
			    size_t client_data_size,
Packit Service 4684c1
			    gnutls_dtls_prestate_st * prestate,
Packit Service 4684c1
			    gnutls_transport_ptr_t ptr,
Packit Service 4684c1
			    gnutls_push_func push_func)
Packit Service 4684c1
{
Packit Service 4684c1
	uint8_t hvr[20 + DTLS_HANDSHAKE_HEADER_SIZE + COOKIE_SIZE];
Packit Service 4684c1
	int hvr_size = 0, ret;
Packit Service 4684c1
	uint8_t digest[C_HASH_SIZE];
Packit Service 4684c1
Packit Service 4684c1
	if (key == NULL || key->data == NULL || key->size == 0)
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
Packit Service 4684c1
Packit Service 4684c1
/* send
Packit Service 4684c1
 *  struct {
Packit Service 4684c1
 *    ContentType type - 1 byte GNUTLS_HANDSHAKE;
Packit Service 4684c1
 *    ProtocolVersion version; - 2 bytes (254,255)
Packit Service 4684c1
 *    uint16 epoch; - 2 bytes (0, 0)
Packit Service 4684c1
 *    uint48 sequence_number; - 4 bytes (0,0,0,0)
Packit Service 4684c1
 *    uint16 length; - 2 bytes (COOKIE_SIZE+1+2)+DTLS_HANDSHAKE_HEADER_SIZE
Packit Service 4684c1
 *    uint8_t fragment[DTLSPlaintext.length];
Packit Service 4684c1
 *  } DTLSPlaintext;
Packit Service 4684c1
 *
Packit Service 4684c1
 *
Packit Service 4684c1
 * struct {
Packit Service 4684c1
 *    HandshakeType msg_type; 1 byte - GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST
Packit Service 4684c1
 *    uint24 length; - COOKIE_SIZE+3
Packit Service 4684c1
 *    uint16 message_seq; - 2 bytes (0,0)
Packit Service 4684c1
 *    uint24 fragment_offset; - 3 bytes (0,0,0)
Packit Service 4684c1
 *    uint24 fragment_length; - same as length
Packit Service 4684c1
 * }
Packit Service 4684c1
 *
Packit Service 4684c1
 * struct {
Packit Service 4684c1
 *   ProtocolVersion server_version;
Packit Service 4684c1
 *   uint8_t cookie<0..32>;
Packit Service 4684c1
 * } HelloVerifyRequest;
Packit Service 4684c1
 */
Packit Service 4684c1
Packit Service 4684c1
	hvr[hvr_size++] = GNUTLS_HANDSHAKE;
Packit Service 4684c1
	/* version */
Packit Service 4684c1
	hvr[hvr_size++] = 254;
Packit Service 4684c1
	hvr[hvr_size++] = 255;
Packit Service 4684c1
Packit Service 4684c1
	/* epoch + seq */
Packit Service 4684c1
	memset(&hvr[hvr_size], 0, 8);
Packit Service 4684c1
	hvr_size += 7;
Packit Service 4684c1
	hvr[hvr_size++] = prestate->record_seq;
Packit Service 4684c1
Packit Service 4684c1
	/* length */
Packit Service 4684c1
	_gnutls_write_uint16(DTLS_HANDSHAKE_HEADER_SIZE + COOKIE_SIZE + 3,
Packit Service 4684c1
			     &hvr[hvr_size]);
Packit Service 4684c1
	hvr_size += 2;
Packit Service 4684c1
Packit Service 4684c1
	/* now handshake headers */
Packit Service 4684c1
	hvr[hvr_size++] = GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST;
Packit Service 4684c1
	_gnutls_write_uint24(COOKIE_SIZE + 3, &hvr[hvr_size]);
Packit Service 4684c1
	hvr_size += 3;
Packit Service 4684c1
Packit Service 4684c1
	/* handshake seq */
Packit Service 4684c1
	hvr[hvr_size++] = 0;
Packit Service 4684c1
	hvr[hvr_size++] = prestate->hsk_write_seq;
Packit Service 4684c1
Packit Service 4684c1
	_gnutls_write_uint24(0, &hvr[hvr_size]);
Packit Service 4684c1
	hvr_size += 3;
Packit Service 4684c1
Packit Service 4684c1
	_gnutls_write_uint24(COOKIE_SIZE + 3, &hvr[hvr_size]);
Packit Service 4684c1
	hvr_size += 3;
Packit Service 4684c1
Packit Service 4684c1
	/* version */
Packit Service 4684c1
	hvr[hvr_size++] = 254;
Packit Service 4684c1
	hvr[hvr_size++] = 255;
Packit Service 4684c1
	hvr[hvr_size++] = COOKIE_SIZE;
Packit Service 4684c1
Packit Service 4684c1
	ret =
Packit Service 4684c1
	    _gnutls_mac_fast(C_HASH, key->data, key->size, client_data,
Packit Service 4684c1
			     client_data_size, digest);
Packit Service 4684c1
	if (ret < 0)
Packit Service 4684c1
		return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
	memcpy(&hvr[hvr_size], digest, COOKIE_MAC_SIZE);
Packit Service 4684c1
	hvr_size += COOKIE_MAC_SIZE;
Packit Service 4684c1
Packit Service 4684c1
	ret = push_func(ptr, hvr, hvr_size);
Packit Service 4684c1
	if (ret < 0)
Packit Service 4684c1
		ret = GNUTLS_E_PUSH_ERROR;
Packit Service 4684c1
Packit Service 4684c1
	return ret;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_dtls_cookie_verify:
Packit Service 4684c1
 * @key: is a random key to be used at cookie generation
Packit Service 4684c1
 * @client_data: contains data identifying the client (i.e. address)
Packit Service 4684c1
 * @client_data_size: The size of client's data
Packit Service 4684c1
 * @_msg: An incoming message that initiates a connection.
Packit Service 4684c1
 * @msg_size: The size of the message.
Packit Service 4684c1
 * @prestate: The cookie of this client.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function will verify the received message for
Packit Service 4684c1
 * a valid cookie. If a valid cookie is returned then
Packit Service 4684c1
 * it should be associated with the session using
Packit Service 4684c1
 * gnutls_dtls_prestate_set();
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function must be called after gnutls_dtls_cookie_send().
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: %GNUTLS_E_SUCCESS (0) on success, or a negative error code.  
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.0
Packit Service 4684c1
 **/
Packit Service 4684c1
int gnutls_dtls_cookie_verify(gnutls_datum_t * key,
Packit Service 4684c1
			      void *client_data, size_t client_data_size,
Packit Service 4684c1
			      void *_msg, size_t msg_size,
Packit Service 4684c1
			      gnutls_dtls_prestate_st * prestate)
Packit Service 4684c1
{
Packit Service 4684c1
	gnutls_datum_t cookie;
Packit Service 4684c1
	int ret;
Packit Service 4684c1
	unsigned int pos, sid_size;
Packit Service 4684c1
	uint8_t *msg = _msg;
Packit Service 4684c1
	uint8_t digest[C_HASH_SIZE];
Packit Service 4684c1
Packit Service 4684c1
	if (key == NULL || key->data == NULL || key->size == 0)
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
Packit Service 4684c1
Packit Service 4684c1
	/* format:
Packit Service 4684c1
	 * version - 2 bytes
Packit Service 4684c1
	 * random - 32 bytes
Packit Service 4684c1
	 * session_id - 1 byte length + content
Packit Service 4684c1
	 * cookie - 1 byte length + content
Packit Service 4684c1
	 */
Packit Service 4684c1
Packit Service 4684c1
	pos = 34 + DTLS_RECORD_HEADER_SIZE + DTLS_HANDSHAKE_HEADER_SIZE;
Packit Service 4684c1
Packit Service 4684c1
	if (msg_size < pos + 1)
Packit Service 4684c1
		return
Packit Service 4684c1
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
Packit Service 4684c1
Packit Service 4684c1
	sid_size = msg[pos++];
Packit Service 4684c1
Packit Service 4684c1
	if (sid_size > 32 || msg_size < pos + sid_size + 1)
Packit Service 4684c1
		return
Packit Service 4684c1
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
Packit Service 4684c1
Packit Service 4684c1
	pos += sid_size;
Packit Service 4684c1
	cookie.size = msg[pos++];
Packit Service 4684c1
Packit Service 4684c1
	if (msg_size < pos + cookie.size + 1)
Packit Service 4684c1
		return
Packit Service 4684c1
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
Packit Service 4684c1
Packit Service 4684c1
	cookie.data = &msg[pos];
Packit Service 4684c1
	if (cookie.size != COOKIE_SIZE) {
Packit Service 4684c1
		if (cookie.size > 0)
Packit Service 4684c1
			_gnutls_audit_log(NULL,
Packit Service 4684c1
					  "Received cookie with illegal size %d. Expected %d\n",
Packit Service 4684c1
					  (int) cookie.size, COOKIE_SIZE);
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_BAD_COOKIE);
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	ret =
Packit Service 4684c1
	    _gnutls_mac_fast(C_HASH, key->data, key->size, client_data,
Packit Service 4684c1
			     client_data_size, digest);
Packit Service 4684c1
	if (ret < 0)
Packit Service 4684c1
		return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
	if (memcmp(digest, cookie.data, COOKIE_MAC_SIZE) != 0)
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_BAD_COOKIE);
Packit Service 4684c1
Packit Service 4684c1
	prestate->record_seq = msg[10];	/* client's record seq */
Packit Service 4684c1
	prestate->hsk_read_seq = msg[DTLS_RECORD_HEADER_SIZE + 5];	/* client's hsk seq */
Packit Service 4684c1
	prestate->hsk_write_seq = 0;	/* we always send zero for this msg */
Packit Service 4684c1
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_dtls_prestate_set:
Packit Service 4684c1
 * @session: a new session
Packit Service 4684c1
 * @prestate: contains the client's prestate
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function will associate the prestate acquired by
Packit Service 4684c1
 * the cookie authentication with the client, with the newly 
Packit Service 4684c1
 * established session.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This functions must be called after a successful gnutls_dtls_cookie_verify()
Packit Service 4684c1
 * and should be succeeded by the actual DTLS handshake using gnutls_handshake().
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.0
Packit Service 4684c1
 **/
Packit Service 4684c1
void gnutls_dtls_prestate_set(gnutls_session_t session,
Packit Service 4684c1
			      gnutls_dtls_prestate_st * prestate)
Packit Service 4684c1
{
Packit Service 4684c1
	record_parameters_st *params;
Packit Service 4684c1
	int ret;
Packit Service 4684c1
Packit Service 4684c1
	if (prestate == NULL)
Packit Service 4684c1
		return;
Packit Service 4684c1
Packit Service 4684c1
	/* we do not care about read_params, since we accept anything
Packit Service 4684c1
	 * the peer sends.
Packit Service 4684c1
	 */
Packit Service 4684c1
	ret = _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT, &params);
Packit Service 4684c1
	if (ret < 0)
Packit Service 4684c1
		return;
Packit Service 4684c1
Packit Service 4684c1
	params->write.sequence_number = prestate->record_seq;
Packit Service 4684c1
Packit Service 4684c1
	session->internals.dtls.hsk_read_seq = prestate->hsk_read_seq;
Packit Service 4684c1
	session->internals.dtls.hsk_write_seq =
Packit Service 4684c1
	    prestate->hsk_write_seq + 1;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_record_get_discarded:
Packit Service 4684c1
 * @session: is a #gnutls_session_t type.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns the number of discarded packets in a
Packit Service 4684c1
 * DTLS connection.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: The number of discarded packets.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.0
Packit Service 4684c1
 **/
Packit Service 4684c1
unsigned int gnutls_record_get_discarded(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	return session->internals.dtls.packets_dropped;
Packit Service 4684c1
}