Secure ReadProperty decoding and BACnetActionCommand (#702)
* Refactored and secured BACnetActionCommand codec into bacaction.c module for command object and added to bacapp module encode/decode with define for enabling and pseudo application tag for internal use. * Simplified bacapp_data_len() and moved into bacdcode module as bacnet_enclosed_data_len() function. * Secured ReadProperty-REQUEST and -ACK decoding. * Removed deprecated Keylist_Key() functions from usage. * Removed pseudo application datatypes from bacapp_data_decode() which only uses primitive application tag encoded values. * Defined INT_MAX when it is not already defined by compiler or libc. * Deprecated bacapp_decode_application_data_len() and bacapp_decode_context_data_len() as they are no longer used in any code in the library. * Added BACnetScale to bacapp module. Improved complex property value decoding. Refactored bacapp_decode_known_property() function. * Refactored and improved the bacapp_snprintf() function for printing EPICS. * Fixed Lighting Output WriteProperty to handle known property decoding.
This commit is contained in:
+139
-114
@@ -120,73 +120,88 @@ int rp_encode_apdu(
|
||||
/** Decode the service request only
|
||||
*
|
||||
* @param apdu Pointer to the buffer for encoding.
|
||||
* @param apdu_len Count of valid bytes in the buffer.
|
||||
* @param apdu_size Count of valid bytes in the buffer.
|
||||
* @param rpdata Pointer to the property data to be encoded.
|
||||
*
|
||||
* @return Bytes decoded or zero on error.
|
||||
* @return number of bytes decoded, or #BACNET_STATUS_REJECT
|
||||
*/
|
||||
int rp_decode_service_request(
|
||||
uint8_t *apdu, unsigned apdu_len, BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
uint8_t *apdu, unsigned apdu_size, BACNET_READ_PROPERTY_DATA *data)
|
||||
{
|
||||
unsigned len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value_type = 0;
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint32_t instance = 0;
|
||||
BACNET_OBJECT_TYPE type = OBJECT_NONE; /* for decoding */
|
||||
uint32_t property = 0; /* for decoding */
|
||||
BACNET_UNSIGNED_INTEGER unsigned_value = 0; /* for decoding */
|
||||
|
||||
/* check for value pointers */
|
||||
if (rpdata) {
|
||||
/* 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) {
|
||||
rpdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
|
||||
return BACNET_STATUS_REJECT;
|
||||
if (!apdu) {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
|
||||
}
|
||||
|
||||
/* Tag 0: Object ID */
|
||||
if (!decode_is_context_tag(&apdu[len++], 0)) {
|
||||
rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
len += decode_object_id(&apdu[len], &type, &rpdata->object_instance);
|
||||
rpdata->object_type = type;
|
||||
/* Tag 1: Property ID */
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
if (tag_number != 1) {
|
||||
rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
len += decode_enumerated(&apdu[len], len_value_type, &property);
|
||||
rpdata->object_property = (BACNET_PROPERTY_ID)property;
|
||||
/* Tag 2: Optional Array Index */
|
||||
if (len < apdu_len) {
|
||||
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);
|
||||
rpdata->array_index = (BACNET_ARRAY_INDEX)unsigned_value;
|
||||
} else {
|
||||
rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
return BACNET_STATUS_REJECT;
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
/* 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) {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
rpdata->array_index = BACNET_ARRAY_ALL;
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
if (data) {
|
||||
data->object_type = type;
|
||||
data->object_instance = instance;
|
||||
}
|
||||
} else {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
/* property-identifier [1] BACnetPropertyIdentifier */
|
||||
len = bacnet_enumerated_context_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 1, &property);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
if (data) {
|
||||
data->object_property = (BACNET_PROPERTY_ID)property;
|
||||
}
|
||||
} else {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
/* property-array-index [2] Unsigned OPTIONAL */
|
||||
len = bacnet_unsigned_context_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 2, &unsigned_value);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
if (data) {
|
||||
data->array_index = unsigned_value;
|
||||
}
|
||||
} else {
|
||||
/* wrong tag - skip apdu_len increment and go to next field */
|
||||
if (data) {
|
||||
data->array_index = BACNET_ARRAY_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
if (len < apdu_len) {
|
||||
if (apdu_len < apdu_size) {
|
||||
/* If something left over now, we have an invalid request */
|
||||
if (rpdata) {
|
||||
rpdata->error_code = ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS;
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
|
||||
return (int)len;
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode APDU for ReadProperty-ACK
|
||||
*
|
||||
@@ -345,7 +360,7 @@ int rp_ack_encode_apdu_object_property_end(uint8_t *apdu)
|
||||
|
||||
/** Encode the acknowledge.
|
||||
*
|
||||
* @param apdu Pointer to the buffer for encoding.
|
||||
* @param apdu Pointer to the buffer for encoding, or NULL for length
|
||||
* @param invoke_id Invoke Id
|
||||
* @param rpdata Pointer to the property data to be encoded.
|
||||
*
|
||||
@@ -389,85 +404,95 @@ int rp_ack_encode_apdu(
|
||||
* the application_data field points into the apdu buffer (is not allocated).
|
||||
*
|
||||
* @param apdu [in] The apdu portion of the ACK reply.
|
||||
* @param apdu_len [in] The total length of the apdu.
|
||||
* @param apdu_size [in] The total length of the apdu.
|
||||
* @param rpdata [out] The structure holding the partially decoded result.
|
||||
* @return Number of decoded bytes (could be less than apdu_len),
|
||||
* or -1 on decoding error.
|
||||
*/
|
||||
int rp_ack_decode_service_request(uint8_t *apdu,
|
||||
int apdu_len, /* total length of the apdu */
|
||||
BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
int apdu_size,
|
||||
BACNET_READ_PROPERTY_DATA *data)
|
||||
{
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value_type = 0;
|
||||
int tag_len = 0; /* length of tag decode */
|
||||
int len = 0; /* total length of decodes */
|
||||
BACNET_OBJECT_TYPE object_type = OBJECT_NONE; /* object type */
|
||||
int apdu_len = 0; /* return value */
|
||||
int len = 0;
|
||||
uint32_t instance = 0;
|
||||
BACNET_OBJECT_TYPE type = OBJECT_NONE; /* for decoding */
|
||||
uint32_t property = 0; /* for decoding */
|
||||
BACNET_UNSIGNED_INTEGER unsigned_value = 0; /* for decoding */
|
||||
int data_len = 0;
|
||||
|
||||
/* Check basics. */
|
||||
if (apdu && (apdu_len >= 8 /*minimum*/)) {
|
||||
/* Tag 0: Object ID */
|
||||
if (!decode_is_context_tag(&apdu[0], 0)) {
|
||||
return -1;
|
||||
if (!apdu) {
|
||||
return -BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
len = 1;
|
||||
len += decode_object_id(
|
||||
&apdu[len], &object_type, &rpdata->object_instance);
|
||||
rpdata->object_type = object_type;
|
||||
/* Tag 1: Property ID */
|
||||
if (len >= apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
if (tag_number != 1) {
|
||||
return -1;
|
||||
}
|
||||
if (len >= apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_enumerated(&apdu[len], len_value_type, &property);
|
||||
rpdata->object_property = (BACNET_PROPERTY_ID)property;
|
||||
/* Tag 2: Optional Array Index */
|
||||
if (len >= apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
if (tag_number == 2) {
|
||||
len += tag_len;
|
||||
len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
|
||||
rpdata->array_index = (BACNET_ARRAY_INDEX)unsigned_value;
|
||||
} else {
|
||||
rpdata->array_index = BACNET_ARRAY_ALL;
|
||||
}
|
||||
/* Tag 3: opening context tag */
|
||||
if (len >= apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
if (decode_is_opening_tag_number(&apdu[len], 3)) {
|
||||
/* a tag number of 3 is not extended so only one octet */
|
||||
len++;
|
||||
/* don't decode the application tag number or its data here */
|
||||
rpdata->application_data = &apdu[len];
|
||||
/* Just to ensure we do not create a wrapped over value here. */
|
||||
if (len < apdu_len) {
|
||||
rpdata->application_data_len =
|
||||
apdu_len - len - 1 /*closing tag */;
|
||||
} else {
|
||||
rpdata->application_data_len = 0;
|
||||
}
|
||||
/* len includes the data and the closing tag */
|
||||
len = apdu_len;
|
||||
} else {
|
||||
return -1;
|
||||
apdu_len += len;
|
||||
if (data) {
|
||||
data->object_type = type;
|
||||
data->object_instance = instance;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* property-identifier [1] BACnetPropertyIdentifier */
|
||||
len = bacnet_enumerated_context_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 1, &property);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
if (data) {
|
||||
data->object_property = (BACNET_PROPERTY_ID)property;
|
||||
}
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* property-array-index [2] Unsigned OPTIONAL */
|
||||
len = bacnet_unsigned_context_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 2, &unsigned_value);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
if (data) {
|
||||
data->array_index = unsigned_value;
|
||||
}
|
||||
} else {
|
||||
/* wrong tag - skip apdu_len increment and go to next field */
|
||||
if (data) {
|
||||
data->array_index = BACNET_ARRAY_ALL;
|
||||
}
|
||||
}
|
||||
/* property-value [3] ABSTRACT-SYNTAX.&Type */
|
||||
if (!bacnet_is_opening_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 3, &len)) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* determine the length of the data blob */
|
||||
data_len = bacnet_enclosed_data_length(&apdu[apdu_len],
|
||||
apdu_size - apdu_len);
|
||||
if (data_len == BACNET_STATUS_ERROR) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* count the opening tag number length */
|
||||
apdu_len += len;
|
||||
if (data_len > MAX_APDU) {
|
||||
/* not enough size in application_data to store the data chunk */
|
||||
return BACNET_STATUS_ERROR;
|
||||
} else if (data) {
|
||||
/* don't decode the application tag number or its data here */
|
||||
data->application_data = &apdu[apdu_len];
|
||||
data->application_data_len = data_len;
|
||||
}
|
||||
apdu_len += data_len;
|
||||
if (!bacnet_is_closing_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 3, &len)) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* count the closing tag number length */
|
||||
apdu_len += len;
|
||||
|
||||
return len;
|
||||
return apdu_len;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user