/* 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; } }
/* 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); } }