From 2af16ab14d0a68088d8f96da43925b9a781f5d05 Mon Sep 17 00:00:00 2001 From: Roy Schneider Date: Tue, 14 Jul 2020 18:41:51 +0200 Subject: [PATCH] Feature/comments and review (#102) * Added comments. Replaced -1 with BACNET_STATUS_ERROR macro. * Added some DogyGen comments * Added missing return value check for booleans. * Check used bits against unused bits in bitstrings. * Added missing return value check for bitstrings. * Added sanity checks and comments. * Fixed implicit type conv warning. * Chose to keep simpler C function methods. Co-authored-by: Steve Karg Co-authored-by: Steve Karg --- src/bacnet/bacdcode.c | 319 ++++++++++++++++---- src/bacnet/bacdevobjpropref.c | 266 +++++++++++------ src/bacnet/bacstr.c | 479 ++++++++++++++++++++++++------ src/bacnet/basic/object/command.c | 6 + src/bacnet/event.c | 2 +- 5 files changed, 838 insertions(+), 234 deletions(-) diff --git a/src/bacnet/bacdcode.c b/src/bacnet/bacdcode.c index 868a6cbd..db49c9c4 100644 --- a/src/bacnet/bacdcode.c +++ b/src/bacnet/bacdcode.c @@ -90,9 +90,14 @@ B'111' interpreted as Type = Closing Tag */ -/* from clause 20.1.2.4 max-segments-accepted */ -/* and clause 20.1.2.5 max-APDU-length-accepted */ -/* returns the encoded octet */ +/** + * Encode the max APDU value and return the encoded octed. + * + * @param max_segs from clause 20.1.2.4 max-segments-accepted + * @param max_apdu from clause 20.1.2.5 max-APDU-length-accepted + * + * @return The encoded octet + */ uint8_t encode_max_segs_max_apdu(int max_segs, int max_apdu) { uint8_t octet = 0; @@ -136,9 +141,14 @@ uint8_t encode_max_segs_max_apdu(int max_segs, int max_apdu) return octet; } -/* from clause 20.1.2.4 max-segments-accepted */ -/* and clause 20.1.2.5 max-APDU-length-accepted */ -/* returns the encoded octet */ +/** + * Decode the given octed into a maximum segments value. + * + * @param octed From clause 20.1.2.4 max-segments-accepted + * and clause 20.1.2.5 max-APDU-length-accepted + * + * @return Returns the maximum segments value. + */ int decode_max_segs(uint8_t octet) { int max_segs = 0; @@ -175,6 +185,14 @@ int decode_max_segs(uint8_t octet) return max_segs; } +/** + * Decode the given octed into a maximum APDU value. + * + * @param octed From clause 20.1.2.4 max-segments-accepted + * and clause 20.1.2.5 max-APDU-length-accepted + * + * @return Returns the maximum APDU value. + */ int decode_max_apdu(uint8_t octet) { int max_apdu = 0; @@ -205,8 +223,18 @@ int decode_max_apdu(uint8_t octet) return max_apdu; } -/* from clause 20.2.1 General Rules for Encoding BACnet Tags */ -/* returns the number of apdu bytes consumed */ +/** + * Encode a BACnet tag and returns the number of bytes consumed. + * (From clause 20.2.1 General Rules for Encoding BACnet Tags) + * + * @param apdu Pointer to the encode buffer. + * @param tag_number Number of the tag to encode, + * see BACNET_APPLICATION_TAG_X macros. + * @param context_specific Indicates to encode in the given context. + * @param len_value_type Indicates the length of the tag's content. + * + * @return Returns the number of apdu bytes consumed. + */ int encode_tag(uint8_t *apdu, uint8_t tag_number, bool context_specific, @@ -249,8 +277,17 @@ int encode_tag(uint8_t *apdu, return len; } -/* from clause 20.2.1.3.2 Constructed Data */ -/* returns the number of apdu bytes consumed */ +/** + * Encode a BACnet opening tag and returns the number + * of bytes consumed. + * (From clause 20.2.1.3.2 Constructed Data.) + * + * @param apdu Pointer to the encode buffer. + * @param tag_number Number of the tag to encode, + * see BACNET_APPLICATION_TAG_X macros. + * + * @return Returns the number of apdu bytes consumed. + */ int encode_opening_tag(uint8_t *apdu, uint8_t tag_number) { int len = 1; @@ -271,8 +308,17 @@ int encode_opening_tag(uint8_t *apdu, uint8_t tag_number) return len; } -/* from clause 20.2.1.3.2 Constructed Data */ -/* returns the number of apdu bytes consumed */ +/** + * Encode a BACnet closing tag and returns the number + * of bytes consumed. + * (From clause 20.2.1.3.2 Constructed Data.) + * + * @param apdu Pointer to the encode buffer. + * @param tag_number Number of the tag to encode, + * see BACNET_APPLICATION_TAG_X macros. + * + * @return Returns the number of apdu bytes consumed. + */ int encode_closing_tag(uint8_t *apdu, uint8_t tag_number) { int len = 1; @@ -345,20 +391,45 @@ int bacnet_tag_number_decode( return len; } +/** + * @brief Returns if at the given pointer a + * opening tag has been found. + * + * @param apdu Pointer to the tag number. + * + * @return true/false + */ bool decode_is_opening_tag(uint8_t *apdu) { return (bool)((apdu[0] & 0x07) == 6); } -/* from clause 20.2.1.3.2 Constructed Data */ -/* returns the number of apdu bytes consumed */ +/** + * @brief Returns if at the given pointer a + * closing tag has been found. + * + * @param apdu Pointer to the tag number. + * + * @return true/false + */ bool decode_is_closing_tag(uint8_t *apdu) { return (bool)((apdu[0] & 0x07) == 7); } -/* from clause 20.2.1.3.2 Constructed Data */ -/* returns the number of apdu bytes consumed */ +/** + * @brief Decodes the tag number and the value, + * that the APDU pointer is addressing. + * (From clause 20.2.1.3.2 Constructed Data) + * + * @param apdu Pointer to the message buffer. + * @param tag_number Pointer to a variable + * taking the tag number. + * @param value Pointer to a variable + * taking the value. + * + * @return number of bytes decoded, or zero if errors occur + */ int decode_tag_number_and_value( uint8_t *apdu, uint8_t *tag_number, uint32_t *value) { @@ -371,6 +442,7 @@ int decode_tag_number_and_value( /* tagged as uint32_t */ if (apdu[len] == 255) { len++; + value32 = 0; len += decode_unsigned32(&apdu[len], &value32); if (value) { *value = value32; @@ -379,6 +451,7 @@ int decode_tag_number_and_value( /* tagged as uint16_t */ else if (apdu[len] == 254) { len++; + value16 = 0; len += decode_unsigned16(&apdu[len], &value16); if (value) { *value = value16; @@ -392,6 +465,7 @@ int decode_tag_number_and_value( len++; } } else if (IS_OPENING_TAG(apdu[0]) && value) { + /* opening tag */ *value = 0; } else if (IS_CLOSING_TAG(apdu[0]) && value) { /* closing tag */ @@ -466,16 +540,37 @@ int bacnet_tag_number_and_value_decode( return len; } -/* from clause 20.2.1.3.2 Constructed Data */ -/* returns true if the tag is context specific and matches */ +/** + * @brief Returns true if the tag is context specific + * and matches, as defined in clause 20.2.1.3.2 Constructed + * Data. + * + * @param apdu Pointer to the tag begin. + * @param tag_number Tag number, that has been decoded before. + * + * @return true on a match, false otherwise. + */ bool decode_is_context_tag(uint8_t *apdu, uint8_t tag_number) { uint8_t my_tag_number = 0; decode_tag_number(apdu, &my_tag_number); + return (bool)(IS_CONTEXT_SPECIFIC(*apdu) && (my_tag_number == tag_number)); } +/** + * @brief Returns true if the tag is context specific + * and matches, as defined in clause 20.2.1.3.2 Constructed + * Data. This function returns the tag length as well. + * + * @param apdu Pointer to the tag begin. + * @param tag_number Tag number, that has been decoded before. + * @param tag_length Pointer to a variable, taking the + * length of the tag in bytes. + * + * @return true on a match, false otherwise. + */ bool decode_is_context_tag_with_length( uint8_t *apdu, uint8_t tag_number, int *tag_length) { @@ -486,18 +581,35 @@ bool decode_is_context_tag_with_length( return (bool)(IS_CONTEXT_SPECIFIC(*apdu) && (my_tag_number == tag_number)); } -/* from clause 20.2.1.3.2 Constructed Data */ -/* returns the true if the tag matches */ +/** + * @brief Returns true if the tag does match and it + * is an opening tag as well. + * As defined in clause 20.2.1.3.2 Constructed Data. + * + * @param apdu Pointer to the tag begin. + * @param tag_number Tag number, that has been decoded before. + * + * @return true on a match, false otherwise. + */ bool decode_is_opening_tag_number(uint8_t *apdu, uint8_t tag_number) { uint8_t my_tag_number = 0; decode_tag_number(apdu, &my_tag_number); + return (bool)(IS_OPENING_TAG(apdu[0]) && (my_tag_number == tag_number)); } -/* from clause 20.2.1.3.2 Constructed Data */ -/* returns the number of apdu bytes consumed */ +/** + * @brief Returns true if the tag does match and it + * is an closing tag as well. + * As defined in clause 20.2.1.3.2 Constructed Data. + * + * @param apdu Pointer to the tag begin. + * @param tag_number Tag number, that has been decoded before. + * + * @return true on a match, false otherwise. + */ bool decode_is_closing_tag_number(uint8_t *apdu, uint8_t tag_number) { uint8_t my_tag_number = 0; @@ -506,28 +618,49 @@ bool decode_is_closing_tag_number(uint8_t *apdu, uint8_t tag_number) return (bool)(IS_CLOSING_TAG(apdu[0]) && (my_tag_number == tag_number)); } -/* from clause 20.2.3 Encoding of a Boolean Value */ -/* and 20.2.1 General Rules for Encoding BACnet Tags */ -/* returns the number of apdu bytes consumed */ +/** + * @brief Encode an boolean value. + * From clause 20.2.3 Encoding of a Boolean Value + * and 20.2.1 General Rules for Encoding BACnet Tags. + * + * @param apdu Pointer to the encode buffer. + * @param boolean_value Boolean value to encode. + * + * @return returns the number of apdu bytes consumed + */ int encode_application_boolean(uint8_t *apdu, bool boolean_value) { int len = 0; - uint32_t len_value = 0; + uint32_t len_value; if (boolean_value) { len_value = 1; + } else { + len_value = 0; } + len = encode_tag(&apdu[0], BACNET_APPLICATION_TAG_BOOLEAN, false, len_value); return len; } -/* context tagged is encoded differently */ +/** + * @brief Encode an boolean value in a context. + * From clause 20.2.3 Encoding of a Boolean Value + * and 20.2.1 General Rules for Encoding BACnet Tags. + * + * @param apdu Pointer to the encode buffer. + * @param tag_number Tag number in which context + * the value shall be encoded. + * @param boolean_value Boolean value to encode. + * + * @return returns the number of apdu bytes consumed + */ int encode_context_boolean( uint8_t *apdu, uint8_t tag_number, bool boolean_value) { - int len = 0; /* return value */ + int len; /* return value */ len = encode_tag(&apdu[0], (uint8_t)tag_number, true, 1); apdu[len] = (bool)(boolean_value ? 1 : 0); @@ -536,6 +669,13 @@ int encode_context_boolean( return len; } +/** + * @brief Decode an boolean value. + * + * @param apdu Pointer to the encode buffer. + * + * @return true/false + */ bool decode_context_boolean(uint8_t *apdu) { bool boolean_value = false; @@ -547,6 +687,17 @@ bool decode_context_boolean(uint8_t *apdu) return boolean_value; } +/** + * @brief Decode an boolean value in the context of a tag. + * + * @param apdu Pointer to the encode buffer. + * @param tag_number Tag number in which context + * the value shall be encoded. + * @param boolean_value Pointer to a boolean variable + * taking the value. + * + * @return The count of bytes decoded or BACNET_STATUS_ERROR. + */ int decode_context_boolean2( uint8_t *apdu, uint8_t tag_number, bool *boolean_value) { @@ -564,33 +715,65 @@ int decode_context_boolean2( return len; } -/* from clause 20.2.3 Encoding of a Boolean Value */ -/* and 20.2.1 General Rules for Encoding BACnet Tags */ -/* returns the number of apdu bytes consumed */ +/** + * @brief Check the length value and return the boolean meaning. + * From clause 20.2.3 Encoding of a Boolean Value + * and 20.2.1 General Rules for Encoding BACnet Tags. + * + * @param len_value Tag length value. + * + * @return true/false + */ bool decode_boolean(uint32_t len_value) { - bool boolean_value = false; + bool boolean_value; if (len_value) { boolean_value = true; + } else { + boolean_value = false; } return boolean_value; } -/* from clause 20.2.2 Encoding of a Null Value */ -/* and 20.2.1 General Rules for Encoding BACnet Tags */ -/* returns the number of apdu bytes consumed */ +/** + * @brief Encode a Null value. + * From clause 20.2.2 Encoding of a Null Value + * and 20.2.1 General Rules for Encoding BACnet Tags. + * + * @param apdu Pointer to the encode buffer. + * + * @return returns the number of apdu bytes consumed. + */ int encode_application_null(uint8_t *apdu) { return encode_tag(&apdu[0], BACNET_APPLICATION_TAG_NULL, false, 0); } +/** + * @brief Encode a Null value in a tag context. + * From clause 20.2.2 Encoding of a Null Value + * and 20.2.1 General Rules for Encoding BACnet Tags. + * + * @param apdu Pointer to the encode buffer. + * @param tag_number Tag number in which context + * the value shall be encoded. + * + * @return returns the number of apdu bytes consumed. + */ int encode_context_null(uint8_t *apdu, uint8_t tag_number) { return encode_tag(&apdu[0], tag_number, true, 0); } +/** + * @brief Reverse the bits of the given byte. + * + * @param in_byte Byte to reverse. + * + * @return Byte with reversed bit order. + */ static uint8_t byte_reverse_bits(uint8_t in_byte) { uint8_t out_byte = 0; @@ -623,35 +806,61 @@ static uint8_t byte_reverse_bits(uint8_t in_byte) return out_byte; } -/* from clause 20.2.10 Encoding of a Bit String Value */ -/* returns the number of apdu bytes consumed */ +/** + * @brief Decode a bit string value. + * (From clause 20.2.10 Encoding of a Bit String Value.) + * + * @param apdu Pointer to the buffer holding the message data. + * @param len_value Length of the Data to decode. + * @param bit_string Pointer to the bit string variable, + * taking the decoded result. + * + * @return Returns the number of apdu bytes consumed. + */ int decode_bitstring( - uint8_t *apdu, uint32_t len_value, BACNET_BIT_STRING *bit_string) + uint8_t * apdu, + uint32_t len_value, + BACNET_BIT_STRING * bit_string) { - int len = 0; - uint8_t unused_bits = 0; - uint32_t i = 0; - uint32_t bytes_used = 0; + int len = 0; /* Return value */ + uint8_t unused_bits; + uint32_t i; + uint32_t bytes_used; - bitstring_init(bit_string); - if (len_value) { - /* the first octet contains the unused bits */ - bytes_used = len_value - 1; - if (bytes_used <= MAX_BITSTRING_BYTES) { - len = 1; - for (i = 0; i < bytes_used; i++) { - bitstring_set_octet( - bit_string, (uint8_t)i, byte_reverse_bits(apdu[len++])); + if (apdu && bit_string) { + /* Init/empty the string. */ + bitstring_init(bit_string); + if (len_value) { + /* The first octet contains the unused bits. */ + bytes_used = len_value - 1; + if (bytes_used <= MAX_BITSTRING_BYTES) { + len = 1; + /* Copy the bytes in reversed bit order. */ + for (i = 0; i < bytes_used; i++) { + bitstring_set_octet(bit_string, (uint8_t) i, + byte_reverse_bits(apdu[len++])); + } + /* Erase the remaining unsed bits. */ + unused_bits = (uint8_t) (apdu[0] & 0x07); + bitstring_set_bits_used(bit_string, (uint8_t) bytes_used, + unused_bits); } - unused_bits = (uint8_t)(apdu[0] & 0x07); - bitstring_set_bits_used( - bit_string, (uint8_t)bytes_used, unused_bits); } } - return len; } +/** + * @brief Decode a bit string value in the given context. + * (From clause 20.2.10 Encoding of a Bit String Value.) + * + * @param apdu Pointer to the buffer holding the message data. + * @param tag_number Tag number (context). + * @param bit_string Pointer to the bit string variable, + * taking the decoded result. + * + * @return Returns the number of bytes dcoded or BACNET_STATUS_ERROR. + */ int decode_context_bitstring( uint8_t *apdu, uint8_t tag_number, BACNET_BIT_STRING *bit_string) { diff --git a/src/bacnet/bacdevobjpropref.c b/src/bacnet/bacdevobjpropref.c index 5a239ff6..d023dca4 100644 --- a/src/bacnet/bacdevobjpropref.c +++ b/src/bacnet/bacdevobjpropref.c @@ -41,6 +41,18 @@ /** @file bacdevobjpropref.c BACnet Application Device Object (Property) * Reference */ +/** + * Encode a property reference for the device object. + * Add an opening tag, encode the property and finally + * add a closing tag as well. + * + * @param apdu Pointer to the buffer to encode to. + * @param tag_number Tag number. + * @param value Pointer to the object property reference, + * used for encoding. + * + * @return Bytes encoded or 0 on failure. + */ int bacapp_encode_context_device_obj_property_ref(uint8_t *apdu, uint8_t tag_number, BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *value) @@ -48,28 +60,39 @@ int bacapp_encode_context_device_obj_property_ref(uint8_t *apdu, int len; int apdu_len = 0; - len = encode_opening_tag(&apdu[apdu_len], tag_number); - apdu_len += len; + if (value) { + len = encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += len; - len = bacapp_encode_device_obj_property_ref(&apdu[apdu_len], value); - apdu_len += len; + len = bacapp_encode_device_obj_property_ref(&apdu[apdu_len], value); + apdu_len += len; - len = encode_closing_tag(&apdu[apdu_len], tag_number); - apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + } return apdu_len; } -/* BACnetDeviceObjectPropertyReference ::= SEQUENCE { - object-identifier [0] BACnetObjectIdentifier, - property-identifier [1] BACnetPropertyIdentifier, - property-array-index [2] Unsigned OPTIONAL, - -- used only with array datatype - -- if omitted with an array then - -- the entire array is referenced - device-identifier [3] BACnetObjectIdentifier OPTIONAL -} -*/ +/** + * Encode a property reference for the device object. + * + * BACnetDeviceObjectPropertyReference ::= SEQUENCE { + * object-identifier [0] BACnetObjectIdentifier, + * property-identifier [1] BACnetPropertyIdentifier, + * property-array-index [2] Unsigned OPTIONAL, + * -- used only with array datatype + * -- if omitted with an array then + * -- the entire array is referenced + * device-identifier [3] BACnetObjectIdentifier OPTIONAL + * } + * + * @param apdu Pointer to the buffer to encode to. + * @param value Pointer to the object property reference, + * used for encoding. + * + * @return Bytes encoded. + */ int bacapp_encode_device_obj_property_ref( uint8_t *apdu, BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *value) { @@ -102,45 +125,56 @@ int bacapp_encode_device_obj_property_ref( return apdu_len; } -/* BACnetDeviceObjectPropertyReference ::= SEQUENCE { - object-identifier [0] BACnetObjectIdentifier, - property-identifier [1] BACnetPropertyIdentifier, - property-array-index [2] Unsigned OPTIONAL, - -- used only with array datatype - -- if omitted with an array then - -- the entire array is referenced - device-identifier [3] BACnetObjectIdentifier OPTIONAL -} -*/ +/** + * Decode a property reference of a device object. + * + * BACnetDeviceObjectPropertyReference ::= SEQUENCE { + * object-identifier [0] BACnetObjectIdentifier, + * property-identifier [1] BACnetPropertyIdentifier, + * property-array-index [2] Unsigned OPTIONAL, + * -- used only with array datatype + * -- if omitted with an array then + * -- the entire array is referenced + * device-identifier [3] BACnetObjectIdentifier OPTIONAL + * } + * + * @param apdu Pointer to the buffer containing the data to decode. + * @param value Pointer to the object property reference, + * used to decode the data into. + * + * @return Bytes decoded or BACNET_STATUS_ERROR on failure. + */ int bacapp_decode_device_obj_property_ref( uint8_t *apdu, BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *value) { int len; int apdu_len = 0; - uint32_t enumValue; + uint32_t enumValue = 0; /* object-identifier [0] BACnetObjectIdentifier */ - if (-1 == - (len = decode_context_object_id(&apdu[apdu_len], 0, - &value->objectIdentifier.type, - &value->objectIdentifier.instance))) { - return -1; + len = + decode_context_object_id(&apdu[apdu_len], 0, + &value->objectIdentifier.type, + &value->objectIdentifier.instance); + if (len == BACNET_STATUS_ERROR) { + return BACNET_STATUS_ERROR; } apdu_len += len; /* property-identifier [1] BACnetPropertyIdentifier */ - if (-1 == - (len = decode_context_enumerated(&apdu[apdu_len], 1, &enumValue))) { - return -1; + len = + decode_context_enumerated(&apdu[apdu_len], 1, &enumValue); + if (len == BACNET_STATUS_ERROR) { + return BACNET_STATUS_ERROR; } value->propertyIdentifier = (BACNET_PROPERTY_ID)enumValue; apdu_len += len; /* property-array-index [2] Unsigned OPTIONAL */ if (decode_is_context_tag(&apdu[apdu_len], 2) && !decode_is_closing_tag(&apdu[apdu_len])) { - if (-1 == - (len = decode_context_unsigned( - &apdu[apdu_len], 2, &value->arrayIndex))) { - return -1; + len = decode_context_unsigned( + &apdu[apdu_len], 2, &value->arrayIndex); + if (len == BACNET_STATUS_ERROR) { + return BACNET_STATUS_ERROR; } apdu_len += len; } else { @@ -164,7 +198,19 @@ int bacapp_decode_device_obj_property_ref( return apdu_len; } -int bacapp_decode_context_device_obj_property_ref(uint8_t *apdu, +/** + * Decode the opening tag and the property reference of + * an device object and check for the closing tag as well. + * + * @param apdu Pointer to the buffer containing the data to decode. + * @param tag_number Tag number + * @param value Pointer to the object property reference, + * used to decode the data into. + * + * @return Bytes decoded or BACNET_STATUS_ERROR on failure. + */ +int bacapp_decode_context_device_obj_property_ref( + uint8_t *apdu, uint8_t tag_number, BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *value) { @@ -175,51 +221,77 @@ int bacapp_decode_context_device_obj_property_ref(uint8_t *apdu, len++; section_length = bacapp_decode_device_obj_property_ref(&apdu[len], value); - - if (section_length == -1) { - len = -1; + if (section_length == BACNET_STATUS_ERROR) { + len = BACNET_STATUS_ERROR; } else { len += section_length; if (decode_is_closing_tag_number(&apdu[len], tag_number)) { len++; } else { - len = -1; + len = BACNET_STATUS_ERROR; } } } else { - len = -1; + len = BACNET_STATUS_ERROR; } return len; } -/* BACnetDeviceObjectReference ::= SEQUENCE { - device-identifier [0] BACnetObjectIdentifier OPTIONAL, - object-identifier [1] BACnetObjectIdentifier -}*/ +/** + * Encode the opening tag and the device object reference + * and finally for the closing tag as well. + * + * BACnetDeviceObjectReference ::= SEQUENCE { + * device-identifier [0] BACnetObjectIdentifier OPTIONAL, + * object-identifier [1] BACnetObjectIdentifier + * } + * + * @param apdu Pointer to the buffer used for encoding. + * @param tag_number Tag number + * @param value Pointer to the device object reference, + * used to encode. + * + * @return Bytes encoded or 0 on failure. + */ int bacapp_encode_context_device_obj_ref( - uint8_t *apdu, uint8_t tag_number, BACNET_DEVICE_OBJECT_REFERENCE *value) + uint8_t *apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_REFERENCE *value) { int len; int apdu_len = 0; - len = encode_opening_tag(&apdu[apdu_len], tag_number); - apdu_len += len; + if (value) { + len = encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += len; - len = bacapp_encode_device_obj_ref(&apdu[apdu_len], value); - apdu_len += len; + len = bacapp_encode_device_obj_ref(&apdu[apdu_len], value); + apdu_len += len; - len = encode_closing_tag(&apdu[apdu_len], tag_number); - apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + } return apdu_len; } -/* BACnetDeviceObjectReference ::= SEQUENCE { - device-identifier [0] BACnetObjectIdentifier OPTIONAL, - object-identifier [1] BACnetObjectIdentifier -}*/ +/** + * Encode the device object reference. + * + * BACnetDeviceObjectReference ::= SEQUENCE { + * device-identifier [0] BACnetObjectIdentifier OPTIONAL, + * object-identifier [1] BACnetObjectIdentifier + * } + * + * @param apdu Pointer to the buffer used for encoding. + * @param value Pointer to the device object reference, + * used to encode. + * + * @return Bytes encoded or 0 on failure. + */ int bacapp_encode_device_obj_ref( - uint8_t *apdu, BACNET_DEVICE_OBJECT_REFERENCE *value) + uint8_t * apdu, + BACNET_DEVICE_OBJECT_REFERENCE * value) { int len; int apdu_len = 0; @@ -234,19 +306,31 @@ int bacapp_encode_device_obj_ref( apdu_len += len; } /* object-identifier [1] BACnetObjectIdentifier */ - len = encode_context_object_id(&apdu[apdu_len], 1, - value->objectIdentifier.type, value->objectIdentifier.instance); + len = + encode_context_object_id(&apdu[apdu_len], 1, + value->objectIdentifier.type, value->objectIdentifier.instance); apdu_len += len; return apdu_len; } -/* BACnetDeviceObjectReference ::= SEQUENCE { - device-identifier [0] BACnetObjectIdentifier OPTIONAL, - object-identifier [1] BACnetObjectIdentifier -}*/ +/** + * Decode the device object reference. + * + * BACnetDeviceObjectReference ::= SEQUENCE { + * device-identifier [0] BACnetObjectIdentifier OPTIONAL, + * object-identifier [1] BACnetObjectIdentifier + * } + * + * @param apdu Pointer to the buffer containing the data to decode. + * @param value Pointer to the object object reference, + * that shall be decoded. + * + * @return Bytes decoded or BACNET_STATUS_ERROR on failure. + */ int bacapp_decode_device_obj_ref( - uint8_t *apdu, BACNET_DEVICE_OBJECT_REFERENCE *value) + uint8_t * apdu, + BACNET_DEVICE_OBJECT_REFERENCE * value) { int len; int apdu_len = 0; @@ -254,11 +338,12 @@ int bacapp_decode_device_obj_ref( /* device-identifier [0] BACnetObjectIdentifier OPTIONAL */ if (decode_is_context_tag(&apdu[apdu_len], 0) && !decode_is_closing_tag(&apdu[apdu_len])) { - if (-1 == - (len = decode_context_object_id(&apdu[apdu_len], 0, - &value->deviceIdentifier.type, - &value->deviceIdentifier.instance))) { - return -1; + len = + decode_context_object_id(&apdu[apdu_len], 0, + &value->deviceIdentifier.type, + &value->deviceIdentifier.instance); + if (len == BACNET_STATUS_ERROR) { + return BACNET_STATUS_ERROR; } apdu_len += len; } else { @@ -266,19 +351,33 @@ int bacapp_decode_device_obj_ref( value->deviceIdentifier.instance = BACNET_NO_DEV_ID; } /* object-identifier [1] BACnetObjectIdentifier */ - if (-1 == - (len = decode_context_object_id(&apdu[apdu_len], 1, - &value->objectIdentifier.type, - &value->objectIdentifier.instance))) { - return -1; + len = + decode_context_object_id(&apdu[apdu_len], 1, + &value->objectIdentifier.type, + &value->objectIdentifier.instance); + if (len == BACNET_STATUS_ERROR) { + return BACNET_STATUS_ERROR; } apdu_len += len; return apdu_len; } +/** + * Decode the context device object reference. Check for + * an opening tag and a closing tag as well. + * + * @param apdu Pointer to the buffer containing the data to decode. + * @param tag_number Tag number + * @param value Pointer to the context device object reference, + * that shall be decoded. + * + * @return Bytes decoded or BACNET_STATUS_ERROR on failure. + */ int bacapp_decode_context_device_obj_ref( - uint8_t *apdu, uint8_t tag_number, BACNET_DEVICE_OBJECT_REFERENCE *value) + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_REFERENCE * value) { int len = 0; int section_length; @@ -286,19 +385,18 @@ int bacapp_decode_context_device_obj_ref( if (decode_is_opening_tag_number(&apdu[len], tag_number)) { len++; section_length = bacapp_decode_device_obj_ref(&apdu[len], value); - - if (section_length == -1) { - len = -1; + if (section_length == BACNET_STATUS_ERROR) { + len = BACNET_STATUS_ERROR; } else { len += section_length; if (decode_is_closing_tag_number(&apdu[len], tag_number)) { len++; } else { - len = -1; + len = BACNET_STATUS_ERROR; } } } else { - len = -1; + len = BACNET_STATUS_ERROR; } return len; } diff --git a/src/bacnet/bacstr.c b/src/bacnet/bacstr.c index f561dbd4..04a13496 100644 --- a/src/bacnet/bacstr.c +++ b/src/bacnet/bacstr.c @@ -48,65 +48,105 @@ #define BACNET_STRING_UTF8_VALIDATION 1 #endif +/** + * Initialize a bit string. + * + * @param bit_string Pointer to the bit string structure. + */ void bitstring_init(BACNET_BIT_STRING *bit_string) { int i; - bit_string->bits_used = 0; - for (i = 0; i < MAX_BITSTRING_BYTES; i++) { - bit_string->value[i] = 0; + if (bit_string) { + bit_string->bits_used = 0; + for (i = 0; i < MAX_BITSTRING_BYTES; i++) { + bit_string->value[i] = 0; + } } } +/** + * Set bits in the bit string. + * + * @param bit_string Pointer to the bit string structure. + * @param bit_number Number of the bit [0..(MAX_BITSTRING_BYTES*8)-1] + * @param value Value 0/1 + */ void bitstring_set_bit( BACNET_BIT_STRING *bit_string, uint8_t bit_number, bool value) { uint8_t byte_number = bit_number / 8; uint8_t bit_mask = 1; - if (byte_number < MAX_BITSTRING_BYTES) { - /* set max bits used */ - if (bit_string->bits_used < (bit_number + 1)) { - bit_string->bits_used = bit_number + 1; - } - bit_mask = bit_mask << (bit_number - (byte_number * 8)); - if (value) { - bit_string->value[byte_number] |= bit_mask; - } else { - bit_string->value[byte_number] &= (~(bit_mask)); + if (bit_string) { + if (byte_number < MAX_BITSTRING_BYTES) { + /* set max bits used */ + if (bit_string->bits_used < (bit_number + 1)) { + bit_string->bits_used = bit_number + 1; + } + bit_mask = bit_mask << (bit_number - (byte_number * 8)); + if (value) { + bit_string->value[byte_number] |= bit_mask; + } else { + bit_string->value[byte_number] &= (~(bit_mask)); + } } } } +/** + * Return the value of a single bit + * out of the bit string. + * + * @param bit_string Pointer to the bit string structure. + * @param bit_number Number of the bit [0..(MAX_BITSTRING_BYTES*8)-1] + * + * @return Value 0/1 + */ bool bitstring_bit(BACNET_BIT_STRING *bit_string, uint8_t bit_number) { bool value = false; uint8_t byte_number = bit_number / 8; uint8_t bit_mask = 1; - if (bit_number < (MAX_BITSTRING_BYTES * 8)) { - bit_mask = bit_mask << (bit_number - (byte_number * 8)); - if (bit_string->value[byte_number] & bit_mask) { - value = true; + if (bit_string) { + if (bit_number < (MAX_BITSTRING_BYTES * 8)) { + bit_mask = bit_mask << (bit_number - (byte_number * 8)); + if (bit_string->value[byte_number] & bit_mask) { + value = true; + } } } return value; } +/** + * Return the number of bits used. + * + * @param bit_string Pointer to the bit string structure. + * + * @return Bits used [0..(MAX_BITSTRING_BYTES*8)-1] + */ uint8_t bitstring_bits_used(BACNET_BIT_STRING *bit_string) { - return bit_string->bits_used; + return (bit_string ? bit_string->bits_used : 0); } -/* returns the number of bytes that a bit string is using */ +/** + * Returns the number of bytes that a bit string is using. + * + * @param bit_string Pointer to the bit string structure. + * + * @return Bytes used [0..MAX_BITSTRING_BYTES] + */ uint8_t bitstring_bytes_used(BACNET_BIT_STRING *bit_string) { uint8_t len = 0; /* return value */ uint8_t used_bytes = 0; uint8_t last_bit = 0; - if (bit_string->bits_used) { + if (bit_string && bit_string->bits_used) { last_bit = bit_string->bits_used - 1; used_bytes = last_bit / 8; /* add one for the first byte */ @@ -117,6 +157,14 @@ uint8_t bitstring_bytes_used(BACNET_BIT_STRING *bit_string) return len; } +/** + * Returns an octed at the given bit position. + * + * @param bit_string Pointer to the bit string structure. + * @param octet_index Byte index of the octed [0..MAX_BITSTRING_BYTES-1] + * + * @return Value of the octed. + */ uint8_t bitstring_octet(BACNET_BIT_STRING *bit_string, uint8_t octet_index) { uint8_t octet = 0; @@ -130,6 +178,15 @@ uint8_t bitstring_octet(BACNET_BIT_STRING *bit_string, uint8_t octet_index) return octet; } +/** + * Set an octed at the given bit position. + * + * @param bit_string Pointer to the bit string structure. + * @param index Byte index of the octed [0..MAX_BITSTRING_BYTES-1] + * @param octet Octet value + * + * @return true on success, false otherwise. + */ bool bitstring_set_octet( BACNET_BIT_STRING *bit_string, uint8_t index, uint8_t octet) { @@ -145,13 +202,25 @@ bool bitstring_set_octet( return status; } +/** + * Write the amount of bits used in the bit + * string structure. + * + * @param bit_string Pointer to the bit string structure. + * @param bytes_used Count of bytes used. + * @param unused_bits Count of remaining unused bits in + * the last byte. + * + * @return true on success or false on error. + */ bool bitstring_set_bits_used( - BACNET_BIT_STRING *bit_string, uint8_t bytes_used, uint8_t unused_bits) + BACNET_BIT_STRING * bit_string, + uint8_t bytes_used, + uint8_t unused_bits) { bool status = false; - if (bit_string) { - /* FIXME: check that bytes_used is at least one? */ + if (bit_string && bytes_used) { bit_string->bits_used = bytes_used * 8; bit_string->bits_used -= unused_bits; status = true; @@ -160,6 +229,13 @@ bool bitstring_set_bits_used( return status; } +/** + * Return the capcity of the bit string. + * + * @param bit_string Pointer to the bit string structure. + * + * @return Capacitiy in bits [0..(MAX_BITSTRING_BYTES*8)] + */ uint8_t bitstring_bits_capacity(BACNET_BIT_STRING *bit_string) { if (bit_string) { @@ -169,6 +245,14 @@ uint8_t bitstring_bits_capacity(BACNET_BIT_STRING *bit_string) } } +/** + * Copy bits from one bit string to another. + * + * @param dest Pointer to the destination bit string structure. + * @param src Pointer to the source bit string structure. + * + * @return true on success, false otherwise. + */ bool bitstring_copy(BACNET_BIT_STRING *dest, BACNET_BIT_STRING *src) { unsigned i; @@ -185,25 +269,34 @@ bool bitstring_copy(BACNET_BIT_STRING *dest, BACNET_BIT_STRING *src) return status; } -/* returns true if the same length and contents */ +/** + * Returns true if the same length and contents. + * + * @param bitstring1 Pointer to the first bit string structure. + * @param bitstring2 Pointer to the second bit string structure. + * + * @return true if the content of both bit strings are + * the same, false otherwise. + */ bool bitstring_same( BACNET_BIT_STRING *bitstring1, BACNET_BIT_STRING *bitstring2) { - int i = 0; /* loop counter */ + int i; /* loop counter */ int bytes_used = 0; uint8_t compare_mask = 0; if (bitstring1 && bitstring2) { + bytes_used = (int)(bitstring1->bits_used / 8); if ((bitstring1->bits_used == bitstring2->bits_used) && - (bitstring1->bits_used / 8 <= MAX_BITSTRING_BYTES)) { - bytes_used = (int)(bitstring1->bits_used / 8); - compare_mask = 0xFF >> (8 - (bitstring1->bits_used % 8)); - + (bytes_used <= MAX_BITSTRING_BYTES)) { + /* compare fully used bytes */ for (i = 0; i < bytes_used; i++) { if (bitstring1->value[i] != bitstring2->value[i]) { return false; } } + /* compare only the relevant bits of last partly used byte */ + compare_mask = 0xFF >> (8 - (bitstring1->bits_used % 8)); if ((bitstring1->value[bytes_used] & compare_mask) != (bitstring2->value[bytes_used] & compare_mask)) { return false; @@ -217,9 +310,18 @@ bool bitstring_same( } #if PRINT_ENABLED -/* converts an null terminated ASCII string to an bitstring. - Expects "1,0,1,0,1,1" or "101011" as the bits - returns true if successfully converted and fits; false if too long */ +/** + * Converts an null terminated ASCII string to an bitstring. + * + * Expects "1,0,1,0,1,1" or "101011" as the bits + * + * @param bit_string Pointer to the bit string structure. + * @param ascii Pointer to a zero terminated string, made up from + * '0' and '1' like "010010011", that shall be + * converted into a bit string. + * + * @return true if successfully converted and fits; false if too long. + */ bool bitstring_init_ascii(BACNET_BIT_STRING *bit_string, const char *ascii) { bool status = false; /* return value */ @@ -262,8 +364,19 @@ bool bitstring_init_ascii(BACNET_BIT_STRING *bit_string, const char *ascii) #endif #define CHARACTER_STRING_CAPACITY (MAX_CHARACTER_STRING_BYTES - 1) -/* returns false if the string exceeds capacity - initialize by using value=NULL */ +/** + * Initialize a BACnet characater string. + * Returns false if the string exceeds capacity. + * Initialize by using value=NULL + * + * @param char_string Pointer to the BACnet string + * @param encoding Encoding that shall be used + * like CHARACTER_UTF8 + * @param value C-string used to initialize the object + * @param length C-String length in characters. + * + * @return true on success, false if the string exceeds capacity. + */ bool characterstring_init(BACNET_CHARACTER_STRING *char_string, uint8_t encoding, const char *value, @@ -299,6 +412,17 @@ bool characterstring_init(BACNET_CHARACTER_STRING *char_string, return status; } +/** + * Initialize a BACnet characater string. + * Returns false if the string exceeds capacity. + * Initialize by using value=NULL + * + * @param char_string Pointer to the BACnet string + * @param value C-string used to initialize the object + * @param tmax C-String length in characters. + * + * @return true/false + */ bool characterstring_init_ansi_safe( BACNET_CHARACTER_STRING *char_string, const char *value, size_t tmax) { @@ -306,6 +430,16 @@ bool characterstring_init_ansi_safe( value ? strnlen(value, tmax) : 0); } +/** + * Initialize a BACnet characater string. + * Returns false if the string exceeds capacity. + * Initialize by using value=NULL + * + * @param char_string Pointer to the BACnet string + * @param value C-string used to initialize the object + * + * @return true/false + */ bool characterstring_init_ansi( BACNET_CHARACTER_STRING *char_string, const char *value) { @@ -313,34 +447,64 @@ bool characterstring_init_ansi( char_string, CHARACTER_ANSI_X34, value, value ? strlen(value) : 0); } +/** + * Copy a character string. + * + * @param dest Pointer to the destination string. + * @param src Pointer to the source string. + * + * @return true/false + */ bool characterstring_copy( BACNET_CHARACTER_STRING *dest, BACNET_CHARACTER_STRING *src) { - return characterstring_init(dest, characterstring_encoding(src), - characterstring_value(src), characterstring_length(src)); -} - -bool characterstring_ansi_copy( - char *dest, size_t dest_max_len, BACNET_CHARACTER_STRING *src) -{ - size_t i; /* counter */ - - if (dest && src && (src->encoding == CHARACTER_ANSI_X34) && - (src->length < dest_max_len)) { - for (i = 0; i < dest_max_len; i++) { - if (i < src->length) { - dest[i] = src->value[i]; - } else { - dest[i] = 0; - } - } - return true; + if (dest && src) { + return characterstring_init(dest, characterstring_encoding(src), + characterstring_value(src), characterstring_length(src)); } return false; } -/* returns true if the character encoding and string contents are the same */ +/** + * Copy a character string into a C-string. + * + * @param dest Pointer to the destination C-string buffer. + * @param dest_max_len Size of the destination C-string buffer. + * @param src Pointer to the source BACnet string. + * + * @return true/false + */ +bool characterstring_ansi_copy( + char *dest, size_t dest_max_len, BACNET_CHARACTER_STRING *src) +{ + size_t i; /* counter */ + + if (dest && src) { + if ((src->encoding == CHARACTER_ANSI_X34) && (src->length < dest_max_len)) { + for (i = 0; i < dest_max_len; i++) { + if (i < src->length) { + dest[i] = src->value[i]; + } else { + dest[i] = 0; + } + } + return true; + } + } + + return false; +} + +/** + * Returns true if the character encoding and string + * contents are the same. + * + * @param dest Pointer to the first string to test. + * @param src Pointer to the second string to test. + * + * @return true if the character encoding and string contents are the same + */ bool characterstring_same( BACNET_CHARACTER_STRING *dest, BACNET_CHARACTER_STRING *src) { @@ -348,12 +512,14 @@ bool characterstring_same( bool same_status = false; if (src && dest) { - if ((src->length == dest->length) && - (src->encoding == dest->encoding)) { + if ((src->encoding == dest->encoding) && + (src->length == dest->length) && + (src->length <= MAX_CHARACTER_STRING_BYTES)) { same_status = true; - for (i = 0; (i < src->length) && same_status; i++) { + for (i = 0; i < src->length; i++) { if (src->value[i] != dest->value[i]) { same_status = false; + break; } } } @@ -370,18 +536,29 @@ bool characterstring_same( return same_status; } +/** + * Returns true if the BACnet string and the C-string + * contents are the same. + * + * @param dest Pointer to the first string to test. + * @param src Pointer to the second string to test. + * + * @return true if the character encoding and string contents are the same + */ bool characterstring_ansi_same(BACNET_CHARACTER_STRING *dest, const char *src) { size_t i; /* counter */ bool same_status = false; if (src && dest) { - if ((dest->length == strlen(src)) && - (dest->encoding == CHARACTER_ANSI_X34)) { + if ((dest->encoding == CHARACTER_ANSI_X34) && + (dest->length == strlen(src)) && + (dest->length <= MAX_CHARACTER_STRING_BYTES)) { same_status = true; - for (i = 0; (i < dest->length) && same_status; i++) { + for (i = 0; i < dest->length; i++) { if (src[i] != dest->value[i]) { same_status = false; + break; } } } @@ -400,7 +577,18 @@ bool characterstring_ansi_same(BACNET_CHARACTER_STRING *dest, const char *src) return same_status; } -/* returns false if the string exceeds capacity */ +/** + * Append some characters to the end of the characterstring + * + * @param char_string Pointer to the BACnet string to which + * the content of the C-string shall be added. + * @param value Pointer to the C-String to be added. + * @param length Count of characters to add. + * + * @param src Pointer to the first string to test. + * + * @return false if the string exceeds capacity. + */ bool characterstring_append( BACNET_CHARACTER_STRING *char_string, const char *value, size_t length) { @@ -420,9 +608,14 @@ bool characterstring_append( return status; } -/* This function sets a new length without changing the value. - If length exceeds capacity, no modification happens and - function returns false. */ +/** + * @brief This function sets a new length without changing + * the value. If length exceeds capacity, no modification + * happens and function returns false. + * + * @return true on success, false if the string exceeds + * capacity. + */ bool characterstring_truncate( BACNET_CHARACTER_STRING *char_string, size_t length) { @@ -587,8 +780,8 @@ bool characterstring_printable(BACNET_CHARACTER_STRING *char_string) #if BACNET_STRING_UTF8_VALIDATION /* Basic UTF-8 manipulation routines - by Jeff Bezanson - placed in the public domain Fall 2005 */ + * by Jeff Bezanson + * placed in the public domain Fall 2005 */ static const char trailingBytesForUTF8[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -601,19 +794,33 @@ static const char trailingBytesForUTF8[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; -/* based on the valid_utf8 routine from the PCRE library by Philip Hazel - length is in bytes, since without knowing whether the string is valid - it's hard to know how many characters there are! */ +/** + * @brief Based on the valid_utf8 routine from the PCRE library by Philip Hazel + * length is in bytes, since without knowing whether the string is valid + * it's hard to know how many characters there are! + * + * @param str Pointer to the character string. + * @param length Count of bytes to check. The count of bytes + * does not necessarily match the count of chars. + * + * @return true if the string is valid, false otherwise. + */ bool utf8_isvalid(const char *str, size_t length) { - const unsigned char *p, *pend = (unsigned char *)str + length; + const unsigned char *p, *pend; unsigned char c; size_t ab; - /* empty string is valid */ + /* An empty string is valid. */ if (length == 0) { return true; } + /* Check pointer. */ + if (!str) { + return false; + } + /* Check characters. */ + pend = (unsigned char *) str + length; for (p = (const unsigned char *)str; p < pend; p++) { c = *p; /* null in middle of string */ @@ -696,26 +903,45 @@ bool utf8_isvalid(const char *str, size_t length) } #endif +/** + * Check if the character string is valid or not. + * + * @param char_string Pointer to the character string. + * + * @return true if the string is valid, false otherwise. + */ bool characterstring_valid(BACNET_CHARACTER_STRING *char_string) { bool valid = false; /* return value */ - if (char_string->encoding < MAX_CHARACTER_STRING_ENCODING) { - if (char_string->encoding == CHARACTER_UTF8) { - if (utf8_isvalid(char_string->value, char_string->length)) { + if (char_string) { + if (char_string->encoding < MAX_CHARACTER_STRING_ENCODING) { + if (char_string->encoding == CHARACTER_UTF8) { + /*UTF8 check*/ + if (utf8_isvalid(char_string->value, char_string->length)) { + valid = true; + } + } else { + /*non UTF8*/ valid = true; } - } else { - valid = true; } } - return valid; } #if BACNET_USE_OCTETSTRING -/* returns false if the string exceeds capacity - initialize by using value=NULL */ +/** + * @brief Initialize an octed string with the given bytes or + * zeros, if NULL for the value is provided. + * + * @param octet_string Pointer to the octed string. + * @param value Pointer to the bytes to be copied to the octed + * string or NULL to initialize the octed string. + * @param length Count of bytes used to fill the octed string. + * + * @return true on success, false if the string exceeds capacity. + */ bool octetstring_init( BACNET_OCTET_STRING *octet_string, uint8_t *value, size_t length) { @@ -746,8 +972,12 @@ bool octetstring_init( } #if PRINT_ENABLED -/* converts an null terminated ASCII Hex string to an octet string. - returns true if successfully converted and fits; false if too long */ +/** @brief Converts an null terminated ASCII Hex string to an octet string. + * + * @param octet_string Pointer to the octed string. + * @param ascii_hex Pointer to the HEx-ASCII string. + * + * @return true if successfully converted and fits; false if too long */ bool octetstring_init_ascii_hex( BACNET_OCTET_STRING *octet_string, const char *ascii_hex) { @@ -756,7 +986,7 @@ bool octetstring_init_ascii_hex( uint8_t value = 0; char hex_pair_string[3] = ""; - if (octet_string) { + if (octet_string && ascii_hex) { octet_string->length = 0; if (ascii_hex[0] == 0) { /* nothing to decode, so success! */ @@ -793,14 +1023,31 @@ bool octetstring_init_ascii_hex( } #endif +/** + * Copy an octed string from source to destination. + * + * @param dest Pointer to the destination octed string. + * @param src Pointer to the source octed string. + * + * @return true on success, false otherwise. + */ bool octetstring_copy(BACNET_OCTET_STRING *dest, BACNET_OCTET_STRING *src) { return octetstring_init( dest, octetstring_value(src), octetstring_length(src)); } -/* returns the number of bytes copied, or 0 if the dest - cannot hold entire octetstring value */ +/** + * @brief Copy bytes from the octed string to a byte buffer. + * + * @param dest Pointer to the byte buffer. + * @param length Bytes to be copied from the + * octed string to the buffer. + * @param src Pointer to the octed string. + * + * @return Returns the number of bytes copied, or 0 if + * the dest cannot hold entire octetstring value. + */ size_t octetstring_copy_value( uint8_t *dest, size_t length, BACNET_OCTET_STRING *src) { @@ -819,7 +1066,15 @@ size_t octetstring_copy_value( return bytes_copied; } -/* returns false if the string exceeds capacity */ +/** + * @brief Append bytes to the end of the octed string. + * + * @param octet_string Pointer to the octed string. + * @param value Pointer to the byte buffer to be appended. + * @param length Bytes to be appended. + * + * @return false if the string exceeds capacity. + */ bool octetstring_append( BACNET_OCTET_STRING *octet_string, uint8_t *value, size_t length) { @@ -839,9 +1094,16 @@ bool octetstring_append( return status; } -/* This function sets a new length without changing the value. - If length exceeds capacity, no modification happens and - function returns false. */ +/** + * @brief This function sets a new length without changing the value. + * If length exceeds capacity, no modification happens and the + * function returns false. + * + * @param octet_string Pointer to the octed string. + * @param length New length the octed string is trucated to. + * + * @return tur on success, false otherwise. + */ bool octetstring_truncate(BACNET_OCTET_STRING *octet_string, size_t length) { bool status = false; /* return value */ @@ -856,7 +1118,14 @@ bool octetstring_truncate(BACNET_OCTET_STRING *octet_string, size_t length) return status; } -/* returns a pointer to the value. */ +/** + * @brief Returns a pointer to the value (data) of + * the given octed string. + * + * @param octet_string Pointer to the octed string. + * + * @return Value as a pointer to a byte array or NULL on error. + */ uint8_t *octetstring_value(BACNET_OCTET_STRING *octet_string) { uint8_t *value = NULL; @@ -868,33 +1137,55 @@ uint8_t *octetstring_value(BACNET_OCTET_STRING *octet_string) return value; } -/* returns the length. */ +/** + * @brief Returns the length in bytes of + * the given octed string. + * + * @param octet_string Pointer to the octed string. + * + * @return Length in bytes. Returns always 0 on error. + */ size_t octetstring_length(BACNET_OCTET_STRING *octet_string) { size_t length = 0; if (octet_string) { - /* FIXME: validate length is within bounds? */ length = octet_string->length; + /* Force length to be within bounds. */ + if (length > MAX_OCTET_STRING_BYTES) { + length = MAX_OCTET_STRING_BYTES; + } } return length; } -/* returns the maximum capacity. */ +/** + * @brief Returns the maximum capacity of an octed string. + * + * @param octet_string Pointer to the octed string. + * + * @return Capacity in bytes. Returns always 0 on error. + */ size_t octetstring_capacity(BACNET_OCTET_STRING *octet_string) { size_t length = 0; if (octet_string) { - /* FIXME: validate length is within bounds? */ length = MAX_OCTET_STRING_BYTES; } return length; } -/* returns true if the same length and contents */ +/** + * @brief Returns true if the same length and contents. + * + * @param octet_string1 Pointer to the first octed string. + * @param octet_string2 Pointer to the second octed string. + * + * @return true if the octed strings are the same, false otherwise. + */ bool octetstring_value_same( BACNET_OCTET_STRING *octet_string1, BACNET_OCTET_STRING *octet_string2) { diff --git a/src/bacnet/basic/object/command.c b/src/bacnet/basic/object/command.c index a84584a9..fc741236 100644 --- a/src/bacnet/basic/object/command.c +++ b/src/bacnet/basic/object/command.c @@ -226,6 +226,9 @@ int cl_decode_apdu(uint8_t *apdu, case BACNET_APPLICATION_TAG_BOOLEAN: len = decode_context_boolean2( &apdu[dec_len], 4, &bcl->Value.type.Boolean); + if (len < 0) { + return BACNET_STATUS_REJECT; + } break; case BACNET_APPLICATION_TAG_UNSIGNED_INT: len = decode_context_unsigned(&apdu[dec_len], 4, &unsigned_value); @@ -256,6 +259,9 @@ int cl_decode_apdu(uint8_t *apdu, case BACNET_APPLICATION_TAG_BIT_STRING: len = decode_context_bitstring( &apdu[dec_len], 4, &bcl->Value.type.Bit_String); + if (len < 0) { + return BACNET_STATUS_REJECT; + } break; case BACNET_APPLICATION_TAG_ENUMERATED: len = decode_context_enumerated( diff --git a/src/bacnet/event.c b/src/bacnet/event.c index 0dbc9ff6..33e918a0 100644 --- a/src/bacnet/event.c +++ b/src/bacnet/event.c @@ -507,7 +507,7 @@ int event_notify_decode_service_request( /* tag 9 - ackRequired */ section_length = decode_context_boolean2(&apdu[len], 9, &data->ackRequired); - if (section_length == -1) { + if (section_length == BACNET_STATUS_ERROR) { return -1; } len += section_length;