/* This function will check if the received record type is * the one we actually expect and adds it to the proper * buffer. The bufel will be deinitialized after calling * this function, even if it fails. */ static int record_add_to_buffers (gnutls_session_t session, struct tls_record_st *recv, content_type_t type, gnutls_handshake_description_t htype, uint64* seq, mbuffer_st* bufel) { int ret; if ((recv->type == type) && (type == GNUTLS_APPLICATION_DATA || type == GNUTLS_CHANGE_CIPHER_SPEC || type == GNUTLS_HANDSHAKE)) { _gnutls_record_buffer_put (session, type, seq, bufel); /* if we received application data as expected then we * deactivate the async timer */ _dtls_async_timer_delete(session); } else { /* if the expected type is different than the received */ switch (recv->type) { case GNUTLS_ALERT: if (bufel->msg.size < 2) { ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); goto unexpected_packet; } _gnutls_record_log ("REC[%p]: Alert[%d|%d] - %s - was received\n", session, bufel->msg.data[0], bufel->msg.data[1], gnutls_alert_get_name ((int) bufel->msg.data[1])); session->internals.last_alert = bufel->msg.data[1]; /* if close notify is received and * the alert is not fatal */ if (bufel->msg.data[1] == GNUTLS_A_CLOSE_NOTIFY && bufel->msg.data[0] != GNUTLS_AL_FATAL) { /* If we have been expecting for an alert do */ session->internals.read_eof = 1; ret = GNUTLS_E_SESSION_EOF; goto cleanup; } else { /* if the alert is FATAL or WARNING * return the apropriate message */ gnutls_assert (); ret = GNUTLS_E_WARNING_ALERT_RECEIVED; if (bufel->msg.data[0] == GNUTLS_AL_FATAL) { session_unresumable (session); session_invalidate (session); ret = gnutls_assert_val(GNUTLS_E_FATAL_ALERT_RECEIVED); } goto cleanup; } break; case GNUTLS_CHANGE_CIPHER_SPEC: if (!(IS_DTLS(session))) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); _gnutls_record_buffer_put (session, recv->type, seq, bufel); break; #ifdef ENABLE_HEARTBEAT case GNUTLS_HEARTBEAT: ret = _gnutls_heartbeat_handle (session, bufel); goto cleanup; #endif case GNUTLS_APPLICATION_DATA: if (session->internals.initial_negotiation_completed == 0) { ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); goto unexpected_packet; } /* the got_application data is only returned * if expecting client hello (for rehandshake * reasons). Otherwise it is an unexpected packet */ if (type == GNUTLS_ALERT || (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && type == GNUTLS_HANDSHAKE)) { /* even if data is unexpected put it into the buffer */ if ((ret = _gnutls_record_buffer_put (session, recv->type, seq, bufel)) < 0) { gnutls_assert (); goto cleanup; } return gnutls_assert_val(GNUTLS_E_GOT_APPLICATION_DATA); } else { ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); goto unexpected_packet; } break; case GNUTLS_HANDSHAKE: /* In DTLS we might receive a handshake replay from the peer to indicate * the our last TLS handshake messages were not received. */ if (IS_DTLS(session)) { if (type == GNUTLS_CHANGE_CIPHER_SPEC) { ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); goto unexpected_packet; } if (_dtls_is_async(session) && _dtls_async_timer_active(session)) { if (session->security_parameters.entity == GNUTLS_SERVER && bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO) { /* client requested rehandshake. Delete the timer */ _dtls_async_timer_delete(session); } else { session->internals.recv_state = RECV_STATE_DTLS_RETRANSMIT; ret = _dtls_retransmit(session); if (ret == 0) { session->internals.recv_state = RECV_STATE_0; ret = gnutls_assert_val(GNUTLS_E_AGAIN); goto unexpected_packet; } goto cleanup; } } } /* This is legal if HELLO_REQUEST is received - and we are a client. * If we are a server, a client may initiate a renegotiation at any time. */ if (session->security_parameters.entity == GNUTLS_SERVER && bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO) { gnutls_assert (); ret = _gnutls_record_buffer_put (session, recv->type, seq, bufel); if (ret < 0) { gnutls_assert (); goto cleanup; } return GNUTLS_E_REHANDSHAKE; } /* If we are already in a handshake then a Hello * Request is illegal. But here we don't really care * since this message will never make it up here. */ /* So we accept it, if it is a Hello. If not, this will * fail and trigger flight retransmissions after some time. */ ret = _gnutls_recv_hello_request (session, bufel->msg.data, bufel->msg.size); goto unexpected_packet; break; default: _gnutls_record_log ("REC[%p]: Received unexpected packet %d (%s) expecting %d (%s)\n", session, recv->type, _gnutls_packet2str(recv->type), type, _gnutls_packet2str(type)); gnutls_assert (); ret = GNUTLS_E_UNEXPECTED_PACKET; goto unexpected_packet; } } return 0; unexpected_packet: if (IS_DTLS(session) && ret != GNUTLS_E_REHANDSHAKE) { _mbuffer_xfree(&bufel); RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret); } cleanup: _mbuffer_xfree(&bufel); return ret; }
/* This function will check if the received record type is * the one we actually expect. */ static int record_check_type (gnutls_session_t session, content_type_t recv_type, content_type_t type, gnutls_handshake_description_t htype, opaque * data, int data_size) { int ret; if ((recv_type == type) && (type == GNUTLS_APPLICATION_DATA || type == GNUTLS_HANDSHAKE || type == GNUTLS_INNER_APPLICATION)) { _gnutls_record_buffer_put (type, session, (void *) data, data_size); } else { switch (recv_type) { case GNUTLS_ALERT: _gnutls_record_log ("REC[%p]: Alert[%d|%d] - %s - was received\n", session, data[0], data[1], gnutls_alert_get_name ((int) data[1])); session->internals.last_alert = data[1]; /* if close notify is received and * the alert is not fatal */ if (data[1] == GNUTLS_A_CLOSE_NOTIFY && data[0] != GNUTLS_AL_FATAL) { /* If we have been expecting for an alert do */ session->internals.read_eof = 1; return GNUTLS_E_INT_RET_0; /* EOF */ } else { /* if the alert is FATAL or WARNING * return the apropriate message */ gnutls_assert (); ret = GNUTLS_E_WARNING_ALERT_RECEIVED; if (data[0] == GNUTLS_AL_FATAL) { session_unresumable (session); session_invalidate (session); ret = GNUTLS_E_FATAL_ALERT_RECEIVED; } return ret; } break; case GNUTLS_CHANGE_CIPHER_SPEC: /* this packet is now handled in the recv_int() * function */ gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET; case GNUTLS_APPLICATION_DATA: if (session->internals.initial_negotiation_completed == 0) { return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); } /* even if data is unexpected put it into the buffer */ if ((ret = _gnutls_record_buffer_put (recv_type, session, (void *) data, data_size)) < 0) { gnutls_assert (); return ret; } /* the got_application data is only returned * if expecting client hello (for rehandshake * reasons). Otherwise it is an unexpected packet */ if (type == GNUTLS_ALERT || (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && type == GNUTLS_HANDSHAKE)) return GNUTLS_E_GOT_APPLICATION_DATA; else { gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET; } break; case GNUTLS_HANDSHAKE: /* This is legal if HELLO_REQUEST is received - and we are a client. * If we are a server, a client may initiate a renegotiation at any time. */ if (session->security_parameters.entity == GNUTLS_SERVER) { gnutls_assert (); ret = _gnutls_record_buffer_put (recv_type, session, (void *) data, data_size); if (ret < 0) { gnutls_assert (); return ret; } return GNUTLS_E_REHANDSHAKE; } /* If we are already in a handshake then a Hello * Request is illegal. But here we don't really care * since this message will never make it up here. */ /* So we accept it */ return _gnutls_recv_hello_request (session, data, data_size); break; case GNUTLS_INNER_APPLICATION: /* even if data is unexpected put it into the buffer */ if ((ret = _gnutls_record_buffer_put (recv_type, session, (void *) data, data_size)) < 0) { gnutls_assert (); return ret; } gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET; break; default: _gnutls_record_log ("REC[%p]: Received Unknown packet %d expecting %d\n", session, recv_type, type); gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } } return 0; }