From a70ce075071b46da9ca20414ba10aeca67a87cc2 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Thu, 26 Feb 2026 10:48:25 -0600 Subject: [PATCH] Secure BACnet decoders and service requests (#1244) * Secured BACnetAssignedAccessRights decoder. * Secured BACnetPropertyState decoder. * Secured BACnetCredentialAuthenticationFactor decoder. * Secured BACnetEventState change-of-state [1] SEQUENCE decoder. * Secured I-Have-Request service decoder. * Secured Add/Remove ListElement service request decoder. * Secured ConfirmedPrivateTransfer-Request and UnconfirmedPrivateTransfer-Request decoders. * Secured ReadPropertyMultiple-Request and -Ack decoders. * Secured TimeSynchronization-Request decoder. * Secured WritePropertyMultiple service decoders * Secured Trend Log object TL_fetch_property() function. * Secured ReadProperty-Ack decider, * Refactor BACnet time sync recipient handling by moving timesync linked list structure into bacdest where the recipient encoder and decoder already existed. * Secured decoding of BACnetPropertyState. * Secured decoding in the LifeSafetyOperation-Request service. * Secured BACnetAuthenticationFactor decoding in the Credential Data Input object. * Fixed WriteProperty decoder to avoid read buffer overrun. Improved WriteProperty error reporting by adding specific reject codes during decoding similar to WritePropertyMultiple. Deduplicated the WriteProperty handling of abort, reject and error codes. * Added BACNET_STACK_DEPRECATED_DISABLE guards around all of the deprecated decoding functions to ensure they are not used except intentionally for legacy code bases. * Changed version to 1.5.0.rc5 for security fix tracking in branch. --- CHANGELOG.md | 14 + src/bacnet/access_rule.c | 2 + src/bacnet/assigned_access_rights.c | 122 ++-- src/bacnet/assigned_access_rights.h | 7 +- src/bacnet/authentication_factor.c | 4 + src/bacnet/authentication_factor_format.c | 4 + src/bacnet/bacaddr.c | 4 + src/bacnet/bacapp.c | 8 + src/bacnet/bacdcode.c | 70 ++- src/bacnet/bacdcode.h | 2 +- src/bacnet/bacdest.c | 47 ++ src/bacnet/bacdest.h | 13 + src/bacnet/bacdevobjpropref.c | 8 + src/bacnet/bacpropstates.c | 112 +++- src/bacnet/bacpropstates.h | 9 + .../basic/object/credential_data_input.c | 77 ++- src/bacnet/basic/object/trendlog.c | 56 +- src/bacnet/basic/service/h_rp_a.c | 9 +- src/bacnet/basic/service/h_rpm.c | 8 +- src/bacnet/basic/service/h_rpm_a.c | 85 +-- src/bacnet/basic/service/h_ts.c | 20 +- src/bacnet/basic/service/h_wp.c | 117 ++-- src/bacnet/basic/service/h_wpm.c | 26 +- src/bacnet/credential_authentication_factor.c | 130 +++-- src/bacnet/credential_authentication_factor.h | 5 +- src/bacnet/datetime.c | 2 + src/bacnet/event.c | 5 +- src/bacnet/getevent.c | 1 - src/bacnet/getevent.h | 1 - src/bacnet/ihave.c | 128 +++-- src/bacnet/list_element.c | 118 ++-- src/bacnet/lso.c | 113 ++-- src/bacnet/ptransfer.c | 539 ++++++++++++------ src/bacnet/rpm.c | 123 ++-- src/bacnet/timestamp.c | 4 + src/bacnet/timesync.c | 256 +++------ src/bacnet/timesync.h | 21 +- src/bacnet/version.h | 2 +- src/bacnet/wp.c | 54 +- src/bacnet/wpm.c | 427 ++++++++------ src/bacnet/wpm.h | 3 + test/bacnet/ptransfer/src/main.c | 60 +- test/bacnet/timesync/src/main.c | 143 ++--- test/bacnet/wpm/src/main.c | 46 +- 44 files changed, 1841 insertions(+), 1164 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4372655..190d1e06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,17 @@ The git repositories are hosted at the following sites: ### Security +* Secured various BACnet type and service request decoders by replacing + deprecated decoders. Secured ReadProperty-Ack decoder, Trend Log object + TL_fetch_property() function, WritePropertyMultiple service decoders, + TimeSynchronization-Request decoder, ReadPropertyMultiple-Request and + ReadPropertyMultiple-Ack decoders, ConfirmedPrivateTransfer-Request + and UnconfirmedPrivateTransfer-Request decoders, Add/Remove ListElement + service request decoder, I-Have-Request service decoder, BACnetEventState + change-of-state decoder, BACnetCredentialAuthenticationFactor decoder, + BACnetPropertyState decoder, BACnetAssignedAccessRights decoder, + LifeSafetyOperation-Request service decoder, BACnetAuthenticationFactor + decoder in the Crediential Data Input object. (#1244) * Secured BVLC decoder by replacing deprecated primitive decoder usage. (#1241) * Secured decoding length underflow in wp_decode_service_request() and bacnet_action_command_decode() which had similar issue. (#1231) @@ -41,6 +52,9 @@ The git repositories are hosted at the following sites: ### Added +* Added BACNET_STACK_DEPRECATED_DISABLE guards around all of the deprecated + decoding functions to ensure they are not used except intentionally for + legacy code bases. (#1244) * Added Who-Is-Router-To-Network process in basic Notification Class when recipient address router MAC address is unknown. (#1243) * Add initialization and unpacking functions for BACnet Character diff --git a/src/bacnet/access_rule.c b/src/bacnet/access_rule.c index bc522698..0e18a43e 100644 --- a/src/bacnet/access_rule.c +++ b/src/bacnet/access_rule.c @@ -167,6 +167,7 @@ int bacnet_access_rule_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode the BACnetAccessRule * @param apdu Pointer to the buffer for decoding. @@ -178,6 +179,7 @@ int bacapp_decode_access_rule(const uint8_t *apdu, BACNET_ACCESS_RULE *data) { return bacnet_access_rule_decode(apdu, MAX_APDU, data); } +#endif /** * @brief Parse a string into a BACnetAccessRule value diff --git a/src/bacnet/assigned_access_rights.c b/src/bacnet/assigned_access_rights.c index 7b3742f8..ef6fc85c 100644 --- a/src/bacnet/assigned_access_rights.c +++ b/src/bacnet/assigned_access_rights.c @@ -9,6 +9,18 @@ #include "bacnet/assigned_access_rights.h" #include "bacnet/bacdcode.h" +/** + * @brief Encode a BACnetAssignedAccessRights structure into an APDU buffer. + * + * BACnetAssignedAccessRights ::= SEQUENCE { + * assigned-access-rights [0]BACnetDeviceObjectReference, + * enable [1] Boolean + * } + * + * @param apdu [in] The APDU buffer, or NULL for length + * @param aar [in] The BACnetAssignedAccessRights structure to encode + * @return number of bytes encoded, or negative on error + */ int bacapp_encode_assigned_access_rights( uint8_t *apdu, const BACNET_ASSIGNED_ACCESS_RIGHTS *aar) { @@ -33,6 +45,16 @@ int bacapp_encode_assigned_access_rights( return apdu_len; } +/** + * @brief Encode a BACnetAssignedAccessRights structure into an APDU buffer, + * with context tag. + * + * @param apdu [in] The APDU buffer, or NULL for length + * @param tag [in] The context tag number to use for the opening and closing + * tags + * @param aar [in] The BACnetAssignedAccessRights structure to encode + * @return number of bytes encoded, or negative on error + */ int bacapp_encode_context_assigned_access_rights( uint8_t *apdu, uint8_t tag, const BACNET_ASSIGNED_ACCESS_RIGHTS *aar) { @@ -51,60 +73,86 @@ int bacapp_encode_context_assigned_access_rights( return apdu_len; } +/** + * @brief Decode a BACnetAssignedAccessRights structure from an APDU buffer. + * + * @param apdu [in] The APDU buffer to decode + * @param apdu_size [in] The size of the APDU buffer + * @param aar [out] The BACnetAssignedAccessRights structure to fill with + * decoded values + * @return number of bytes decoded, or negative on error + */ int bacapp_decode_assigned_access_rights( - const uint8_t *apdu, BACNET_ASSIGNED_ACCESS_RIGHTS *aar) + const uint8_t *apdu, size_t apdu_size, BACNET_ASSIGNED_ACCESS_RIGHTS *data) { - int len; - int apdu_len = 0; + int len = 0, apdu_len = 0; + bool enable = false; + BACNET_DEVICE_OBJECT_REFERENCE *reference = NULL; - if (decode_is_context_tag(&apdu[apdu_len], 0)) { - len = bacapp_decode_context_device_obj_ref( - &apdu[apdu_len], 0, &aar->assigned_access_rights); - if (len < 0) { - return -1; - } else { - apdu_len += len; - } - } else { - return -1; + if (data) { + reference = &data->assigned_access_rights; } - - if (decode_is_context_tag(&apdu[apdu_len], 1)) { - len = decode_context_boolean2(&apdu[apdu_len], 1, &aar->enable); - if (len < 0) { - return -1; - } else { - apdu_len += len; - } + len = bacnet_device_object_reference_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, reference); + if (len <= 0) { + return BACNET_STATUS_ERROR; } else { - return -1; + apdu_len += len; + /* reference already decoded in place */ + } + len = bacnet_boolean_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &enable); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } else { + apdu_len += len; + if (data) { + data->enable = enable; + } } return apdu_len; } +/** + * @brief Decode a BACnetAssignedAccessRights structure from an APDU buffer, + * with context tag. + * + * @param apdu [in] The APDU buffer to decode + * @param apdu_size [in] The size of the APDU buffer + * @param tag [in] The context tag number to use for the opening and closing + * tags + * @param aar [out] The BACnetAssignedAccessRights structure to fill with + * decoded values + * @return number of bytes decoded, or negative on error + */ int bacapp_decode_context_assigned_access_rights( - const uint8_t *apdu, uint8_t tag, BACNET_ASSIGNED_ACCESS_RIGHTS *aar) + const uint8_t *apdu, + size_t apdu_size, + uint8_t tag, + BACNET_ASSIGNED_ACCESS_RIGHTS *data) { - int len = 0; - int section_length; + int len = 0, apdu_len = 0; - if (decode_is_opening_tag_number(&apdu[len], tag)) { - len++; - section_length = bacapp_decode_assigned_access_rights(&apdu[len], aar); - - if (section_length == -1) { - len = -1; + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, tag, &len)) { + apdu_len += len; + len = bacapp_decode_assigned_access_rights( + &apdu[apdu_len], apdu_size - apdu_len, data); + if (len < 0) { + apdu_len = len; } else { - len += section_length; - if (decode_is_closing_tag_number(&apdu[len], tag)) { - len++; + apdu_len += len; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, tag, &len)) { + apdu_len += len; } else { - len = -1; + apdu_len = BACNET_STATUS_ERROR; } } } else { - len = -1; + apdu_len = BACNET_STATUS_ERROR; } - return len; + + return apdu_len; } diff --git a/src/bacnet/assigned_access_rights.h b/src/bacnet/assigned_access_rights.h index 5d46cc99..433f3ef9 100644 --- a/src/bacnet/assigned_access_rights.h +++ b/src/bacnet/assigned_access_rights.h @@ -33,10 +33,13 @@ int bacapp_encode_context_assigned_access_rights( uint8_t *apdu, uint8_t tag, const BACNET_ASSIGNED_ACCESS_RIGHTS *aar); BACNET_STACK_EXPORT int bacapp_decode_assigned_access_rights( - const uint8_t *apdu, BACNET_ASSIGNED_ACCESS_RIGHTS *aar); + const uint8_t *apdu, size_t apdu_size, BACNET_ASSIGNED_ACCESS_RIGHTS *aar); BACNET_STACK_EXPORT int bacapp_decode_context_assigned_access_rights( - const uint8_t *apdu, uint8_t tag, BACNET_ASSIGNED_ACCESS_RIGHTS *aar); + const uint8_t *apdu, + size_t apdu_size, + uint8_t tag, + BACNET_ASSIGNED_ACCESS_RIGHTS *aar); #ifdef __cplusplus } diff --git a/src/bacnet/authentication_factor.c b/src/bacnet/authentication_factor.c index 32b0a055..d40d2e08 100644 --- a/src/bacnet/authentication_factor.c +++ b/src/bacnet/authentication_factor.c @@ -131,6 +131,7 @@ int bacnet_authentication_factor_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode the BACnetAuthenticationFactor complex data * @param apdu Pointer to the buffer for decoding. @@ -143,6 +144,7 @@ int bacapp_decode_authentication_factor( { return bacnet_authentication_factor_decode(apdu, MAX_APDU, af); } +#endif /** * @brief Decode the context tagged BACnetAuthenticationFactor complex data @@ -186,6 +188,7 @@ int bacnet_authentication_factor_context_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode the context tagged BACnetAuthenticationFactor complex data * @param apdu Pointer to the buffer for decoding. @@ -201,3 +204,4 @@ int bacapp_decode_context_authentication_factor( { return bacnet_authentication_factor_context_decode(apdu, MAX_APDU, tag, af); } +#endif diff --git a/src/bacnet/authentication_factor_format.c b/src/bacnet/authentication_factor_format.c index cb13ae81..3e15b1fd 100644 --- a/src/bacnet/authentication_factor_format.c +++ b/src/bacnet/authentication_factor_format.c @@ -146,6 +146,7 @@ int bacnet_authentication_factor_format_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode a BACnetAuthenticationFactorFormat property value * @param apdu Pointer to the buffer for decoding. @@ -160,6 +161,7 @@ int bacapp_decode_authentication_factor_format( { return bacnet_authentication_factor_format_decode(apdu, MAX_APDU, data); } +#endif /** * @brief Decode a context tagged BACnetAuthenticationFactorFormat property @@ -201,6 +203,7 @@ int bacnet_authentication_factor_format_context_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode a context tagged BACnetAuthenticationFactorFormat property * value @@ -219,3 +222,4 @@ int bacapp_decode_context_authentication_factor_format( return bacnet_authentication_factor_format_context_decode( apdu, MAX_APDU, tag, data); } +#endif diff --git a/src/bacnet/bacaddr.c b/src/bacnet/bacaddr.c index 397abaee..a48ca86c 100644 --- a/src/bacnet/bacaddr.c +++ b/src/bacnet/bacaddr.c @@ -556,6 +556,7 @@ int encode_bacnet_address(uint8_t *apdu, const BACNET_ADDRESS *destination) return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode a BACnetAddress and returns the number of apdu bytes consumed. * @param apdu Receive buffer @@ -567,6 +568,7 @@ int decode_bacnet_address(const uint8_t *apdu, BACNET_ADDRESS *value) { return bacnet_address_decode(apdu, MAX_APDU, value); } +#endif /** * @brief Encode a context encoded BACnetAddress @@ -592,6 +594,7 @@ int encode_context_bacnet_address( return len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /* * @brief Decodes a context tagged BACnetAddress value from APDU buffer * @param apdu - the APDU buffer @@ -605,6 +608,7 @@ int decode_context_bacnet_address( { return bacnet_address_context_decode(apdu, MAX_APDU, tag_number, value); } +#endif /** * @brief Encode a BACnetVMACEntry value diff --git a/src/bacnet/bacapp.c b/src/bacnet/bacapp.c index d73746f1..bd41e9cd 100644 --- a/src/bacnet/bacapp.c +++ b/src/bacnet/bacapp.c @@ -564,6 +564,7 @@ int bacapp_data_decode( return len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode the data and store it into value. * @param apdu Receive buffer @@ -584,6 +585,7 @@ int bacapp_decode_data( return bacapp_data_decode( apdu, MAX_APDU, tag_data_type, len_value_type, value); } +#endif /** * @brief Decode the BACnet Application Data @@ -694,6 +696,7 @@ bool bacapp_decode_application_data_safe( return ret; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode the data to determine the data length * @param apdu Pointer to the received data. @@ -708,7 +711,9 @@ int bacapp_decode_data_len( (void)apdu; return bacnet_application_data_length(tag_number, len_value_type); } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Determine the BACnet Application Data number of APDU bytes consumed * @param apdu - buffer of data to be decoded @@ -735,6 +740,7 @@ int bacapp_decode_application_data_len(const uint8_t *apdu, unsigned apdu_size) return len; } +#endif /** * @brief Encode a BACnet Context tagged data, @@ -853,6 +859,7 @@ int bacapp_encode_context_data_value( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Lookup an application tag for specific context tagged data * @param property - object property identifier @@ -875,6 +882,7 @@ bacapp_context_tag_type(BACNET_PROPERTY_ID property, uint8_t tag_number) return (BACNET_APPLICATION_TAG)tag; } +#endif /** * @brief Encode a BACnet Context tagged data diff --git a/src/bacnet/bacdcode.c b/src/bacnet/bacdcode.c index 2d76a39d..a9dc46d4 100644 --- a/src/bacnet/bacdcode.c +++ b/src/bacnet/bacdcode.c @@ -361,6 +361,7 @@ int encode_closing_tag(uint8_t *apdu, uint8_t tag_number) return len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * Decode a BACnet tag and returns the number of bytes consumed. * @@ -390,6 +391,7 @@ int decode_tag_number(const uint8_t *apdu, uint8_t *tag_number) return len; } +#endif /** * @brief Decode the BACnet Tag Number @@ -577,6 +579,7 @@ int bacnet_tag_decode(const uint8_t *apdu, uint32_t apdu_size, BACNET_TAG *tag) return len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Returns if at the given pointer a * opening tag has been found. @@ -588,6 +591,7 @@ bool decode_is_opening_tag(const uint8_t *apdu) { return (bool)((apdu[0] & 0x07) == 6); } +#endif /** * @brief Returns true if an opening tag has been found. @@ -610,6 +614,7 @@ bool bacnet_is_opening_tag(const uint8_t *apdu, uint32_t apdu_size) return tag; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Returns if at the given pointer a * closing tag has been found. @@ -621,6 +626,7 @@ bool decode_is_closing_tag(const uint8_t *apdu) { return (bool)((apdu[0] & 0x07) == 7); } +#endif /** * @brief Returns true if a closing tag has been found. @@ -664,6 +670,7 @@ bool bacnet_is_context_specific(const uint8_t *apdu, uint32_t apdu_size) return tag; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes the tag number and the value, * that the APDU pointer is addressing. @@ -725,7 +732,9 @@ int decode_tag_number_and_value( return len; } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode the BACnet Tag Number and Value * as defined in clause 20.2.1.3.2 Constructed Data @@ -759,6 +768,7 @@ int bacnet_tag_number_and_value_decode( return len; } +#endif /** * @brief Determine the data length from the application tag number @@ -889,6 +899,7 @@ int bacnet_enclosed_data_length(const uint8_t *apdu, size_t apdu_size) return total_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Returns true if the tag is context specific * and matches, as defined in clause 20.2.1.3.2 Constructed @@ -908,7 +919,9 @@ bool decode_is_context_tag(const uint8_t *apdu, uint8_t tag_number) return (bool)(IS_CONTEXT_SPECIFIC(*apdu) && (my_tag_number == tag_number)); } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Returns true if the tag is context specific * and matches, as defined in clause 20.2.1.3.2 Constructed @@ -931,6 +944,7 @@ bool decode_is_context_tag_with_length( return (bool)(IS_CONTEXT_SPECIFIC(*apdu) && (my_tag_number == tag_number)); } +#endif /** * @brief Returns true if the tag is context specific @@ -974,6 +988,7 @@ bool bacnet_is_context_tag_number( return match; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Returns true if the tag does match and it * is an opening tag as well. @@ -993,6 +1008,7 @@ bool decode_is_opening_tag_number(const uint8_t *apdu, uint8_t tag_number) return (bool)(IS_OPENING_TAG(apdu[0]) && (my_tag_number == tag_number)); } +#endif /** * @brief Returns true if the tag number matches is an opening tag. @@ -1033,6 +1049,7 @@ bool bacnet_is_opening_tag_number( return match; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Returns true if the tag does match and it * is an closing tag as well. @@ -1051,6 +1068,7 @@ bool decode_is_closing_tag_number(const uint8_t *apdu, uint8_t tag_number) decode_tag_number(apdu, &my_tag_number); return (bool)(IS_CLOSING_TAG(apdu[0]) && (my_tag_number == tag_number)); } +#endif /** * @brief Returns true if the tag number matches is an closing tag. @@ -1141,11 +1159,12 @@ int encode_context_boolean( return len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode an boolean value. * @param apdu Pointer to the encode buffer. * @return true/false - * @deprecated Use bacnet_boolean_context_decode() instead + * @deprecated Use bacnet_boolean_context_value_decode() instead */ bool decode_context_boolean(const uint8_t *apdu) { @@ -1157,7 +1176,9 @@ bool decode_context_boolean(const uint8_t *apdu) return boolean_value; } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode an boolean value in the context of a tag. * @@ -1187,6 +1208,7 @@ int decode_context_boolean2( return len; } +#endif /** * @brief Check the length value and return the boolean meaning. @@ -1526,6 +1548,7 @@ uint8_t bacnet_byte_reverse_bits(uint8_t in_byte) return out_byte; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode a bit string value. * (From clause 20.2.10 Encoding of a Bit String Value.) @@ -1570,6 +1593,7 @@ int decode_bitstring( return len; } +#endif /** * @brief Decodes from bytes into a BACnet bit string value. @@ -1735,6 +1759,7 @@ int bacnet_bitstring_context_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode a bit string value in the given context. * (From clause 20.2.10 Encoding of a Bit String Value.) @@ -1764,6 +1789,7 @@ int decode_context_bitstring( return len; } +#endif /** * @brief Encode the BACnet Bit String Value @@ -2131,6 +2157,7 @@ bool bacnet_object_id_same( return status; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode the BACnet Object Identifier Value when context encoded * as defined in clause 20.2.14 Encoding of an Object Identifier Value @@ -2161,6 +2188,7 @@ int decode_context_object_id( return len; } +#endif /** * @brief Encode the BACnet Object Identifier Value @@ -2654,6 +2682,7 @@ int bacnet_octet_string_decode( return len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode the BACnet Octet String Value * from clause 20.2.8 Encoding of an Octet String Value @@ -2673,7 +2702,9 @@ int decode_octet_string( return bacnet_octet_string_decode(apdu, apdu_len_max, len_value, value); } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Octet String context encoding * from clause 20.2.8 Encoding of an Octet String Value @@ -2712,6 +2743,7 @@ int decode_context_octet_string( return len; } +#endif /** * @brief Encode an application tagged BACnet Octet String Value @@ -2829,6 +2861,7 @@ int bacnet_octet_string_context_decode( } #endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Encode the BACnet Character String Value * from 20.2.9 Encoding of a Character String Value @@ -2867,6 +2900,7 @@ uint32_t encode_bacnet_character_string_safe( return apdu_len; } +#endif /** * @brief Encode the BACnet Character String Value @@ -3000,6 +3034,7 @@ int bacnet_character_string_decode( return len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Character String value * from clause 20.2.9 Encoding of a Character String Value @@ -3019,6 +3054,7 @@ int decode_character_string( return bacnet_character_string_decode(apdu, apdu_size, len_value, value); } +#endif /** * @brief Encode an application tagged BACnet Character String value @@ -3135,6 +3171,7 @@ int bacnet_character_string_context_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Character String value * from clause 20.2.9 Encoding of a Character String Value @@ -3162,6 +3199,7 @@ int decode_context_character_string( return len; } +#endif /** * @brief Encode the BACnet Character String Value @@ -3737,6 +3775,7 @@ int bacnet_unsigned_application_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Unsigned value * from clause 20.2.4 Encoding of an Unsigned Integer Value @@ -3760,7 +3799,9 @@ int decode_unsigned( return bacnet_unsigned_decode(apdu, apdu_size, len_value, value); } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Unsigned value * from clause 20.2.4 Encoding of an Unsigned Integer Value @@ -3791,6 +3832,7 @@ int decode_context_unsigned( return len; } +#endif /** * @brief Encode the BACnet Unsigned value @@ -4061,6 +4103,7 @@ int bacnet_enumerated_context_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Enumerated value * from clause 20.2.11 Encoding of an Enumerated Value @@ -4087,6 +4130,7 @@ int decode_context_enumerated( return len; } +#endif /** * @brief Encode the BACnet Enumerated Value @@ -4315,6 +4359,7 @@ int bacnet_signed_application_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Signed Integer * from clause 20.2.5 Encoding of an Signed Integer Value @@ -4334,7 +4379,9 @@ int decode_signed(const uint8_t *apdu, uint32_t len_value, int32_t *value) return bacnet_signed_decode(apdu, apdu_size, len_value, value); } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Signed Integer * from clause 20.2.5 Encoding of an Signed Integer Value @@ -4361,6 +4408,7 @@ int decode_context_signed( return len; } +#endif /** * @brief Decodes from bytes into a BACnet Signed Integer @@ -4640,6 +4688,7 @@ int bacnet_real_application_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode a context tagged single precision floating value. * From clause 20.2.6 Encoding of a Real Number Value @@ -4668,6 +4717,7 @@ int decode_context_real( return len; } +#endif #if BACNET_USE_DOUBLE /** @@ -4874,6 +4924,7 @@ int bacnet_double_application_decode( * if wrong tag number or malformed * @deprecated use bacnet_double_context_decode() instead */ +#if defined(BACNET_STACK_DEPRECATED_DISABLE) int decode_context_double( const uint8_t *apdu, uint8_t tag_number, double *double_value) { @@ -4889,6 +4940,7 @@ int decode_context_double( return len; } #endif +#endif /** * @brief Encode a Time Value @@ -5117,6 +5169,7 @@ int bacnet_time_application_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Time Value * from clause 20.2.13 Encoding of a Time Value @@ -5135,7 +5188,9 @@ int decode_bacnet_time(const uint8_t *apdu, BACNET_TIME *value) return bacnet_time_decode(apdu, apdu_size, len_value, value); } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Time Value * from clause 20.2.13 Encoding of a Time Value @@ -5162,7 +5217,9 @@ int decode_bacnet_time_safe( return decode_bacnet_time(apdu, btime); } } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Time Value application encoded * from clause 20.2.13 Encoding of a Time Value @@ -5190,7 +5247,9 @@ int decode_application_time(const uint8_t *apdu, BACNET_TIME *btime) return len; } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Time Value context encoded * from clause 20.2.13 Encoding of a Time Value @@ -5216,6 +5275,7 @@ int decode_context_bacnet_time( return len; } +#endif /** * @brief Encode a Date value @@ -5309,6 +5369,7 @@ int encode_context_date( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Date Value * From clause 20.2.12 Encoding of a Date Value @@ -5329,7 +5390,9 @@ int decode_date(const uint8_t *apdu, BACNET_DATE *bdate) return 4; } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Date Value * From clause 20.2.12 Encoding of a Date Value @@ -5355,6 +5418,7 @@ int decode_date_safe( return decode_date(apdu, bdate); } } +#endif /** * @brief Decodes from bytes into a BACnet Date Value @@ -5504,6 +5568,7 @@ int bacnet_date_application_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Date Value application encoded * From clause 20.2.12 Encoding of a Date Value @@ -5528,7 +5593,9 @@ int decode_application_date(const uint8_t *apdu, BACNET_DATE *value) return len; } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes from bytes into a BACnet Date Value context encoded * From clause 20.2.12 Encoding of a Date Value @@ -5556,6 +5623,7 @@ int decode_context_date( return len; } +#endif /** * @brief Encode a context tagged BACnetTimerStateChangeValue diff --git a/src/bacnet/bacdcode.h b/src/bacnet/bacdcode.h index 96610c72..674b1419 100644 --- a/src/bacnet/bacdcode.h +++ b/src/bacnet/bacdcode.h @@ -211,7 +211,7 @@ bool decode_boolean(uint32_t len_value); BACNET_STACK_EXPORT int encode_context_boolean( uint8_t *apdu, uint8_t tag_number, bool boolean_value); -BACNET_STACK_DEPRECATED("Use bacnet_boolean_context_decode() instead") +BACNET_STACK_DEPRECATED("Use bacnet_boolean_context_value_decode() instead") BACNET_STACK_EXPORT bool decode_context_boolean(const uint8_t *apdu); BACNET_STACK_EXPORT diff --git a/src/bacnet/bacdest.c b/src/bacnet/bacdest.c index 5f14025b..182957d9 100644 --- a/src/bacnet/bacdest.c +++ b/src/bacnet/bacdest.c @@ -558,6 +558,53 @@ int bacnet_recipient_encode(uint8_t *apdu, const BACNET_RECIPIENT *recipient) return apdu_len; } +/** + * @brief Encode a list of BACnetRecipient complex data types + * @param apdu Pointer to the buffer for encoding. + * @param list_head Pointer to the head of the linked list of BACnetRecipient + * @return bytes encoded or zero if nothing is encoded + */ +int bacnet_recipient_list_encode( + uint8_t *apdu, BACNET_RECIPIENT_LIST *list_head) +{ + int apdu_len = 0, len = 0; + BACNET_RECIPIENT_LIST *list_entry; + + if (!list_head) { + /* encoded nothing */ + return 0; + } + /* how big? */ + list_entry = list_head; + while (list_entry != NULL) { + len = bacnet_recipient_encode(apdu, &list_entry->recipient); + apdu_len += len; + if (apdu) { + apdu += len; + } + list_entry = list_entry->next; + } + + return apdu_len; +} + +/** + * @brief Convert an array of BACnetRecipient to linked list + * @param array pointer to element zero of the array + * @param size number of elements in the array + */ +void bacnet_recipient_list_link_array(BACNET_RECIPIENT_LIST *array, size_t size) +{ + size_t i = 0; + + for (i = 0; i < size; i++) { + if (i > 0) { + array[i - 1].next = &array[i]; + } + array[i].next = NULL; + } +} + /** * @brief Encode a BACnetRecipient complex data type * @param apdu - the APDU buffer diff --git a/src/bacnet/bacdest.h b/src/bacnet/bacdest.h index ac52ae0d..894a6ea0 100644 --- a/src/bacnet/bacdest.h +++ b/src/bacnet/bacdest.h @@ -36,6 +36,13 @@ typedef struct BACnet_Recipient { } type; } BACNET_RECIPIENT; +struct BACnet_Recipient_List; +typedef struct BACnet_Recipient_List { + BACNET_RECIPIENT recipient; + /* simple linked list */ + struct BACnet_Recipient_List *next; +} BACNET_RECIPIENT_LIST; + typedef struct BACnet_Destination { /** * BACnetDestination ::= SEQUENCE { @@ -111,6 +118,12 @@ bool bacnet_recipient_device_valid(const BACNET_RECIPIENT *recipient); BACNET_STACK_EXPORT int bacnet_recipient_encode(uint8_t *apdu, const BACNET_RECIPIENT *recipient); BACNET_STACK_EXPORT +int bacnet_recipient_list_encode( + uint8_t *apdu, BACNET_RECIPIENT_LIST *list_head); +BACNET_STACK_EXPORT +void bacnet_recipient_list_link_array( + BACNET_RECIPIENT_LIST *array, size_t size); +BACNET_STACK_EXPORT int bacnet_recipient_context_encode( uint8_t *apdu, uint8_t tag_number, const BACNET_RECIPIENT *recipient); BACNET_STACK_EXPORT diff --git a/src/bacnet/bacdevobjpropref.c b/src/bacnet/bacdevobjpropref.c index d5386de8..76f1062f 100644 --- a/src/bacnet/bacdevobjpropref.c +++ b/src/bacnet/bacdevobjpropref.c @@ -311,6 +311,7 @@ bool bacnet_device_object_property_reference_copy( return status; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * Decode a property reference of a device object. * @@ -337,7 +338,9 @@ int bacapp_decode_device_obj_property_ref( return bacnet_device_object_property_reference_decode( apdu, MAX_APDU, value); } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * Decode the opening tag and the property reference of * an device object and check for the closing tag as well. @@ -359,6 +362,7 @@ int bacapp_decode_context_device_obj_property_ref( return bacnet_device_object_property_reference_context_decode( apdu, MAX_APDU, tag_number, value); } +#endif /** * Encode the opening tag and the device object reference @@ -599,6 +603,7 @@ bool bacnet_device_object_reference_copy( return status; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * Decode the device object reference. * @@ -619,7 +624,9 @@ int bacapp_decode_device_obj_ref( { return bacnet_device_object_reference_decode(apdu, MAX_APDU, value); } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * Decode the context device object reference. Check for * an opening tag and a closing tag as well. @@ -640,6 +647,7 @@ int bacapp_decode_context_device_obj_ref( return bacnet_device_object_reference_context_decode( apdu, MAX_APDU, tag_number, value); } +#endif /** * @brief Encode a BACnetObjectPropertyReference diff --git a/src/bacnet/bacpropstates.c b/src/bacnet/bacpropstates.c index a873ca63..db4a6895 100644 --- a/src/bacnet/bacpropstates.c +++ b/src/bacnet/bacpropstates.c @@ -49,6 +49,7 @@ int bacapp_property_state_decode( BACNET_TAG tag = { 0 }; uint32_t enum_value = 0; int32_t integer_value = 0; + bool boolean_value = false; int apdu_len = 0; int len = 0; @@ -68,8 +69,13 @@ int bacapp_property_state_decode( return BACNET_STATUS_ERROR; } if (value) { - value->state.booleanValue = decode_context_boolean(&apdu[apdu_len]); - apdu_len++; + len = bacnet_boolean_context_value_decode( + &apdu[apdu_len], apdu_size - apdu_len, &boolean_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + value->state.booleanValue = boolean_value; + apdu_len += len; } } else if (tag.number == PROP_STATE_INTEGER_VALUE) { len = bacnet_signed_decode( @@ -300,37 +306,103 @@ int bacapp_property_state_decode( return apdu_len; } +/** + * @brief Decodes BACnetPropertyState from bytes into a data structure, with + * opening and closing tags + * @param apdu - buffer of data to be decoded + * @param apdu_size - number of bytes in the buffer + * @param tag_number - expected tag number for the opening and closing tags + * @param value - decoded value, if decoded + * @return number of bytes decoded, or #BACNET_STATUS_ERROR (-1) if malformed + */ +int bacapp_property_state_context_decode( + const uint8_t *apdu, + uint32_t apdu_size, + uint8_t tag_number, + BACNET_PROPERTY_STATE *value) +{ + int len = 0, apdu_len = 0; + + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + len = bacapp_property_state_decode( + &apdu[apdu_len], apdu_size - apdu_len, value); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +#if defined(BACNET_STACK_DEPRECATED_DISABLE) +/** + * @brief Decodes BACnetPropertyState from bytes into a data structure, with + * opening and closing tags + * @param apdu - buffer of data to be decoded + * @param value - decoded value, if decoded + * @return number of bytes decoded, or #BACNET_STATUS_ERROR (-1) if malformed + * @deprecated This function is deprecated. + * Use bacapp_property_state_decode() instead. + */ int bacapp_decode_property_state( const uint8_t *apdu, BACNET_PROPERTY_STATE *value) { return bacapp_property_state_decode(apdu, MAX_APDU, value); } +#endif +#if defined(BACNET_STACK_DEPRECATED_DISABLE) +/** + * @brief Decodes BACnetPropertyState from bytes into a data structure, with + * opening and closing tags + * @param apdu - buffer of data to be decoded + * @param tag_number - expected tag number for the opening and closing tags + * @param value - decoded value, if decoded + * @return number of bytes decoded, or #BACNET_STATUS_ERROR (-1) if malformed + * @deprecated This function is deprecated. + * Use bacapp_property_state_context_decode() instead. + */ int bacapp_decode_context_property_state( const uint8_t *apdu, uint8_t tag_number, BACNET_PROPERTY_STATE *value) { - int len = 0; - int section_length; + int len = 0, apdu_len = 0; + int apdu_size = MAX_APDU; - if (decode_is_opening_tag_number(&apdu[len], tag_number)) { - len++; - section_length = bacapp_decode_property_state(&apdu[len], value); - - if (section_length == -1) { - len = -1; - } else { - len += section_length; - if (decode_is_closing_tag_number(&apdu[len], tag_number)) { - len++; - } else { - len = -1; - } - } + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) { + apdu_len += len; } else { - len = -1; + return BACNET_STATUS_ERROR; } - return len; + len = bacapp_property_state_decode( + &apdu[apdu_len], apdu_size - apdu_len, value); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + + return apdu_len; } +#endif /** * @brief Encode the BACnetPropertyState diff --git a/src/bacnet/bacpropstates.h b/src/bacnet/bacpropstates.h index 81b4ebff..7e2f7e40 100644 --- a/src/bacnet/bacpropstates.h +++ b/src/bacnet/bacpropstates.h @@ -91,10 +91,19 @@ BACNET_STACK_EXPORT int bacapp_property_state_decode( const uint8_t *apdu, uint32_t apdu_size, BACNET_PROPERTY_STATE *value); +BACNET_STACK_EXPORT +int bacapp_property_state_context_decode( + const uint8_t *apdu, + uint32_t apdu_size, + uint8_t tag_number, + BACNET_PROPERTY_STATE *value); + +BACNET_STACK_DEPRECATED("Use bacapp_property_state_decode() instead") BACNET_STACK_EXPORT int bacapp_decode_property_state( const uint8_t *apdu, BACNET_PROPERTY_STATE *value); +BACNET_STACK_DEPRECATED("Use bacapp_property_state_context_decode() instead") BACNET_STACK_EXPORT int bacapp_decode_context_property_state( const uint8_t *apdu, uint8_t tag_number, BACNET_PROPERTY_STATE *value); diff --git a/src/bacnet/basic/object/credential_data_input.c b/src/bacnet/basic/object/credential_data_input.c index 661c37ed..3db6fb60 100644 --- a/src/bacnet/basic/object/credential_data_input.c +++ b/src/bacnet/basic/object/credential_data_input.c @@ -88,6 +88,9 @@ void Credential_Data_Input_Writable_Property_List( } } +/** + * @brief Initialize the Credential Data Input object data structures. + */ void Credential_Data_Input_Init(void) { unsigned i; @@ -112,9 +115,11 @@ void Credential_Data_Input_Init(void) return; } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need validate that the */ -/* given instance exists */ +/** + * @brief Check if the given object instance is valid. + * @param object_instance - object-instance number of the object + * @return true if the instance is valid, false otherwise + */ bool Credential_Data_Input_Valid_Instance(uint32_t object_instance) { if (object_instance < MAX_CREDENTIAL_DATA_INPUTS) { @@ -124,24 +129,32 @@ bool Credential_Data_Input_Valid_Instance(uint32_t object_instance) return false; } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then count how many you have */ +/** + * @brief Get the total count of Credential Data Input objects. + * @return The count of Credential Data Input objects. + */ unsigned Credential_Data_Input_Count(void) { return MAX_CREDENTIAL_DATA_INPUTS; } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the instance */ -/* that correlates to the correct index */ +/** + * @brief Convert an object instance number to an index in the object array. + * @param object_instance - object-instance number of the object + * @return The index corresponding to the given object instance, or + * MAX_CREDENTIAL_DATA_INPUTS if the instance is invalid. + */ uint32_t Credential_Data_Input_Index_To_Instance(unsigned index) { return index; } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the index */ -/* that correlates to the correct instance number */ +/** + * @brief Convert an object instance number to an index in the object array. + * @param object_instance - object-instance number of the object + * @return The index corresponding to the given object instance, or + * MAX_CREDENTIAL_DATA_INPUTS if the instance is invalid. + */ unsigned Credential_Data_Input_Instance_To_Index(uint32_t object_instance) { unsigned index = MAX_CREDENTIAL_DATA_INPUTS; @@ -153,7 +166,13 @@ unsigned Credential_Data_Input_Instance_To_Index(uint32_t object_instance) return index; } -/* note: the object name must be unique within this device */ +/** + * @brief Get the object name for a given Credential Data Input object instance. + * @param object_instance - object-instance number of the object + * @param object_name - pointer to a BACNET_CHARACTER_STRING to receive the + * object name + * @return true if the object name was successfully retrieved, false otherwise + */ bool Credential_Data_Input_Object_Name( uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) { @@ -170,6 +189,11 @@ bool Credential_Data_Input_Object_Name( return status; } +/** + * @brief Check if the Credential Data Input object instance is out of service. + * @param object_instance - object-instance number of the object + * @return true if the object instance is out of service, false otherwise + */ bool Credential_Data_Input_Out_Of_Service(uint32_t instance) { unsigned index = 0; @@ -183,6 +207,13 @@ bool Credential_Data_Input_Out_Of_Service(uint32_t instance) return oos_flag; } +/** + * @brief Set the out of service flag for a given Credential Data Input object + * instance. + * @param instance - object-instance number of the object + * @param oos_flag - true to set the object instance as out of service, false to + * set it as in service + */ void Credential_Data_Input_Out_Of_Service_Set(uint32_t instance, bool oos_flag) { unsigned index = 0; @@ -193,7 +224,14 @@ void Credential_Data_Input_Out_Of_Service_Set(uint32_t instance, bool oos_flag) } } -/* return apdu len, or BACNET_STATUS_ERROR on error */ +/** + * @brief Read a property value for a given Credential Data Input object + * instance. + * @param rpdata - pointer to a BACNET_READ_PROPERTY_DATA structure containing + * the read request data + * @return The number of bytes encoded in the response, or BACNET_STATUS_ERROR + * on error. + */ int Credential_Data_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { int len = 0; @@ -299,7 +337,13 @@ int Credential_Data_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) return apdu_len; } -/* returns true if successful */ +/** + * @brief Write a property value for a given Credential Data Input object + * instance. + * @param wp_data - pointer to a BACNET_WRITE_PROPERTY_DATA structure containing + * the write request data + * @return true if the property was successfully written, false otherwise + */ bool Credential_Data_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) { bool status = false; /* return value */ @@ -324,8 +368,9 @@ bool Credential_Data_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) if (Credential_Data_Input_Out_Of_Service( wp_data->object_instance)) { BACNET_AUTHENTICATION_FACTOR tmp; - len = bacapp_decode_authentication_factor( - wp_data->application_data, &tmp); + len = bacnet_authentication_factor_decode( + wp_data->application_data, wp_data->application_data_len, + &tmp); if (len > 0) { memcpy( &cdi_descr[object_index].present_value, &tmp, diff --git a/src/bacnet/basic/object/trendlog.c b/src/bacnet/basic/object/trendlog.c index 62628bf3..f3f15d54 100644 --- a/src/bacnet/basic/object/trendlog.c +++ b/src/bacnet/basic/object/trendlog.c @@ -1640,10 +1640,9 @@ static void TL_fetch_property(int iLog) uint8_t ucCount; TL_LOG_INFO *CurrentLog; TL_DATA_REC TempRec; - uint8_t tag_number = 0; - uint32_t len_value_type = 0; BACNET_BIT_STRING TempBits; BACNET_UNSIGNED_INTEGER unsigned_value = 0; + BACNET_TAG tag = { 0 }; CurrentLog = &LogInfo[iLog]; @@ -1662,40 +1661,46 @@ static void TL_fetch_property(int iLog) TempRec.ucRecType = TL_TYPE_ERROR; } else { /* Decode data returned and see if we can fit it into the log */ - iLen = - decode_tag_number_and_value(ValueBuf, &tag_number, &len_value_type); - switch (tag_number) { + iLen = bacnet_tag_decode(ValueBuf, sizeof(ValueBuf), &tag); + } + if ((iLen > 0) && (tag.application)) { + switch (tag.number) { case BACNET_APPLICATION_TAG_NULL: TempRec.ucRecType = TL_TYPE_NULL; break; case BACNET_APPLICATION_TAG_BOOLEAN: TempRec.ucRecType = TL_TYPE_BOOL; - TempRec.Datum.ucBoolean = decode_boolean(len_value_type); + TempRec.Datum.ucBoolean = decode_boolean(tag.len_value_type); break; case BACNET_APPLICATION_TAG_UNSIGNED_INT: TempRec.ucRecType = TL_TYPE_UNSIGN; - decode_unsigned( - &ValueBuf[iLen], len_value_type, &unsigned_value); + bacnet_unsigned_decode( + &ValueBuf[iLen], sizeof(ValueBuf) - iLen, + tag.len_value_type, &unsigned_value); TempRec.Datum.ulUValue = unsigned_value; break; case BACNET_APPLICATION_TAG_SIGNED_INT: TempRec.ucRecType = TL_TYPE_SIGN; - decode_signed( - &ValueBuf[iLen], len_value_type, &TempRec.Datum.lSValue); + bacnet_signed_decode( + &ValueBuf[iLen], sizeof(ValueBuf) - iLen, + tag.len_value_type, &TempRec.Datum.lSValue); break; case BACNET_APPLICATION_TAG_REAL: TempRec.ucRecType = TL_TYPE_REAL; - decode_real_safe( - &ValueBuf[iLen], len_value_type, &TempRec.Datum.fReal); + bacnet_real_decode( + &ValueBuf[iLen], sizeof(ValueBuf) - iLen, + tag.len_value_type, &TempRec.Datum.fReal); break; case BACNET_APPLICATION_TAG_BIT_STRING: TempRec.ucRecType = TL_TYPE_BITS; - decode_bitstring(&ValueBuf[iLen], len_value_type, &TempBits); + bacnet_bitstring_decode( + &ValueBuf[iLen], sizeof(ValueBuf) - iLen, + tag.len_value_type, &TempBits); /* We truncate any bitstrings at 32 bits to conserve space */ if (bitstring_bits_used(&TempBits) < 32) { /* Store the bytes used and the bits free @@ -1722,8 +1727,9 @@ static void TL_fetch_property(int iLog) case BACNET_APPLICATION_TAG_ENUMERATED: TempRec.ucRecType = TL_TYPE_ENUM; - decode_enumerated( - &ValueBuf[iLen], len_value_type, &TempRec.Datum.ulEnum); + bacnet_enumerated_decode( + &ValueBuf[iLen], sizeof(ValueBuf) - iLen, + tag.len_value_type, &TempRec.Datum.ulEnum); break; default: @@ -1734,12 +1740,22 @@ static void TL_fetch_property(int iLog) break; } /* Finally insert the status flags into the record */ - iLen = decode_tag_number_and_value( - StatusBuf, &tag_number, &len_value_type); - decode_bitstring(&StatusBuf[iLen], len_value_type, &TempBits); - TempRec.ucStatus = 128 | bitstring_octet(&TempBits, 0); + iLen = bacnet_bitstring_application_decode( + StatusBuf, sizeof(StatusBuf), &TempBits); + if (iLen > 0) { + TempRec.ucStatus = 128 | bitstring_octet(&TempBits, 0); + } else { + /* If we couldn't decode the status flags, just set the bit to + * say they are not present */ + TempRec.ucStatus = 0; + } + } else { + /* We couldn't decode the value, so we will just log an error with + * the error code for the value read attempt */ + TempRec.Datum.Error.usClass = ERROR_CLASS_SERVICES; + TempRec.Datum.Error.usCode = ERROR_CODE_OTHER; + TempRec.ucRecType = TL_TYPE_ERROR; } - Logs[iLog][CurrentLog->iIndex++] = TempRec; if (CurrentLog->iIndex >= TL_MAX_ENTRIES) { CurrentLog->iIndex = 0; diff --git a/src/bacnet/basic/service/h_rp_a.c b/src/bacnet/basic/service/h_rp_a.c index 61b63943..b5c2b1f3 100644 --- a/src/bacnet/basic/service/h_rp_a.c +++ b/src/bacnet/basic/service/h_rp_a.c @@ -153,7 +153,7 @@ int rp_ack_fully_decode_service_request( BACNET_PROPERTY_REFERENCE *rp1_property; /* single property */ BACNET_APPLICATION_DATA_VALUE *value, *old_value; uint8_t *vdata; - int vlen, len; + int vlen, len, tag_len; decoded_len = rp_ack_decode_service_request(apdu, apdu_len, &rp1data); if (decoded_len > 0) { @@ -204,10 +204,9 @@ int rp_ack_fully_decode_service_request( vlen -= len; vdata += len; /* If unexpected closing tag here: */ - if (vlen && decode_is_closing_tag_number(vdata, 3)) { - decoded_len++; - vlen--; - vdata++; + if (vlen && + bacnet_is_closing_tag_number(vdata, vlen, 3, &tag_len)) { + decoded_len += tag_len; break; } else { if (len == 0) { diff --git a/src/bacnet/basic/service/h_rpm.c b/src/bacnet/basic/service/h_rpm.c index 59d9d162..f06322ac 100644 --- a/src/bacnet/basic/service/h_rpm.c +++ b/src/bacnet/basic/service/h_rpm.c @@ -219,6 +219,7 @@ void handler_read_property_multiple( BACNET_RPM_DATA rpmdata; int apdu_len = 0; int npdu_len = 0; + int tag_len = 0; int error = 0; if (service_data) { @@ -451,11 +452,12 @@ void handler_read_property_multiple( } } - if (decode_is_closing_tag_number( - &service_request[decode_len], 1)) { + if (bacnet_is_closing_tag_number( + &service_request[decode_len], + service_len - decode_len, 1, &tag_len)) { /* Reached end of property list so cap the result list */ - decode_len++; + decode_len += tag_len; len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]); copy_len = memcopy( &Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], diff --git a/src/bacnet/basic/service/h_rpm_a.c b/src/bacnet/basic/service/h_rpm_a.c index 73ee3fd7..1b39e3ad 100644 --- a/src/bacnet/basic/service/h_rpm_a.c +++ b/src/bacnet/basic/service/h_rpm_a.c @@ -45,8 +45,8 @@ int rpm_ack_decode_service_request( uint32_t error_value = 0; /* decoded error value */ int len = 0; /* number of bytes returned from decoding */ uint8_t tag_number = 0; /* decoded tag number */ - uint32_t len_value = 0; /* decoded length value */ int data_len = 0; /* data blob length */ + int tag_len = 0; /* length of the tag portion of the data */ BACNET_READ_ACCESS_DATA *rpm_object; BACNET_READ_ACCESS_DATA *old_rpm_object; BACNET_PROPERTY_REFERENCE *rpm_property; @@ -95,23 +95,25 @@ int rpm_ack_decode_service_request( decoded_len += len; apdu_len -= len; apdu += len; - if (apdu_len && decode_is_opening_tag_number(apdu, 4)) { + if (apdu_len && + bacnet_is_opening_tag_number(apdu, apdu_len, 4, &tag_len)) { data_len = bacnet_enclosed_data_length(apdu, apdu_len); /* propertyValue */ - decoded_len++; - apdu_len--; - apdu++; + decoded_len += tag_len; + apdu_len -= tag_len; + apdu += tag_len; value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE)); rpm_property->value = value; - if (apdu_len && decode_is_closing_tag_number(apdu, 4)) { + if (apdu_len && + bacnet_is_closing_tag_number(apdu, apdu_len, 4, &tag_len)) { /* Special case for an empty array or list */ if (value) { bacapp_value_list_init(value, 1); value->tag = BACNET_APPLICATION_TAG_EMPTYLIST; } - decoded_len++; - apdu_len--; - apdu++; + decoded_len += tag_len; + apdu_len -= tag_len; + apdu += tag_len; } else { /* one or more (array or list) elements to decode */ while (value && (apdu_len > 0)) { @@ -142,10 +144,12 @@ int rpm_ack_decode_service_request( decoded_len += len; apdu_len -= len; apdu += len; - if (apdu_len && decode_is_closing_tag_number(apdu, 4)) { - decoded_len++; - apdu_len--; - apdu++; + if (apdu_len && + bacnet_is_closing_tag_number( + apdu, apdu_len, 4, &tag_len)) { + decoded_len += tag_len; + apdu_len -= tag_len; + apdu += tag_len; break; } else if (len > 0) { old_value = value; @@ -164,39 +168,48 @@ int rpm_ack_decode_service_request( } } } - } else if (apdu_len && decode_is_opening_tag_number(apdu, 5)) { + } else if ( + apdu_len && + bacnet_is_opening_tag_number(apdu, apdu_len, 5, &tag_len)) { /* propertyAccessError */ - decoded_len++; - apdu_len--; - apdu++; + decoded_len += tag_len; + apdu_len -= tag_len; + apdu += tag_len; /* decode the class and code sequence */ - len = - decode_tag_number_and_value(apdu, &tag_number, &len_value); + len = bacnet_enumerated_application_decode( + apdu, apdu_len, &error_value); + if (len <= 0) { + PERROR("RPM Ack: unable to decode error class!\n"); + return BACNET_STATUS_ERROR; + } decoded_len += len; apdu_len -= len; apdu += len; - /* FIXME: we could validate that the tag is enumerated... */ - len = decode_enumerated(apdu, len_value, &error_value); rpm_property->error.error_class = (BACNET_ERROR_CLASS)error_value; + len = bacnet_enumerated_application_decode( + apdu, apdu_len, &error_value); + if (len <= 0) { + PERROR("RPM Ack: unable to decode error code!\n"); + return BACNET_STATUS_ERROR; + } decoded_len += len; apdu_len -= len; apdu += len; - len = - decode_tag_number_and_value(apdu, &tag_number, &len_value); - decoded_len += len; - apdu_len -= len; - apdu += len; - /* FIXME: we could validate that the tag is enumerated... */ - len = decode_enumerated(apdu, len_value, &error_value); rpm_property->error.error_code = (BACNET_ERROR_CODE)error_value; - decoded_len += len; - apdu_len -= len; - apdu += len; - if (apdu_len && decode_is_closing_tag_number(apdu, 5)) { - decoded_len++; - apdu_len--; - apdu++; + if (apdu_len) { + if (bacnet_is_closing_tag_number( + apdu, apdu_len, 5, &tag_len)) { + decoded_len += tag_len; + apdu_len -= tag_len; + apdu += tag_len; + } else { + PERROR( + "RPM Ack: expected closing tag for error code, got " + "%d\n", + tag_number); + return BACNET_STATUS_ERROR; + } } } old_rpm_property = rpm_property; @@ -204,7 +217,7 @@ int rpm_ack_decode_service_request( old_rpm_property->next = rpm_property; } len = rpm_decode_object_end(apdu, apdu_len); - if (len) { + if (len > 0) { decoded_len += len; apdu_len -= len; apdu += len; diff --git a/src/bacnet/basic/service/h_ts.c b/src/bacnet/basic/service/h_ts.c index 5bd23b20..07e76871 100644 --- a/src/bacnet/basic/service/h_ts.c +++ b/src/bacnet/basic/service/h_ts.c @@ -13,6 +13,7 @@ /* BACnet Stack API */ #include "bacnet/datetime.h" #include "bacnet/bacdcode.h" +#include "bacnet/bacdest.h" #include "bacnet/timesync.h" #include "bacnet/bacaddr.h" #include "bacnet/npdu.h" @@ -146,9 +147,10 @@ static void handler_timesync_send(BACNET_DATE_TIME *current_date_time) unsigned index = 0; for (index = 0; index < MAX_TIME_SYNC_RECIPIENTS; index++) { - if (Time_Sync_Recipients[index].tag == 1) { + if (Time_Sync_Recipients[index].recipient.tag == + BACNET_RECIPIENT_TAG_ADDRESS) { Send_TimeSync_Remote( - &Time_Sync_Recipients[index].type.address, + &Time_Sync_Recipients[index].recipient.type.address, ¤t_date_time->date, ¤t_date_time->time); } } @@ -221,8 +223,10 @@ bool handler_timesync_recipient_address_set( bool status = false; if (address && (index < MAX_TIME_SYNC_RECIPIENTS)) { - Time_Sync_Recipients[index].tag = 1; - bacnet_address_copy(&Time_Sync_Recipients[index].type.address, address); + Time_Sync_Recipients[index].recipient.tag = + BACNET_RECIPIENT_TAG_ADDRESS; + bacnet_address_copy( + &Time_Sync_Recipients[index].recipient.type.address, address); status = true; } @@ -256,12 +260,10 @@ void handler_timesync_init(void) unsigned i = 0; /* connect linked list */ - for (; i < (MAX_TIME_SYNC_RECIPIENTS - 1); i++) { - Time_Sync_Recipients[i].next = &Time_Sync_Recipients[i + 1]; - Time_Sync_Recipients[i + 1].next = NULL; - } + bacnet_recipient_list_link_array( + Time_Sync_Recipients, MAX_TIME_SYNC_RECIPIENTS); for (i = 0; i < MAX_TIME_SYNC_RECIPIENTS; i++) { - Time_Sync_Recipients[i].tag = 0xFF; + Time_Sync_Recipients[i].recipient.tag = BACNET_RECIPIENT_TAG_MAX; } } #endif diff --git a/src/bacnet/basic/service/h_wp.c b/src/bacnet/basic/service/h_wp.c index f740ea86..fbe019dd 100644 --- a/src/bacnet/basic/service/h_wp.c +++ b/src/bacnet/basic/service/h_wp.c @@ -88,9 +88,8 @@ void handler_write_property( BACNET_ADDRESS *src, BACNET_CONFIRMED_SERVICE_DATA *service_data) { - BACNET_WRITE_PROPERTY_DATA wp_data; + BACNET_WRITE_PROPERTY_DATA wp_data = { 0 }; int len = 0; - bool bcontinue = true; bool success = false; int pdu_len = 0; BACNET_NPDU_DATA npdu_data; @@ -104,75 +103,69 @@ void handler_write_property( &Handler_Transmit_Buffer[0], src, &my_address, &npdu_data); debug_print("WP: Received Request!\n"); if (service_len == 0) { - len = reject_encode_apdu( - &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, - REJECT_REASON_MISSING_REQUIRED_PARAMETER); + wp_data.error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + len = BACNET_STATUS_REJECT; debug_print("WP: Missing Required Parameter. Sending Reject!\n"); - bcontinue = false; } else if (service_data->segmented_message) { - len = abort_encode_apdu( - &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, - ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); + wp_data.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + len = BACNET_STATUS_ABORT; debug_print("WP: Segmented message. Sending Abort!\n"); - bcontinue = false; - } - if (bcontinue) { + } else { /* decode the service request only */ + wp_data.error_class = ERROR_CLASS_PROPERTY; + wp_data.error_code = ERROR_CODE_SUCCESS; len = wp_decode_service_request(service_request, service_len, &wp_data); - if (len > 0) { - debug_fprintf( - stderr, - "WP: type=%lu instance=%lu property=%lu priority=%lu " - "index=%ld\n", - (unsigned long)wp_data.object_type, - (unsigned long)wp_data.object_instance, - (unsigned long)wp_data.object_property, - (unsigned long)wp_data.priority, (long)wp_data.array_index); + } + if (len > 0) { + debug_fprintf( + stderr, + "WP: type=%lu instance=%lu property=%lu priority=%lu " + "index=%ld\n", + (unsigned long)wp_data.object_type, + (unsigned long)wp_data.object_instance, + (unsigned long)wp_data.object_property, + (unsigned long)wp_data.priority, (long)wp_data.array_index); + } else { + debug_print("WP: Unable to decode Request!\n"); + } + if (len > 0) { + success = handler_write_property_relinquish_bypass(&wp_data); + if (success) { + /* this object property is not commandable, + and therefore, not able to be relinquished, + so it "shall not be changed, and + the write shall be considered successful." */ } else { - debug_print("WP: Unable to decode Request!\n"); + if (write_property_bacnet_array_valid(&wp_data)) { + success = Device_Write_Property(&wp_data); + } } - /* bad decoding or something we didn't understand - send an abort */ - if (len <= 0) { + if (success) { + len = encode_simple_ack( + &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, + SERVICE_CONFIRMED_WRITE_PROPERTY); + debug_print("WP: Sending Simple Ack!\n"); + } else { + len = BACNET_STATUS_ERROR; + } + } + if (len < 0) { + if (abort_valid_error_code(wp_data.error_code)) { len = abort_encode_apdu( &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, - ABORT_REASON_OTHER, true); - debug_print("WP: Bad Encoding. Sending Abort!\n"); - bcontinue = false; - } - if (bcontinue) { - success = handler_write_property_relinquish_bypass(&wp_data); - if (success) { - /* this object property is not commandable, - and therefore, not able to be relinquished, - so it "shall not be changed, and - the write shall be considered successful." */ - } else { - if (write_property_bacnet_array_valid(&wp_data)) { - success = Device_Write_Property(&wp_data); - } - } - if (success) { - len = encode_simple_ack( - &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, - SERVICE_CONFIRMED_WRITE_PROPERTY); - debug_print("WP: Sending Simple Ack!\n"); - } else if (abort_valid_error_code(wp_data.error_code)) { - len = abort_encode_apdu( - &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, - abort_convert_error_code(wp_data.error_code), true); - debug_print("WP: Sending Abort!\n"); - } else if (reject_valid_error_code(wp_data.error_code)) { - len = reject_encode_apdu( - &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, - reject_convert_error_code(wp_data.error_code)); - debug_print("WP: Sending Reject!\n"); - } else { - len = bacerror_encode_apdu( - &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, - SERVICE_CONFIRMED_WRITE_PROPERTY, wp_data.error_class, - wp_data.error_code); - debug_print("WP: Sending Error!\n"); - } + abort_convert_error_code(wp_data.error_code), true); + debug_print("WP: Sending Abort!\n"); + } else if (reject_valid_error_code(wp_data.error_code)) { + len = reject_encode_apdu( + &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, + reject_convert_error_code(wp_data.error_code)); + debug_print("WP: Sending Reject!\n"); + } else { + len = bacerror_encode_apdu( + &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, + SERVICE_CONFIRMED_WRITE_PROPERTY, wp_data.error_class, + wp_data.error_code); + debug_print("WP: Sending Error!\n"); } } /* Send PDU */ diff --git a/src/bacnet/basic/service/h_wpm.c b/src/bacnet/basic/service/h_wpm.c index c40ac968..a4dba509 100644 --- a/src/bacnet/basic/service/h_wpm.c +++ b/src/bacnet/basic/service/h_wpm.c @@ -45,6 +45,7 @@ static int write_property_multiple_decode( { int len = 0; int offset = 0; + int tag_len = 0; uint8_t tag_number = 0; /* decode service request */ @@ -54,7 +55,9 @@ static int write_property_multiple_decode( if (len > 0) { offset += len; /* Opening tag 1 - List of Properties */ - if (decode_is_opening_tag_number(&apdu[offset++], 1)) { + if (bacnet_is_opening_tag_number( + &apdu[offset], apdu_len - offset, 1, &tag_len)) { + offset += tag_len; do { /* decode a 'Property Identifier': (3) an optional 'Property Array Index' @@ -85,9 +88,10 @@ static int write_property_multiple_decode( return len; } /* Closing tag 1 - List of Properties */ - if (decode_is_closing_tag_number(&apdu[offset], 1)) { + if (bacnet_is_closing_tag_number( + &apdu[offset], apdu_len - offset, 1, &len)) { tag_number = 1; - offset++; + offset += len; } else { /* it was not tag 1, decode next Property Identifier */ tag_number = 0; @@ -101,7 +105,7 @@ static int write_property_multiple_decode( } } while (offset < apdu_len); - return len; + return offset; } /** Handler for a WriteProperty Service request. @@ -166,21 +170,21 @@ void handler_write_property_multiple( debug_print("WPM: Sending Ack!\n"); } else { /* handle any errors */ - if (len == BACNET_STATUS_ABORT) { + if (abort_valid_error_code(wp_data.error_code)) { apdu_len = abort_encode_apdu( &Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, abort_convert_error_code(wp_data.error_code), true); debug_print("WPM: Sending Abort!\n"); - } else if (len == BACNET_STATUS_ERROR) { - apdu_len = wpm_error_ack_encode_apdu( - &Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, - &wp_data); - debug_print("WPM: Sending Error!\n"); - } else if (len == BACNET_STATUS_REJECT) { + } else if (reject_valid_error_code(wp_data.error_code)) { apdu_len = reject_encode_apdu( &Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, reject_convert_error_code(wp_data.error_code)); debug_print("WPM: Sending Reject!\n"); + } else { + apdu_len = wpm_error_ack_encode_apdu( + &Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, + &wp_data); + debug_print("WPM: Sending Error!\n"); } } pdu_len = npdu_len + apdu_len; diff --git a/src/bacnet/credential_authentication_factor.c b/src/bacnet/credential_authentication_factor.c index 686de618..5636d9f1 100644 --- a/src/bacnet/credential_authentication_factor.c +++ b/src/bacnet/credential_authentication_factor.c @@ -10,6 +10,20 @@ #include "bacnet/credential_authentication_factor.h" #include "bacnet/bacdcode.h" +/** + * @brief Encode a BACnetCredentialAuthenticationFactor structure into an APDU + * buffer. + * + * BACnetCredentialAuthenticationFactor ::= SEQUENCE { + * disable[0] BACnetAccessAuthenticationFactorDisable, + * authentication-factor[1] BACnetAuthenticationFactor + * } + * + * @param apdu [in] The APDU buffer, or NULL for length + * @param factor [in] The BACnetCredentialAuthenticationFactor structure to + * encode + * @return number of bytes encoded, or negative on error + */ int bacapp_encode_credential_authentication_factor( uint8_t *apdu, const BACNET_CREDENTIAL_AUTHENTICATION_FACTOR *factor) { @@ -34,6 +48,16 @@ int bacapp_encode_credential_authentication_factor( return apdu_len; } +/** + * @brief Encode a BACnetCredentialAuthenticationFactor structure into an APDU + * buffer, with context tag. + * + * @param apdu [in] The APDU buffer, or NULL for length + * @param tag [in] The context tag number + * @param factor [in] The BACnetCredentialAuthenticationFactor structure to + * encode + * @return number of bytes encoded, or negative on error + */ int bacapp_encode_context_credential_authentication_factor( uint8_t *apdu, uint8_t tag, @@ -55,68 +79,90 @@ int bacapp_encode_context_credential_authentication_factor( return apdu_len; } +/** + * @brief Decode a BACnetCredentialAuthenticationFactor structure from an APDU + * buffer. + * + * BACnetCredentialAuthenticationFactor ::= SEQUENCE { + * disable[0] BACnetAccessAuthenticationFactorDisable, + * authentication-factor[1] BACnetAuthenticationFactor + * } + * + * @param apdu [in] The APDU buffer + * @param apdu_size [in] The size of the APDU buffer + * @param factor [out] The BACnetCredentialAuthenticationFactor structure to + * decode + * @return number of bytes decoded, or negative on error + */ int bacapp_decode_credential_authentication_factor( - const uint8_t *apdu, BACNET_CREDENTIAL_AUTHENTICATION_FACTOR *factor) + const uint8_t *apdu, + size_t apdu_size, + BACNET_CREDENTIAL_AUTHENTICATION_FACTOR *factor) { int len; int apdu_len = 0; uint32_t disable = factor->disable; - if (decode_is_context_tag(&apdu[apdu_len], 0)) { - len = decode_context_enumerated(&apdu[apdu_len], 0, &disable); - if (len < 0) { - return -1; - } else if (disable < UINT16_MAX) { - apdu_len += len; - factor->disable = - (BACNET_ACCESS_AUTHENTICATION_FACTOR_DISABLE)disable; - } else { - return -1; - } + /* disable[0] BACnetAccessAuthenticationFactorDisable */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &disable); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } else if (disable < UINT16_MAX) { + apdu_len += len; + factor->disable = (BACNET_ACCESS_AUTHENTICATION_FACTOR_DISABLE)disable; } else { - return -1; + return BACNET_STATUS_ERROR; } - - if (decode_is_context_tag(&apdu[apdu_len], 1)) { - len = bacapp_decode_context_authentication_factor( - &apdu[apdu_len], 1, &factor->authentication_factor); - if (len < 0) { - return -1; - } else { - apdu_len += len; - } + len = bacnet_authentication_factor_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, + &factor->authentication_factor); + if (len <= 0) { + return BACNET_STATUS_ERROR; } else { - return -1; + apdu_len += len; } return apdu_len; } +/** + * @brief Decode a BACnetCredentialAuthenticationFactor structure from an APDU + * buffer, with context tag. + * @param apdu [in] The APDU buffer + * @param apdu_size [in] The size of the APDU buffer + * @param tag [in] The context tag number + * @param factor [out] The BACnetCredentialAuthenticationFactor structure to + * decode + * @return number of bytes decoded, or negative on error + */ int bacapp_decode_context_credential_authentication_factor( const uint8_t *apdu, + size_t apdu_size, uint8_t tag, BACNET_CREDENTIAL_AUTHENTICATION_FACTOR *factor) { - int len = 0; - int section_length; + int len = 0, apdu_len = 0; - if (decode_is_opening_tag_number(&apdu[len], tag)) { - len++; - section_length = - bacapp_decode_credential_authentication_factor(&apdu[len], factor); - - if (section_length == -1) { - len = -1; - } else { - len += section_length; - if (decode_is_closing_tag_number(&apdu[len], tag)) { - len++; - } else { - len = -1; - } - } + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, tag, &len)) { + apdu_len += len; } else { - len = -1; + return BACNET_STATUS_ERROR; } - return len; + len = bacapp_decode_credential_authentication_factor( + &apdu[apdu_len], apdu_size - apdu_len, factor); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, tag, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + + return apdu_len; } diff --git a/src/bacnet/credential_authentication_factor.h b/src/bacnet/credential_authentication_factor.h index 97fed36f..3c5d993a 100644 --- a/src/bacnet/credential_authentication_factor.h +++ b/src/bacnet/credential_authentication_factor.h @@ -36,10 +36,13 @@ int bacapp_encode_context_credential_authentication_factor( const BACNET_CREDENTIAL_AUTHENTICATION_FACTOR *factor); BACNET_STACK_EXPORT int bacapp_decode_credential_authentication_factor( - const uint8_t *apdu, BACNET_CREDENTIAL_AUTHENTICATION_FACTOR *factor); + const uint8_t *apdu, + size_t apdu_size, + BACNET_CREDENTIAL_AUTHENTICATION_FACTOR *factor); BACNET_STACK_EXPORT int bacapp_decode_context_credential_authentication_factor( const uint8_t *apdu, + size_t apdu_size, uint8_t tag, BACNET_CREDENTIAL_AUTHENTICATION_FACTOR *factor); diff --git a/src/bacnet/datetime.c b/src/bacnet/datetime.c index ea4ef8a1..34bd1172 100644 --- a/src/bacnet/datetime.c +++ b/src/bacnet/datetime.c @@ -1288,6 +1288,7 @@ int bacnet_datetime_context_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decodes a context tagged BACnetDateTime value from APDU buffer * @param apdu - the APDU buffer @@ -1301,6 +1302,7 @@ int bacapp_decode_context_datetime( { return bacnet_datetime_context_decode(apdu, MAX_APDU, tag_number, value); } +#endif /** * @brief Compare BACnetDateRange complex data types diff --git a/src/bacnet/event.c b/src/bacnet/event.c index 17cf53ba..e012ec06 100644 --- a/src/bacnet/event.c +++ b/src/bacnet/event.c @@ -1898,8 +1898,9 @@ int event_notify_decode_service_request( } else { property_state = NULL; } - len = bacapp_decode_context_property_state( - &apdu[apdu_len], 0, property_state); + len = bacapp_property_state_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, + property_state); if (len > 0) { apdu_len += len; } else { diff --git a/src/bacnet/getevent.c b/src/bacnet/getevent.c index d1c8ed6d..760bb211 100644 --- a/src/bacnet/getevent.c +++ b/src/bacnet/getevent.c @@ -69,7 +69,6 @@ size_t getevent_service_request_encode( * @param lastReceivedObjectIdentifier Object identifier * * @return Bytes encoded. - * @deprecated Use getevent_apdu_encode() instead */ int getevent_encode_apdu( uint8_t *apdu, uint8_t invoke_id, const BACNET_OBJECT_ID *data) diff --git a/src/bacnet/getevent.h b/src/bacnet/getevent.h index 09d825c1..dfae3101 100644 --- a/src/bacnet/getevent.h +++ b/src/bacnet/getevent.h @@ -42,7 +42,6 @@ BACNET_STACK_EXPORT int getevent_apdu_encode( uint8_t *apdu, const BACNET_OBJECT_ID *lastReceivedObjectIdentifier); -BACNET_STACK_DEPRECATED("Use getevent_apdu_encode() instead") BACNET_STACK_EXPORT int getevent_encode_apdu( uint8_t *apdu, diff --git a/src/bacnet/ihave.c b/src/bacnet/ihave.c index 70a17316..6202031f 100644 --- a/src/bacnet/ihave.c +++ b/src/bacnet/ihave.c @@ -25,21 +25,32 @@ int ihave_encode_apdu(uint8_t *apdu, const BACNET_I_HAVE_DATA *data) int len = 0; /* length of each encoding */ int apdu_len = 0; /* total length of the apdu, return value */ - if (apdu && data) { + if (apdu) { apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; apdu[1] = SERVICE_UNCONFIRMED_I_HAVE; - apdu_len = 2; + } + len = 2; + apdu_len += len; + if (apdu) { + apdu += len; + } + if (data) { /* deviceIdentifier */ len = encode_application_object_id( - &apdu[apdu_len], data->device_id.type, data->device_id.instance); + apdu, data->device_id.type, data->device_id.instance); apdu_len += len; + if (apdu) { + apdu += len; + } /* objectIdentifier */ len = encode_application_object_id( - &apdu[apdu_len], data->object_id.type, data->object_id.instance); + apdu, data->object_id.type, data->object_id.instance); apdu_len += len; + if (apdu) { + apdu += len; + } /* objectName */ - len = encode_application_character_string( - &apdu[apdu_len], &data->object_name); + len = encode_application_character_string(apdu, &data->object_name); apdu_len += len; } @@ -51,85 +62,96 @@ int ihave_encode_apdu(uint8_t *apdu, const BACNET_I_HAVE_DATA *data) /** * Decode the I Have request only * + * I-Have-Request ::= SEQUENCE { + * device-identifierBACnetObjectIdentifier, + * object-identifierBACnetObjectIdentifier, + * object-nameCharacterString + * } + * * @param apdu Pointer to the APDU buffer - * @param apdu_len Valid bytes in the buffer + * @param apdu_size Number of valid bytes in the buffer * @param data Pointer to the I Have data structure. * * @return Bytes decoded. */ int ihave_decode_service_request( - const uint8_t *apdu, unsigned apdu_len, BACNET_I_HAVE_DATA *data) + const uint8_t *apdu, unsigned apdu_size, BACNET_I_HAVE_DATA *data) { - int len = 0; - uint8_t tag_number = 0; - uint32_t len_value = 0; + int len = 0, apdu_len = 0; BACNET_OBJECT_TYPE decoded_type = OBJECT_NONE; /* for decoding */ + uint32_t decoded_instance = 0; /* for decoding */ + BACNET_CHARACTER_STRING *decoded_string = NULL; /* for decoding */ - if ((apdu_len >= 2) && data) { - /* deviceIdentifier */ - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) { - len += decode_object_id( - &apdu[len], &decoded_type, &data->device_id.instance); - data->device_id.type = decoded_type; - } else { - return -1; - } - /* objectIdentifier */ - if ((unsigned)len >= apdu_len) { - return -1; - } - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) { - len += decode_object_id( - &apdu[len], &decoded_type, &data->object_id.instance); - data->object_id.type = decoded_type; - } else { - return -1; - } - /* objectName */ - if ((unsigned)len >= apdu_len) { - return -1; - } - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if (tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING) { - len += decode_character_string( - &apdu[len], len_value, &data->object_name); - } else { - return -1; - } - } else { + if (!apdu || (apdu_size < 2)) { return -1; } + /* deviceIdentifier */ + len = bacnet_object_id_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, &decoded_type, + &decoded_instance); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } else { + if (data) { + data->device_id.type = decoded_type; + data->device_id.instance = decoded_instance; + } + } + apdu_len += len; + /* objectIdentifier */ + len = bacnet_object_id_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, &decoded_type, + &decoded_instance); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } else { + if (data) { + data->object_id.type = decoded_type; + data->object_id.instance = decoded_instance; + } + } + apdu_len += len; + /* objectName */ + if (data) { + decoded_string = &data->object_name; + } + len = bacnet_character_string_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, decoded_string); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } else { + /* nothing else to do, the string is already decoded in place */ + } + apdu_len += len; - return len; + return apdu_len; } /** * Decode the I Have * * @param apdu Pointer to the APDU buffer - * @param apdu_len Valid bytes in the buffer + * @param apdu_size Number of valid bytes in the buffer * @param data Pointer to the I Have data structure. * * @return Bytes decoded. */ int ihave_decode_apdu( - const uint8_t *apdu, unsigned apdu_len, BACNET_I_HAVE_DATA *data) + const uint8_t *apdu, unsigned apdu_size, BACNET_I_HAVE_DATA *data) { int len = 0; - if ((!apdu) || (apdu_len < 2)) { - return -1; + if ((!apdu) || (apdu_size < 2)) { + return BACNET_STATUS_ERROR; } /* optional checking - most likely was already done prior to this call */ if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) { - return -1; + return BACNET_STATUS_ERROR; } if (apdu[1] != SERVICE_UNCONFIRMED_I_HAVE) { - return -1; + return BACNET_STATUS_ERROR; } - len = ihave_decode_service_request(&apdu[2], apdu_len - 2, data); + len = ihave_decode_service_request(&apdu[2], apdu_size - 2, data); return len; } diff --git a/src/bacnet/list_element.c b/src/bacnet/list_element.c index d16791bd..a6957151 100644 --- a/src/bacnet/list_element.c +++ b/src/bacnet/list_element.c @@ -107,17 +107,15 @@ size_t list_element_service_request_encode( /** * @brief Decode the Add/Remove ListElement service request only * @param apdu Pointer to the buffer for decoding. - * @param apdu_len Count of valid bytes in the buffer. + * @param apdu_size Count of valid bytes in the buffer. * @param list_element Pointer to the property decoded data to be stored * * @return Bytes decoded or BACNET_STATUS_REJECT on error. */ int list_element_decode_service_request( - uint8_t *apdu, unsigned apdu_len, BACNET_LIST_ELEMENT_DATA *list_element) + uint8_t *apdu, unsigned apdu_size, BACNET_LIST_ELEMENT_DATA *list_element) { - unsigned len = 0, application_data_len = 0; - uint8_t tag_number = 0; - uint32_t len_value_type = 0; + int len = 0, application_data_len = 0, apdu_len = 0; BACNET_OBJECT_TYPE object_type = OBJECT_NONE; uint32_t object_instance = 0; uint32_t property = 0; @@ -125,7 +123,7 @@ int list_element_decode_service_request( /* Must have at least 2 tags, an object id and a property identifier * of at least 1 byte in length to have any chance of parsing */ - if (apdu_len < 7) { + if (apdu_size < 7) { if (list_element) { list_element->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; @@ -134,107 +132,87 @@ int list_element_decode_service_request( } /* Tag 0: Object ID */ - if (!decode_is_context_tag(&apdu[len++], 0)) { + len = bacnet_object_id_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &object_type, + &object_instance); + if (len <= 0) { if (list_element) { list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; } return BACNET_STATUS_REJECT; } - len += decode_object_id(&apdu[len], &object_type, &object_instance); + apdu_len += len; if (list_element) { list_element->object_type = object_type; list_element->object_instance = object_instance; } /* Tag 1: Property ID */ - len += - decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); - if (tag_number != 1) { + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &property); + if (len <= 0) { if (list_element) { list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; } return BACNET_STATUS_REJECT; } - if (len >= apdu_len) { - if (list_element) { - list_element->error_code = - ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; - } - return BACNET_STATUS_REJECT; - } - len += decode_enumerated(&apdu[len], len_value_type, &property); if (list_element) { list_element->object_property = (BACNET_PROPERTY_ID)property; } - if (len >= apdu_len) { - if (list_element) { - list_element->error_code = - ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; - } - return BACNET_STATUS_REJECT; - } + apdu_len += len; /* Tag 2: Optional Array Index */ - if (decode_is_opening_tag_number(&apdu[len], 3)) { - /* this is tag 3, therefore, optional tag 2 is not present */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &unsigned_value); + if (len > 0) { + if (list_element) { + list_element->array_index = (BACNET_ARRAY_INDEX)unsigned_value; + } + apdu_len += len; + } else if (len == 0) { + /* optional, so not an error if not present */ if (list_element) { list_element->array_index = BACNET_ARRAY_ALL; } } else { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value_type); - if ((tag_number == 2) && (len < apdu_len)) { - len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value); - if (list_element) { - list_element->array_index = (BACNET_ARRAY_INDEX)unsigned_value; - } - } - } - if (len >= apdu_len) { if (list_element) { - list_element->error_code = - ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; } return BACNET_STATUS_REJECT; } /* Tag 3: opening context tag */ - if (decode_is_opening_tag_number(&apdu[len], 3)) { - /* an opening tag number of 3 is not extended so only one octet */ - len++; - /* don't decode the application tag number or its data here */ - if (list_element) { - list_element->application_data = &apdu[len]; - } - application_data_len = apdu_len - len - 1 /*closing tag */; - if (list_element) { - /* Just to ensure we do not create a wrapped over value here. */ - if (len < apdu_len) { - list_element->application_data_len = application_data_len; - } else { - list_element->application_data_len = 0; + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 3, &len)) { + application_data_len = + bacnet_enclosed_data_length(&apdu[apdu_len], apdu_size - apdu_len); + if (application_data_len < 0) { + if (list_element) { + list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; } + return BACNET_STATUS_REJECT; } - len += application_data_len; + /* add the tag length */ + apdu_len += len; + /* postpone the application data */ + if (list_element) { + list_element->application_data = &apdu[apdu_len]; + list_element->application_data_len = application_data_len; + } + apdu_len += application_data_len; } else { if (list_element) { list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; } return BACNET_STATUS_REJECT; } - if (len >= apdu_len) { - if (list_element) { - list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; - } - return BACNET_STATUS_REJECT; - } - if (decode_is_closing_tag_number(&apdu[len], 3)) { - /* a closing tag number of 3 is not extended so only one octet */ - len++; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 3, &len)) { + apdu_len += len; } else { if (list_element) { list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; } return BACNET_STATUS_REJECT; } - if (len < apdu_len) { + if (apdu_len < apdu_size) { /* If something left over now, we have an invalid request */ if (list_element) { list_element->error_code = ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS; @@ -242,7 +220,7 @@ int list_element_decode_service_request( return BACNET_STATUS_REJECT; } - return (int)len; + return (int)apdu_len; } /** @@ -330,9 +308,7 @@ int list_element_error_ack_decode( return 0; } /* Opening Context tag 0 - Error */ - if (decode_is_opening_tag_number(apdu, 0)) { - /* opening tag 0 is 1 byte */ - len = 1; + if (bacnet_is_opening_tag_number(apdu, apdu_size, 0, &len)) { apdu_len += len; apdu += len; } else { @@ -357,9 +333,7 @@ int list_element_error_ack_decode( return 0; } /* Closing Context tag 0 - Error */ - if (decode_is_closing_tag_number(apdu, 0)) { - /* closing tag 0 is 1 byte */ - len = 1; + if (bacnet_is_closing_tag_number(apdu, apdu_size - apdu_len, 0, &len)) { apdu_len += len; apdu += len; } else { diff --git a/src/bacnet/lso.c b/src/bacnet/lso.c index ae8f551f..d6f754c3 100644 --- a/src/bacnet/lso.c +++ b/src/bacnet/lso.c @@ -13,6 +13,14 @@ /** * @brief Encode APDU for LifeSafetyOperation-Request + * + * LifeSafetyOperation-Request ::= SEQUENCE { + * requesting-process-identifier[0] Unsigned32, + * requesting-source[1] CharacterString, + * request[2] BACnetLifeSafetyOperation, + * object-identifier[3] BACnetObjectIdentifier OPTIONAL + * } + * * @param apdu Pointer to the buffer, or NULL for length * @param data Pointer to the data to encode. * @return number of bytes encoded, or zero on error. @@ -108,55 +116,86 @@ size_t life_safety_operation_request_encode( return apdu_len; } +/** + * @brief Decode the LifeSafetyOperation-Request + * + * * LifeSafetyOperation-Request ::= SEQUENCE { + * requesting-process-identifier[0] Unsigned32, + * requesting-source[1] CharacterString, + * request[2] BACnetLifeSafetyOperation, + * object-identifier[3] BACnetObjectIdentifier OPTIONAL + * } + + * @param apdu Pointer to the buffer to decode from + * @param apdu_size number of bytes available in the buffer + * @param data Pointer to the data to decode into. + * @return number of bytes decoded, or BACNET_STATUS_ERROR on error. + */ int lso_decode_service_request( - const uint8_t *apdu, unsigned apdu_len, BACNET_LSO_DATA *data) + const uint8_t *apdu, unsigned apdu_size, BACNET_LSO_DATA *data) { - int len = 0; /* return value */ - int section_length = 0; /* length returned from decoding */ + int len = 0; /* length returned from decoding */ + int apdu_len = 0; /* return value */ uint32_t operation = 0; /* handles decoded value */ - BACNET_UNSIGNED_INTEGER unsigned_value = 0; + BACNET_UNSIGNED_INTEGER unsigned_value = 0; /* for decoding*/ + BACNET_CHARACTER_STRING *decoded_string = NULL; /* for decoding */ + BACNET_OBJECT_TYPE object_type = 0; + uint32_t instance = 0; /* check for value pointers */ - if (apdu_len && data) { - /* Tag 0: Object ID */ - section_length = - decode_context_unsigned(&apdu[len], 0, &unsigned_value); - if (section_length == BACNET_STATUS_ERROR) { - return BACNET_STATUS_ERROR; - } + if (!apdu && !apdu_size) { + return 0; + } + /* requesting-process-identifier[0] Unsigned32 */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &unsigned_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + if (data) { data->processId = (uint32_t)unsigned_value; - len += section_length; - section_length = decode_context_character_string( - &apdu[len], 1, &data->requestingSrc); - if (section_length == BACNET_STATUS_ERROR) { - return BACNET_STATUS_ERROR; - } - len += section_length; - section_length = decode_context_enumerated(&apdu[len], 2, &operation); - if (section_length == BACNET_STATUS_ERROR) { - return BACNET_STATUS_ERROR; - } + } + apdu_len += len; + if (data) { + decoded_string = &data->requestingSrc; + } + /* requesting-source[1] CharacterString */ + len = bacnet_character_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, decoded_string); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + /* request[2] BACnetLifeSafetyOperation */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &operation); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + if (operation > LIFE_SAFETY_OP_PROPRIETARY_MAX) { + return BACNET_STATUS_ERROR; + } + if (data) { data->operation = (BACNET_LIFE_SAFETY_OPERATION)operation; - len += section_length; - /* - ** This is an optional parameter, so don't fail if it doesn't exist - */ - if (decode_is_context_tag(&apdu[len], 3)) { - section_length = decode_context_object_id( - &apdu[len], 3, &data->targetObject.type, - &data->targetObject.instance); - if (section_length == BACNET_STATUS_ERROR) { - return BACNET_STATUS_ERROR; - } + } + apdu_len += len; + /* object-identifier[3] BACnetObjectIdentifier OPTIONAL */ + len = bacnet_object_id_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, &object_type, &instance); + if (len > 0) { + if (data) { + data->targetObject.type = object_type; + data->targetObject.instance = instance; data->use_target = true; - len += section_length; - } else { + } + apdu_len += len; + } else { + if (data) { data->use_target = false; data->targetObject.type = OBJECT_NONE; data->targetObject.instance = 0; } - return len; } - return 0; + return apdu_len; } diff --git a/src/bacnet/ptransfer.c b/src/bacnet/ptransfer.c index 33645496..a898f302 100644 --- a/src/bacnet/ptransfer.c +++ b/src/bacnet/ptransfer.c @@ -12,47 +12,99 @@ #include "bacnet/bacdcode.h" #include "bacnet/ptransfer.h" -/* encode service */ -static int pt_encode_apdu( - uint8_t *apdu, - uint16_t max_apdu, - const BACNET_PRIVATE_TRANSFER_DATA *private_data) +/** + * @brief Encode the service parameters for both confirmed and + * unconfirmed private transfer + * + * ConfirmedPrivateTransfer-Request ::= SEQUENCE { + * vendorID [0] Unsigned, + * serviceNumber [1] Unsigned, + * serviceParameters [2] ABSTRACT-SYNTAX.&Type OPTIONAL + * } + * UnconfirmedPrivateTransfer-Request ::= SEQUENCE { + * vendorID [0] Unsigned, + * serviceNumber [1] Unsigned, + * serviceParameters [2] ABSTRACT-SYNTAX.&Type OPTIONAL + * } + * + * @param apdu [in] The APDU buffer for encoding, or NULL for length + * @param apdu_size [in] The maximum APDU size for encoding + * @param private_data [in] The BACNET_PRIVATE_TRANSFER_DATA structure to encode + * @return number of bytes encoded, or negative on error + */ +int private_transfer_request_encode( + uint8_t *apdu, const BACNET_PRIVATE_TRANSFER_DATA *data) { int len = 0; /* length of each encoding */ int apdu_len = 0; /* total length of the apdu, return value */ - /* - Unconfirmed/ConfirmedPrivateTransfer-Request ::= SEQUENCE { - vendorID [0] Unsigned, - serviceNumber [1] Unsigned, - serviceParameters [2] ABSTRACT-SYNTAX.&Type OPTIONAL - } - */ - /* unused parameter */ - (void)max_apdu; + + len = encode_context_unsigned(apdu, 0, data->vendorID); + apdu_len += len; if (apdu) { - len = - encode_context_unsigned(&apdu[apdu_len], 0, private_data->vendorID); - apdu_len += len; - len = encode_context_unsigned( - &apdu[apdu_len], 1, private_data->serviceNumber); - apdu_len += len; - len = encode_opening_tag(&apdu[apdu_len], 2); - apdu_len += len; - for (len = 0; len < private_data->serviceParametersLen; len++) { - apdu[apdu_len] = private_data->serviceParameters[len]; - apdu_len++; + apdu += len; + } + len = encode_context_unsigned(apdu, 1, data->serviceNumber); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_opening_tag(apdu, 2); + apdu_len += len; + if (apdu) { + apdu += len; + } + for (len = 0; len < data->serviceParametersLen; len++) { + if (apdu) { + *apdu = data->serviceParameters[len]; + apdu++; } - len = encode_closing_tag(&apdu[apdu_len], 2); - apdu_len += len; + apdu_len++; + } + len = encode_closing_tag(apdu, 2); + apdu_len += len; + + return apdu_len; +} + +/** + * @brief Encode the service parameters for both confirmed and + * unconfirmed private transfer, with APDU length checking + * @param apdu [in] The APDU buffer for encoding, or NULL for length + * @param apdu_size [in] The maximum APDU size for encoding + * @param private_data [in] The BACNET_PRIVATE_TRANSFER_DATA structure to encode + * @return number of bytes encoded, or zero on error + */ +int private_transfer_request_service_encode( + uint8_t *apdu, uint16_t apdu_size, const BACNET_PRIVATE_TRANSFER_DATA *data) +{ + size_t apdu_len = 0; /* total length of the apdu, return value */ + + apdu_len = private_transfer_request_encode(NULL, data); + if (apdu_len > apdu_size) { + apdu_len = 0; + } else { + apdu_len = private_transfer_request_encode(apdu, data); } return apdu_len; } +/** + * @brief Encode a ConfirmedPrivateTransfer-Error APDU + * ConfirmedPrivateTransfer-Error ::= SEQUENCE { + * errorType [0] Error, + * vendorID [1] Unsigned, + * serviceNumber [2] Unsigned, + * errorParameters [3] ABSTRACT-SYNTAX.&Type OPTIONAL + * } + * @param apdu [in] The APDU buffer for encoding, or NULL for length + * @param apdu_size [in] The maximum APDU size for encoding + * @param invoke_id [in] The invoke ID for the error response + * @param data [in] The BACNET_PRIVATE_TRANSFER_DATA structure to encode + * @return number of bytes encoded, or zero on error + */ int ptransfer_encode_apdu( - uint8_t *apdu, - uint8_t invoke_id, - const BACNET_PRIVATE_TRANSFER_DATA *private_data) + uint8_t *apdu, uint8_t invoke_id, const BACNET_PRIVATE_TRANSFER_DATA *data) { int apdu_len = 0; /* total length of the apdu, return value */ int len = 0; @@ -62,17 +114,20 @@ int ptransfer_encode_apdu( apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); apdu[2] = invoke_id; apdu[3] = SERVICE_CONFIRMED_PRIVATE_TRANSFER; - apdu_len = 4; - len = pt_encode_apdu( - &apdu[apdu_len], (uint16_t)(MAX_APDU - apdu_len), private_data); - apdu_len += len; } + len = 4; + apdu_len += len; + if (apdu) { + apdu += len; + } + len = private_transfer_request_encode(apdu, data); + apdu_len += len; return apdu_len; } int uptransfer_encode_apdu( - uint8_t *apdu, const BACNET_PRIVATE_TRANSFER_DATA *private_data) + uint8_t *apdu, const BACNET_PRIVATE_TRANSFER_DATA *data) { int apdu_len = 0; /* total length of the apdu, return value */ int len = 0; @@ -80,65 +135,197 @@ int uptransfer_encode_apdu( if (apdu) { apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; apdu[1] = SERVICE_UNCONFIRMED_PRIVATE_TRANSFER; - apdu_len = 2; - len = pt_encode_apdu( - &apdu[apdu_len], (uint16_t)(MAX_APDU - apdu_len), private_data); + } + len = 2; + apdu_len += len; + if (apdu) { + apdu += len; + } + len = private_transfer_request_encode(apdu, data); + apdu_len += len; + + return apdu_len; +} + +/** + * @brief Decode the service parameters for both confirmed and + * unconfirmed private transfer + * ConfirmedPrivateTransfer-Request ::= SEQUENCE { + * vendorID [0] Unsigned, + * serviceNumber [1] Unsigned, + * serviceParameters [2] ABSTRACT-SYNTAX.&Type OPTIONAL + * } + * UnconfirmedPrivateTransfer-Request ::= SEQUENCE { + * vendorID [0] Unsigned, + * serviceNumber [1] Unsigned, + * serviceParameters [2] ABSTRACT-SYNTAX.&Type OPTIONAL + * } + * @param apdu [in] The APDU buffer for decoding + * @param apdu_size [in] The length of the APDU buffer for decoding + * @param private_data [out] The BACNET_PRIVATE_TRANSFER_DATA structure to + * decode into + * @return number of bytes decoded, or negative on error + */ +int ptransfer_decode_service_request( + uint8_t *apdu, unsigned apdu_size, BACNET_PRIVATE_TRANSFER_DATA *data) +{ + int apdu_len = 0, len = 0, application_len = 0; + BACNET_UNSIGNED_INTEGER unsigned_value = 0; + + /* check for value pointers */ + if (!(apdu && apdu_size)) { + return BACNET_STATUS_ERROR; + } + /* Tag 0: vendorID */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &unsigned_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (data) { + if (unsigned_value > UINT16_MAX) { + data->vendorID = UINT16_MAX; + } else { + data->vendorID = (uint16_t)unsigned_value; + } + } + /* Tag 1: serviceNumber */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &unsigned_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (data) { + if (unsigned_value > UINT32_MAX) { + data->serviceNumber = UINT32_MAX; + } else { + data->serviceNumber = (uint32_t)unsigned_value; + } + } + /* Tag 2: serviceParameters */ + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, &len)) { + application_len = + bacnet_enclosed_data_length(&apdu[apdu_len], apdu_size - apdu_len); + if (application_len < 0) { + return BACNET_STATUS_ERROR; + } + /* add the tag length */ apdu_len += len; + /* postpone decoding the serviceParameters */ + if (data) { + data->serviceParameters = &apdu[apdu_len]; + data->serviceParametersLen = application_len; + } + apdu_len += application_len; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; } return apdu_len; } -/* decode the service request only */ -int ptransfer_decode_service_request( +/** + * @brief Encode an Error acknowledge for a Private Transfer service. + * ConfirmedPrivateTransfer-Error ::= SEQUENCE { + * errorType [0] Error, + * vendorID [1] Unsigned, + * serviceNumber [2] Unsigned, + * errorParameters [3] ABSTRACT-SYNTAX.&Type OPTIONAL + * } + * @param apdu [in] The APDU buffer for encoding, or NULL for length + * @param error_class [in] The BACNET_ERROR_CLASS to encode + * @param error_code [in] The BACNET_ERROR_CODE to encode + * @param private_data [in] The BACNET_PRIVATE_TRANSFER_DATA structure to encode + * @return number of bytes encoded, or zero on error + */ +int ptransfer_error_encode_service( uint8_t *apdu, - unsigned apdu_len, - BACNET_PRIVATE_TRANSFER_DATA *private_data) + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code, + const BACNET_PRIVATE_TRANSFER_DATA *private_data) { - int len = 0; /* return value */ - int decode_len = 0; /* return value */ - BACNET_UNSIGNED_INTEGER unsigned_value = 0; + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; /* length of the part of the encoding */ - /* check for value pointers */ - if (apdu_len && private_data) { - /* Tag 0: vendorID */ - decode_len = decode_context_unsigned(&apdu[len], 0, &unsigned_value); - if (decode_len < 0) { - return -1; - } - len = decode_len; - private_data->vendorID = (uint16_t)unsigned_value; - /* Tag 1: serviceNumber */ - decode_len = decode_context_unsigned(&apdu[len], 1, &unsigned_value); - if (decode_len < 0) { - return -1; - } - len += decode_len; - private_data->serviceNumber = unsigned_value; - /* Tag 2: serviceParameters */ - if (decode_is_opening_tag_number(&apdu[len], 2)) { - /* a tag number of 2 is not extended so only one octet */ - len++; - /* don't decode the serviceParameters here */ - private_data->serviceParameters = &apdu[len]; - private_data->serviceParametersLen = - (int)apdu_len - len - 1 /*closing tag */; - /* len includes the data and the closing tag */ - len = (int)apdu_len; - } else { - return -1; - } + len = encode_opening_tag(apdu, 0); + apdu_len += len; + if (apdu) { + apdu += len; } + len = encode_application_enumerated(apdu, error_class); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_enumerated(apdu, error_code); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, 0); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_context_unsigned(apdu, 1, private_data->vendorID); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_context_unsigned(apdu, 2, private_data->serviceNumber); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_opening_tag(apdu, 3); + apdu_len += len; + if (apdu) { + apdu += len; + } + for (len = 0; len < private_data->serviceParametersLen; len++) { + if (apdu) { + *apdu = private_data->serviceParameters[len]; + apdu++; + } + apdu_len++; + } + len = encode_closing_tag(apdu, 3); + apdu_len += len; - return len; + return apdu_len; } +/** + * @brief Encode an Error acknowledge for a Private Transfer service. + * ConfirmedPrivateTransfer-Error ::= SEQUENCE { + * errorType [0] Error, + * vendorID [1] Unsigned, + * serviceNumber [2] Unsigned, + * errorParameters [3] ABSTRACT-SYNTAX.&Type OPTIONAL + * } + * @param apdu [in] The APDU buffer for encoding, or NULL for length + * @param apdu_size [in] The maximum APDU size for encoding + * @param invoke_id [in] The invoke ID for the error response + * @param error_class [in] The BACNET_ERROR_CLASS to encode + * @param error_code [in] The BACNET_ERROR_CODE to encode + * @param private_data [in] The BACNET_PRIVATE_TRANSFER_DATA structure to encode + * @return number of bytes encoded, or zero on error + */ int ptransfer_error_encode_apdu( uint8_t *apdu, uint8_t invoke_id, BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code, - const BACNET_PRIVATE_TRANSFER_DATA *private_data) + const BACNET_PRIVATE_TRANSFER_DATA *data) { int apdu_len = 0; /* total length of the apdu, return value */ int len = 0; /* length of the part of the encoding */ @@ -147,126 +334,124 @@ int ptransfer_error_encode_apdu( apdu[0] = PDU_TYPE_ERROR; apdu[1] = invoke_id; apdu[2] = SERVICE_CONFIRMED_PRIVATE_TRANSFER; - apdu_len = 3; - /* service parameters */ - /* - ConfirmedPrivateTransfer-Error ::= SEQUENCE { - errorType [0] Error, - vendorID [1] Unsigned, - serviceNumber [2] Unsigned, - errorParameters [3] ABSTRACT-SYNTAX.&Type OPTIONAL - } - */ - len = encode_opening_tag(&apdu[apdu_len], 0); - apdu_len += len; - len = encode_application_enumerated(&apdu[apdu_len], error_class); - apdu_len += len; - len = encode_application_enumerated(&apdu[apdu_len], error_code); - apdu_len += len; - len = encode_closing_tag(&apdu[apdu_len], 0); - apdu_len += len; - len = - encode_context_unsigned(&apdu[apdu_len], 1, private_data->vendorID); - apdu_len += len; - len = encode_context_unsigned( - &apdu[apdu_len], 2, private_data->serviceNumber); - apdu_len += len; - len = encode_opening_tag(&apdu[apdu_len], 3); - apdu_len += len; - for (len = 0; len < private_data->serviceParametersLen; len++) { - apdu[apdu_len] = private_data->serviceParameters[len]; - apdu_len++; - } - len = encode_closing_tag(&apdu[apdu_len], 3); - apdu_len += len; } + len = 3; + apdu_len += len; + if (apdu) { + apdu += len; + } + /* service parameters */ + len = ptransfer_error_encode_service(apdu, error_class, error_code, data); + apdu_len += len; return apdu_len; } -/* decode the service request only */ +/** + * @brief Decode an Error acknowledge for a Private Transfer service. + * @param apdu [in] The APDU buffer for decoding. + * @param apdu_size [in] The length of the APDU buffer. + * @param error_class [out] The decoded error class. + * @param error_code [out] The decoded error code. + * @param private_data [out] The decoded private transfer data. + * @return number of bytes decoded, or negative on error. + */ int ptransfer_error_decode_service_request( uint8_t *apdu, - unsigned apdu_len, + unsigned apdu_size, BACNET_ERROR_CLASS *error_class, BACNET_ERROR_CODE *error_code, BACNET_PRIVATE_TRANSFER_DATA *private_data) { - int len = 0; /* return value */ - int decode_len = 0; /* return value */ - uint8_t tag_number = 0; - uint32_t len_value_type = 0; + int apdu_len = 0, len = 0; + int application_len = 0; uint32_t enum_value = 0; BACNET_UNSIGNED_INTEGER unsigned_value = 0; - /* check for value pointers */ - if (apdu_len && private_data) { - /* Tag 0: Error */ - if (decode_is_opening_tag_number(&apdu[len], 0)) { - /* a tag number of 0 is not extended so only one octet */ - len++; - /* error class */ - decode_len = decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value_type); - len += decode_len; - if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) { - return 0; - } - decode_len = - decode_enumerated(&apdu[len], len_value_type, &enum_value); - len += decode_len; - if (error_class) { - *error_class = (BACNET_ERROR_CLASS)enum_value; - } - /* error code */ - decode_len = decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value_type); - len += decode_len; - if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) { - return 0; - } - decode_len = - decode_enumerated(&apdu[len], len_value_type, &enum_value); - len += decode_len; - if (error_code) { - *error_code = (BACNET_ERROR_CODE)enum_value; - } - if (decode_is_closing_tag_number(&apdu[len], 0)) { - /* a tag number of 0 is not extended so only one octet */ - len++; - } else { - return 0; - } - } - /* Tag 1: vendorID */ - decode_len = decode_context_unsigned(&apdu[len], 1, &unsigned_value); - if (decode_len < 0) { - return -1; - } - len += decode_len; + /* check for value pointer and minimum size */ + if (!(apdu && apdu_size)) { + return BACNET_STATUS_ERROR; + } + /* Tag 0: Error */ + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* error class */ + len = bacnet_enumerated_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, &enum_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (error_class) { + *error_class = (BACNET_ERROR_CLASS)enum_value; + } + /* error code */ + len = bacnet_enumerated_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, &enum_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (error_code) { + *error_code = (BACNET_ERROR_CODE)enum_value; + } + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* Tag 1: vendorID */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &unsigned_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (private_data) { private_data->vendorID = (uint16_t)unsigned_value; - /* Tag 2: serviceNumber */ - decode_len = decode_context_unsigned(&apdu[len], 2, &unsigned_value); - if (decode_len < 0) { - return -1; - } - len += decode_len; + } + /* Tag 2: serviceNumber */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &unsigned_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (private_data) { private_data->serviceNumber = (uint32_t)unsigned_value; - /* Tag 3: serviceParameters */ - if (decode_is_opening_tag_number(&apdu[len], 3)) { - /* a tag number of 2 is not extended so only one octet */ - len++; - /* don't decode the serviceParameters here */ - private_data->serviceParameters = &apdu[len]; - private_data->serviceParametersLen = - (int)apdu_len - len - 1 /*closing tag */; - } else { - return -1; + } + /* Tag 3: serviceParameters */ + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 3, &len)) { + application_len = + bacnet_enclosed_data_length(&apdu[apdu_len], apdu_size - apdu_len); + if (application_len < 0) { + return BACNET_STATUS_ERROR; } - /* we could check for a closing tag of 3 */ + /* update the tag length */ + apdu_len += len; + /* postpone serviceParameters decoding */ + if (private_data) { + private_data->serviceParameters = &apdu[apdu_len]; + private_data->serviceParametersLen = application_len; + } + apdu_len += application_len; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 3, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; } - return len; + return apdu_len; } int ptransfer_ack_encode_apdu( diff --git a/src/bacnet/rpm.c b/src/bacnet/rpm.c index 8cab8f29..47048a32 100644 --- a/src/bacnet/rpm.c +++ b/src/bacnet/rpm.c @@ -260,50 +260,60 @@ int rpm_encode_apdu( * @return number of decoded bytes, or negative on failure. */ int rpm_decode_object_id( - const uint8_t *apdu, unsigned apdu_len, BACNET_RPM_DATA *rpmdata) + const uint8_t *apdu, unsigned apdu_size, BACNET_RPM_DATA *rpmdata) { - int len = 0; + int len = 0, apdu_len = 0; + uint32_t instance = 0; BACNET_OBJECT_TYPE type = OBJECT_NONE; /* for decoding */ /* check for value pointers */ - if (apdu && apdu_len && rpmdata) { - if (apdu_len < 5) { /* Must be at least 2 tags and an object id */ - rpmdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + if (apdu && apdu_size) { + if (apdu_size < 6) { /* Must be at least 2 tags and an object id */ + if (rpmdata) { + rpmdata->error_code = + ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } return BACNET_STATUS_REJECT; } /* Tag 0: Object ID */ - if (!decode_is_context_tag(&apdu[len++], 0)) { - rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + len = bacnet_object_id_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &type, &instance); + if (len <= 0) { + if (rpmdata) { + rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } return BACNET_STATUS_REJECT; } - len += decode_object_id(&apdu[len], &type, &rpmdata->object_instance); - rpmdata->object_type = type; + if (rpmdata) { + rpmdata->object_type = type; + rpmdata->object_instance = instance; + } + apdu_len += len; /* Tag 1: sequence of ReadAccessSpecification */ - if (!decode_is_opening_tag_number(&apdu[len], 1)) { - rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + if (!bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 1, &len)) { + if (rpmdata) { + rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } return BACNET_STATUS_REJECT; } - len++; /* opening tag is only one octet */ + apdu_len += len; } - return len; + return apdu_len; } /** * @brief Decode the end portion of the service request only. * @param apdu application data unit buffer for decoding - * @param apdu_len [in] Count of valid bytes in the buffer. + * @param apdu_size [in] Count of valid bytes in the buffer. * @return number of decoded bytes, or negative on failure. */ -int rpm_decode_object_end(const uint8_t *apdu, unsigned apdu_len) +int rpm_decode_object_end(const uint8_t *apdu, unsigned apdu_size) { int len = 0; /* total length of the apdu, return value */ - if (apdu && apdu_len) { - if (decode_is_closing_tag_number(apdu, 1) == true) { - len = 1; - } - } + (void)bacnet_is_closing_tag_number(apdu, apdu_size, 1, &len); return len; } @@ -319,63 +329,54 @@ int rpm_decode_object_end(const uint8_t *apdu, unsigned apdu_len) * } * * @param apdu application data unit buffer for decoding - * @param apdu_len Count of received bytes. + * @param apdu_size Count of received bytes. * @param rpmdata Pointer to the data structure to be filled. * @return number of decoded bytes, or negative on failure. */ int rpm_decode_object_property( - const uint8_t *apdu, unsigned apdu_len, BACNET_RPM_DATA *rpmdata) + const uint8_t *apdu, unsigned apdu_size, BACNET_RPM_DATA *rpmdata) { - int len = 0; - int option_len = 0; - uint8_t tag_number = 0; - uint32_t len_value_type = 0; + int len = 0, apdu_len = 0; uint32_t property = 0; /* for decoding */ BACNET_UNSIGNED_INTEGER unsigned_value = 0; /* for decoding */ - /* check for valid pointers */ - if (apdu && apdu_len && rpmdata) { - /* Tag 0: propertyIdentifier */ - if (!IS_CONTEXT_SPECIFIC(apdu[len])) { - rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + /* check for valid pointer and minimum size */ + if (apdu && apdu_size) { + /* propertyIdentifier [0] BACnetPropertyIdentifier */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &property); + if (len <= 0) { + if (rpmdata) { + rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } return BACNET_STATUS_REJECT; } - - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value_type); - if (tag_number != 0) { - rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; - return BACNET_STATUS_REJECT; + if (rpmdata) { + rpmdata->object_property = (BACNET_PROPERTY_ID)property; } - /* Should be at least the unsigned value + 1 tag left */ - if ((len + len_value_type) >= apdu_len) { - rpmdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; - return BACNET_STATUS_REJECT; - } - len += decode_enumerated(&apdu[len], len_value_type, &property); - rpmdata->object_property = (BACNET_PROPERTY_ID)property; - /* Assume most probable outcome */ - rpmdata->array_index = BACNET_ARRAY_ALL; - /* Tag 1: Optional propertyArrayIndex */ - if (IS_CONTEXT_SPECIFIC(apdu[len]) && !IS_CLOSING_TAG(apdu[len])) { - option_len = decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value_type); - if (tag_number == 1) { - len += option_len; - /* Should be at least the unsigned array index + 1 tag left */ - if ((len + len_value_type) >= apdu_len) { - rpmdata->error_code = - ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; - return BACNET_STATUS_REJECT; - } - len += decode_unsigned( - &apdu[len], len_value_type, &unsigned_value); + apdu_len += len; + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &unsigned_value); + if (len > 0) { + /* propertyArrayIndex [1] Unsigned OPTIONAL */ + apdu_len += len; + if (rpmdata) { rpmdata->array_index = unsigned_value; } + } else if (len == 0) { + /* optional - assume ALL array elements */ + if (rpmdata) { + rpmdata->array_index = BACNET_ARRAY_ALL; + } + } else { + if (rpmdata) { + rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; } } - return len; + return apdu_len; } /** diff --git a/src/bacnet/timestamp.c b/src/bacnet/timestamp.c index 43f76594..2aeda6fa 100644 --- a/src/bacnet/timestamp.c +++ b/src/bacnet/timestamp.c @@ -282,6 +282,7 @@ int bacnet_timestamp_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode a time stamp from the given buffer. * @param apdu Pointer to the APDU buffer. @@ -294,6 +295,7 @@ int bacapp_decode_timestamp(const uint8_t *apdu, BACNET_TIMESTAMP *value) { return bacnet_timestamp_decode(apdu, MAX_APDU, value); } +#endif /** * @brief Decode a time stamp and check for opening and closing tags. @@ -337,6 +339,7 @@ int bacnet_timestamp_context_decode( return apdu_len; } +#if defined(BACNET_STACK_DEPRECATED_DISABLE) /** * @brief Decode a time stamp and check for opening and closing tags. * @param apdu Pointer to the APDU buffer. @@ -360,6 +363,7 @@ int bacapp_decode_context_timestamp( return len; } +#endif /** * @brief Parse an ascii string for the timestamp diff --git a/src/bacnet/timesync.c b/src/bacnet/timesync.c index 04fecf53..ded833c7 100644 --- a/src/bacnet/timesync.c +++ b/src/bacnet/timesync.c @@ -15,6 +15,41 @@ #include "bacnet/timesync.h" #if BACNET_SVC_TS_A +/** + * @brief Encode the time synchronisation service. + * + * TimeSynchronization-Request ::= SEQUENCE { + * time BACnetDateTime + * } + * + * @param apdu [in] Buffer in which the APDU contents are written + * @param service [in] Time service that shall be encoded, like + * SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION. + * @param my_date [in] Pointer to the date structure used to encode. + * @param my_time [in] Pointer to the time structure used to encode. + * + * @return Count of encoded bytes. + */ +int timesync_encode_apdu_service_parameters( + uint8_t *apdu, const BACNET_DATE *my_date, const BACNET_TIME *my_time) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (my_date && my_time) { + /* encode the date and time */ + len = encode_application_date(apdu, my_date); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_time(apdu, my_time); + apdu_len += len; + } + + return apdu_len; +} + /** Encode the time synchronisation service. * * @param apdu [in] Buffer in which the APDU contents are written @@ -34,15 +69,17 @@ int timesync_encode_apdu_service( int len = 0; /* length of each encoding */ int apdu_len = 0; /* total length of the apdu, return value */ - if (apdu && my_date && my_time) { + if (apdu) { apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; apdu[1] = service; - apdu_len = 2; - len = encode_application_date(&apdu[apdu_len], my_date); - apdu_len += len; - len = encode_application_time(&apdu[apdu_len], my_time); - apdu_len += len; } + len = 2; + apdu_len += len; + if (apdu) { + apdu += len; + } + len = timesync_encode_apdu_service_parameters(apdu, my_date, my_time); + apdu_len += len; return apdu_len; } @@ -81,7 +118,7 @@ int timesync_encode_apdu( /** Decode the service request only. * * @param apdu [in] Buffer in which the APDU contents are read - * @param apdu_len [in] length of the APDU buffer. + * @param apdu_size [in] length of the APDU buffer. * @param my_date [in] Pointer to the date structure filled in. * @param my_time [in] Pointer to the time structure filled in. * @@ -89,40 +126,29 @@ int timesync_encode_apdu( */ int timesync_decode_service_request( const uint8_t *apdu, - unsigned apdu_len, + unsigned apdu_size, BACNET_DATE *my_date, BACNET_TIME *my_time) { - int len = 0; - uint8_t tag_number = 0; - uint32_t len_value = 0; + int len = 0, apdu_len = 0; - if (apdu_len && my_date && my_time) { - /* date */ - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if (tag_number == BACNET_APPLICATION_TAG_DATE) { - if ((unsigned)(len + 4) <= apdu_len) { - len += decode_date(&apdu[len], my_date); - } else { - return -1; - } - } else { - return -1; - } - /* time */ - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if (tag_number == BACNET_APPLICATION_TAG_TIME) { - if ((unsigned)(len + 4) <= apdu_len) { - len += decode_bacnet_time(&apdu[len], my_time); - } else { - return -1; - } - } else { - return -1; - } + if (!(apdu && apdu_size)) { + return BACNET_STATUS_ERROR; } + len = bacnet_date_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, my_date); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + len = bacnet_time_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, my_time); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; - return len; + return apdu_len; } /** Handle a request to encode the list of timesync recipients. @@ -143,78 +169,23 @@ int timesync_decode_service_request( * } * * @param apdu [out] Buffer in which the APDU contents are built. - * @param max_apdu [in] Max length of the APDU buffer. + * @param apdu_size [in] Max length of the APDU buffer. * @param recipient [in] BACNET_RECIPIENT_LIST type linked list of recipients. * * @return How many bytes were encoded in the buffer, or * BACNET_STATUS_ABORT if the response would not fit within the buffer. */ int timesync_encode_timesync_recipients( - uint8_t *apdu, unsigned max_apdu, BACNET_RECIPIENT_LIST *recipient) + uint8_t *apdu, unsigned apdu_size, BACNET_RECIPIENT_LIST *list_head) { - int len = 0; int apdu_len = 0; - BACNET_OCTET_STRING octet_string; - BACNET_RECIPIENT_LIST *pRecipient; - if ((!apdu) || (max_apdu < 1) || (!recipient)) { - return (0); - } - - pRecipient = recipient; - while (pRecipient != NULL) { - if (pRecipient->tag == 0) { - if (max_apdu >= (1 + 4)) { - /* CHOICE - device [0] BACnetObjectIdentifier */ - if (pRecipient->type.device.type == OBJECT_DEVICE) { - len = encode_context_object_id( - &apdu[apdu_len], 0, pRecipient->type.device.type, - pRecipient->type.device.instance); - apdu_len += len; - } - } else { - return BACNET_STATUS_ABORT; - } - } else if (pRecipient->tag == 1) { - if (pRecipient->type.address.net) { - len = (int)(1 + 3 + 2 + pRecipient->type.address.len + 1); - } else { - len = (int)(1 + 3 + 2 + pRecipient->type.address.mac_len + 1); - } - if (max_apdu >= (unsigned)len) { - /* CHOICE - address [1] BACnetAddress - opening */ - len = encode_opening_tag(&apdu[apdu_len], 1); - apdu_len += len; - /* network-number Unsigned16, */ - /* -- A value of 0 indicates the local network */ - len = encode_application_unsigned( - &apdu[apdu_len], pRecipient->type.address.net); - apdu_len += len; - /* mac-address OCTET STRING */ - /* -- A string of length 0 indicates a broadcast */ - if (pRecipient->type.address.net == BACNET_BROADCAST_NETWORK) { - octetstring_init(&octet_string, NULL, 0); - } else if (pRecipient->type.address.net) { - octetstring_init( - &octet_string, &pRecipient->type.address.adr[0], - pRecipient->type.address.len); - } else { - octetstring_init( - &octet_string, &pRecipient->type.address.mac[0], - pRecipient->type.address.mac_len); - } - len = encode_application_octet_string( - &apdu[apdu_len], &octet_string); - apdu_len += len; - /* CHOICE - address [1] BACnetAddress - closing */ - len = encode_closing_tag(&apdu[apdu_len], 1); - apdu_len += len; - } else { - /* not a valid tag - don't encode this one */ - } - } - pRecipient = pRecipient->next; + apdu_len = bacnet_recipient_list_encode(NULL, list_head); + if (apdu_len > apdu_size) { + /* response would not fit within the buffer */ + return BACNET_STATUS_ABORT; } + apdu_len = bacnet_recipient_list_encode(apdu, list_head); return apdu_len; } @@ -244,89 +215,32 @@ int timesync_encode_timesync_recipients( * BACNET_STATUS_ABORT if there was a problem decoding the buffer */ int timesync_decode_timesync_recipients( - const uint8_t *apdu, unsigned max_apdu, BACNET_RECIPIENT_LIST *recipient) + const uint8_t *apdu, unsigned apdu_size, BACNET_RECIPIENT_LIST *list_head) { int len = 0; int apdu_len = 0; - int tag_len = 0; - uint8_t tag_number = 0; - uint32_t len_value_type = 0; - BACNET_UNSIGNED_INTEGER unsigned_value = 0; - BACNET_OCTET_STRING octet_string; - BACNET_RECIPIENT_LIST *pRecipient; + BACNET_RECIPIENT_LIST *list; + BACNET_RECIPIENT *recipient; - if ((!apdu) || (max_apdu < 1) || (!recipient)) { + if ((!apdu) || (apdu_size == 0)) { return BACNET_STATUS_ABORT; } - - pRecipient = recipient; - while (pRecipient != NULL) { - /* device [0] BACnetObjectIdentifier */ - if (decode_is_context_tag(&apdu[apdu_len], 0)) { - pRecipient->tag = 0; - if ((unsigned)(apdu_len + 4) > max_apdu) { - return BACNET_STATUS_ABORT; - } - len = decode_context_object_id( - &apdu[apdu_len], 0, &pRecipient->type.device.type, - &pRecipient->type.device.instance); - if (len < 0) { - return BACNET_STATUS_ABORT; - } - apdu_len += len; - } else if (decode_is_opening_tag_number(&apdu[apdu_len], 1)) { - apdu_len += 1; - pRecipient->tag = 1; - /* network-number Unsigned16 */ - tag_len = decode_tag_number_and_value( - &apdu[apdu_len], &tag_number, &len_value_type); - apdu_len += tag_len; - if ((unsigned)apdu_len > max_apdu) { - return BACNET_STATUS_ABORT; - } - if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { - return BACNET_STATUS_ABORT; - } - len = decode_unsigned( - &apdu[apdu_len], len_value_type, &unsigned_value); - pRecipient->type.address.net = (uint16_t)unsigned_value; - apdu_len += len; - if ((unsigned)apdu_len > max_apdu) { - return BACNET_STATUS_ABORT; - } - /* mac-address OCTET STRING */ - tag_len = decode_tag_number_and_value( - &apdu[apdu_len], &tag_number, &len_value_type); - apdu_len += tag_len; - if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) { - return BACNET_STATUS_ABORT; - } - len = bacnet_octet_string_decode( - &apdu[apdu_len], max_apdu - apdu_len, len_value_type, - &octet_string); - if (len < 0) { - return BACNET_STATUS_ERROR; - } - apdu_len += len; - if (octetstring_length(&octet_string) == 0) { - /* -- A string of length 0 indicates a broadcast */ - } else if (pRecipient->type.address.net) { - pRecipient->type.address.len = octetstring_copy_value( - &pRecipient->type.address.adr[0], - sizeof(pRecipient->type.address.adr), &octet_string); - } else { - pRecipient->type.address.mac_len = octetstring_copy_value( - &pRecipient->type.address.mac[0], - sizeof(pRecipient->type.address.mac), &octet_string); - } - if (!decode_is_closing_tag_number(&apdu[apdu_len], 1)) { - return BACNET_STATUS_ABORT; - } - apdu_len += 1; + list = list_head; + while (apdu_len < apdu_size) { + if (list) { + recipient = &list->recipient; } else { + recipient = NULL; + } + len = bacnet_recipient_decode( + &apdu[apdu_len], apdu_size - apdu_len, recipient); + if (len <= 0) { return BACNET_STATUS_ABORT; } - pRecipient = pRecipient->next; + apdu_len += len; + if (list) { + list = list->next; + } } return apdu_len; diff --git a/src/bacnet/timesync.h b/src/bacnet/timesync.h index 6d7fde6f..7526bff4 100644 --- a/src/bacnet/timesync.h +++ b/src/bacnet/timesync.h @@ -13,23 +13,7 @@ #include /* BACnet Stack defines - first */ #include "bacnet/bacdef.h" - -struct BACnet_Recipient_List; -typedef struct BACnet_Recipient_List { - /* - BACnetRecipient ::= CHOICE { - device [0] BACnetObjectIdentifier, - address [1] BACnetAddress - } - */ - uint8_t tag; - union { - BACNET_OBJECT_ID device; - BACNET_ADDRESS address; - } type; - /* simple linked list */ - struct BACnet_Recipient_List *next; -} BACNET_RECIPIENT_LIST; +#include "bacnet/bacdest.h" #ifdef __cplusplus extern "C" { @@ -37,6 +21,9 @@ extern "C" { /* encode service */ BACNET_STACK_EXPORT +int timesync_encode_apdu_service_parameters( + uint8_t *apdu, const BACNET_DATE *my_date, const BACNET_TIME *my_time); +BACNET_STACK_EXPORT int timesync_utc_encode_apdu( uint8_t *apdu, const BACNET_DATE *my_date, const BACNET_TIME *my_time); BACNET_STACK_EXPORT diff --git a/src/bacnet/version.h b/src/bacnet/version.h index 0e201d7a..b2e16841 100644 --- a/src/bacnet/version.h +++ b/src/bacnet/version.h @@ -15,7 +15,7 @@ #define BACNET_VERSION(x, y, z) (((x) << 16) + ((y) << 8) + (z)) #endif -#define BACNET_VERSION_TEXT "1.5.0.rc4" +#define BACNET_VERSION_TEXT "1.5.0.rc5" #define BACNET_VERSION_CODE BACNET_VERSION(1, 5, 0) #define BACNET_VERSION_MAJOR ((BACNET_VERSION_CODE >> 16) & 0xFF) #define BACNET_VERSION_MINOR ((BACNET_VERSION_CODE >> 8) & 0xFF) diff --git a/src/bacnet/wp.c b/src/bacnet/wp.c index 09b7bf28..13a328f0 100644 --- a/src/bacnet/wp.c +++ b/src/bacnet/wp.c @@ -198,21 +198,30 @@ int wp_decode_service_request( /* check for value pointers */ if (!apdu) { + if (wpdata) { + wpdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } return BACNET_STATUS_ERROR; } + if (wpdata) { + wpdata->error_code = ERROR_CODE_OTHER; + wpdata->array_index = BACNET_ARRAY_ALL; + wpdata->priority = BACNET_MAX_PRIORITY; + wpdata->application_data_len = 0; + } /* object-identifier [0] BACnetObjectIdentifier */ len = bacnet_object_id_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 0, &type, &instance); if (len > 0) { - if (instance > BACNET_MAX_INSTANCE) { - return BACNET_STATUS_ERROR; - } apdu_len += len; if (wpdata) { wpdata->object_type = type; wpdata->object_instance = instance; } } else { + if (wpdata) { + wpdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } return BACNET_STATUS_ERROR; } /* property-identifier [1] BACnetPropertyIdentifier */ @@ -224,6 +233,9 @@ int wp_decode_service_request( wpdata->object_property = (BACNET_PROPERTY_ID)property; } } else { + if (wpdata) { + wpdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } return BACNET_STATUS_ERROR; } /* property-array-index [2] Unsigned OPTIONAL */ @@ -235,7 +247,7 @@ int wp_decode_service_request( wpdata->array_index = unsigned_value; } } else { - /* wrong tag - skip apdu_len increment and go to next field */ + /* wrong tag, OPTIONAL, skip apdu_len increment and go to next field */ if (wpdata) { wpdata->array_index = BACNET_ARRAY_ALL; } @@ -243,20 +255,35 @@ int wp_decode_service_request( /* property-value [3] ABSTRACT-SYNTAX.&Type */ if (!bacnet_is_opening_tag_number( &apdu[apdu_len], apdu_size - apdu_len, 3, &len)) { + if (wpdata) { + wpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } return BACNET_STATUS_ERROR; } /* determine the length of the data blob */ imax = bacnet_enclosed_data_length(&apdu[apdu_len], apdu_size - apdu_len); - if (imax == BACNET_STATUS_ERROR) { + if (imax < 0) { + if (wpdata) { + wpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } return BACNET_STATUS_ERROR; } /* count the opening tag number length */ apdu_len += len; /* copy the data from the APDU */ - if (imax > MAX_APDU) { - /* not enough size in application_data to store the data chunk */ + if (imax > (apdu_size - apdu_len)) { + /* not enough bytes in APDU to avoid buffer overrun */ + if (wpdata) { + wpdata->error_code = ERROR_CODE_REJECT_BUFFER_OVERFLOW; + } return BACNET_STATUS_ERROR; - } else if (wpdata) { + } + if (wpdata) { + if (imax > sizeof(wpdata->application_data)) { + /* not enough bytes in application_data to store the data chunk */ + wpdata->error_code = ERROR_CODE_REJECT_BUFFER_OVERFLOW; + return BACNET_STATUS_ERROR; + } for (i = 0; i < imax; i++) { wpdata->application_data[i] = apdu[apdu_len + i]; } @@ -266,6 +293,9 @@ int wp_decode_service_request( apdu_len += imax; if (!bacnet_is_closing_tag_number( &apdu[apdu_len], apdu_size - apdu_len, 3, &len)) { + if (wpdata) { + wpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } return BACNET_STATUS_ERROR; } /* count the closing tag number length */ @@ -286,9 +316,17 @@ int wp_decode_service_request( wpdata->priority = (uint8_t)unsigned_value; } } else { + if (wpdata) { + wpdata->error_code = + ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE; + } return BACNET_STATUS_ERROR; } } else { + if (wpdata) { + wpdata->error_code = + ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } return BACNET_STATUS_ERROR; } } diff --git a/src/bacnet/wpm.c b/src/bacnet/wpm.c index 2d0061d6..eb15302b 100644 --- a/src/bacnet/wpm.c +++ b/src/bacnet/wpm.c @@ -38,43 +38,41 @@ * @return Count of decoded bytes. */ int wpm_decode_object_id( - const uint8_t *apdu, uint16_t apdu_len, BACNET_WRITE_PROPERTY_DATA *wp_data) + const uint8_t *apdu, + uint16_t apdu_size, + BACNET_WRITE_PROPERTY_DATA *wp_data) { - uint8_t tag_number = 0; - uint32_t len_value = 0; uint32_t object_instance = 0; BACNET_OBJECT_TYPE object_type = OBJECT_NONE; - uint16_t len = 0; + int len = 0, apdu_len = 0; - if (apdu && (apdu_len > 5)) { + /* check for valid pointer and minimum size */ + if (apdu && (apdu_size > 5)) { /* Context tag 0 - Object ID */ - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if ((tag_number == 0) && (apdu_len > len)) { - apdu_len -= len; - if (apdu_len >= 4) { - len += decode_object_id( - &apdu[len], &object_type, &object_instance); - if (wp_data) { - wp_data->object_type = object_type; - wp_data->object_instance = object_instance; - } - apdu_len -= len; - } else { - if (wp_data) { - wp_data->error_code = - ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; - } - return BACNET_STATUS_REJECT; - } - } else { + len = bacnet_object_id_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &object_type, + &object_instance); + if (len < 0) { if (wp_data) { wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; } return BACNET_STATUS_REJECT; + } else if (len == 0) { + if (wp_data) { + wp_data->error_code = + ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } + return BACNET_STATUS_REJECT; + } + apdu_len += len; + if (wp_data) { + wp_data->object_type = object_type; + wp_data->object_instance = object_instance; } /* just test for the next tag - no need to decode it here */ /* Context tag 1: sequence of BACnetPropertyValue */ - if (apdu_len && !decode_is_opening_tag_number(&apdu[len], 1)) { + if (!bacnet_is_opening_tag_number( + &apdu[len], apdu_size - apdu_len, 1, NULL)) { if (wp_data) { wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; } @@ -91,6 +89,17 @@ int wpm_decode_object_id( } /** Decoding for an object property. + * + * BACnetPropertyValue ::= SEQUENCE { + * property-identifier[0] BACnetPropertyIdentifier, + * property-array-index[1] Unsigned OPTIONAL, + * -- used only with array datatypes + * -- if omitted with an array the entire array is referenced + * property-value[2] ABSTRACT-SYNTAX.&Type, + * -- any datatype appropriate for the specified property + * priority[3] Unsigned (1..16) OPTIONAL + * -- used only when property is commandable + * } * * @param apdu [in] The contents of the APDU buffer. * @param apdu_len [in] The length of the APDU buffer. @@ -99,93 +108,126 @@ int wpm_decode_object_id( * @return Bytes decoded */ int wpm_decode_object_property( - const uint8_t *apdu, uint16_t apdu_len, BACNET_WRITE_PROPERTY_DATA *wp_data) + const uint8_t *apdu, + uint16_t apdu_size, + BACNET_WRITE_PROPERTY_DATA *wp_data) { - uint8_t tag_number = 0; - uint32_t len_value = 0; uint32_t enum_value = 0; BACNET_UNSIGNED_INTEGER unsigned_value = 0; - int len = 0, i = 0, imax = 0; + int len = 0, apdu_len = 0, i = 0, imax = 0; - if ((apdu) && (apdu_len) && (wp_data)) { - wp_data->array_index = BACNET_ARRAY_ALL; - wp_data->priority = BACNET_MAX_PRIORITY; - wp_data->application_data_len = 0; - /* tag 0 - Property Identifier */ - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if (tag_number == 0) { - len += decode_enumerated(&apdu[len], len_value, &enum_value); - wp_data->object_property = enum_value; - } else { - wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; - return BACNET_STATUS_REJECT; - } - - /* tag 1 - Property Array Index - optional */ - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if (tag_number == 1) { - len += decode_unsigned(&apdu[len], len_value, &unsigned_value); - wp_data->array_index = (uint32_t)unsigned_value; - - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - } - /* tag 2 - Property Value */ - if ((tag_number == 2) && (decode_is_opening_tag(&apdu[len - 1]))) { - len--; - imax = bacnet_enclosed_data_length(&apdu[len], apdu_len - len); - len++; - if (imax != BACNET_STATUS_ERROR) { - /* copy application data, check max length */ - if (imax > (apdu_len - len)) { - imax = (apdu_len - len); - } - for (i = 0; i < imax; i++) { - wp_data->application_data[i] = apdu[len + i]; - } - wp_data->application_data_len = imax; - len += imax; - if (len < apdu_len) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - /* closing tag 2 */ - if ((tag_number != 2) && - (decode_is_closing_tag(&apdu[len - 1]))) { - wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; - return BACNET_STATUS_REJECT; - } - } else { - wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; - return BACNET_STATUS_REJECT; - } - } else { - wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; - return BACNET_STATUS_REJECT; - } - } else { - wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; - return BACNET_STATUS_REJECT; - } - - /* tag 3 - Priority - optional */ - if (len < apdu_len) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value); - if (tag_number == 3) { - len += decode_unsigned(&apdu[len], len_value, &unsigned_value); - wp_data->priority = (uint8_t)unsigned_value; - } else { - len--; - } - } - } else { + if (!apdu || (apdu_size == 0)) { if (wp_data) { wp_data->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; } return BACNET_STATUS_REJECT; } + if (wp_data) { + wp_data->error_code = ERROR_CODE_OTHER; + wp_data->array_index = BACNET_ARRAY_ALL; + wp_data->priority = BACNET_MAX_PRIORITY; + wp_data->application_data_len = 0; + } + /* property-identifier[0] BACnetPropertyIdentifier */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &enum_value); + if (len <= 0) { + if (wp_data) { + wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + apdu_len += len; + if (wp_data) { + wp_data->object_property = enum_value; + } + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &unsigned_value); + if (len > 0) { + /* property-array-index [1] Unsigned OPTIONAL */ + apdu_len += len; + if (wp_data) { + wp_data->array_index = unsigned_value; + } + } else if (len == 0) { + /* optional - assume ALL array elements */ + if (wp_data) { + wp_data->array_index = BACNET_ARRAY_ALL; + } + } else { + if (wp_data) { + wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + /* property-value[2] ABSTRACT-SYNTAX.&Type */ + if (!bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, &len)) { + if (wp_data) { + wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + /* note: evaluate with the tag as the first octet */ + imax = bacnet_enclosed_data_length(&apdu[apdu_len], apdu_size - apdu_len); + if (imax == BACNET_STATUS_ERROR) { + if (wp_data) { + wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + /* add the tag length */ + apdu_len += len; + if (imax > (apdu_size - apdu_len)) { + /* bigger than the buffer we are reading from */ + if (wp_data) { + wp_data->error_code = ERROR_CODE_REJECT_BUFFER_OVERFLOW; + } + return BACNET_STATUS_REJECT; + } + if (wp_data) { + if (imax > sizeof(wp_data->application_data)) { + /* bigger than the buffer we are writing to */ + wp_data->error_code = ERROR_CODE_REJECT_BUFFER_OVERFLOW; + return BACNET_STATUS_REJECT; + } + for (i = 0; i < imax; i++) { + wp_data->application_data[i] = apdu[apdu_len + i]; + } + wp_data->application_data_len = imax; + } + /* add the application data length */ + apdu_len += imax; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, &len)) { + apdu_len += len; + } else { + if (wp_data) { + wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + /* priority[3] Unsigned (1..16) OPTIONAL */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, &unsigned_value); + if (len > 0) { + apdu_len += len; + if (wp_data) { + wp_data->priority = unsigned_value; + } + } else if (len == 0) { + /* OPTIONAL - no priority */ + if (wp_data) { + wp_data->priority = BACNET_NO_PRIORITY; + } + } else { + if (wp_data) { + wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } - return len; + return apdu_len; } /** @@ -442,39 +484,92 @@ int wpm_ack_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id) /** Encode an Error acknowledge in the APDU. * - * @param apdu [in] The APDU buffer. + * @param apdu [in] The APDU buffer, or NULL for length + * @param wp_data [in] Data of the invoked property. + * + * @return number of bytes encoded. + */ +int wpm_error_ack_service_encode( + uint8_t *apdu, const BACNET_WRITE_PROPERTY_DATA *wp_data) +{ + int len = 0, apdu_len = 0; + + len = encode_opening_tag(apdu, 0); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_enumerated(apdu, wp_data->error_class); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_enumerated(apdu, wp_data->error_code); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, 0); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_opening_tag(apdu, 1); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_context_object_id( + apdu, 0, wp_data->object_type, wp_data->object_instance); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_context_enumerated(apdu, 1, wp_data->object_property); + apdu_len += len; + if (apdu) { + apdu += len; + } + if (wp_data->array_index != BACNET_ARRAY_ALL) { + len = encode_context_unsigned(apdu, 2, wp_data->array_index); + apdu_len += len; + if (apdu) { + apdu += len; + } + } + len = encode_closing_tag(apdu, 1); + apdu_len += len; + + return apdu_len; +} + +/** Encode an Error acknowledge in the APDU. + * + * @param apdu [in] The APDU buffer, or NULL for length * @param invoke_id [in] Invoked service ID. * @param wp_data [in] Data of the invoked property. * - * @return Bytes encoded. + * @return number of bytes encoded. */ int wpm_error_ack_encode_apdu( uint8_t *apdu, uint8_t invoke_id, const BACNET_WRITE_PROPERTY_DATA *wp_data) { - int len = 0; + int len = 0, apdu_len = 0; if (apdu) { - apdu[len++] = PDU_TYPE_ERROR; - apdu[len++] = invoke_id; - apdu[len++] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE; - - len += encode_opening_tag(&apdu[len], 0); - len += encode_application_enumerated(&apdu[len], wp_data->error_class); - len += encode_application_enumerated(&apdu[len], wp_data->error_code); - len += encode_closing_tag(&apdu[len], 0); - - len += encode_opening_tag(&apdu[len], 1); - len += encode_context_object_id( - &apdu[len], 0, wp_data->object_type, wp_data->object_instance); - len += - encode_context_enumerated(&apdu[len], 1, wp_data->object_property); - - if (wp_data->array_index != BACNET_ARRAY_ALL) { - len += encode_context_unsigned(&apdu[len], 2, wp_data->array_index); - } - len += encode_closing_tag(&apdu[len], 1); + apdu[0] = PDU_TYPE_ERROR; + apdu[1] = invoke_id; + apdu[2] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE; } - return len; + len = 3; + apdu_len += len; + if (apdu) { + apdu += len; + } + len = wpm_error_ack_service_encode(apdu, wp_data); + apdu_len += len; + + return apdu_len; } #if !BACNET_SVC_SERVER @@ -498,91 +593,54 @@ int wpm_error_ack_decode_apdu( BACNET_WRITE_PROPERTY_DATA *wp_data) { int len = 0, apdu_len = 0; - const uint8_t *apdu_offset = NULL; BACNET_ERROR_CLASS error_class = ERROR_CLASS_SERVICES; BACNET_ERROR_CODE error_code = ERROR_CODE_SUCCESS; BACNET_OBJECT_PROPERTY_REFERENCE value; - if (apdu) { - apdu_offset = apdu; + if (!apdu || (apdu_size == 0)) { + if (wp_data) { + wp_data->error_class = ERROR_CLASS_SERVICES; + wp_data->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } + return 0; } if (wp_data) { wp_data->error_class = ERROR_CLASS_SERVICES; wp_data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE; } /* Context tag 0 - Error */ - if (apdu_size == 0) { - return 0; - } - if (decode_is_opening_tag_number(apdu_offset, 0)) { - len = 1; + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, &len)) { apdu_len += len; - if (apdu) { - apdu_offset = &apdu[apdu_len]; - if (apdu_size > len) { - apdu_size -= len; - } else { - return 0; - } - } } else { return 0; } len = bacerror_decode_error_class_and_code( - apdu_offset, apdu_size, &error_class, &error_code); + &apdu[apdu_len], apdu_size - apdu_len, &error_class, &error_code); if (len > 0) { if (wp_data) { wp_data->error_class = error_class; wp_data->error_code = error_code; } apdu_len += len; - if (apdu) { - apdu_offset = &apdu[apdu_len]; - if (apdu_size > len) { - apdu_size -= len; - } else { - return 0; - } - } } else { return 0; } - if (apdu_size == 0) { - return 0; - } - if (decode_is_closing_tag_number(apdu_offset, 0)) { - len = 1; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, &len)) { apdu_len += len; - if (apdu) { - apdu_offset = &apdu[apdu_len]; - if (apdu_size > len) { - apdu_size -= len; - } else { - return 0; - } - } } else { return 0; } /* Context tag 1 - BACnetObjectPropertyReference */ - if (apdu_size == 0) { - return 0; - } - if (decode_is_opening_tag_number(apdu_offset, 1)) { - len = 1; + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 1, &len)) { apdu_len += len; - if (apdu) { - apdu_offset = &apdu[apdu_len]; - if (apdu_size > len) { - apdu_size -= len; - } else { - return 0; - } - } } else { return 0; } - len = bacapp_decode_obj_property_ref(apdu_offset, apdu_size, &value); + len = bacapp_decode_obj_property_ref( + &apdu[apdu_len], apdu_size - apdu_len, &value); if (len > 0) { if (wp_data) { wp_data->object_instance = value.object_identifier.instance; @@ -591,22 +649,11 @@ int wpm_error_ack_decode_apdu( wp_data->array_index = value.property_array_index; } apdu_len += len; - if (apdu) { - apdu_offset = &apdu[apdu_len]; - if (apdu_size > len) { - apdu_size -= len; - } else { - return 0; - } - } } else { return 0; } - if (apdu_size == 0) { - return 0; - } - if (decode_is_closing_tag_number(apdu_offset, 1)) { - len = 1; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 1, &len)) { apdu_len += len; } else { return 0; diff --git a/src/bacnet/wpm.h b/src/bacnet/wpm.h index 7afb52db..cf8587b6 100644 --- a/src/bacnet/wpm.h +++ b/src/bacnet/wpm.h @@ -70,6 +70,9 @@ int wpm_encode_apdu( BACNET_STACK_EXPORT int wpm_ack_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id); +BACNET_STACK_EXPORT +int wpm_error_ack_service_encode( + uint8_t *apdu, const BACNET_WRITE_PROPERTY_DATA *wp_data); BACNET_STACK_EXPORT int wpm_error_ack_encode_apdu( uint8_t *apdu, diff --git a/test/bacnet/ptransfer/src/main.c b/test/bacnet/ptransfer/src/main.c index 34a7c2b1..d99ddc54 100644 --- a/test/bacnet/ptransfer/src/main.c +++ b/test/bacnet/ptransfer/src/main.c @@ -20,12 +20,11 @@ */ static int ptransfer_decode_apdu( uint8_t *apdu, - unsigned apdu_len, + unsigned apdu_size, uint8_t *invoke_id, BACNET_PRIVATE_TRANSFER_DATA *private_data) { - int len = 0; - unsigned offset = 0; + int len = 0, apdu_len = 0; if (!apdu) { return -1; @@ -40,23 +39,27 @@ static int ptransfer_decode_apdu( if (apdu[3] != SERVICE_CONFIRMED_PRIVATE_TRANSFER) { return -1; } - offset = 4; + apdu_len = 4; - if (apdu_len > offset) { + if (apdu_size > apdu_len) { len = ptransfer_decode_service_request( - &apdu[offset], apdu_len - offset, private_data); + &apdu[apdu_len], apdu_size - apdu_len, private_data); + if (len < 0) { + return -1; + } + apdu_len += len; } - return len; + return apdu_len; } static int uptransfer_decode_apdu( uint8_t *apdu, - unsigned apdu_len, + unsigned apdu_size, BACNET_PRIVATE_TRANSFER_DATA *private_data) { int len = 0; - unsigned offset = 0; + unsigned apdu_len = 0; if (!apdu) { return -1; @@ -68,13 +71,17 @@ static int uptransfer_decode_apdu( if (apdu[1] != SERVICE_UNCONFIRMED_PRIVATE_TRANSFER) { return -1; } - offset = 2; - if (apdu_len > offset) { + apdu_len = 2; + if (apdu_size > apdu_len) { len = ptransfer_decode_service_request( - &apdu[offset], apdu_len - offset, private_data); + &apdu[apdu_len], apdu_size - apdu_len, private_data); + if (len < 0) { + return -1; + } + apdu_len += len; } - return len; + return apdu_len; } static int ptransfer_ack_decode_apdu( @@ -257,7 +264,7 @@ static void test_Private_Transfer_Request(void) { uint8_t apdu[480] = { 0 }; uint8_t test_value[480] = { 0 }; - int len = 0; + int len = 0, test_len = 0; int apdu_len = 0; uint8_t invoke_id = 128; uint8_t test_invoke_id = 0; @@ -281,12 +288,12 @@ static void test_Private_Transfer_Request(void) private_data.serviceParameters = &test_value[0]; private_data.serviceParametersLen = private_data_len; - len = ptransfer_encode_apdu(&apdu[0], invoke_id, &private_data); - zassert_not_equal(len, 0, NULL); - apdu_len = len; - len = + apdu_len = ptransfer_encode_apdu(&apdu[0], invoke_id, &private_data); + zassert_not_equal(apdu_len, 0, NULL); + test_len = ptransfer_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data); - zassert_not_equal(len, -1, NULL); + zassert_not_equal(test_len, -1, NULL); + zassert_equal(apdu_len, test_len, NULL); zassert_equal(test_data.vendorID, private_data.vendorID, NULL); zassert_equal(test_data.serviceNumber, private_data.serviceNumber, NULL); zassert_equal( @@ -295,6 +302,7 @@ static void test_Private_Transfer_Request(void) len = bacapp_decode_application_data( test_data.serviceParameters, test_data.serviceParametersLen, &test_data_value); + zassert_not_equal(len, -1, NULL); zassert_true(bacapp_same_value(&data_value, &test_data_value), NULL); return; @@ -309,7 +317,7 @@ static void test_Unconfirmed_Private_Transfer_Request(void) uint8_t apdu[480] = { 0 }; uint8_t test_value[480] = { 0 }; int len = 0; - int apdu_len = 0; + int apdu_len = 0, test_len = 0; int private_data_len = 0; char private_data_chunk[32] = { "I Love You, Patricia!" }; BACNET_APPLICATION_DATA_VALUE data_value = { 0 }; @@ -330,11 +338,12 @@ static void test_Unconfirmed_Private_Transfer_Request(void) private_data.serviceParameters = &test_value[0]; private_data.serviceParametersLen = private_data_len; - len = uptransfer_encode_apdu(&apdu[0], &private_data); - zassert_not_equal(len, 0, NULL); - apdu_len = len; - len = uptransfer_decode_apdu(&apdu[0], apdu_len, &test_data); - zassert_not_equal(len, -1, NULL); + apdu_len = uptransfer_encode_apdu(&apdu[0], &private_data); + zassert_not_equal(apdu_len, 0, NULL); + test_len = uptransfer_decode_apdu(&apdu[0], apdu_len, &test_data); + zassert_not_equal(test_len, -1, NULL); + zassert_equal( + apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); zassert_equal(test_data.vendorID, private_data.vendorID, NULL); zassert_equal(test_data.serviceNumber, private_data.serviceNumber, NULL); zassert_equal( @@ -343,6 +352,7 @@ static void test_Unconfirmed_Private_Transfer_Request(void) len = bacapp_decode_application_data( test_data.serviceParameters, test_data.serviceParametersLen, &test_data_value); + zassert_not_equal(len, -1, NULL); zassert_true(bacapp_same_value(&data_value, &test_data_value), NULL); return; diff --git a/test/bacnet/timesync/src/main.c b/test/bacnet/timesync/src/main.c index 63475357..27d3d96d 100644 --- a/test/bacnet/timesync/src/main.c +++ b/test/bacnet/timesync/src/main.c @@ -17,52 +17,23 @@ /** * @brief Test */ -static void testTimeSyncRecipientData( - const BACNET_RECIPIENT_LIST *recipient1, - const BACNET_RECIPIENT_LIST *recipient2) +static void testTimeSyncRecipientListData( + const BACNET_RECIPIENT_LIST *list_head_a, + const BACNET_RECIPIENT_LIST *list_head_b) { - unsigned i = 0; + bool status = false; + const BACNET_RECIPIENT_LIST *list_a; + const BACNET_RECIPIENT_LIST *list_b; - if (recipient1 && recipient2) { - zassert_equal(recipient1->tag, recipient2->tag, NULL); - if (recipient1->tag == 0) { - zassert_equal( - recipient1->type.device.type, recipient2->type.device.type, - NULL); - zassert_equal( - recipient1->type.device.instance, - recipient2->type.device.instance, NULL); - } else if (recipient1->tag == 1) { - zassert_equal( - recipient1->type.address.net, recipient2->type.address.net, - NULL); - if (recipient1->type.address.net == BACNET_BROADCAST_NETWORK) { - zassert_equal( - recipient1->type.address.mac_len, - recipient2->type.address.mac_len, NULL); - } else if (recipient1->type.address.net) { - zassert_equal( - recipient1->type.address.len, recipient2->type.address.len, - NULL); - for (i = 0; i < recipient1->type.address.len; i++) { - zassert_equal( - recipient1->type.address.adr[i], - recipient2->type.address.adr[i], NULL); - } - } else { - zassert_equal( - recipient1->type.address.mac_len, - recipient2->type.address.mac_len, NULL); - for (i = 0; i < recipient1->type.address.mac_len; i++) { - zassert_equal( - recipient1->type.address.mac[i], - recipient2->type.address.mac[i], NULL); - } - } - } else { - zassert_true(recipient1->tag <= 1, NULL); - } + list_a = list_head_a; + list_b = list_head_b; + while (list_a && list_b) { + status = bacnet_recipient_same(&list_a->recipient, &list_b->recipient); + zassert_true(status, NULL); + list_a = list_a->next; + list_b = list_b->next; } + zassert_true(list_a == NULL && list_b == NULL, NULL); } #if defined(CONFIG_ZTEST_NEW_API) @@ -72,54 +43,66 @@ static void testTimeSyncRecipient(void) #endif { uint8_t apdu[480] = { 0 }; - int len = 0; + int apdu_len = 0, test_len = 0; BACNET_RECIPIENT_LIST recipient[4] = { 0 }; BACNET_RECIPIENT_LIST test_recipient[4] = { 0 }; /* link the recipient list */ - recipient[0].next = &recipient[1]; - recipient[1].next = &recipient[2]; - recipient[2].next = &recipient[3]; - recipient[3].next = NULL; - /* link the test recipient list */ - test_recipient[0].next = &test_recipient[1]; - test_recipient[1].next = &test_recipient[2]; - test_recipient[2].next = &test_recipient[3]; - test_recipient[3].next = NULL; + bacnet_recipient_list_link_array(recipient, ARRAY_SIZE(recipient)); + bacnet_recipient_list_link_array( + test_recipient, ARRAY_SIZE(test_recipient)); /* load the test data - device */ - recipient[0].tag = 0; - recipient[0].type.device.type = OBJECT_DEVICE; - recipient[0].type.device.instance = 1234; + recipient[0].recipient.tag = BACNET_RECIPIENT_TAG_DEVICE; + recipient[0].recipient.type.device.type = OBJECT_DEVICE; + recipient[0].recipient.type.device.instance = 1234; /* load the test data - address */ /* network = broadcast */ - recipient[1].tag = 1; - recipient[1].type.address.net = BACNET_BROADCAST_NETWORK; - recipient[1].type.address.mac_len = 0; + recipient[1].recipient.tag = BACNET_RECIPIENT_TAG_ADDRESS; + recipient[1].recipient.type.address.net = BACNET_BROADCAST_NETWORK; + recipient[1].recipient.type.address.mac_len = 0; /* network = non-zero */ - recipient[2].tag = 1; - recipient[2].type.address.net = 4201; - recipient[2].type.address.adr[0] = 127; - recipient[2].type.address.len = 1; + recipient[2].recipient.tag = BACNET_RECIPIENT_TAG_ADDRESS; + recipient[2].recipient.type.address.net = 4201; + recipient[2].recipient.type.address.adr[0] = 127; + recipient[2].recipient.type.address.len = 1; /* network = zero */ - recipient[3].tag = 1; - recipient[3].type.address.net = 0; - recipient[3].type.address.mac[0] = 10; - recipient[3].type.address.mac[1] = 1; - recipient[3].type.address.mac[2] = 0; - recipient[3].type.address.mac[3] = 86; - recipient[3].type.address.mac[4] = 0xBA; - recipient[3].type.address.mac[5] = 0xC1; - recipient[3].type.address.mac_len = 6; + recipient[3].recipient.tag = BACNET_RECIPIENT_TAG_ADDRESS; + recipient[3].recipient.type.address.net = 0; + recipient[3].recipient.type.address.mac[0] = 10; + recipient[3].recipient.type.address.mac[1] = 1; + recipient[3].recipient.type.address.mac[2] = 0; + recipient[3].recipient.type.address.mac[3] = 86; + recipient[3].recipient.type.address.mac[4] = 0xBA; + recipient[3].recipient.type.address.mac[5] = 0xC1; + recipient[3].recipient.type.address.mac_len = 6; /* perform positive test */ - len = timesync_encode_timesync_recipients( + apdu_len = timesync_encode_timesync_recipients( &apdu[0], sizeof(apdu), &recipient[0]); - zassert_not_equal(len, BACNET_STATUS_ABORT, NULL); - zassert_true(len > 0, NULL); - len = timesync_decode_timesync_recipients( - &apdu[0], sizeof(apdu), &test_recipient[0]); - zassert_not_equal(len, BACNET_STATUS_ABORT, NULL); - zassert_true(len > 0, NULL); - testTimeSyncRecipientData(&recipient[0], &test_recipient[0]); + zassert_not_equal(apdu_len, BACNET_STATUS_ABORT, NULL); + zassert_true(apdu_len > 0, NULL); + test_len = timesync_decode_timesync_recipients( + &apdu[0], apdu_len, &test_recipient[0]); + zassert_equal(apdu_len, test_len, NULL); + zassert_true(test_len > 0, NULL); + testTimeSyncRecipientListData(&recipient[0], &test_recipient[0]); + while (apdu_len) { + apdu_len--; + test_len = timesync_decode_timesync_recipients( + &apdu[0], apdu_len, &test_recipient[0]); + if ((apdu_len == 18) || (apdu_len == 11) || (apdu_len == 5)) { + /* some valid shorter lists */ + zassert_equal(test_len, apdu_len, NULL); + } else { + zassert_equal( + test_len, BACNET_STATUS_ABORT, "apdu_len=%d test_len=%d", + apdu_len, test_len); + } + } + /* negative test */ + apdu_len = timesync_encode_timesync_recipients( + &apdu[0], sizeof(apdu), &recipient[0]); + test_len = timesync_decode_timesync_recipients(&apdu[0], apdu_len, NULL); + zassert_equal(test_len, apdu_len, NULL); } static int timesync_decode_apdu_service( diff --git a/test/bacnet/wpm/src/main.c b/test/bacnet/wpm/src/main.c index a17474f3..3a725af8 100644 --- a/test/bacnet/wpm/src/main.c +++ b/test/bacnet/wpm/src/main.c @@ -54,7 +54,7 @@ static void testWritePropertyMultiple(void) BACNET_WRITE_PROPERTY_DATA wp_data = { 0 }; uint8_t invoke_id = 1; uint8_t test_invoke_id = 0; - int apdu_len = 0; + int apdu_len = 0, null_len = 0; int len = 0; uint8_t apdu[480] = { 0 }; int offset = 0; @@ -125,8 +125,11 @@ static void testWritePropertyMultiple(void) (3) an optional 'Property Array Index' (4) a 'Property Value' (5) an optional 'Priority' */ + null_len = wpm_decode_object_property( + &apdu[offset], apdu_len - offset, NULL); len = wpm_decode_object_property( &apdu[offset], apdu_len - offset, &wp_data); + zassert_equal(len, null_len, NULL); zassert_not_equal(len, 0, NULL); offset += len; printf( @@ -148,6 +151,43 @@ static void testWritePropertyMultiple(void) } while (tag_number != 1); } while (offset < apdu_len); } + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(wp_tests, testWritePropertyMultiple_ACK) +#else +static void testWritePropertyMultiple_ACK(void) +#endif +{ + uint8_t invoke_id = 1; + int len = 0, apdu_len = 0, null_len = 0; + uint8_t apdu[480] = { 0 }; + BACNET_WRITE_PROPERTY_DATA data = { 0 }, test_data = { 0 }; + + len = wpm_ack_encode_apdu_init(apdu, invoke_id); + zassert_equal(len, 3, NULL); + zassert_equal(apdu[0], PDU_TYPE_SIMPLE_ACK, NULL); + zassert_equal(apdu[1], invoke_id, NULL); + + null_len = wpm_error_ack_encode_apdu(NULL, invoke_id, &data); + apdu_len = wpm_error_ack_encode_apdu(apdu, invoke_id, &data); + zassert_not_equal(apdu_len, BACNET_STATUS_ERROR, NULL); + zassert_equal(apdu_len, null_len, NULL); + zassert_equal(apdu[0], PDU_TYPE_ERROR, NULL); + zassert_equal(apdu[1], invoke_id, NULL); + + null_len = wpm_error_ack_service_encode(NULL, &data); + apdu_len = wpm_error_ack_service_encode(apdu, &data); + zassert_not_equal(apdu_len, BACNET_STATUS_ERROR, NULL); + len = wpm_error_ack_decode_apdu(apdu, apdu_len, &test_data); + zassert_not_equal(len, BACNET_STATUS_ERROR, NULL); + zassert_equal( + test_data.error_class, data.error_class, "class=%u test-class=%u", + test_data.error_class, data.error_class); + zassert_equal( + test_data.error_code, data.error_code, "code=%u test-code=%u", + test_data.error_code, data.error_code); +} + /** * @} */ @@ -157,7 +197,9 @@ ZTEST_SUITE(wp_tests, NULL, NULL, NULL, NULL, NULL); #else void test_main(void) { - ztest_test_suite(wp_tests, ztest_unit_test(testWritePropertyMultiple)); + ztest_test_suite( + wp_tests, ztest_unit_test(testWritePropertyMultiple), + ztest_unit_test(testWritePropertyMultiple_ACK)); ztest_run_test_suite(wp_tests); }