Bugfix/deprecate decode tag number and value (#481)

* added or updated secure the BACnet primitive value decoders - the core codecs - named bacnet_x_decode(), bacnet_x_application_decode() and bacnet_x_context_decode where x is one of the 13 BACnet primitive value names.  The updated API includes an APDU size to prevent over-reading of an APDU buffer while decoding.  Improved or added unit test code coverage for the BACnet primitive value decoders.

* marked the insecure decoding API as 'deprecated' which is defined in src/bacnet/basic/sys/platform.h and can be disabled during a build. 

* added secure decoders for BACnetTimeValue, BACnetHostNPort, BACnetTimeStamp, BACnetAddress, and Weekly_Schedule and improved unit test code coverage.

* improved test code coverage for BACnet objects and properties.

* secured AtomicReadFile and AtomicWriteFile service decoders and improved unit test code coverage.

* secured BACnet Error service decoder and improved unit test code coverage.

---------

Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Steve Karg
2023-09-08 11:39:27 -05:00
committed by GitHub
parent bc8c261153
commit f641aacddb
67 changed files with 6103 additions and 3145 deletions
+472 -251
View File
@@ -39,135 +39,225 @@
/** @file arf.c Atomic Read File */
/* encode service */
/**
* @brief Encode the AtomicReadFile service request
*
* AtomicReadFile-Request ::= SEQUENCE {
* file-identifier BACnetObjectIdentifier,
* access-method CHOICE {
* stream-access [0] SEQUENCE {
* file-start-position INTEGER,
* requested-octet-count Unsigned
* },
* record-access [1] SEQUENCE {
* file-start-record INTEGER,
* requested-record-count Unsigned
* }
* }
* }
*
* @param apdu Pointer to the buffer for encoded values
* @param data Pointer to the service data used for encoding values
* @return number of bytes encoded
*/
int arf_service_encode_apdu(uint8_t *apdu, BACNET_ATOMIC_READ_FILE_DATA *data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0;
len = encode_application_object_id(
apdu, data->object_type, data->object_instance);
apdu_len += len;
if (apdu) {
apdu += len;
}
switch (data->access) {
case FILE_STREAM_ACCESS:
len = encode_opening_tag(apdu, 0);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_application_signed(
apdu, data->type.stream.fileStartPosition);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_application_unsigned(
apdu, data->type.stream.requestedOctetCount);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_closing_tag(apdu, 0);
apdu_len += len;
break;
case FILE_RECORD_ACCESS:
len = encode_opening_tag(apdu, 1);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_application_signed(
apdu, data->type.record.fileStartRecord);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_application_unsigned(
apdu, data->type.record.RecordCount);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_closing_tag(apdu, 1);
apdu_len += len;
break;
default:
break;
}
return apdu_len;
}
/**
* @brief Encode the AtomicReadFile service request
* @param apdu Pointer to the buffer for decoding.
* @param invoke_id original invoke id for request
* @param data Pointer to the property decoded data to be stored
* @return number of bytes encoded
*/
int arf_encode_apdu(
uint8_t *apdu, uint8_t invoke_id, BACNET_ATOMIC_READ_FILE_DATA *data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0;
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_ATOMIC_READ_FILE; /* service choice */
apdu_len = 4;
apdu_len += encode_application_object_id(
&apdu[apdu_len], data->object_type, data->object_instance);
switch (data->access) {
case FILE_STREAM_ACCESS:
apdu_len += encode_opening_tag(&apdu[apdu_len], 0);
apdu_len += encode_application_signed(
&apdu[apdu_len], data->type.stream.fileStartPosition);
apdu_len += encode_application_unsigned(
&apdu[apdu_len], data->type.stream.requestedOctetCount);
apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
break;
case FILE_RECORD_ACCESS:
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
apdu_len += encode_application_signed(
&apdu[apdu_len], data->type.record.fileStartRecord);
apdu_len += encode_application_unsigned(
&apdu[apdu_len], data->type.record.RecordCount);
apdu_len += encode_closing_tag(&apdu[apdu_len], 1);
break;
default:
break;
}
}
len = 4;
apdu_len += len;
if (apdu) {
apdu += len;
}
len = arf_service_encode_apdu(apdu, data);
apdu_len += len;
return apdu_len;
}
/* decode the service request only */
/**
* @brief Decode the AtomicReadFile service request
*
* AtomicReadFile-Request ::= SEQUENCE {
* file-identifier BACnetObjectIdentifier,
* access-method CHOICE {
* stream-access [0] SEQUENCE {
* file-start-position INTEGER,
* requested-octet-count Unsigned
* },
* record-access [1] SEQUENCE {
* file-start-record INTEGER,
* requested-record-count Unsigned
* }
* }
* }
*
* @param apdu Pointer to the buffer for decoding.
* @param apdu_size Count of valid bytes in the buffer.
* @param data Pointer to the property decoded data to be stored
* or NULL for length
*
* @return number of bytes decoded or BACNET_STATUS_ERROR on error.
*/
int arf_decode_service_request(
uint8_t *apdu, unsigned apdu_len_max, BACNET_ATOMIC_READ_FILE_DATA *data)
uint8_t *apdu, unsigned apdu_size, BACNET_ATOMIC_READ_FILE_DATA *data)
{
int len = 0;
int apdu_len = BACNET_STATUS_ERROR;
int tag_len = 0;
int apdu_len = 0;
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
uint32_t object_instance = 0;
int32_t signed_integer;
BACNET_UNSIGNED_INTEGER unsigned_integer;
/* check for value pointers */
if ((apdu_len_max == 0) || (!data)) {
tag_len = bacnet_object_id_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &object_type, &object_instance);
if (tag_len <= 0) {
return BACNET_STATUS_ERROR;
}
len = bacnet_object_id_application_decode(
&apdu[0], apdu_len_max, &object_type, &object_instance);
if (len <= 0) {
return BACNET_STATUS_ERROR;
if (data) {
data->object_type = (BACNET_OBJECT_TYPE)object_type;
data->object_instance = object_instance;
}
data->object_type = (BACNET_OBJECT_TYPE)object_type;
data->object_instance = object_instance;
apdu_len = len;
if (apdu_len < apdu_len_max) {
if (decode_is_opening_tag_number(&apdu[apdu_len], 0)) {
apdu_len += tag_len;
if (bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 0, &tag_len)) {
if (data) {
data->access = FILE_STREAM_ACCESS;
/* tag number 0 is not extended so only one octet */
apdu_len++;
/* fileStartPosition */
if (apdu_len >= apdu_len_max) {
return BACNET_STATUS_ERROR;
}
len = bacnet_signed_application_decode(&apdu[apdu_len],
apdu_len_max - apdu_len, &data->type.stream.fileStartPosition);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
/* requestedOctetCount */
if (apdu_len >= apdu_len_max) {
return BACNET_STATUS_ERROR;
}
len = bacnet_unsigned_application_decode(&apdu[apdu_len],
apdu_len_max, &data->type.stream.requestedOctetCount);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
/* closing tag */
if (apdu_len >= apdu_len_max) {
return BACNET_STATUS_ERROR;
}
if (!decode_is_closing_tag_number(&apdu[apdu_len], 0)) {
return BACNET_STATUS_ERROR;
}
/* tag number 0 is not extended so only one octet */
apdu_len++;
} else if (decode_is_opening_tag_number(&apdu[len], 1)) {
data->access = FILE_RECORD_ACCESS;
/* tag number 1 is not extended so only one octet */
apdu_len++;
if (apdu_len >= apdu_len_max) {
return BACNET_STATUS_ERROR;
}
/* fileStartRecord */
len = bacnet_signed_application_decode(&apdu[apdu_len],
apdu_len_max - apdu_len, &data->type.record.fileStartRecord);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
if (apdu_len >= apdu_len_max) {
return BACNET_STATUS_ERROR;
}
/* RecordCount */
len = bacnet_unsigned_application_decode(
&apdu[apdu_len], apdu_len_max, &data->type.record.RecordCount);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
if (apdu_len >= apdu_len_max) {
return BACNET_STATUS_ERROR;
}
if (!decode_is_closing_tag_number(&apdu[apdu_len], 1)) {
return BACNET_STATUS_ERROR;
}
/* tag number 1 is not extended so only one octet */
apdu_len++;
} else {
}
apdu_len += tag_len;
/* fileStartPosition */
tag_len = bacnet_signed_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &signed_integer);
if (tag_len <= 0) {
return BACNET_STATUS_ERROR;
}
if (data) {
data->type.stream.fileStartPosition = signed_integer;
}
apdu_len += tag_len;
/* requestedOctetCount */
tag_len = bacnet_unsigned_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &unsigned_integer);
if (tag_len <= 0) {
return BACNET_STATUS_ERROR;
}
if (data) {
data->type.stream.requestedOctetCount = unsigned_integer;
}
apdu_len += tag_len;
/* closing tag */
if (!bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 0, &tag_len)) {
return BACNET_STATUS_ERROR;
}
apdu_len += tag_len;
} else if (bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 1, &tag_len)) {
if (data) {
data->access = FILE_RECORD_ACCESS;
}
apdu_len += tag_len;
/* fileStartRecord */
tag_len = bacnet_signed_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &signed_integer);
if (tag_len <= 0) {
return BACNET_STATUS_ERROR;
}
if (data) {
data->type.record.fileStartRecord = signed_integer;
}
apdu_len += tag_len;
/* RecordCount */
tag_len = bacnet_unsigned_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &unsigned_integer);
if (tag_len <= 0) {
return BACNET_STATUS_ERROR;
}
if (data) {
data->type.record.RecordCount = unsigned_integer;
}
apdu_len += tag_len;
if (!bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 1, &tag_len)) {
return BACNET_STATUS_ERROR;
}
apdu_len += tag_len;
} else {
return BACNET_STATUS_ERROR;
}
@@ -175,205 +265,336 @@ int arf_decode_service_request(
return apdu_len;
}
/**
* @brief Decoding for AtomicReadFile APDU service data
* @param apdu Pointer to the buffer for decoding.
* @param apdu_size size of the buffer for decoding.
* @param invoke_id [in] Invoked service ID.
* @param data Pointer to the property data values to be stored,
* or NULL for length
* @return number of bytes decoded, or BACNET_STATUS_ERROR on error
*/
int arf_decode_apdu(uint8_t *apdu,
unsigned apdu_len,
unsigned apdu_size,
uint8_t *invoke_id,
BACNET_ATOMIC_READ_FILE_DATA *data)
{
int len = 0;
unsigned offset = 0;
int apdu_len = 0, len = 0;
if (!apdu) {
return BACNET_STATUS_ERROR;
}
/* optional checking - most likely was already done prior to this call */
if (apdu_size < 4) {
return BACNET_STATUS_ERROR;
}
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) {
return BACNET_STATUS_ERROR;
}
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (invoke_id) {
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
}
if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_READ_FILE) {
return BACNET_STATUS_ERROR;
}
offset = 4;
if (apdu_len > offset) {
len =
arf_decode_service_request(&apdu[offset], apdu_len - offset, data);
len = 4;
apdu_len += len;
len =
arf_decode_service_request(&apdu[apdu_len], apdu_size - apdu_len, data);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
return len;
return apdu_len;
}
/* encode service */
int arf_ack_encode_apdu(
uint8_t *apdu, uint8_t invoke_id, BACNET_ATOMIC_READ_FILE_DATA *data)
/**
* @brief Encode the AtomicReadFile-ACK service request
*
* AtomicReadFile-ACK ::= SEQUENCE {
* end-of-file BOOLEAN,
* access-method CHOICE {
* stream-access [0] SEQUENCE {
* file-start-position INTEGER,
* file-data OCTET STRING
* },
* record-access [1] SEQUENCE {
* file-start-record INTEGER,
* returned-record-count Unsigned,
* file-record-data SEQUENCE OF OCTET STRING
* }
* }
* }
*
* @param apdu Pointer to the buffer for encoding, or NULL for length
* @param data Pointer to the data to be encoded
* @return number of bytes encoded
*/
int arf_ack_service_encode_apdu(
uint8_t *apdu, BACNET_ATOMIC_READ_FILE_DATA *data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0;
uint32_t i = 0;
/* endOfFile */
len = encode_application_boolean(apdu, data->endOfFile);
if (apdu) {
apdu[0] = PDU_TYPE_COMPLEX_ACK;
apdu[1] = invoke_id;
apdu[2] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; /* service choice */
apdu_len = 3;
/* endOfFile */
apdu_len +=
encode_application_boolean(&apdu[apdu_len], data->endOfFile);
switch (data->access) {
case FILE_STREAM_ACCESS:
apdu_len += encode_opening_tag(&apdu[apdu_len], 0);
apdu_len += encode_application_signed(
&apdu[apdu_len], data->type.stream.fileStartPosition);
apdu_len += encode_application_octet_string(
&apdu[apdu_len], &data->fileData[0]);
apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
break;
case FILE_RECORD_ACCESS:
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
apdu_len += encode_application_signed(
&apdu[apdu_len], data->type.record.fileStartRecord);
apdu_len += encode_application_unsigned(
&apdu[apdu_len], data->type.record.RecordCount);
for (i = 0; i < data->type.record.RecordCount; i++) {
apdu_len += encode_application_octet_string(
&apdu[apdu_len], &data->fileData[i]);
apdu += len;
}
apdu_len += len;
switch (data->access) {
case FILE_STREAM_ACCESS:
len = encode_opening_tag(apdu, 0);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_application_signed(
apdu, data->type.stream.fileStartPosition);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_application_octet_string(apdu, &data->fileData[0]);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_closing_tag(apdu, 0);
apdu_len += len;
break;
case FILE_RECORD_ACCESS:
len = encode_opening_tag(apdu, 1);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_application_signed(
apdu, data->type.record.fileStartRecord);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_application_unsigned(
apdu, data->type.record.RecordCount);
apdu_len += len;
if (apdu) {
apdu += len;
}
for (i = 0; i < data->type.record.RecordCount; i++) {
len = encode_application_octet_string(apdu, &data->fileData[i]);
apdu_len += len;
if (apdu) {
apdu += len;
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 1);
break;
default:
break;
}
}
len = encode_closing_tag(apdu, 1);
apdu_len += len;
break;
default:
break;
}
return apdu_len;
}
/* decode the service request only */
int arf_ack_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_ATOMIC_READ_FILE_DATA *data)
/**
* @brief Encode the AtomicReadFile-ACK service request
* @param apdu Pointer to the buffer for decoding.
* @param invoke_id original invoke id for request
* @param data Pointer to the property decoded data to be stored
* @return number of bytes encoded
*/
int arf_ack_encode_apdu(
uint8_t *apdu, uint8_t invoke_id, BACNET_ATOMIC_READ_FILE_DATA *data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0;
int tag_len = 0;
int decoded_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint32_t i = 0;
/* check for value pointers */
if (apdu_len && data) {
len =
decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type);
if (tag_number != BACNET_APPLICATION_TAG_BOOLEAN) {
return BACNET_STATUS_ERROR;
}
data->endOfFile = decode_boolean(len_value_type);
if (decode_is_opening_tag_number(&apdu[len], 0)) {
data->access = FILE_STREAM_ACCESS;
/* a tag number is not extended so only one octet */
len++;
/* fileStartPosition */
tag_len = decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) {
return BACNET_STATUS_ERROR;
}
len += decode_signed(&apdu[len], len_value_type,
&data->type.stream.fileStartPosition);
/* fileData */
tag_len = decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) {
return BACNET_STATUS_ERROR;
}
decoded_len = decode_octet_string(
&apdu[len], len_value_type, &data->fileData[0]);
if ((uint32_t)decoded_len != len_value_type) {
return BACNET_STATUS_ERROR;
}
len += decoded_len;
if (!decode_is_closing_tag_number(&apdu[len], 0)) {
return BACNET_STATUS_ERROR;
}
/* a tag number is not extended so only one octet */
len++;
} else if (decode_is_opening_tag_number(&apdu[len], 1)) {
data->access = FILE_RECORD_ACCESS;
/* a tag number is not extended so only one octet */
len++;
/* fileStartRecord */
tag_len = decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) {
return BACNET_STATUS_ERROR;
}
len += decode_signed(
&apdu[len], len_value_type, &data->type.record.fileStartRecord);
/* returnedRecordCount */
tag_len = decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
return BACNET_STATUS_ERROR;
}
len += decode_unsigned(
&apdu[len], len_value_type, &data->type.record.RecordCount);
for (i = 0; i < data->type.record.RecordCount; i++) {
/* fileData */
tag_len = decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) {
return BACNET_STATUS_ERROR;
}
decoded_len = decode_octet_string(
&apdu[len], len_value_type, &data->fileData[i]);
if ((uint32_t)decoded_len != len_value_type) {
return BACNET_STATUS_ERROR;
}
len += decoded_len;
}
if (!decode_is_closing_tag_number(&apdu[len], 1)) {
return BACNET_STATUS_ERROR;
}
/* a tag number is not extended so only one octet */
len++;
} else {
return BACNET_STATUS_ERROR;
}
if (apdu) {
apdu[0] = PDU_TYPE_COMPLEX_ACK;
apdu[1] = invoke_id;
apdu[2] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; /* service choice */
}
len = 3;
apdu_len += len;
if (apdu) {
apdu += len;
}
len = arf_ack_service_encode_apdu(apdu, data);
apdu_len += len;
return len;
return apdu_len;
}
/**
* @brief Decoding for AtomicReadFile-ACK APDU service data
*
* AtomicReadFile-ACK ::= SEQUENCE {
* end-of-file BOOLEAN,
* access-method CHOICE {
* stream-access [0] SEQUENCE {
* file-start-position INTEGER,
* file-data OCTET STRING
* },
* record-access [1] SEQUENCE {
* file-start-record INTEGER,
* returned-record-count Unsigned,
* file-record-data SEQUENCE OF OCTET STRING
* }
* }
* }
*
* @param apdu Pointer to the buffer for decoding.
* @param apdu_size size of the buffer for decoding.
* @param data Pointer to the property data to be encoded,
* or NULL for length
* @return Bytes encoded or BACNET_STATUS_ERROR on error.
*/
int arf_ack_decode_service_request(
uint8_t *apdu, unsigned apdu_size, BACNET_ATOMIC_READ_FILE_DATA *data)
{
int apdu_len = 0;
int len = 0;
bool endOfFile;
int32_t signed_integer;
BACNET_UNSIGNED_INTEGER record_count, i;
BACNET_OCTET_STRING *octet_string = NULL;
len = bacnet_boolean_application_decode(apdu, apdu_size, &endOfFile);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (data) {
data->endOfFile = endOfFile;
}
apdu_len += len;
if (bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 0, &len)) {
if (data) {
data->access = FILE_STREAM_ACCESS;
}
apdu_len += len;
/* fileStartPosition */
len = bacnet_signed_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &signed_integer);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (data) {
data->type.stream.fileStartPosition = signed_integer;
}
apdu_len += len;
/* fileData */
if (data) {
octet_string = &data->fileData[0];
}
len = bacnet_octet_string_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, octet_string);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
if (!bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 0, &len)) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
} else if (bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 1, &len)) {
if (data) {
data->access = FILE_RECORD_ACCESS;
}
apdu_len += len;
/* fileStartRecord */
len = bacnet_signed_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &signed_integer);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (data) {
data->type.record.fileStartRecord = signed_integer;
}
apdu_len += len;
/* returnedRecordCount */
len = bacnet_unsigned_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &record_count);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (data) {
data->type.record.RecordCount = record_count;
}
apdu_len += len;
for (i = 0; i < record_count; i++) {
/* fileData */
if (i >= BACNET_READ_FILE_RECORD_COUNT) {
octet_string = NULL;
} else if (data) {
octet_string = &data->fileData[i];
} else {
octet_string = NULL;
}
len = bacnet_octet_string_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, octet_string);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
}
if (!bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 1, &len)) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
} else {
return BACNET_STATUS_ERROR;
}
return apdu_len;
}
/**
* @brief Decoding for AtomicReadFile-ACK APDU service data
* @param apdu Pointer to the buffer for decoding.
* @param apdu_size size of the buffer for decoding.
* @param invoke_id [in] Invoked service ID.
* @param data Pointer to the property data values to be stored,
* or NULL for length
* @return number of bytes decoded, or BACNET_STATUS_ERROR on error
*/
int arf_ack_decode_apdu(uint8_t *apdu,
unsigned apdu_len,
unsigned apdu_size,
uint8_t *invoke_id,
BACNET_ATOMIC_READ_FILE_DATA *data)
{
int len = 0;
unsigned offset = 0;
int apdu_len = 0, len = 0;
if (!apdu) {
return BACNET_STATUS_ERROR;
}
/* optional checking - most likely was already done prior to this call */
if (apdu_size < 3) {
return BACNET_STATUS_ERROR;
}
if (apdu[0] != PDU_TYPE_COMPLEX_ACK) {
return BACNET_STATUS_ERROR;
}
*invoke_id = apdu[1]; /* invoke id - filled in by net layer */
if (invoke_id) {
*invoke_id = apdu[1]; /* invoke id - filled in by net layer */
}
if (apdu[2] != SERVICE_CONFIRMED_ATOMIC_READ_FILE) {
return BACNET_STATUS_ERROR;
}
offset = 3;
if (apdu_len > offset) {
len = arf_ack_decode_service_request(
&apdu[offset], apdu_len - offset, data);
len = 3;
apdu_len += len;
len = arf_ack_decode_service_request(
&apdu[apdu_len], apdu_size - apdu_len, data);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
return len;
return apdu_len;
}