Blame lib/ext/heartbeat.c

Packit Service 4684c1
/*
Packit Service 4684c1
 * Copyright (C) 2012,2013 Free Software Foundation, Inc.
Packit Service 4684c1
 * Copyright (C) 2013 Nikos Mavrogiannopoulos
Packit Service 4684c1
 *
Packit Service 4684c1
 * Author: Nikos Mavrogiannopoulos
Packit Service 4684c1
 *
Packit Service 4684c1
 * This file is part of GnuTLS.
Packit Service 4684c1
 *
Packit Service 4684c1
 * The GnuTLS 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
/* This file implements the TLS heartbeat extension.
Packit Service 4684c1
 */
Packit Service 4684c1
Packit Service 4684c1
#include "errors.h"
Packit Service 4684c1
#include "gnutls_int.h"
Packit Service 4684c1
#include <dtls.h>
Packit Service 4684c1
#include <record.h>
Packit Service 4684c1
#include <ext/heartbeat.h>
Packit Service 4684c1
#include <hello_ext.h>
Packit Service 4684c1
#include <random.h>
Packit Service 4684c1
Packit Service 4684c1
#ifdef ENABLE_HEARTBEAT
Packit Service 4684c1
/**
Packit Service 4684c1
  * gnutls_heartbeat_enable:
Packit Service 4684c1
  * @session: is a #gnutls_session_t type.
Packit Service 4684c1
  * @type: one of the GNUTLS_HB_* flags
Packit Service 4684c1
  *
Packit Service 4684c1
  * If this function is called with the %GNUTLS_HB_PEER_ALLOWED_TO_SEND
Packit Service 4684c1
  * @type, GnuTLS will allow heartbeat messages to be received. Moreover it also
Packit Service 4684c1
  * request the peer to accept heartbeat messages. This function
Packit Service 4684c1
  * must be called prior to TLS handshake.
Packit Service 4684c1
  *
Packit Service 4684c1
  * If the @type used is %GNUTLS_HB_LOCAL_ALLOWED_TO_SEND, then the peer
Packit Service 4684c1
  * will be asked to accept heartbeat messages but not send ones.
Packit Service 4684c1
  *
Packit Service 4684c1
  * The function gnutls_heartbeat_allowed() can be used to test Whether
Packit Service 4684c1
  * locally generated heartbeat messages can be accepted by the peer.
Packit Service 4684c1
  *
Packit Service 4684c1
  * Since: 3.1.2
Packit Service 4684c1
  **/
Packit Service 4684c1
void gnutls_heartbeat_enable(gnutls_session_t session, unsigned int type)
Packit Service 4684c1
{
Packit Service 4684c1
	gnutls_ext_priv_data_t epriv;
Packit Service 4684c1
Packit Service 4684c1
	epriv = (void*)(intptr_t)type;
Packit Service 4684c1
	_gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_HEARTBEAT,
Packit Service 4684c1
				     epriv);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
  * gnutls_heartbeat_allowed:
Packit Service 4684c1
  * @session: is a #gnutls_session_t type.
Packit Service 4684c1
  * @type: one of %GNUTLS_HB_LOCAL_ALLOWED_TO_SEND and %GNUTLS_HB_PEER_ALLOWED_TO_SEND
Packit Service 4684c1
  *
Packit Service 4684c1
  * This function will check whether heartbeats are allowed
Packit Service 4684c1
  * to be sent or received in this session. 
Packit Service 4684c1
  *
Packit Service 4684c1
  * Returns: Non zero if heartbeats are allowed.
Packit Service 4684c1
  *
Packit Service 4684c1
  * Since: 3.1.2
Packit Service 4684c1
  **/
Packit Service 4684c1
unsigned gnutls_heartbeat_allowed(gnutls_session_t session, unsigned int type)
Packit Service 4684c1
{
Packit Service 4684c1
	gnutls_ext_priv_data_t epriv;
Packit Service 4684c1
Packit Service 4684c1
	if (session->internals.handshake_in_progress != 0)
Packit Service 4684c1
		return 0; /* not allowed */
Packit Service 4684c1
Packit Service 4684c1
	if (_gnutls_hello_ext_get_priv
Packit Service 4684c1
	    (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0)
Packit Service 4684c1
		return 0;	/* Not enabled */
Packit Service 4684c1
Packit Service 4684c1
	if (type == GNUTLS_HB_LOCAL_ALLOWED_TO_SEND) {
Packit Service 4684c1
		if (((intptr_t)epriv) & LOCAL_ALLOWED_TO_SEND)
Packit Service 4684c1
			return 1;
Packit Service 4684c1
	} else if (((intptr_t)epriv) & GNUTLS_HB_PEER_ALLOWED_TO_SEND)
Packit Service 4684c1
		return 1;
Packit Service 4684c1
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
#define DEFAULT_PADDING_SIZE 16
Packit Service 4684c1
Packit Service 4684c1
/*
Packit Service 4684c1
 * Sends heartbeat data.
Packit Service 4684c1
 */
Packit Service 4684c1
static int
Packit Service 4684c1
heartbeat_send_data(gnutls_session_t session, const void *data,
Packit Service 4684c1
		    size_t data_size, uint8_t type)
Packit Service 4684c1
{
Packit Service 4684c1
	int ret, pos;
Packit Service 4684c1
	uint8_t *response;
Packit Service 4684c1
Packit Service 4684c1
	response = gnutls_malloc(1 + 2 + data_size + DEFAULT_PADDING_SIZE);
Packit Service 4684c1
	if (response == NULL)
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
Packit Service 4684c1
Packit Service 4684c1
	pos = 0;
Packit Service 4684c1
	response[pos++] = type;
Packit Service 4684c1
Packit Service 4684c1
	_gnutls_write_uint16(data_size, &response[pos]);
Packit Service 4684c1
	pos += 2;
Packit Service 4684c1
Packit Service 4684c1
	memcpy(&response[pos], data, data_size);
Packit Service 4684c1
	pos += data_size;
Packit Service 4684c1
Packit Service 4684c1
	ret =
Packit Service 4684c1
	    gnutls_rnd(GNUTLS_RND_NONCE, &response[pos],
Packit Service 4684c1
		       DEFAULT_PADDING_SIZE);
Packit Service 4684c1
	if (ret < 0) {
Packit Service 4684c1
		gnutls_assert();
Packit Service 4684c1
		goto cleanup;
Packit Service 4684c1
	}
Packit Service 4684c1
	pos += DEFAULT_PADDING_SIZE;
Packit Service 4684c1
Packit Service 4684c1
	ret =
Packit Service 4684c1
	    _gnutls_send_int(session, GNUTLS_HEARTBEAT, -1,
Packit Service 4684c1
			     EPOCH_WRITE_CURRENT, response, pos,
Packit Service 4684c1
			     MBUFFER_FLUSH);
Packit Service 4684c1
Packit Service 4684c1
      cleanup:
Packit Service 4684c1
	gnutls_free(response);
Packit Service 4684c1
	return ret;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_heartbeat_ping:
Packit Service 4684c1
 * @session: is a #gnutls_session_t type.
Packit Service 4684c1
 * @data_size: is the length of the ping payload.
Packit Service 4684c1
 * @max_tries: if flags is %GNUTLS_HEARTBEAT_WAIT then this sets the number of retransmissions. Use zero for indefinite (until timeout).
Packit Service 4684c1
 * @flags: if %GNUTLS_HEARTBEAT_WAIT then wait for pong or timeout instead of returning immediately.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function sends a ping to the peer. If the @flags is set
Packit Service 4684c1
 * to %GNUTLS_HEARTBEAT_WAIT then it waits for a reply from the peer.
Packit Service 4684c1
 * 
Packit Service 4684c1
 * Note that it is highly recommended to use this function with the
Packit Service 4684c1
 * flag %GNUTLS_HEARTBEAT_WAIT, or you need to handle retransmissions
Packit Service 4684c1
 * and timeouts manually.
Packit Service 4684c1
 *
Packit Service 4684c1
 * The total TLS data transmitted as part of the ping message are given by
Packit Service 4684c1
 * the following formula: MAX(16, @data_size)+gnutls_record_overhead_size()+3.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.1.2
Packit Service 4684c1
 **/
Packit Service 4684c1
int
Packit Service 4684c1
gnutls_heartbeat_ping(gnutls_session_t session, size_t data_size,
Packit Service 4684c1
		      unsigned int max_tries, unsigned int flags)
Packit Service 4684c1
{
Packit Service 4684c1
	int ret;
Packit Service 4684c1
	unsigned int retries = 1, diff;
Packit Service 4684c1
	struct timespec now;
Packit Service 4684c1
Packit Service 4684c1
	if (data_size > MAX_HEARTBEAT_LENGTH)
Packit Service 4684c1
		return
Packit Service 4684c1
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
Packit Service 4684c1
Packit Service 4684c1
	if (gnutls_heartbeat_allowed
Packit Service 4684c1
	    (session, GNUTLS_HB_LOCAL_ALLOWED_TO_SEND) == 0)
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
Packit Service 4684c1
Packit Service 4684c1
	/* resume previous call if interrupted */
Packit Service 4684c1
	if (session->internals.record_send_buffer.byte_length > 0 &&
Packit Service 4684c1
	    session->internals.record_send_buffer.head != NULL &&
Packit Service 4684c1
	    session->internals.record_send_buffer.head->type ==
Packit Service 4684c1
	    GNUTLS_HEARTBEAT)
Packit Service 4684c1
		return _gnutls_io_write_flush(session);
Packit Service 4684c1
Packit Service 4684c1
	switch (session->internals.hb_state) {
Packit Service 4684c1
	case SHB_SEND1:
Packit Service 4684c1
		if (data_size > DEFAULT_PADDING_SIZE)
Packit Service 4684c1
			data_size -= DEFAULT_PADDING_SIZE;
Packit Service 4684c1
		else
Packit Service 4684c1
			data_size = 0;
Packit Service 4684c1
Packit Service 4684c1
		_gnutls_buffer_reset(&session->internals.hb_local_data);
Packit Service 4684c1
Packit Service 4684c1
		ret =
Packit Service 4684c1
		    _gnutls_buffer_resize(&session->internals.
Packit Service 4684c1
					  hb_local_data, data_size);
Packit Service 4684c1
		if (ret < 0)
Packit Service 4684c1
			return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
		ret =
Packit Service 4684c1
		    gnutls_rnd(GNUTLS_RND_NONCE,
Packit Service 4684c1
				session->internals.hb_local_data.data,
Packit Service 4684c1
				data_size);
Packit Service 4684c1
		if (ret < 0)
Packit Service 4684c1
			return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
		gnutls_gettime(&session->internals.hb_ping_start);
Packit Service 4684c1
		session->internals.hb_local_data.length = data_size;
Packit Service 4684c1
		session->internals.hb_state = SHB_SEND2;
Packit Service 4684c1
Packit Service 4684c1
		FALLTHROUGH;
Packit Service 4684c1
	case SHB_SEND2:
Packit Service 4684c1
		session->internals.hb_actual_retrans_timeout_ms =
Packit Service 4684c1
		    session->internals.hb_retrans_timeout_ms;
Packit Service 4684c1
	      retry:
Packit Service 4684c1
		ret =
Packit Service 4684c1
		    heartbeat_send_data(session,
Packit Service 4684c1
					session->internals.hb_local_data.
Packit Service 4684c1
					data,
Packit Service 4684c1
					session->internals.hb_local_data.
Packit Service 4684c1
					length, HEARTBEAT_REQUEST);
Packit Service 4684c1
		if (ret < 0)
Packit Service 4684c1
			return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
		gnutls_gettime(&session->internals.hb_ping_sent);
Packit Service 4684c1
Packit Service 4684c1
		if (!(flags & GNUTLS_HEARTBEAT_WAIT)) {
Packit Service 4684c1
			session->internals.hb_state = SHB_SEND1;
Packit Service 4684c1
			break;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		session->internals.hb_state = SHB_RECV;
Packit Service 4684c1
		FALLTHROUGH;
Packit Service 4684c1
Packit Service 4684c1
	case SHB_RECV:
Packit Service 4684c1
		ret =
Packit Service 4684c1
		    _gnutls_recv_int(session, GNUTLS_HEARTBEAT,
Packit Service 4684c1
				     NULL, 0, NULL,
Packit Service 4684c1
				     session->internals.
Packit Service 4684c1
				     hb_actual_retrans_timeout_ms);
Packit Service 4684c1
		if (ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED) {
Packit Service 4684c1
			session->internals.hb_state = SHB_SEND1;
Packit Service 4684c1
			break;
Packit Service 4684c1
		} else if (ret == GNUTLS_E_TIMEDOUT) {
Packit Service 4684c1
			retries++;
Packit Service 4684c1
			if (max_tries > 0 && retries > max_tries) {
Packit Service 4684c1
				session->internals.hb_state = SHB_SEND1;
Packit Service 4684c1
				return gnutls_assert_val(ret);
Packit Service 4684c1
			}
Packit Service 4684c1
Packit Service 4684c1
			gnutls_gettime(&now;;
Packit Service 4684c1
			diff =
Packit Service 4684c1
			    timespec_sub_ms(&now,
Packit Service 4684c1
					    &session->internals.
Packit Service 4684c1
					    hb_ping_start);
Packit Service 4684c1
			if (diff > session->internals.hb_total_timeout_ms) {
Packit Service 4684c1
				session->internals.hb_state = SHB_SEND1;
Packit Service 4684c1
				return
Packit Service 4684c1
				    gnutls_assert_val(GNUTLS_E_TIMEDOUT);
Packit Service 4684c1
			}
Packit Service 4684c1
Packit Service 4684c1
			session->internals.hb_actual_retrans_timeout_ms *=
Packit Service 4684c1
			    2;
Packit Service 4684c1
			session->internals.hb_actual_retrans_timeout_ms %=
Packit Service 4684c1
			    MAX_DTLS_TIMEOUT;
Packit Service 4684c1
Packit Service 4684c1
			session->internals.hb_state = SHB_SEND2;
Packit Service 4684c1
			goto retry;
Packit Service 4684c1
		} else if (ret < 0) {
Packit Service 4684c1
			session->internals.hb_state = SHB_SEND1;
Packit Service 4684c1
			return gnutls_assert_val(ret);
Packit Service 4684c1
		}
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_heartbeat_pong:
Packit Service 4684c1
 * @session: is a #gnutls_session_t type.
Packit Service 4684c1
 * @flags: should be zero
Packit Service 4684c1
 *
Packit Service 4684c1
 * This function replies to a ping by sending a pong to the peer.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Since: 3.1.2
Packit Service 4684c1
 **/
Packit Service 4684c1
int gnutls_heartbeat_pong(gnutls_session_t session, unsigned int flags)
Packit Service 4684c1
{
Packit Service 4684c1
	int ret;
Packit Service 4684c1
Packit Service 4684c1
	if (session->internals.record_send_buffer.byte_length > 0 &&
Packit Service 4684c1
	    session->internals.record_send_buffer.head != NULL &&
Packit Service 4684c1
	    session->internals.record_send_buffer.head->type ==
Packit Service 4684c1
	    GNUTLS_HEARTBEAT)
Packit Service 4684c1
		return _gnutls_io_write_flush(session);
Packit Service 4684c1
Packit Service 4684c1
	if (session->internals.hb_remote_data.length == 0)
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
Packit Service 4684c1
Packit Service 4684c1
	ret =
Packit Service 4684c1
	    heartbeat_send_data(session,
Packit Service 4684c1
				session->internals.hb_remote_data.data,
Packit Service 4684c1
				session->internals.hb_remote_data.length,
Packit Service 4684c1
				HEARTBEAT_RESPONSE);
Packit Service 4684c1
Packit Service 4684c1
	_gnutls_buffer_reset(&session->internals.hb_remote_data);
Packit Service 4684c1
Packit Service 4684c1
	if (ret < 0)
Packit Service 4684c1
		return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/*
Packit Service 4684c1
 * Processes a heartbeat message. 
Packit Service 4684c1
 */
Packit Service 4684c1
int _gnutls_heartbeat_handle(gnutls_session_t session, mbuffer_st * bufel)
Packit Service 4684c1
{
Packit Service 4684c1
	int ret;
Packit Service 4684c1
	unsigned type;
Packit Service 4684c1
	unsigned pos;
Packit Service 4684c1
	uint8_t *msg = _mbuffer_get_udata_ptr(bufel);
Packit Service 4684c1
	size_t hb_len, len = _mbuffer_get_udata_size(bufel);
Packit Service 4684c1
Packit Service 4684c1
	if (gnutls_heartbeat_allowed
Packit Service 4684c1
	    (session, GNUTLS_HB_PEER_ALLOWED_TO_SEND) == 0)
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
Packit Service 4684c1
Packit Service 4684c1
	if (len < 3 + DEFAULT_PADDING_SIZE)
Packit Service 4684c1
		return
Packit Service 4684c1
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
Packit Service 4684c1
Packit Service 4684c1
	pos = 0;
Packit Service 4684c1
	type = msg[pos++];
Packit Service 4684c1
Packit Service 4684c1
	hb_len = _gnutls_read_uint16(&msg[pos]);
Packit Service 4684c1
	if (hb_len > len - 3 - DEFAULT_PADDING_SIZE)
Packit Service 4684c1
		return
Packit Service 4684c1
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
Packit Service 4684c1
Packit Service 4684c1
	pos += 2;
Packit Service 4684c1
Packit Service 4684c1
	switch (type) {
Packit Service 4684c1
	case HEARTBEAT_REQUEST:
Packit Service 4684c1
		_gnutls_buffer_reset(&session->internals.hb_remote_data);
Packit Service 4684c1
Packit Service 4684c1
		ret =
Packit Service 4684c1
		    _gnutls_buffer_resize(&session->internals.
Packit Service 4684c1
					  hb_remote_data, hb_len);
Packit Service 4684c1
		if (ret < 0)
Packit Service 4684c1
			return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
		if (hb_len > 0)
Packit Service 4684c1
			memcpy(session->internals.hb_remote_data.data,
Packit Service 4684c1
			       &msg[pos], hb_len);
Packit Service 4684c1
		session->internals.hb_remote_data.length = hb_len;
Packit Service 4684c1
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PING_RECEIVED);
Packit Service 4684c1
Packit Service 4684c1
	case HEARTBEAT_RESPONSE:
Packit Service 4684c1
Packit Service 4684c1
		if (hb_len != session->internals.hb_local_data.length)
Packit Service 4684c1
			return
Packit Service 4684c1
			    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
Packit Service 4684c1
Packit Service 4684c1
		if (hb_len > 0 &&
Packit Service 4684c1
		    memcmp(&msg[pos],
Packit Service 4684c1
			   session->internals.hb_local_data.data,
Packit Service 4684c1
			   hb_len) != 0) {
Packit Service 4684c1
			if (IS_DTLS(session))
Packit Service 4684c1
				return gnutls_assert_val(GNUTLS_E_AGAIN);	/* ignore it */
Packit Service 4684c1
			else
Packit Service 4684c1
				return
Packit Service 4684c1
				    gnutls_assert_val
Packit Service 4684c1
				    (GNUTLS_E_UNEXPECTED_PACKET);
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		_gnutls_buffer_reset(&session->internals.hb_local_data);
Packit Service 4684c1
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PONG_RECEIVED);
Packit Service 4684c1
	default:
Packit Service 4684c1
		_gnutls_record_log
Packit Service 4684c1
		    ("REC[%p]: HB: received unknown type %u\n", session,
Packit Service 4684c1
		     type);
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
Packit Service 4684c1
	}
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_heartbeat_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 ping
Packit Service 4684c1
 * message. This function is useful when ping is used in
Packit Service 4684c1
 * non-blocking mode, to estimate when to call gnutls_heartbeat_ping()
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.1.2
Packit Service 4684c1
 **/
Packit Service 4684c1
unsigned int gnutls_heartbeat_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
	diff = timespec_sub_ms(&now, &session->internals.hb_ping_sent);
Packit Service 4684c1
	if (diff >= session->internals.hb_actual_retrans_timeout_ms)
Packit Service 4684c1
		return 0;
Packit Service 4684c1
	else
Packit Service 4684c1
		return session->internals.hb_actual_retrans_timeout_ms -
Packit Service 4684c1
		    diff;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * gnutls_heartbeat_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 override the timeouts for the DTLS heartbeat
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 request 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
 * Since: 3.1.2
Packit Service 4684c1
 **/
Packit Service 4684c1
void gnutls_heartbeat_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
	session->internals.hb_retrans_timeout_ms = retrans_timeout;
Packit Service 4684c1
	session->internals.hb_total_timeout_ms = total_timeout;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
static int
Packit Service 4684c1
_gnutls_heartbeat_recv_params(gnutls_session_t session,
Packit Service 4684c1
			      const uint8_t * data, size_t _data_size)
Packit Service 4684c1
{
Packit Service 4684c1
	unsigned policy;
Packit Service 4684c1
	gnutls_ext_priv_data_t epriv;
Packit Service 4684c1
Packit Service 4684c1
	if (_gnutls_hello_ext_get_priv
Packit Service 4684c1
	    (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0) {
Packit Service 4684c1
		if (session->security_parameters.entity == GNUTLS_CLIENT)
Packit Service 4684c1
			return
Packit Service 4684c1
			    gnutls_assert_val
Packit Service 4684c1
			    (GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
Packit Service 4684c1
		return 0;	/* Not enabled */
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	if (_data_size == 0)
Packit Service 4684c1
		return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
Packit Service 4684c1
Packit Service 4684c1
	policy = (intptr_t)epriv;
Packit Service 4684c1
Packit Service 4684c1
	if (data[0] == 1)
Packit Service 4684c1
		policy |= LOCAL_ALLOWED_TO_SEND;
Packit Service 4684c1
	else if (data[0] == 2)
Packit Service 4684c1
		policy |= LOCAL_NOT_ALLOWED_TO_SEND;
Packit Service 4684c1
	else
Packit Service 4684c1
		return
Packit Service 4684c1
		    gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
Packit Service 4684c1
Packit Service 4684c1
	epriv = (void*)(intptr_t)policy;
Packit Service 4684c1
	_gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_HEARTBEAT,
Packit Service 4684c1
				     epriv);
Packit Service 4684c1
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static int
Packit Service 4684c1
_gnutls_heartbeat_send_params(gnutls_session_t session,
Packit Service 4684c1
			      gnutls_buffer_st * extdata)
Packit Service 4684c1
{
Packit Service 4684c1
	gnutls_ext_priv_data_t epriv;
Packit Service 4684c1
	uint8_t p;
Packit Service 4684c1
Packit Service 4684c1
	if (_gnutls_hello_ext_get_priv
Packit Service 4684c1
	    (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0)
Packit Service 4684c1
		return 0;	/* nothing to send - not enabled */
Packit Service 4684c1
Packit Service 4684c1
	if (((intptr_t)epriv) & GNUTLS_HB_PEER_ALLOWED_TO_SEND)
Packit Service 4684c1
		p = 1;
Packit Service 4684c1
	else			/*if (epriv.num & GNUTLS_HB_PEER_NOT_ALLOWED_TO_SEND) */
Packit Service 4684c1
		p = 2;
Packit Service 4684c1
Packit Service 4684c1
	if (_gnutls_buffer_append_data(extdata, &p, 1) < 0)
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
Packit Service 4684c1
Packit Service 4684c1
	return 1;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static int
Packit Service 4684c1
_gnutls_heartbeat_pack(gnutls_ext_priv_data_t epriv, gnutls_buffer_st * ps)
Packit Service 4684c1
{
Packit Service 4684c1
	int ret;
Packit Service 4684c1
Packit Service 4684c1
	BUFFER_APPEND_NUM(ps, (intptr_t)epriv);
Packit Service 4684c1
Packit Service 4684c1
	return 0;
Packit Service 4684c1
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static int
Packit Service 4684c1
_gnutls_heartbeat_unpack(gnutls_buffer_st * ps,
Packit Service 4684c1
			 gnutls_ext_priv_data_t * _priv)
Packit Service 4684c1
{
Packit Service 4684c1
	gnutls_ext_priv_data_t epriv;
Packit Service 4684c1
	int ret;
Packit Service 4684c1
Packit Service 4684c1
	BUFFER_POP_CAST_NUM(ps, epriv);
Packit Service 4684c1
Packit Service 4684c1
	*_priv = epriv;
Packit Service 4684c1
Packit Service 4684c1
	ret = 0;
Packit Service 4684c1
      error:
Packit Service 4684c1
	return ret;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
const hello_ext_entry_st ext_mod_heartbeat = {
Packit Service 4684c1
	.name = "Heartbeat",
Packit Service 4684c1
	.tls_id = 15,
Packit Service 4684c1
	.gid = GNUTLS_EXTENSION_HEARTBEAT,
Packit Service 4684c1
	.client_parse_point = GNUTLS_EXT_TLS,
Packit Service 4684c1
	.server_parse_point = GNUTLS_EXT_TLS,
Packit Service 4684c1
	.validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO |
Packit Service 4684c1
		    GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO,
Packit Service 4684c1
	.recv_func = _gnutls_heartbeat_recv_params,
Packit Service 4684c1
	.send_func = _gnutls_heartbeat_send_params,
Packit Service 4684c1
	.pack_func = _gnutls_heartbeat_pack,
Packit Service 4684c1
	.unpack_func = _gnutls_heartbeat_unpack,
Packit Service 4684c1
	.deinit_func = NULL,
Packit Service 4684c1
	.cannot_be_overriden = 1
Packit Service 4684c1
};
Packit Service 4684c1
Packit Service 4684c1
#else
Packit Service 4684c1
void gnutls_heartbeat_enable(gnutls_session_t session, unsigned int type)
Packit Service 4684c1
{
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
unsigned gnutls_heartbeat_allowed(gnutls_session_t session, unsigned int type)
Packit Service 4684c1
{
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
int
Packit Service 4684c1
gnutls_heartbeat_ping(gnutls_session_t session, size_t data_size,
Packit Service 4684c1
		      unsigned int max_tries, unsigned int flags)
Packit Service 4684c1
{
Packit Service 4684c1
	return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
int gnutls_heartbeat_pong(gnutls_session_t session, unsigned int flags)
Packit Service 4684c1
{
Packit Service 4684c1
	return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
unsigned int gnutls_heartbeat_get_timeout(gnutls_session_t session)
Packit Service 4684c1
{
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
void gnutls_heartbeat_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
	return;
Packit Service 4684c1
}
Packit Service 4684c1
#endif