Secure ReadPropertyMultiple code, and improve unit test coverage. (#650)

This commit is contained in:
Steve Karg
2024-05-21 08:28:50 -05:00
committed by GitHub
parent 60964a4796
commit 4cd7acebcd
5 changed files with 302 additions and 331 deletions
+58 -78
View File
@@ -1,29 +1,13 @@
/**************************************************************************
*
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
* Inspired by John Stachler <John.Stachler@lennoxind.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/**
* @file
* @brief BACnet ReadPropertyMultiple-Request handler
* @author Steve Karg <skarg@users.sourceforge.net>
* @author John Stachler <John.Stachler@lennoxind.com>
* @author Peter McShane <petermcs@users.sourceforge.net>
* @author Roy Schneider <postmaster@overthehill.de>
* @date 2007
* @copyright SPDX-License-Identifier: MIT
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -47,12 +31,21 @@
#endif
#include "bacnet/basic/tsm/tsm.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/sys/debug.h"
#include "bacnet/datalink/datalink.h"
/** @file h_rpm.c Handles Read Property Multiple requests. */
static uint8_t Temp_Buf[MAX_APDU] = { 0 };
/**
* @brief Fetches the lists of properties (array of BACNET_PROPERTY_ID's) for
* this object type and the special properties ALL or REQUIRED or OPTIONAL.
* @param pPropertyList reference for the list of ALL, REQUIRED, and OPTIONAL
* properties.
* @param special_property The special property ALL, REQUIRED, or OPTIONAL
* to fetch.
* @param index The index of the property to fetch.
* @return The property ID or -1 if not found.
*/
static BACNET_PROPERTY_ID RPM_Object_Property(
struct special_property_list_t *pPropertyList,
BACNET_PROPERTY_ID special_property,
@@ -87,6 +80,16 @@ static BACNET_PROPERTY_ID RPM_Object_Property(
return (BACNET_PROPERTY_ID)property;
}
/**
* @brief Fetches the number of properties (array of BACNET_PROPERTY_ID's) for
* this object type belonging to the special properties ALL or REQUIRED or
* OPTIONAL.
* @param pPropertyList reference for the list of ALL, REQUIRED, and OPTIONAL
* properties.
* @param special_property The special property ALL, REQUIRED, or OPTIONAL
* to fetch.
* @return The number of properties.
*/
static unsigned RPM_Object_Property_Count(
struct special_property_list_t *pPropertyList,
BACNET_PROPERTY_ID special_property)
@@ -105,8 +108,16 @@ static unsigned RPM_Object_Property_Count(
return count;
}
/** Encode the RPM property returning the length of the encoding,
or 0 if there is no room to fit the encoding. */
/**
* @brief Encode the RPM property returning the length of the encoding,
* or 0 if there is no room to fit the encoding.
* @param apdu [out] The buffer to encode the property into.
* @param offset [in] The offset into the buffer to start encoding.
* @param max_apdu [in] The maximum length of the buffer.
* @param rpmdata [in] The RPM data to encode.
* @return The length of the encoding, or 0 if there is no room to fit the
* encoding.
*/
static int RPM_Encode_Property(
uint8_t *apdu, uint16_t offset, uint16_t max_apdu, BACNET_RPM_DATA *rpmdata)
{
@@ -221,9 +232,7 @@ void handler_read_property_multiple(uint8_t *service_request,
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
debug_fprintf(stderr, "RPM: Segmented message. Sending Abort!\r\n");
} else {
/* decode apdu request & encode apdu reply
encode complex ack, invoke id, service choice */
@@ -239,9 +248,7 @@ void handler_read_property_multiple(uint8_t *service_request,
decode_len += len;
} else {
/* bad encoding - skip to error/reject/abort handling */
#if PRINT_ENABLED
fprintf(stderr, "RPM: Bad Encoding.\n");
#endif
debug_fprintf(stderr, "RPM: Bad Encoding.\n");
error = len;
berror = true;
break;
@@ -273,9 +280,7 @@ void handler_read_property_multiple(uint8_t *service_request,
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
debug_fprintf(stderr, "RPM: Response too big!\r\n");
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
@@ -292,9 +297,7 @@ void handler_read_property_multiple(uint8_t *service_request,
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
debug_fprintf(stderr, "RPM: Bad Encoding.\n");
error = len;
berror = true;
break; /* The berror flag ensures that both loops will
@@ -319,10 +322,8 @@ void handler_read_property_multiple(uint8_t *service_request,
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(stderr,
debug_fprintf(stderr,
"RPM: Too full for property!\r\n");
#endif
error = len;
/* The berror flag ensures that
both loops will be broken! */
@@ -342,10 +343,8 @@ void handler_read_property_multiple(uint8_t *service_request,
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr,
debug_fprintf(stderr,
"RPM: Too full to encode property!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
@@ -364,10 +363,8 @@ void handler_read_property_multiple(uint8_t *service_request,
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr,
debug_fprintf(stderr,
"RPM: Too full to encode error!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
@@ -400,10 +397,8 @@ void handler_read_property_multiple(uint8_t *service_request,
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(stderr,
debug_fprintf(stderr,
"RPM: Too full for property!\r\n");
#endif
error = len;
/* The berror flag ensures that
both loops will be broken! */
@@ -423,10 +418,8 @@ void handler_read_property_multiple(uint8_t *service_request,
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(stderr,
debug_fprintf(stderr,
"RPM: Too full for property!\r\n");
#endif
error = len;
berror = true;
break; /* The berror flag ensures that
@@ -444,10 +437,8 @@ void handler_read_property_multiple(uint8_t *service_request,
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(stderr,
debug_fprintf(stderr,
"RPM: Too full for individual property!\r\n");
#endif
error = len;
berror = true;
break; /* The berror flag ensures that both loops */
@@ -464,10 +455,8 @@ void handler_read_property_multiple(uint8_t *service_request,
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,
debug_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;
@@ -496,10 +485,8 @@ void handler_read_property_multiple(uint8_t *service_request,
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
#if PRINT_ENABLED
fprintf(
debug_fprintf(
stderr, "RPM: Message too large. Sending Abort!\n");
#endif
}
}
}
@@ -510,24 +497,18 @@ void handler_read_property_multiple(uint8_t *service_request,
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
debug_fprintf(stderr, "RPM: Sending Abort!\n");
} 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
debug_fprintf(stderr, "RPM: Sending Error!\n");
} 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
debug_fprintf(stderr, "RPM: Sending Reject!\n");
}
}
@@ -535,9 +516,8 @@ void handler_read_property_multiple(uint8_t *service_request,
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
debug_fprintf(stderr, "RPM: Failed to send PDU (errno=%d)!\n",
errno);
}
}
}