int bacapp_decode_context_time_value(uint8_t * apdu, uint8_t tag_number, BACNET_TIME_VALUE * value) { int len = 0; int section_length; if (decode_is_opening_tag_number(&apdu[len], tag_number)) len++; else return -1; section_length = bacapp_decode_time_value(&apdu[len], value); if (section_length > 0) len += section_length; else return -1; if (decode_is_closing_tag_number(&apdu[len], tag_number)) len++; else return -1; return len; }
int bacapp_decode_context_assigned_access_rights( uint8_t * apdu, uint8_t tag, BACNET_ASSIGNED_ACCESS_RIGHTS * aar) { int len = 0; int section_length; 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; } else { len += section_length; if (decode_is_closing_tag_number(&apdu[len], tag)) { len++; } else { len = -1; } } } else { len = -1; } return len; }
int bacapp_decode_context_access_rule( uint8_t * apdu, uint8_t tag_number, BACNET_ACCESS_RULE * rule) { int len = 0; int section_length; if (decode_is_opening_tag_number(&apdu[len], tag_number)) { len++; section_length = bacapp_decode_access_rule(&apdu[len], rule); if (section_length == -1) { len = -1; } else { len += section_length; if (decode_is_closing_tag_number(&apdu[len], tag_number)) { len++; } else { len = -1; } } } else { len = -1; } return len; }
int bacapp_decode_context_property_state( uint8_t * apdu, uint8_t tag_number, BACNET_PROPERTY_STATE * value) { int len = 0; int section_length; 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; } } } else { len = -1; } return len; }
int bacapp_decode_context_device_obj_ref( uint8_t * apdu, uint8_t tag_number, BACNET_DEVICE_OBJECT_REFERENCE * value) { int len = 0; int section_length; if (decode_is_opening_tag_number(&apdu[len], tag_number)) { len++; section_length = bacapp_decode_device_obj_ref(&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; } } } else { len = -1; } return len; }
int bacapp_decode_context_datetime( uint8_t * apdu, uint8_t tag_number, BACNET_DATE_TIME * value) { int apdu_len = 0; int len; if (decode_is_opening_tag_number(&apdu[apdu_len], tag_number)) { apdu_len++; } else { return -1; } if (-1 == (len = bacapp_decode_datetime(&apdu[apdu_len], value))) { return -1; } else { apdu_len += len; } if (decode_is_closing_tag_number(&apdu[apdu_len], tag_number)) { apdu_len++; } else { return -1; } return apdu_len; }
/* is this the end of the list of this objects properties values? */ int rpm_ack_decode_object_end( uint8_t * apdu, unsigned apdu_len) { int len = 0; /* total length of the apdu, return value */ if (apdu && apdu_len) { if (decode_is_closing_tag_number(apdu, 1)) len = 1; } return len; }
int bacapp_decode_context_timestamp( uint8_t * apdu, uint8_t tag_number, BACNET_TIMESTAMP * value) { int len = 0; int section_len; if (decode_is_opening_tag_number(&apdu[len], tag_number)) { len++; section_len = bacapp_decode_timestamp(&apdu[len], value); if (section_len > 0) { len += section_len; if (decode_is_closing_tag_number(&apdu[len], tag_number)) { len++; } else { return -1; } } } return len; }
/* decode the service request only */ int arf_decode_service_request( uint8_t * apdu, unsigned apdu_len, BACNET_ATOMIC_READ_FILE_DATA * data) { int len = 0; int tag_len = 0; uint8_t tag_number = 0; uint32_t len_value_type = 0; uint16_t type = 0; /* for decoding */ /* check for value pointers */ if (apdu_len && data) { len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type); if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID) return -1; len += decode_object_id(&apdu[len], &type, &data->object_instance); data->object_type = (BACNET_OBJECT_TYPE) type; if (decode_is_opening_tag_number(&apdu[len], 0)) { data->access = FILE_STREAM_ACCESS; /* a tag number is not extended so only one octet */ len++; /* fileStartPosition */ tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) return -1; len += decode_signed(&apdu[len], len_value_type, &data->type.stream.fileStartPosition); /* requestedOctetCount */ tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) return -1; len += decode_unsigned(&apdu[len], len_value_type, &data->type.stream.requestedOctetCount); if (!decode_is_closing_tag_number(&apdu[len], 0)) return -1; /* a tag number is not extended so only one octet */ len++; } else if (decode_is_opening_tag_number(&apdu[len], 1)) { data->access = FILE_RECORD_ACCESS; /* a tag number is not extended so only one octet */ len++; /* fileStartRecord */ tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) return -1; len += decode_signed(&apdu[len], len_value_type, &data->type.record.fileStartRecord); /* RecordCount */ tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) return -1; len += decode_unsigned(&apdu[len], len_value_type, &data->type.record.RecordCount); if (!decode_is_closing_tag_number(&apdu[len], 1)) return -1; /* a tag number is not extended so only one octet */ len++; } else return -1; } return len; }
/* decode the service request only */ int arf_ack_decode_service_request( uint8_t * apdu, unsigned apdu_len, BACNET_ATOMIC_READ_FILE_DATA * data) { int len = 0; int tag_len = 0; uint8_t tag_number = 0; uint32_t len_value_type = 0; /* check for value pointers */ if (apdu_len && data) { len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type); if (tag_number != BACNET_APPLICATION_TAG_BOOLEAN) return -1; data->endOfFile = decode_boolean(len_value_type); if (decode_is_opening_tag_number(&apdu[len], 0)) { data->access = FILE_STREAM_ACCESS; /* a tag number is not extended so only one octet */ len++; /* fileStartPosition */ tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) return -1; len += decode_signed(&apdu[len], len_value_type, &data->type.stream.fileStartPosition); /* fileData */ tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) return -1; len += decode_octet_string(&apdu[len], len_value_type, &data->fileData); if (!decode_is_closing_tag_number(&apdu[len], 0)) return -1; /* a tag number is not extended so only one octet */ len++; } else if (decode_is_opening_tag_number(&apdu[len], 1)) { data->access = FILE_RECORD_ACCESS; /* a tag number is not extended so only one octet */ len++; /* fileStartRecord */ tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) return -1; len += decode_signed(&apdu[len], len_value_type, &data->type.record.fileStartRecord); /* returnedRecordCount */ tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) return -1; len += decode_unsigned(&apdu[len], len_value_type, &data->type.record.RecordCount); /* fileData */ tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) return -1; len += decode_octet_string(&apdu[len], len_value_type, &data->fileData); if (!decode_is_closing_tag_number(&apdu[len], 1)) return -1; /* a tag number is not extended so only one octet */ len++; } else return -1; } return len; }
/* decode the service request only */ int ptransfer_error_decode_service_request( uint8_t * apdu, unsigned apdu_len, 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; uint32_t 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, &unsigned_value); len += decode_len; if (error_class) { *error_class = (BACNET_ERROR_CLASS) unsigned_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, &unsigned_value); len += decode_len; if (error_code) { *error_code = (BACNET_ERROR_CODE) unsigned_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; 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; private_data->serviceNumber = 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; } /* we could check for a closing tag of 3 */ } return 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; }
void testReadPropertyMultipleAck( Test * pTest) { uint8_t apdu[480] = { 0 }; int len = 0; int test_len = 0; int apdu_len = 0; uint8_t invoke_id = 12; uint8_t test_invoke_id = 0; uint8_t *service_request = NULL; unsigned service_request_len = 0; BACNET_OBJECT_TYPE object_type = OBJECT_DEVICE; uint32_t object_instance = 0; BACNET_PROPERTY_ID object_property = PROP_OBJECT_IDENTIFIER; uint32_t array_index = 0; BACNET_APPLICATION_DATA_VALUE application_data[4] = { {0} }; BACNET_APPLICATION_DATA_VALUE test_application_data = { 0 }; uint8_t application_data_buffer[MAX_APDU] = { 0 }; int application_data_buffer_len = 0; BACNET_ERROR_CLASS error_class; BACNET_ERROR_CODE error_code; BACNET_RPM_DATA rpmdata; /* build the RPM - try to make it easy for the Application Layer development */ /* IDEA: similar construction, but pass apdu, apdu_len pointer, size of apdu to let the called function handle the out of space problem that these get into by returning a boolean of success/failure. It almost needs to use the keylist library or something similar. Also check case of storing a backoff point (i.e. save enough room for object_end) */ apdu_len = rpm_ack_encode_apdu_init(&apdu[0], invoke_id); /* object beginning */ rpmdata.object_type = OBJECT_DEVICE; rpmdata.object_instance = 123; apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len], &rpmdata); /* reply property */ apdu_len += rpm_ack_encode_apdu_object_property(&apdu[apdu_len], PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL); /* reply value */ application_data[0].tag = BACNET_APPLICATION_TAG_OBJECT_ID; application_data[0].type.Object_Id.type = OBJECT_DEVICE; application_data[0].type.Object_Id.instance = 123; application_data_buffer_len = bacapp_encode_application_data(&application_data_buffer[0], &application_data[0]); apdu_len += rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len], &application_data_buffer[0], application_data_buffer_len); /* reply property */ apdu_len += rpm_ack_encode_apdu_object_property(&apdu[apdu_len], PROP_OBJECT_TYPE, BACNET_ARRAY_ALL); /* reply value */ application_data[1].tag = BACNET_APPLICATION_TAG_ENUMERATED; application_data[1].type.Enumerated = OBJECT_DEVICE; application_data_buffer_len = bacapp_encode_application_data(&application_data_buffer[0], &application_data[1]); apdu_len += rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len], &application_data_buffer[0], application_data_buffer_len); /* object end */ apdu_len += rpm_ack_encode_apdu_object_end(&apdu[apdu_len]); /* object beginning */ rpmdata.object_type = OBJECT_ANALOG_INPUT; rpmdata.object_instance = 33; apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len], &rpmdata); /* reply property */ apdu_len += rpm_ack_encode_apdu_object_property(&apdu[apdu_len], PROP_PRESENT_VALUE, BACNET_ARRAY_ALL); /* reply value */ application_data[2].tag = BACNET_APPLICATION_TAG_REAL; application_data[2].type.Real = 0.0; application_data_buffer_len = bacapp_encode_application_data(&application_data_buffer[0], &application_data[2]); apdu_len += rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len], &application_data_buffer[0], application_data_buffer_len); /* reply property */ apdu_len += rpm_ack_encode_apdu_object_property(&apdu[apdu_len], PROP_DEADBAND, BACNET_ARRAY_ALL); /* reply error */ apdu_len += rpm_ack_encode_apdu_object_property_error(&apdu[apdu_len], ERROR_CLASS_PROPERTY, ERROR_CODE_UNKNOWN_PROPERTY); /* object end */ apdu_len += rpm_ack_encode_apdu_object_end(&apdu[apdu_len]); ct_test(pTest, apdu_len != 0); /****** decode the packet ******/ test_len = rpm_ack_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &service_request, /* will point to the service request in the apdu */ &service_request_len); ct_test(pTest, test_len != -1); ct_test(pTest, test_invoke_id == invoke_id); ct_test(pTest, service_request != NULL); ct_test(pTest, service_request_len > 0); /* the first part should be the first object id */ test_len = rpm_ack_decode_object_id(service_request, service_request_len, &object_type, &object_instance); ct_test(pTest, test_len != -1); ct_test(pTest, object_type == OBJECT_DEVICE); ct_test(pTest, object_instance == 123); len = test_len; /* extract the property */ test_len = rpm_ack_decode_object_property(&service_request[len], service_request_len - len, &object_property, &array_index); ct_test(pTest, object_property == PROP_OBJECT_IDENTIFIER); ct_test(pTest, array_index == BACNET_ARRAY_ALL); len += test_len; /* what is the result? An error or a value? */ ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4)); len++; /* decode the object property portion of the service request */ /* note: if this was an array, there could have been more than one element to decode */ test_len = bacapp_decode_application_data(&service_request[len], service_request_len - len, &test_application_data); ct_test(pTest, test_len > 0); ct_test(pTest, bacapp_same_value(&application_data[0], &test_application_data)); len += test_len; ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4)); len++; /* see if there is another property */ test_len = rpm_ack_decode_object_property(&service_request[len], service_request_len - len, &object_property, &array_index); ct_test(pTest, test_len != -1); ct_test(pTest, object_property == PROP_OBJECT_TYPE); ct_test(pTest, array_index == BACNET_ARRAY_ALL); len += test_len; /* what is the result value? */ ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4)); len++; /* decode the object property portion of the service request */ test_len = bacapp_decode_application_data(&service_request[len], service_request_len - len, &test_application_data); ct_test(pTest, test_len > 0); ct_test(pTest, bacapp_same_value(&application_data[1], &test_application_data)); len += test_len; ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4)); len++; /* see if there is another property */ /* this time we should fail */ test_len = rpm_ack_decode_object_property(&service_request[len], service_request_len - len, &object_property, &array_index); ct_test(pTest, test_len == -1); /* see if it is the end of this object */ test_len = rpm_ack_decode_object_end(&service_request[len], service_request_len - len); ct_test(pTest, test_len == 1); len += test_len; /* try to decode another object id */ test_len = rpm_ack_decode_object_id(&service_request[len], service_request_len - len, &object_type, &object_instance); ct_test(pTest, test_len != -1); ct_test(pTest, object_type == OBJECT_ANALOG_INPUT); ct_test(pTest, object_instance == 33); len += test_len; /* decode the object property portion of the service request only */ test_len = rpm_ack_decode_object_property(&service_request[len], service_request_len - len, &object_property, &array_index); ct_test(pTest, test_len != -1); ct_test(pTest, object_property == PROP_PRESENT_VALUE); ct_test(pTest, array_index == BACNET_ARRAY_ALL); len += test_len; /* what is the result value? */ ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4)); len++; /* decode the object property portion of the service request */ test_len = bacapp_decode_application_data(&service_request[len], service_request_len - len, &test_application_data); ct_test(pTest, test_len > 0); ct_test(pTest, bacapp_same_value(&application_data[2], &test_application_data)); len += test_len; ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4)); len++; /* see if there is another property */ test_len = rpm_ack_decode_object_property(&service_request[len], service_request_len - len, &object_property, &array_index); ct_test(pTest, test_len != -1); ct_test(pTest, object_property == PROP_DEADBAND); ct_test(pTest, array_index == BACNET_ARRAY_ALL); len += test_len; /* what is the result value? */ ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 5)); len++; /* it was an error reply */ test_len = bacerror_decode_error_class_and_code(&service_request[len], service_request_len - len, &error_class, &error_code); ct_test(pTest, test_len != 0); ct_test(pTest, error_class == ERROR_CLASS_PROPERTY); ct_test(pTest, error_code == ERROR_CODE_UNKNOWN_PROPERTY); len += test_len; ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 5)); len++; /* is there another property? */ test_len = rpm_ack_decode_object_property(&service_request[len], service_request_len - len, &object_property, &array_index); ct_test(pTest, test_len == -1); /* got an error -1, is it the end of this object? */ test_len = rpm_ack_decode_object_end(&service_request[len], service_request_len - len); ct_test(pTest, test_len == 1); len += test_len; /* check for another object */ test_len = rpm_ack_decode_object_id(&service_request[len], service_request_len - len, &object_type, &object_instance); ct_test(pTest, test_len == 0); ct_test(pTest, len == service_request_len); }
/* FIXME: there could be various error messages returned using unique values less than zero */ int wp_decode_service_request( uint8_t * apdu, unsigned apdu_len, BACNET_WRITE_PROPERTY_DATA * wpdata) { int len = 0; int tag_len = 0; uint8_t tag_number = 0; uint32_t len_value_type = 0; uint16_t type = 0; /* for decoding */ uint32_t property = 0; /* for decoding */ uint32_t unsigned_value = 0; int i = 0; /* loop counter */ /* check for value pointers */ if (apdu_len && wpdata) { /* Tag 0: Object ID */ if (!decode_is_context_tag(&apdu[len++], 0)) return -1; len += decode_object_id(&apdu[len], &type, &wpdata->object_instance); wpdata->object_type = (BACNET_OBJECT_TYPE) type; /* Tag 1: Property ID */ len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); if (tag_number != 1) return -1; len += decode_enumerated(&apdu[len], len_value_type, &property); wpdata->object_property = (BACNET_PROPERTY_ID) property; /* Tag 2: Optional Array Index */ /* note: decode without incrementing len so we can check for opening tag */ 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); wpdata->array_index = unsigned_value; } else wpdata->array_index = BACNET_ARRAY_ALL; /* Tag 3: opening context tag */ if (!decode_is_opening_tag_number(&apdu[len], 3)) return -1; /* determine the length of the data blob */ wpdata->application_data_len = bacapp_data_len(&apdu[len], apdu_len - len, (BACNET_PROPERTY_ID) property); /* a tag number of 3 is not extended so only one octet */ len++; /* copy the data from the APDU */ for (i = 0; i < wpdata->application_data_len; i++) { wpdata->application_data[i] = apdu[len + i]; } /* add on the data length */ len += wpdata->application_data_len; if (!decode_is_closing_tag_number(&apdu[len], 3)) return -2; /* a tag number of 3 is not extended so only one octet */ len++; /* Tag 4: optional Priority - assumed MAX if not explicitly set */ wpdata->priority = BACNET_MAX_PRIORITY; if ((unsigned) len < apdu_len) { tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); if (tag_number == 4) { len += tag_len; len = decode_unsigned(&apdu[len], len_value_type, &unsigned_value); if ((unsigned_value >= BACNET_MIN_PRIORITY) && (unsigned_value <= BACNET_MAX_PRIORITY)) { wpdata->priority = (uint8_t) unsigned_value; } else return -5; } } } return len; }
/** Process the APDU header and invoke the appropriate service handler * to manage the received request. * Almost all requests and ACKs invoke this function. * @ingroup MISCHNDLR * * @param src [in] The BACNET_ADDRESS of the message's source. * @param apdu [in] The apdu portion of the request, to be processed. * @param apdu_len [in] The total (remaining) length of the apdu. */ void apdu_handler( BACNET_ADDRESS * src, uint8_t * apdu, /* APDU data */ uint16_t apdu_len) { BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 }; BACNET_CONFIRMED_SERVICE_ACK_DATA service_ack_data = { 0 }; //uint8_t invoke_id = 0; uint8_t service_choice = 0; uint8_t *service_request = NULL; uint16_t service_request_len = 0; int len = 0; /* counts where we are in PDU */ uint8_t tag_number = 0; uint32_t len_value = 0; uint32_t error_code = 0; uint32_t error_class = 0; // uint8_t reason = 0; // bool server = false; if (apdu) { /* PDU Type */ switch (apdu[0] & 0xF0) { case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: len = (int) apdu_decode_confirmed_service_request(&apdu[0], apdu_len, &service_data, &service_choice, &service_request, &service_request_len); #if 0 if (apdu_confirmed_dcc_disabled(service_choice)) { tbd: chelsea /* When network communications are completely disabled, only DeviceCommunicationControl and ReinitializeDevice APDUs shall be processed and no messages shall be initiated. */ break; } if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) && (Confirmed_Function[service_choice])) Confirmed_Function[service_choice] (service_request, service_request_len/*, src, &service_data*/); else if (Unrecognized_Service_Handler) Unrecognized_Service_Handler(service_request, service_request_len/*, src, &service_data*/); #endif // #if READ_WRITE_PROPERTY #if 1 if (service_choice == SERVICE_CONFIRMED_READ_PROPERTY) { handler_read_property(service_request, service_request_len, src, &service_data); } else if (service_choice == SERVICE_CONFIRMED_WRITE_PROPERTY) { handler_write_property(service_request, service_request_len, src, &service_data); } else #endif if (service_choice == SERVICE_CONFIRMED_PRIVATE_TRANSFER) { handler_private_transfer(apdu,apdu_len,src); // add private transfer by chelsea } else { handler_unrecognized_service(service_request, service_request_len, src, &service_data); } break; case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: { //U8_T i; service_choice = apdu[1]; service_request = &apdu[2]; service_request_len = apdu_len - 2; #if 0 if (apdu_unconfirmed_dcc_disabled(service_choice)) { /* When network communications are disabled, only DeviceCommunicationControl and ReinitializeDevice APDUs shall be processed and no messages shall be initiated. If communications have been initiation disabled, then WhoIs may be processed. */ break; } if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) { if (Unconfirmed_Function[service_choice]) Unconfirmed_Function[service_choice] ( service_request, service_request_len/*, src*/); // tbd: chelsea } #endif if (service_choice == SERVICE_UNCONFIRMED_WHO_IS) { if( modbus.protocal == BAC_MSTP) { handler_who_is(service_request, service_request_len, src); } else if((modbus.protocal == BAC_IP) || (modbus.protocal == BAC_GSM)) { Send_I_Am(&Handler_Transmit_Buffer[0]); } } else if (service_choice == SERVICE_UNCONFIRMED_PRIVATE_TRANSFER) { // add unconfirmedPrivateTransfer handler, for TEMCO private handler_private_transfer(apdu,apdu_len,src); } } break; case PDU_TYPE_SIMPLE_ACK: // invoke_id = apdu[1]; service_choice = apdu[2]; switch (service_choice) { case SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM: case SERVICE_CONFIRMED_COV_NOTIFICATION: case SERVICE_CONFIRMED_EVENT_NOTIFICATION: case SERVICE_CONFIRMED_SUBSCRIBE_COV: case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY: case SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION: /* Object Access Services */ case SERVICE_CONFIRMED_ADD_LIST_ELEMENT: case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT: case SERVICE_CONFIRMED_DELETE_OBJECT: case SERVICE_CONFIRMED_WRITE_PROPERTY: case SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE: /* Remote Device Management Services */ case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL: case SERVICE_CONFIRMED_REINITIALIZE_DEVICE: case SERVICE_CONFIRMED_TEXT_MESSAGE: /* Virtual Terminal Services */ case SERVICE_CONFIRMED_VT_CLOSE: /* Security Services */ case SERVICE_CONFIRMED_REQUEST_KEY: if (Confirmed_ACK_Function[service_choice] != NULL) { // tbd: chelsea // ((confirmed_simple_ack_function) // Confirmed_ACK_Function[service_choice]) (src, // invoke_id); } // tsm_free_invoke_id(invoke_id); break; default: break; } break; case PDU_TYPE_COMPLEX_ACK: service_ack_data.segmented_message = (apdu[0] & BIT3) ? true : false; service_ack_data.more_follows = (apdu[0] & BIT2) ? true : false; // invoke_id = service_ack_data.invoke_id = apdu[1]; len = 2; if (service_ack_data.segmented_message) { service_ack_data.sequence_number = apdu[len++]; service_ack_data.proposed_window_number = apdu[len++]; } service_choice = apdu[len++]; service_request = &apdu[len]; service_request_len = apdu_len - (uint16_t) len; switch (service_choice) { case SERVICE_CONFIRMED_GET_ALARM_SUMMARY: case SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY: case SERVICE_CONFIRMED_GET_EVENT_INFORMATION: /* File Access Services */ case SERVICE_CONFIRMED_ATOMIC_READ_FILE: case SERVICE_CONFIRMED_ATOMIC_WRITE_FILE: /* Object Access Services */ case SERVICE_CONFIRMED_CREATE_OBJECT: case SERVICE_CONFIRMED_READ_PROPERTY: case SERVICE_CONFIRMED_READ_PROP_CONDITIONAL: case SERVICE_CONFIRMED_READ_PROP_MULTIPLE: case SERVICE_CONFIRMED_READ_RANGE: case SERVICE_CONFIRMED_PRIVATE_TRANSFER: /* Virtual Terminal Services */ case SERVICE_CONFIRMED_VT_OPEN: case SERVICE_CONFIRMED_VT_DATA: /* Security Services */ case SERVICE_CONFIRMED_AUTHENTICATE: if (Confirmed_ACK_Function[service_choice] != NULL) { // (Confirmed_ACK_Function[service_choice]) // (service_request, service_request_len/*, src, // &service_ack_data*/); // tbd: chelsea } // tsm_free_invoke_id(invoke_id); break; default: break; } break; case PDU_TYPE_SEGMENT_ACK: /* FIXME: what about a denial of service attack here? we could check src to see if that matched the tsm */ // tsm_free_invoke_id(invoke_id); break; case PDU_TYPE_ERROR: // invoke_id = apdu[1]; service_choice = apdu[2]; len = 3; /* FIXME: Currently special case for C_P_T but there are others which may need consideration such as ChangeList-Error, CreateObject-Error, WritePropertyMultiple-Error and VTClose_Error but they may be left as is for now until support for these services is added */ if (service_choice == SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over opening tag 0 */ if (decode_is_opening_tag_number(&apdu[len], 0)) { len++; /* a tag number of 0 is not extended so only one octet */ } } len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); /* FIXME: we could validate that the tag is enumerated... */ len += decode_enumerated(&apdu[len], len_value, &error_class); len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); /* FIXME: we could validate that the tag is enumerated... */ len += decode_enumerated(&apdu[len], len_value, &error_code); if (service_choice == SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over closing tag 0 */ if (decode_is_closing_tag_number(&apdu[len], 0)) { len++; /* a tag number of 0 is not extended so only one octet */ } } if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) { // if (Error_Function[service_choice]) // Error_Function[service_choice] (src, invoke_id/*, // (BACNET_ERROR_CLASS) error_class, // (BACNET_ERROR_CODE) error_code*/); // tbd: chelsea } // tsm_free_invoke_id(invoke_id); break; case PDU_TYPE_REJECT: // invoke_id = apdu[1]; // reason = apdu[2]; // if (Reject_Function) // Reject_Function(src, invoke_id, reason); // tsm_free_invoke_id(invoke_id); break; case PDU_TYPE_ABORT: // server = apdu[0] & 0x01; // invoke_id = apdu[1]; // reason = apdu[2]; // if (Abort_Function) // Abort_Function(src, invoke_id, reason, server); // tsm_free_invoke_id(invoke_id); break; default: break; } memset(Handler_Transmit_Buffer,0,MAX_PDU); //printf("apdu_done\r\n"); } return; }
/** 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; }
/** Handler for a ReadPropertyMultiple Service request. * @ingroup DSRPM * This handler will be invoked by apdu_handler() if it has been enabled * by a call to apdu_set_confirmed_handler(). * This handler builds a response packet, which is * - an Abort if * - the message is segmented * - if decoding fails * - if the response would be too large * - the result from each included read request, if it succeeds * - an Error if processing fails for all, or individual errors if only some fail, * or there isn't enough room in the APDU to fit the data. * * @param service_request [in] The contents of the service request. * @param service_len [in] The length of the service_request. * @param src [in] BACNET_ADDRESS of the source of the message * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information * decoded from the APDU header of this message. */ void handler_read_property_multiple( uint8_t * service_request, uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data) { int len = 0; uint16_t copy_len = 0; uint16_t decode_len = 0; int pdu_len = 0; BACNET_NPDU_DATA npdu_data; int bytes_sent; BACNET_ADDRESS my_address; BACNET_RPM_DATA rpmdata; int apdu_len = 0; int npdu_len = 0; int error = 0; /* jps_debug - see if we are utilizing all the buffer */ /* memset(&Handler_Transmit_Buffer[0], 0xff, sizeof(Handler_Transmit_Buffer)); */ /* encode the NPDU portion of the packet */ datalink_get_my_address(&my_address); npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); npdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data); if (service_data->segmented_message) { rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; #if PRINT_ENABLED fprintf(stderr, "RPM: Segmented message. Sending Abort!\r\n"); #endif goto RPM_FAILURE; } /* decode apdu request & encode apdu reply encode complex ack, invoke id, service choice */ apdu_len = rpm_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id); for (;;) { /* Start by looking for an object ID */ len = rpm_decode_object_id(&service_request[decode_len], service_len - decode_len, &rpmdata); if (len >= 0) { /* Got one so skip to next stage */ decode_len += len; } else { /* bad encoding - skip to error/reject/abort handling */ #if PRINT_ENABLED fprintf(stderr, "RPM: Bad Encoding.\n"); #endif error = len; goto RPM_FAILURE; } /* Test for case of indefinite Device object instance */ if ((rpmdata.object_type == OBJECT_DEVICE) && (rpmdata.object_instance == BACNET_MAX_INSTANCE)) { rpmdata.object_instance = Device_Object_Instance_Number(); } /* Stick this object id into the reply - if it will fit */ len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata); copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len, len, MAX_APDU); if (copy_len == 0) { #if PRINT_ENABLED fprintf(stderr, "RPM: Response too big!\r\n"); #endif rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; goto RPM_FAILURE; } apdu_len += copy_len; /* do each property of this object of the RPM request */ for (;;) { /* Fetch a property */ len = rpm_decode_object_property(&service_request[decode_len], service_len - decode_len, &rpmdata); if (len < 0) { /* bad encoding - skip to error/reject/abort handling */ #if PRINT_ENABLED fprintf(stderr, "RPM: Bad Encoding.\n"); #endif error = len; goto RPM_FAILURE; } decode_len += len; /* handle the special properties */ if ((rpmdata.object_property == PROP_ALL) || (rpmdata.object_property == PROP_REQUIRED) || (rpmdata.object_property == PROP_OPTIONAL)) { struct special_property_list_t property_list; unsigned property_count = 0; unsigned index = 0; BACNET_PROPERTY_ID special_object_property; if (rpmdata.array_index != BACNET_ARRAY_ALL) { /* No array index options for this special property. Encode error for this object property response */ len = rpm_ack_encode_apdu_object_property(&Temp_Buf[0], rpmdata.object_property, rpmdata.array_index); copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len, len, MAX_APDU); if (copy_len == 0) { #if PRINT_ENABLED fprintf(stderr, "RPM: Too full to encode property!\r\n"); #endif rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; goto RPM_FAILURE; } apdu_len += len; len = rpm_ack_encode_apdu_object_property_error(&Temp_Buf[0], ERROR_CLASS_PROPERTY, ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY); copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len, len, MAX_APDU); if (copy_len == 0) { #if PRINT_ENABLED fprintf(stderr, "RPM: Too full to encode error!\r\n"); #endif rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; goto RPM_FAILURE; } apdu_len += len; } else { special_object_property = rpmdata.object_property; Device_Objects_Property_List(rpmdata.object_type, &property_list); property_count = RPM_Object_Property_Count(&property_list, special_object_property); if (property_count == 0) { /* handle the error code - but use the special property */ len = RPM_Encode_Property(&Handler_Transmit_Buffer [npdu_len], (uint16_t) apdu_len, MAX_APDU, &rpmdata); if (len > 0) { apdu_len += len; } else { #if PRINT_ENABLED fprintf(stderr, "RPM: Too full for special property!\r\n"); #endif error = len; goto RPM_FAILURE; } } else { for (index = 0; index < property_count; index++) { rpmdata.object_property = RPM_Object_Property(&property_list, special_object_property, index); len = RPM_Encode_Property(&Handler_Transmit_Buffer [npdu_len], (uint16_t) apdu_len, MAX_APDU, &rpmdata); if (len > 0) { apdu_len += len; } else { #if PRINT_ENABLED fprintf(stderr, "RPM: Too full for property!\r\n"); #endif error = len; goto RPM_FAILURE; } } } } } else { /* handle an individual property */ len = RPM_Encode_Property(&Handler_Transmit_Buffer[npdu_len], (uint16_t) apdu_len, MAX_APDU, &rpmdata); if (len > 0) { apdu_len += len; } else { #if PRINT_ENABLED fprintf(stderr, "RPM: Too full for individual property!\r\n"); #endif error = len; goto RPM_FAILURE; } } if (decode_is_closing_tag_number(&service_request[decode_len], 1)) { /* Reached end of property list so cap the result list */ decode_len++; len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]); copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len, len, MAX_APDU); if (copy_len == 0) { #if PRINT_ENABLED fprintf(stderr, "RPM: Too full to encode object end!\r\n"); #endif rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; goto RPM_FAILURE; } else { apdu_len += copy_len; } break; /* finished with this property list */ } } if (decode_len >= service_len) { /* Reached the end so finish up */ break; } } if (apdu_len > service_data->max_resp) { /* too big for the sender - send an abort */ rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; #if PRINT_ENABLED fprintf(stderr, "RPM: Message too large. Sending Abort!\n"); #endif goto RPM_FAILURE; } RPM_FAILURE: if (error) { if (error == BACNET_STATUS_ABORT) { apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, abort_convert_error_code(rpmdata.error_code), true); #if PRINT_ENABLED fprintf(stderr, "RPM: Sending Abort!\n"); #endif } else if (error == BACNET_STATUS_ERROR) { apdu_len = bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, SERVICE_CONFIRMED_READ_PROP_MULTIPLE, rpmdata.error_class, rpmdata.error_code); #if PRINT_ENABLED fprintf(stderr, "RPM: Sending Error!\n"); #endif } else if (error == BACNET_STATUS_REJECT) { apdu_len = reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, reject_convert_error_code(rpmdata.error_code)); #if PRINT_ENABLED fprintf(stderr, "RPM: Sending Reject!\n"); #endif } } pdu_len = apdu_len + npdu_len; bytes_sent = datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); #if PRINT_ENABLED if (bytes_sent <= 0) { fprintf(stderr, "RPM: Failed to send PDU (%s)!\n", strerror(errno)); } #else bytes_sent = bytes_sent; #endif }
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; }