Coverage Report

Created: 2026-11-12 07:00

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