diff --git a/src/bacnet/basic/service/h_apdu.c b/src/bacnet/basic/service/h_apdu.c index 3c50d24d..53f0ef59 100644 --- a/src/bacnet/basic/service/h_apdu.c +++ b/src/bacnet/basic/service/h_apdu.c @@ -554,10 +554,12 @@ static bool apdu_unconfirmed_dcc_disabled(uint8_t service_choice) * @param apdu [in] The apdu portion of the request, to be processed. * @param apdu_len [in] The total (remaining) length of the apdu. */ -void apdu_handler(BACNET_ADDRESS *src, +void apdu_handler( + BACNET_ADDRESS *src, uint8_t *apdu, /* APDU data */ uint16_t apdu_len) { + BACNET_PDU_TYPE pdu_type; BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 }; uint8_t service_choice = 0; uint8_t *service_request = NULL; @@ -572,157 +574,166 @@ void apdu_handler(BACNET_ADDRESS *src, bool server = false; #endif - if (apdu) { - /* PDU Type */ - switch (apdu[0] & 0xF0) { - case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: - len = apdu_decode_confirmed_service_request(&apdu[0], apdu_len, - &service_data, &service_choice, &service_request, - &service_request_len); - if (len == 0) { - /* service data unable to be decoded - simply drop */ - break; - } - if (apdu_confirmed_dcc_disabled(service_choice)) { - /* When network communications are completely disabled, - only DeviceCommunicationControl and ReinitializeDevice - APDUs shall be processed and no messages shall be - initiated. */ - break; - } - if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) && - (Confirmed_Function[service_choice])) { - Confirmed_Function[service_choice](service_request, - service_request_len, src, &service_data); - } else if (Unrecognized_Service_Handler) { - Unrecognized_Service_Handler(service_request, - service_request_len, src, &service_data); - } - break; - case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: - if (apdu_len < 2) { - break; - } - service_choice = apdu[1]; - service_request = &apdu[2]; - service_request_len = apdu_len - 2; - if (apdu_unconfirmed_dcc_disabled(service_choice)) { - /* When network communications are disabled, - only DeviceCommunicationControl and - ReinitializeDevice APDUs shall be processed and no - messages shall be initiated. If communications have - been initiation disabled, then WhoIs may be - processed. */ - break; - } - if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) { - if (Unconfirmed_Function[service_choice]) { - Unconfirmed_Function[service_choice]( - service_request, service_request_len, src); - } - } - break; -#if !BACNET_SVC_SERVER - case PDU_TYPE_SIMPLE_ACK: - if (apdu_len < 3) { - break; - } - invoke_id = apdu[1]; - service_choice = apdu[2]; - if (apdu_confirmed_simple_ack_service(service_choice)) { - if (Confirmed_ACK_Function[service_choice].simple != - NULL) { - Confirmed_ACK_Function[service_choice].simple( - src, invoke_id); - } - tsm_free_invoke_id(invoke_id); - } - break; - case PDU_TYPE_COMPLEX_ACK: - if (apdu_len < 3) { - break; - } - service_ack_data.segmented_message = - (apdu[0] & BIT(3)) ? true : false; - service_ack_data.more_follows = - (apdu[0] & BIT(2)) ? true : false; - invoke_id = service_ack_data.invoke_id = apdu[1]; - len = 2; - if (service_ack_data.segmented_message) { - service_ack_data.sequence_number = apdu[len++]; - service_ack_data.proposed_window_number = apdu[len++]; - } - service_choice = apdu[len++]; - service_request = &apdu[len]; - service_request_len = apdu_len - (uint16_t)len; - if (!apdu_confirmed_simple_ack_service(service_choice)) { - if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) { - if (Confirmed_ACK_Function[service_choice] - .complex != NULL) { - Confirmed_ACK_Function[service_choice].complex( - service_request, service_request_len, src, - &service_ack_data); - } - } - tsm_free_invoke_id(invoke_id); - } - break; - case PDU_TYPE_SEGMENT_ACK: - /* FIXME: what about a denial of service attack here? - we could check src to see if that matched the tsm */ - tsm_free_invoke_id(invoke_id); - break; - case PDU_TYPE_ERROR: - if (apdu_len < 3) { - break; - } - invoke_id = apdu[1]; - service_choice = apdu[2]; - if (apdu_complex_error(service_choice)) { - if (Error_Function[service_choice].complex) { - Error_Function[service_choice].complex(src, - invoke_id, service_choice, &apdu[3], - apdu_len - 3); - } - } else if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) { - len = bacerror_decode_error_class_and_code( - &apdu[3], apdu_len - 3, &error_class, &error_code); - if ((len != 0) && - (Error_Function[service_choice].error)) { - Error_Function[service_choice].error(src, invoke_id, - (BACNET_ERROR_CLASS)error_class, - (BACNET_ERROR_CODE)error_code); - } - } - tsm_free_invoke_id(invoke_id); - break; - case PDU_TYPE_REJECT: - if (apdu_len < 3) { - break; - } - invoke_id = apdu[1]; - reason = apdu[2]; - if (Reject_Function) { - Reject_Function(src, invoke_id, reason); - } - tsm_free_invoke_id(invoke_id); - break; - case PDU_TYPE_ABORT: - if (apdu_len < 3) { - break; - } - server = apdu[0] & 0x01; - invoke_id = apdu[1]; - reason = apdu[2]; - if (Abort_Function) { - Abort_Function(src, invoke_id, reason, server); - } - tsm_free_invoke_id(invoke_id); - break; -#endif - default: - break; - } + if (!apdu) { + return; + } + if (apdu_len == 0) { + return; + } + pdu_type = apdu[0] & 0xF0; + switch (pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + len = apdu_decode_confirmed_service_request( + apdu, apdu_len, &service_data, &service_choice, + &service_request, &service_request_len); + if (len == 0) { + /* service data unable to be decoded - simply drop */ + break; + } + if (apdu_confirmed_dcc_disabled(service_choice)) { + /* When network communications are completely disabled, + only DeviceCommunicationControl and ReinitializeDevice + APDUs shall be processed and no messages shall be + initiated. */ + break; + } + if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) && + (Confirmed_Function[service_choice])) { + Confirmed_Function[service_choice]( + service_request, service_request_len, src, &service_data); + } else if (Unrecognized_Service_Handler) { + Unrecognized_Service_Handler( + service_request, service_request_len, src, &service_data); + } + break; + case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: + if (apdu_len < 2) { + break; + } + service_choice = apdu[1]; + /* prepare the service request buffer and length */ + service_request_len = apdu_len - 2; + service_request = &apdu[2]; + if (apdu_unconfirmed_dcc_disabled(service_choice)) { + /* When network communications are disabled, + only DeviceCommunicationControl and + ReinitializeDevice APDUs shall be processed and no + messages shall be initiated. If communications have + been initiation disabled, then WhoIs may be + processed. */ + break; + } + if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) { + if (Unconfirmed_Function[service_choice]) { + Unconfirmed_Function[service_choice]( + service_request, service_request_len, src); + } + } + break; +#if !BACNET_SVC_SERVER + case PDU_TYPE_SIMPLE_ACK: + if (apdu_len < 3) { + break; + } + invoke_id = apdu[1]; + service_choice = apdu[2]; + if (apdu_confirmed_simple_ack_service(service_choice)) { + if (Confirmed_ACK_Function[service_choice].simple != NULL) { + Confirmed_ACK_Function[service_choice].simple( + src, invoke_id); + } + tsm_free_invoke_id(invoke_id); + } + break; + case PDU_TYPE_COMPLEX_ACK: + if (apdu_len < 3) { + break; + } + service_ack_data.segmented_message = + (apdu[0] & BIT(3)) ? true : false; + service_ack_data.more_follows = (apdu[0] & BIT(2)) ? true : false; + invoke_id = service_ack_data.invoke_id = apdu[1]; + len = 2; + if (service_ack_data.segmented_message) { + if (apdu_len < 5) { + break; + } + service_ack_data.sequence_number = apdu[len++]; + service_ack_data.proposed_window_number = apdu[len++]; + } + service_choice = apdu[len++]; + /* prepare the service request buffer and length */ + service_request_len = apdu_len - (uint16_t)len; + service_request = &apdu[len]; + if (!apdu_confirmed_simple_ack_service(service_choice)) { + if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) { + if (Confirmed_ACK_Function[service_choice].complex != + NULL) { + Confirmed_ACK_Function[service_choice].complex( + service_request, service_request_len, src, + &service_ack_data); + } + } + tsm_free_invoke_id(invoke_id); + } + break; + case PDU_TYPE_SEGMENT_ACK: + /* FIXME: what about a denial of service attack here? + we could check src to see if that matched the tsm */ + tsm_free_invoke_id(invoke_id); + break; + case PDU_TYPE_ERROR: + if (apdu_len < 3) { + break; + } + invoke_id = apdu[1]; + service_choice = apdu[2]; + /* prepare the service request buffer and length */ + service_request_len = apdu_len - 3; + service_request = &apdu[3]; + if (apdu_complex_error(service_choice)) { + if (Error_Function[service_choice].complex) { + Error_Function[service_choice].complex( + src, invoke_id, service_choice, service_request, + service_request_len); + } + } else if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) { + len = bacerror_decode_error_class_and_code( + service_request, service_request_len, &error_class, + &error_code); + if ((len != 0) && (Error_Function[service_choice].error)) { + Error_Function[service_choice].error( + src, invoke_id, (BACNET_ERROR_CLASS)error_class, + (BACNET_ERROR_CODE)error_code); + } + } + tsm_free_invoke_id(invoke_id); + break; + case PDU_TYPE_REJECT: + if (apdu_len < 3) { + break; + } + invoke_id = apdu[1]; + reason = apdu[2]; + if (Reject_Function) { + Reject_Function(src, invoke_id, reason); + } + tsm_free_invoke_id(invoke_id); + break; + case PDU_TYPE_ABORT: + if (apdu_len < 3) { + break; + } + server = apdu[0] & 0x01; + invoke_id = apdu[1]; + reason = apdu[2]; + if (Abort_Function) { + Abort_Function(src, invoke_id, reason, server); + } + tsm_free_invoke_id(invoke_id); + break; +#endif + default: + break; } - return; }