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); }