/* returns the last stored handshake packet. */ static int get_last_packet(gnutls_session_t session, gnutls_handshake_description_t htype, handshake_buffer_st * hsk) { handshake_buffer_st* recv_buf = session->internals.handshake_recv_buffer; if (IS_DTLS(session)) { if (session->internals.handshake_recv_buffer_size == 0 || (session->internals.dtls.hsk_read_seq != recv_buf[LAST_ELEMENT].sequence)) goto timeout; if (htype != recv_buf[LAST_ELEMENT].htype) { hsk->htype = recv_buf[LAST_ELEMENT].htype; return gnutls_assert_val(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET); } else if ((recv_buf[LAST_ELEMENT].start_offset == 0 && recv_buf[LAST_ELEMENT].end_offset == recv_buf[LAST_ELEMENT].length -1) || recv_buf[LAST_ELEMENT].length == 0) { session->internals.dtls.hsk_read_seq++; _gnutls_handshake_buffer_move(hsk, &recv_buf[LAST_ELEMENT]); session->internals.handshake_recv_buffer_size--; return 0; } else goto timeout; } else /* TLS */ { if (session->internals.handshake_recv_buffer_size > 0 && recv_buf[0].length == recv_buf[0].data.length) { if (cmp_hsk_types(htype, recv_buf[0].htype) == 0) { hsk->htype = recv_buf[LAST_ELEMENT].htype; return gnutls_assert_val(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET); } _gnutls_handshake_buffer_move(hsk, &recv_buf[0]); session->internals.handshake_recv_buffer_size--; return 0; } else return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); } timeout: if (time(0)-session->internals.dtls.handshake_start_time > session->internals.dtls.total_timeout/1000) return gnutls_assert_val(GNUTLS_E_TIMEDOUT); else { if (session->internals.dtls.blocking != 0) millisleep(50); return gnutls_assert_val(GNUTLS_E_AGAIN); } }
/* returns the last stored handshake packet. */ static int get_last_packet(gnutls_session_t session, gnutls_handshake_description_t htype, handshake_buffer_st * hsk, unsigned int optional) { handshake_buffer_st *recv_buf = session->internals.handshake_recv_buffer; if (IS_DTLS(session)) { if (session->internals.handshake_recv_buffer_size == 0 || (session->internals.dtls.hsk_read_seq != recv_buf[LAST_ELEMENT].sequence)) goto timeout; if (htype != recv_buf[LAST_ELEMENT].htype) { if (optional == 0) _gnutls_audit_log(session, "Received unexpected handshake message '%s' (%d). Expected '%s' (%d)\n", _gnutls_handshake2str (recv_buf[0].htype), (int) recv_buf[0].htype, _gnutls_handshake2str (htype), (int) htype); return gnutls_assert_val (GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET); } else if ((recv_buf[LAST_ELEMENT].start_offset == 0 && recv_buf[LAST_ELEMENT].end_offset == recv_buf[LAST_ELEMENT].length - 1) || recv_buf[LAST_ELEMENT].length == 0) { session->internals.dtls.hsk_read_seq++; _gnutls_handshake_buffer_move(hsk, &recv_buf [LAST_ELEMENT]); session->internals.handshake_recv_buffer_size--; return 0; } else { /* if we don't have a complete handshake message, but we * have queued data waiting, try again to reconstruct the * handshake packet, using the queued */ if (recv_buf[LAST_ELEMENT].end_offset != recv_buf[LAST_ELEMENT].length - 1 && record_check_unprocessed(session) > 0) return gnutls_assert_val(GNUTLS_E_INT_CHECK_AGAIN); else goto timeout; } } else { /* TLS */ if (session->internals.handshake_recv_buffer_size > 0 && recv_buf[0].length == recv_buf[0].data.length) { if (cmp_hsk_types(htype, recv_buf[0].htype) == 0) { return gnutls_assert_val (GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET); } _gnutls_handshake_buffer_move(hsk, &recv_buf[0]); session->internals.handshake_recv_buffer_size--; return 0; } else return gnutls_assert_val (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); } timeout: RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, 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); } }