Feature/app data buffer check (#79)

* Added comments and buffer overflow checks

* Removed backslashs from C-code.
This commit is contained in:
Roy Schneider
2020-04-28 15:45:03 +02:00
committed by GitHub
parent 89929ee802
commit 0abcbea971
20 changed files with 1588 additions and 635 deletions
+199 -79
View File
@@ -84,11 +84,23 @@ static struct Address_Cache_Entry {
#define BAC_ADDR_SHORT_TIME BAC_ADDR_SECS_1HOUR
#define BAC_ADDR_FOREVER 0xFFFFFFFF /* Permenant entry */
/**
* @brief Set the index of the first (top) address being protected.
*
* @param top_protected_entry_index top protected index [0..n-1]
*/
void address_protected_entry_index_set(uint32_t top_protected_entry_index)
{
Top_Protected_Entry = top_protected_entry_index;
if (top_protected_entry_index <= (MAX_ADDRESS_CACHE - 1)) {
Top_Protected_Entry = top_protected_entry_index;
}
}
/**
* @brief Set the address of our own device.
*
* @param own_id Own device id
*/
void address_own_device_id_set(uint32_t own_id)
{
Own_Device_ID = own_id;
@@ -182,9 +194,8 @@ void address_remove_device(uint32_t device_id)
* entry available to free up. Does not check for free entries as it is
* assumed we are calling this due to the lack of those.
*
* @return Candidate for deletation.
* @return Pointer to the entry that has been removed or NULL.
*/
static struct Address_Cache_Entry *address_remove_oldest(void)
{
struct Address_Cache_Entry *pMatch;
@@ -383,11 +394,10 @@ static void address_file_init(const char *pFilename)
}
#endif
/****************************************************************************
* Clear down the cache and make sure the full complement of entries are *
* available. Assume no persistance of memory. *
****************************************************************************/
/**
* Clear down the cache and make sure the full complement of entries are
* available. Assume no persistance of memory.
*/
void address_init(void)
{
struct Address_Cache_Entry *pMatch;
@@ -405,14 +415,13 @@ void address_init(void)
return;
}
/****************************************************************************
* Clear down the cache of any non bound, expired or reserved entries. *
* Leave static and unexpired bound entries alone. For use where the cache *
* is held in persistant memory which can survive a reset or power cycle. *
* This reduces the network traffic on restarts as the cache will have much *
* of its entries intact. *
****************************************************************************/
/**
* Clear down the cache of any non bound, expired or reserved entries.
* Leave static and unexpired bound entries alone. For use where the cache
* is held in persistant memory which can survive a reset or power cycle.
* This reduces the network traffic on restarts as the cache will have much
* of its entries intact.
*/
void address_init_partial(void)
{
struct Address_Cache_Entry *pMatch;
@@ -441,13 +450,16 @@ void address_init_partial(void)
return;
}
/****************************************************************************
* Set the TTL info for the given device entry. If it is a bound entry we *
* set it to static or normal and can change the TTL. If it is unbound we *
* can only set the TTL. This is done as a seperate function at the moment *
* to avoid breaking the current API. *
****************************************************************************/
/**
* Set the TTL info for the given device entry. If it is a bound entry we
* set it to static or normal and can change the TTL. If it is unbound we
* can only set the TTL. This is done as a seperate function at the moment
* to avoid breaking the current API.
*
* @param device_id Device-Id
* @param TimeOut Timeout in seconds.
* @param StaticFlag Flag indicating if being a static address.
*/
void address_set_device_TTL(
uint32_t device_id, uint32_t TimeOut, bool StaticFlag)
{
@@ -476,6 +488,13 @@ void address_set_device_TTL(
}
}
/**
* Return the cached address for the given device-id
*
* @param device_id Device-Id
* @param max_apdu Pointer to a variable, taking the maximum APDU size.
* @param src Pointer to address structure for return.
*/
bool address_get_by_device(
uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src)
{
@@ -500,8 +519,14 @@ bool address_get_by_device(
return found;
}
/* find a device id from a given MAC address */
/**
* Find a device id from a given MAC address.
*
* @param src Pointer to address structure to search for.
* @param device_id Pointer to the device id variable for return.
*
* @return true/false
*/
bool address_get_device_id(BACNET_ADDRESS *src, uint32_t *device_id)
{
struct Address_Cache_Entry *pMatch;
@@ -525,6 +550,13 @@ bool address_get_device_id(BACNET_ADDRESS *src, uint32_t *device_id)
return found;
}
/**
* Add a device using the given id, max_apdu and address.
*
* @param device_id Pointer to the device id variable for return.
* @param max_apdu Maximum APDU size.
* @param src Pointer to address structure to add.
*/
void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
{
bool found = false; /* return value */
@@ -543,6 +575,7 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
/* existing device or bind request outstanding - update address */
pMatch = Address_Cache;
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
/* Device already in the list, then update the values. */
if (((pMatch->Flags & BAC_ADDR_IN_USE) != 0) &&
(pMatch->device_id == device_id)) {
bacnet_address_copy(&pMatch->address, src);
@@ -572,7 +605,7 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
pMatch++;
}
/* new device - add to cache if there is room */
/* New device - add to cache if there is room. */
if (!found) {
pMatch = Address_Cache;
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
@@ -591,7 +624,7 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
}
}
/* See if we can squeeze it in */
/* If adding has failed, see if we can squeeze it in by removed the oldest entry. */
if (!found) {
pMatch = address_remove_oldest();
if (pMatch != NULL) {
@@ -606,8 +639,19 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
return;
}
/* returns true if device is already bound */
/* also returns the address and max apdu if already bound */
/**
* Check if the device is in the list. If yes, return the values.
* Otherwise add the device to the list.
* Returns true if device is already bound.
* Also returns the address and max apdu if already bound.
*
* @param device_id Pointer to the device id variable for return.
* @param device_ttl Pointer to a variable taking the time to live in seconds.
* @param max_apdu Max APDU size of the device.
* @param src Pointer to the BACnet address.
*
* @return true if device is already bound
*/
bool address_device_bind_request(uint32_t device_id,
uint32_t *device_ttl,
unsigned *max_apdu,
@@ -675,14 +719,31 @@ bool address_device_bind_request(uint32_t device_id,
return (false);
}
/* returns true if device is already bound */
/* also returns the address and max apdu if already bound */
/**
* Check if the device is in the list. If yes, return the values.
* Otherwise add the device to the list.
* Returns true if device is already bound.
* Also returns the address and max apdu if already bound.
*
* @param device_id Pointer to the device id variable for return.
* @param max_apdu Max APDU size of the device.
* @param src Pointer to the BACnet address.
*
* @return true if device is already bound
*/
bool address_bind_request(
uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src)
{
return address_device_bind_request(device_id, NULL, max_apdu, src);
}
/**
* For an existing device add a binding.
*
* @param device_id Pointer to the device id variable for return.
* @param max_apdu Max APDU size of the device.
* @param src Pointer to the BACnet address.
*/
void address_add_binding(
uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
{
@@ -709,6 +770,17 @@ void address_add_binding(
return;
}
/**
* Return the device information from the given index in the table.
*
* @param index Table index [0..MAX_ADDRESS_CACHE-1]
* @param device_id Pointer to the variable taking the device id.
* @param device_ttl Pointer to the variable taking the Time To Life for the device.
* @param max_apdu Pointer to the variable taking the max APDU size of the device.
* @param src Pointer to the BACnet address.
*
* @return true/false
*/
bool address_device_get_by_index(unsigned index,
uint32_t *device_id,
uint32_t *device_ttl,
@@ -741,6 +813,16 @@ bool address_device_get_by_index(unsigned index,
return found;
}
/**
* Return the device information from the given index in the table.
*
* @param index Table index [0..MAX_ADDRESS_CACHE-1]
* @param device_id Pointer to the variable taking the device id.
* @param max_apdu Pointer to the variable taking the max APDU size of the device.
* @param src Pointer to the BACnet address.
*
* @return true/false
*/
bool address_get_by_index(unsigned index,
uint32_t *device_id,
unsigned *max_apdu,
@@ -749,6 +831,11 @@ bool address_get_by_index(unsigned index,
return address_device_get_by_index(index, device_id, NULL, max_apdu, src);
}
/**
* Return the count of cached addresses.
*
* @return A value between zero and MAX_ADDRESS_CACHE.
*/
unsigned address_count(void)
{
struct Address_Cache_Entry *pMatch;
@@ -768,43 +855,55 @@ unsigned address_count(void)
return count;
}
/****************************************************************************
* Build a list of the current bindings for the device address binding *
* property. *
****************************************************************************/
/**
* Build a list of the current bindings for the device address binding
* property. Basically encode the address list to be send out.
*
* @param apdu Pointer to the APDU
* @param apdu_len Remaining buffer size.
*
* @return Count of encoded bytes.
*/
int address_list_encode(uint8_t *apdu, unsigned apdu_len)
{
int iLen = 0;
struct Address_Cache_Entry *pMatch;
BACNET_OCTET_STRING MAC_Address;
/* FIXME: I really shouild check the length remaining here but it is
fairly pointless until we have the true length remaining in
the packet to work with as at the moment it is just MAX_APDU */
(void)apdu_len;
/* look for matching address */
/* Look for matching address. */
pMatch = Address_Cache;
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) ==
BAC_ADDR_IN_USE) {
iLen += encode_application_object_id(
&apdu[iLen], OBJECT_DEVICE, pMatch->device_id);
iLen +=
encode_application_unsigned(&apdu[iLen], pMatch->address.net);
&apdu[iLen], OBJECT_DEVICE, pMatch->device_id);
iLen += encode_application_unsigned(&apdu[iLen], pMatch->address.net);
if ((unsigned)iLen >= apdu_len) {
break;
}
/* pick the appropriate type of entry from the cache */
if (pMatch->address.len != 0) {
/* BAC */
if ((unsigned)(iLen + pMatch->address.len) >= apdu_len) {
break;
}
octetstring_init(
&MAC_Address, pMatch->address.adr, pMatch->address.len);
iLen +=
encode_application_octet_string(&apdu[iLen], &MAC_Address);
iLen += encode_application_octet_string(&apdu[iLen], &MAC_Address);
} else {
/* MAC*/
if ((unsigned)(iLen + pMatch->address.mac_len) >= apdu_len) {
break;
}
octetstring_init(
&MAC_Address, pMatch->address.mac, pMatch->address.mac_len);
iLen +=
encode_application_octet_string(&apdu[iLen], &MAC_Address);
iLen += encode_application_octet_string(&apdu[iLen], &MAC_Address);
}
/* Any space left? */
if ((unsigned)iLen >= apdu_len) {
break;
}
}
pMatch++;
@@ -813,29 +912,33 @@ int address_list_encode(uint8_t *apdu, unsigned apdu_len)
return (iLen);
}
/****************************************************************************
* Build a list of the current bindings for the device address binding *
* property as required for the ReadsRange functionality. *
* We assume we only get called for "Read All" or "By Position" requests. *
* *
* We need to treat the address cache as a contiguous array but in reality *
* it could be sparsely populated. We can get the count but we can only *
* extract entries by doing a linear scan starting from the first entry in *
* the cache and picking them off one by one. *
* *
* We do assume the list cannot change whilst we are accessing it so would *
* not be multithread safe if there are other tasks that change the cache. *
* *
/**
* Build a list of the current bindings for the device address binding
* property as required for the ReadsRange functionality.
* We assume we only get called for "Read All" or "By Position" requests.
*
* We need to treat the address cache as a contiguous array but in reality
* it could be sparsely populated. We can get the count but we can only
* extract entries by doing a linear scan starting from the first entry in
* the cache and picking them off one by one.
*
* We do assume the list cannot change whilst we are accessing it so would
* not be multithread safe if there are other tasks that change the cache.
*
* We take the simple approach here to filling the buffer by taking a max *
* size for a single entry and then stopping if there is less than that *
* left in the buffer. You could build each entry in a seperate buffer and *
* determine the exact length before copying but this is time consuming, *
* requires more memory and would probably only let you sqeeeze one more *
* entry in on occasion. The value is calculated as 5 bytes for the device *
* ID + 3 bytes for the network number and nine bytes for the MAC address *
* oct string to give 17 bytes (the minimum possible is 5 + 2 + 3 = 10). *
****************************************************************************/
* size for a single entry and then stopping if there is less than that
* left in the buffer. You could build each entry in a seperate buffer and
* determine the exact length before copying but this is time consuming,
* requires more memory and would probably only let you sqeeeze one more
* entry in on occasion. The value is calculated as 5 bytes for the device
* ID + 3 bytes for the network number and nine bytes for the MAC address
* oct string to give 17 bytes (the minimum possible is 5 + 2 + 3 = 10).
*
* @param apdu Pointer to the encode buffer.
* @param pRequest Pointer to the read_range request structure.
*
* @return Bytes encoded.
*/
#define ACACHE_MAX_ENC 17 /* Maximum size of encoded cache entry, see above */
int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
@@ -851,6 +954,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
uint32_t uiTarget = 0; /* Last entry we are required to encode */
uint32_t uiRemaining = 0; /* Amount of unused space in packet */
if ((!pRequest) || (!apdu)) {
return 0;
}
/* Initialise result flags to all false */
bitstring_init(&pRequest->ResultFlags);
bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, false);
@@ -861,7 +968,7 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
pRequest->ItemCount = 0; /* Start out with nothing */
uiTotal = address_count(); /* What do we have to work with here ? */
if (uiTotal == 0) { /* Bail out now if nowt */
if (uiTotal == 0) { /* Bail out now if not. */
return (0);
}
@@ -921,6 +1028,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
while ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) !=
BAC_ADDR_IN_USE) { /* Find first bound entry */
pMatch++;
/* Shall not happen as the count has been checked first. */
if (pMatch > &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
return(0); /* Issue with the table. */
}
}
/* Seek to start position */
@@ -932,6 +1043,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
} else {
pMatch++;
}
/* Shall not happen as the count has been checked first. */
if (pMatch > &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
return(0); /* Issue with the table. */
}
}
uiFirst = uiIndex; /* Record where we started from */
@@ -976,6 +1091,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
while ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) !=
BAC_ADDR_IN_USE) { /* Find next bound entry */
pMatch++;
/* Can normally not happen. */
if (pMatch > &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
return(0); /* Issue with the table. */
}
}
}
@@ -991,15 +1110,16 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
return (iLen);
}
/****************************************************************************
* Scan the cache and eliminate any expired entries. Should be called *
* periodically to ensure the cache is managed correctly. If this function *
* is never called at all the whole cache is effectivly rendered static and *
* entries never expire unless explictely deleted. *
****************************************************************************/
/**
* Scan the cache and eliminate any expired entries. Should be called
* periodically to ensure the cache is managed correctly. If this function
* is never called at all the whole cache is effectivly rendered static and
* entries never expire unless explictely deleted.
*
* @param uSeconds Approximate number of seconds since last call to this function
*/
void address_cache_timer(uint16_t uSeconds)
{ /* Approximate number of seconds since last call to this function */
{
struct Address_Cache_Entry *pMatch;
pMatch = Address_Cache;
+1 -1
View File
@@ -422,7 +422,7 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
if (wp_data == NULL) {
return false;
}
if ((wp_data->application_data == NULL) || \
if ((wp_data->application_data == NULL) ||
(wp_data->application_data_len == 0)) {
return false;
}
+2 -2
View File
@@ -384,7 +384,7 @@ int CharacterString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
if (rpdata == NULL) {
return 0;
}
if ((rpdata->application_data == NULL) || \
if ((rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
@@ -488,7 +488,7 @@ bool CharacterString_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
if (wp_data == NULL) {
return false;
}
if ((wp_data->application_data == NULL) || \
if ((wp_data->application_data == NULL) ||
(wp_data->application_data_len == 0)) {
return false;
}
+236 -211
View File
@@ -182,6 +182,7 @@ void handler_read_property_multiple(uint8_t *service_request,
BACNET_ADDRESS *src,
BACNET_CONFIRMED_SERVICE_DATA *service_data)
{
bool berror = false;
int len = 0;
uint16_t copy_len = 0;
uint16_t decode_len = 0;
@@ -194,234 +195,258 @@ void handler_read_property_multiple(uint8_t *service_request,
int npdu_len = 0;
int error = 0;
/* jps_debug - see if we are utilizing all the buffer */
/* memset(&Handler_Transmit_Buffer[0], 0xff,
* sizeof(Handler_Transmit_Buffer)); */
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
npdu_len = npdu_encode_pdu(
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
if (service_data->segmented_message) {
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
#if PRINT_ENABLED
fprintf(stderr, "RPM: Segmented message. Sending Abort!\r\n");
#endif
goto RPM_FAILURE;
}
/* decode apdu request & encode apdu reply
encode complex ack, invoke id, service choice */
apdu_len = rpm_ack_encode_apdu_init(
&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id);
for (;;) {
/* Start by looking for an object ID */
len = rpm_decode_object_id(
&service_request[decode_len], service_len - decode_len, &rpmdata);
if (len >= 0) {
/* Got one so skip to next stage */
decode_len += len;
} else {
/* bad encoding - skip to error/reject/abort handling */
#if PRINT_ENABLED
fprintf(stderr, "RPM: Bad Encoding.\n");
#endif
error = len;
goto RPM_FAILURE;
}
if (service_data && (service_len > 0)) {
/* jps_debug - see if we are utilizing all the buffer */
/* memset(&Handler_Transmit_Buffer[0], 0xff,
* sizeof(Handler_Transmit_Buffer)); */
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
npdu_len = npdu_encode_pdu(
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
/* Test for case of indefinite Device object instance */
if ((rpmdata.object_type == OBJECT_DEVICE) &&
(rpmdata.object_instance == BACNET_MAX_INSTANCE)) {
rpmdata.object_instance = Device_Object_Instance_Number();
}
/* Stick this object id into the reply - if it will fit */
len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0],
apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Response too big!\r\n");
#endif
if (service_data->segmented_message) {
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
}
#if PRINT_ENABLED
fprintf(stderr, "RPM: Segmented message. Sending Abort!\r\n");
#endif
} else {
/* decode apdu request & encode apdu reply
encode complex ack, invoke id, service choice */
apdu_len = rpm_ack_encode_apdu_init(
&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id);
apdu_len += copy_len;
/* do each property of this object of the RPM request */
for (;;) {
/* Fetch a property */
len = rpm_decode_object_property(&service_request[decode_len],
service_len - decode_len, &rpmdata);
if (len < 0) {
/* bad encoding - skip to error/reject/abort handling */
#if PRINT_ENABLED
fprintf(stderr, "RPM: Bad Encoding.\n");
#endif
error = len;
goto RPM_FAILURE;
}
decode_len += len;
/* handle the special properties */
if ((rpmdata.object_property == PROP_ALL) ||
(rpmdata.object_property == PROP_REQUIRED) ||
(rpmdata.object_property == PROP_OPTIONAL)) {
struct special_property_list_t property_list;
unsigned property_count = 0;
unsigned index = 0;
BACNET_PROPERTY_ID special_object_property;
if (rpmdata.array_index != BACNET_ARRAY_ALL) {
/* No array index options for this special property.
Encode error for this object property response */
len = rpm_ack_encode_apdu_object_property(&Temp_Buf[0],
rpmdata.object_property, rpmdata.array_index);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(
stderr, "RPM: Too full to encode property!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
}
apdu_len += len;
len = rpm_ack_encode_apdu_object_property_error(
&Temp_Buf[0], ERROR_CLASS_PROPERTY,
ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Too full to encode error!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
}
apdu_len += len;
for (;;) {
/* Start by looking for an object ID */
len = rpm_decode_object_id(
&service_request[decode_len], service_len - decode_len, &rpmdata);
if (len >= 0) {
/* Got one so skip to next stage */
decode_len += len;
} else {
special_object_property = rpmdata.object_property;
Device_Objects_Property_List(rpmdata.object_type,
rpmdata.object_instance, &property_list);
property_count = RPM_Object_Property_Count(
&property_list, special_object_property);
if (property_count == 0) {
/* this only happens with the OPTIONAL property */
/* 135-2016bl-2. Clarify ReadPropertyMultiple
response on OPTIONAL when empty. */
/* If no optional properties are supported then
an empty 'List of Results' shall be returned
for the specified property.*/
} else {
for (index = 0; index < property_count; index++) {
rpmdata.object_property = RPM_Object_Property(
&property_list, special_object_property, index);
len = RPM_Encode_Property(
&Handler_Transmit_Buffer[npdu_len],
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
/* bad encoding - skip to error/reject/abort handling */
#if PRINT_ENABLED
fprintf(stderr, "RPM: Bad Encoding.\n");
#endif
error = len;
berror = true;
break;
}
/* Test for case of indefinite Device object instance */
if ((rpmdata.object_type == OBJECT_DEVICE) &&
(rpmdata.object_instance == BACNET_MAX_INSTANCE)) {
rpmdata.object_instance = Device_Object_Instance_Number();
}
/* Stick this object id into the reply - if it will fit */
len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata);
copy_len =
memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0],
apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Response too big!\r\n");
#endif
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
berror = true;
break;
}
apdu_len += copy_len;
/* do each property of this object of the RPM request */
for (;;) {
/* Fetch a property */
len = rpm_decode_object_property(&service_request[decode_len],
service_len - decode_len, &rpmdata);
if (len < 0) {
/* bad encoding - skip to error/reject/abort handling */
#if PRINT_ENABLED
fprintf(stderr, "RPM: Bad Encoding.\n");
#endif
error = len;
berror = true;
break; // The berror flag ensures that both loops will be broken!
}
decode_len += len;
/* handle the special properties */
if ((rpmdata.object_property == PROP_ALL) ||
(rpmdata.object_property == PROP_REQUIRED) ||
(rpmdata.object_property == PROP_OPTIONAL)) {
struct special_property_list_t property_list;
unsigned property_count = 0;
unsigned index = 0;
BACNET_PROPERTY_ID special_object_property;
if (rpmdata.array_index != BACNET_ARRAY_ALL) {
/* No array index options for this special property.
Encode error for this object property response */
len = rpm_ack_encode_apdu_object_property(&Temp_Buf[0],
rpmdata.object_property, rpmdata.array_index);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(
stderr, "RPM: Too full for property!\r\n");
#endif
error = len;
goto RPM_FAILURE;
stderr, "RPM: Too full to encode property!\r\n");
#endif
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
berror = true;
break; // The berror flag ensures that both loops will be broken!
}
apdu_len += len;
len = rpm_ack_encode_apdu_object_property_error(
&Temp_Buf[0], ERROR_CLASS_PROPERTY,
ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Too full to encode error!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
berror = true;
break; // The berror flag ensures that both loops will be broken!
}
apdu_len += len;
} else {
special_object_property = rpmdata.object_property;
Device_Objects_Property_List(rpmdata.object_type,
rpmdata.object_instance, &property_list);
property_count = RPM_Object_Property_Count(
&property_list, special_object_property);
if (property_count == 0) {
/* This only happens with the OPTIONAL property */
/* 135-2016bl-2. Clarify ReadPropertyMultiple
response on OPTIONAL when empty. */
/* If no optional properties are supported then
an empty 'List of Results' shall be returned
for the specified property.*/
} else {
for (index = 0; index < property_count; index++) {
rpmdata.object_property = RPM_Object_Property(
&property_list, special_object_property, index);
len = RPM_Encode_Property(
&Handler_Transmit_Buffer[npdu_len],
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(
stderr, "RPM: Too full for property!\r\n");
#endif
error = len;
berror = true;
break; // The berror flag ensures that both loops will be broken!
}
}
}
}
} else {
/* handle an individual property */
len = RPM_Encode_Property(&Handler_Transmit_Buffer[npdu_len],
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(
stderr, "RPM: Too full for individual property!\r\n");
#endif
error = len;
berror = true;
break; // The berror flag ensures that both loops will be broken!
}
}
if (decode_is_closing_tag_number(&service_request[decode_len], 1)) {
/* Reached end of property list so cap the result list */
decode_len++;
len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Too full to encode object end!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
berror = true;
break; // The berror flag ensures that both loops will be broken!
} else {
apdu_len += copy_len;
}
break; /* finished with this property list */
}
} // for(;;)
if (berror) {
break;
}
} else {
/* handle an individual property */
len = RPM_Encode_Property(&Handler_Transmit_Buffer[npdu_len],
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(
stderr, "RPM: Too full for individual property!\r\n");
#endif
error = len;
goto RPM_FAILURE;
if (decode_len >= service_len) {
/* Reached the end so finish up */
break;
}
}
if (decode_is_closing_tag_number(&service_request[decode_len], 1)) {
/* Reached end of property list so cap the result list */
decode_len++;
len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Too full to encode object end!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
} // for(;;)
/* If not having an error so far, check the remaining space. */
if (!berror) {
if (apdu_len > service_data->max_resp) {
/* too big for the sender - send an abort */
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
} else {
apdu_len += copy_len;
#if PRINT_ENABLED
fprintf(stderr, "RPM: Message too large. Sending Abort!\n");
#endif
}
break; /* finished with this property list */
}
}
if (decode_len >= service_len) {
/* Reached the end so finish up */
break;
/* Error fallback. */
if (error) {
if (error == BACNET_STATUS_ABORT) {
apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
abort_convert_error_code(rpmdata.error_code), true);
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Abort!\n");
#endif
} else if (error == BACNET_STATUS_ERROR) {
apdu_len = bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
rpmdata.error_class, rpmdata.error_code);
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Error!\n");
#endif
} else if (error == BACNET_STATUS_REJECT) {
apdu_len = reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
reject_convert_error_code(rpmdata.error_code));
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Reject!\n");
#endif
}
}
}
if (apdu_len > service_data->max_resp) {
/* too big for the sender - send an abort */
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
#if PRINT_ENABLED
fprintf(stderr, "RPM: Message too large. Sending Abort!\n");
#endif
goto RPM_FAILURE;
}
RPM_FAILURE:
if (error) {
if (error == BACNET_STATUS_ABORT) {
apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
abort_convert_error_code(rpmdata.error_code), true);
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Abort!\n");
#endif
} else if (error == BACNET_STATUS_ERROR) {
apdu_len = bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
rpmdata.error_class, rpmdata.error_code);
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Error!\n");
#endif
} else if (error == BACNET_STATUS_REJECT) {
apdu_len = reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
reject_convert_error_code(rpmdata.error_code));
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Reject!\n");
#endif
pdu_len = apdu_len + npdu_len;
bytes_sent = datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Failed to send PDU (%s)!\n", strerror(errno));
#endif
}
}
pdu_len = apdu_len + npdu_len;
bytes_sent = datalink_send_pdu(
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Failed to send PDU (%s)!\n", strerror(errno));
#endif
}
}
+1 -1
View File
@@ -136,7 +136,7 @@ void handler_write_property(uint8_t *service_request,
/* Send PDU */
pdu_len += len;
bytes_sent = datalink_send_pdu( \
bytes_sent = datalink_send_pdu(
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
+192 -82
View File
@@ -56,21 +56,33 @@
/* Generic node routines */
/******************************************************************** */
/* grab memory for a node */
/** Grab memory for a node (Keylist_Node).
*
* @return Pointer to the allocated memory or
* NULL under an Out Of Memory situation.
*/
static struct Keylist_Node *NodeCreate(void)
{
return calloc(1, sizeof(struct Keylist_Node));
}
/* grab memory for a list */
/** Grab memory for a list (Keylist).
*
* @return Pointer to the allocated memory or
* NULL under an Out Of Memory situation.
*/
static struct Keylist *KeylistCreate(void)
{
return calloc(1, sizeof(struct Keylist));
}
/* check to see if the array is big enough for an addition */
/* or is too big when we are deleting and we can shrink */
/* returns TRUE if success, FALSE if failed */
/** Check to see if the array is big enough for an addition
* or is too big when we are deleting and we can shrink.
*
* @param list Pointer to the list to be tested.
*
* @return Returns TRUE if success, FALSE if failed
*/
static int CheckArraySize(OS_Keylist list)
{
int new_size = 0; /* set it up so that no size change is the default */
@@ -112,13 +124,20 @@ static int CheckArraySize(OS_Keylist list)
return TRUE;
}
/* find the index of the key that we are looking for */
/* since it is sorted, we can optimize the search */
/* returns TRUE if found, and FALSE not found */
/* returns the found key and the index where it was found in parameters */
/* If the key is not found, the nearest index from the bottom will be returned,
/** Find the index of the key that we are looking for.
* Since it is sorted, we can optimize the search.
* returns TRUE if found, and FALSE not found.
* Returns the found key and the index where it was found in parameters.
* If the key is not found, the nearest index from the bottom will be returned,
* allowing the ability to find where an key should go into the list.
*
* @param list Pointer to the list
* @param key Key to search for
* @param pIndex Pointer to the variable taking the index were the key
* had been found.
*
* @return TRUE if found, and FALSE if not
*/
/* allowing the ability to find where an key should go into the list. */
static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
{
struct Keylist_Node *node; /* holds the new node */
@@ -127,7 +146,12 @@ static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
int index = 0; /* our current search place in the array */
KEY current_key = 0; /* place holder for current node key */
int status = FALSE; /* return value */
if (!list || !list->array || !list->count) {
if (!list) {
*pIndex = 0;
return (FALSE);
}
if (!list->array || !list->count) {
*pIndex = 0;
return (FALSE);
}
@@ -148,12 +172,12 @@ static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
left = index + 1;
}
} while ((key != current_key) && (left <= right));
if (key == current_key) {
status = TRUE;
*pIndex = index;
}
else {
} else {
/* where the index should be */
if (key > current_key) {
*pIndex = index + 1;
@@ -168,7 +192,15 @@ static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
/******************************************************************** */
/* list data functions */
/******************************************************************** */
/* inserts a node into its sorted position */
/** Inserts a node into its sorted position.
*
* @param list Pointer to the list
* @param key Key to be inserted
* @param data Pointer to the data hold by the key.
* This pointer needs to be poiting to static memory
* as it will be stored in the list and later used
* by retrieving the key again.
*/
int Keylist_Data_Add(OS_Keylist list, KEY key, void *data)
{
struct Keylist_Node *node; /* holds the new node */
@@ -191,9 +223,7 @@ int Keylist_Data_Add(OS_Keylist list, KEY key, void *data)
for (i = list->count; i > index; i--) {
list->array[i] = list->array[i - 1];
}
}
else {
} else {
index = 0;
}
@@ -209,138 +239,199 @@ int Keylist_Data_Add(OS_Keylist list, KEY key, void *data)
return index;
}
/* deletes a node specified by its index */
/* returns the data from the node */
/** Deletes a node specified by its index
* returns the data from the node
*
* @param list Pointer to the list
* @param index Index of the key to be deleted
*
* @returns Pointer to the data of the deleted key or NULL.
*/
void *Keylist_Data_Delete_By_Index(OS_Keylist list, int index)
{
struct Keylist_Node *node;
void *data = NULL;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node)
data = node->data;
/* move the nodes to account for the deleted one */
if (list->count == 1) {
/* There is no node shifting to do */
} else if (index == (list->count - 1)) {
/* We are the last one */
/* There is no node shifting to do */
} else {
/* Move all the nodes down one */
int i; /* counter */
int count = list->count - 1;
for (i = index; i < count; i++) {
list->array[i] = list->array[i + 1];
if (list) {
if (list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node) {
data = node->data;
}
/* move the nodes to account for the deleted one */
if (list->count == 1) {
/* There is no node shifting to do */
} else if (index == (list->count - 1)) {
/* We are the last one */
/* There is no node shifting to do */
} else {
/* Move all the nodes down one */
int i; /* counter */
int count = list->count - 1;
for (i = index; i < count; i++) {
list->array[i] = list->array[i + 1];
}
}
list->count--;
if (node) {
free(node);
}
}
list->count--;
if (node) {
free(node);
}
/* potentially reduce the size of the array */
(void)CheckArraySize(list);
/* potentially reduce the size of the array */
(void)CheckArraySize(list);
}
}
return (data);
}
/* deletes a node specified by its key */
/* returns the data from the node */
/** Deletes a node specified by its key/
* returns the data from the node
*
* @param list Pointer to the list
* @param key Key to be deleted
*
* @returns Pointer to the data of the deleted key or NULL.
*/
void *Keylist_Data_Delete(OS_Keylist list, KEY key)
{
void *data = NULL; /* return value */
int index; /* where the node is in the array */
if (list) {
if (FindIndex(list, key, &index))
if (FindIndex(list, key, &index)) {
data = Keylist_Data_Delete_By_Index(list, index);
}
}
return data;
}
/* returns the data from last node, and removes it from the list */
/** Returns the data from last node, and
* removes it from the list.
*
* @param list Pointer to the list
*
* @return Pointer to the data that might be NULL.
*/
void *Keylist_Data_Pop(OS_Keylist list)
{
void *data = NULL; /* return value */
int index; /* position in the array */
if (list && list->count) {
index = list->count - 1;
data = Keylist_Data_Delete_By_Index(list, index);
if (list) {
if (list->count) {
index = list->count - 1;
data = Keylist_Data_Delete_By_Index(list, index);
}
}
return data;
}
/* returns the data from the node specified by key */
/** Returns the data from the node specified by key.
*
* @param list Pointer to the list
* @param key Key to be deleted
*
* @returns Pointer to the data, that might be NULL.
*/
void *Keylist_Data(OS_Keylist list, KEY key)
{
struct Keylist_Node *node = NULL;
int index = 0; /* used to look up the index of node */
if (list && list->array && list->count) {
if (FindIndex(list, key, &index))
node = list->array[index];
if (list) {
if (list->array && list->count) {
if (FindIndex(list, key, &index)) {
node = list->array[index];
}
}
}
return node ? node->data : NULL;
}
/* returns the index from the node specified by key */
/** Returns the index from the node specified by key.
*
* @param list Pointer to the list
* @param key Key whose index shall be retrieved.
*
* @return Index of the key or -1, if not found.
*/
int Keylist_Index(OS_Keylist list, KEY key)
{
int index = -1; /* used to look up the index of node */
if (list && list->array && list->count) {
if (!FindIndex(list, key, &index)) {
index = -1;
if (list) {
if (list->array && list->count) {
if (!FindIndex(list, key, &index)) {
index = -1;
}
}
}
return index;
}
/* returns the data specified by index */
/** Returns the data specified by index
*
* @param list Pointer to the list
* @param index Key whose data shall be retrieved.
*
* @return Pointer to the data that might be NULL.
*/
void *Keylist_Data_Index(OS_Keylist list, int index)
{
struct Keylist_Node *node = NULL;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count))
node = list->array[index];
if (list) {
if (list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
}
}
return node ? node->data : NULL;
}
/* return the key at the given index */
/** Return the key at the given index.
*
* @param list Pointer to the list
* @param index Index that shall be returned
*
* @return Key for the index or 0.
*/
KEY Keylist_Key(OS_Keylist list, int index)
{
KEY key = 0; /* return value */
struct Keylist_Node *node;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node)
key = node->key;
if (list) {
if (list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node) {
key = node->key;
}
}
}
return key;
}
/* returns the next empty key from the list */
/** Returns the next empty key from the list.
*
* @param list Pointer to the list
* @param key Key whose index shall be retrieved.
*
* @return Next empty key or 'key=key' if there is none.
*/
KEY Keylist_Next_Empty_Key(OS_Keylist list, KEY key)
{
int index;
if (list) {
while (FindIndex(list, key, &index)) {
if (KEY_LAST(key))
if (KEY_LAST(key)) {
break;
}
key++;
}
}
@@ -348,29 +439,47 @@ KEY Keylist_Next_Empty_Key(OS_Keylist list, KEY key)
return key;
}
/* return the number of nodes in this list */
/** Return the number of nodes in this list.
*
* @param list Pointer to the list
*
* @return Count of key in the list.
*/
int Keylist_Count(OS_Keylist list)
{
return list->count;
int cnt = 0;
if (list) {
cnt = list->count;
}
return(cnt);
}
/******************************************************************** */
/* Public List functions */
/******************************************************************** */
/* returns head of the list or NULL on failure. */
/** Returns head of the list or NULL on failure.
*
* @return Pointer to the key list or NUL if creation failed.
*/
OS_Keylist Keylist_Create(void)
{
struct Keylist *list;
list = KeylistCreate();
if (list)
if (list) {
CheckArraySize(list);
}
return list;
}
/* delete specified list */
/** Delete specified list.
*
* @param list Pointer to the list
*/
void Keylist_Delete(OS_Keylist list)
{ /* list number to be deleted */
if (list) {
@@ -378,8 +487,9 @@ void Keylist_Delete(OS_Keylist list)
while (list->count) {
(void)Keylist_Data_Delete_By_Index(list, 0);
}
if (list->array)
if (list->array) {
free(list->array);
}
free(list);
}