/* Takes a buffer in multiple chunks and puts all the data in a single * contiguous segment, ensuring that the @align_pos is 16-byte aligned. * * Returns 0 on success or an error code otherwise. * * Cost: O(n) * n: number of segments initially in the buffer */ int _mbuffer_linearize_align16(mbuffer_head_st * buf, unsigned align_pos) { mbuffer_st *bufel, *cur; gnutls_datum_t msg; size_t pos = 0; if (buf->length == 0) { /* Nothing to do */ return 0; } bufel = _mbuffer_head_get_first(buf, NULL); if (buf->length == 1 && is_aligned16(bufel, align_pos)) return 0; bufel = _mbuffer_alloc_align16(buf->byte_length, align_pos); if (!bufel) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } for (cur = _mbuffer_head_get_first(buf, &msg); msg.data != NULL; cur = _mbuffer_head_get_next(cur, &msg)) { memcpy(&bufel->msg.data[pos], msg.data, msg.size); bufel->msg.size += msg.size; pos += msg.size; } _mbuffer_head_clear(buf); _mbuffer_enqueue(buf, bufel); return 0; }
/* This function writes the data that are left in the * Handshake write buffer (ie. because the previous write was * interrupted. * */ ssize_t _gnutls_handshake_io_write_flush (gnutls_session_t session) { mbuffer_head_st *const send_buffer = &session->internals.handshake_send_buffer; gnutls_datum_t msg; int ret; uint16_t epoch; ssize_t total = 0; mbuffer_st *cur; _gnutls_write_log ("HWRITE FLUSH: %d bytes in buffer.\n", (int) send_buffer->byte_length); if (IS_DTLS(session)) return _dtls_transmit(session); for (cur = _mbuffer_head_get_first (send_buffer, &msg); cur != NULL; cur = _mbuffer_head_get_first (send_buffer, &msg)) { epoch = cur->epoch; ret = _gnutls_send_int (session, cur->type, cur->htype, epoch, msg.data, msg.size, 0); if (ret >= 0) { total += ret; ret = _mbuffer_head_remove_bytes (send_buffer, ret); if (ret == 1) _gnutls_epoch_refcount_dec(session, epoch); _gnutls_write_log ("HWRITE: wrote %d bytes, %d bytes left.\n", ret, (int) send_buffer->byte_length); } else { _gnutls_write_log ("HWRITE error: code %d, %d bytes left.\n", ret, (int) send_buffer->byte_length); gnutls_assert (); return ret; } } return _gnutls_io_write_flush (session); }
int _gnutls_record_buffer_get (content_type_t type, gnutls_session_t session, opaque * data, size_t length, opaque seq[8]) { gnutls_datum_t msg; mbuffer_st* bufel; if (length == 0 || data == NULL) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg); if (bufel == NULL) return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); if (type != bufel->type) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); if (msg.size <= length) length = msg.size; if (seq) memcpy(seq, bufel->record_sequence.i, 8); memcpy(data, msg.data, length); _mbuffer_head_remove_bytes(&session->internals.record_buffer, length); return length; }
/* This function writes the data that are left in the * TLS write buffer (ie. because the previous write was * interrupted. */ ssize_t _gnutls_io_write_flush (gnutls_session_t session) { gnutls_datum_t msg; mbuffer_head_st *send_buffer = &session->internals.record_send_buffer; int ret; ssize_t sent = 0, tosend = 0; giovec_t iovec[MAX_QUEUE]; int i = 0; mbuffer_st *cur; _gnutls_write_log ("WRITE FLUSH: %d bytes in buffer.\n", (int) send_buffer->byte_length); for (cur = _mbuffer_head_get_first (send_buffer, &msg); cur != NULL; cur = _mbuffer_head_get_next (cur, &msg)) { iovec[i].iov_base = msg.data; iovec[i++].iov_len = msg.size; tosend += msg.size; /* we buffer up to MAX_QUEUE messages */ if (i >= MAX_QUEUE) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } } ret = _gnutls_writev (session, iovec, i); if (ret >= 0) { _mbuffer_head_remove_bytes (send_buffer, ret); _gnutls_write_log ("WRITE: wrote %d bytes, %d bytes left.\n", ret, (int) send_buffer->byte_length); sent += ret; } else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { _gnutls_write_log ("WRITE interrupted: %d bytes left.\n", (int) send_buffer->byte_length); return ret; } else { _gnutls_write_log ("WRITE error: code %d, %d bytes left.\n", ret, (int) send_buffer->byte_length); gnutls_assert (); return ret; } if (sent < tosend) { return gnutls_assert_val(GNUTLS_E_AGAIN); } return sent; }
int _gnutls_record_buffer_get(content_type_t type, gnutls_session_t session, uint8_t * data, size_t length, uint8_t seq[8]) { gnutls_datum_t msg; mbuffer_st *bufel; if (length == 0 || data == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg); if (bufel == NULL) return gnutls_assert_val (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); if (type != bufel->type) { if (IS_DTLS(session)) _gnutls_audit_log(session, "Discarded unexpected %s (%d) packet (expecting: %s (%d))\n", _gnutls_packet2str(bufel->type), (int) bufel->type, _gnutls_packet2str(type), (int) type); else _gnutls_debug_log("received unexpected packet: %s(%d)\n", _gnutls_packet2str(bufel->type), (int)bufel->type); _mbuffer_head_remove_bytes(&session->internals. record_buffer, msg.size); return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); } if (msg.size <= length) length = msg.size; if (seq) memcpy(seq, bufel->record_sequence.i, 8); memcpy(data, msg.data, length); _mbuffer_head_remove_bytes(&session->internals.record_buffer, length); return length; }
/* This is a receive function for the gnutls handshake * protocol. Makes sure that we have received all data. * * htype is the next handshake packet expected. */ int _gnutls_parse_record_buffered_msgs(gnutls_session_t session) { gnutls_datum_t msg; mbuffer_st *bufel = NULL, *prev = NULL; int ret; size_t data_size; handshake_buffer_st *recv_buf = session->internals.handshake_recv_buffer; bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg); if (bufel == NULL) return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; if (!IS_DTLS(session)) { ssize_t append, header_size; do { if (bufel->type != GNUTLS_HANDSHAKE) return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET); if (unlikely (session->internals.handshake_recv_buffer_size == 0 && msg.size < HANDSHAKE_HEADER_SIZE(session) && session->internals.handshake_header_recv_buffer.byte_length < HANDSHAKE_HEADER_SIZE(session) - msg.size)) { bufel = _mbuffer_head_pop_first(&session->internals.record_buffer); _mbuffer_enqueue(&session->internals.handshake_header_recv_buffer, bufel); break; } else if (session->internals.handshake_recv_buffer_size > 0 && recv_buf[0].length > recv_buf[0].data.length) { /* this is the rest of a previous message */ append = MIN(msg.size, recv_buf[0].length - recv_buf[0].data.length); ret = _gnutls_buffer_append_data(&recv_buf [0].data, msg.data, append); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_head_remove_bytes(&session-> internals. record_buffer, append); } else { /* received new message */ if (unlikely (session->internals. handshake_header_recv_buffer.length > 0)) { bufel = _mbuffer_head_pop_first(&session->internals. record_buffer); _mbuffer_enqueue(&session->internals. handshake_header_recv_buffer, bufel); ret = _mbuffer_linearize_align16(&session->internals. handshake_header_recv_buffer, get_total_headers(session)); if (ret < 0) return gnutls_assert_val(ret); bufel = _mbuffer_head_pop_first(&session->internals. handshake_header_recv_buffer); _mbuffer_head_push_first(&session->internals. record_buffer, bufel); } ret = parse_handshake_header(session, bufel, &recv_buf[0]); if (ret < 0) return gnutls_assert_val(ret); header_size = ret; session->internals. handshake_recv_buffer_size = 1; _mbuffer_set_uhead_size(bufel, header_size); data_size = MIN(recv_buf[0].length, _mbuffer_get_udata_size(bufel)); ret = _gnutls_buffer_append_data(&recv_buf [0].data, _mbuffer_get_udata_ptr (bufel), data_size); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_set_uhead_size(bufel, 0); _mbuffer_head_remove_bytes(&session-> internals. record_buffer, data_size + header_size); } /* if packet is complete then return it */ if (recv_buf[0].length == recv_buf[0].data.length) { return 0; } bufel = _mbuffer_head_get_first(&session->internals. record_buffer, &msg); } while (bufel != NULL); /* if we are here it means that the received packets were not * enough to complete the handshake packet. */ return gnutls_assert_val(GNUTLS_E_AGAIN); } else { /* DTLS */ handshake_buffer_st tmp; do { /* we now * 0. parse headers * 1. insert to handshake_recv_buffer * 2. sort handshake_recv_buffer on sequence numbers * 3. return first packet if completed or GNUTLS_E_AGAIN. */ do { if (bufel->type != GNUTLS_HANDSHAKE) { gnutls_assert(); goto next; /* ignore packet */ } _gnutls_handshake_buffer_init(&tmp); ret = parse_handshake_header(session, bufel, &tmp); if (ret < 0) { gnutls_assert(); _gnutls_audit_log(session, "Invalid handshake packet headers. Discarding.\n"); break; } _mbuffer_consume(&session->internals. record_buffer, bufel, ret); data_size = MIN(tmp.length, tmp.end_offset - tmp.start_offset + 1); ret = _gnutls_buffer_append_data(&tmp.data, _mbuffer_get_udata_ptr (bufel), data_size); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_consume(&session->internals. record_buffer, bufel, data_size); ret = merge_handshake_packet(session, &tmp); if (ret < 0) return gnutls_assert_val(ret); } while (_mbuffer_get_udata_size(bufel) > 0); prev = bufel; bufel = _mbuffer_dequeue(&session->internals. record_buffer, bufel); _mbuffer_xfree(&prev); continue; next: bufel = _mbuffer_head_get_next(bufel, NULL); } while (bufel != NULL); /* sort in descending order */ if (session->internals.handshake_recv_buffer_size > 1) qsort(recv_buf, session->internals. handshake_recv_buffer_size, sizeof(recv_buf[0]), handshake_compare); while (session->internals.handshake_recv_buffer_size > 0 && recv_buf[LAST_ELEMENT].sequence < session->internals.dtls.hsk_read_seq) { _gnutls_audit_log(session, "Discarded replayed handshake packet with sequence %d\n", recv_buf[LAST_ELEMENT].sequence); _gnutls_handshake_buffer_clear(&recv_buf [LAST_ELEMENT]); session->internals.handshake_recv_buffer_size--; } return 0; } }
/* @ms: is the number of milliseconds to wait for data. Use zero for indefinite. * * This will receive record layer packets and add them to * application_data_buffer and handshake_data_buffer. * * If the htype is not -1 then handshake timeouts * will be enforced. */ ssize_t _gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, unsigned int ms) { uint64 *packet_sequence; gnutls_datum_t ciphertext; mbuffer_st* bufel = NULL, *decrypted = NULL; gnutls_datum_t t; int ret; unsigned int empty_fragments = 0; record_parameters_st *record_params; record_state_st *record_state; struct tls_record_st record; begin: if (empty_fragments > session->internals.priorities.max_empty_records) { gnutls_assert (); return GNUTLS_E_TOO_MANY_EMPTY_PACKETS; } if (session->internals.read_eof != 0) { /* if we have already read an EOF */ return 0; } else if (session_is_valid (session) != 0 || session->internals.may_not_read != 0) return gnutls_assert_val(GNUTLS_E_INVALID_SESSION); /* get the record state parameters */ ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params); if (ret < 0) return gnutls_assert_val (ret); /* Safeguard against processing data with an incomplete cipher state. */ if (!record_params->initialized) return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR); record_state = &record_params->read; /* receive headers */ ret = recv_headers(session, type, htype, &record, &ms); if (ret < 0) { ret = gnutls_assert_val_fatal(ret); goto recv_error; } if (IS_DTLS(session)) packet_sequence = &record.sequence; else packet_sequence = &record_state->sequence_number; /* Read the packet data and insert it to record_recv_buffer. */ ret = _gnutls_io_read_buffered (session, record.packet_size, record.type, &ms); if (ret != record.packet_size) { gnutls_assert(); goto recv_error; } /* ok now we are sure that we have read all the data - so * move on ! */ ret = _mbuffer_linearize (&session->internals.record_recv_buffer); if (ret < 0) return gnutls_assert_val(ret); bufel = _mbuffer_head_get_first (&session->internals.record_recv_buffer, NULL); if (bufel == NULL) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); /* We allocate the maximum possible to allow few compressed bytes to expand to a * full record. */ decrypted = _mbuffer_alloc(record.length, record.length); if (decrypted == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ciphertext.data = (uint8_t*)_mbuffer_get_udata_ptr(bufel) + record.header_size; ciphertext.size = record.length; /* decrypt the data we got. */ t.data = _mbuffer_get_udata_ptr(decrypted); t.size = _mbuffer_get_udata_size(decrypted); ret = _gnutls_decrypt (session, &ciphertext, &t, record.type, record_params, packet_sequence); if (ret >= 0) _mbuffer_set_udata_size(decrypted, ret); _mbuffer_head_remove_bytes (&session->internals.record_recv_buffer, record.header_size + record.length); if (ret < 0) { gnutls_assert(); _gnutls_audit_log(session, "Discarded message[%u] due to invalid decryption\n", (unsigned int)_gnutls_uint64touint32 (packet_sequence)); goto sanity_check_error; } /* check for duplicates. We check after the message * is processed and authenticated to avoid someone * messing with our windows. */ if (IS_DTLS(session) && session->internals.no_replay_protection == 0) { ret = _dtls_record_check(record_params, packet_sequence); if (ret < 0) { _gnutls_audit_log(session, "Discarded duplicate message[%u.%u]: %s\n", (unsigned int)record.sequence.i[0]*256 +(unsigned int)record.sequence.i[1], (unsigned int) _gnutls_uint64touint32 (packet_sequence), _gnutls_packet2str (record.type)); goto sanity_check_error; } _gnutls_record_log ("REC[%p]: Decrypted Packet[%u.%u] %s(%d) with length: %d\n", session, (unsigned int)record.sequence.i[0]*256 +(unsigned int)record.sequence.i[1], (unsigned int) _gnutls_uint64touint32 (packet_sequence), _gnutls_packet2str (record.type), record.type, (int)_mbuffer_get_udata_size(decrypted)); } else { _gnutls_record_log ("REC[%p]: Decrypted Packet[%u] %s(%d) with length: %d\n", session, (unsigned int) _gnutls_uint64touint32 (packet_sequence), _gnutls_packet2str (record.type), record.type, (int)_mbuffer_get_udata_size(decrypted)); } /* increase sequence number */ if (!IS_DTLS(session) && sequence_increment (session, &record_state->sequence_number) != 0) { session_invalidate (session); gnutls_assert (); ret = GNUTLS_E_RECORD_LIMIT_REACHED; goto sanity_check_error; } /* (originally for) TLS 1.0 CBC protection. * Actually this code is called if we just received * an empty packet. An empty TLS packet is usually * sent to protect some vulnerabilities in the CBC mode. * In that case we go to the beginning and start reading * the next packet. */ if (_mbuffer_get_udata_size(decrypted) == 0) { _mbuffer_xfree(&decrypted); empty_fragments++; goto begin; } if (record.v2) decrypted->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2; else { uint8_t * p = _mbuffer_get_udata_ptr(decrypted); decrypted->htype = p[0]; } ret = record_add_to_buffers (session, &record, type, htype, packet_sequence, decrypted); /* bufel is now either deinitialized or buffered somewhere else */ if (ret < 0) return gnutls_assert_val(ret); return ret; discard: session->internals.dtls.packets_dropped++; /* discard the whole received fragment. */ bufel = _mbuffer_head_pop_first(&session->internals.record_recv_buffer); _mbuffer_xfree(&bufel); return gnutls_assert_val(GNUTLS_E_AGAIN); sanity_check_error: if (IS_DTLS(session)) { session->internals.dtls.packets_dropped++; ret = gnutls_assert_val(GNUTLS_E_AGAIN); goto cleanup; } session_unresumable (session); session_invalidate (session); cleanup: _mbuffer_xfree(&decrypted); return ret; recv_error: if (ret < 0 && (gnutls_error_is_fatal (ret) == 0 || ret == GNUTLS_E_TIMEDOUT)) return ret; if (type == GNUTLS_ALERT) /* we were expecting close notify */ { session_invalidate (session); gnutls_assert (); return 0; } if (IS_DTLS(session)) { goto discard; } session_invalidate (session); session_unresumable (session); if (ret == 0) return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; else return ret; }
static int recv_headers( gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, struct tls_record_st* record, unsigned int *ms) { int ret; gnutls_datum_t raw; /* raw headers */ /* Read the headers. */ record->header_size = record->packet_size = RECORD_HEADER_SIZE(session); ret = _gnutls_io_read_buffered (session, record->header_size, -1, ms); if (ret != record->header_size) { if (ret < 0 && gnutls_error_is_fatal (ret) == 0) return ret; if (ret > 0) ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; else if (ret == 0) ret = GNUTLS_E_PREMATURE_TERMINATION; return gnutls_assert_val(ret); } ret = _mbuffer_linearize (&session->internals.record_recv_buffer); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_head_get_first (&session->internals.record_recv_buffer, &raw); if (raw.size < RECORD_HEADER_SIZE(session)) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); record_read_headers (session, raw.data, type, htype, record); /* Check if the DTLS epoch is valid */ if (IS_DTLS(session)) { if (_gnutls_epoch_is_valid(session, record->epoch) == 0) { _gnutls_audit_log(session, "Discarded message[%u] with invalid epoch %u.\n", (unsigned int)_gnutls_uint64touint32 (&record->sequence), (unsigned int)record->sequence.i[0]*256+(unsigned int)record->sequence.i[1]); gnutls_assert(); /* doesn't matter, just a fatal error */ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } } /* Here we check if the Type of the received packet is * ok. */ if ((ret = check_recv_type (session, record->type)) < 0) return gnutls_assert_val(ret); /* Here we check if the advertized version is the one we * negotiated in the handshake. */ if ((ret = record_check_version (session, htype, record->version)) < 0) return gnutls_assert_val(ret); if (record->length > MAX_RECV_SIZE(session)) { _gnutls_audit_log (session, "Received packet with illegal length: %u\n", (unsigned int)record->length); return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); } _gnutls_record_log ("REC[%p]: Expected Packet %s(%d)\n", session, _gnutls_packet2str (type), type); _gnutls_record_log ("REC[%p]: Received Packet %s(%d) with length: %d\n", session, _gnutls_packet2str (record->type), record->type, record->length); return 0; }
/* * Return zero if session tickets haven't been enabled. */ int _gnutls_recv_new_session_ticket(gnutls_session_t session) { uint8_t *p; int data_size; gnutls_buffer_st buf; uint16_t ticket_len; int ret; session_ticket_ext_st *priv = NULL; gnutls_ext_priv_data_t epriv; if (session->internals.flags & GNUTLS_NO_TICKETS) return 0; if (!session->internals.session_ticket_renew) return 0; /* This is the last flight and peer cannot be sure * we have received it unless we notify him. So we * wait for a message and retransmit if needed. */ if (IS_DTLS(session) && !_dtls_is_async(session)) { unsigned have; mbuffer_st *bufel = NULL; have = gnutls_record_check_pending(session) + record_check_unprocessed(session); if (have != 0) { bufel = _mbuffer_head_get_first(&session->internals.record_buffer, NULL); } if (have == 0 || (bufel && bufel->type != GNUTLS_HANDSHAKE)) { ret = _dtls_wait_and_retransmit(session); if (ret < 0) return gnutls_assert_val(ret); } } ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET, 0, &buf); if (ret < 0) return gnutls_assert_val_fatal(ret); p = buf.data; data_size = buf.length; DECR_LENGTH_COM(data_size, 4, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error); /* skip over lifetime hint */ p += 4; DECR_LENGTH_COM(data_size, 2, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error); ticket_len = _gnutls_read_uint16(p); p += 2; DECR_LENGTH_COM(data_size, ticket_len, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error); priv = gnutls_calloc(1, sizeof(*priv)); if (!priv) { gnutls_assert(); ret = GNUTLS_E_MEMORY_ERROR; goto error; } priv->session_ticket = gnutls_realloc_fast(priv->session_ticket, ticket_len); if (!priv->session_ticket) { gnutls_free(priv); gnutls_assert(); ret = GNUTLS_E_MEMORY_ERROR; goto error; } memcpy(priv->session_ticket, p, ticket_len); priv->session_ticket_len = ticket_len; epriv = priv; /* Discard the current session ID. (RFC5077 3.4) */ ret = _gnutls_generate_session_id(session->security_parameters. session_id, &session->security_parameters. session_id_size); if (ret < 0) { gnutls_assert(); session_ticket_deinit_data(epriv); ret = GNUTLS_E_INTERNAL_ERROR; goto error; } ret = 0; _gnutls_handshake_log ("HSK[%p]: received session ticket\n", session); session->internals.hsk_flags |= HSK_TICKET_RECEIVED; _gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_SESSION_TICKET, epriv); error: _gnutls_buffer_clear(&buf); return ret; }
/* This is a receive function for the gnutls handshake * protocol. Makes sure that we have received all data. */ static int parse_record_buffered_msgs (gnutls_session_t session, gnutls_handshake_description_t htype, handshake_buffer_st * hsk) { gnutls_datum_t msg; mbuffer_st* bufel = NULL, *prev = NULL; int ret; size_t data_size; handshake_buffer_st* recv_buf = session->internals.handshake_recv_buffer; bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg); if (bufel == NULL) return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; if (!IS_DTLS(session)) { ssize_t remain, append, header_size; do { if (bufel->type != GNUTLS_HANDSHAKE) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); /* if we have a half received message the complete it. */ remain = recv_buf[0].length - recv_buf[0].data.length; /* this is the rest of a previous message */ if (session->internals.handshake_recv_buffer_size > 0 && recv_buf[0].length > 0 && remain > 0) { if (msg.size <= remain) append = msg.size; else append = remain; ret = _gnutls_buffer_append_data(&recv_buf[0].data, msg.data, append); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_head_remove_bytes(&session->internals.record_buffer, append); } else /* received new message */ { ret = parse_handshake_header(session, bufel, htype, &recv_buf[0]); if (ret < 0) return gnutls_assert_val(ret); header_size = ret; session->internals.handshake_recv_buffer_size = 1; _mbuffer_set_uhead_size(bufel, header_size); data_size = MIN(recv_buf[0].length, _mbuffer_get_udata_size(bufel)); ret = _gnutls_buffer_append_data(&recv_buf[0].data, _mbuffer_get_udata_ptr(bufel), data_size); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_set_uhead_size(bufel, 0); _mbuffer_head_remove_bytes(&session->internals.record_buffer, data_size+header_size); if (cmp_hsk_types(htype, recv_buf[0].htype) == 0) { /* an unexpected packet */ hsk->htype = recv_buf[0].htype; return gnutls_assert_val(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET); } } /* if packet is complete then return it */ if (recv_buf[0].length == recv_buf[0].data.length) { return get_last_packet(session, htype, hsk); } bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg); } while(bufel != NULL); /* if we are here it means that the received packets were not * enough to complete the handshake packet. */ return gnutls_assert_val(GNUTLS_E_AGAIN); } else /* DTLS */ { handshake_buffer_st tmp; do { /* we now * 0. parse headers * 1. insert to handshake_recv_buffer * 2. sort handshake_recv_buffer on sequence numbers * 3. return first packet if completed or GNUTLS_E_AGAIN. */ do { if (bufel->type != GNUTLS_HANDSHAKE) { gnutls_assert(); goto next; /* ignore packet */ } _gnutls_handshake_buffer_init(&tmp); ret = parse_handshake_header(session, bufel, htype, &tmp); if (ret < 0) { gnutls_assert(); _gnutls_audit_log("Invalid handshake packet headers. Discarding.\n"); break; } _mbuffer_consume(&session->internals.record_buffer, bufel, ret); data_size = MIN(tmp.length, tmp.end_offset-tmp.start_offset+1); ret = _gnutls_buffer_append_data(&tmp.data, _mbuffer_get_udata_ptr(bufel), data_size); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_consume(&session->internals.record_buffer, bufel, data_size); ret = merge_handshake_packet(session, &tmp); if (ret < 0) return gnutls_assert_val(ret); } while(_mbuffer_get_udata_size(bufel) > 0); prev = bufel; bufel = _mbuffer_dequeue(&session->internals.record_buffer, bufel); _mbuffer_xfree(&prev); continue; next: bufel = _mbuffer_head_get_next(bufel, NULL); } while(bufel != NULL); /* sort in descending order */ if (session->internals.handshake_recv_buffer_size > 1) qsort(recv_buf, session->internals.handshake_recv_buffer_size, sizeof(recv_buf[0]), handshake_compare); while(session->internals.handshake_recv_buffer_size > 0 && recv_buf[LAST_ELEMENT].sequence < session->internals.dtls.hsk_read_seq) { _gnutls_audit_log("Discarded replayed handshake packet with sequence %d\n", recv_buf[LAST_ELEMENT].sequence); _gnutls_handshake_buffer_clear(&recv_buf[LAST_ELEMENT]); session->internals.handshake_recv_buffer_size--; } return get_last_packet(session, htype, hsk); } }