|
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
|