diff --git a/apps/mstpcap/main.c b/apps/mstpcap/main.c index 35d4137b..9ee87814 100644 --- a/apps/mstpcap/main.c +++ b/apps/mstpcap/main.c @@ -136,7 +136,7 @@ static void mstp_monitor_i_am(uint8_t mac, const uint8_t *pdu, uint16_t pdu_len) BACNET_ADDRESS dest = { 0 }; BACNET_NPDU_DATA npdu_data = { 0 }; int apdu_offset = 0; - uint16_t apdu_len = 0; + uint16_t apdu_len = 0, service_len = 0; const uint8_t *apdu = NULL; uint8_t pdu_type = 0; uint8_t service_choice = 0; @@ -156,10 +156,12 @@ static void mstp_monitor_i_am(uint8_t mac, const uint8_t *pdu, uint16_t pdu_len) (apdu_len >= 2)) { service_choice = apdu[1]; service_request = &apdu[2]; + service_len = apdu_len - 2; if (service_choice == SERVICE_UNCONFIRMED_I_AM) { - len = iam_decode_service_request( - service_request, &device_id, NULL, NULL, NULL); - if (len != -1) { + len = bacnet_iam_request_decode( + service_request, service_len, &device_id, NULL, NULL, + NULL); + if (len > 0) { MSTP_Statistics[mac].device_id = device_id; } } diff --git a/apps/whois/main.c b/apps/whois/main.c index ddbd5515..5cb28129 100644 --- a/apps/whois/main.c +++ b/apps/whois/main.c @@ -109,9 +109,9 @@ static void my_i_am_handler( uint16_t vendor_id = 0; unsigned i = 0; - (void)service_len; - len = iam_decode_service_request( - service_request, &device_id, &max_apdu, &segmentation, &vendor_id); + len = bacnet_iam_request_decode( + service_request, service_len, &device_id, &max_apdu, &segmentation, + &vendor_id); if (BACnet_Debug_Enabled) { fprintf(stderr, "Received I-Am Request"); } diff --git a/apps/writefile/main.c b/apps/writefile/main.c index 585cd506..84fe9075 100644 --- a/apps/writefile/main.c +++ b/apps/writefile/main.c @@ -95,9 +95,9 @@ static void LocalIAmHandler( uint16_t vendor_id = 0; (void)src; - (void)service_len; - len = iam_decode_service_request( - service_request, &device_id, &max_apdu, &segmentation, &vendor_id); + len = bacnet_iam_request_decode( + service_request, service_len, &device_id, &max_apdu, &segmentation, + &vendor_id); if (len != -1) { address_add(device_id, max_apdu, src); } else { diff --git a/ports/win32/main.c b/ports/win32/main.c index 707bd925..577bc176 100644 --- a/ports/win32/main.c +++ b/ports/win32/main.c @@ -131,9 +131,9 @@ static void LocalIAmHandler( uint16_t vendor_id = 0; (void)src; - (void)service_len; - len = iam_decode_service_request( - service_request, &device_id, &max_apdu, &segmentation, &vendor_id); + len = bacnet_iam_request_decode( + service_request, service_len, &device_id, &max_apdu, &segmentation, + &vendor_id); fprintf(stderr, "Received I-Am Request"); if (len != -1) { fprintf(stderr, " from %u!\n", device_id); diff --git a/src/bacnet/basic/client/bac-rw.c b/src/bacnet/basic/client/bac-rw.c index e7d0692f..bf210b0a 100644 --- a/src/bacnet/basic/client/bac-rw.c +++ b/src/bacnet/basic/client/bac-rw.c @@ -156,9 +156,9 @@ static void My_I_Am_Bind( bool found = false; bool bind = false; - (void)service_len; - len = iam_decode_service_request( - service_request, &device_id, &max_apdu, &segmentation, &vendor_id); + len = bacnet_iam_request_decode( + service_request, service_len, &device_id, &max_apdu, &segmentation, + &vendor_id); if (len > 0) { found = address_bind_request(device_id, NULL, NULL); if (!found) { diff --git a/src/bacnet/basic/service/h_iam.c b/src/bacnet/basic/service/h_iam.c index e428ce39..a46c5f8e 100644 --- a/src/bacnet/basic/service/h_iam.c +++ b/src/bacnet/basic/service/h_iam.c @@ -33,9 +33,9 @@ void handler_i_am_add( int segmentation = 0; uint16_t vendor_id = 0; - (void)service_len; - len = iam_decode_service_request( - service_request, &device_id, &max_apdu, &segmentation, &vendor_id); + len = bacnet_iam_request_decode( + service_request, service_len, &device_id, &max_apdu, &segmentation, + &vendor_id); #if PRINT_ENABLED fprintf(stderr, "Received I-Am Request"); #endif @@ -73,9 +73,9 @@ void handler_i_am_bind( int segmentation = 0; uint16_t vendor_id = 0; - (void)service_len; - len = iam_decode_service_request( - service_request, &device_id, &max_apdu, &segmentation, &vendor_id); + len = bacnet_iam_request_decode( + service_request, service_len, &device_id, &max_apdu, &segmentation, + &vendor_id); if (len > 0) { /* only add address if requested to bind */ address_add_binding(device_id, max_apdu, src); diff --git a/src/bacnet/iam.c b/src/bacnet/iam.c index d8e1961e..31a11fb3 100644 --- a/src/bacnet/iam.c +++ b/src/bacnet/iam.c @@ -14,6 +14,75 @@ #include "bacnet/bacdcode.h" #include "bacnet/iam.h" +/** + * @brief Encode the I-Am Request. + * @param apdu Transmit buffer + * @param device_id Device Id + * @param max_apdu Transmit buffer size. + * @param segmentation True, if segmentation shall be featured. + * @param vendor_id Vendor Id + * @return Total length of the apdu, zero otherwise. + */ +int bacnet_iam_request_encode( + uint8_t *apdu, + uint32_t device_id, + unsigned max_apdu, + int segmentation, + uint16_t vendor_id) +{ + int len; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + len = encode_application_object_id(apdu, OBJECT_DEVICE, device_id); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_unsigned(apdu, max_apdu); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_enumerated(apdu, (uint32_t)segmentation); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_unsigned(apdu, vendor_id); + apdu_len += len; + + return apdu_len; +} + +/** + * @brief Encode the WriteGroup service request + * @param apdu Pointer to the buffer for encoding into + * @param apdu_size number of bytes available in the buffer + * @param data Pointer to the service data used for encoding values + * @return number of bytes encoded, or zero if unable to encode or too large + */ +size_t bacnet_iam_service_request_encode( + uint8_t *apdu, + size_t apdu_size, + uint32_t device_id, + unsigned max_apdu, + int segmentation, + uint16_t vendor_id) +{ + size_t apdu_len = 0; /* total length of the apdu, return value */ + + apdu_len = bacnet_iam_request_encode( + NULL, device_id, max_apdu, segmentation, vendor_id); + if (apdu_len > apdu_size) { + apdu_len = 0; + } else { + apdu_len = bacnet_iam_request_encode( + apdu, device_id, max_apdu, segmentation, vendor_id); + } + + return apdu_len; +} + /** * @brief Encode the I-Am service. * @@ -38,17 +107,94 @@ int iam_encode_apdu( if (apdu) { apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; apdu[1] = SERVICE_UNCONFIRMED_I_AM; /* service choice */ - apdu_len = 2; - len = encode_application_object_id( - &apdu[apdu_len], OBJECT_DEVICE, device_id); - apdu_len += len; - len = encode_application_unsigned(&apdu[apdu_len], max_apdu); - apdu_len += len; - len = encode_application_enumerated( - &apdu[apdu_len], (uint32_t)segmentation); - apdu_len += len; - len = encode_application_unsigned(&apdu[apdu_len], vendor_id); - apdu_len += len; + } + len = 2; + apdu_len += len; + if (apdu) { + apdu += len; + } + len = bacnet_iam_request_encode( + apdu, device_id, max_apdu, segmentation, vendor_id); + apdu_len += len; + + return apdu_len; +} + +/** + * @brief Decode the I-Am-Request. + * @ingroup BIBB-DM-DOB + * @param apdu [in] Buffer containing the APDU + * @param apdu_size [in] The length of the APDU + * @param pDevice_id Pointer to the variable that shall take the device Id. + * @param pMax_apdu Pointer to a variable that shall take the decoded length. + * @param pSegmentation Pointer to a variable taking if segmentation is used. + * @param pVendor_id Pointer to a variable taking the vendor id. + * @return The number of bytes decoded , or #BACNET_STATUS_ERROR on error + */ +int bacnet_iam_request_decode( + const uint8_t *apdu, + unsigned apdu_size, + uint32_t *pDevice_id, + unsigned *pMax_apdu, + int *pSegmentation, + uint16_t *pVendor_id) +{ + int len = 0; + int apdu_len = 0; /* total length of the apdu, return value */ + /* should be a Device Object */ + BACNET_OBJECT_TYPE object_type = OBJECT_NONE; + uint32_t object_instance = 0; + uint32_t enum_value = 0; + BACNET_UNSIGNED_INTEGER unsigned_value = 0; + + /* OBJECT ID - object id */ + len = bacnet_object_id_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, &object_type, &object_instance); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (object_type != OBJECT_DEVICE) { + return BACNET_STATUS_ERROR; + } + if (pDevice_id) { + *pDevice_id = object_instance; + } + /* MAX APDU - unsigned */ + len = bacnet_unsigned_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, &unsigned_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (pMax_apdu) { + *pMax_apdu = (unsigned)unsigned_value; + } + /* Segmentation - enumerated */ + len = bacnet_enumerated_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, &enum_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (enum_value >= MAX_BACNET_SEGMENTATION) { + return BACNET_STATUS_ERROR; + } + if (pSegmentation) { + *pSegmentation = (int)enum_value; + } + /* Vendor ID - unsigned16 */ + len = bacnet_unsigned_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, &unsigned_value); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (unsigned_value > 0xFFFF) { + return BACNET_STATUS_ERROR; + } + if (pVendor_id) { + *pVendor_id = (uint16_t)unsigned_value; } return apdu_len; @@ -72,69 +218,6 @@ int iam_decode_service_request( int *pSegmentation, uint16_t *pVendor_id) { - int len = 0; - int apdu_len = 0; /* total length of the apdu, return value */ - /* should be a Device Object */ - BACNET_OBJECT_TYPE object_type = OBJECT_NONE; - uint32_t object_instance = 0; - uint8_t tag_number = 0; - uint32_t len_value = 0; - uint32_t enum_value = 0; - BACNET_UNSIGNED_INTEGER unsigned_value = 0; - - /* OBJECT ID - object id */ - len = decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value); - apdu_len += len; - if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID) { - return -1; - } - len = decode_object_id(&apdu[apdu_len], &object_type, &object_instance); - apdu_len += len; - if (object_type != OBJECT_DEVICE) { - return -1; - } - if (pDevice_id) { - *pDevice_id = object_instance; - } - /* MAX APDU - unsigned */ - len = decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value); - apdu_len += len; - if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { - return -1; - } - len = decode_unsigned(&apdu[apdu_len], len_value, &unsigned_value); - apdu_len += len; - if (pMax_apdu) { - *pMax_apdu = (unsigned)unsigned_value; - } - /* Segmentation - enumerated */ - len = decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value); - apdu_len += len; - if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) { - return -1; - } - len = decode_enumerated(&apdu[apdu_len], len_value, &enum_value); - apdu_len += len; - if (enum_value >= MAX_BACNET_SEGMENTATION) { - return -1; - } - if (pSegmentation) { - *pSegmentation = (int)enum_value; - } - /* Vendor ID - unsigned16 */ - len = decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value); - apdu_len += len; - if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { - return -1; - } - len = decode_unsigned(&apdu[apdu_len], len_value, &unsigned_value); - apdu_len += len; - if (unsigned_value > 0xFFFF) { - return -1; - } - if (pVendor_id) { - *pVendor_id = (uint16_t)unsigned_value; - } - - return apdu_len; + return bacnet_iam_request_decode( + apdu, MAX_APDU, pDevice_id, pMax_apdu, pSegmentation, pVendor_id); } diff --git a/src/bacnet/iam.h b/src/bacnet/iam.h index fa5d6c21..27be8a61 100644 --- a/src/bacnet/iam.h +++ b/src/bacnet/iam.h @@ -36,6 +36,32 @@ int iam_decode_service_request( int *pSegmentation, uint16_t *pVendor_id); +BACNET_STACK_EXPORT +int bacnet_iam_request_decode( + const uint8_t *apdu, + unsigned apdu_size, + uint32_t *pDevice_id, + unsigned *pMax_apdu, + int *pSegmentation, + uint16_t *pVendor_id); + +BACNET_STACK_EXPORT +int bacnet_iam_request_encode( + uint8_t *apdu, + uint32_t device_id, + unsigned max_apdu, + int segmentation, + uint16_t vendor_id); + +BACNET_STACK_EXPORT +size_t bacnet_iam_service_request_encode( + uint8_t *apdu, + size_t apdu_size, + uint32_t device_id, + unsigned max_apdu, + int segmentation, + uint16_t vendor_id); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/test/bacnet/iam/src/main.c b/test/bacnet/iam/src/main.c index cc8b0288..60d531c0 100644 --- a/test/bacnet/iam/src/main.c +++ b/test/bacnet/iam/src/main.c @@ -26,7 +26,7 @@ static int iam_decode_apdu( int *pSegmentation, uint16_t *pVendor_id) { - int apdu_len = 0; /* total length of the apdu, return value */ + int apdu_len; /* valid data? */ if (!apdu) { @@ -39,7 +39,8 @@ static int iam_decode_apdu( if (apdu[1] != SERVICE_UNCONFIRMED_I_AM) { return -1; } - apdu_len = iam_decode_service_request( + apdu_len = 2; + apdu_len += iam_decode_service_request( &apdu[2], pDevice_id, pMax_apdu, pSegmentation, pVendor_id); return apdu_len; @@ -52,7 +53,7 @@ static void testIAm(void) #endif { uint8_t apdu[480] = { 0 }; - int len = 0; + int test_len = 0, null_len = 0, apdu_len; uint32_t device_id = 42; unsigned max_apdu = 480; int segmentation = SEGMENTATION_NONE; @@ -62,19 +63,86 @@ static void testIAm(void) int test_segmentation = 0; uint16_t test_vendor_id = 0; - len = + null_len = + iam_encode_apdu(NULL, device_id, max_apdu, segmentation, vendor_id); + apdu_len = iam_encode_apdu(&apdu[0], device_id, max_apdu, segmentation, vendor_id); - zassert_not_equal(len, 0, NULL); + zassert_not_equal(apdu_len, 0, NULL); + zassert_equal(apdu_len, null_len, NULL); - len = iam_decode_apdu( + test_len = iam_decode_apdu( &apdu[0], &test_device_id, &test_max_apdu, &test_segmentation, &test_vendor_id); - - zassert_not_equal(len, -1, NULL); + zassert_equal( + test_len, apdu_len, "test_len=%d apdu_len=%d", test_len, apdu_len); zassert_equal(test_device_id, device_id, NULL); zassert_equal(test_vendor_id, vendor_id, NULL); zassert_equal(test_max_apdu, max_apdu, NULL); zassert_equal(test_segmentation, segmentation, NULL); + + null_len = bacnet_iam_request_encode( + NULL, device_id, max_apdu, segmentation, vendor_id); + apdu_len = bacnet_iam_request_encode( + &apdu[0], device_id, max_apdu, segmentation, vendor_id); + zassert_not_equal(apdu_len, 0, NULL); + zassert_equal(apdu_len, null_len, NULL); + test_len = bacnet_iam_request_decode( + &apdu[0], sizeof(apdu), &test_device_id, &test_max_apdu, + &test_segmentation, &test_vendor_id); + zassert_equal(test_len, apdu_len, NULL); + zassert_equal(test_device_id, device_id, NULL); + zassert_equal(test_vendor_id, vendor_id, NULL); + zassert_equal(test_max_apdu, max_apdu, NULL); + zassert_equal(test_segmentation, segmentation, NULL); + test_len = bacnet_iam_request_decode( + NULL, apdu_len, &test_device_id, &test_max_apdu, &test_segmentation, + &test_vendor_id); + zassert_equal(test_len, BACNET_STATUS_ERROR, NULL); + test_len = bacnet_iam_request_decode( + &apdu[0], apdu_len, NULL, &test_max_apdu, &test_segmentation, + &test_vendor_id); + zassert_equal(test_len, apdu_len, NULL); + test_len = bacnet_iam_request_decode( + &apdu[0], apdu_len, &test_device_id, NULL, &test_segmentation, + &test_vendor_id); + zassert_equal(test_len, apdu_len, NULL); + test_len = bacnet_iam_request_decode( + &apdu[0], apdu_len, &test_device_id, &test_max_apdu, NULL, + &test_vendor_id); + zassert_equal(test_len, apdu_len, NULL); + test_len = bacnet_iam_request_decode( + &apdu[0], apdu_len, &test_device_id, &test_max_apdu, &test_segmentation, + NULL); + zassert_equal(test_len, apdu_len, NULL); + while (apdu_len) { + apdu_len--; + test_len = bacnet_iam_request_decode( + &apdu[0], apdu_len, &test_device_id, &test_max_apdu, + &test_segmentation, &test_vendor_id); + zassert_equal( + test_len, BACNET_STATUS_ERROR, "apdu_len=%d test_len=%d", apdu_len, + test_len); + } + apdu_len = bacnet_iam_service_request_encode( + apdu, sizeof(apdu), device_id, max_apdu, segmentation, vendor_id); + zassert_not_equal(apdu_len, 0, NULL); + zassert_equal(apdu_len, null_len, NULL); + while (apdu_len) { + apdu_len--; + test_len = bacnet_iam_service_request_encode( + apdu, apdu_len, device_id, max_apdu, segmentation, vendor_id); + zassert_equal(test_len, 0, NULL); + } + /* internal bounds checking - segmentation enumeration */ + apdu_len = bacnet_iam_request_encode( + &apdu[0], device_id, max_apdu, MAX_BACNET_SEGMENTATION, vendor_id); + zassert_not_equal(apdu_len, 0, NULL); + test_len = bacnet_iam_request_decode( + &apdu[0], sizeof(apdu), &test_device_id, &test_max_apdu, + &test_segmentation, &test_vendor_id); + zassert_equal( + test_len, BACNET_STATUS_ERROR, "apdu_len=%d test_len=%d", apdu_len, + test_len); } /** * @}