/* 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); } }
/* will merge the given handshake_buffer_st to the handshake_recv_buffer * list. The given hsk packet will be released in any case (success or failure). * Only used in DTLS. */ static int merge_handshake_packet(gnutls_session_t session, handshake_buffer_st * hsk) { int exists = 0, i, pos = 0; int ret; for (i = 0; i < session->internals.handshake_recv_buffer_size; i++) { if (session->internals.handshake_recv_buffer[i].htype == hsk->htype) { exists = 1; pos = i; break; } } if (!exists) pos = session->internals.handshake_recv_buffer_size; if (pos >= MAX_HANDSHAKE_MSGS) return gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS); if (!exists) { if (hsk->length > 0 && hsk->end_offset > 0 && hsk->end_offset - hsk->start_offset + 1 != hsk->length) { ret = _gnutls_buffer_resize(&hsk->data, hsk->length); if (ret < 0) return gnutls_assert_val(ret); hsk->data.length = hsk->length; memmove(&hsk->data.data[hsk->start_offset], hsk->data.data, hsk->end_offset - hsk->start_offset + 1); } session->internals.handshake_recv_buffer_size++; /* rewrite headers to make them look as each packet came as a single fragment */ _gnutls_write_uint24(hsk->length, &hsk->header[1]); _gnutls_write_uint24(0, &hsk->header[6]); _gnutls_write_uint24(hsk->length, &hsk->header[9]); _gnutls_handshake_buffer_move(&session->internals. handshake_recv_buffer[pos], hsk); } else { if (hsk->start_offset < session->internals.handshake_recv_buffer[pos]. start_offset && hsk->end_offset + 1 >= session->internals.handshake_recv_buffer[pos]. start_offset) { memcpy(&session->internals. handshake_recv_buffer[pos].data.data[hsk-> start_offset], hsk->data.data, hsk->data.length); session->internals.handshake_recv_buffer[pos]. start_offset = hsk->start_offset; session->internals.handshake_recv_buffer[pos]. end_offset = MIN(hsk->end_offset, session->internals. handshake_recv_buffer[pos].end_offset); } else if (hsk->end_offset > session->internals.handshake_recv_buffer[pos]. end_offset && hsk->start_offset <= session->internals.handshake_recv_buffer[pos]. end_offset + 1) { memcpy(&session->internals. handshake_recv_buffer[pos].data.data[hsk-> start_offset], hsk->data.data, hsk->data.length); session->internals.handshake_recv_buffer[pos]. end_offset = hsk->end_offset; session->internals.handshake_recv_buffer[pos]. start_offset = MIN(hsk->start_offset, session->internals. handshake_recv_buffer[pos].start_offset); } _gnutls_handshake_buffer_clear(hsk); } return 0; }
/* 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); }