corrected copyright notices
[gnutls.git] / lib / ext / heartbeat.c
blob89d01202b754eed0534959891f6f2b48fe6af6cd
1 /*
2 * Copyright (C) 2012 Free Software Foundation, Inc.
4 * Author: Olga Smolenchuk
6 * This file is part of GnuTLS.
8 * The GnuTLS is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 3 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
23 #include <gnutls_errors.h>
24 #include <gnutls_int.h>
25 #include <gnutls_dtls.h>
26 #include <gnutls_record.h>
27 #include <ext/heartbeat.h>
28 #include <gnutls_extensions.h>
29 #include <random.h>
31 /**
32 * gnutls_heartbeat_enable:
33 * @session: is a #gnutls_session_t structure.
34 * @type: one of the GNUTLS_HB_* flags
36 * This function will allow heartbeat messages to be
37 * received.
39 * Since: 3.1.2
40 **/
41 void
42 gnutls_heartbeat_enable (gnutls_session_t session, unsigned int type)
44 extension_priv_data_t epriv;
46 epriv.num = type;
47 _gnutls_ext_set_session_data (session, GNUTLS_EXTENSION_HEARTBEAT,
48 epriv);
51 /*-
52 * Convenience helper:
54 * Returns:
55 * textual policy description or NULL.
56 -*/
57 static inline const char *
58 _gnutls_heartbeat (unsigned policy)
60 if (policy & GNUTLS_HB_PEER_ALLOWED_TO_SEND)
61 return "PEER ALLOWED TO SEND";
62 else if (policy & GNUTLS_HB_PEER_NOT_ALLOWED_TO_SEND)
63 return "PEER NOT ALLOWED TO SEND";
64 return "Unknown policy";
67 /**
68 * gnutls_heartbeat_allowed:
69 * @session: is a #gnutls_session_t structure.
70 * @type: one of %GNUTLS_HB_LOCAL_ALLOWED_TO_SEND and %GNUTLS_HB_PEER_ALLOWED_TO_SEND
72 * This function will check whether heartbeats are allowed
73 * to be sent or received in this session.
75 * Returns: Non zero if heartbeats are allowed.
77 * Since: 3.1.2
78 **/
79 int
80 gnutls_heartbeat_allowed (gnutls_session_t session, unsigned int type)
82 extension_priv_data_t epriv;
84 if (_gnutls_ext_get_session_data
85 (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0)
86 return 0; /* Not enabled */
88 if (type == GNUTLS_HB_LOCAL_ALLOWED_TO_SEND)
90 if (epriv.num & LOCAL_ALLOWED_TO_SEND)
91 return 1;
93 else if (epriv.num & GNUTLS_HB_PEER_ALLOWED_TO_SEND)
94 return 1;
96 return 0;
99 static int
100 heartbeat_allow_recv (gnutls_session_t session)
102 return gnutls_heartbeat_allowed(session, GNUTLS_HB_PEER_ALLOWED_TO_SEND);
105 static int
106 heartbeat_allow_send (gnutls_session_t session)
108 return gnutls_heartbeat_allowed(session, GNUTLS_HB_LOCAL_ALLOWED_TO_SEND);
111 #define DEFAULT_PAYLOAD_SIZE 16
114 * heartbeat_send_data:
115 * @session: is a #gnutls_session_t structure.
116 * @data: contains the data to send.
117 * @data_size: is the length of the data.
118 * @start_timer: true if the heartbeat timer is to be started.
120 * This function has the similar semantics with gnutls_record_send() The only
121 * difference is that it uses GNUTLS_HEARTBEAT content type.
123 * This function send either HeartBeat request or response message
124 * with proper padding. It set timeout and timestamp without check - it's up to
125 * caller to make sure no messages are already in-flight and handle timeout expiration.
127 * Returns: The number of bytes sent, or a negative error code. The
128 * number of bytes sent might be less than @data_size. The maximum
129 * number of bytes this function can send in a single call depends
130 * on the negotiated maximum record size.
132 static int
133 heartbeat_send_data (gnutls_session_t session, const void *data,
134 size_t data_size, uint8_t type)
136 int ret;
137 gnutls_buffer_st response;
138 uint8_t payload[DEFAULT_PAYLOAD_SIZE];
139 _gnutls_buffer_init (&response);
141 ret = gnutls_rnd (GNUTLS_RND_RANDOM, payload, DEFAULT_PAYLOAD_SIZE);
142 if (ret < 0)
143 return gnutls_assert_val(ret);
145 BUFFER_APPEND (&response, &type, 1);
146 BUFFER_APPEND_PFX2 (&response, data, data_size);
148 BUFFER_APPEND (&response, payload, DEFAULT_PAYLOAD_SIZE);
149 ret = _gnutls_send_int (session, GNUTLS_HEARTBEAT, -1,
150 EPOCH_WRITE_CURRENT, response.data,
151 response.length, MBUFFER_FLUSH);
153 _gnutls_record_log ("REC[%p]: HB sent: %d\n", session, ret);
155 _gnutls_buffer_clear (&response);
156 return ret;
160 * gnutls_heartbeat_ping:
161 * @session: is a #gnutls_session_t structure.
162 * @data_size: is the length of the ping payload.
163 * @max_tries: if flags is %GNUTLS_HEARTBEAT_WAIT then this sets the number of retransmissions. Use zero for indefinite (until timeout).
164 * @flags: if %GNUTLS_HEARTBEAT_WAIT then wait for pong or timeout instead of returning immediately.
166 * This function sends a ping to the peer. If the @flags is set
167 * to %GNUTLS_HEARTBEAT_WAIT then it waits for a reply from the peer.
169 * Note that it is highly recommended to use this function with the
170 * flag %GNUTLS_HEARTBEAT_WAIT, or you need to handle retransmissions
171 * and timeouts manually.
173 * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
175 * Since: 3.1.2
178 gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size,
179 unsigned int max_tries, unsigned int flags)
181 int ret;
182 unsigned int retries = 1, diff;
183 struct timespec now;
185 if (data_size > MAX_HEARTBEAT_LENGTH)
186 return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
188 if (!heartbeat_allow_send (session))
189 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
191 switch(session->internals.hb_state)
193 case SHB_SEND1:
194 _gnutls_record_log
195 ("REC[%p]: sending HB_REQUEST with length: %zu to peer\n", session, data_size);
197 if (data_size > DEFAULT_PAYLOAD_SIZE)
198 data_size -= DEFAULT_PAYLOAD_SIZE;
199 else
200 data_size = 0;
202 _gnutls_buffer_reset(&session->internals.hb_local_data);
204 ret = _gnutls_buffer_resize (&session->internals.hb_local_data, data_size);
205 if (ret < 0)
206 return gnutls_assert_val(ret);
208 ret = _gnutls_rnd (GNUTLS_RND_NONCE, session->internals.hb_local_data.data, data_size);
209 if (ret < 0)
210 return gnutls_assert_val(ret);
212 gettime (&session->internals.hb_ping_start);
213 session->internals.hb_local_data.length = data_size;
214 session->internals.hb_state = SHB_SEND2;
215 case SHB_SEND2:
216 session->internals.hb_actual_retrans_timeout_ms = session->internals.hb_retrans_timeout_ms;
217 retry:
218 ret = heartbeat_send_data (session, session->internals.hb_local_data.data,
219 session->internals.hb_local_data.length,
220 HEARTBEAT_REQUEST);
221 if (ret < 0)
222 return gnutls_assert_val(ret);
224 gettime (&session->internals.hb_ping_sent);
226 if (!(flags & GNUTLS_HEARTBEAT_WAIT))
228 session->internals.hb_state = SHB_SEND1;
229 break;
232 session->internals.hb_state = SHB_RECV;
234 case SHB_RECV:
235 ret = _gnutls_recv_int(session, GNUTLS_HEARTBEAT, -1, NULL, 0, NULL, session->internals.hb_actual_retrans_timeout_ms);
236 if (ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED)
238 session->internals.hb_state = SHB_SEND1;
239 break;
241 else if (ret == GNUTLS_E_TIMEDOUT)
243 retries++;
244 if (max_tries > 0 && retries > max_tries)
246 session->internals.hb_state = SHB_SEND1;
247 return gnutls_assert_val(ret);
250 gettime(&now);
251 diff = _dtls_timespec_sub_ms(&now, &session->internals.hb_ping_start);
252 if (diff > session->internals.hb_total_timeout_ms)
254 session->internals.hb_state = SHB_SEND1;
255 return gnutls_assert_val(GNUTLS_E_TIMEDOUT);
258 session->internals.hb_actual_retrans_timeout_ms *= 2;
259 session->internals.hb_actual_retrans_timeout_ms %= MAX_DTLS_TIMEOUT;
261 session->internals.hb_state = SHB_SEND2;
262 goto retry;
264 else if (ret < 0)
266 session->internals.hb_state = SHB_SEND1;
267 return gnutls_assert_val(ret);
271 return 0;
275 * gnutls_heartbeat_pong:
276 * @session: is a #gnutls_session_t structure.
277 * @flags: should be zero
279 * This function replies to a ping by sending a pong to the peer.
281 * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
283 * Since: 3.1.2
286 gnutls_heartbeat_pong (gnutls_session_t session, unsigned int flags)
288 int ret;
290 if (session->internals.hb_remote_data.length == 0)
291 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
293 ret = heartbeat_send_data (session, session->internals.hb_remote_data.data,
294 session->internals.hb_remote_data.length,
295 HEARTBEAT_RESPONSE);
297 _gnutls_buffer_reset (&session->internals.hb_remote_data);
298 return ret;
302 * Process HB message in buffer.
303 * @bufel: the (suspected) HeartBeat message (TLV+padding)
305 * Returns:
306 * processing result
307 * GNUTLS_E_AGAIN if processed OK
308 * GNUTLS_E_HEARTBEAT_PONG_FAILED on response send failure for request message
309 * GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER on payload mismatch for response message
312 _gnutls_heartbeat_handle (gnutls_session_t session, mbuffer_st * bufel)
314 char pr[128];
315 int ret;
316 uint8_t *msg = _mbuffer_get_udata_ptr (bufel);
317 size_t hb_len, len = _mbuffer_get_udata_size (bufel);
319 if (!heartbeat_allow_recv (session))
320 return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET);
322 if (len < 4)
323 return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
325 hb_len = _gnutls_read_uint16 (msg + 1);
326 if (hb_len > len - 3)
327 return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET);
329 switch (msg[0])
331 case HEARTBEAT_REQUEST:
332 _gnutls_record_log
333 ("REC[%p]: received HEARTBEAT_REQUEST\n", session);
335 _gnutls_buffer_reset(&session->internals.hb_remote_data);
337 ret = _gnutls_buffer_resize (&session->internals.hb_remote_data, hb_len);
338 if (ret < 0)
339 return gnutls_assert_val(ret);
341 if (hb_len > 0)
342 memcpy(session->internals.hb_remote_data.data, msg+3, hb_len);
343 session->internals.hb_remote_data.length = hb_len;
345 return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PING_RECEIVED);
347 case HEARTBEAT_RESPONSE:
349 if (hb_len != session->internals.hb_local_data.length)
350 return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET);
352 if (hb_len > 0 && memcmp (msg + 3, session->internals.hb_local_data.data,
353 hb_len) != 0)
355 _gnutls_record_log ("REC[%p]: HB: %s - received\n", session,
356 _gnutls_bin2hex (msg + 3, hb_len, pr,
357 sizeof (pr), NULL));
358 return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET);
361 _gnutls_buffer_reset (&session->internals.hb_local_data);
363 return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PONG_RECEIVED);
364 default:
365 _gnutls_record_log
366 ("REC[%p]: HB: received unknown type %u\n",
367 session, msg[0]);
368 return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET);
373 * gnutls_heartbeat_get_timeout:
374 * @session: is a #gnutls_session_t structure.
376 * This function will return the milliseconds remaining
377 * for a retransmission of the previously sent ping
378 * message. This function is useful when ping is used in
379 * non-blocking mode, to estimate when to call gnutls_heartbeat_ping()
380 * if no packets have been received.
382 * Returns: the remaining time in milliseconds.
384 * Since: 3.1.2
386 unsigned int gnutls_heartbeat_get_timeout (gnutls_session_t session)
388 struct timespec now;
389 unsigned int diff;
391 gettime(&now);
392 diff = _dtls_timespec_sub_ms(&now, &session->internals.hb_ping_sent);
393 if (diff >= session->internals.hb_actual_retrans_timeout_ms)
394 return 0;
395 else
396 return session->internals.hb_actual_retrans_timeout_ms - diff;
400 * gnutls_heartbeat_set_timeouts:
401 * @session: is a #gnutls_session_t structure.
402 * @retrans_timeout: The time at which a retransmission will occur in milliseconds
403 * @total_timeout: The time at which the connection will be aborted, in milliseconds.
405 * This function will set the timeouts required for the DTLS handshake
406 * protocol. The retransmission timeout is the time after which a
407 * message from the peer is not received, the previous messages will
408 * be retransmitted. The total timeout is the time after which the
409 * handshake will be aborted with %GNUTLS_E_TIMEDOUT.
411 * The DTLS protocol recommends the values of 1 sec and 60 seconds
412 * respectively.
414 * If the retransmission timeout is zero then the handshake will operate
415 * in a non-blocking way, i.e., return %GNUTLS_E_AGAIN.
417 * Since: 3.1.2
419 void gnutls_heartbeat_set_timeouts (gnutls_session_t session, unsigned int retrans_timeout,
420 unsigned int total_timeout)
422 session->internals.hb_retrans_timeout_ms = retrans_timeout;
423 session->internals.hb_total_timeout_ms = total_timeout;
427 static int
428 _gnutls_heartbeat_recv_params (gnutls_session_t session,
429 const uint8_t * data, size_t _data_size)
431 heartbeat_policy_t pol;
432 extension_priv_data_t epriv;
434 if (_gnutls_ext_get_session_data
435 (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0)
437 if (session->security_parameters.entity == GNUTLS_CLIENT)
438 return gnutls_assert_val (GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
439 return 0; /* Not enabled */
442 if (_data_size == 0)
443 return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
445 _gnutls_debug_log ("HB: received parameter %u (%zu bytes)\n",
446 (unsigned)data[0], _data_size);
448 pol = epriv.num;
450 switch (data[0])
452 case 1:
453 pol |= LOCAL_ALLOWED_TO_SEND;
454 break;
455 case 2:
456 pol |= LOCAL_NOT_ALLOWED_TO_SEND;
457 break;
458 default:
459 return gnutls_assert_val (GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
462 epriv.num = pol;
463 _gnutls_ext_set_session_data (session, GNUTLS_EXTENSION_HEARTBEAT,
464 epriv);
466 return 0;
469 static int
470 _gnutls_heartbeat_send_params (gnutls_session_t session,
471 gnutls_buffer_st * extdata)
473 extension_priv_data_t epriv;
474 uint8_t p;
476 if (_gnutls_ext_get_session_data
477 (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0)
478 return 0; /* nothing to send - not enabled */
480 if (epriv.num & GNUTLS_HB_PEER_ALLOWED_TO_SEND)
481 p = 1;
482 else /*if (epriv.num & GNUTLS_HB_PEER_NOT_ALLOWED_TO_SEND)*/
483 p = 2;
485 _gnutls_debug_log ("HB: sending parameter %u\n", (unsigned)p);
486 if (_gnutls_buffer_append_data (extdata, &p, 1) < 0)
487 return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
489 return 1; /* number of bytes added for sending */
492 static int
493 _gnutls_heartbeat_pack (extension_priv_data_t _priv, gnutls_buffer_st * ps)
495 int ret;
496 BUFFER_APPEND_NUM (ps, _priv.num);
497 return GNUTLS_E_SUCCESS;
500 static int
501 _gnutls_heartbeat_unpack (gnutls_buffer_st * ps,
502 extension_priv_data_t * _priv)
504 int ret;
505 extension_priv_data_t epriv;
506 BUFFER_POP_NUM (ps, epriv.num);
507 *_priv = epriv;
508 ret = 0;
509 error:
510 return ret;
514 extension_entry_st ext_mod_heartbeat = {
515 .name = "HEARTBEAT",
516 .type = GNUTLS_EXTENSION_HEARTBEAT,
517 .parse_type = GNUTLS_EXT_TLS,
519 .recv_func = _gnutls_heartbeat_recv_params,
520 .send_func = _gnutls_heartbeat_send_params,
521 .pack_func = _gnutls_heartbeat_pack,
522 .unpack_func = _gnutls_heartbeat_unpack,
523 .deinit_func = NULL