/** 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; }
/** 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; }
bool Notification_Class_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data) { NOTIFICATION_CLASS_INFO *CurrentNotify; NOTIFICATION_CLASS_INFO TmpNotify; BACNET_APPLICATION_DATA_VALUE value; bool status = false; int iOffset = 0; uint8_t idx = 0; int len = 0; CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(wp_data-> object_instance)]; /* decode the some of the request */ len = bacapp_decode_application_data(wp_data->application_data, wp_data->application_data_len, &value); if (len < 0) { /* error while decoding - a value larger than we can handle */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; return false; } if ((wp_data->object_property != PROP_PRIORITY) && (wp_data->array_index != BACNET_ARRAY_ALL)) { /* only array properties can have array options */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; return false; } switch (wp_data->object_property) { case PROP_PRIORITY: status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, &wp_data->error_class, &wp_data->error_code); if (status) { if (wp_data->array_index == 0) { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; } else if (wp_data->array_index == BACNET_ARRAY_ALL) { /* FIXME: wite all array */ } else if (wp_data->array_index <= 3) { CurrentNotify->Priority[wp_data->array_index - 1] = value.type.Unsigned_Int; } else { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; } } break; case PROP_ACK_REQUIRED: status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BIT_STRING, &wp_data->error_class, &wp_data->error_code); if (status) { if (value.type.Bit_String.bits_used == 3) { CurrentNotify->Ack_Required = value.type.Bit_String.value[0]; } else { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; } } break; case PROP_RECIPIENT_LIST: memset(&TmpNotify, 0x00, sizeof(NOTIFICATION_CLASS_INFO)); /* decode all packed */ while (iOffset < wp_data->application_data_len) { /* Decode Valid Days */ len = bacapp_decode_application_data(&wp_data-> application_data[iOffset], wp_data->application_data_len, &value); if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { /* Bad decode, wrong tag or following required parameter missing */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } if (value.type.Bit_String.bits_used == MAX_BACNET_DAYS_OF_WEEK) /* store value */ TmpNotify.Recipient_List[idx].ValidDays = value.type.Bit_String.value[0]; else { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_OTHER; return false; } iOffset += len; /* Decode From Time */ len = bacapp_decode_application_data(&wp_data-> application_data[iOffset], wp_data->application_data_len, &value); if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { /* Bad decode, wrong tag or following required parameter missing */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } /* store value */ TmpNotify.Recipient_List[idx].FromTime = value.type.Time; iOffset += len; /* Decode To Time */ len = bacapp_decode_application_data(&wp_data-> application_data[iOffset], wp_data->application_data_len, &value); if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { /* Bad decode, wrong tag or following required parameter missing */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } /* store value */ TmpNotify.Recipient_List[idx].ToTime = value.type.Time; iOffset += len; /* context tag [0] - Device */ if (decode_is_context_tag(&wp_data->application_data[iOffset], 0)) { TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_DEVICE; /* Decode Network Number */ len = bacapp_decode_context_data(&wp_data-> application_data[iOffset], wp_data->application_data_len, &value, PROP_RECIPIENT_LIST); if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_OBJECT_ID)) { /* Bad decode, wrong tag or following required parameter missing */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } /* store value */ TmpNotify.Recipient_List[idx].Recipient._. DeviceIdentifier = value.type.Object_Id.instance; iOffset += len; } /* opening tag [1] - Recipient */ else if (decode_is_opening_tag_number(&wp_data-> application_data[iOffset], 1)) { iOffset++; TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_ADDRESS; /* Decode Network Number */ len = bacapp_decode_application_data(&wp_data-> application_data[iOffset], wp_data->application_data_len, &value); if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { /* Bad decode, wrong tag or following required parameter missing */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } /* store value */ TmpNotify.Recipient_List[idx].Recipient._.Address.net = value.type.Unsigned_Int; iOffset += len; /* Decode Address */ len = bacapp_decode_application_data(&wp_data-> application_data[iOffset], wp_data->application_data_len, &value); if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_OCTET_STRING)) { /* Bad decode, wrong tag or following required parameter missing */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } /* store value */ if (TmpNotify.Recipient_List[idx].Recipient._.Address. net == 0) { memcpy(TmpNotify.Recipient_List[idx].Recipient._. Address.mac, value.type.Octet_String.value, value.type.Octet_String.length); TmpNotify.Recipient_List[idx].Recipient._.Address. mac_len = value.type.Octet_String.length; } else { memcpy(TmpNotify.Recipient_List[idx].Recipient._. Address.adr, value.type.Octet_String.value, value.type.Octet_String.length); TmpNotify.Recipient_List[idx].Recipient._.Address.len = value.type.Octet_String.length; } iOffset += len; /* closing tag [1] - Recipient */ if (decode_is_closing_tag_number(&wp_data-> application_data[iOffset], 1)) iOffset++; else { /* Bad decode, wrong tag or following required parameter missing */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } } else { /* Bad decode, wrong tag or following required parameter missing */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } /* Process Identifier */ len = bacapp_decode_application_data(&wp_data-> application_data[iOffset], wp_data->application_data_len, &value); if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { /* Bad decode, wrong tag or following required parameter missing */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } /* store value */ TmpNotify.Recipient_List[idx].ProcessIdentifier = value.type.Unsigned_Int; iOffset += len; /* Issue Confirmed Notifications */ len = bacapp_decode_application_data(&wp_data-> application_data[iOffset], wp_data->application_data_len, &value); if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BOOLEAN)) { /* Bad decode, wrong tag or following required parameter missing */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } /* store value */ TmpNotify.Recipient_List[idx].ConfirmedNotify = value.type.Boolean; iOffset += len; /* Transitions */ len = bacapp_decode_application_data(&wp_data-> application_data[iOffset], wp_data->application_data_len, &value); if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { /* Bad decode, wrong tag or following required parameter missing */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } if (value.type.Bit_String.bits_used == MAX_BACNET_EVENT_TRANSITION) /* store value */ TmpNotify.Recipient_List[idx].Transitions = value.type.Bit_String.value[0]; else { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_OTHER; return false; } iOffset += len; /* Increasing element of list */ if (++idx >= NC_MAX_RECIPIENTS) { wp_data->error_class = ERROR_CLASS_RESOURCES; wp_data->error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; return false; } } /* Decoded all recipient list */ /* copy elements from temporary object */ for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) { BACNET_ADDRESS src = { 0 }; unsigned max_apdu = 0; int32_t DeviceID; CurrentNotify->Recipient_List[idx] = TmpNotify.Recipient_List[idx]; if (CurrentNotify->Recipient_List[idx].Recipient. RecipientType == RECIPIENT_TYPE_DEVICE) { /* copy Device_ID */ DeviceID = CurrentNotify->Recipient_List[idx].Recipient._. DeviceIdentifier; address_bind_request(DeviceID, &max_apdu, &src); } else if (CurrentNotify->Recipient_List[idx].Recipient. RecipientType == RECIPIENT_TYPE_ADDRESS) { /* copy Address */ /* src = CurrentNotify->Recipient_List[idx].Recipient._.Address; */ /* address_bind_request(BACNET_MAX_INSTANCE, &max_apdu, &src); */ } } status = true; break; default: wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; break; } return status; }