diff --git a/src/bacnet/arf.c b/src/bacnet/arf.c index b0e30eaa..a470f8f4 100644 --- a/src/bacnet/arf.c +++ b/src/bacnet/arf.c @@ -80,83 +80,99 @@ int arf_encode_apdu( /* decode the service request only */ int arf_decode_service_request( - uint8_t *apdu, unsigned apdu_len, BACNET_ATOMIC_READ_FILE_DATA *data) + uint8_t *apdu, unsigned apdu_len_max, 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 */ + int apdu_len = BACNET_STATUS_ERROR; + uint16_t object_type = 0; + uint32_t object_instance = 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_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)) { + if ((apdu_len_max == 0) || (!data)) { + return BACNET_STATUS_ERROR; + } + len = bacnet_object_id_application_decode( + &apdu[0], apdu_len_max, &object_type, &object_instance); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + data->object_type = (BACNET_OBJECT_TYPE)object_type; + data->object_instance = object_instance; + apdu_len = len; + if (apdu_len < apdu_len_max) { + if (decode_is_opening_tag_number(&apdu[apdu_len], 0)) { data->access = FILE_STREAM_ACCESS; - /* a tag number is not extended so only one octet */ - len++; + /* tag number 0 is not extended so only one octet */ + apdu_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; + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; } - len += decode_signed(&apdu[len], len_value_type, - &data->type.stream.fileStartPosition); + len = bacnet_signed_application_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &data->type.stream.fileStartPosition); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; /* 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; + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; } - len += decode_unsigned(&apdu[len], len_value_type, - &data->type.stream.requestedOctetCount); - if (!decode_is_closing_tag_number(&apdu[len], 0)) { - return -1; + len = bacnet_unsigned_application_decode(&apdu[apdu_len], + apdu_len_max, &data->type.stream.requestedOctetCount); + if (len <= 0) { + return BACNET_STATUS_ERROR; } - /* a tag number is not extended so only one octet */ - len++; + apdu_len += len; + /* closing tag */ + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; + } + if (!decode_is_closing_tag_number(&apdu[apdu_len], 0)) { + return BACNET_STATUS_ERROR; + } + /* tag number 0 is not extended so only one octet */ + apdu_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++; + /* tag number 1 is not extended so only one octet */ + apdu_len++; + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; + } /* 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 = bacnet_signed_application_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &data->type.record.fileStartRecord); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; } - 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 = bacnet_unsigned_application_decode( + &apdu[apdu_len], apdu_len_max, &data->type.record.RecordCount); + if (len <= 0) { + return BACNET_STATUS_ERROR; } - len += decode_unsigned( - &apdu[len], len_value_type, &data->type.record.RecordCount); - if (!decode_is_closing_tag_number(&apdu[len], 1)) { - return -1; + apdu_len += len; + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; } - /* a tag number is not extended so only one octet */ - len++; + if (!decode_is_closing_tag_number(&apdu[apdu_len], 1)) { + return BACNET_STATUS_ERROR; + } + /* tag number 1 is not extended so only one octet */ + apdu_len++; } else { - return -1; + return BACNET_STATUS_ERROR; } + } else { + return BACNET_STATUS_ERROR; } - return len; + return apdu_len; } int arf_decode_apdu(uint8_t *apdu, @@ -168,16 +184,16 @@ int arf_decode_apdu(uint8_t *apdu, unsigned offset = 0; if (!apdu) { - return -1; + return BACNET_STATUS_ERROR; } /* optional checking - most likely was already done prior to this call */ if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { - return -1; + return BACNET_STATUS_ERROR; } /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_READ_FILE) { - return -1; + return BACNET_STATUS_ERROR; } offset = 4; @@ -249,7 +265,7 @@ int arf_ack_decode_service_request( len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type); if (tag_number != BACNET_APPLICATION_TAG_BOOLEAN) { - return -1; + return BACNET_STATUS_ERROR; } data->endOfFile = decode_boolean(len_value_type); if (decode_is_opening_tag_number(&apdu[len], 0)) { @@ -261,7 +277,7 @@ int arf_ack_decode_service_request( &apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) { - return -1; + return BACNET_STATUS_ERROR; } len += decode_signed(&apdu[len], len_value_type, &data->type.stream.fileStartPosition); @@ -270,16 +286,16 @@ int arf_ack_decode_service_request( &apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) { - return -1; + return BACNET_STATUS_ERROR; } decoded_len = decode_octet_string( &apdu[len], len_value_type, &data->fileData[0]); if ((uint32_t)decoded_len != len_value_type) { - return -1; + return BACNET_STATUS_ERROR; } len += decoded_len; if (!decode_is_closing_tag_number(&apdu[len], 0)) { - return -1; + return BACNET_STATUS_ERROR; } /* a tag number is not extended so only one octet */ len++; @@ -292,7 +308,7 @@ int arf_ack_decode_service_request( &apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) { - return -1; + return BACNET_STATUS_ERROR; } len += decode_signed( &apdu[len], len_value_type, &data->type.record.fileStartRecord); @@ -301,7 +317,7 @@ int arf_ack_decode_service_request( &apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { - return -1; + return BACNET_STATUS_ERROR; } len += decode_unsigned( &apdu[len], len_value_type, &data->type.record.RecordCount); @@ -311,22 +327,22 @@ int arf_ack_decode_service_request( &apdu[len], &tag_number, &len_value_type); len += tag_len; if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) { - return -1; + return BACNET_STATUS_ERROR; } decoded_len = decode_octet_string( &apdu[len], len_value_type, &data->fileData[i]); if ((uint32_t)decoded_len != len_value_type) { - return -1; + return BACNET_STATUS_ERROR; } len += decoded_len; } if (!decode_is_closing_tag_number(&apdu[len], 1)) { - return -1; + return BACNET_STATUS_ERROR; } /* a tag number is not extended so only one octet */ len++; } else { - return -1; + return BACNET_STATUS_ERROR; } } @@ -342,15 +358,15 @@ int arf_ack_decode_apdu(uint8_t *apdu, unsigned offset = 0; if (!apdu) { - return -1; + return BACNET_STATUS_ERROR; } /* optional checking - most likely was already done prior to this call */ if (apdu[0] != PDU_TYPE_COMPLEX_ACK) { - return -1; + return BACNET_STATUS_ERROR; } *invoke_id = apdu[1]; /* invoke id - filled in by net layer */ if (apdu[2] != SERVICE_CONFIRMED_ATOMIC_READ_FILE) { - return -1; + return BACNET_STATUS_ERROR; } offset = 3; @@ -496,6 +512,37 @@ void testAtomicReadFile(Test *pTest) return; } +void testAtomicReadFileMalformed(Test *pTest) +{ + uint8_t apdu[480] = { 0 }; + /* payloads with malformation */ + uint8_t payload_1[] = { 0xc4, 0x02, 0x80, 0x00, 0x00, 0x0e, 0x35, 0xff, + 0xdf, 0x62, 0xee, 0x00, 0x00, 0x22, 0x05, 0x84, 0x0f }; + uint8_t payload_2[] = { 0xc4, 0x02, 0x80, 0x00, 0x00, 0x0e, 0x31, 0x00, + 0x25, 0xff, 0xd4, 0x9e, 0xbf, 0x79, 0x05, 0x84 }; + BACNET_ATOMIC_READ_FILE_DATA data = { 0 }; + int len = 0; + uint8_t test_invoke_id = 0; + + len = arf_decode_apdu(NULL, sizeof(apdu), &test_invoke_id, &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); + len = arf_decode_apdu(apdu, sizeof(apdu), &test_invoke_id, &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = 0; + apdu[2] = 1; + len = arf_decode_apdu(apdu, sizeof(apdu), &test_invoke_id, &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); + apdu[2] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; + len = arf_decode_apdu(apdu, sizeof(apdu), &test_invoke_id, &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); + /* malformed payload testing */ + len = arf_decode_service_request(payload_1, sizeof(payload_1), &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); + len = arf_decode_service_request(payload_2, sizeof(payload_2), &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); +} + #ifdef TEST_ATOMIC_READ_FILE int main(void) { @@ -508,6 +555,8 @@ int main(void) assert(rc); rc = ct_addTestFunction(pTest, testAtomicReadFileAck); assert(rc); + rc = ct_addTestFunction(pTest, testAtomicReadFileMalformed); + assert(rc); ct_setStream(pTest, stdout); ct_run(pTest); diff --git a/src/bacnet/awf.c b/src/bacnet/awf.c index 91d8834d..377ffa6a 100644 --- a/src/bacnet/awf.c +++ b/src/bacnet/awf.c @@ -85,106 +85,115 @@ int awf_encode_apdu( /* decode the service request only */ int awf_decode_service_request( - uint8_t *apdu, unsigned apdu_len, BACNET_ATOMIC_WRITE_FILE_DATA *data) + uint8_t *apdu, unsigned apdu_len_max, BACNET_ATOMIC_WRITE_FILE_DATA *data) { int len = 0; - int tag_len = 0; - int decoded_len = 0; - uint8_t tag_number = 0; - uint32_t len_value_type = 0; - int32_t signed_value = 0; - uint32_t unsigned_value = 0; - uint16_t type = 0; /* for decoding */ + int apdu_len = BACNET_STATUS_ERROR; + uint16_t object_type = 0; + uint32_t object_instance = 0; uint32_t i = 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_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)) { + if ((apdu_len_max == 0) || (!data)) { + return BACNET_STATUS_ERROR; + } + len = bacnet_object_id_application_decode( + &apdu[0], apdu_len_max, &object_type, &object_instance); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + data->object_type = (BACNET_OBJECT_TYPE)object_type; + data->object_instance = object_instance; + apdu_len = len; + if (apdu_len < apdu_len_max) { + if (decode_is_opening_tag_number(&apdu[apdu_len], 0)) { data->access = FILE_STREAM_ACCESS; - /* a tag number of 2 is not extended so only one octet */ - len++; + /* a tag number of 0 is not extended so only one octet */ + apdu_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; + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; } - len += decode_signed(&apdu[len], len_value_type, &signed_value); - data->type.stream.fileStartPosition = signed_value; + len = bacnet_signed_application_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &data->type.stream.fileStartPosition); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; /* 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; + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; } - decoded_len = decode_octet_string( - &apdu[len], len_value_type, &data->fileData[0]); - if ((uint32_t)decoded_len != len_value_type) { - return -1; + len = bacnet_octet_string_application_decode( + &apdu[apdu_len], apdu_len_max, &data->fileData[0]); + if (len <= 0) { + return BACNET_STATUS_ERROR; } - len += decoded_len; - if (!decode_is_closing_tag_number(&apdu[len], 0)) { - return -1; + apdu_len += len; + /* closing tag */ + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; } - /* a tag number is not extended so only one octet */ - len++; - } else if (decode_is_opening_tag_number(&apdu[len], 1)) { + if (!decode_is_closing_tag_number(&apdu[apdu_len], 0)) { + return BACNET_STATUS_ERROR; + } + /* a tag number of 0 is not extended so only one octet */ + apdu_len++; + } else if (decode_is_opening_tag_number(&apdu[apdu_len], 1)) { data->access = FILE_RECORD_ACCESS; - /* a tag number is not extended so only one octet */ - len++; + /* a tag number of 0 is not extended so only one octet */ + apdu_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; + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; } - len += decode_signed(&apdu[len], len_value_type, &signed_value); - data->type.record.fileStartRecord = signed_value; + len = bacnet_signed_application_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &data->type.record.fileStartRecord); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; /* 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; + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; + } + len = bacnet_unsigned_application_decode(&apdu[apdu_len], + apdu_len_max, &data->type.record.returnedRecordCount); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; } - len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value); - data->type.record.returnedRecordCount = unsigned_value; /* fileData */ for (i = 0; i < data->type.record.returnedRecordCount; i++) { - 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; + if (i < BACNET_WRITE_FILE_RECORD_COUNT) { + len = bacnet_octet_string_application_decode( + &apdu[apdu_len], apdu_len_max, &data->fileData[i]); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + /* closing tag or another record */ + if (apdu_len >= apdu_len_max) { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; } - decoded_len = decode_octet_string( - &apdu[len], len_value_type, &data->fileData[i]); - if ((uint32_t)decoded_len != len_value_type) { - return -1; - } - len += decoded_len; } - if (!decode_is_closing_tag_number(&apdu[len], 1)) { - return -1; + if (!decode_is_closing_tag_number(&apdu[apdu_len], 1)) { + return BACNET_STATUS_ERROR; } - /* a tag number is not extended so only one octet */ - len++; + /* tag number 1 is not extended so only one octet */ + apdu_len++; } else { - return -1; + return BACNET_STATUS_ERROR; } } - return len; + return apdu_len; } int awf_decode_apdu(uint8_t *apdu, @@ -196,16 +205,16 @@ int awf_decode_apdu(uint8_t *apdu, unsigned offset = 0; if (!apdu) { - return -1; + return BACNET_STATUS_ERROR; } /* optional checking - most likely was already done prior to this call */ if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { - return -1; + return BACNET_STATUS_ERROR; } /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_WRITE_FILE) { - return -1; + return BACNET_STATUS_ERROR; } offset = 4; @@ -265,7 +274,7 @@ int awf_ack_decode_service_request( len += decode_signed( &apdu[len], len_value_type, &data->type.record.fileStartRecord); } else { - return -1; + return BACNET_STATUS_ERROR; } } @@ -281,15 +290,15 @@ int awf_ack_decode_apdu(uint8_t *apdu, unsigned offset = 0; if (!apdu) { - return -1; + return BACNET_STATUS_ERROR; } /* optional checking - most likely was already done prior to this call */ if (apdu[0] != PDU_TYPE_COMPLEX_ACK) { - return -1; + return BACNET_STATUS_ERROR; } *invoke_id = apdu[1]; /* invoke id - filled in by net layer */ if (apdu[2] != SERVICE_CONFIRMED_ATOMIC_WRITE_FILE) { - return -1; + return BACNET_STATUS_ERROR; } offset = 3; @@ -320,7 +329,7 @@ void testAtomicWriteFileAccess(Test *pTest, BACNET_ATOMIC_WRITE_FILE_DATA *data) apdu_len = len; len = awf_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data); - ct_test(pTest, len != -1); + ct_test(pTest, len != BACNET_STATUS_ERROR); ct_test(pTest, test_data.object_type == data->object_type); ct_test(pTest, test_data.object_instance == data->object_instance); ct_test(pTest, test_data.access == data->access); @@ -385,7 +394,14 @@ void testAtomicWriteFileAckAccess( apdu_len = len; len = awf_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data); - ct_test(pTest, len != -1); + if (len == BACNET_STATUS_ERROR) { + if (data->access == FILE_STREAM_ACCESS) { + printf("testing FILE_STREAM_ACCESS failed decode\n"); + } else if (data->access == FILE_RECORD_ACCESS) { + printf("testing FILE_RECORD_ACCESS failed decode\n"); + } + } + ct_test(pTest, len != BACNET_STATUS_ERROR); ct_test(pTest, test_data.access == data->access); if (test_data.access == FILE_STREAM_ACCESS) { ct_test(pTest, @@ -413,6 +429,39 @@ void testAtomicWriteFileAck(Test *pTest) return; } +void testAtomicWriteFileMalformed(Test *pTest) +{ + uint8_t apdu[480] = { 0 }; + /* payloads with malformation */ + uint8_t payload_1[] = { 0xc4, 0x02, 0x80, 0x00, 0x00, 0x0e, 0x35, 0xff, + 0x5e, 0xd5, 0xc0, 0x85, 0x0a, 0x62, 0x64, 0x0a, 0x0f }; + uint8_t payload_2[] = { 0xc4, 0x02, 0x80, 0x00, 0x00, 0x0e, 0x35, 0xff, + 0xc4, 0x4d, 0x92, 0xd9, 0x0a, 0x62, 0x64, 0x0a, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f }; + BACNET_ATOMIC_WRITE_FILE_DATA data = { 0 }; + int len = 0; + uint8_t test_invoke_id = 0; + + len = awf_decode_apdu(NULL, sizeof(apdu), &test_invoke_id, &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); + len = awf_decode_apdu(apdu, sizeof(apdu), &test_invoke_id, &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = 0; + apdu[2] = 1; + len = awf_decode_apdu(apdu, sizeof(apdu), &test_invoke_id, &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); + apdu[2] = SERVICE_CONFIRMED_ATOMIC_WRITE_FILE; + len = awf_decode_apdu(apdu, sizeof(apdu), &test_invoke_id, &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); + /* malformed payload testing */ + len = awf_decode_service_request(payload_1, sizeof(payload_1), &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); + len = awf_decode_service_request(payload_2, sizeof(payload_2), &data); + ct_test(pTest, len == BACNET_STATUS_ERROR); +} + #ifdef TEST_ATOMIC_WRITE_FILE int main(void) { @@ -425,6 +474,8 @@ int main(void) assert(rc); rc = ct_addTestFunction(pTest, testAtomicWriteFileAck); assert(rc); + rc = ct_addTestFunction(pTest, testAtomicWriteFileMalformed); + assert(rc); ct_setStream(pTest, stdout); ct_run(pTest); diff --git a/src/bacnet/bacapp.c b/src/bacnet/bacapp.c index 396335fc..6923b7c1 100644 --- a/src/bacnet/bacapp.c +++ b/src/bacnet/bacapp.c @@ -243,7 +243,7 @@ int bacapp_decode_data(uint8_t *apdu, case BACNET_APPLICATION_TAG_OBJECT_ID: { uint16_t object_type = 0; uint32_t instance = 0; - len = decode_object_id_safe( + len = bacnet_object_id_decode( &apdu[0], len_value_type, &object_type, &instance); value->type.Object_Id.type = object_type; value->type.Object_Id.instance = instance; @@ -271,8 +271,17 @@ int bacapp_decode_data(uint8_t *apdu, return len; } +/** + * @brief Decode the BACnet Application Data + * + * @param apdu - buffer of data to be decoded + * @param apdu_len_max - number of bytes in the buffer + * @param value - decoded value, if decoded + * + * @return the number of apdu bytes consumed, or #BACNET_STATUS_ERROR + */ int bacapp_decode_application_data( - uint8_t *apdu, unsigned max_apdu_len, BACNET_APPLICATION_DATA_VALUE *value) + uint8_t *apdu, unsigned apdu_len_max, BACNET_APPLICATION_DATA_VALUE *value) { int len = 0; int tag_len = 0; @@ -280,19 +289,27 @@ int bacapp_decode_application_data( uint8_t tag_number = 0; uint32_t len_value_type = 0; - /* FIXME: use max_apdu_len! */ - (void)max_apdu_len; if (apdu && value && !IS_CONTEXT_SPECIFIC(*apdu)) { value->context_specific = false; - tag_len = - decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type); - if (tag_len) { + tag_len = bacnet_tag_number_and_value_decode( + &apdu[0], apdu_len_max, &tag_number, &len_value_type); + if (tag_len > 0) { len += tag_len; value->tag = tag_number; - decode_len = bacapp_decode_data( - &apdu[len], tag_number, len_value_type, value); - if (value->tag != MAX_BACNET_APPLICATION_TAG) { - len += decode_len; + if (len <= apdu_len_max) { + decode_len = + bacapp_decode_data_len(NULL, tag_number, len_value_type); + if (decode_len <= (apdu_len_max - len)) { + decode_len = bacapp_decode_data( + &apdu[len], tag_number, len_value_type, value); + if (value->tag != MAX_BACNET_APPLICATION_TAG) { + len += decode_len; + } else { + len = BACNET_STATUS_ERROR; + } + } else { + len = BACNET_STATUS_ERROR; + } } else { len = BACNET_STATUS_ERROR; } @@ -342,7 +359,7 @@ bool bacapp_decode_application_data_safe(uint8_t *new_apdu, if (value && apdu_len_remaining > 0 && !IS_CONTEXT_SPECIFIC(apdu[apdu_len])) { value->context_specific = false; - tag_len = decode_tag_number_and_value_safe( + tag_len = bacnet_tag_number_and_value_decode( &apdu[apdu_len], apdu_len_remaining, &tag_number, &len_value_type); /* If tag_len is zero, then the tag information is truncated */ if (tag_len) { @@ -375,34 +392,41 @@ int bacapp_decode_data_len( { int len = 0; - if (apdu) { - switch (tag_data_type) { - case BACNET_APPLICATION_TAG_NULL: - break; - case BACNET_APPLICATION_TAG_BOOLEAN: - break; - case BACNET_APPLICATION_TAG_UNSIGNED_INT: - case BACNET_APPLICATION_TAG_SIGNED_INT: - case BACNET_APPLICATION_TAG_REAL: - case BACNET_APPLICATION_TAG_DOUBLE: - case BACNET_APPLICATION_TAG_OCTET_STRING: - case BACNET_APPLICATION_TAG_CHARACTER_STRING: - case BACNET_APPLICATION_TAG_BIT_STRING: - case BACNET_APPLICATION_TAG_ENUMERATED: - case BACNET_APPLICATION_TAG_DATE: - case BACNET_APPLICATION_TAG_TIME: - case BACNET_APPLICATION_TAG_OBJECT_ID: - len = (int)len_value_type; - break; - default: - break; - } + (void)apdu; + switch (tag_data_type) { + case BACNET_APPLICATION_TAG_NULL: + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + case BACNET_APPLICATION_TAG_SIGNED_INT: + case BACNET_APPLICATION_TAG_REAL: + case BACNET_APPLICATION_TAG_DOUBLE: + case BACNET_APPLICATION_TAG_OCTET_STRING: + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + case BACNET_APPLICATION_TAG_BIT_STRING: + case BACNET_APPLICATION_TAG_ENUMERATED: + case BACNET_APPLICATION_TAG_DATE: + case BACNET_APPLICATION_TAG_TIME: + case BACNET_APPLICATION_TAG_OBJECT_ID: + len = (int)len_value_type; + break; + default: + break; } return len; } -int bacapp_decode_application_data_len(uint8_t *apdu, unsigned max_apdu_len) +/** + * @brief Determine the BACnet Application Data number of APDU bytes consumed + * + * @param apdu - buffer of data to be decoded + * @param apdu_len_max - number of bytes in the buffer + * + * @return number of bytes decoded, or zero if errors occur + */ +int bacapp_decode_application_data_len(uint8_t *apdu, unsigned apdu_len_max) { int len = 0; int tag_len = 0; @@ -410,15 +434,13 @@ int bacapp_decode_application_data_len(uint8_t *apdu, unsigned max_apdu_len) uint8_t tag_number = 0; uint32_t len_value_type = 0; - /* FIXME: use max_apdu_len! */ - (void)max_apdu_len; if (apdu && !IS_CONTEXT_SPECIFIC(*apdu)) { - tag_len = - decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type); - if (tag_len) { + tag_len = bacnet_tag_number_and_value_decode( + &apdu[0], apdu_len_max, &tag_number, &len_value_type); + if (tag_len > 0) { len += tag_len; decode_len = - bacapp_decode_data_len(&apdu[len], tag_number, len_value_type); + bacapp_decode_data_len(NULL, tag_number, len_value_type); len += decode_len; } } @@ -730,8 +752,17 @@ int bacapp_decode_context_data(uint8_t *apdu, return apdu_len; } +/** + * @brief Determine the BACnet Context Data number of APDU bytes consumed + * + * @param apdu - buffer of data to be decoded + * @param apdu_len_max - number of bytes in the buffer + * @param property - context property identifier + * + * @return number of bytes decoded, or zero if errors occur + */ int bacapp_decode_context_data_len( - uint8_t *apdu, unsigned max_apdu_len, BACNET_PROPERTY_ID property) + uint8_t *apdu, unsigned apdu_len_max, BACNET_PROPERTY_ID property) { int apdu_len = 0, len = 0; int tag_len = 0; @@ -739,17 +770,14 @@ int bacapp_decode_context_data_len( uint32_t len_value_type = 0; uint8_t tag = 0; - /* FIXME: use max_apdu_len! */ - (void)max_apdu_len; if (apdu && IS_CONTEXT_SPECIFIC(*apdu)) { - tag_len = - decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type); + tag_len = bacnet_tag_number_and_value_decode( + &apdu[0], apdu_len_max, &tag_number, &len_value_type); if (tag_len) { apdu_len = tag_len; tag = bacapp_context_tag_type(property, tag_number); if (tag < MAX_BACNET_APPLICATION_TAG) { - len = bacapp_decode_data_len( - &apdu[apdu_len], tag, len_value_type); + len = bacapp_decode_data_len(NULL, tag, len_value_type); apdu_len += len; } else { apdu_len += len_value_type; @@ -878,7 +906,7 @@ bool bacapp_copy(BACNET_APPLICATION_DATA_VALUE *dest_value, Include a value property identifier for context specific data such as the value received in a WriteProperty request */ int bacapp_data_len( - uint8_t *apdu, unsigned max_apdu_len, BACNET_PROPERTY_ID property) + uint8_t *apdu, unsigned apdu_len_max, BACNET_PROPERTY_ID property) { int len = 0; int total_len = 0; @@ -889,31 +917,32 @@ int bacapp_data_len( uint32_t value = 0; if (IS_OPENING_TAG(apdu[0])) { - len = decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &value); + len = bacnet_tag_number_and_value_decode( + &apdu[apdu_len], apdu_len_max - apdu_len, &tag_number, &value); apdu_len += len; opening_tag_number = tag_number; opening_tag_number_counter = 1; while (opening_tag_number_counter) { if (IS_OPENING_TAG(apdu[apdu_len])) { - len = decode_tag_number_and_value( - &apdu[apdu_len], &tag_number, &value); + len = bacnet_tag_number_and_value_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &tag_number, &value); if (tag_number == opening_tag_number) { opening_tag_number_counter++; } } else if (IS_CLOSING_TAG(apdu[apdu_len])) { - len = decode_tag_number_and_value( - &apdu[apdu_len], &tag_number, &value); + len = bacnet_tag_number_and_value_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &tag_number, &value); if (tag_number == opening_tag_number) { opening_tag_number_counter--; } } else if (IS_CONTEXT_SPECIFIC(apdu[apdu_len])) { /* context-specific tagged data */ len = bacapp_decode_context_data_len( - &apdu[apdu_len], max_apdu_len - apdu_len, property); + &apdu[apdu_len], apdu_len_max - apdu_len, property); } else { /* application tagged data */ len = bacapp_decode_application_data_len( - &apdu[apdu_len], max_apdu_len - apdu_len); + &apdu[apdu_len], apdu_len_max - apdu_len); } apdu_len += len; if (opening_tag_number_counter) { @@ -925,7 +954,7 @@ int bacapp_data_len( break; } } - if ((unsigned)apdu_len > max_apdu_len) { + if ((unsigned)apdu_len > apdu_len_max) { /* error: exceeding our buffer limit */ total_len = BACNET_STATUS_ERROR; break; diff --git a/src/bacnet/bacdcode.c b/src/bacnet/bacdcode.c index 37495bef..98acf2c4 100644 --- a/src/bacnet/bacdcode.c +++ b/src/bacnet/bacdcode.c @@ -313,15 +313,23 @@ int decode_tag_number(uint8_t *apdu, uint8_t *tag_number) return len; } -/* Same as function above, but will safely fail if packet has been truncated */ -int decode_tag_number_safe( - uint8_t *apdu, uint32_t apdu_len_remaining, uint8_t *tag_number) +/** + * @brief Decodes from bytes into a BACnet Tag Number + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param tag_number - BACnet tag number + * + * @return number of bytes decoded, or zero if errors occur + */ +int bacnet_tag_number_decode( + uint8_t *apdu, uint32_t apdu_len_max, uint8_t *tag_number) { int len = 0; /* return value */ /* decode the tag number first */ - if (apdu_len_remaining >= 1) { - if (IS_EXTENDED_TAG_NUMBER(apdu[0]) && apdu_len_remaining >= 2) { + if (apdu_len_max >= 1) { + if (IS_EXTENDED_TAG_NUMBER(apdu[0]) && apdu_len_max >= 2) { /* extended tag */ if (tag_number) { *tag_number = apdu[1]; @@ -396,21 +404,28 @@ int decode_tag_number_and_value( return len; } -/* Same as function above, but will safely fail is packet has been truncated */ -int decode_tag_number_and_value_safe(uint8_t *apdu, - uint32_t apdu_len_remaining, - uint8_t *tag_number, - uint32_t *value) +/** + * @brief Decode the BACnet Tag Number and Value + * as defined in clause 20.2.1.3.2 Constructed Data + * + * @param apdu - buffer of data to be decoded + * @param apdu_len_max - number of bytes in the buffer + * @param tag_number - decoded tag number, if decoded + * @param value - decoded value, if decoded + * + * @return the number of apdu bytes consumed + */ +int bacnet_tag_number_and_value_decode( + uint8_t *apdu, uint32_t apdu_len_max, uint8_t *tag_number, uint32_t *value) { int len = 0; - len = decode_tag_number_safe(&apdu[0], apdu_len_remaining, tag_number); - + len = bacnet_tag_number_decode(&apdu[0], apdu_len_max, tag_number); if (len > 0) { - apdu_len_remaining -= len; + apdu_len_max -= len; if (IS_EXTENDED_VALUE(apdu[0])) { /* tagged as uint32_t */ - if (apdu[len] == 255 && apdu_len_remaining >= 5) { + if ((apdu[len] == 255) && (apdu_len_max >= 5)) { uint32_t value32; len++; len += decode_unsigned32(&apdu[len], &value32); @@ -419,7 +434,7 @@ int decode_tag_number_and_value_safe(uint8_t *apdu, } } /* tagged as uint16_t */ - else if (apdu[len] == 254 && apdu_len_remaining >= 3) { + else if ((apdu[len] == 254) && (apdu_len_max >= 3)) { uint16_t value16; len++; len += decode_unsigned16(&apdu[len], &value16); @@ -428,7 +443,7 @@ int decode_tag_number_and_value_safe(uint8_t *apdu, } } /* no tag - must be uint8_t */ - else if (apdu[len] < 254 && apdu_len_remaining >= 1) { + else if ((apdu[len] < 254) && (apdu_len_max >= 1)) { if (value) { *value = apdu[len]; } @@ -447,6 +462,7 @@ int decode_tag_number_and_value_safe(uint8_t *apdu, *value = apdu[0] & 0x07; } } + return len; } @@ -721,18 +737,116 @@ int decode_object_id(uint8_t *apdu, uint16_t *object_type, uint32_t *instance) return len; } -int decode_object_id_safe(uint8_t *apdu, - uint32_t len_value, +int bacnet_object_id_decode(uint8_t *apdu, + uint32_t len_value_type, uint16_t *object_type, uint32_t *instance) { - if (len_value != 4) { + if (len_value_type != 4) { return 0; } else { return decode_object_id(apdu, object_type, instance); } } +/** + * @brief Decode the BACnet Object Identifier Value when application encoded + * as defined in clause 20.2.14 Encoding of an Object Identifier Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer of data to be decoded + * @param apdu_len_max - number of bytes in the buffer + * @param object_type - decoded object type, if decoded + * @param object_instance - decoded object instance, if decoded + * + * @return the number of apdu bytes consumed, or #BACNET_STATUS_ERROR (-1) + */ +int bacnet_object_id_application_decode(uint8_t *apdu, + uint16_t apdu_len_max, + uint16_t *object_type, + uint32_t *object_instance) +{ + int len = 0; + int apdu_len = BACNET_STATUS_ERROR; + uint8_t tag_number; + uint32_t len_value_type = 0; + + len = bacnet_tag_number_and_value_decode( + &apdu[len], apdu_len_max, &tag_number, &len_value_type); + if ((len > 0) && (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID)) { + apdu_len = len; + if (apdu_len < apdu_len_max) { + if ((apdu_len_max - apdu_len) >= len_value_type) { + len = bacnet_object_id_decode( + &apdu[len], len_value_type, object_type, object_instance); + if (len > 0) { + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } + + return apdu_len; +} + +/** + * @brief Decode the BACnet Object Identifier Value when context encoded + * as defined in clause 20.2.14 Encoding of an Object Identifier Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param tag_value - context tag number expected + * @param object_type - decoded object type, if decoded + * @param object_instance - decoded object instance, if decoded + * + * @return number of bytes decoded, zero if wrong tag number, + * or #BACNET_STATUS_ERROR (-1) if malformed + */ +int bacnet_object_id_context_decode(uint8_t *apdu, + uint16_t apdu_len_max, + uint8_t tag_value, + uint16_t *object_type, + uint32_t *object_instance) +{ + int apdu_len = 0; + unsigned len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + if (apdu_len_max) { + if (decode_is_context_tag(&apdu[apdu_len], tag_value) && + !decode_is_closing_tag(&apdu[apdu_len])) { + len = bacnet_tag_number_and_value_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &tag_number, &len_value_type); + if (len > 0) { + apdu_len += len; + if (apdu_len < apdu_len_max) { + len = bacnet_object_id_decode(&apdu[apdu_len], + len_value_type, object_type, object_instance); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } + } + + return apdu_len; +} + int decode_context_object_id(uint8_t *apdu, uint8_t tag_number, uint16_t *object_type, @@ -866,23 +980,52 @@ int encode_context_octet_string( return apdu_len; } -/* from clause 20.2.8 Encoding of an Octet String Value */ -/* and 20.2.1 General Rules for Encoding BACnet Tags */ -/* returns the number of apdu bytes consumed */ -int decode_octet_string( - uint8_t *apdu, uint32_t len_value, BACNET_OCTET_STRING *octet_string) +/** + * @brief Decode the BACnet Octet String Value + * from clause 20.2.8 Encoding of an Octet String Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param len_value - number of bytes in the unsigned value encoding, may be + * zero + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, or BACNET_STATUS_ERROR if errors occur + */ +int bacnet_octet_string_decode(uint8_t *apdu, + uint16_t apdu_len_max, + uint32_t len_value, + BACNET_OCTET_STRING *value) { - int len = 0; /* return value */ + int len = BACNET_STATUS_ERROR; bool status = false; - status = octetstring_init(octet_string, &apdu[0], len_value); - if (status) { - len = (int)len_value; + if (value && (len_value <= apdu_len_max)) { + if (len_value > 0) { + status = octetstring_init(value, &apdu[0], len_value); + } else { + status = octetstring_init(value, NULL, 0); + } + if (status) { + len = (int)len_value; + } } return len; } +/* from clause 20.2.8 Encoding of an Octet String Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed, or BACNET_STATUS_ERROR */ +int decode_octet_string( + uint8_t *apdu, uint32_t len_value, BACNET_OCTET_STRING *value) +{ + const uint16_t apdu_len_max = MAX_APDU; + + return bacnet_octet_string_decode(apdu, apdu_len_max, len_value, value); +} + int decode_context_octet_string( uint8_t *apdu, uint8_t tag_number, BACNET_OCTET_STRING *octet_string) { @@ -905,6 +1048,45 @@ int decode_context_octet_string( return len; } + +/** + * @brief Decodes from bytes into a BACnet Octet String application encoding + * from clause 20.2.8 Encoding of an Octet String Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer of data to be decoded + * @param apdu_len_max - number of bytes in the buffer + * @param value - decoded value, if decoded + * + * @return the number of apdu bytes consumed, or #BACNET_STATUS_ERROR (-1) + */ +int bacnet_octet_string_application_decode( + uint8_t *apdu, uint16_t apdu_len_max, BACNET_OCTET_STRING *value) +{ + int len = 0; + int apdu_len = BACNET_STATUS_ERROR; + uint8_t tag_number; + uint32_t len_value_type = 0; + + len = bacnet_tag_number_and_value_decode( + &apdu[len], apdu_len_max, &tag_number, &len_value_type); + if ((len > 0) && (tag_number == BACNET_APPLICATION_TAG_OCTET_STRING)) { + apdu_len = len; + if (apdu_len < apdu_len_max) { + len = bacnet_octet_string_decode( + &apdu[len], apdu_len_max - apdu_len, len_value_type, value); + if (len != BACNET_STATUS_ERROR) { + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } + + return apdu_len; +} #endif /* from clause 20.2.9 Encoding of a Character String Value */ @@ -980,68 +1162,175 @@ int encode_context_character_string( return len; } -/* from clause 20.2.9 Encoding of a Character String Value */ -/* and 20.2.1 General Rules for Encoding BACnet Tags */ -/* returns the number of apdu bytes consumed */ -int decode_character_string( - uint8_t *apdu, uint32_t len_value, BACNET_CHARACTER_STRING *char_string) +/** + * @brief Decodes from bytes into a BACnet Character String value + * from clause 20.2.9 Encoding of a Character String Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param len_value - number of bytes in the unsigned value encoding + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, or zero if errors occur + */ +int bacnet_character_string_decode(uint8_t *apdu, + uint16_t apdu_len_max, + uint32_t len_value, + BACNET_CHARACTER_STRING *char_string) { - int len = 0; /* return value */ bool status = false; + char *string_value = NULL; + int len = 0; - status = characterstring_init( - char_string, apdu[0], (char *)&apdu[1], len_value - 1); - if (status) { - len = (int)len_value; + /* check to see if the APDU is long enough */ + if (len_value <= apdu_len_max) { + if (len_value > 1) { + string_value = (char *)&apdu[1]; + } + status = characterstring_init( + char_string, apdu[0], string_value, len_value - 1); + if (status) { + len = (int)len_value; + } } return len; } +/** + * @brief Decodes from bytes into a BACnet Character String value + * from clause 20.2.9 Encoding of a Character String Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param len_value - number of bytes in the unsigned value encoding + * @param value - the character string value decoded + * + * @return number of bytes decoded, or zero if errors occur + */ +int decode_character_string( + uint8_t *apdu, uint32_t len_value, BACNET_CHARACTER_STRING *value) +{ + const uint16_t apdu_len_max = MAX_APDU; + + return bacnet_character_string_decode(apdu, apdu_len_max, len_value, value); +} + +/** + * @brief Decodes from bytes into a BACnet Character String value + * from clause 20.2.9 Encoding of a Character String Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param tag_value - context tag number expected + * @param value - the character string value decoded + * + * @return number of bytes decoded, or zero if tag number mismatch, or + * #BACNET_STATUS_ERROR (-1) if malformed + */ +int bacnet_character_string_context_decode(uint8_t *apdu, + uint16_t apdu_len_max, + uint8_t tag_value, + BACNET_CHARACTER_STRING *value) +{ + int apdu_len = 0; + unsigned len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + if (apdu_len_max) { + if (decode_is_context_tag(&apdu[apdu_len], tag_value) && + !decode_is_closing_tag(&apdu[apdu_len])) { + len = bacnet_tag_number_and_value_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &tag_number, &len_value_type); + if (len > 0) { + apdu_len += len; + if (apdu_len < apdu_len_max) { + len = bacnet_character_string_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, len_value_type, value); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } + } + + return apdu_len; +} + +/** + * @brief Decodes from bytes into a BACnet Character String value + * from clause 20.2.9 Encoding of a Character String Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param tag_value - context tag number expected + * @param value - the character string value decoded + * + * @return number of bytes decoded, #BACNET_STATUS_ERROR (-1) if + * wrong tag number, or #BACNET_STATUS_ERROR (-1) if malformed + */ int decode_context_character_string( - uint8_t *apdu, uint8_t tag_number, BACNET_CHARACTER_STRING *char_string) + uint8_t *apdu, uint8_t tag_value, BACNET_CHARACTER_STRING *value) { int len = 0; /* return value */ - bool status = false; - uint32_t len_value = 0; + const uint16_t apdu_len_max = MAX_APDU; - if (decode_is_context_tag(&apdu[len], tag_number) && - !decode_is_closing_tag(&apdu[len])) { - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - - status = characterstring_init( - char_string, apdu[len], (char *)&apdu[len + 1], len_value - 1); - if (status) { - len += len_value; - } - } else { + len = bacnet_character_string_context_decode( + apdu, apdu_len_max, tag_value, value); + if (len == 0) { len = BACNET_STATUS_ERROR; } return len; } -/* from clause 20.2.4 Encoding of an Unsigned Integer Value */ -/* and 20.2.1 General Rules for Encoding BACnet Tags */ -/* returns the number of apdu bytes consumed */ -int decode_unsigned(uint8_t *apdu, uint32_t len_value, uint32_t *value) +/** + * @brief Decodes from bytes into a BACnet Unsigned value + * from clause 20.2.4 Encoding of an Unsigned Integer Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param len_value - number of bytes in the unsigned value encoding + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, or zero if errors occur + */ +int bacnet_unsigned_decode( + uint8_t *apdu, uint16_t apdu_len_max, uint32_t len_value, uint32_t *value) { + int len = 0; uint16_t unsigned16_value = 0; - if (value) { + if (value && (len_value <= apdu_len_max)) { switch (len_value) { case 1: *value = apdu[0]; + len = (int)len_value; break; case 2: decode_unsigned16(&apdu[0], &unsigned16_value); *value = unsigned16_value; + len = (int)len_value; break; case 3: decode_unsigned24(&apdu[0], value); + len = (int)len_value; break; case 4: decode_unsigned32(&apdu[0], value); + len = (int)len_value; break; default: *value = 0; @@ -1049,21 +1338,136 @@ int decode_unsigned(uint8_t *apdu, uint32_t len_value, uint32_t *value) } } - return (int)len_value; + return len; } -int decode_context_unsigned(uint8_t *apdu, uint8_t tag_number, uint32_t *value) +/** + * @brief Decodes from bytes into a BACnet Unsigned value + * from clause 20.2.4 Encoding of an Unsigned Integer Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param tag_value - context tag number expected + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, zero if wrong tag number, + * or error (-1) if malformed + */ +int bacnet_unsigned_context_decode( + uint8_t *apdu, uint16_t apdu_len_max, uint8_t tag_value, uint32_t *value) { - uint32_t len_value; + int apdu_len = 0; + unsigned len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + if (apdu_len_max) { + if (decode_is_context_tag(&apdu[apdu_len], tag_value) && + !decode_is_closing_tag(&apdu[apdu_len])) { + len = bacnet_tag_number_and_value_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &tag_number, &len_value_type); + if (len > 0) { + apdu_len += len; + if (apdu_len < apdu_len_max) { + len = bacnet_unsigned_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, len_value_type, value); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } + } + + return apdu_len; +} + +/** + * @brief Decodes from bytes into a BACnet Unsigned value + * from clause 20.2.4 Encoding of an Unsigned Integer Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer of data to be decoded + * @param apdu_len_max - number of bytes in the buffer + * @param value - decoded value, if decoded + * + * @return the number of apdu bytes consumed, or #BACNET_STATUS_ERROR (-1) + */ +int bacnet_unsigned_application_decode( + uint8_t *apdu, uint16_t apdu_len_max, uint32_t *value) +{ + int len = 0; + int apdu_len = BACNET_STATUS_ERROR; + uint8_t tag_number; + uint32_t len_value_type = 0; + + len = bacnet_tag_number_and_value_decode( + &apdu[len], apdu_len_max, &tag_number, &len_value_type); + if ((len > 0) && (tag_number == BACNET_APPLICATION_TAG_UNSIGNED_INT)) { + apdu_len = len; + if (apdu_len < apdu_len_max) { + len = bacnet_unsigned_decode( + &apdu[len], apdu_len_max - apdu_len, len_value_type, value); + if (len > 0) { + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } + + return apdu_len; +} + +/** + * @brief Decodes from bytes into a BACnet Unsigned value + * from clause 20.2.4 Encoding of an Unsigned Integer Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param len_value - number of bytes in the unsigned value encoding + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, or zero if errors occur + */ +int decode_unsigned(uint8_t *apdu, uint32_t len_value, uint32_t *value) +{ + const uint16_t apdu_len_max = 4; + + return bacnet_unsigned_decode(apdu, apdu_len_max, len_value, value); +} + +/** + * @brief Decodes from bytes into a BACnet Unsigned value + * from clause 20.2.4 Encoding of an Unsigned Integer Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param tag_value - context tag number expected + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, #BACNET_STATUS_ERROR (-1) if + * wrong tag number, or error (-1) if malformed + */ +int decode_context_unsigned(uint8_t *apdu, uint8_t tag_value, uint32_t *value) +{ + const uint16_t apdu_len_max = 6; int len = 0; - if (decode_is_context_tag(&apdu[len], tag_number) && - !decode_is_closing_tag(&apdu[len])) { - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - len += decode_unsigned(&apdu[len], len_value, value); - } else { + len = bacnet_unsigned_context_decode(apdu, apdu_len_max, tag_value, value); + if (len == 0) { len = BACNET_STATUS_ERROR; } + return len; } @@ -1126,35 +1530,113 @@ int encode_application_unsigned(uint8_t *apdu, uint32_t value) return len; } -/* from clause 20.2.11 Encoding of an Enumerated Value */ -/* and 20.2.1 General Rules for Encoding BACnet Tags */ -/* returns the number of apdu bytes consumed */ -int decode_enumerated(uint8_t *apdu, uint32_t len_value, uint32_t *value) +/** + * @brief Decodes from bytes into a BACnet Enumerated value + * from clause 20.2.11 Encoding of an Enumerated Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param len_value - number of bytes in the unsigned value encoding + * @param value - the enumerated value decoded + * + * @return number of bytes decoded, or zero if errors occur + */ +int bacnet_enumerated_decode( + uint8_t *apdu, uint16_t apdu_len_max, uint32_t len_value, uint32_t *value) { - uint32_t unsigned_value = 0; - int len; - - len = decode_unsigned(apdu, len_value, &unsigned_value); - if (value) { - *value = unsigned_value; - } - - return len; + return bacnet_unsigned_decode(apdu, apdu_len_max, len_value, value); } +/** + * @brief Decodes from bytes into a BACnet Enumerated value + * from clause 20.2.11 Encoding of an Enumerated Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param len_value - number of bytes in the unsigned value encoding + * @param value - the enumerated value decoded + * + * @return number of bytes decoded, or zero if errors occur + */ +int decode_enumerated(uint8_t *apdu, uint32_t len_value, uint32_t *value) +{ + const uint16_t apdu_len_max = 4; + + return bacnet_enumerated_decode(apdu, apdu_len_max, len_value, value); +} + +/** + * @brief Decodes from bytes into a BACnet Enumerated value + * from clause 20.2.11 Encoding of an Enumerated Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param tag_value - context tag number expected + * @param value - the enumerated value decoded + * + * @return number of bytes decoded, or zero if tag number mismatch, or + * #BACNET_STATUS_ERROR (-1) if malformed + */ +int bacnet_enumerated_context_decode( + uint8_t *apdu, uint16_t apdu_len_max, uint8_t tag_value, uint32_t *value) +{ + int apdu_len = 0; + unsigned len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + if (apdu_len_max) { + if (decode_is_context_tag(&apdu[apdu_len], tag_value) && + !decode_is_closing_tag(&apdu[apdu_len])) { + len = bacnet_tag_number_and_value_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &tag_number, &len_value_type); + if (len > 0) { + apdu_len += len; + if (apdu_len < apdu_len_max) { + len = bacnet_enumerated_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, len_value_type, value); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } + } + + return apdu_len; +} + +/** + * @brief Decodes from bytes into a BACnet Enumerated value + * from clause 20.2.11 Encoding of an Enumerated Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param tag_value - context tag number expected + * @param value - the enumerated value decoded + * + * @return number of bytes decoded, #BACNET_STATUS_ERROR (-1) if + * wrong tag number, or error (-1) if malformed + */ int decode_context_enumerated(uint8_t *apdu, uint8_t tag_value, uint32_t *value) { + const uint16_t apdu_len_max = 6; int len = 0; - uint8_t tag_number; - uint32_t len_value; - if (decode_is_context_tag(&apdu[len], tag_value) && - !decode_is_closing_tag(&apdu[len])) { - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - len += decode_enumerated(&apdu[len], len_value, value); - } else { + len = + bacnet_enumerated_context_decode(apdu, apdu_len_max, tag_value, value); + if (len == 0) { len = BACNET_STATUS_ERROR; } + return len; } @@ -1206,24 +1688,36 @@ int encode_context_enumerated(uint8_t *apdu, uint8_t tag_number, uint32_t value) } #if BACNET_USE_SIGNED -/* from clause 20.2.5 Encoding of a Signed Integer Value */ -/* and 20.2.1 General Rules for Encoding BACnet Tags */ -/* returns the number of apdu bytes consumed */ -int decode_signed(uint8_t *apdu, uint32_t len_value, int32_t *value) +/** + * @brief Decode the BACnet Signed Integer Value when application encoded + * as defined in clause 20.2.5 Encoding of a Signed Integer Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param len_value - number of bytes in the unsigned value encoding + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, or zero if errors occur + */ +int bacnet_signed_decode( + uint8_t *apdu, uint16_t apdu_len_max, uint32_t len_value, int32_t *value) { - if (value) { + int len = 0; + + if (apdu && value && (len_value <= apdu_len_max)) { switch (len_value) { case 1: - decode_signed8(&apdu[0], value); + len = decode_signed8(&apdu[0], value); break; case 2: - decode_signed16(&apdu[0], value); + len = decode_signed16(&apdu[0], value); break; case 3: - decode_signed24(&apdu[0], value); + len = decode_signed24(&apdu[0], value); break; case 4: - decode_signed32(&apdu[0], value); + len = decode_signed32(&apdu[0], value); break; default: *value = 0; @@ -1231,21 +1725,137 @@ int decode_signed(uint8_t *apdu, uint32_t len_value, int32_t *value) } } - return (int)len_value; + return len; } -int decode_context_signed(uint8_t *apdu, uint8_t tag_number, int32_t *value) +/** + * @brief Decodes from bytes into a BACnet Signed value + * as defined in clause 20.2.5 Encoding of a Signed Integer Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param tag_value - context tag number expected + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, zero if wrong tag number, + * or error (-1) if malformed + */ +int bacnet_signed_context_decode( + uint8_t *apdu, uint16_t apdu_len_max, uint8_t tag_value, int32_t *value) { - uint32_t len_value; + int apdu_len = 0; + unsigned len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + if (apdu_len_max) { + if (decode_is_context_tag(&apdu[apdu_len], tag_value) && + !decode_is_closing_tag(&apdu[apdu_len])) { + len = bacnet_tag_number_and_value_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &tag_number, &len_value_type); + if (len > 0) { + apdu_len += len; + if (apdu_len < apdu_len_max) { + len = bacnet_signed_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, len_value_type, value); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } + } + + return apdu_len; +} + +/** + * @brief Decode the BACnet Signed Integer Value when application encoded + * as defined in clause 20.2.5 Encoding of a Signed Integer Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer of data to be decoded + * @param apdu_len_max - number of bytes in the buffer + * @param value - decoded value, if decoded + * + * @return the number of apdu bytes consumed, or #BACNET_STATUS_ERROR (-1) + */ +int bacnet_signed_application_decode( + uint8_t *apdu, uint16_t apdu_len_max, int32_t *value) +{ + int len = 0; + int apdu_len = BACNET_STATUS_ERROR; + uint8_t tag_number; + uint32_t len_value_type = 0; + + len = bacnet_tag_number_and_value_decode( + &apdu[len], apdu_len_max, &tag_number, &len_value_type); + if ((len > 0) && (tag_number == BACNET_APPLICATION_TAG_SIGNED_INT)) { + apdu_len = len; + if (apdu_len < apdu_len_max) { + len = bacnet_signed_decode( + &apdu[len], apdu_len_max - apdu_len, len_value_type, value); + if (len > 0) { + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } + + return apdu_len; +} + +/** + * @brief Decodes from bytes into a BACnet Unsigned value + * from clause 20.2.4 Encoding of an Unsigned Integer Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer holding the bytes + * @param len_value - number of bytes in the unsigned value encoding + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, #BACNET_STATUS_ERROR (-1) if + * wrong tag number, or error (-1) if malformed + */ +int decode_signed(uint8_t *apdu, uint32_t len_value, int32_t *value) +{ + const unsigned apdu_len_max = 4; + + return bacnet_signed_decode(apdu, apdu_len_max, len_value, value); +} + +/** + * @brief Decodes from bytes into a BACnet Unsigned value + * from clause 20.2.4 Encoding of an Unsigned Integer Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer holding the bytes + * @param tag_value - context tag number expected + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, #BACNET_STATUS_ERROR (-1) if + * wrong tag number, or error (-1) if malformed + */ +int decode_context_signed(uint8_t *apdu, uint8_t tag_value, int32_t *value) +{ + const uint16_t apdu_len_max = 6; int len = 0; - if (decode_is_context_tag(&apdu[len], tag_number) && - !decode_is_closing_tag(&apdu[len])) { - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - len += decode_signed(&apdu[len], len_value, value); - } else { + len = bacnet_signed_context_decode(apdu, apdu_len_max, tag_value, value); + if (len == 0) { len = BACNET_STATUS_ERROR; } + return len; } @@ -1407,17 +2017,133 @@ int encode_context_time(uint8_t *apdu, uint8_t tag_number, BACNET_TIME *btime) return len; } +/** + * @brief Decodes from bytes into a BACnet Time Value + * from clause 20.2.13 Encoding of a Time Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param len_value - number of bytes encoded + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, or zero if errors occur + */ +int bacnet_time_decode(uint8_t *apdu, + uint16_t apdu_len_max, + uint32_t len_value, + BACNET_TIME *value) +{ + int len = 0; + + if (value && (len_value <= apdu_len_max) && (len_value == 4)) { + /* length of time is 4 octets, as per 20.2.13 */ + value->hour = apdu[0]; + value->min = apdu[1]; + value->sec = apdu[2]; + value->hundredths = apdu[3]; + len = (int)len_value; + } + + return len; +} + +/** + * @brief Decodes from bytes into a BACnet Time Value context encoded + * from clause 20.2.13 Encoding of a Time Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer to hold the bytes + * @param apdu_len_max - number of bytes in the buffer to decode + * @param tag_value - context tag number expected + * @param value - the unsigned value decoded + * + * @return number of bytes decoded, zero if wrong tag number, + * or error (-1) if malformed + */ +int bacnet_time_context_decode( + uint8_t *apdu, uint16_t apdu_len_max, uint8_t tag_value, BACNET_TIME *value) +{ + int apdu_len = 0; + unsigned len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + if (apdu_len_max) { + if (decode_is_context_tag(&apdu[apdu_len], tag_value) && + !decode_is_closing_tag(&apdu[apdu_len])) { + len = bacnet_tag_number_and_value_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &tag_number, &len_value_type); + if (len > 0) { + apdu_len += len; + if (apdu_len < apdu_len_max) { + len = bacnet_time_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, len_value_type, value); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } + } + + return apdu_len; +} + +/** + * @brief Decodes from bytes into a BACnet Time Value application encoded + * from clause 20.2.13 Encoding of a Time Value + * and 20.2.1 General Rules for Encoding BACnet Tags + * + * @param apdu - buffer of data to be decoded + * @param apdu_len_max - number of bytes in the buffer + * @param value - decoded value, if decoded + * + * @return the number of apdu bytes consumed, or #BACNET_STATUS_ERROR (-1) + */ +int bacnet_time_application_decode( + uint8_t *apdu, uint16_t apdu_len_max, BACNET_TIME *value) +{ + int len = 0; + int apdu_len = BACNET_STATUS_ERROR; + uint8_t tag_number; + uint32_t len_value_type = 0; + + len = bacnet_tag_number_and_value_decode( + &apdu[len], apdu_len_max, &tag_number, &len_value_type); + if ((len > 0) && (tag_number == BACNET_APPLICATION_TAG_TIME)) { + apdu_len = len; + if (apdu_len < apdu_len_max) { + len = bacnet_time_decode( + &apdu[len], apdu_len_max - apdu_len, len_value_type, value); + if (len > 0) { + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } + + return apdu_len; +} + /* from clause 20.2.13 Encoding of a Time Value */ /* and 20.2.1 General Rules for Encoding BACnet Tags */ /* returns the number of apdu bytes consumed */ -int decode_bacnet_time(uint8_t *apdu, BACNET_TIME *btime) +int decode_bacnet_time(uint8_t *apdu, BACNET_TIME *value) { - btime->hour = apdu[0]; - btime->min = apdu[1]; - btime->sec = apdu[2]; - btime->hundredths = apdu[3]; + const uint16_t apdu_len_max = 4; + const uint32_t len_value = 4; - return 4; + return bacnet_time_decode(apdu, apdu_len_max, len_value, value); } int decode_bacnet_time_safe( @@ -2164,12 +2890,14 @@ static void testBACDCodeCharacterString(Test *pTest) static void testBACDCodeObject(Test *pTest) { - uint8_t object_array[4] = { 0 }; - uint8_t encoded_array[4] = { 0 }; + uint8_t object_array[32] = { 0 }; + uint8_t encoded_array[32] = { 0 }; uint16_t type = OBJECT_BINARY_INPUT; uint16_t decoded_type = OBJECT_ANALOG_OUTPUT; uint32_t instance = 123; uint32_t decoded_instance = 0; + int len = 0; + uint8_t tag_number = 0; encode_bacnet_object_id(&encoded_array[0], type, instance); decode_object_id(&encoded_array[0], &decoded_type, &decoded_instance); @@ -2180,17 +2908,47 @@ static void testBACDCodeObject(Test *pTest) memcmp(&object_array[0], &encoded_array[0], sizeof(object_array)) == 0); for (type = 0; type < 1024; type++) { for (instance = 0; instance <= BACNET_MAX_INSTANCE; instance += 1024) { - encode_bacnet_object_id(&encoded_array[0], type, instance); - decode_object_id( - &encoded_array[0], &decoded_type, &decoded_instance); + /* test application encoded */ + len = + encode_application_object_id(&encoded_array[0], type, instance); + ct_test(pTest, len > 0); + bacnet_object_id_application_decode( + &encoded_array[0], len, &decoded_type, &decoded_instance); ct_test(pTest, decoded_type == type); ct_test(pTest, decoded_instance == instance); - encode_bacnet_object_id(&object_array[0], type, instance); - ct_test(pTest, - memcmp(&object_array[0], &encoded_array[0], - sizeof(object_array)) == 0); + /* test context encoded */ + tag_number = 99; + len = encode_context_object_id( + &encoded_array[0], tag_number, type, instance); + ct_test(pTest, len > 0); + len = decode_context_object_id(&encoded_array[0], tag_number, + &decoded_type, &decoded_instance); + ct_test(pTest, len > 0); + ct_test(pTest, decoded_type == type); + ct_test(pTest, decoded_instance == instance); + tag_number = 100; + len = decode_context_object_id(&encoded_array[0], tag_number, + &decoded_type, &decoded_instance); + ct_test(pTest, len == BACNET_STATUS_ERROR); } } + /* test context encoded */ + tag_number = 1; + type = OBJECT_BINARY_INPUT; + instance = 123; + for (tag_number = 0; tag_number < 254; tag_number++) { + len = encode_context_object_id( + &encoded_array[0], tag_number, type, instance); + ct_test(pTest, len > 0); + len = decode_context_object_id( + &encoded_array[0], tag_number, &decoded_type, &decoded_instance); + ct_test(pTest, len > 0); + ct_test(pTest, decoded_type == type); + ct_test(pTest, decoded_instance == instance); + len = decode_context_object_id( + &encoded_array[0], 254, &decoded_type, &decoded_instance); + ct_test(pTest, len == BACNET_STATUS_ERROR); + } return; } @@ -2449,21 +3207,23 @@ static void testSignedContextDecodes(Test *pTest) static void testEnumeratedContextDecodes(Test *pTest) { - uint8_t apdu[MAX_APDU]; - int inLen; - int outLen; - int outLen2; + uint8_t apdu[MAX_APDU] = { 0 }; + int inLen = 0; + int outLen = 0; + int outLen2 = 0; uint8_t large_context_tag = 0xfe; /* 32 bit number */ uint32_t in = 0xdeadbeef; - uint32_t out; + uint32_t out = 0; outLen2 = decode_context_enumerated(apdu, 9, &out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); in = 0xdeadbeef; inLen = encode_context_enumerated(apdu, 10, in); outLen = decode_context_enumerated(apdu, 10, &out); + outLen2 = decode_context_enumerated(apdu, 9, &out); ct_test(pTest, inLen == outLen); ct_test(pTest, in == out); diff --git a/src/bacnet/bacdcode.h b/src/bacnet/bacdcode.h index a18996c7..e6d6502c 100644 --- a/src/bacnet/bacdcode.h +++ b/src/bacnet/bacdcode.h @@ -57,15 +57,18 @@ extern "C" { int decode_tag_number( uint8_t * apdu, uint8_t * tag_number); - int decode_tag_number_safe( + + int bacnet_tag_number_decode( uint8_t * apdu, uint32_t apdu_len_remaining, uint8_t * tag_number); + int decode_tag_number_and_value( uint8_t * apdu, uint8_t * tag_number, uint32_t * value); - int decode_tag_number_and_value_safe( + + int bacnet_tag_number_and_value_decode( uint8_t * apdu, uint32_t apdu_len_remaining, uint8_t * tag_number, @@ -170,13 +173,24 @@ extern "C" { int decode_object_id( uint8_t * apdu, uint16_t * object_type, - uint32_t * instance); + uint32_t * object_instance); - int decode_object_id_safe( + int bacnet_object_id_decode( uint8_t * apdu, uint32_t len_value, uint16_t * object_type, uint32_t * instance); + int bacnet_object_id_application_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint16_t * object_type, + uint32_t * object_instance); + int bacnet_object_id_context_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint8_t tag_value, + uint16_t * object_type, + uint32_t * instance); int decode_context_object_id( uint8_t * apdu, @@ -220,6 +234,15 @@ extern "C" { uint8_t tag_number, BACNET_OCTET_STRING * octet_string); + int bacnet_octet_string_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint32_t len_value, + BACNET_OCTET_STRING * value); + int bacnet_octet_string_application_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + BACNET_OCTET_STRING * value); /* from clause 20.2.9 Encoding of a Character String Value */ /* and 20.2.1 General Rules for Encoding BACnet Tags */ @@ -248,6 +271,16 @@ extern "C" { uint8_t * apdu, uint8_t tag_number, BACNET_CHARACTER_STRING * char_string); + int bacnet_character_string_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint32_t len_value, + BACNET_CHARACTER_STRING * char_string); + int bacnet_character_string_context_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint8_t tag_value, + BACNET_CHARACTER_STRING * value); /* from clause 20.2.4 Encoding of an Unsigned Integer Value */ @@ -272,6 +305,21 @@ extern "C" { uint8_t tag_number, uint32_t * value); + int bacnet_unsigned_decode( + uint8_t * apdu, + uint16_t apdu_max_len, + uint32_t len_value, + uint32_t * value); + int bacnet_unsigned_application_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint32_t * value); + int bacnet_unsigned_context_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint8_t tag_number, + uint32_t * value); + /* from clause 20.2.5 Encoding of a Signed Integer Value */ /* and 20.2.1 General Rules for Encoding BACnet Tags */ /* returns the number of apdu bytes consumed */ @@ -294,10 +342,34 @@ extern "C" { uint8_t tag_number, int32_t * value); + int bacnet_signed_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint32_t len_value, + int32_t * value); + int bacnet_signed_context_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint8_t tag_value, + int32_t * value); + int bacnet_signed_application_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + int32_t * value); /* from clause 20.2.11 Encoding of an Enumerated Value */ /* and 20.2.1 General Rules for Encoding BACnet Tags */ /* returns the number of apdu bytes consumed */ + int bacnet_enumerated_decode( + uint8_t * apdu, + uint16_t apdu_max_len, + uint32_t len_value, + uint32_t * value); + int bacnet_enumerated_context_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint8_t tag_value, + uint32_t * value); int decode_enumerated( uint8_t * apdu, uint32_t len_value, @@ -345,6 +417,20 @@ extern "C" { uint8_t tag_number, BACNET_TIME * btime); + int bacnet_time_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint32_t len_value, + BACNET_TIME * value); + int bacnet_time_context_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + uint8_t tag_value, + BACNET_TIME * value); + int bacnet_time_application_decode( + uint8_t * apdu, + uint16_t apdu_len_max, + BACNET_TIME * value); /* BACnet Date */ /* year = years since 1900 */ diff --git a/src/bacnet/dcc.c b/src/bacnet/dcc.c index d7ea5a85..b0a7defb 100644 --- a/src/bacnet/dcc.c +++ b/src/bacnet/dcc.c @@ -162,58 +162,84 @@ int dcc_encode_apdu(uint8_t *apdu, /* decode the service request only */ int dcc_decode_service_request(uint8_t *apdu, - unsigned apdu_len, + unsigned apdu_len_max, uint16_t *timeDuration, BACNET_COMMUNICATION_ENABLE_DISABLE *enable_disable, BACNET_CHARACTER_STRING *password) { + int apdu_len = 0; unsigned len = 0; uint8_t tag_number = 0; uint32_t len_value_type = 0; uint32_t value32 = 0; - /* check for value pointers */ - if (apdu_len) { - /* Tag 0: timeDuration, in minutes --optional-- - * But if not included, take it as indefinite, - * which we return as "very large" */ - if (decode_is_context_tag(&apdu[len], 0)) { - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value_type); - len += decode_unsigned(&apdu[len], len_value_type, &value32); - if (timeDuration) { - *timeDuration = (uint16_t)value32; + if (apdu_len_max) { + /* Tag 0: timeDuration, in minutes --optional-- */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_len_max - apdu_len, 0, &value32); + if (len > 0) { + apdu_len += len; + if (value32 <= UINT16_MAX) { + if (timeDuration) { + *timeDuration = (uint16_t)value32; + } + } else { + return BACNET_STATUS_ERROR; } + } else if (len < 0) { + return BACNET_STATUS_ERROR; } else if (timeDuration) { /* zero indicates infinite duration and results in no timeout */ *timeDuration = 0; } - /* Tag 1: enable_disable */ - if (!decode_is_context_tag(&apdu[len], 1)) { - return -1; - } - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value_type); - len += decode_enumerated(&apdu[len], len_value_type, &value32); - if (enable_disable) { - *enable_disable = (BACNET_COMMUNICATION_ENABLE_DISABLE)value32; - } - /* Tag 2: password --optional-- */ - if (len < apdu_len) { - if (!decode_is_context_tag(&apdu[len], 2)) { - return -1; + if (apdu_len < apdu_len_max) { + /* Tag 1: enable_disable */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_len_max - apdu_len, 1, &value32); + if (len > 0) { + apdu_len += len; + if (enable_disable) { + *enable_disable = + (BACNET_COMMUNICATION_ENABLE_DISABLE)value32; + } + } else { + return BACNET_STATUS_ERROR; + } + } + if (apdu_len < apdu_len_max) { + /* Tag 2: password --optional-- */ + if (!decode_is_context_tag(&apdu[apdu_len], 2)) { + /* since this is the last value of the packet, + if there is data here it must be the specific + context tag number or result in an error */ + return BACNET_STATUS_ERROR; + } + len = bacnet_tag_number_and_value_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, &tag_number, &len_value_type); + if (len > 0) { + apdu_len += len; + if (apdu_len < apdu_len_max) { + len = bacnet_character_string_decode(&apdu[apdu_len], + apdu_len_max - apdu_len, len_value_type, password); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; } - len += decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value_type); - len += - decode_character_string(&apdu[len], len_value_type, password); } else if (password) { + /* no optional password - set to NULL */ characterstring_init_ansi(password, NULL); } } - return (int)len; + return apdu_len; } #ifdef TEST @@ -299,6 +325,47 @@ void test_DeviceCommunicationControl(Test *pTest) return; } +void test_DeviceCommunicationControlMalformedData(Test *pTest) +{ + /* payload with enable-disable, and password with wrong characterstring + * length */ + uint8_t payload_1[] = { 0x19, 0x00, 0x2a, 0x00, 0x41 }; + /* payload with enable-disable, and password with wrong characterstring + * length */ + uint8_t payload_2[] = { 0x19, 0x00, 0x2d, 0x55, 0x00, 0x66, 0x69, 0x73, + 0x74, 0x65, 0x72 }; + /* payload with enable-disable - wrong context tag number for password */ + uint8_t payload_3[] = { 0x19, 0x01, 0x3d, 0x09, 0x00, 0x66, 0x69, 0x73, + 0x74, 0x65, 0x72 }; + /* payload with duration, enable-disable, and password */ + uint8_t payload_4[] = { 0x00, 0x05, 0xf1, 0x11, 0x0a, 0x00, 0x19, 0x00, + 0x2d, 0x09, 0x00, 0x66, 0x69, 0x73, 0x74, 0x65, 0x72 }; + /* payload submitted with bug report */ + uint8_t payload_5[] = { 0x0d, 0xff, 0x80, 0x00, 0x03, 0x1a, 0x0a, 0x19, + 0x00, 0x2a, 0x00, 0x41 }; + int len = 0; + uint8_t test_invoke_id = 0; + uint16_t test_timeDuration = 0; + BACNET_COMMUNICATION_ENABLE_DISABLE test_enable_disable; + BACNET_CHARACTER_STRING test_password; + + len = dcc_decode_apdu(&payload_1[0], sizeof(payload_1), &test_invoke_id, + &test_timeDuration, &test_enable_disable, &test_password); + ct_test(pTest, len == BACNET_STATUS_ERROR); + len = dcc_decode_apdu(&payload_2[0], sizeof(payload_2), &test_invoke_id, + &test_timeDuration, &test_enable_disable, &test_password); + ct_test(pTest, len == BACNET_STATUS_ERROR); + len = dcc_decode_apdu(&payload_3[0], sizeof(payload_3), &test_invoke_id, + &test_timeDuration, &test_enable_disable, &test_password); + ct_test(pTest, len == BACNET_STATUS_ERROR); + len = dcc_decode_apdu(&payload_4[0], sizeof(payload_4), &test_invoke_id, + &test_timeDuration, &test_enable_disable, &test_password); + ct_test(pTest, len == BACNET_STATUS_ERROR); + len = dcc_decode_apdu(&payload_5[0], sizeof(payload_5), &test_invoke_id, + &test_timeDuration, &test_enable_disable, &test_password); + ct_test(pTest, len == BACNET_STATUS_ERROR); +} + #ifdef TEST_DEVICE_COMMUNICATION_CONTROL int main(void) { @@ -309,6 +376,9 @@ int main(void) /* individual tests */ rc = ct_addTestFunction(pTest, test_DeviceCommunicationControl); assert(rc); + rc = + ct_addTestFunction(pTest, test_DeviceCommunicationControlMalformedData); + assert(rc); ct_setStream(pTest, stdout); ct_run(pTest); diff --git a/test/awf.cbp b/test/awf.cbp new file mode 100644 index 00000000..11dd7705 --- /dev/null +++ b/test/awf.cbp @@ -0,0 +1,88 @@ + + + + + +