/src/gnutls/lib/ext/heartbeat.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2012,2013 Free Software Foundation, Inc. |
3 | | * Copyright (C) 2013 Nikos Mavrogiannopoulos |
4 | | * |
5 | | * Author: Nikos Mavrogiannopoulos |
6 | | * |
7 | | * This file is part of GnuTLS. |
8 | | * |
9 | | * The GnuTLS is free software; you can redistribute it and/or |
10 | | * modify it under the terms of the GNU Lesser General Public License |
11 | | * as published by the Free Software Foundation; either version 2.1 of |
12 | | * the License, or (at your option) any later version. |
13 | | * |
14 | | * This library is distributed in the hope that it will be useful, but |
15 | | * WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | | * Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public License |
20 | | * along with this program. If not, see <https://www.gnu.org/licenses/> |
21 | | * |
22 | | */ |
23 | | |
24 | | /* This file implements the TLS heartbeat extension. |
25 | | */ |
26 | | |
27 | | #include "errors.h" |
28 | | #include "gnutls_int.h" |
29 | | #include <dtls.h> |
30 | | #include <record.h> |
31 | | #include <ext/heartbeat.h> |
32 | | #include <hello_ext.h> |
33 | | #include <random.h> |
34 | | |
35 | | #ifdef ENABLE_HEARTBEAT |
36 | | /** |
37 | | * gnutls_heartbeat_enable: |
38 | | * @session: is a #gnutls_session_t type. |
39 | | * @type: one of the GNUTLS_HB_* flags |
40 | | * |
41 | | * If this function is called with the %GNUTLS_HB_PEER_ALLOWED_TO_SEND |
42 | | * @type, GnuTLS will allow heartbeat messages to be received. Moreover it also |
43 | | * request the peer to accept heartbeat messages. This function |
44 | | * must be called prior to TLS handshake. |
45 | | * |
46 | | * If the @type used is %GNUTLS_HB_LOCAL_ALLOWED_TO_SEND, then the peer |
47 | | * will be asked to accept heartbeat messages but not send ones. |
48 | | * |
49 | | * The function gnutls_heartbeat_allowed() can be used to test Whether |
50 | | * locally generated heartbeat messages can be accepted by the peer. |
51 | | * |
52 | | * Since: 3.1.2 |
53 | | **/ |
54 | | void gnutls_heartbeat_enable(gnutls_session_t session, unsigned int type) |
55 | | { |
56 | | gnutls_ext_priv_data_t epriv; |
57 | | |
58 | | epriv = (void *)(intptr_t) type; |
59 | | _gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_HEARTBEAT, epriv); |
60 | | } |
61 | | |
62 | | /** |
63 | | * gnutls_heartbeat_allowed: |
64 | | * @session: is a #gnutls_session_t type. |
65 | | * @type: one of %GNUTLS_HB_LOCAL_ALLOWED_TO_SEND and %GNUTLS_HB_PEER_ALLOWED_TO_SEND |
66 | | * |
67 | | * This function will check whether heartbeats are allowed |
68 | | * to be sent or received in this session. |
69 | | * |
70 | | * Returns: Non zero if heartbeats are allowed. |
71 | | * |
72 | | * Since: 3.1.2 |
73 | | **/ |
74 | | unsigned gnutls_heartbeat_allowed(gnutls_session_t session, unsigned int type) |
75 | | { |
76 | | gnutls_ext_priv_data_t epriv; |
77 | | |
78 | | if (session->internals.handshake_in_progress != 0) |
79 | | return 0; /* not allowed */ |
80 | | |
81 | | if (_gnutls_hello_ext_get_priv |
82 | | (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0) |
83 | | return 0; /* Not enabled */ |
84 | | |
85 | | if (type == GNUTLS_HB_LOCAL_ALLOWED_TO_SEND) { |
86 | | if (((intptr_t) epriv) & LOCAL_ALLOWED_TO_SEND) |
87 | | return 1; |
88 | | } else if (((intptr_t) epriv) & GNUTLS_HB_PEER_ALLOWED_TO_SEND) |
89 | | return 1; |
90 | | |
91 | | return 0; |
92 | | } |
93 | | |
94 | | # define DEFAULT_PADDING_SIZE 16 |
95 | | |
96 | | /* |
97 | | * Sends heartbeat data. |
98 | | */ |
99 | | static int |
100 | | heartbeat_send_data(gnutls_session_t session, const void *data, |
101 | | size_t data_size, uint8_t type) |
102 | | { |
103 | | int ret, pos; |
104 | | uint8_t *response; |
105 | | |
106 | | response = gnutls_malloc(1 + 2 + data_size + DEFAULT_PADDING_SIZE); |
107 | | if (response == NULL) |
108 | | return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
109 | | |
110 | | pos = 0; |
111 | | response[pos++] = type; |
112 | | |
113 | | _gnutls_write_uint16(data_size, &response[pos]); |
114 | | pos += 2; |
115 | | |
116 | | memcpy(&response[pos], data, data_size); |
117 | | pos += data_size; |
118 | | |
119 | | ret = |
120 | | gnutls_rnd(GNUTLS_RND_NONCE, &response[pos], DEFAULT_PADDING_SIZE); |
121 | | if (ret < 0) { |
122 | | gnutls_assert(); |
123 | | goto cleanup; |
124 | | } |
125 | | pos += DEFAULT_PADDING_SIZE; |
126 | | |
127 | | ret = |
128 | | _gnutls_send_int(session, GNUTLS_HEARTBEAT, -1, |
129 | | EPOCH_WRITE_CURRENT, response, pos, MBUFFER_FLUSH); |
130 | | |
131 | | cleanup: |
132 | | gnutls_free(response); |
133 | | return ret; |
134 | | } |
135 | | |
136 | | /** |
137 | | * gnutls_heartbeat_ping: |
138 | | * @session: is a #gnutls_session_t type. |
139 | | * @data_size: is the length of the ping payload. |
140 | | * @max_tries: if flags is %GNUTLS_HEARTBEAT_WAIT then this sets the number of retransmissions. Use zero for indefinite (until timeout). |
141 | | * @flags: if %GNUTLS_HEARTBEAT_WAIT then wait for pong or timeout instead of returning immediately. |
142 | | * |
143 | | * This function sends a ping to the peer. If the @flags is set |
144 | | * to %GNUTLS_HEARTBEAT_WAIT then it waits for a reply from the peer. |
145 | | * |
146 | | * Note that it is highly recommended to use this function with the |
147 | | * flag %GNUTLS_HEARTBEAT_WAIT, or you need to handle retransmissions |
148 | | * and timeouts manually. |
149 | | * |
150 | | * The total TLS data transmitted as part of the ping message are given by |
151 | | * the following formula: MAX(16, @data_size)+gnutls_record_overhead_size()+3. |
152 | | * |
153 | | * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. |
154 | | * |
155 | | * Since: 3.1.2 |
156 | | **/ |
157 | | int |
158 | | gnutls_heartbeat_ping(gnutls_session_t session, size_t data_size, |
159 | | unsigned int max_tries, unsigned int flags) |
160 | | { |
161 | | int ret; |
162 | | unsigned int retries = 1, diff; |
163 | | struct timespec now; |
164 | | |
165 | | if (data_size > MAX_HEARTBEAT_LENGTH) |
166 | | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
167 | | |
168 | | if (gnutls_heartbeat_allowed |
169 | | (session, GNUTLS_HB_LOCAL_ALLOWED_TO_SEND) == 0) |
170 | | return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); |
171 | | |
172 | | /* resume previous call if interrupted */ |
173 | | if (session->internals.record_send_buffer.byte_length > 0 && |
174 | | session->internals.record_send_buffer.head != NULL && |
175 | | session->internals.record_send_buffer.head->type == |
176 | | GNUTLS_HEARTBEAT) |
177 | | return _gnutls_io_write_flush(session); |
178 | | |
179 | | switch (session->internals.hb_state) { |
180 | | case SHB_SEND1: |
181 | | if (data_size > DEFAULT_PADDING_SIZE) |
182 | | data_size -= DEFAULT_PADDING_SIZE; |
183 | | else |
184 | | data_size = 0; |
185 | | |
186 | | _gnutls_buffer_reset(&session->internals.hb_local_data); |
187 | | |
188 | | ret = |
189 | | _gnutls_buffer_resize(&session->internals.hb_local_data, |
190 | | data_size); |
191 | | if (ret < 0) |
192 | | return gnutls_assert_val(ret); |
193 | | |
194 | | ret = |
195 | | gnutls_rnd(GNUTLS_RND_NONCE, |
196 | | session->internals.hb_local_data.data, |
197 | | data_size); |
198 | | if (ret < 0) |
199 | | return gnutls_assert_val(ret); |
200 | | |
201 | | gnutls_gettime(&session->internals.hb_ping_start); |
202 | | session->internals.hb_local_data.length = data_size; |
203 | | session->internals.hb_state = SHB_SEND2; |
204 | | |
205 | | FALLTHROUGH; |
206 | | case SHB_SEND2: |
207 | | session->internals.hb_actual_retrans_timeout_ms = |
208 | | session->internals.hb_retrans_timeout_ms; |
209 | | retry: |
210 | | ret = |
211 | | heartbeat_send_data(session, |
212 | | session->internals.hb_local_data.data, |
213 | | session->internals.hb_local_data.length, |
214 | | HEARTBEAT_REQUEST); |
215 | | if (ret < 0) |
216 | | return gnutls_assert_val(ret); |
217 | | |
218 | | gnutls_gettime(&session->internals.hb_ping_sent); |
219 | | |
220 | | if (!(flags & GNUTLS_HEARTBEAT_WAIT)) { |
221 | | session->internals.hb_state = SHB_SEND1; |
222 | | break; |
223 | | } |
224 | | |
225 | | session->internals.hb_state = SHB_RECV; |
226 | | FALLTHROUGH; |
227 | | |
228 | | case SHB_RECV: |
229 | | ret = |
230 | | _gnutls_recv_int(session, GNUTLS_HEARTBEAT, |
231 | | NULL, 0, NULL, |
232 | | session->internals. |
233 | | hb_actual_retrans_timeout_ms); |
234 | | if (ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED) { |
235 | | session->internals.hb_state = SHB_SEND1; |
236 | | break; |
237 | | } else if (ret == GNUTLS_E_TIMEDOUT) { |
238 | | retries++; |
239 | | if (max_tries > 0 && retries > max_tries) { |
240 | | session->internals.hb_state = SHB_SEND1; |
241 | | return gnutls_assert_val(ret); |
242 | | } |
243 | | |
244 | | gnutls_gettime(&now); |
245 | | diff = |
246 | | timespec_sub_ms(&now, |
247 | | &session->internals.hb_ping_start); |
248 | | if (diff > session->internals.hb_total_timeout_ms) { |
249 | | session->internals.hb_state = SHB_SEND1; |
250 | | return gnutls_assert_val(GNUTLS_E_TIMEDOUT); |
251 | | } |
252 | | |
253 | | session->internals.hb_actual_retrans_timeout_ms *= 2; |
254 | | session->internals.hb_actual_retrans_timeout_ms %= |
255 | | MAX_DTLS_TIMEOUT; |
256 | | |
257 | | session->internals.hb_state = SHB_SEND2; |
258 | | goto retry; |
259 | | } else if (ret < 0) { |
260 | | session->internals.hb_state = SHB_SEND1; |
261 | | return gnutls_assert_val(ret); |
262 | | } |
263 | | } |
264 | | |
265 | | return 0; |
266 | | } |
267 | | |
268 | | /** |
269 | | * gnutls_heartbeat_pong: |
270 | | * @session: is a #gnutls_session_t type. |
271 | | * @flags: should be zero |
272 | | * |
273 | | * This function replies to a ping by sending a pong to the peer. |
274 | | * |
275 | | * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. |
276 | | * |
277 | | * Since: 3.1.2 |
278 | | **/ |
279 | | int gnutls_heartbeat_pong(gnutls_session_t session, unsigned int flags) |
280 | | { |
281 | | int ret; |
282 | | |
283 | | if (session->internals.record_send_buffer.byte_length > 0 && |
284 | | session->internals.record_send_buffer.head != NULL && |
285 | | session->internals.record_send_buffer.head->type == |
286 | | GNUTLS_HEARTBEAT) |
287 | | return _gnutls_io_write_flush(session); |
288 | | |
289 | | if (session->internals.hb_remote_data.length == 0) |
290 | | return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); |
291 | | |
292 | | ret = |
293 | | heartbeat_send_data(session, |
294 | | session->internals.hb_remote_data.data, |
295 | | session->internals.hb_remote_data.length, |
296 | | HEARTBEAT_RESPONSE); |
297 | | |
298 | | _gnutls_buffer_reset(&session->internals.hb_remote_data); |
299 | | |
300 | | if (ret < 0) |
301 | | return gnutls_assert_val(ret); |
302 | | |
303 | | return 0; |
304 | | } |
305 | | |
306 | | /* |
307 | | * Processes a heartbeat message. |
308 | | */ |
309 | | int _gnutls_heartbeat_handle(gnutls_session_t session, mbuffer_st * bufel) |
310 | | { |
311 | | int ret; |
312 | | unsigned type; |
313 | | unsigned pos; |
314 | | uint8_t *msg = _mbuffer_get_udata_ptr(bufel); |
315 | | size_t hb_len, len = _mbuffer_get_udata_size(bufel); |
316 | | |
317 | | if (gnutls_heartbeat_allowed |
318 | | (session, GNUTLS_HB_PEER_ALLOWED_TO_SEND) == 0) |
319 | | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); |
320 | | |
321 | | if (len < 3 + DEFAULT_PADDING_SIZE) |
322 | | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
323 | | |
324 | | pos = 0; |
325 | | type = msg[pos++]; |
326 | | |
327 | | hb_len = _gnutls_read_uint16(&msg[pos]); |
328 | | if (hb_len > len - 3 - DEFAULT_PADDING_SIZE) |
329 | | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
330 | | |
331 | | pos += 2; |
332 | | |
333 | | switch (type) { |
334 | | case HEARTBEAT_REQUEST: |
335 | | _gnutls_buffer_reset(&session->internals.hb_remote_data); |
336 | | |
337 | | ret = |
338 | | _gnutls_buffer_resize(&session->internals.hb_remote_data, |
339 | | hb_len); |
340 | | if (ret < 0) |
341 | | return gnutls_assert_val(ret); |
342 | | |
343 | | if (hb_len > 0) |
344 | | memcpy(session->internals.hb_remote_data.data, |
345 | | &msg[pos], hb_len); |
346 | | session->internals.hb_remote_data.length = hb_len; |
347 | | |
348 | | return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PING_RECEIVED); |
349 | | |
350 | | case HEARTBEAT_RESPONSE: |
351 | | |
352 | | if (hb_len != session->internals.hb_local_data.length) |
353 | | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); |
354 | | |
355 | | if (hb_len > 0 && |
356 | | memcmp(&msg[pos], |
357 | | session->internals.hb_local_data.data, |
358 | | hb_len) != 0) { |
359 | | if (IS_DTLS(session)) |
360 | | return gnutls_assert_val(GNUTLS_E_AGAIN); /* ignore it */ |
361 | | else |
362 | | return |
363 | | gnutls_assert_val |
364 | | (GNUTLS_E_UNEXPECTED_PACKET); |
365 | | } |
366 | | |
367 | | _gnutls_buffer_reset(&session->internals.hb_local_data); |
368 | | |
369 | | return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PONG_RECEIVED); |
370 | | default: |
371 | | _gnutls_record_log |
372 | | ("REC[%p]: HB: received unknown type %u\n", session, type); |
373 | | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); |
374 | | } |
375 | | } |
376 | | |
377 | | /** |
378 | | * gnutls_heartbeat_get_timeout: |
379 | | * @session: is a #gnutls_session_t type. |
380 | | * |
381 | | * This function will return the milliseconds remaining |
382 | | * for a retransmission of the previously sent ping |
383 | | * message. This function is useful when ping is used in |
384 | | * non-blocking mode, to estimate when to call gnutls_heartbeat_ping() |
385 | | * if no packets have been received. |
386 | | * |
387 | | * Returns: the remaining time in milliseconds. |
388 | | * |
389 | | * Since: 3.1.2 |
390 | | **/ |
391 | | unsigned int gnutls_heartbeat_get_timeout(gnutls_session_t session) |
392 | | { |
393 | | struct timespec now; |
394 | | unsigned int diff; |
395 | | |
396 | | gnutls_gettime(&now); |
397 | | diff = timespec_sub_ms(&now, &session->internals.hb_ping_sent); |
398 | | if (diff >= session->internals.hb_actual_retrans_timeout_ms) |
399 | | return 0; |
400 | | else |
401 | | return session->internals.hb_actual_retrans_timeout_ms - diff; |
402 | | } |
403 | | |
404 | | /** |
405 | | * gnutls_heartbeat_set_timeouts: |
406 | | * @session: is a #gnutls_session_t type. |
407 | | * @retrans_timeout: The time at which a retransmission will occur in milliseconds |
408 | | * @total_timeout: The time at which the connection will be aborted, in milliseconds. |
409 | | * |
410 | | * This function will override the timeouts for the DTLS heartbeat |
411 | | * protocol. The retransmission timeout is the time after which a |
412 | | * message from the peer is not received, the previous request will |
413 | | * be retransmitted. The total timeout is the time after which the |
414 | | * handshake will be aborted with %GNUTLS_E_TIMEDOUT. |
415 | | * |
416 | | * Since: 3.1.2 |
417 | | **/ |
418 | | void gnutls_heartbeat_set_timeouts(gnutls_session_t session, |
419 | | unsigned int retrans_timeout, |
420 | | unsigned int total_timeout) |
421 | | { |
422 | | session->internals.hb_retrans_timeout_ms = retrans_timeout; |
423 | | session->internals.hb_total_timeout_ms = total_timeout; |
424 | | } |
425 | | |
426 | | static int |
427 | | _gnutls_heartbeat_recv_params(gnutls_session_t session, |
428 | | const uint8_t * data, size_t _data_size) |
429 | | { |
430 | | unsigned policy; |
431 | | gnutls_ext_priv_data_t epriv; |
432 | | |
433 | | if (_gnutls_hello_ext_get_priv |
434 | | (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0) { |
435 | | if (session->security_parameters.entity == GNUTLS_CLIENT) |
436 | | return |
437 | | gnutls_assert_val |
438 | | (GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); |
439 | | return 0; /* Not enabled */ |
440 | | } |
441 | | |
442 | | if (_data_size == 0) |
443 | | return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; |
444 | | |
445 | | policy = (intptr_t) epriv; |
446 | | |
447 | | if (data[0] == 1) |
448 | | policy |= LOCAL_ALLOWED_TO_SEND; |
449 | | else if (data[0] == 2) |
450 | | policy |= LOCAL_NOT_ALLOWED_TO_SEND; |
451 | | else |
452 | | return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); |
453 | | |
454 | | epriv = (void *)(intptr_t) policy; |
455 | | _gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_HEARTBEAT, epriv); |
456 | | |
457 | | return 0; |
458 | | } |
459 | | |
460 | | static int |
461 | | _gnutls_heartbeat_send_params(gnutls_session_t session, |
462 | | gnutls_buffer_st * extdata) |
463 | | { |
464 | | gnutls_ext_priv_data_t epriv; |
465 | | uint8_t p; |
466 | | |
467 | | if (_gnutls_hello_ext_get_priv |
468 | | (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0) |
469 | | return 0; /* nothing to send - not enabled */ |
470 | | |
471 | | if (((intptr_t) epriv) & GNUTLS_HB_PEER_ALLOWED_TO_SEND) |
472 | | p = 1; |
473 | | else /*if (epriv.num & GNUTLS_HB_PEER_NOT_ALLOWED_TO_SEND) */ |
474 | | p = 2; |
475 | | |
476 | | if (_gnutls_buffer_append_data(extdata, &p, 1) < 0) |
477 | | return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
478 | | |
479 | | return 1; |
480 | | } |
481 | | |
482 | | static int |
483 | | _gnutls_heartbeat_pack(gnutls_ext_priv_data_t epriv, gnutls_buffer_st * ps) |
484 | | { |
485 | | int ret; |
486 | | |
487 | | BUFFER_APPEND_NUM(ps, (intptr_t) epriv); |
488 | | |
489 | | return 0; |
490 | | |
491 | | } |
492 | | |
493 | | static int |
494 | | _gnutls_heartbeat_unpack(gnutls_buffer_st * ps, gnutls_ext_priv_data_t * _priv) |
495 | | { |
496 | | gnutls_ext_priv_data_t epriv; |
497 | | int ret; |
498 | | |
499 | | BUFFER_POP_CAST_NUM(ps, epriv); |
500 | | |
501 | | *_priv = epriv; |
502 | | |
503 | | ret = 0; |
504 | | error: |
505 | | return ret; |
506 | | } |
507 | | |
508 | | const hello_ext_entry_st ext_mod_heartbeat = { |
509 | | .name = "Heartbeat", |
510 | | .tls_id = 15, |
511 | | .gid = GNUTLS_EXTENSION_HEARTBEAT, |
512 | | .client_parse_point = GNUTLS_EXT_TLS, |
513 | | .server_parse_point = GNUTLS_EXT_TLS, |
514 | | .validity = |
515 | | GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | |
516 | | GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_EE | |
517 | | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, |
518 | | .recv_func = _gnutls_heartbeat_recv_params, |
519 | | .send_func = _gnutls_heartbeat_send_params, |
520 | | .pack_func = _gnutls_heartbeat_pack, |
521 | | .unpack_func = _gnutls_heartbeat_unpack, |
522 | | .deinit_func = NULL, |
523 | | .cannot_be_overriden = 1 |
524 | | }; |
525 | | |
526 | | #else |
527 | | void gnutls_heartbeat_enable(gnutls_session_t session, unsigned int type) |
528 | 0 | { |
529 | 0 | } |
530 | | |
531 | | unsigned gnutls_heartbeat_allowed(gnutls_session_t session, unsigned int type) |
532 | 0 | { |
533 | 0 | return 0; |
534 | 0 | } |
535 | | |
536 | | int |
537 | | gnutls_heartbeat_ping(gnutls_session_t session, size_t data_size, |
538 | | unsigned int max_tries, unsigned int flags) |
539 | 0 | { |
540 | 0 | return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); |
541 | 0 | } |
542 | | |
543 | | int gnutls_heartbeat_pong(gnutls_session_t session, unsigned int flags) |
544 | 0 | { |
545 | 0 | return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); |
546 | 0 | } |
547 | | |
548 | | unsigned int gnutls_heartbeat_get_timeout(gnutls_session_t session) |
549 | 0 | { |
550 | 0 | return 0; |
551 | 0 | } |
552 | | |
553 | | void gnutls_heartbeat_set_timeouts(gnutls_session_t session, |
554 | | unsigned int retrans_timeout, |
555 | | unsigned int total_timeout) |
556 | 0 | { |
557 | 0 | return; |
558 | 0 | } |
559 | | #endif |