Coverage Report

Created: 2025-12-31 06:37

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
76.5k
#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.11M
{
71
1.11M
  bufel->type = type;
72
1.11M
  bufel->record_sequence = seq;
73
74
1.11M
  _mbuffer_enqueue(&session->internals.record_buffer, bufel);
75
1.11M
  _gnutls_buffers_log("BUF[REC]: Inserted %d bytes of Data(%d)\n",
76
1.11M
          (int)bufel->msg.size, (int)type);
77
78
1.11M
  return;
79
1.11M
}
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.35k
{
116
2.35k
  gnutls_datum_t msg;
117
2.35k
  mbuffer_st *bufel;
118
119
2.35k
  if (length == 0 || data == NULL) {
120
0
    gnutls_assert();
121
0
    return GNUTLS_E_INVALID_REQUEST;
122
0
  }
123
124
2.35k
  bufel = _mbuffer_head_get_first(&session->internals.record_buffer,
125
2.35k
          &msg);
126
2.35k
  if (bufel == NULL)
127
0
    return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
128
129
2.35k
  if (type != bufel->type) {
130
1.75k
    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.75k
    else
138
1.75k
      _gnutls_debug_log(
139
1.75k
        "received unexpected packet: %s(%d)\n",
140
1.75k
        _gnutls_packet2str(bufel->type),
141
1.75k
        (int)bufel->type);
142
143
1.75k
    _mbuffer_head_remove_bytes(&session->internals.record_buffer,
144
1.75k
             msg.size);
145
1.75k
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
146
1.75k
  }
147
148
595
  if (msg.size <= length)
149
595
    length = msg.size;
150
151
595
  if (seq)
152
0
    _gnutls_write_uint64(bufel->record_sequence, seq);
153
154
595
  memcpy(data, msg.data, length);
155
595
  _mbuffer_head_remove_bytes(&session->internals.record_buffer, length);
156
157
595
  return length;
158
2.35k
}
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.14M
{
189
2.14M
  session->internals.errnum = 0;
190
2.14M
}
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.09M
{
299
2.09M
  size_t left;
300
2.09M
  ssize_t i = 0;
301
2.09M
  size_t max_size = max_record_recv_size(session);
302
2.09M
  uint8_t *ptr;
303
2.09M
  gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;
304
2.09M
  int ret;
305
2.09M
  struct timespec t1, t2;
306
2.09M
  unsigned int diff;
307
308
2.09M
  session->internals.direction = 0;
309
310
2.09M
  *bufel = _mbuffer_alloc_align16(MAX(max_size, size),
311
2.09M
          get_total_headers(session));
312
2.09M
  if (!*bufel) {
313
0
    gnutls_assert();
314
0
    return GNUTLS_E_MEMORY_ERROR;
315
0
  }
316
2.09M
  ptr = (*bufel)->msg.data;
317
318
2.09M
  left = size;
319
4.17M
  while (left > 0) {
320
2.09M
    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.09M
    reset_errno(session);
331
332
2.09M
    i = pull_func(fd, &ptr[size - left], left);
333
334
2.09M
    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.09M
    } else {
358
2.09M
      _gnutls_read_log("READ: Got %d bytes from %p\n", (int)i,
359
2.09M
           fd);
360
361
2.09M
      if (i == 0)
362
14.5k
        break; /* EOF */
363
2.09M
    }
364
365
2.08M
    left -= i;
366
2.08M
    (*bufel)->msg.size += i;
367
368
2.08M
    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.08M
  }
379
380
2.09M
finish:
381
382
2.09M
  _gnutls_read_log("READ: read %d bytes from %p\n", (int)(size - left),
383
2.09M
       fd);
384
385
2.09M
  if (size - left == 0)
386
11.5k
    _mbuffer_xfree(bufel);
387
388
2.09M
  return (size - left);
389
390
0
cleanup:
391
0
  _mbuffer_xfree(bufel);
392
0
  return ret;
393
2.09M
}
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.09M
{
404
2.09M
  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.09M
  else
408
2.09M
    return _gnutls_stream_read(session, bufel, size, pull_func, ms);
409
2.09M
}
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
45.0k
{
419
45.0k
  unsigned int j = 0;
420
45.0k
  size_t total = 0;
421
45.0k
  ssize_t ret = 0;
422
423
121k
  for (j = 0; j < giovec_cnt; j++) {
424
76.5k
    if (vec) {
425
0
      ret = session->internals.vec_push_func(fd, &giovec[j],
426
0
                     1);
427
76.5k
    } else {
428
76.5k
      size_t sent = 0;
429
76.5k
      ssize_t left = giovec[j].iov_len;
430
76.5k
      char *p = giovec[j].iov_base;
431
76.5k
      do {
432
76.5k
        ret = session->internals.push_func(fd, p, left);
433
76.5k
        if (ret > 0) {
434
76.5k
          sent += ret;
435
76.5k
          left -= ret;
436
76.5k
          p += ret;
437
76.5k
        }
438
76.5k
      } while (ret > 0 && left > 0);
439
440
76.5k
      if (sent > 0)
441
76.5k
        ret = sent;
442
76.5k
    }
443
444
76.5k
    if (ret == -1) {
445
0
      gnutls_assert();
446
0
      break;
447
0
    }
448
449
76.5k
    total += ret;
450
451
76.5k
    if ((size_t)ret != giovec[j].iov_len)
452
0
      break;
453
76.5k
  }
454
455
45.0k
  if (total > 0)
456
45.0k
    return total;
457
458
0
  return ret;
459
45.0k
}
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
45.0k
{
466
45.0k
  int i;
467
45.0k
  bool is_dtls = IS_DTLS(session);
468
45.0k
  unsigned no_writev = 0;
469
45.0k
  gnutls_transport_ptr_t fd = session->internals.transport_send_ptr;
470
471
45.0k
  reset_errno(session);
472
473
45.0k
  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
45.0k
  } else if (session->internals.push_func != NULL) {
488
45.0k
    i = _gnutls_writev_emu(session, fd, giovec, giovec_cnt, 0);
489
45.0k
  } else
490
0
    return gnutls_assert_val(GNUTLS_E_PUSH_ERROR);
491
492
45.0k
  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
45.0k
  return i;
500
45.0k
}
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.39M
{
520
2.39M
  ssize_t ret;
521
2.39M
  size_t min;
522
2.39M
  mbuffer_st *bufel = NULL;
523
2.39M
  size_t recvdata, readsize;
524
525
2.39M
  if (total > max_record_recv_size(session) || total == 0) {
526
19
    gnutls_assert();
527
19
    return GNUTLS_E_RECORD_OVERFLOW;
528
19
  }
529
530
  /* calculate the actual size, ie. get the minimum of the
531
   * buffered data and the requested data.
532
   */
533
2.39M
  min = MIN(session->internals.record_recv_buffer.byte_length, total);
534
2.39M
  if (min > 0) {
535
    /* if we have enough buffered data
536
     * then just return them.
537
     */
538
1.38M
    if (min == total) {
539
295k
      return min;
540
295k
    }
541
1.38M
  }
542
543
  /* min is over zero. recvdata is the data we must
544
   * receive in order to return the requested data.
545
   */
546
2.09M
  recvdata = total - min;
547
2.09M
  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.09M
  if ((session->internals.record_recv_buffer.byte_length + recvdata) >
553
2.09M
      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.09M
  if (readsize > 0) {
561
2.09M
    ret = _gnutls_read(session, &bufel, readsize,
562
2.09M
           session->internals.pull_func, ms);
563
564
    /* return immediately if we got an interrupt or eagain
565
     * error.
566
     */
567
2.09M
    if (ret < 0) {
568
0
      return gnutls_assert_val(ret);
569
0
    }
570
571
2.09M
    if (ret == 0) /* EOF */
572
11.5k
      return gnutls_assert_val(0);
573
574
    /* copy fresh data to our buffer.
575
     */
576
2.08M
    _gnutls_read_log(
577
2.08M
      "RB: Have %d bytes into buffer. Adding %d bytes.\n",
578
2.08M
      (int)session->internals.record_recv_buffer.byte_length,
579
2.08M
      (int)ret);
580
2.08M
    _gnutls_read_log("RB: Requested %d bytes\n", (int)total);
581
582
2.08M
    _mbuffer_enqueue(&session->internals.record_recv_buffer, bufel);
583
584
2.08M
    if (IS_DTLS(session))
585
0
      ret = MIN(total, session->internals.record_recv_buffer
586
2.08M
             .byte_length);
587
2.08M
    else
588
2.08M
      ret = session->internals.record_recv_buffer.byte_length;
589
590
2.08M
    if ((ret > 0) && ((size_t)ret < total)) /* Short Read */
591
2.99k
      return gnutls_assert_val(GNUTLS_E_AGAIN);
592
2.08M
    else
593
2.08M
      return ret;
594
2.08M
  } else
595
0
    return gnutls_assert_val(0);
596
2.09M
}
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
76.7k
{
614
76.7k
  mbuffer_head_st *const send_buffer =
615
76.7k
    &session->internals.record_send_buffer;
616
617
  /* to know where the procedure was interrupted.
618
   */
619
76.7k
  session->internals.direction = 1;
620
621
76.7k
  _mbuffer_enqueue(send_buffer, bufel);
622
623
76.7k
  _gnutls_write_log("WRITE: enqueued %d bytes for %p. Total %d bytes.\n",
624
76.7k
        (int)bufel->msg.size,
625
76.7k
        session->internals.transport_recv_ptr,
626
76.7k
        (int)send_buffer->byte_length);
627
628
76.7k
  if (mflag == MBUFFER_FLUSH)
629
0
    return _gnutls_io_write_flush(session);
630
76.7k
  else
631
76.7k
    return bufel->msg.size;
632
76.7k
}
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
57.2k
{
642
57.2k
  gnutls_datum_t msg;
643
57.2k
  mbuffer_head_st *send_buffer = &session->internals.record_send_buffer;
644
57.2k
  int ret;
645
57.2k
  ssize_t sent = 0, tosend = 0;
646
57.2k
  giovec_t iovec[MAX_QUEUE];
647
57.2k
  int i = 0;
648
57.2k
  mbuffer_st *cur;
649
650
57.2k
  session->internals.direction = 1;
651
57.2k
  _gnutls_write_log("WRITE FLUSH: %d bytes in buffer.\n",
652
57.2k
        (int)send_buffer->byte_length);
653
654
133k
  for (cur = _mbuffer_head_get_first(send_buffer, &msg); cur != NULL;
655
76.5k
       cur = _mbuffer_head_get_next(cur, &msg)) {
656
76.5k
    iovec[i].iov_base = msg.data;
657
76.5k
    iovec[i++].iov_len = msg.size;
658
76.5k
    tosend += msg.size;
659
660
    /* we buffer up to MAX_QUEUE messages */
661
76.5k
    if (i >= MAX_QUEUE) {
662
0
      gnutls_assert();
663
0
      return GNUTLS_E_INTERNAL_ERROR;
664
0
    }
665
76.5k
  }
666
667
57.2k
  if (tosend == 0) {
668
12.2k
    gnutls_assert();
669
12.2k
    return 0;
670
12.2k
  }
671
672
45.0k
  ret = _gnutls_writev(session, iovec, i, tosend);
673
45.0k
  if (ret >= 0) {
674
45.0k
    _mbuffer_head_remove_bytes(send_buffer, ret);
675
45.0k
    _gnutls_write_log("WRITE: wrote %d bytes, %d bytes left.\n",
676
45.0k
          ret, (int)send_buffer->byte_length);
677
678
45.0k
    sent += ret;
679
45.0k
  } 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
45.0k
  if (sent < tosend) {
698
0
    return gnutls_assert_val(GNUTLS_E_AGAIN);
699
0
  }
700
701
45.0k
  return sent;
702
45.0k
}
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
57.3k
{
748
57.3k
  mbuffer_head_st *const send_buffer =
749
57.3k
    &session->internals.handshake_send_buffer;
750
57.3k
  gnutls_datum_t msg;
751
57.3k
  int ret;
752
57.3k
  uint16_t epoch;
753
57.3k
  mbuffer_st *cur;
754
755
57.3k
  _gnutls_write_log("HWRITE FLUSH: %d bytes in buffer.\n",
756
57.3k
        (int)send_buffer->byte_length);
757
758
57.3k
  if (IS_DTLS(session))
759
0
    return _dtls_transmit(session);
760
761
148k
  for (cur = _mbuffer_head_get_first(send_buffer, &msg); cur != NULL;
762
90.8k
       cur = _mbuffer_head_get_first(send_buffer, &msg)) {
763
90.8k
    epoch = cur->epoch;
764
765
90.8k
    if (session->internals.h_read_func) {
766
14.0k
      record_parameters_st *params;
767
768
14.0k
      ret = _gnutls_epoch_get(session, epoch, &params);
769
14.0k
      if (ret < 0)
770
0
        return gnutls_assert_val(ret);
771
14.0k
      ret = session->internals.h_read_func(
772
14.0k
        session, params->write.level, cur->htype,
773
14.0k
        msg.data, msg.size);
774
14.0k
      if (ret < 0)
775
0
        return gnutls_assert_val(ret);
776
777
14.0k
      ret = msg.size;
778
76.8k
    } else {
779
76.8k
      ret = _gnutls_send_int(session, cur->type, cur->htype,
780
76.8k
                 epoch, msg.data, msg.size, 0);
781
76.8k
    }
782
783
90.8k
    if (ret >= 0) {
784
90.7k
      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
90.7k
      if (ret == 1)
788
90.2k
        _gnutls_epoch_refcount_dec(session, epoch);
789
790
90.7k
      _gnutls_write_log(
791
90.7k
        "HWRITE: wrote %d bytes, %d bytes left.\n", ret,
792
90.7k
        (int)send_buffer->byte_length);
793
794
90.7k
    } else {
795
106
      _gnutls_write_log(
796
106
        "HWRITE error: code %d, %d bytes left.\n", ret,
797
106
        (int)send_buffer->byte_length);
798
799
106
      gnutls_assert();
800
106
      return ret;
801
106
    }
802
90.8k
  }
803
804
57.2k
  return _gnutls_io_write_flush(session);
805
57.3k
}
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
90.3k
{
815
90.3k
  mbuffer_head_st *send_buffer;
816
817
90.3k
  if (IS_DTLS(session)) {
818
0
    bufel->handshake_sequence =
819
0
      session->internals.dtls.hsk_write_seq - 1;
820
0
  }
821
822
90.3k
  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
90.3k
  bufel->epoch = (uint16_t)_gnutls_epoch_refcount_inc(
827
90.3k
    session, EPOCH_WRITE_CURRENT);
828
90.3k
  bufel->htype = htype;
829
90.3k
  if (bufel->htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC)
830
13.2k
    bufel->type = GNUTLS_CHANGE_CIPHER_SPEC;
831
77.1k
  else
832
77.1k
    bufel->type = GNUTLS_HANDSHAKE;
833
834
90.3k
  _mbuffer_enqueue(send_buffer, bufel);
835
836
90.3k
  _gnutls_write_log("HWRITE: enqueued [%s] %d. Total %d bytes.\n",
837
90.3k
        _gnutls_handshake2str(bufel->htype),
838
90.3k
        (int)bufel->msg.size, (int)send_buffer->byte_length);
839
840
90.3k
  return 0;
841
90.3k
}
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
848
0
  if (e1->sequence <= e2->sequence)
849
0
    return 1;
850
0
  else
851
0
    return -1;
852
0
}
853
854
3.58k
#define SSL2_HEADERS 1
855
static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel,
856
          handshake_buffer_st *hsk)
857
89.5k
{
858
89.5k
  uint8_t *dataptr = NULL; /* for realloc */
859
89.5k
  size_t handshake_header_size = HANDSHAKE_HEADER_SIZE(session),
860
89.5k
         data_size, frag_size;
861
862
  /* Note: SSL2_HEADERS == 1 */
863
89.5k
  if (_mbuffer_get_udata_size(bufel) < handshake_header_size)
864
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
865
866
89.5k
  dataptr = _mbuffer_get_udata_ptr(bufel);
867
868
  /* if reading a client hello of SSLv2 */
869
89.5k
#ifdef ENABLE_SSL2
870
89.5k
  if (unlikely(!IS_DTLS(session) &&
871
89.5k
         bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)) {
872
3.58k
    handshake_header_size =
873
3.58k
      SSL2_HEADERS; /* we've already read one byte */
874
875
3.58k
    frag_size =
876
3.58k
      _mbuffer_get_udata_size(bufel) -
877
3.58k
      handshake_header_size; /* we've read the first byte */
878
879
3.58k
    if (dataptr[0] != GNUTLS_HANDSHAKE_CLIENT_HELLO)
880
38
      return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
881
882
3.54k
    hsk->rtype = hsk->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2;
883
884
3.54k
    hsk->sequence = 0;
885
3.54k
    hsk->start_offset = 0;
886
3.54k
    hsk->length = frag_size;
887
3.54k
  } else
888
85.9k
#endif
889
85.9k
  { /* TLS or DTLS handshake headers */
890
891
85.9k
    hsk->rtype = hsk->htype = dataptr[0];
892
893
    /* we do not use DECR_LEN because we know
894
     * that the packet has enough data.
895
     */
896
85.9k
    hsk->length = _gnutls_read_uint24(&dataptr[1]);
897
898
85.9k
    if (IS_DTLS(session)) {
899
0
      hsk->sequence = _gnutls_read_uint16(&dataptr[4]);
900
0
      hsk->start_offset = _gnutls_read_uint24(&dataptr[6]);
901
0
      frag_size = _gnutls_read_uint24(&dataptr[9]);
902
85.9k
    } else {
903
85.9k
      hsk->sequence = 0;
904
85.9k
      hsk->start_offset = 0;
905
85.9k
      frag_size = MIN((_mbuffer_get_udata_size(bufel) -
906
85.9k
           handshake_header_size),
907
85.9k
          hsk->length);
908
85.9k
    }
909
910
    /* TLS1.3: distinguish server hello versus hello retry request.
911
     * The epitome of slick protocol design. */
912
85.9k
    if (hsk->htype == GNUTLS_HANDSHAKE_SERVER_HELLO &&
913
27.4k
        hsk->start_offset == 0 && !IS_DTLS(session)) {
914
27.4k
      if (_mbuffer_get_udata_size(bufel) >
915
27.4k
            handshake_header_size + 2 +
916
27.4k
              GNUTLS_RANDOM_SIZE &&
917
27.1k
          memcmp(dataptr + handshake_header_size + 2,
918
27.1k
           HRR_RANDOM, GNUTLS_RANDOM_SIZE) == 0) {
919
1.61k
        hsk->htype =
920
1.61k
          GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST;
921
1.61k
      }
922
27.4k
    }
923
85.9k
  }
924
89.5k
  data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size;
925
926
89.5k
  if (frag_size > 0)
927
81.1k
    hsk->end_offset = hsk->start_offset + frag_size - 1;
928
8.35k
  else
929
8.35k
    hsk->end_offset = 0;
930
931
89.5k
  _gnutls_handshake_log(
932
89.5k
    "HSK[%p]: %s (%u) was received. Length %d[%d], frag offset %d, frag length: %d, sequence: %d\n",
933
89.5k
    session, _gnutls_handshake2str(hsk->htype),
934
89.5k
    (unsigned)hsk->htype, (int)hsk->length, (int)data_size,
935
89.5k
    hsk->start_offset, (int)frag_size, (int)hsk->sequence);
936
937
89.5k
  hsk->header_size = handshake_header_size;
938
89.5k
  memcpy(hsk->header, _mbuffer_get_udata_ptr(bufel),
939
89.5k
         handshake_header_size);
940
941
89.5k
  if (hsk->length > 0 &&
942
82.5k
      (frag_size > data_size ||
943
82.5k
       (frag_size > 0 && hsk->end_offset >= hsk->length))) {
944
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
945
89.5k
  } else if (hsk->length == 0 && hsk->end_offset != 0 &&
946
0
       hsk->start_offset != 0)
947
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
948
949
89.5k
  return handshake_header_size;
950
89.5k
}
951
952
static void _gnutls_handshake_buffer_move(handshake_buffer_st *dst,
953
            handshake_buffer_st *src)
954
85.8k
{
955
85.8k
  memcpy(dst, src, sizeof(*dst));
956
85.8k
  memset(src, 0, sizeof(*src));
957
85.8k
  src->htype = -1;
958
85.8k
}
959
960
/* will merge the given handshake_buffer_st to the handshake_recv_buffer
961
 * list. The given hsk packet will be released in any case (success or failure).
962
 * Only used in DTLS.
963
 */
964
static int merge_handshake_packet(gnutls_session_t session,
965
          handshake_buffer_st *hsk)
966
0
{
967
0
  int exists = 0, i, pos = 0;
968
0
  int ret;
969
970
0
  for (i = 0; i < session->internals.handshake_recv_buffer_size; i++) {
971
0
    if (session->internals.handshake_recv_buffer[i].htype ==
972
0
        hsk->htype) {
973
0
      exists = 1;
974
0
      pos = i;
975
0
      break;
976
0
    }
977
0
  }
978
979
0
  if (!exists)
980
0
    pos = session->internals.handshake_recv_buffer_size;
981
982
0
  if (pos >= MAX_HANDSHAKE_MSGS)
983
0
    return gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS);
984
985
0
  if (!exists) {
986
0
    if (hsk->length != hsk->data.length) {
987
0
      ret = _gnutls_buffer_resize(&hsk->data, hsk->length);
988
0
      if (ret < 0)
989
0
        return gnutls_assert_val(ret);
990
991
0
      hsk->data.length = hsk->length;
992
0
    }
993
994
0
    if (hsk->length > 0 && hsk->end_offset > 0 &&
995
0
        hsk->end_offset - hsk->start_offset + 1 != hsk->length) {
996
0
      memmove(&hsk->data.data[hsk->start_offset],
997
0
        hsk->data.data,
998
0
        hsk->end_offset - hsk->start_offset + 1);
999
0
    }
1000
1001
0
    session->internals.handshake_recv_buffer_size++;
1002
1003
    /* rewrite headers to make them look as each packet came as a single fragment */
1004
0
    _gnutls_write_uint24(hsk->length, &hsk->header[1]);
1005
0
    _gnutls_write_uint24(0, &hsk->header[6]);
1006
0
    _gnutls_write_uint24(hsk->length, &hsk->header[9]);
1007
1008
0
    _gnutls_handshake_buffer_move(
1009
0
      &session->internals.handshake_recv_buffer[pos], hsk);
1010
1011
0
  } else {
1012
0
    if (hsk->start_offset <
1013
0
          session->internals.handshake_recv_buffer[pos]
1014
0
            .start_offset &&
1015
0
        hsk->end_offset + 1 >=
1016
0
          session->internals.handshake_recv_buffer[pos]
1017
0
            .start_offset) {
1018
0
      memcpy(&session->internals.handshake_recv_buffer[pos]
1019
0
          .data.data[hsk->start_offset],
1020
0
             hsk->data.data, hsk->data.length);
1021
0
      session->internals.handshake_recv_buffer[pos]
1022
0
        .start_offset = hsk->start_offset;
1023
0
      session->internals.handshake_recv_buffer[pos]
1024
0
        .end_offset = MIN(
1025
0
        hsk->end_offset,
1026
0
        session->internals.handshake_recv_buffer[pos]
1027
0
          .end_offset);
1028
0
    } else if (hsk->end_offset >
1029
0
           session->internals.handshake_recv_buffer[pos]
1030
0
             .end_offset &&
1031
0
         hsk->start_offset <=
1032
0
           session->internals.handshake_recv_buffer[pos]
1033
0
               .end_offset +
1034
0
             1) {
1035
0
      memcpy(&session->internals.handshake_recv_buffer[pos]
1036
0
          .data.data[hsk->start_offset],
1037
0
             hsk->data.data, hsk->data.length);
1038
1039
0
      session->internals.handshake_recv_buffer[pos]
1040
0
        .end_offset = hsk->end_offset;
1041
0
      session->internals.handshake_recv_buffer[pos]
1042
0
        .start_offset = MIN(
1043
0
        hsk->start_offset,
1044
0
        session->internals.handshake_recv_buffer[pos]
1045
0
          .start_offset);
1046
0
    }
1047
0
    _gnutls_handshake_buffer_clear(hsk);
1048
0
  }
1049
1050
0
  return 0;
1051
0
}
1052
1053
/* returns non-zero on match and zero on mismatch
1054
 */
1055
inline static int cmp_hsk_types(gnutls_handshake_description_t expected,
1056
        gnutls_handshake_description_t recvd)
1057
117k
{
1058
117k
  if (expected == GNUTLS_HANDSHAKE_ANY)
1059
0
    return 1;
1060
1061
117k
#ifdef ENABLE_SSL2
1062
117k
  if (expected == GNUTLS_HANDSHAKE_CLIENT_HELLO &&
1063
13.4k
      recvd == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)
1064
3.54k
    return 1;
1065
114k
#endif
1066
114k
  if (expected != recvd)
1067
32.0k
    return 0;
1068
1069
82.3k
  return 1;
1070
114k
}
1071
1072
0
#define LAST_ELEMENT (session->internals.handshake_recv_buffer_size - 1)
1073
1074
/* returns the last stored handshake packet.
1075
 */
1076
static int get_last_packet(gnutls_session_t session,
1077
         gnutls_handshake_description_t htype,
1078
         handshake_buffer_st *hsk, unsigned int optional)
1079
1.37M
{
1080
1.37M
  handshake_buffer_st *recv_buf =
1081
1.37M
    session->internals.handshake_recv_buffer;
1082
1083
1.37M
  if (IS_DTLS(session)) {
1084
0
    if (session->internals.handshake_recv_buffer_size == 0 ||
1085
0
        (session->internals.dtls.hsk_read_seq !=
1086
0
         recv_buf[LAST_ELEMENT].sequence))
1087
0
      goto timeout;
1088
1089
0
    if (htype != recv_buf[LAST_ELEMENT].htype) {
1090
0
      if (optional == 0)
1091
0
        _gnutls_audit_log(
1092
0
          session,
1093
0
          "Received unexpected handshake message '%s' (%d). Expected '%s' (%d)\n",
1094
0
          _gnutls_handshake2str(
1095
0
            recv_buf[0].htype),
1096
0
          (int)recv_buf[0].htype,
1097
0
          _gnutls_handshake2str(htype),
1098
0
          (int)htype);
1099
1100
0
      return gnutls_assert_val(
1101
0
        GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET);
1102
0
    }
1103
1104
0
    else if ((recv_buf[LAST_ELEMENT].start_offset == 0 &&
1105
0
        recv_buf[LAST_ELEMENT].end_offset ==
1106
0
          recv_buf[LAST_ELEMENT].length - 1) ||
1107
0
       recv_buf[LAST_ELEMENT].length == 0) {
1108
0
      session->internals.dtls.hsk_read_seq++;
1109
0
      _gnutls_handshake_buffer_move(hsk,
1110
0
                  &recv_buf[LAST_ELEMENT]);
1111
0
      session->internals.handshake_recv_buffer_size--;
1112
0
      return 0;
1113
0
    } else {
1114
      /* if we don't have a complete handshake message, but we
1115
       * have queued data waiting, try again to reconstruct the
1116
       * handshake packet, using the queued */
1117
0
      if (recv_buf[LAST_ELEMENT].end_offset !=
1118
0
            recv_buf[LAST_ELEMENT].length - 1 &&
1119
0
          record_check_unprocessed(session) > 0)
1120
0
        return gnutls_assert_val(
1121
0
          GNUTLS_E_INT_CHECK_AGAIN);
1122
0
      else
1123
0
        goto timeout;
1124
0
    }
1125
1.37M
  } else { /* TLS */
1126
1127
1.37M
    if (session->internals.handshake_recv_buffer_size > 0 &&
1128
1.19M
        recv_buf[0].length == recv_buf[0].data.length) {
1129
117k
      if (cmp_hsk_types(htype, recv_buf[0].htype) == 0) {
1130
32.0k
        return gnutls_assert_val(
1131
32.0k
          GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET);
1132
32.0k
      }
1133
1134
85.8k
      _gnutls_handshake_buffer_move(hsk, &recv_buf[0]);
1135
85.8k
      session->internals.handshake_recv_buffer_size--;
1136
85.8k
      return 0;
1137
117k
    } else
1138
1.26M
      return gnutls_assert_val(
1139
1.37M
        GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
1140
1.37M
  }
1141
1142
0
timeout:
1143
0
  RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, 0);
1144
0
}
1145
1146
/* This is a receive function for the gnutls handshake
1147
 * protocol. Makes sure that we have received all data.
1148
 *
1149
 * htype is the next handshake packet expected.
1150
 */
1151
int _gnutls_parse_record_buffered_msgs(gnutls_session_t session)
1152
2.35M
{
1153
2.35M
  gnutls_datum_t msg;
1154
2.35M
  mbuffer_st *bufel = NULL, *prev = NULL;
1155
2.35M
  int ret;
1156
2.35M
  size_t data_size;
1157
2.35M
  handshake_buffer_st *recv_buf =
1158
2.35M
    session->internals.handshake_recv_buffer;
1159
1160
2.35M
  bufel = _mbuffer_head_get_first(&session->internals.record_buffer,
1161
2.35M
          &msg);
1162
2.35M
  if (bufel == NULL)
1163
1.21M
    return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
1164
1165
1.13M
  if (!IS_DTLS(session)) {
1166
1.13M
    ssize_t append, header_size;
1167
1168
1.13M
    do {
1169
1.13M
      if (bufel->type != GNUTLS_HANDSHAKE)
1170
0
        return gnutls_assert_val(
1171
1.13M
          GNUTLS_E_UNEXPECTED_PACKET);
1172
1173
1.13M
      if (unlikely(
1174
1.13M
            session->internals
1175
1.13M
                .handshake_recv_buffer_size ==
1176
1.13M
              0 &&
1177
1.13M
            msg.size < HANDSHAKE_HEADER_SIZE(session) &&
1178
1.13M
            session->internals
1179
1.13M
                .handshake_header_recv_buffer
1180
1.13M
                .byte_length <
1181
1.13M
              HANDSHAKE_HEADER_SIZE(session) -
1182
1.13M
                msg.size)) {
1183
4.02k
        bufel = _mbuffer_head_pop_first(
1184
4.02k
          &session->internals.record_buffer);
1185
4.02k
        _mbuffer_enqueue(
1186
4.02k
          &session->internals
1187
4.02k
             .handshake_header_recv_buffer,
1188
4.02k
          bufel);
1189
4.02k
        break;
1190
1.13M
      } else if (session->internals.handshake_recv_buffer_size >
1191
1.13M
             0 &&
1192
1.04M
           recv_buf[0].length >
1193
1.04M
             recv_buf[0].data.length) {
1194
        /* this is the rest of a previous message */
1195
1.04M
        append = MIN(msg.size,
1196
1.04M
               recv_buf[0].length -
1197
1.04M
                 recv_buf[0].data.length);
1198
1199
1.04M
        ret = _gnutls_buffer_append_data(
1200
1.04M
          &recv_buf[0].data, msg.data, append);
1201
1.04M
        if (ret < 0)
1202
0
          return gnutls_assert_val(ret);
1203
1204
1.04M
        _mbuffer_head_remove_bytes(
1205
1.04M
          &session->internals.record_buffer,
1206
1.04M
          append);
1207
1.04M
      } else { /* received new message */
1208
89.5k
        if (unlikely(
1209
89.5k
              session->internals
1210
89.5k
                .handshake_header_recv_buffer
1211
89.5k
                .length > 0)) {
1212
1.86k
          bufel = _mbuffer_head_pop_first(
1213
1.86k
            &session->internals
1214
1.86k
               .record_buffer);
1215
1.86k
          _mbuffer_enqueue(
1216
1.86k
            &session->internals
1217
1.86k
               .handshake_header_recv_buffer,
1218
1.86k
            bufel);
1219
1.86k
          ret = _mbuffer_linearize_align16(
1220
1.86k
            &session->internals
1221
1.86k
               .handshake_header_recv_buffer,
1222
1.86k
            get_total_headers(session));
1223
1.86k
          if (ret < 0)
1224
0
            return gnutls_assert_val(ret);
1225
1.86k
          bufel = _mbuffer_head_pop_first(
1226
1.86k
            &session->internals
1227
1.86k
               .handshake_header_recv_buffer);
1228
1.86k
          _mbuffer_head_push_first(
1229
1.86k
            &session->internals
1230
1.86k
               .record_buffer,
1231
1.86k
            bufel);
1232
1.86k
        }
1233
1234
89.5k
        ret = parse_handshake_header(session, bufel,
1235
89.5k
                   &recv_buf[0]);
1236
89.5k
        if (ret < 0)
1237
38
          return gnutls_assert_val(ret);
1238
1239
89.5k
        header_size = ret;
1240
89.5k
        session->internals.handshake_recv_buffer_size =
1241
89.5k
          1;
1242
1243
89.5k
        _mbuffer_set_uhead_size(bufel, header_size);
1244
1245
89.5k
        data_size = MIN(recv_buf[0].length,
1246
89.5k
            _mbuffer_get_udata_size(bufel));
1247
89.5k
        ret = _gnutls_buffer_append_data(
1248
89.5k
          &recv_buf[0].data,
1249
89.5k
          _mbuffer_get_udata_ptr(bufel),
1250
89.5k
          data_size);
1251
89.5k
        if (ret < 0)
1252
0
          return gnutls_assert_val(ret);
1253
89.5k
        _mbuffer_set_uhead_size(bufel, 0);
1254
89.5k
        _mbuffer_head_remove_bytes(
1255
89.5k
          &session->internals.record_buffer,
1256
89.5k
          data_size + header_size);
1257
89.5k
      }
1258
1259
      /* if packet is complete then return it
1260
       */
1261
1.13M
      if (recv_buf[0].length == recv_buf[0].data.length) {
1262
86.3k
        return 0;
1263
86.3k
      }
1264
1.04M
      bufel = _mbuffer_head_get_first(
1265
1.04M
        &session->internals.record_buffer, &msg);
1266
1.04M
    } while (bufel != NULL);
1267
1268
    /* if we are here it means that the received packets were not
1269
     * enough to complete the handshake packet.
1270
     */
1271
1.04M
    return gnutls_assert_val(GNUTLS_E_AGAIN);
1272
1.13M
  } else { /* DTLS */
1273
1274
0
    handshake_buffer_st tmp;
1275
1276
0
    do {
1277
      /* we now
1278
       * 0. parse headers
1279
       * 1. insert to handshake_recv_buffer
1280
       * 2. sort handshake_recv_buffer on sequence numbers
1281
       * 3. return first packet if completed or GNUTLS_E_AGAIN.
1282
       */
1283
0
      do {
1284
0
        if (bufel->type != GNUTLS_HANDSHAKE) {
1285
0
          gnutls_assert();
1286
0
          goto next; /* ignore packet */
1287
0
        }
1288
1289
0
        _gnutls_handshake_buffer_init(&tmp);
1290
1291
0
        ret = parse_handshake_header(session, bufel,
1292
0
                   &tmp);
1293
0
        if (ret < 0) {
1294
0
          gnutls_assert();
1295
0
          _gnutls_audit_log(
1296
0
            session,
1297
0
            "Invalid handshake packet headers. Discarding.\n");
1298
0
          break;
1299
0
        }
1300
1301
0
        _mbuffer_consume(
1302
0
          &session->internals.record_buffer,
1303
0
          bufel, ret);
1304
1305
0
        data_size = MIN(tmp.length,
1306
0
            tmp.end_offset -
1307
0
              tmp.start_offset + 1);
1308
1309
0
        ret = _gnutls_buffer_append_data(
1310
0
          &tmp.data,
1311
0
          _mbuffer_get_udata_ptr(bufel),
1312
0
          data_size);
1313
0
        if (ret < 0)
1314
0
          return gnutls_assert_val(ret);
1315
1316
0
        _mbuffer_consume(
1317
0
          &session->internals.record_buffer,
1318
0
          bufel, data_size);
1319
1320
0
        ret = merge_handshake_packet(session, &tmp);
1321
0
        if (ret < 0)
1322
0
          return gnutls_assert_val(ret);
1323
1324
0
      } while (_mbuffer_get_udata_size(bufel) > 0);
1325
1326
0
      prev = bufel;
1327
0
      bufel = _mbuffer_dequeue(
1328
0
        &session->internals.record_buffer, bufel);
1329
1330
0
      _mbuffer_xfree(&prev);
1331
0
      continue;
1332
1333
0
    next:
1334
0
      bufel = _mbuffer_head_get_next(bufel, NULL);
1335
0
    } while (bufel != NULL);
1336
1337
    /* sort in descending order */
1338
0
    if (session->internals.handshake_recv_buffer_size > 1)
1339
0
      qsort(recv_buf,
1340
0
            session->internals.handshake_recv_buffer_size,
1341
0
            sizeof(recv_buf[0]), handshake_compare);
1342
1343
0
    while (session->internals.handshake_recv_buffer_size > 0 &&
1344
0
           recv_buf[LAST_ELEMENT].sequence <
1345
0
             session->internals.dtls.hsk_read_seq) {
1346
0
      _gnutls_audit_log(
1347
0
        session,
1348
0
        "Discarded replayed handshake packet with sequence %d\n",
1349
0
        recv_buf[LAST_ELEMENT].sequence);
1350
0
      _gnutls_handshake_buffer_clear(&recv_buf[LAST_ELEMENT]);
1351
0
      session->internals.handshake_recv_buffer_size--;
1352
0
    }
1353
1354
0
    return 0;
1355
0
  }
1356
1.13M
}
1357
1358
/* This is a receive function for the gnutls handshake
1359
 * protocol. Makes sure that we have received all data.
1360
 */
1361
ssize_t _gnutls_handshake_io_recv_int(gnutls_session_t session,
1362
              gnutls_handshake_description_t htype,
1363
              handshake_buffer_st *hsk,
1364
              unsigned int optional)
1365
1.29M
{
1366
1.29M
  int ret;
1367
1.29M
  unsigned int tleft = 0;
1368
1.29M
  int retries = 7;
1369
1370
1.29M
  ret = get_last_packet(session, htype, hsk, optional);
1371
1.29M
  if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED &&
1372
1.29M
      ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE &&
1373
31.5k
      ret != GNUTLS_E_INT_CHECK_AGAIN) {
1374
31.5k
    return gnutls_assert_val(ret);
1375
31.5k
  }
1376
1377
  /* try using the already existing records before
1378
   * trying to receive.
1379
   */
1380
1.26M
  ret = _gnutls_parse_record_buffered_msgs(session);
1381
1382
1.26M
  if (ret == 0) {
1383
44.3k
    ret = get_last_packet(session, htype, hsk, optional);
1384
44.3k
  }
1385
1386
1.26M
  if (IS_DTLS(session)) {
1387
0
    if (ret >= 0)
1388
0
      return ret;
1389
1.26M
  } else {
1390
1.26M
    if ((ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE && ret < 0) ||
1391
1.24M
        ret >= 0)
1392
46.5k
      return gnutls_assert_val(ret);
1393
1.26M
  }
1394
1395
  /* If handshake is handled manually, don't receive records from I/O */
1396
1.21M
  if (session->internals.h_read_func)
1397
24.0k
    return GNUTLS_E_AGAIN;
1398
1399
1.19M
  if (htype != (gnutls_handshake_description_t)-1) {
1400
1.19M
    ret = handshake_remaining_time(session);
1401
1.19M
    if (ret < 0)
1402
0
      return gnutls_assert_val(ret);
1403
1.19M
    tleft = ret;
1404
1.19M
  }
1405
1406
1.19M
  do {
1407
    /* if we don't have a complete message waiting for us, try
1408
     * receiving more */
1409
1.19M
    ret = _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, htype,
1410
1.19M
                tleft);
1411
1.19M
    if (ret < 0)
1412
102k
      return gnutls_assert_val_fatal(ret);
1413
1414
1.08M
    ret = _gnutls_parse_record_buffered_msgs(session);
1415
1.08M
    if (ret == 0) {
1416
42.0k
      ret = get_last_packet(session, htype, hsk, optional);
1417
42.0k
    }
1418
    /* we put an upper limit (retries) to the number of partial handshake
1419
     * messages in a record packet. */
1420
1.08M
  } while (IS_DTLS(session) && ret == GNUTLS_E_INT_CHECK_AGAIN &&
1421
0
     retries-- > 0);
1422
1423
1.08M
  if (unlikely(IS_DTLS(session) && ret == GNUTLS_E_INT_CHECK_AGAIN)) {
1424
0
    ret = gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS);
1425
0
  }
1426
1427
1.08M
  return ret;
1428
1.19M
}