Bugfix/service request refactor size check (#553)

* refactor service requests from service header

* add APDU size checking and length feature

* add unit tests to check for length when passing NULL buffer

---------

Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Steve Karg
2024-01-05 08:59:45 -06:00
committed by GitHub
parent 5ca14e5320
commit bb081d28da
39 changed files with 2614 additions and 1514 deletions
+84 -24
View File
@@ -160,22 +160,91 @@ bool dcc_set_status_duration(
#if BACNET_SVC_DCC_A
/**
* Encode service
*
* @brief Encode DeviceCommunicationControl service
* @param apdu Pointer to the APDU buffer used for encoding.
* @param invoke_id Invoke-Id
* @param timeDuration Optional time duration in minutes.
* @param enable_disable Enable/disable communication
* @param password Pointer to an optional password.
*
* @return Bytes encoded or zero on an error.
*/
int dcc_encode_apdu(uint8_t *apdu,
uint8_t invoke_id,
uint16_t timeDuration, /* 0=optional */
int dcc_apdu_encode(uint8_t *apdu,
uint16_t timeDuration,
BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable,
BACNET_CHARACTER_STRING *password)
{ /* NULL=optional */
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
/* optional timeDuration */
if (timeDuration) {
len = encode_context_unsigned(apdu, 0, timeDuration);
apdu_len += len;
if (apdu) {
apdu += len;
}
}
/* enable disable */
len = encode_context_enumerated(apdu, 1, enable_disable);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* optional password */
if (password) {
if ((password->length >= 1) && (password->length <= 20)) {
len = encode_context_character_string(apdu, 2, password);
apdu_len += len;
}
}
return apdu_len;
}
/**
* @brief Encode the COVNotification service request
* @param apdu Pointer to the buffer for encoding into
* @param apdu_size number of bytes available in the buffer
* @param timeDuration Optional time duration in minutes.
* @param enable_disable Enable/disable communication
* @param password Pointer to an optional password.
* @return number of bytes encoded, or zero if unable to encode or too large
*/
size_t dcc_service_request_encode(uint8_t *apdu,
size_t apdu_size,
uint16_t timeDuration,
BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable,
BACNET_CHARACTER_STRING *password)
{
size_t apdu_len = 0; /* total length of the apdu, return value */
apdu_len = dcc_apdu_encode(NULL, timeDuration, enable_disable, password);
if (apdu_len > apdu_size) {
apdu_len = 0;
} else {
apdu_len =
dcc_apdu_encode(apdu, timeDuration, enable_disable, password);
}
return apdu_len;
}
/**
* Encode service
*
* @param apdu Pointer to the APDU buffer used for encoding.
* @param invoke_id Invoke-Id
* @param timeDuration Optional time duration in minutes, 0=optional
* @param enable_disable Enable/disable communication
* @param password Pointer to an optional password, NULL=optional
* @return Bytes encoded or zero on an error.
*/
int dcc_encode_apdu(uint8_t *apdu,
uint8_t invoke_id,
uint16_t timeDuration,
BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable,
BACNET_CHARACTER_STRING *password)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
@@ -184,24 +253,15 @@ int dcc_encode_apdu(uint8_t *apdu,
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL;
apdu_len = 4;
/* optional timeDuration */
if (timeDuration) {
len = encode_context_unsigned(&apdu[apdu_len], 0, timeDuration);
apdu_len += len;
}
/* enable disable */
len = encode_context_enumerated(&apdu[apdu_len], 1, enable_disable);
apdu_len += len;
/* optional password */
if (password) {
if ((password->length >= 1) && (password->length <= 20)) {
len = encode_context_character_string(
&apdu[apdu_len], 2, password);
apdu_len += len;
}
}
}
len = 4;
apdu_len += len;
if (apdu) {
apdu += len;
}
len = dcc_apdu_encode(
apdu, timeDuration, enable_disable, password);
apdu_len += len;
return apdu_len;
}