f806c5829b
* pre-commit: Update and enable clang-format check There is newer version from clang-format so use that. We do not yet want 18 as that is little bit too new. * Format some thing by hand which clang-format "breaks" Clang-format will format some things little bit off in some cases. Format some things by hand so we get cleaner end result. * Run clang-format with ``` pre-commit run --all-files clang-format ``` We have already in previously checked places where clang-format does not make good format and ignored those (hopefully most of the things). --------- Co-authored-by: Kari Argillander <kari.argillander@fidelix.com>
599 lines
18 KiB
C
599 lines
18 KiB
C
/**
|
|
* @file
|
|
* @brief BACnet AtomicReadFile service structures, codecs, and handlers.
|
|
* @author Steve Karg <skarg@users.sourceforge.net>
|
|
* @date 2005
|
|
* @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/arf.h"
|
|
|
|
/**
|
|
* @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, const 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 encoded values
|
|
* @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 atomicreadfile_service_request_encode(
|
|
uint8_t *apdu, size_t apdu_size, const BACNET_ATOMIC_READ_FILE_DATA *data)
|
|
{
|
|
size_t apdu_len = 0; /* total length of the apdu, return value */
|
|
|
|
apdu_len = arf_service_encode_apdu(NULL, data);
|
|
if (apdu_len > apdu_size) {
|
|
apdu_len = 0;
|
|
} else {
|
|
apdu_len = arf_service_encode_apdu(apdu, data);
|
|
}
|
|
|
|
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, const 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 */
|
|
}
|
|
len = 4;
|
|
apdu_len += len;
|
|
if (apdu) {
|
|
apdu += len;
|
|
}
|
|
len = arf_service_encode_apdu(apdu, data);
|
|
apdu_len += len;
|
|
|
|
return apdu_len;
|
|
}
|
|
|
|
/**
|
|
* @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(
|
|
const uint8_t *apdu, unsigned apdu_size, BACNET_ATOMIC_READ_FILE_DATA *data)
|
|
{
|
|
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;
|
|
|
|
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;
|
|
}
|
|
if (data) {
|
|
data->object_type = (BACNET_OBJECT_TYPE)object_type;
|
|
data->object_instance = object_instance;
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
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(
|
|
const uint8_t *apdu,
|
|
unsigned apdu_size,
|
|
uint8_t *invoke_id,
|
|
BACNET_ATOMIC_READ_FILE_DATA *data)
|
|
{
|
|
int apdu_len = 0, len = 0;
|
|
|
|
if (!apdu) {
|
|
return BACNET_STATUS_ERROR;
|
|
}
|
|
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); */
|
|
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;
|
|
}
|
|
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 apdu_len;
|
|
}
|
|
|
|
/**
|
|
* @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, const 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 += 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;
|
|
}
|
|
}
|
|
len = encode_closing_tag(apdu, 1);
|
|
apdu_len += len;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return apdu_len;
|
|
}
|
|
|
|
/**
|
|
* @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, const 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_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 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(
|
|
const 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(
|
|
const uint8_t *apdu,
|
|
unsigned apdu_size,
|
|
uint8_t *invoke_id,
|
|
BACNET_ATOMIC_READ_FILE_DATA *data)
|
|
{
|
|
int apdu_len = 0, len = 0;
|
|
|
|
if (!apdu) {
|
|
return BACNET_STATUS_ERROR;
|
|
}
|
|
if (apdu_size < 3) {
|
|
return BACNET_STATUS_ERROR;
|
|
}
|
|
if (apdu[0] != PDU_TYPE_COMPLEX_ACK) {
|
|
return BACNET_STATUS_ERROR;
|
|
}
|
|
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;
|
|
}
|
|
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 apdu_len;
|
|
}
|