int rpm_ack_decode_object_property( uint8_t * apdu, unsigned apdu_len, BACNET_PROPERTY_ID * object_property, uint32_t * array_index) { unsigned len = 0; unsigned tag_len = 0; uint8_t tag_number = 0; uint32_t len_value_type = 0; uint32_t property = 0; /* for decoding */ uint32_t array_value = 0; /* for decoding */ /* check for valid pointers */ if (apdu && apdu_len && object_property && array_index) { /* Tag 2: propertyIdentifier */ if (!IS_CONTEXT_SPECIFIC(apdu[len])) return -1; len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); if (tag_number != 2) return -1; len += decode_enumerated(&apdu[len], len_value_type, &property); if (object_property) *object_property = (BACNET_PROPERTY_ID) property; /* Tag 3: Optional propertyArrayIndex */ if ((len < apdu_len) && IS_CONTEXT_SPECIFIC(apdu[len]) && (!IS_CLOSING_TAG(apdu[len]))) { tag_len = (unsigned) decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); if (tag_number == 3) { len += tag_len; len += decode_unsigned(&apdu[len], len_value_type, &array_value); *array_index = array_value; } else { *array_index = BACNET_ARRAY_ALL; } } else { *array_index = BACNET_ARRAY_ALL; } } return (int) len; }
/** Decode the received RP data into a linked list of the results, with the * same data structure used by RPM ACK replies. * This function is provided to provide common handling for RP and RPM data, * and fully decodes the value(s) portion of the data for one property. * @ingroup DSRP * @see rp_ack_decode_service_request(), rpm_ack_decode_service_request() * * @param apdu [in] The received apdu data. * @param apdu_len [in] Total length of the apdu. * @param read_access_data [out] Pointer to the head of the linked list * where the RP data is to be stored. * @return Number of decoded bytes (could be less than apdu_len), * or -1 on decoding error. */ int rp_ack_fully_decode_service_request( uint8_t * apdu, int apdu_len, BACNET_READ_ACCESS_DATA * read_access_data) { int decoded_len = 0; /* return value */ BACNET_READ_PROPERTY_DATA rp1data; BACNET_PROPERTY_REFERENCE *rp1_property; /* single property */ BACNET_APPLICATION_DATA_VALUE *value, *old_value; uint8_t *vdata; int vlen, len; decoded_len = rp_ack_decode_service_request(apdu, apdu_len, &rp1data); if (decoded_len > 0) { /* Then we have to transfer to the BACNET_READ_ACCESS_DATA structure * and decode the value(s) portion */ read_access_data->object_type = rp1data.object_type; read_access_data->object_instance = rp1data.object_instance; rp1_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); read_access_data->listOfProperties = rp1_property; if (rp1_property == NULL) { /* can't proceed if calloc failed. */ return BACNET_STATUS_ERROR; } rp1_property->propertyIdentifier = rp1data.object_property; rp1_property->propertyArrayIndex = rp1data.array_index; /* Is there no Error case possible here, as there is when decoding RPM? */ /* rp1_property->error.error_class = ?? */ /* rp_ack_decode_service_request() processing already removed the * Opening and Closing '3' Tags. * note: if this is an array, there will be more than one element to decode */ vdata = rp1data.application_data; vlen = rp1data.application_data_len; value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE)); rp1_property->value = value; old_value = value; while (value && vdata && (vlen > 0)) { if (IS_CONTEXT_SPECIFIC(*vdata)) { len = bacapp_decode_context_data(vdata, vlen, value, rp1_property->propertyIdentifier); } else { len = bacapp_decode_application_data(vdata, vlen, value); } if (len < 0) { /* unable to decode the data */ while (value) { /* free the linked list of values */ old_value = value; value = value->next; free(old_value); } free(rp1_property); read_access_data->listOfProperties = NULL; return len; } decoded_len += len; vlen -= len; vdata += len; /* If unexpected closing tag here: */ if (vlen && decode_is_closing_tag_number(vdata, 3)) { decoded_len++; vlen--; vdata++; break; } else { if (len == 0) { /* nothing decoded and no closing tag, so malformed */ while (value) { /* free the linked list of values */ old_value = value; value = value->next; free(old_value); } free(rp1_property); read_access_data->listOfProperties = NULL; return BACNET_STATUS_ERROR; } if (vlen > 0) { /* If more values */ old_value = value; value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE)); old_value->next = value; } } } } return decoded_len; }
/** Decode the received RPM data and make a linked list of the results. * @ingroup DSRPM * * @param apdu [in] The received apdu data. * @param apdu_len [in] Total length of the apdu. * @param read_access_data [out] Pointer to the head of the linked list * where the RPM data is to be stored. * @return The number of bytes decoded, or -1 on error */ int rpm_ack_decode_service_request( uint8_t * apdu, int apdu_len, BACNET_READ_ACCESS_DATA * read_access_data) { int decoded_len = 0; /* return value */ 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 */ BACNET_READ_ACCESS_DATA *rpm_object; BACNET_READ_ACCESS_DATA *old_rpm_object; BACNET_PROPERTY_REFERENCE *rpm_property; BACNET_PROPERTY_REFERENCE *old_rpm_property; BACNET_APPLICATION_DATA_VALUE *value; BACNET_APPLICATION_DATA_VALUE *old_value; assert(read_access_data != NULL); rpm_object = read_access_data; old_rpm_object = rpm_object; while (rpm_object && apdu_len) { len = rpm_ack_decode_object_id(apdu, apdu_len, &rpm_object->object_type, &rpm_object->object_instance); if (len <= 0) { old_rpm_object->next = NULL; free(rpm_object); break; } decoded_len += len; apdu_len -= len; apdu += len; rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); rpm_object->listOfProperties = rpm_property; old_rpm_property = rpm_property; while (rpm_property && apdu_len) { len = rpm_ack_decode_object_property(apdu, apdu_len, &rpm_property->propertyIdentifier, &rpm_property->propertyArrayIndex); if (len <= 0) { old_rpm_property->next = NULL; if (rpm_object->listOfProperties == rpm_property) { /* was this the only property in the list? */ rpm_object->listOfProperties = NULL; } free(rpm_property); break; } decoded_len += len; apdu_len -= len; apdu += len; if (apdu_len && decode_is_opening_tag_number(apdu, 4)) { /* propertyValue */ decoded_len++; apdu_len--; apdu++; /* note: if this is an array, there will be more than one element to decode */ value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE)); rpm_property->value = value; old_value = value; while (value && (apdu_len > 0)) { if (IS_CONTEXT_SPECIFIC(*apdu)) { len = bacapp_decode_context_data(apdu, apdu_len, value, rpm_property->propertyIdentifier); } else { len = bacapp_decode_application_data(apdu, apdu_len, value); } /* If len == 0 then it's an empty structure, which is OK. */ if (len < 0) { /* problem decoding */ /* calling function will free the memory */ return BACNET_STATUS_ERROR; } decoded_len += len; apdu_len -= len; apdu += len; if (apdu_len && decode_is_closing_tag_number(apdu, 4)) { decoded_len++; apdu_len--; apdu++; break; } else { old_value = value; value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE)); old_value->next = value; } } } else if (apdu_len && decode_is_opening_tag_number(apdu, 5)) { /* propertyAccessError */ decoded_len++; apdu_len--; apdu++; /* decode the class and code sequence */ 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_class = error_value; 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 = 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++; } } old_rpm_property = rpm_property; rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); old_rpm_property->next = rpm_property; } len = rpm_decode_object_end(apdu, apdu_len); if (len) { decoded_len += len; apdu_len -= len; apdu += len; } if (apdu_len) { old_rpm_object = rpm_object; rpm_object = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); old_rpm_object->next = rpm_object; } } return decoded_len; }
/* BACnetPropertyReference ::= SEQUENCE { propertyIdentifier [0] BACnetPropertyIdentifier, propertyArrayIndex [1] Unsigned OPTIONAL --used only with array datatype -- if omitted with an array the entire array is referenced } */ int rpm_decode_object_property( uint8_t * apdu, unsigned apdu_len, BACNET_RPM_DATA * rpmdata) { unsigned len = 0; unsigned option_len = 0; uint8_t tag_number = 0; uint32_t len_value_type = 0; uint32_t property = 0; /* for decoding */ uint32_t array_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; 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; } /* 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 = (unsigned) 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, &array_value); rpmdata->array_index = array_value; } } } return (int) len; }