1 /*
2 * Copyright (C) 2000-2012 Free Software Foundation, Inc.
3 *
4 * Author: Nikos Mavrogiannopoulos
5 *
6 * This file is part of GnuTLS.
7 *
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 2.1 of
11 * the License, or (at your option) any later version.
12 *
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.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>
20 *
21 */
22
23 /*
24 * This file holds all the buffering code used in gnutls.
25 * The buffering code works as:
26 *
27 * RECORD LAYER:
28 * 1. uses a buffer to hold data (application/handshake),
29 * we got but they were not requested, yet.
30 * (see gnutls_record_buffer_put(), gnutls_record_buffer_get_size() etc.)
31 *
32 * 2. uses a buffer to hold data that were incomplete (ie the read/write
33 * was interrupted)
34 * (see _gnutls_io_read_buffered(), _gnutls_io_write_buffered() etc.)
35 *
36 * HANDSHAKE LAYER:
37 * 1. Uses buffer to hold the last received handshake message.
38 * (see _gnutls_handshake_hash_buffer_put() etc.)
39 *
40 */
41
42 #include "gnutls_int.h"
43 #include "errors.h"
44 #include <num.h>
45 #include <record.h>
46 #include <buffers.h>
47 #include <mbuffers.h>
48 #include <state.h>
49 #include <dtls.h>
50 #include <system.h>
51 #include <constate.h> /* gnutls_epoch_get */
52 #include <handshake.h> /* remaining_time() */
53 #include <errno.h>
54 #include <system.h>
55 #include "debug.h"
56
57 #ifndef EAGAIN
58 #define EAGAIN EWOULDBLOCK
59 #endif
60
61 /* this is the maximum number of messages allowed to queue.
62 */
63 #define MAX_QUEUE 32
64
65 /* Buffers received packets of type APPLICATION DATA,
66 * HANDSHAKE DATA and HEARTBEAT.
67 */
68 void
_gnutls_record_buffer_put(gnutls_session_t session,content_type_t type,uint64_t seq,mbuffer_st * bufel)69 _gnutls_record_buffer_put(gnutls_session_t session,
70 content_type_t type, uint64_t seq,
71 mbuffer_st * bufel)
72 {
73
74 bufel->type = type;
75 bufel->record_sequence = seq;
76
77 _mbuffer_enqueue(&session->internals.record_buffer, bufel);
78 _gnutls_buffers_log("BUF[REC]: Inserted %d bytes of Data(%d)\n",
79 (int) bufel->msg.size, (int) type);
80
81 return;
82 }
83
84 /**
85 * gnutls_record_check_pending:
86 * @session: is a #gnutls_session_t type.
87 *
88 * This function checks if there are unread data
89 * in the gnutls buffers. If the return value is
90 * non-zero the next call to gnutls_record_recv()
91 * is guaranteed not to block.
92 *
93 * Returns: Returns the size of the data or zero.
94 **/
gnutls_record_check_pending(gnutls_session_t session)95 size_t gnutls_record_check_pending(gnutls_session_t session)
96 {
97 return _gnutls_record_buffer_get_size(session);
98 }
99
100 /**
101 * gnutls_record_check_corked:
102 * @session: is a #gnutls_session_t type.
103 *
104 * This function checks if there pending corked
105 * data in the gnutls buffers --see gnutls_record_cork().
106 *
107 * Returns: Returns the size of the corked data or zero.
108 *
109 * Since: 3.2.8
110 **/
gnutls_record_check_corked(gnutls_session_t session)111 size_t gnutls_record_check_corked(gnutls_session_t session)
112 {
113 return session->internals.record_presend_buffer.length;
114 }
115
116 int
_gnutls_record_buffer_get(content_type_t type,gnutls_session_t session,uint8_t * data,size_t length,uint8_t seq[8])117 _gnutls_record_buffer_get(content_type_t type,
118 gnutls_session_t session, uint8_t * data,
119 size_t length, uint8_t seq[8])
120 {
121 gnutls_datum_t msg;
122 mbuffer_st *bufel;
123
124 if (length == 0 || data == NULL) {
125 gnutls_assert();
126 return GNUTLS_E_INVALID_REQUEST;
127 }
128
129 bufel =
130 _mbuffer_head_get_first(&session->internals.record_buffer,
131 &msg);
132 if (bufel == NULL)
133 return
134 gnutls_assert_val
135 (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
136
137 if (type != bufel->type) {
138 if (IS_DTLS(session))
139 _gnutls_audit_log(session,
140 "Discarded unexpected %s (%d) packet (expecting: %s (%d))\n",
141 _gnutls_packet2str(bufel->type),
142 (int) bufel->type,
143 _gnutls_packet2str(type),
144 (int) type);
145 else
146 _gnutls_debug_log("received unexpected packet: %s(%d)\n",
147 _gnutls_packet2str(bufel->type), (int)bufel->type);
148
149 _mbuffer_head_remove_bytes(&session->internals.
150 record_buffer, msg.size);
151 return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
152 }
153
154 if (msg.size <= length)
155 length = msg.size;
156
157 if (seq)
158 _gnutls_write_uint64(bufel->record_sequence, seq);
159
160 memcpy(data, msg.data, length);
161 _mbuffer_head_remove_bytes(&session->internals.record_buffer,
162 length);
163
164 return length;
165 }
166
167 int
_gnutls_record_buffer_get_packet(content_type_t type,gnutls_session_t session,gnutls_packet_t * packet)168 _gnutls_record_buffer_get_packet(content_type_t type, gnutls_session_t session, gnutls_packet_t *packet)
169 {
170 mbuffer_st *bufel;
171
172 bufel =
173 _mbuffer_head_pop_first(&session->internals.record_buffer);
174 if (bufel == NULL)
175 return
176 gnutls_assert_val
177 (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
178
179 if (type != bufel->type) {
180 if (IS_DTLS(session))
181 _gnutls_audit_log(session,
182 "Discarded unexpected %s (%d) packet (expecting: %s)\n",
183 _gnutls_packet2str(bufel->type),
184 (int) bufel->type,
185 _gnutls_packet2str(type));
186 _mbuffer_head_remove_bytes(&session->internals.
187 record_buffer, bufel->msg.size);
188 return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
189 }
190
191 *packet = bufel;
192
193 return bufel->msg.size - bufel->mark;
194 }
195
reset_errno(gnutls_session_t session)196 inline static void reset_errno(gnutls_session_t session)
197 {
198 session->internals.errnum = 0;
199 }
200
get_errno(gnutls_session_t session)201 inline static int get_errno(gnutls_session_t session)
202 {
203 int ret;
204
205 if (session->internals.errnum != 0)
206 ret = session->internals.errnum;
207 else
208 ret =
209 session->internals.errno_func(session->internals.
210 transport_recv_ptr);
211 return ret;
212 }
213
214 inline static
errno_to_gerr(int err,unsigned dtls)215 int errno_to_gerr(int err, unsigned dtls)
216 {
217 switch (err) {
218 case EAGAIN:
219 return GNUTLS_E_AGAIN;
220 case EINTR:
221 return GNUTLS_E_INTERRUPTED;
222 case EMSGSIZE:
223 if (dtls != 0)
224 return GNUTLS_E_LARGE_PACKET;
225 else
226 return GNUTLS_E_PUSH_ERROR;
227 case ECONNRESET:
228 return GNUTLS_E_PREMATURE_TERMINATION;
229 default:
230 gnutls_assert();
231 return GNUTLS_E_PUSH_ERROR;
232 }
233 }
234
235 static ssize_t
_gnutls_dgram_read(gnutls_session_t session,mbuffer_st ** bufel,gnutls_pull_func pull_func,unsigned int * ms)236 _gnutls_dgram_read(gnutls_session_t session, mbuffer_st ** bufel,
237 gnutls_pull_func pull_func, unsigned int *ms)
238 {
239 ssize_t i, ret;
240 uint8_t *ptr;
241 struct timespec t1, t2;
242 size_t max_size, recv_size;
243 gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;
244 unsigned int diff;
245
246 max_size = max_record_recv_size(session);
247 recv_size = max_size;
248
249 session->internals.direction = 0;
250
251 if (ms && *ms > 0) {
252 ret = _gnutls_io_check_recv(session, *ms);
253 if (ret < 0)
254 return gnutls_assert_val(ret);
255 gnutls_gettime(&t1);
256 }
257
258 *bufel = _mbuffer_alloc_align16(max_size, get_total_headers(session));
259 if (*bufel == NULL)
260 return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
261
262 ptr = (*bufel)->msg.data;
263
264 reset_errno(session);
265 i = pull_func(fd, ptr, recv_size);
266
267 if (i < 0) {
268 int err = get_errno(session);
269
270 _gnutls_read_log("READ: %d returned from %p, errno=%d\n",
271 (int) i, fd, err);
272
273 ret = errno_to_gerr(err, 1);
274 goto cleanup;
275 } else {
276 _gnutls_read_log("READ: Got %d bytes from %p\n", (int) i,
277 fd);
278 if (i == 0) {
279 /* If we get here, we likely have a stream socket.
280 * That assumption may not work on DCCP. */
281 gnutls_assert();
282 ret = 0;
283 goto cleanup;
284 }
285
286 _mbuffer_set_udata_size(*bufel, i);
287 }
288
289 if (ms && *ms > 0) {
290 gnutls_gettime(&t2);
291 diff = timespec_sub_ms(&t2, &t1);
292 if (diff < *ms)
293 *ms -= diff;
294 else {
295 ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
296 goto cleanup;
297 }
298 }
299
300 _gnutls_read_log("READ: read %d bytes from %p\n", (int) i, fd);
301
302 return i;
303
304 cleanup:
305 _mbuffer_xfree(bufel);
306 return ret;
307 }
308
309 static ssize_t
_gnutls_stream_read(gnutls_session_t session,mbuffer_st ** bufel,size_t size,gnutls_pull_func pull_func,unsigned int * ms)310 _gnutls_stream_read(gnutls_session_t session, mbuffer_st ** bufel,
311 size_t size, gnutls_pull_func pull_func,
312 unsigned int *ms)
313 {
314 size_t left;
315 ssize_t i = 0;
316 size_t max_size = max_record_recv_size(session);
317 uint8_t *ptr;
318 gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;
319 int ret;
320 struct timespec t1, t2;
321 unsigned int diff;
322
323 session->internals.direction = 0;
324
325 *bufel = _mbuffer_alloc_align16(MAX(max_size, size), get_total_headers(session));
326 if (!*bufel) {
327 gnutls_assert();
328 return GNUTLS_E_MEMORY_ERROR;
329 }
330 ptr = (*bufel)->msg.data;
331
332 left = size;
333 while (left > 0) {
334 if (ms && *ms > 0) {
335 ret = _gnutls_io_check_recv(session, *ms);
336 if (ret < 0) {
337 gnutls_assert();
338 goto cleanup;
339 }
340
341 gnutls_gettime(&t1);
342 }
343
344 reset_errno(session);
345
346 i = pull_func(fd, &ptr[size - left], left);
347
348 if (i < 0) {
349 int err = get_errno(session);
350
351 _gnutls_read_log
352 ("READ: %d returned from %p, errno=%d gerrno=%d\n",
353 (int) i, fd, errno,
354 session->internals.errnum);
355
356 if (err == EAGAIN || err == EINTR) {
357 if (size - left > 0) {
358
359 _gnutls_read_log
360 ("READ: returning %d bytes from %p\n",
361 (int) (size - left), fd);
362
363 goto finish;
364 }
365
366 ret = errno_to_gerr(err, 0);
367 goto cleanup;
368 } else {
369 gnutls_assert();
370 ret = GNUTLS_E_PULL_ERROR;
371 goto cleanup;
372 }
373 } else {
374
375 _gnutls_read_log("READ: Got %d bytes from %p\n",
376 (int) i, fd);
377
378 if (i == 0)
379 break; /* EOF */
380 }
381
382 left -= i;
383 (*bufel)->msg.size += i;
384
385 if (ms && *ms > 0 && *ms != GNUTLS_INDEFINITE_TIMEOUT) {
386 gnutls_gettime(&t2);
387 diff = timespec_sub_ms(&t2, &t1);
388 if (diff < *ms)
389 *ms -= diff;
390 else {
391 ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
392 goto cleanup;
393 }
394 }
395 }
396
397 finish:
398
399 _gnutls_read_log("READ: read %d bytes from %p\n",
400 (int) (size - left), fd);
401
402 if (size - left == 0)
403 _mbuffer_xfree(bufel);
404
405 return (size - left);
406
407 cleanup:
408 _mbuffer_xfree(bufel);
409 return ret;
410 }
411
412
413 /* This function is like read. But it does not return -1 on error.
414 * It does return gnutls_errno instead.
415 *
416 * Flags are only used if the default recv() function is being used.
417 */
418 static ssize_t
_gnutls_read(gnutls_session_t session,mbuffer_st ** bufel,size_t size,gnutls_pull_func pull_func,unsigned int * ms)419 _gnutls_read(gnutls_session_t session, mbuffer_st ** bufel,
420 size_t size, gnutls_pull_func pull_func, unsigned int *ms)
421 {
422 if (IS_DTLS(session))
423 /* Size is not passed, since a whole datagram will be read. */
424 return _gnutls_dgram_read(session, bufel, pull_func, ms);
425 else
426 return _gnutls_stream_read(session, bufel, size, pull_func,
427 ms);
428 }
429
430 /* @vec: if non-zero then the vector function will be used to
431 * push the data.
432 */
433 static ssize_t
_gnutls_writev_emu(gnutls_session_t session,gnutls_transport_ptr_t fd,const giovec_t * giovec,unsigned int giovec_cnt,unsigned vec)434 _gnutls_writev_emu(gnutls_session_t session, gnutls_transport_ptr_t fd,
435 const giovec_t * giovec, unsigned int giovec_cnt, unsigned vec)
436 {
437 unsigned int j = 0;
438 size_t total = 0;
439 ssize_t ret = 0;
440
441 for (j = 0; j < giovec_cnt; j++) {
442 if (vec) {
443 ret = session->internals.vec_push_func(fd, &giovec[j], 1);
444 } else {
445 size_t sent = 0;
446 ssize_t left = giovec[j].iov_len;
447 char *p = giovec[j].iov_base;
448 do {
449 ret =
450 session->internals.push_func(fd, p,
451 left);
452 if (ret > 0) {
453 sent += ret;
454 left -= ret;
455 p += ret;
456 }
457 } while(ret > 0 && left > 0);
458
459 if (sent > 0)
460 ret = sent;
461 }
462
463 if (ret == -1) {
464 gnutls_assert();
465 break;
466 }
467
468 total += ret;
469
470 if ((size_t) ret != giovec[j].iov_len)
471 break;
472 }
473
474 if (total > 0)
475 return total;
476
477 return ret;
478 }
479
480 /* @total: The sum of the data in giovec
481 */
482 static ssize_t
_gnutls_writev(gnutls_session_t session,const giovec_t * giovec,unsigned giovec_cnt,unsigned total)483 _gnutls_writev(gnutls_session_t session, const giovec_t * giovec,
484 unsigned giovec_cnt, unsigned total)
485 {
486 int i;
487 bool is_dtls = IS_DTLS(session);
488 unsigned no_writev = 0;
489 gnutls_transport_ptr_t fd = session->internals.transport_send_ptr;
490
491 reset_errno(session);
492
493 if (session->internals.vec_push_func != NULL) {
494 if (is_dtls && giovec_cnt > 1) {
495 if (total > session->internals.dtls.mtu) {
496 no_writev = 1;
497 }
498 }
499
500 if (no_writev == 0) {
501 i = session->internals.vec_push_func(fd, giovec, giovec_cnt);
502 } else {
503 i = _gnutls_writev_emu(session, fd, giovec, giovec_cnt, 1);
504 }
505 } else if (session->internals.push_func != NULL) {
506 i = _gnutls_writev_emu(session, fd, giovec, giovec_cnt, 0);
507 } else
508 return gnutls_assert_val(GNUTLS_E_PUSH_ERROR);
509
510 if (i == -1) {
511 int err = get_errno(session);
512 _gnutls_debug_log("WRITE: %d returned from %p, errno: %d\n",
513 i, fd, err);
514
515 return errno_to_gerr(err, is_dtls);
516 }
517 return i;
518 }
519
520 /*
521 * @ms: a pointer to the number of milliseconds to wait for data. Use zero or NULL for indefinite.
522 *
523 * This function is like recv(with MSG_PEEK). But it does not return -1 on error.
524 * It does return gnutls_errno instead.
525 * This function reads data from the socket and keeps them in a buffer, of up to
526 * max_record_recv_size.
527 *
528 * This is not a general purpose function. It returns EXACTLY the data requested,
529 * which are stored in a local (in the session) buffer.
530 *
531 * If the @ms parameter is non zero then this function will return before
532 * the given amount of milliseconds or return GNUTLS_E_TIMEDOUT.
533 *
534 */
535 ssize_t
_gnutls_io_read_buffered(gnutls_session_t session,size_t total,content_type_t recv_type,unsigned int * ms)536 _gnutls_io_read_buffered(gnutls_session_t session, size_t total,
537 content_type_t recv_type, unsigned int *ms)
538 {
539 ssize_t ret;
540 size_t min;
541 mbuffer_st *bufel = NULL;
542 size_t recvdata, readsize;
543
544 if (total > max_record_recv_size(session) || total == 0) {
545 gnutls_assert();
546 return GNUTLS_E_RECORD_OVERFLOW;
547 }
548
549 /* calculate the actual size, ie. get the minimum of the
550 * buffered data and the requested data.
551 */
552 min =
553 MIN(session->internals.record_recv_buffer.byte_length, total);
554 if (min > 0) {
555 /* if we have enough buffered data
556 * then just return them.
557 */
558 if (min == total) {
559 return min;
560 }
561 }
562
563 /* min is over zero. recvdata is the data we must
564 * receive in order to return the requested data.
565 */
566 recvdata = total - min;
567 readsize = recvdata;
568
569 /* Check if the previously read data plus the new data to
570 * receive are longer than the maximum receive buffer size.
571 */
572 if ((session->internals.record_recv_buffer.byte_length +
573 recvdata) > max_record_recv_size(session)) {
574 gnutls_assert(); /* internal error */
575 return GNUTLS_E_INVALID_REQUEST;
576 }
577
578 /* READ DATA
579 */
580 if (readsize > 0) {
581 ret =
582 _gnutls_read(session, &bufel, readsize,
583 session->internals.pull_func, ms);
584
585 /* return immediately if we got an interrupt or eagain
586 * error.
587 */
588 if (ret < 0) {
589 return gnutls_assert_val(ret);
590 }
591
592 if (ret == 0) /* EOF */
593 return gnutls_assert_val(0);
594
595 /* copy fresh data to our buffer.
596 */
597 _gnutls_read_log
598 ("RB: Have %d bytes into buffer. Adding %d bytes.\n",
599 (int) session->internals.record_recv_buffer.
600 byte_length, (int) ret);
601 _gnutls_read_log("RB: Requested %d bytes\n", (int) total);
602
603 _mbuffer_enqueue(&session->internals.record_recv_buffer,
604 bufel);
605
606 if (IS_DTLS(session))
607 ret =
608 MIN(total,
609 session->internals.record_recv_buffer.
610 byte_length);
611 else
612 ret =
613 session->internals.record_recv_buffer.
614 byte_length;
615
616 if ((ret > 0) && ((size_t) ret < total)) /* Short Read */
617 return gnutls_assert_val(GNUTLS_E_AGAIN);
618 else
619 return ret;
620 } else
621 return gnutls_assert_val(0);
622 }
623
624 /* This function is like write. But it does not return -1 on error.
625 * It does return gnutls_errno instead.
626 *
627 * This function takes full responsibility of freeing msg->data.
628 *
629 * In case of E_AGAIN and E_INTERRUPTED errors, you must call
630 * gnutls_write_flush(), until it returns ok (0).
631 *
632 * We need to push exactly the data in msg->size, since we cannot send
633 * less data. In TLS the peer must receive the whole packet in order
634 * to decrypt and verify the integrity.
635 *
636 */
637 ssize_t
_gnutls_io_write_buffered(gnutls_session_t session,mbuffer_st * bufel,unsigned int mflag)638 _gnutls_io_write_buffered(gnutls_session_t session,
639 mbuffer_st * bufel, unsigned int mflag)
640 {
641 mbuffer_head_st *const send_buffer =
642 &session->internals.record_send_buffer;
643
644 /* to know where the procedure was interrupted.
645 */
646 session->internals.direction = 1;
647
648 _mbuffer_enqueue(send_buffer, bufel);
649
650 _gnutls_write_log
651 ("WRITE: enqueued %d bytes for %p. Total %d bytes.\n",
652 (int) bufel->msg.size, session->internals.transport_recv_ptr,
653 (int) send_buffer->byte_length);
654
655 if (mflag == MBUFFER_FLUSH)
656 return _gnutls_io_write_flush(session);
657 else
658 return bufel->msg.size;
659 }
660
661 typedef ssize_t(*send_func) (gnutls_session_t, const giovec_t *, int);
662
663 /* This function writes the data that are left in the
664 * TLS write buffer (ie. because the previous write was
665 * interrupted.
666 */
_gnutls_io_write_flush(gnutls_session_t session)667 ssize_t _gnutls_io_write_flush(gnutls_session_t session)
668 {
669 gnutls_datum_t msg;
670 mbuffer_head_st *send_buffer =
671 &session->internals.record_send_buffer;
672 int ret;
673 ssize_t sent = 0, tosend = 0;
674 giovec_t iovec[MAX_QUEUE];
675 int i = 0;
676 mbuffer_st *cur;
677
678 session->internals.direction = 1;
679 _gnutls_write_log("WRITE FLUSH: %d bytes in buffer.\n",
680 (int) send_buffer->byte_length);
681
682 for (cur = _mbuffer_head_get_first(send_buffer, &msg);
683 cur != NULL; cur = _mbuffer_head_get_next(cur, &msg)) {
684 iovec[i].iov_base = msg.data;
685 iovec[i++].iov_len = msg.size;
686 tosend += msg.size;
687
688 /* we buffer up to MAX_QUEUE messages */
689 if (i >= MAX_QUEUE) {
690 gnutls_assert();
691 return GNUTLS_E_INTERNAL_ERROR;
692 }
693 }
694
695 if (tosend == 0) {
696 gnutls_assert();
697 return 0;
698 }
699
700 ret = _gnutls_writev(session, iovec, i, tosend);
701 if (ret >= 0) {
702 _mbuffer_head_remove_bytes(send_buffer, ret);
703 _gnutls_write_log
704 ("WRITE: wrote %d bytes, %d bytes left.\n", ret,
705 (int) send_buffer->byte_length);
706
707 sent += ret;
708 } else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
709 _gnutls_write_log("WRITE interrupted: %d bytes left.\n",
710 (int) send_buffer->byte_length);
711 return ret;
712 } else if (ret == GNUTLS_E_LARGE_PACKET) {
713 _mbuffer_head_remove_bytes(send_buffer, tosend);
714 _gnutls_write_log
715 ("WRITE cannot send large packet (%u bytes).\n",
716 (unsigned int) tosend);
717 return ret;
718 } else {
719 _gnutls_write_log("WRITE error: code %d, %d bytes left.\n",
720 ret, (int) send_buffer->byte_length);
721
722 gnutls_assert();
723 return ret;
724 }
725
726 if (sent < tosend) {
727 return gnutls_assert_val(GNUTLS_E_AGAIN);
728 }
729
730 return sent;
731 }
732
733 /* Checks whether there are received data within
734 * a timeframe.
735 *
736 * Returns 0 if data were received, GNUTLS_E_TIMEDOUT
737 * on timeout and a negative error code on error.
738 */
_gnutls_io_check_recv(gnutls_session_t session,unsigned int ms)739 int _gnutls_io_check_recv(gnutls_session_t session, unsigned int ms)
740 {
741 gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;
742 int ret = 0, err;
743
744 if (NO_TIMEOUT_FUNC_SET(session)) {
745 _gnutls_debug_log("The pull function has been replaced but not the pull timeout.\n");
746 return gnutls_assert_val(GNUTLS_E_PULL_ERROR);
747 }
748
749 reset_errno(session);
750
751 ret = session->internals.pull_timeout_func(fd, ms);
752 if (ret == -1) {
753 err = get_errno(session);
754 _gnutls_read_log
755 ("READ_TIMEOUT: %d returned from %p, errno=%d (timeout: %u)\n",
756 (int) ret, fd, err, ms);
757 return errno_to_gerr(err, IS_DTLS(session));
758 }
759
760 if (ret > 0)
761 return 0;
762 else
763 return GNUTLS_E_TIMEDOUT;
764 }
765
766 /* HANDSHAKE buffers part
767 */
768
769 /* This function writes the data that are left in the
770 * Handshake write buffer (ie. because the previous write was
771 * interrupted.
772 *
773 */
_gnutls_handshake_io_write_flush(gnutls_session_t session)774 ssize_t _gnutls_handshake_io_write_flush(gnutls_session_t session)
775 {
776 mbuffer_head_st *const send_buffer =
777 &session->internals.handshake_send_buffer;
778 gnutls_datum_t msg;
779 int ret;
780 uint16_t epoch;
781 ssize_t total = 0;
782 mbuffer_st *cur;
783
784 _gnutls_write_log("HWRITE FLUSH: %d bytes in buffer.\n",
785 (int) send_buffer->byte_length);
786
787 if (IS_DTLS(session))
788 return _dtls_transmit(session);
789
790 for (cur = _mbuffer_head_get_first(send_buffer, &msg);
791 cur != NULL; cur = _mbuffer_head_get_first(send_buffer, &msg))
792 {
793 epoch = cur->epoch;
794
795 ret = _gnutls_send_int(session, cur->type,
796 cur->htype,
797 epoch, msg.data, msg.size, 0);
798
799 if (ret >= 0) {
800 total += ret;
801
802 ret = _mbuffer_head_remove_bytes(send_buffer, ret);
803 /* for each queued message we send, ensure that
804 * we drop the epoch refcount set in _gnutls_handshake_io_cache_int(). */
805 if (ret == 1)
806 _gnutls_epoch_refcount_dec(session, epoch);
807
808 _gnutls_write_log
809 ("HWRITE: wrote %d bytes, %d bytes left.\n",
810 ret, (int) send_buffer->byte_length);
811
812 } else {
813 _gnutls_write_log
814 ("HWRITE error: code %d, %d bytes left.\n",
815 ret, (int) send_buffer->byte_length);
816
817 gnutls_assert();
818 return ret;
819 }
820 }
821
822 return _gnutls_io_write_flush(session);
823 }
824
825
826 /* This is a send function for the gnutls handshake
827 * protocol. Just makes sure that all data have been sent.
828 *
829 */
830 int
_gnutls_handshake_io_cache_int(gnutls_session_t session,gnutls_handshake_description_t htype,mbuffer_st * bufel)831 _gnutls_handshake_io_cache_int(gnutls_session_t session,
832 gnutls_handshake_description_t htype,
833 mbuffer_st * bufel)
834 {
835 mbuffer_head_st *send_buffer;
836
837 if (IS_DTLS(session)) {
838 bufel->handshake_sequence =
839 session->internals.dtls.hsk_write_seq - 1;
840 }
841
842 send_buffer = &session->internals.handshake_send_buffer;
843
844 /* ensure that our epoch does not get garbage collected
845 * before we send all queued messages with it */
846 bufel->epoch =
847 (uint16_t) _gnutls_epoch_refcount_inc(session,
848 EPOCH_WRITE_CURRENT);
849 bufel->htype = htype;
850 if (bufel->htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC)
851 bufel->type = GNUTLS_CHANGE_CIPHER_SPEC;
852 else
853 bufel->type = GNUTLS_HANDSHAKE;
854
855 _mbuffer_enqueue(send_buffer, bufel);
856
857 _gnutls_write_log
858 ("HWRITE: enqueued [%s] %d. Total %d bytes.\n",
859 _gnutls_handshake2str(bufel->htype), (int) bufel->msg.size,
860 (int) send_buffer->byte_length);
861
862 return 0;
863 }
864
handshake_compare(const void * _e1,const void * _e2)865 static int handshake_compare(const void *_e1, const void *_e2)
866 {
867 const handshake_buffer_st *e1 = _e1;
868 const handshake_buffer_st *e2 = _e2;
869
870 if (e1->sequence <= e2->sequence)
871 return 1;
872 else
873 return -1;
874 }
875
876 #define SSL2_HEADERS 1
877 static int
parse_handshake_header(gnutls_session_t session,mbuffer_st * bufel,handshake_buffer_st * hsk)878 parse_handshake_header(gnutls_session_t session, mbuffer_st * bufel,
879 handshake_buffer_st * hsk)
880 {
881 uint8_t *dataptr = NULL; /* for realloc */
882 size_t handshake_header_size =
883 HANDSHAKE_HEADER_SIZE(session), data_size, frag_size;
884
885 /* Note: SSL2_HEADERS == 1 */
886 if (_mbuffer_get_udata_size(bufel) < handshake_header_size)
887 return
888 gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
889
890 dataptr = _mbuffer_get_udata_ptr(bufel);
891
892 /* if reading a client hello of SSLv2 */
893 #ifdef ENABLE_SSL2
894 if (unlikely
895 (!IS_DTLS(session)
896 && bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)) {
897 handshake_header_size = SSL2_HEADERS; /* we've already read one byte */
898
899 frag_size = _mbuffer_get_udata_size(bufel) - handshake_header_size; /* we've read the first byte */
900
901 if (dataptr[0] != GNUTLS_HANDSHAKE_CLIENT_HELLO)
902 return
903 gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
904
905 hsk->rtype = hsk->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2;
906
907 hsk->sequence = 0;
908 hsk->start_offset = 0;
909 hsk->length = frag_size;
910 } else
911 #endif
912 { /* TLS or DTLS handshake headers */
913
914
915 hsk->rtype = hsk->htype = dataptr[0];
916
917 /* we do not use DECR_LEN because we know
918 * that the packet has enough data.
919 */
920 hsk->length = _gnutls_read_uint24(&dataptr[1]);
921
922 if (IS_DTLS(session)) {
923 hsk->sequence = _gnutls_read_uint16(&dataptr[4]);
924 hsk->start_offset =
925 _gnutls_read_uint24(&dataptr[6]);
926 frag_size =
927 _gnutls_read_uint24(&dataptr[9]);
928 } else {
929 hsk->sequence = 0;
930 hsk->start_offset = 0;
931 frag_size =
932 MIN((_mbuffer_get_udata_size(bufel) -
933 handshake_header_size), hsk->length);
934 }
935
936 /* TLS1.3: distinguish server hello versus hello retry request.
937 * The epitome of slick protocol design. */
938 if (hsk->htype == GNUTLS_HANDSHAKE_SERVER_HELLO && hsk->start_offset == 0 && !IS_DTLS(session)) {
939 if (_mbuffer_get_udata_size(bufel) > handshake_header_size+2+GNUTLS_RANDOM_SIZE &&
940 memcmp(dataptr+handshake_header_size+2, HRR_RANDOM, GNUTLS_RANDOM_SIZE) == 0) {
941 hsk->htype = GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST;
942 }
943 }
944 }
945 data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size;
946
947 if (frag_size > 0)
948 hsk->end_offset = hsk->start_offset + frag_size - 1;
949 else
950 hsk->end_offset = 0;
951
952 _gnutls_handshake_log
953 ("HSK[%p]: %s (%u) was received. Length %d[%d], frag offset %d, frag length: %d, sequence: %d\n",
954 session, _gnutls_handshake2str(hsk->htype),
955 (unsigned) hsk->htype, (int) hsk->length, (int) data_size,
956 hsk->start_offset, (int) frag_size,
957 (int) hsk->sequence);
958
959 hsk->header_size = handshake_header_size;
960 memcpy(hsk->header, _mbuffer_get_udata_ptr(bufel),
961 handshake_header_size);
962
963 if (hsk->length > 0 && (frag_size > data_size ||
964 (frag_size > 0 &&
965 hsk->end_offset >= hsk->length))) {
966 return
967 gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
968 }
969 else if (hsk->length == 0 && hsk->end_offset != 0
970 && hsk->start_offset != 0)
971 return
972 gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
973
974 return handshake_header_size;
975 }
976
_gnutls_handshake_buffer_move(handshake_buffer_st * dst,handshake_buffer_st * src)977 static void _gnutls_handshake_buffer_move(handshake_buffer_st * dst,
978 handshake_buffer_st * src)
979 {
980 memcpy(dst, src, sizeof(*dst));
981 memset(src, 0, sizeof(*src));
982 src->htype = -1;
983 }
984
985 /* will merge the given handshake_buffer_st to the handshake_recv_buffer
986 * list. The given hsk packet will be released in any case (success or failure).
987 * Only used in DTLS.
988 */
merge_handshake_packet(gnutls_session_t session,handshake_buffer_st * hsk)989 static int merge_handshake_packet(gnutls_session_t session,
990 handshake_buffer_st * hsk)
991 {
992 int exists = 0, i, pos = 0;
993 int ret;
994
995 for (i = 0; i < session->internals.handshake_recv_buffer_size; i++) {
996 if (session->internals.handshake_recv_buffer[i].htype ==
997 hsk->htype) {
998 exists = 1;
999 pos = i;
1000 break;
1001 }
1002 }
1003
1004 if (!exists)
1005 pos = session->internals.handshake_recv_buffer_size;
1006
1007 if (pos >= MAX_HANDSHAKE_MSGS)
1008 return
1009 gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS);
1010
1011 if (!exists) {
1012 if (hsk->length > 0 && hsk->end_offset > 0
1013 && hsk->end_offset - hsk->start_offset + 1 !=
1014 hsk->length) {
1015 ret =
1016 _gnutls_buffer_resize(&hsk->data, hsk->length);
1017 if (ret < 0)
1018 return gnutls_assert_val(ret);
1019
1020 hsk->data.length = hsk->length;
1021
1022 memmove(&hsk->data.data[hsk->start_offset],
1023 hsk->data.data,
1024 hsk->end_offset - hsk->start_offset + 1);
1025 }
1026
1027 session->internals.handshake_recv_buffer_size++;
1028
1029 /* rewrite headers to make them look as each packet came as a single fragment */
1030 _gnutls_write_uint24(hsk->length, &hsk->header[1]);
1031 _gnutls_write_uint24(0, &hsk->header[6]);
1032 _gnutls_write_uint24(hsk->length, &hsk->header[9]);
1033
1034 _gnutls_handshake_buffer_move(&session->internals.
1035 handshake_recv_buffer[pos],
1036 hsk);
1037
1038 } else {
1039 if (hsk->start_offset <
1040 session->internals.handshake_recv_buffer[pos].
1041 start_offset
1042 && hsk->end_offset + 1 >=
1043 session->internals.handshake_recv_buffer[pos].
1044 start_offset) {
1045 memcpy(&session->internals.
1046 handshake_recv_buffer[pos].data.data[hsk->
1047 start_offset],
1048 hsk->data.data, hsk->data.length);
1049 session->internals.handshake_recv_buffer[pos].
1050 start_offset = hsk->start_offset;
1051 session->internals.handshake_recv_buffer[pos].
1052 end_offset =
1053 MIN(hsk->end_offset,
1054 session->internals.
1055 handshake_recv_buffer[pos].end_offset);
1056 } else if (hsk->end_offset >
1057 session->internals.handshake_recv_buffer[pos].
1058 end_offset
1059 && hsk->start_offset <=
1060 session->internals.handshake_recv_buffer[pos].
1061 end_offset + 1) {
1062 memcpy(&session->internals.
1063 handshake_recv_buffer[pos].data.data[hsk->
1064 start_offset],
1065 hsk->data.data, hsk->data.length);
1066
1067 session->internals.handshake_recv_buffer[pos].
1068 end_offset = hsk->end_offset;
1069 session->internals.handshake_recv_buffer[pos].
1070 start_offset =
1071 MIN(hsk->start_offset,
1072 session->internals.
1073 handshake_recv_buffer[pos].start_offset);
1074 }
1075 _gnutls_handshake_buffer_clear(hsk);
1076 }
1077
1078 return 0;
1079 }
1080
1081 /* returns non-zero on match and zero on mismatch
1082 */
cmp_hsk_types(gnutls_handshake_description_t expected,gnutls_handshake_description_t recvd)1083 inline static int cmp_hsk_types(gnutls_handshake_description_t expected,
1084 gnutls_handshake_description_t recvd)
1085 {
1086 if (expected == GNUTLS_HANDSHAKE_ANY)
1087 return 1;
1088
1089 #ifdef ENABLE_SSL2
1090 if (expected == GNUTLS_HANDSHAKE_CLIENT_HELLO
1091 && recvd == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)
1092 return 1;
1093 #endif
1094 if (expected != recvd)
1095 return 0;
1096
1097 return 1;
1098 }
1099
1100 #define LAST_ELEMENT (session->internals.handshake_recv_buffer_size-1)
1101
1102 /* returns the last stored handshake packet.
1103 */
get_last_packet(gnutls_session_t session,gnutls_handshake_description_t htype,handshake_buffer_st * hsk,unsigned int optional)1104 static int get_last_packet(gnutls_session_t session,
1105 gnutls_handshake_description_t htype,
1106 handshake_buffer_st * hsk,
1107 unsigned int optional)
1108 {
1109 handshake_buffer_st *recv_buf =
1110 session->internals.handshake_recv_buffer;
1111
1112 if (IS_DTLS(session)) {
1113 if (session->internals.handshake_recv_buffer_size == 0 ||
1114 (session->internals.dtls.hsk_read_seq !=
1115 recv_buf[LAST_ELEMENT].sequence))
1116 goto timeout;
1117
1118 if (htype != recv_buf[LAST_ELEMENT].htype) {
1119 if (optional == 0)
1120 _gnutls_audit_log(session,
1121 "Received unexpected handshake message '%s' (%d). Expected '%s' (%d)\n",
1122 _gnutls_handshake2str
1123 (recv_buf[0].htype),
1124 (int) recv_buf[0].htype,
1125 _gnutls_handshake2str
1126 (htype), (int) htype);
1127
1128 return
1129 gnutls_assert_val
1130 (GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET);
1131 }
1132
1133 else if ((recv_buf[LAST_ELEMENT].start_offset == 0 &&
1134 recv_buf[LAST_ELEMENT].end_offset ==
1135 recv_buf[LAST_ELEMENT].length - 1)
1136 || recv_buf[LAST_ELEMENT].length == 0) {
1137 session->internals.dtls.hsk_read_seq++;
1138 _gnutls_handshake_buffer_move(hsk,
1139 &recv_buf
1140 [LAST_ELEMENT]);
1141 session->internals.handshake_recv_buffer_size--;
1142 return 0;
1143 } else {
1144 /* if we don't have a complete handshake message, but we
1145 * have queued data waiting, try again to reconstruct the
1146 * handshake packet, using the queued */
1147 if (recv_buf[LAST_ELEMENT].end_offset != recv_buf[LAST_ELEMENT].length - 1 &&
1148 record_check_unprocessed(session) > 0)
1149 return gnutls_assert_val(GNUTLS_E_INT_CHECK_AGAIN);
1150 else
1151 goto timeout;
1152 }
1153 } else { /* TLS */
1154
1155 if (session->internals.handshake_recv_buffer_size > 0
1156 && recv_buf[0].length == recv_buf[0].data.length) {
1157 if (cmp_hsk_types(htype, recv_buf[0].htype) == 0) {
1158 return
1159 gnutls_assert_val
1160 (GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET);
1161 }
1162
1163 _gnutls_handshake_buffer_move(hsk, &recv_buf[0]);
1164 session->internals.handshake_recv_buffer_size--;
1165 return 0;
1166 } else
1167 return
1168 gnutls_assert_val
1169 (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
1170 }
1171
1172 timeout:
1173 RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, 0);
1174 }
1175
1176 /* This is a receive function for the gnutls handshake
1177 * protocol. Makes sure that we have received all data.
1178 *
1179 * htype is the next handshake packet expected.
1180 */
_gnutls_parse_record_buffered_msgs(gnutls_session_t session)1181 int _gnutls_parse_record_buffered_msgs(gnutls_session_t session)
1182 {
1183 gnutls_datum_t msg;
1184 mbuffer_st *bufel = NULL, *prev = NULL;
1185 int ret;
1186 size_t data_size;
1187 handshake_buffer_st *recv_buf =
1188 session->internals.handshake_recv_buffer;
1189
1190 bufel =
1191 _mbuffer_head_get_first(&session->internals.record_buffer,
1192 &msg);
1193 if (bufel == NULL)
1194 return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
1195
1196 if (!IS_DTLS(session)) {
1197 ssize_t append, header_size;
1198
1199 do {
1200 if (bufel->type != GNUTLS_HANDSHAKE)
1201 return
1202 gnutls_assert_val
1203 (GNUTLS_E_UNEXPECTED_PACKET);
1204
1205 if (unlikely
1206 (session->internals.handshake_recv_buffer_size == 0 &&
1207 msg.size < HANDSHAKE_HEADER_SIZE(session) &&
1208 session->internals.handshake_header_recv_buffer.byte_length <
1209 HANDSHAKE_HEADER_SIZE(session) - msg.size)) {
1210 bufel = _mbuffer_head_pop_first(&session->internals.record_buffer);
1211 _mbuffer_enqueue(&session->internals.handshake_header_recv_buffer,
1212 bufel);
1213 break;
1214 } else if (session->internals.handshake_recv_buffer_size >
1215 0 && recv_buf[0].length > recv_buf[0].data.length) {
1216 /* this is the rest of a previous message */
1217 append = MIN(msg.size,
1218 recv_buf[0].length -
1219 recv_buf[0].data.length);
1220
1221 ret =
1222 _gnutls_buffer_append_data(&recv_buf
1223 [0].data,
1224 msg.data,
1225 append);
1226 if (ret < 0)
1227 return gnutls_assert_val(ret);
1228
1229 _mbuffer_head_remove_bytes(&session->
1230 internals.
1231 record_buffer,
1232 append);
1233 } else { /* received new message */
1234 if (unlikely
1235 (session->internals.
1236 handshake_header_recv_buffer.length > 0)) {
1237 bufel = _mbuffer_head_pop_first(&session->internals.
1238 record_buffer);
1239 _mbuffer_enqueue(&session->internals.
1240 handshake_header_recv_buffer,
1241 bufel);
1242 ret = _mbuffer_linearize_align16(&session->internals.
1243 handshake_header_recv_buffer,
1244 get_total_headers(session));
1245 if (ret < 0)
1246 return gnutls_assert_val(ret);
1247 bufel = _mbuffer_head_pop_first(&session->internals.
1248 handshake_header_recv_buffer);
1249 _mbuffer_head_push_first(&session->internals.
1250 record_buffer,
1251 bufel);
1252 }
1253
1254 ret =
1255 parse_handshake_header(session, bufel,
1256 &recv_buf[0]);
1257 if (ret < 0)
1258 return gnutls_assert_val(ret);
1259
1260 header_size = ret;
1261 session->internals.
1262 handshake_recv_buffer_size = 1;
1263
1264 _mbuffer_set_uhead_size(bufel,
1265 header_size);
1266
1267 data_size =
1268 MIN(recv_buf[0].length,
1269 _mbuffer_get_udata_size(bufel));
1270 ret =
1271 _gnutls_buffer_append_data(&recv_buf
1272 [0].data,
1273 _mbuffer_get_udata_ptr
1274 (bufel),
1275 data_size);
1276 if (ret < 0)
1277 return gnutls_assert_val(ret);
1278 _mbuffer_set_uhead_size(bufel, 0);
1279 _mbuffer_head_remove_bytes(&session->
1280 internals.
1281 record_buffer,
1282 data_size +
1283 header_size);
1284 }
1285
1286 /* if packet is complete then return it
1287 */
1288 if (recv_buf[0].length == recv_buf[0].data.length) {
1289 return 0;
1290 }
1291 bufel =
1292 _mbuffer_head_get_first(&session->internals.
1293 record_buffer, &msg);
1294 }
1295 while (bufel != NULL);
1296
1297 /* if we are here it means that the received packets were not
1298 * enough to complete the handshake packet.
1299 */
1300 return gnutls_assert_val(GNUTLS_E_AGAIN);
1301 } else { /* DTLS */
1302
1303 handshake_buffer_st tmp;
1304
1305 do {
1306 /* we now
1307 * 0. parse headers
1308 * 1. insert to handshake_recv_buffer
1309 * 2. sort handshake_recv_buffer on sequence numbers
1310 * 3. return first packet if completed or GNUTLS_E_AGAIN.
1311 */
1312 do {
1313 if (bufel->type != GNUTLS_HANDSHAKE) {
1314 gnutls_assert();
1315 goto next; /* ignore packet */
1316 }
1317
1318 _gnutls_handshake_buffer_init(&tmp);
1319
1320 ret =
1321 parse_handshake_header(session, bufel,
1322 &tmp);
1323 if (ret < 0) {
1324 gnutls_assert();
1325 _gnutls_audit_log(session,
1326 "Invalid handshake packet headers. Discarding.\n");
1327 break;
1328 }
1329
1330 _mbuffer_consume(&session->internals.
1331 record_buffer, bufel,
1332 ret);
1333
1334 data_size =
1335 MIN(tmp.length,
1336 tmp.end_offset - tmp.start_offset +
1337 1);
1338
1339 ret =
1340 _gnutls_buffer_append_data(&tmp.data,
1341 _mbuffer_get_udata_ptr
1342 (bufel),
1343 data_size);
1344 if (ret < 0)
1345 return gnutls_assert_val(ret);
1346
1347 _mbuffer_consume(&session->internals.
1348 record_buffer, bufel,
1349 data_size);
1350
1351 ret =
1352 merge_handshake_packet(session, &tmp);
1353 if (ret < 0)
1354 return gnutls_assert_val(ret);
1355
1356 }
1357 while (_mbuffer_get_udata_size(bufel) > 0);
1358
1359 prev = bufel;
1360 bufel =
1361 _mbuffer_dequeue(&session->internals.
1362 record_buffer, bufel);
1363
1364 _mbuffer_xfree(&prev);
1365 continue;
1366
1367 next:
1368 bufel = _mbuffer_head_get_next(bufel, NULL);
1369 }
1370 while (bufel != NULL);
1371
1372 /* sort in descending order */
1373 if (session->internals.handshake_recv_buffer_size > 1)
1374 qsort(recv_buf,
1375 session->internals.
1376 handshake_recv_buffer_size,
1377 sizeof(recv_buf[0]), handshake_compare);
1378
1379 while (session->internals.handshake_recv_buffer_size > 0 &&
1380 recv_buf[LAST_ELEMENT].sequence <
1381 session->internals.dtls.hsk_read_seq) {
1382 _gnutls_audit_log(session,
1383 "Discarded replayed handshake packet with sequence %d\n",
1384 recv_buf[LAST_ELEMENT].sequence);
1385 _gnutls_handshake_buffer_clear(&recv_buf
1386 [LAST_ELEMENT]);
1387 session->internals.handshake_recv_buffer_size--;
1388 }
1389
1390 return 0;
1391 }
1392 }
1393
1394 /* This is a receive function for the gnutls handshake
1395 * protocol. Makes sure that we have received all data.
1396 */
1397 ssize_t
_gnutls_handshake_io_recv_int(gnutls_session_t session,gnutls_handshake_description_t htype,handshake_buffer_st * hsk,unsigned int optional)1398 _gnutls_handshake_io_recv_int(gnutls_session_t session,
1399 gnutls_handshake_description_t htype,
1400 handshake_buffer_st * hsk,
1401 unsigned int optional)
1402 {
1403 int ret;
1404 unsigned int tleft = 0;
1405 int retries = 7;
1406
1407 ret = get_last_packet(session, htype, hsk, optional);
1408 if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED &&
1409 ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE &&
1410 ret != GNUTLS_E_INT_CHECK_AGAIN) {
1411 return gnutls_assert_val(ret);
1412 }
1413
1414 /* try using the already existing records before
1415 * trying to receive.
1416 */
1417 ret = _gnutls_parse_record_buffered_msgs(session);
1418
1419 if (ret == 0) {
1420 ret = get_last_packet(session, htype, hsk, optional);
1421 }
1422
1423 if (IS_DTLS(session)) {
1424 if (ret >= 0)
1425 return ret;
1426 } else {
1427 if ((ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
1428 && ret < 0) || ret >= 0)
1429 return gnutls_assert_val(ret);
1430 }
1431
1432 if (htype != (gnutls_handshake_description_t) -1) {
1433 ret = handshake_remaining_time(session);
1434 if (ret < 0)
1435 return gnutls_assert_val(ret);
1436 tleft = ret;
1437 }
1438
1439 do {
1440 /* if we don't have a complete message waiting for us, try
1441 * receiving more */
1442 ret =
1443 _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, htype,
1444 tleft);
1445 if (ret < 0)
1446 return gnutls_assert_val_fatal(ret);
1447
1448 ret = _gnutls_parse_record_buffered_msgs(session);
1449 if (ret == 0) {
1450 ret = get_last_packet(session, htype, hsk, optional);
1451 }
1452 /* we put an upper limit (retries) to the number of partial handshake
1453 * messages in a record packet. */
1454 } while(IS_DTLS(session) && ret == GNUTLS_E_INT_CHECK_AGAIN && retries-- > 0);
1455
1456 if (unlikely(IS_DTLS(session) && ret == GNUTLS_E_INT_CHECK_AGAIN)) {
1457 ret = gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS);
1458 }
1459
1460 return ret;
1461 }
1462