diff --git a/CHANGELOG.md b/CHANGELOG.md index 590813c4..fd2026ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ The git repositories are hosted at the following sites: ### Security +* Secured GetEventInformation-Request and -ACK decoder and encoder. (#1026) + ### Added * Added more writable properties to Analog, Binary, and Multistate Output @@ -48,6 +50,8 @@ The git repositories are hosted at the following sites: ### Fixed +* Fixed GetEvent usage of linked list by initializing next in all + the examples and unit test. (#1026) * Fixed usage of Keylist_Data_Add() return value in Calendar, CharacterString Value, Load Control, and BACnet/SC Network Port objects. (#1016) diff --git a/apps/getevent/main.c b/apps/getevent/main.c index 94071dd4..4c8e7b28 100644 --- a/apps/getevent/main.c +++ b/apps/getevent/main.c @@ -111,14 +111,10 @@ static void My_Get_Event_Ack_Handler( BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data) { int len = 0; - int i; - BACNET_GET_EVENT_INFORMATION_DATA data[MAX_OBJ_IDS_IN_GE_ACK]; + BACNET_GET_EVENT_INFORMATION_DATA data[MAX_OBJ_IDS_IN_GE_ACK] = { 0 }; (void)src; - for (i = 0; i < MAX_OBJ_IDS_IN_GE_ACK - 1; i++) { - data[i].next = &data[i + 1]; - } - + getevent_information_link_array(&data[0], ARRAY_SIZE(data)); printf( "Recieved Ack. Saved invoke ID was %i, service returned %i\n", Request_Invoke_ID, service_data->invoke_id); diff --git a/src/bacnet/basic/service/h_getevent.c b/src/bacnet/basic/service/h_getevent.c index c2b0be02..48dc9048 100644 --- a/src/bacnet/basic/service/h_getevent.c +++ b/src/bacnet/basic/service/h_getevent.c @@ -96,7 +96,7 @@ void handler_get_event_information( BACNET_ADDRESS my_address; BACNET_OBJECT_ID object_id; unsigned i = 0, j = 0; /* counter */ - BACNET_GET_EVENT_INFORMATION_DATA getevent_data; + BACNET_GET_EVENT_INFORMATION_DATA getevent_data = { 0 }; int valid_event = 0; /* initialize type of 'Last Received Object Identifier' using max value */ diff --git a/src/bacnet/basic/service/h_getevent_a.c b/src/bacnet/basic/service/h_getevent_a.c index b8095053..1c24106a 100644 --- a/src/bacnet/basic/service/h_getevent_a.c +++ b/src/bacnet/basic/service/h_getevent_a.c @@ -42,20 +42,18 @@ void get_event_ack_handler( BACNET_ADDRESS *src, BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data) { - uint8_t i = 0; uint16_t apdu_len = 0; bool more_events = false; /* initialize array big enough to accommodate multiple get event data in APDU */ - BACNET_GET_EVENT_INFORMATION_DATA get_event_data[MAX_NUMBER_OF_EVENTS]; + BACNET_GET_EVENT_INFORMATION_DATA get_event_data[MAX_NUMBER_OF_EVENTS] = { + 0 + }; (void)src; (void)service_data; - for (i = 1; i < MAX_NUMBER_OF_EVENTS; i++) { - /* Create linked list */ - get_event_data[i - 1].next = &get_event_data[i]; - } - + getevent_information_link_array( + &get_event_data[0], ARRAY_SIZE(get_event_data)); apdu_len = getevent_ack_decode_service_request( &service_request[0], service_len, &get_event_data[0], &more_events); diff --git a/src/bacnet/getevent.c b/src/bacnet/getevent.c index 8c43d571..d1c8ed6d 100644 --- a/src/bacnet/getevent.c +++ b/src/bacnet/getevent.c @@ -14,6 +14,11 @@ /** * @brief Encode APDU for GetEvent-Request service + * + * GetEventInformation-Request ::= SEQUENCE { + * last-received-object-identifier [0] BACnetObjectIdentifier OPTIONAL + * } + * * @param apdu Pointer to a buffer, or NULL for length * @param lastReceivedObjectIdentifier Object identifier * @return Bytes encoded. @@ -92,249 +97,425 @@ int getevent_encode_apdu( /** Decode the service request only * * @param apdu APDU buffer to encode to. - * @param apdu_len Valid bytes in the buffer. + * @param apdu_size Valid bytes in the buffer. * @param lastReceivedObjectIdentifier Object identifier * - * @return Bytes encoded. + * @return number of bytes decoded, zero if tag mismatch, + * or #BACNET_STATUS_ERROR (-1) if malformed */ int getevent_decode_service_request( const uint8_t *apdu, - unsigned apdu_len, + unsigned apdu_size, BACNET_OBJECT_ID *lastReceivedObjectIdentifier) { - unsigned len = 0; + int len = 0; + BACNET_OBJECT_TYPE *object_type = NULL; + uint32_t *object_instance = NULL; - /* check for value pointers */ - if (apdu_len && lastReceivedObjectIdentifier) { - /* Tag 0: Object ID - optional */ - if (!decode_is_context_tag(&apdu[len++], 0)) { - return -1; - } - if (len < apdu_len) { - len += decode_object_id( - &apdu[len], &lastReceivedObjectIdentifier->type, - &lastReceivedObjectIdentifier->instance); - } - } - - return (int)len; -} - -int getevent_ack_encode_apdu_init( - uint8_t *apdu, size_t max_apdu, uint8_t invoke_id) -{ - int apdu_len = 0; /* total length of the apdu, return value */ - - if (apdu && (max_apdu >= 4)) { - apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ - apdu[1] = invoke_id; /* original invoke id from request */ - apdu[2] = SERVICE_CONFIRMED_GET_EVENT_INFORMATION; - apdu_len = 3; - /* service ack follows */ - /* Tag 0: listOfEventSummaries */ - apdu_len += encode_opening_tag(&apdu[apdu_len], 0); - } - - return apdu_len; -} - -int getevent_ack_encode_apdu_data( - uint8_t *apdu, - size_t max_apdu, - BACNET_GET_EVENT_INFORMATION_DATA *get_event_data) -{ - int apdu_len = 0; /* total length of the apdu, return value */ - BACNET_GET_EVENT_INFORMATION_DATA *event_data; - unsigned i = 0; /* counter */ - - (void)max_apdu; - if (apdu) { - event_data = get_event_data; - while (event_data) { - /* Tag 0: objectIdentifier */ - apdu_len += encode_context_object_id( - &apdu[apdu_len], 0, event_data->objectIdentifier.type, - event_data->objectIdentifier.instance); - /* Tag 1: eventState */ - apdu_len += encode_context_enumerated( - &apdu[apdu_len], 1, event_data->eventState); - /* Tag 2: acknowledgedTransitions */ - apdu_len += encode_context_bitstring( - &apdu[apdu_len], 2, &event_data->acknowledgedTransitions); - /* Tag 3: eventTimeStamps */ - apdu_len += encode_opening_tag(&apdu[apdu_len], 3); - for (i = 0; i < 3; i++) { - apdu_len += bacapp_encode_timestamp( - &apdu[apdu_len], &event_data->eventTimeStamps[i]); - } - apdu_len += encode_closing_tag(&apdu[apdu_len], 3); - /* Tag 4: notifyType */ - apdu_len += encode_context_enumerated( - &apdu[apdu_len], 4, event_data->notifyType); - /* Tag 5: eventEnable */ - apdu_len += encode_context_bitstring( - &apdu[apdu_len], 5, &event_data->eventEnable); - /* Tag 6: eventPriorities */ - apdu_len += encode_opening_tag(&apdu[apdu_len], 6); - for (i = 0; i < 3; i++) { - apdu_len += encode_application_unsigned( - &apdu[apdu_len], event_data->eventPriorities[i]); - } - apdu_len += encode_closing_tag(&apdu[apdu_len], 6); - event_data = event_data->next; - } - } - - return apdu_len; -} - -int getevent_ack_encode_apdu_end( - uint8_t *apdu, size_t max_apdu, bool moreEvents) -{ - int apdu_len = 0; /* total length of the apdu, return value */ - - (void)max_apdu; - if (apdu) { - apdu_len += encode_closing_tag(&apdu[apdu_len], 0); - apdu_len += encode_context_boolean(&apdu[apdu_len], 1, moreEvents); - } - - return apdu_len; -} - -int getevent_ack_decode_service_request( - const uint8_t *apdu, - int apdu_len, /* total length of the apdu */ - BACNET_GET_EVENT_INFORMATION_DATA *get_event_data, - bool *moreEvents) -{ - uint8_t tag_number = 0; - uint32_t len_value = 0; - int len = 0; /* total length of decodes */ - uint32_t enum_value = 0; /* for decoding */ - BACNET_UNSIGNED_INTEGER unsigned_value = 0; - BACNET_GET_EVENT_INFORMATION_DATA *event_data; - unsigned i = 0; /* counter */ - - /* FIXME: check apdu_len against the len during decode */ - event_data = get_event_data; - if (apdu && apdu_len && event_data && moreEvents) { - if (!decode_is_opening_tag_number(&apdu[len], 0)) { - return -1; - } - len++; - while (event_data) { - /* Tag 0: objectIdentifier */ - if (decode_is_context_tag(&apdu[len], 0)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - len += decode_object_id( - &apdu[len], &event_data->objectIdentifier.type, - &event_data->objectIdentifier.instance); - } else { - return -1; - } - /* Tag 1: eventState */ - if (decode_is_context_tag(&apdu[len], 1)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - len += decode_enumerated(&apdu[len], len_value, &enum_value); - if (enum_value < EVENT_STATE_MAX) { - event_data->eventState = (BACNET_EVENT_STATE)enum_value; - } else { - return -1; - } - } else { - return -1; - } - /* Tag 2: acknowledgedTransitions */ - if (decode_is_context_tag(&apdu[len], 2)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - len += decode_bitstring( - &apdu[len], len_value, - &event_data->acknowledgedTransitions); - } else { - return -1; - } - /* Tag 3: eventTimeStamps */ - if (decode_is_opening_tag_number(&apdu[len], 3)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - for (i = 0; i < 3; i++) { - len += bacapp_decode_timestamp( - &apdu[len], &event_data->eventTimeStamps[i]); - } - } else { - return -1; - } - if (decode_is_closing_tag_number(&apdu[len], 3)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - } else { - return -1; - } - /* Tag 4: notifyType */ - if (decode_is_context_tag(&apdu[len], 4)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - len += decode_enumerated(&apdu[len], len_value, &enum_value); - if (enum_value < NOTIFY_MAX) { - event_data->notifyType = (BACNET_NOTIFY_TYPE)enum_value; - } else { - return -1; - } - } else { - return -1; - } - /* Tag 5: eventEnable */ - if (decode_is_context_tag(&apdu[len], 5)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - len += decode_bitstring( - &apdu[len], len_value, &event_data->eventEnable); - } else { - return -1; - } - /* Tag 6: eventPriorities */ - if (decode_is_opening_tag_number(&apdu[len], 6)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - for (i = 0; i < 3; i++) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - len += - decode_unsigned(&apdu[len], len_value, &unsigned_value); - event_data->eventPriorities[i] = (uint32_t)unsigned_value; - } - } else { - return -1; - } - if (decode_is_closing_tag_number(&apdu[len], 6)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - } else { - return -1; - } - if (decode_is_closing_tag_number(&apdu[len], 0)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - event_data->next = NULL; - } - event_data = event_data->next; - } - if (decode_is_context_tag(&apdu[len], 1)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - if (len_value == 1) { - *moreEvents = decode_context_boolean(&apdu[len++]); - } else { - *moreEvents = false; - } - } else { - return -1; - } + if (lastReceivedObjectIdentifier) { + object_type = &lastReceivedObjectIdentifier->type; + object_instance = &lastReceivedObjectIdentifier->instance; } + len = bacnet_object_id_context_decode( + apdu, apdu_size, 0, object_type, object_instance); return len; } + +/** + * @brief Encode the GetEventInformation-ACK service + * @param apdu Pointer to the buffer for encoding into + * @param max_apdu Maximum number of bytes available in the buffer + * @param invoke_id Invoke ID + * @return number of bytes encoded, or zero if unable to encode or too large + */ +int getevent_ack_encode_apdu_init( + uint8_t *apdu, size_t max_apdu, uint8_t invoke_id) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu && (max_apdu >= 3)) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ + apdu[1] = invoke_id; /* original invoke id from request */ + apdu[2] = SERVICE_CONFIRMED_GET_EVENT_INFORMATION; + } + len = 3; /* length of the header */ + apdu_len += len; + if (apdu) { + apdu += len; + } + /* service ack follows */ + /* Tag 0: listOfEventSummaries */ + len = encode_opening_tag(apdu, 0); + apdu_len += len; + + return apdu_len; +} + +/** + * @brief Encode one or more GetEventInformation-ACK service data + * + * GetEventInformation-ACK ::= SEQUENCE { + * list-of-event-summaries [0] SEQUENCE OF SEQUENCE { + * object-identifier[0] BACnetObjectIdentifier, + * event-state[1] BACnetEventState, + * acknowledged-transitions[2] BACnetEventTransitionBits, + * event-timestamps[3] SEQUENCE SIZE (3) OF BACnetTimeStamp, + * notify-type[4] BACnetNotifyType, + * event-enable[5] BACnetEventTransitionBits, + * event-priorities[6] SEQUENCE SIZE (3) OF Unsigned + * }, + * more-events [1] Boolean + * } + * + * @param apdu Pointer to the buffer for encoding into + * @param head Pointer to the head of service data used for encoding values + * @return number of bytes encoded + */ +int getevent_information_ack_encode( + uint8_t *apdu, BACNET_GET_EVENT_INFORMATION_DATA *head) +{ + int len = 0, apdu_len = 0; + BACNET_GET_EVENT_INFORMATION_DATA *data; + unsigned i = 0; /* counter */ + + data = head; + while (data) { + /* Tag 0: objectIdentifier */ + len = encode_context_object_id( + apdu, 0, data->objectIdentifier.type, + data->objectIdentifier.instance); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* Tag 1: eventState */ + len = encode_context_enumerated(apdu, 1, data->eventState); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* Tag 2: acknowledgedTransitions */ + len = encode_context_bitstring(apdu, 2, &data->acknowledgedTransitions); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* Tag 3: eventTimeStamps */ + len = encode_opening_tag(apdu, 3); + apdu_len += len; + if (apdu) { + apdu += len; + } + for (i = 0; i < 3; i++) { + len = bacapp_encode_timestamp(apdu, &data->eventTimeStamps[i]); + apdu_len += len; + if (apdu) { + apdu += len; + } + } + len = encode_closing_tag(apdu, 3); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* Tag 4: notifyType */ + len = encode_context_enumerated(apdu, 4, data->notifyType); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* Tag 5: eventEnable */ + len = encode_context_bitstring(apdu, 5, &data->eventEnable); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* Tag 6: eventPriorities */ + len = encode_opening_tag(apdu, 6); + apdu_len += len; + if (apdu) { + apdu += len; + } + for (i = 0; i < 3; i++) { + len = encode_application_unsigned(apdu, data->eventPriorities[i]); + apdu_len += len; + if (apdu) { + apdu += len; + } + } + len = encode_closing_tag(apdu, 6); + apdu_len += len; + if (apdu) { + apdu += len; + } + data = data->next; + } + + return apdu_len; +} + +/** + * @brief Encode the GetEventInformation-ACK service data + * @param apdu Pointer to the buffer for encoding into + * @param apdu_size Maximum number of bytes available in the buffer + * @param get_event_data Pointer to the service data used for encoding values + * @return number of bytes encoded, or zero if unable to encode or too large + */ +int getevent_ack_encode_apdu_data( + uint8_t *apdu, size_t apdu_size, BACNET_GET_EVENT_INFORMATION_DATA *data) +{ + size_t apdu_len = 0; /* total length of the apdu, return value */ + + apdu_len = getevent_information_ack_encode(NULL, data); + if (apdu_len > apdu_size) { + apdu_len = 0; + } else { + apdu_len = getevent_information_ack_encode(apdu, data); + } + + return apdu_len; +} + +/** + * @brief Encode the end of the GetEventInformation-ACK service + * @param apdu Pointer to the buffer for encoding into, or NULL for length + * @param moreEvents More events flag + * @return number of bytes encoded + */ +int getevent_information_ack_end_encode(uint8_t *apdu, bool moreEvents) +{ + int len = 0, apdu_len = 0; + + len = encode_closing_tag(apdu, 0); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_context_boolean(apdu, 1, moreEvents); + apdu_len += len; + + return apdu_len; +} + +/** + * @brief Encode the end of the GetEventInformation-ACK service + * @param apdu Pointer to the buffer for encoding into + * @param max_apdu Maximum number of bytes available in the buffer + * @param moreEvents More events flag + * @return number of bytes encoded, or zero if unable to encode or too large + */ +int getevent_ack_encode_apdu_end( + uint8_t *apdu, size_t apdu_size, bool moreEvents) +{ + size_t apdu_len = 0; /* total length of the apdu, return value */ + + apdu_len = getevent_information_ack_end_encode(NULL, moreEvents); + if (apdu_len > apdu_size) { + apdu_len = 0; + } else { + apdu_len = getevent_information_ack_end_encode(apdu, moreEvents); + } + + return apdu_len; +} + +/** + * @brief Decode the GetEventInformation-ACK service + * @param apdu Pointer to the buffer for decoding from + * @param apdu_size Total length of the APDU + * @param invoke_id Invoke ID + * @param get_event_data Pointer to one or more service data used for + * decoding values + * @param moreEvents More events flag + * @return number of bytes decoded, or BACNET_STATUS_ERROR on error + */ +int getevent_ack_decode_service_request( + const uint8_t *apdu, + int apdu_size, + BACNET_GET_EVENT_INFORMATION_DATA *get_event_data, + bool *moreEvents) +{ + int len = 0, apdu_len = 0; + uint32_t enum_value = 0; /* for decoding */ + BACNET_UNSIGNED_INTEGER unsigned_value = 0; + BACNET_OBJECT_TYPE *object_type; + uint32_t *object_instance; + BACNET_BIT_STRING *bitstring_value; + BACNET_TIMESTAMP *timestamp_value; + BACNET_GET_EVENT_INFORMATION_DATA *data; + unsigned i = 0; /* counter */ + + if (!apdu) { + return BACNET_STATUS_ERROR; + } + if (apdu_size == 0) { + return BACNET_STATUS_ERROR; + } + if (!bacnet_is_opening_tag_number(apdu, apdu_size - apdu_len, 0, &len)) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + data = get_event_data; + while (apdu_len < apdu_size) { + if (data) { + object_type = &data->objectIdentifier.type; + object_instance = &data->objectIdentifier.instance; + } else { + object_type = NULL; + object_instance = NULL; + } + /* Tag 0: objectIdentifier */ + len = bacnet_object_id_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, object_type, + object_instance); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + /* Tag 1: eventState */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &enum_value); + if (len > 0) { + if (data) { + if (enum_value < EVENT_STATE_MAX) { + data->eventState = (BACNET_EVENT_STATE)enum_value; + } else { + return BACNET_STATUS_ERROR; + } + } + } else { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + /* Tag 2: acknowledgedTransitions */ + if (data) { + bitstring_value = &data->acknowledgedTransitions; + } else { + bitstring_value = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, bitstring_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + /* Tag 3: eventTimeStamps */ + if (!bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 3, &len)) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + for (i = 0; i < 3; i++) { + if (data) { + timestamp_value = &data->eventTimeStamps[i]; + } else { + timestamp_value = NULL; + } + len = bacnet_timestamp_decode( + &apdu[apdu_len], apdu_size - apdu_len, timestamp_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + } + if (!bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 3, &len)) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + /* Tag 4: notifyType */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 4, &enum_value); + if (len > 0) { + if (enum_value < NOTIFY_MAX) { + if (data) { + data->notifyType = (BACNET_NOTIFY_TYPE)enum_value; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + /* Tag 5: eventEnable */ + if (data) { + bitstring_value = &data->eventEnable; + } else { + bitstring_value = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 5, bitstring_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + /* Tag 6: eventPriorities */ + if (!bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 6, &len)) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + for (i = 0; i < 3; i++) { + len = bacnet_unsigned_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, &unsigned_value); + if (len > 0) { + if (unsigned_value <= UINT32_MAX) { + if (data) { + data->eventPriorities[i] = (uint32_t)unsigned_value; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + } + if (!bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 6, &len)) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, &len)) { + if (data) { + data->next = NULL; /* mark end of the list */ + } + apdu_len += len; + break; + } + if (data) { + data = data->next; + } + } + /* Tag 1: moreEvents */ + len = bacnet_boolean_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, moreEvents); + if (len <= 0) { + return BACNET_STATUS_ERROR; /* malformed */ + } + apdu_len += len; + + return apdu_len; +} + +/** + * @brief Convert an array of GetEventInformation-Request to linked list + * @param array pointer to element zero of the array + * @param size number of elements in the array + */ +void getevent_information_link_array( + BACNET_GET_EVENT_INFORMATION_DATA *array, size_t size) +{ + size_t i = 0; + + for (i = 0; i < size; i++) { + if (i > 0) { + array[i - 1].next = &array[i]; + } + array[i].next = NULL; + } +} diff --git a/src/bacnet/getevent.h b/src/bacnet/getevent.h index 0fc00e91..09d825c1 100644 --- a/src/bacnet/getevent.h +++ b/src/bacnet/getevent.h @@ -55,29 +55,40 @@ size_t getevent_service_request_encode( BACNET_STACK_EXPORT int getevent_decode_service_request( - const uint8_t *apdu, unsigned apdu_len, BACNET_OBJECT_ID *object_id); + const uint8_t *apdu, unsigned apdu_size, BACNET_OBJECT_ID *object_id); BACNET_STACK_EXPORT int getevent_ack_encode_apdu_init( - uint8_t *apdu, size_t max_apdu, uint8_t invoke_id); + uint8_t *apdu, size_t apdu_size, uint8_t invoke_id); + +BACNET_STACK_EXPORT +int getevent_information_ack_encode( + uint8_t *apdu, BACNET_GET_EVENT_INFORMATION_DATA *head); BACNET_STACK_EXPORT int getevent_ack_encode_apdu_data( uint8_t *apdu, - size_t max_apdu, + size_t apdu_size, BACNET_GET_EVENT_INFORMATION_DATA *get_event_data); +BACNET_STACK_EXPORT +int getevent_information_ack_end_encode(uint8_t *apdu, bool moreEvents); + BACNET_STACK_EXPORT int getevent_ack_encode_apdu_end( - uint8_t *apdu, size_t max_apdu, bool moreEvents); + uint8_t *apdu, size_t apdu_size, bool moreEvents); BACNET_STACK_EXPORT int getevent_ack_decode_service_request( const uint8_t *apdu, - int apdu_len, /* total length of the apdu */ + int apdu_size, BACNET_GET_EVENT_INFORMATION_DATA *get_event_data, bool *moreEvents); +BACNET_STACK_EXPORT +void getevent_information_link_array( + BACNET_GET_EVENT_INFORMATION_DATA *array, size_t size); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/test/bacnet/getevent/src/main.c b/test/bacnet/getevent/src/main.c index 7ecfba99..34464ee8 100644 --- a/test/bacnet/getevent/src/main.c +++ b/test/bacnet/getevent/src/main.c @@ -64,27 +64,25 @@ static int getevent_ack_decode_apdu( BACNET_GET_EVENT_INFORMATION_DATA *get_event_data, bool *moreEvents) { - int len = 0; - int offset = 0; - if (!apdu) { - return -1; + return BACNET_STATUS_ERROR; + } + if (apdu_len < 3) { + return BACNET_STATUS_ERROR; /* too short */ } /* optional checking - most likely was already done prior to this call */ if (apdu[0] != PDU_TYPE_COMPLEX_ACK) { - return -1; + return BACNET_STATUS_ERROR; + } + if (invoke_id) { + *invoke_id = apdu[1]; } - *invoke_id = apdu[1]; if (apdu[2] != SERVICE_CONFIRMED_GET_EVENT_INFORMATION) { - return -1; - } - offset = 3; - if (apdu_len > offset) { - len = getevent_ack_decode_service_request( - &apdu[offset], apdu_len - offset, get_event_data, moreEvents); + return BACNET_STATUS_ERROR; } - return len; + return getevent_ack_decode_service_request( + &apdu[3], apdu_len - 3, get_event_data, moreEvents); } #if defined(CONFIG_ZTEST_NEW_API) @@ -94,46 +92,48 @@ static void testGetEventInformationAck(void) #endif { uint8_t apdu[480] = { 0 }; - int len = 0; - int apdu_len = 0; + int len = 0, apdu_len = 0, null_len = 0; uint8_t invoke_id = 1; uint8_t test_invoke_id = 0; - BACNET_GET_EVENT_INFORMATION_DATA event_data; - BACNET_GET_EVENT_INFORMATION_DATA test_event_data; + BACNET_GET_EVENT_INFORMATION_DATA event_data[1] = { 0 }; + BACNET_GET_EVENT_INFORMATION_DATA test_event_data[1] = { 0 }; bool moreEvents = false; bool test_moreEvents = false; unsigned i = 0; - event_data.objectIdentifier.type = OBJECT_BINARY_INPUT; - event_data.objectIdentifier.instance = 1; - event_data.eventState = EVENT_STATE_NORMAL; - bitstring_init(&event_data.acknowledgedTransitions); - bitstring_set_bit( - &event_data.acknowledgedTransitions, TRANSITION_TO_OFFNORMAL, false); - bitstring_set_bit( - &event_data.acknowledgedTransitions, TRANSITION_TO_FAULT, false); - bitstring_set_bit( - &event_data.acknowledgedTransitions, TRANSITION_TO_NORMAL, false); - for (i = 0; i < 3; i++) { - event_data.eventTimeStamps[i].tag = TIME_STAMP_SEQUENCE; - event_data.eventTimeStamps[i].value.sequenceNum = 0; - } - event_data.notifyType = NOTIFY_ALARM; - bitstring_init(&event_data.eventEnable); - bitstring_set_bit(&event_data.eventEnable, TRANSITION_TO_OFFNORMAL, true); - bitstring_set_bit(&event_data.eventEnable, TRANSITION_TO_FAULT, true); - bitstring_set_bit(&event_data.eventEnable, TRANSITION_TO_NORMAL, true); - for (i = 0; i < 3; i++) { - event_data.eventPriorities[i] = 1; - } - event_data.next = NULL; + getevent_information_link_array( + &test_event_data[0], ARRAY_SIZE(test_event_data)); + getevent_information_link_array(&event_data[0], ARRAY_SIZE(event_data)); + event_data[0].objectIdentifier.type = OBJECT_BINARY_INPUT; + event_data[0].objectIdentifier.instance = 1; + event_data[0].eventState = EVENT_STATE_NORMAL; + bitstring_init(&event_data[0].acknowledgedTransitions); + bitstring_set_bit( + &event_data[0].acknowledgedTransitions, TRANSITION_TO_OFFNORMAL, false); + bitstring_set_bit( + &event_data[0].acknowledgedTransitions, TRANSITION_TO_FAULT, false); + bitstring_set_bit( + &event_data[0].acknowledgedTransitions, TRANSITION_TO_NORMAL, false); + for (i = 0; i < 3; i++) { + event_data[0].eventTimeStamps[i].tag = TIME_STAMP_SEQUENCE; + event_data[0].eventTimeStamps[i].value.sequenceNum = 0; + } + event_data[0].notifyType = NOTIFY_ALARM; + bitstring_init(&event_data[0].eventEnable); + bitstring_set_bit( + &event_data[0].eventEnable, TRANSITION_TO_OFFNORMAL, true); + bitstring_set_bit(&event_data[0].eventEnable, TRANSITION_TO_FAULT, true); + bitstring_set_bit(&event_data[0].eventEnable, TRANSITION_TO_NORMAL, true); + for (i = 0; i < 3; i++) { + event_data[0].eventPriorities[i] = 1; + } len = getevent_ack_encode_apdu_init(&apdu[0], sizeof(apdu), invoke_id); zassert_not_equal(len, 0, NULL); zassert_not_equal(len, -1, NULL); apdu_len = len; len = getevent_ack_encode_apdu_data( - &apdu[apdu_len], sizeof(apdu) - apdu_len, &event_data); + &apdu[apdu_len], sizeof(apdu) - apdu_len, &event_data[0]); zassert_not_equal(len, 0, NULL); zassert_not_equal(len, -1, NULL); apdu_len += len; @@ -142,20 +142,28 @@ static void testGetEventInformationAck(void) zassert_not_equal(len, 0, NULL); zassert_not_equal(len, -1, NULL); apdu_len += len; + null_len = getevent_ack_decode_apdu(&apdu[0], apdu_len, NULL, NULL, NULL); len = getevent_ack_decode_apdu( &apdu[0], apdu_len, /* total length of the apdu */ - &test_invoke_id, &test_event_data, &test_moreEvents); + &test_invoke_id, &test_event_data[0], &test_moreEvents); + zassert_equal(null_len, len, "null_len=%d len=%d", null_len, len); zassert_not_equal(len, -1, NULL); zassert_equal(test_invoke_id, invoke_id, NULL); zassert_equal( - event_data.objectIdentifier.type, test_event_data.objectIdentifier.type, - NULL); + event_data[0].objectIdentifier.type, + test_event_data[0].objectIdentifier.type, NULL); zassert_equal( - event_data.objectIdentifier.instance, - test_event_data.objectIdentifier.instance, NULL); + event_data[0].objectIdentifier.instance, + test_event_data[0].objectIdentifier.instance, NULL); - zassert_equal(event_data.eventState, test_event_data.eventState, NULL); + zassert_equal( + event_data[0].eventState, test_event_data[0].eventState, NULL); + while (apdu_len) { + apdu_len--; + len = getevent_ack_decode_apdu(&apdu[0], apdu_len, NULL, NULL, NULL); + zassert_equal(len, BACNET_STATUS_ERROR, NULL); + } } #if defined(CONFIG_ZTEST_NEW_API)