diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a89bc3a..e8768011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ The git repositories are hosted at the following sites: * https://bacnet.sourceforge.net/ * https://github.com/bacnet-stack/bacnet-stack/ -## [Unreleased] - 2026-02-19 +## [Unreleased] - 2026-02-23 ### Security @@ -41,6 +41,8 @@ The git repositories are hosted at the following sites: ### Added +* Add initialization and unpacking functions for BACnet Character + String buffer structure. (#1242) * Add Host-N-Port minimal encode and decode which utilizes the octet string and character string buffer encode and decode. (#1239) * Added API for extending the basic Device object and children with diff --git a/src/bacnet/bacdcode.c b/src/bacnet/bacdcode.c index ed87b4fc..39925202 100644 --- a/src/bacnet/bacdcode.c +++ b/src/bacnet/bacdcode.c @@ -3456,6 +3456,65 @@ int bacnet_character_string_buffer_context_decode( return apdu_len; } +/** + * @brief Initializes a BACnet Character String buffer value + * + * @param value - the BACnet Character String buffer value to be initialized + * @param encoding - pointer to the BACnet Character String encoding value + * @param buffer - pointer to the buffer to hold the string value + * @param buffer_length - pointer to hold the buffer length + * @param buffer_size - size of the buffer in bytes + */ +void bacnet_character_string_buffer_init( + BACNET_CHARACTER_STRING_BUFFER *value, + uint8_t encoding, + char *buffer, + size_t buffer_size) +{ + if (value) { + value->encoding = encoding; + value->buffer = buffer; + value->buffer_size = buffer_size; + value->buffer_length = 0; + } +} + +/** + * @brief Unpacks a BACnet Character String buffer value from bytes + * + * @param value - the BACnet Character String buffer value to be unpacked + * @param encoding - pointer to the BACnet Character String encoding value + * @param buffer - pointer to the buffer to hold the string value + * @param buffer_length - pointer to hold the buffer length + * + * @return true if unpacked successfully, false if errors occur + */ +bool bacnet_character_string_buffer_unpack( + const BACNET_CHARACTER_STRING_BUFFER *value, + uint8_t *encoding, + char *buffer, + uint32_t *buffer_length) +{ + if (value) { + if (value->buffer_length > value->buffer_size) { + /* fail if buffer length is greater than buffer size */ + return false; + } + if (encoding) { + *encoding = value->encoding; + } + if (buffer_length) { + *buffer_length = value->buffer_length; + } + if (buffer && value->buffer && value->buffer_length) { + memcpy(buffer, value->buffer, value->buffer_length); + } + return true; + } + + return false; +} + /** * @brief Decodes from bytes into a BACnet Unsigned value * from clause 20.2.4 Encoding of an Unsigned Integer Value diff --git a/src/bacnet/bacdcode.h b/src/bacnet/bacdcode.h index c8454844..5aa7364a 100644 --- a/src/bacnet/bacdcode.h +++ b/src/bacnet/bacdcode.h @@ -524,6 +524,18 @@ int bacnet_character_string_buffer_context_decode( uint32_t apdu_size, uint8_t tag_value, BACNET_CHARACTER_STRING_BUFFER *value); +BACNET_STACK_EXPORT +void bacnet_character_string_buffer_init( + BACNET_CHARACTER_STRING_BUFFER *value, + uint8_t encoding, + char *buffer, + size_t buffer_size); +BACNET_STACK_EXPORT +bool bacnet_character_string_buffer_unpack( + const BACNET_CHARACTER_STRING_BUFFER *value, + uint8_t *encoding, + char *buffer, + uint32_t *buffer_length); BACNET_STACK_EXPORT int encode_bacnet_character_string( diff --git a/src/bacnet/hostnport.c b/src/bacnet/hostnport.c index 0bc2681c..70922b6f 100644 --- a/src/bacnet/hostnport.c +++ b/src/bacnet/hostnport.c @@ -457,9 +457,11 @@ int host_n_port_minimal_address_decode( { int apdu_len = 0, len = 0; uint8_t *buffer = NULL; + uint32_t buffer_length = 0; size_t buffer_size = 0; BACNET_TAG tag = { 0 }; BACNET_CHARACTER_STRING_BUFFER char_string = { 0 }; + bool status = false; /* default reject code */ if (error_code) { @@ -507,10 +509,9 @@ int host_n_port_minimal_address_decode( } else if (tag.context && (tag.number == 2)) { if (address) { address->tag = BACNET_HOST_ADDRESS_TAG_NAME; - char_string.buffer = address->host.name.fqdn; - char_string.buffer_size = sizeof(address->host.name.fqdn); - char_string.encoding = CHARACTER_ANSI_X34; - char_string.buffer_length = 0; + bacnet_character_string_buffer_init( + &char_string, CHARACTER_ANSI_X34, address->host.name.fqdn, + sizeof(address->host.name.fqdn)); } len = bacnet_character_string_buffer_decode( &apdu[apdu_len], apdu_size - apdu_len, tag.len_value_type, @@ -522,13 +523,16 @@ int host_n_port_minimal_address_decode( return BACNET_STATUS_REJECT; } if (address) { - if (char_string.buffer_length > char_string.buffer_size) { + status = bacnet_character_string_buffer_unpack( + &char_string, NULL, NULL, &buffer_length); + if (status) { + address->host.name.length = buffer_length; + } else { if (error_code) { *error_code = ERROR_CODE_REJECT_BUFFER_OVERFLOW; } return BACNET_STATUS_REJECT; } - address->host.name.length = char_string.buffer_length; } apdu_len += len; } else { diff --git a/test/bacnet/bacdcode/src/main.c b/test/bacnet/bacdcode/src/main.c index 381a9e6c..3d7aa2bc 100644 --- a/test/bacnet/bacdcode/src/main.c +++ b/test/bacnet/bacdcode/src/main.c @@ -2415,17 +2415,19 @@ static void test_bacnet_character_string_buffer(void) { uint8_t apdu[50] = { 0 }; char buffer[26] = { 0 }, test_buffer[26] = { 0 }; + char unpack_buffer[26] = { 0 }; BACNET_CHARACTER_STRING_BUFFER value = { 0 }, test_value = { 0 }; int apdu_len = 0, null_len = 0, test_len = 0; uint8_t tag_number = 0; + uint8_t unpack_encoding = 0; + uint32_t unpack_buffer_length = 0; unsigned i; int diff = 0; /* for memcmp */ - value.buffer = buffer; - value.buffer_size = sizeof(buffer); - value.encoding = CHARACTER_ANSI_X34; - test_value.buffer = test_buffer; - test_value.buffer_size = sizeof(test_buffer); + bacnet_character_string_buffer_init( + &value, CHARACTER_ANSI_X34, buffer, sizeof(buffer)); + bacnet_character_string_buffer_init( + &test_value, CHARACTER_ANSI_X34, test_buffer, sizeof(test_buffer)); apdu_len = encode_application_character_string_buffer(apdu, &value); null_len = encode_application_character_string_buffer(NULL, &value); zassert_equal(apdu_len, null_len, NULL); @@ -2450,6 +2452,12 @@ static void test_bacnet_character_string_buffer(void) zassert_equal(diff, 0, NULL); zassert_equal(value.buffer_length, test_value.buffer_length, NULL); zassert_equal(value.encoding, test_value.encoding, NULL); + bacnet_character_string_buffer_unpack( + &value, &unpack_encoding, unpack_buffer, &unpack_buffer_length); + zassert_equal(value.buffer_length, unpack_buffer_length, NULL); + zassert_equal(value.encoding, unpack_encoding, NULL); + diff = memcmp(buffer, unpack_buffer, value.buffer_length); + zassert_equal(diff, 0, NULL); /* context tagged */ tag_number = i; apdu_len = bacnet_character_string_buffer_context_encode( @@ -2469,6 +2477,12 @@ static void test_bacnet_character_string_buffer(void) zassert_equal(diff, 0, NULL); zassert_equal(value.buffer_length, test_value.buffer_length, NULL); zassert_equal(value.encoding, test_value.encoding, NULL); + bacnet_character_string_buffer_unpack( + &value, &unpack_encoding, unpack_buffer, &unpack_buffer_length); + zassert_equal(value.buffer_length, unpack_buffer_length, NULL); + zassert_equal(value.encoding, unpack_encoding, NULL); + diff = memcmp(buffer, unpack_buffer, value.buffer_length); + zassert_equal(diff, 0, NULL); } }