14f033ceda
* Fixed GetEvent usage of linked list by initializing next in all the examples and unit test. * Secured GetEventInformation encoders by accepting NULL for APDU for determining the size, and exploit the NULL APDU for size checking during encoding. Secured the GetEventInformation decoders and removed the use of deprecated decoder API.
522 lines
16 KiB
C
522 lines
16 KiB
C
/**
|
|
* @file
|
|
* @brief BACnet GetEventNotification encode and decode functions
|
|
* @author Steve Karg <skarg@users.sourceforge.net>
|
|
* @date 2009
|
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
|
*/
|
|
#include <stdint.h>
|
|
/* BACnet Stack defines - first */
|
|
#include "bacnet/bacdef.h"
|
|
/* BACnet Stack API */
|
|
#include "bacnet/bacdcode.h"
|
|
#include "bacnet/getevent.h"
|
|
|
|
/**
|
|
* @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.
|
|
*/
|
|
int getevent_apdu_encode(
|
|
uint8_t *apdu, const BACNET_OBJECT_ID *lastReceivedObjectIdentifier)
|
|
{
|
|
int len = 0;
|
|
int apdu_len = 0;
|
|
|
|
/* encode optional parameter */
|
|
if (lastReceivedObjectIdentifier) {
|
|
len = encode_context_object_id(
|
|
apdu, 0, lastReceivedObjectIdentifier->type,
|
|
lastReceivedObjectIdentifier->instance);
|
|
apdu_len += len;
|
|
}
|
|
|
|
return apdu_len;
|
|
}
|
|
|
|
/**
|
|
* @brief Encode the GetEvent-Request service
|
|
* @param apdu Pointer to the buffer for encoding into
|
|
* @param apdu_size number of bytes available in the buffer
|
|
* @param data Pointer to the service data used for encoding values
|
|
* @return number of bytes encoded, or zero if unable to encode or too large
|
|
*/
|
|
size_t getevent_service_request_encode(
|
|
uint8_t *apdu, size_t apdu_size, const BACNET_OBJECT_ID *data)
|
|
{
|
|
size_t apdu_len = 0; /* total length of the apdu, return value */
|
|
|
|
apdu_len = getevent_apdu_encode(NULL, data);
|
|
if (apdu_len > apdu_size) {
|
|
apdu_len = 0;
|
|
} else {
|
|
apdu_len = getevent_apdu_encode(apdu, data);
|
|
}
|
|
|
|
return apdu_len;
|
|
}
|
|
|
|
/** Encode service
|
|
*
|
|
* @param apdu APDU buffer to encode to.
|
|
* @param invoke_id Invoke ID
|
|
* @param lastReceivedObjectIdentifier Object identifier
|
|
*
|
|
* @return Bytes encoded.
|
|
* @deprecated Use getevent_apdu_encode() instead
|
|
*/
|
|
int getevent_encode_apdu(
|
|
uint8_t *apdu, uint8_t invoke_id, const BACNET_OBJECT_ID *data)
|
|
{
|
|
int len = 0; /* length of each encoding */
|
|
int apdu_len = 0; /* total length of the apdu, return value */
|
|
|
|
if (apdu) {
|
|
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
|
|
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
|
|
apdu[2] = invoke_id;
|
|
apdu[3] = SERVICE_CONFIRMED_GET_EVENT_INFORMATION;
|
|
}
|
|
len = 4;
|
|
apdu_len += len;
|
|
if (apdu) {
|
|
apdu += len;
|
|
}
|
|
len = getevent_apdu_encode(apdu, data);
|
|
apdu_len += len;
|
|
|
|
return apdu_len;
|
|
}
|
|
|
|
/** Decode the service request only
|
|
*
|
|
* @param apdu APDU buffer to encode to.
|
|
* @param apdu_size Valid bytes in the buffer.
|
|
* @param lastReceivedObjectIdentifier Object identifier
|
|
*
|
|
* @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_size,
|
|
BACNET_OBJECT_ID *lastReceivedObjectIdentifier)
|
|
{
|
|
int len = 0;
|
|
BACNET_OBJECT_TYPE *object_type = NULL;
|
|
uint32_t *object_instance = NULL;
|
|
|
|
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;
|
|
}
|
|
}
|