]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2764 in SNORT/snort3 from ~JRITTLE/snort3:iec104_trace_fix to...
authorMike Stepanek (mstepane) <mstepane@cisco.com>
Thu, 25 Feb 2021 15:03:21 +0000 (15:03 +0000)
committerMike Stepanek (mstepane) <mstepane@cisco.com>
Thu, 25 Feb 2021 15:03:21 +0000 (15:03 +0000)
Squashed commit of the following:

commit 888682bccf55b3b6f93c6d2a023fc295e34b99d6
Author: jrittle <jrittle@cisco.com>
Date:   Wed Feb 24 09:40:49 2021 -0500

    iec104: additional input sanitization, syntax, and style changes

src/service_inspectors/iec104/iec104_module.h
src/service_inspectors/iec104/iec104_parse_apdu.cc
src/service_inspectors/iec104/iec104_parse_information_object_elements.cc
src/service_inspectors/iec104/iec104_parse_information_object_elements.h
src/service_inspectors/iec104/iec104_trace.h
src/service_inspectors/iec104/ips_iec104_apci_type.cc
src/service_inspectors/iec104/ips_iec104_asdu_func.cc

index 37ef3245ccd16d9d93bb08693330595ea13c9820..bf25f598bb556c1847047acfb2a421d621f0d7d1 100644 (file)
@@ -66,11 +66,6 @@ public:
     const snort::TraceOption* get_trace_options() const override;
 };
 
-#endif
-
-#ifndef IEC104_RULES
-#define IEC104_RULES
-
 #define IEC104_BAD_LENGTH 1
 #define IEC104_BAD_START 2
 #define IEC104_RESERVED_ASDU_TYPE 3
index f7a852122a06cc55e15726f4d3dc294443ea9f50..187cf1379b0365bab5aec5b614258a6ae053813c 100644 (file)
@@ -1330,7 +1330,7 @@ static void parseIec104GenericIOGroup(const GenericIec104AsduIOGroup* genericIOG
 
 static void parseIec104GenericAsdu(uint32_t asduType, const Iec104ApciI* apci)
 {
-    uint8_t verifiedNumberOfElements = parseIec104Vsq(apci);
+    uint32_t verifiedNumberOfElements = parseIec104Vsq(apci);
     parseIec104CauseOfTx(apci);
     parseIec104TwoOctetCommonAddress(apci);
 
@@ -1339,1187 +1339,1190 @@ static void parseIec104GenericAsdu(uint32_t asduType, const Iec104ApciI* apci)
     genericIOGroup.asduType = asduType;
     genericIOGroup.apduSize = apci->header.length;
 
-    // iterate over the reported number of elements overlaying the structures
-    for (uint32_t i = 0; i < verifiedNumberOfElements; i++)
-    {
-
-        // 
-        // Handle Structure Qualifier == 1
-        //
-        if (apci->asdu.variableStructureQualifier.sq)
-        {
-            // IOA should only be printed on the first iteration in SQ1
-            if (i == 0)
-            {
-                genericIOGroup.includeIOA = true;
-            }
-            else
-            {
-                genericIOGroup.includeIOA = false;
-            }
-
-            // fill genericIOGroup with the appropriate asdu depending on the type
-            switch (asduType)
-            {
-            case IEC104_ASDU_M_SP_NA_1:
-            {
-                // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
-                genericIOGroup.m_sp_na_1IOGroup = &apci->asdu.m_sp_na_1;
-
-                // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
-                // since `i` will be 0 on the first go round this works for all iterations
-                // since C adds based on the pointer type we only need to cast and increment
-                const Iec104M_SP_NA_1_IO_Subgroup* curIo = &apci->asdu.m_sp_na_1.subgroup + i;
-                genericIOGroup.m_sp_na_1IOSubgroup = curIo;
-
-                break;
-            }
-
-            // case IEC104_ASDU_M_SP_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            case IEC104_ASDU_M_DP_NA_1:
-            {
-                // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
-                genericIOGroup.m_dp_na_1IOGroup = &apci->asdu.m_dp_na_1;
-
-                // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
-                // since `i` will be 0 on the first go round this works for all iterations
-                // since C adds based on the pointer type we only need to cast and increment
-                const Iec104M_DP_NA_1_IO_Subgroup* curIo = &apci->asdu.m_dp_na_1.subgroup + i;
-                genericIOGroup.m_dp_na_1IOSubgroup = curIo;
-
-                break;
-            }
-
-            // case IEC104_ASDU_M_DP_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            case IEC104_ASDU_M_ST_NA_1:
-            {
-                // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
-                genericIOGroup.m_st_na_1IOGroup = &apci->asdu.m_st_na_1;
-
-                // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
-                // since `i` will be 0 on the first go round this works for all iterations
-                // since C adds based on the pointer type we only need to cast and increment
-                const Iec104M_ST_NA_1_IO_Subgroup* curIo = &apci->asdu.m_st_na_1.subgroup + i;
-                genericIOGroup.m_st_na_1IOSubgroup = curIo;
-
-                break;
-            }
-
-            // case IEC104_ASDU_M_ST_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            case IEC104_ASDU_M_BO_NA_1:
-            {
-                // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
-                genericIOGroup.m_bo_na_1IOGroup = &apci->asdu.m_bo_na_1;
-
-                // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
-                // since `i` will be 0 on the first go round this works for all iterations
-                // since C adds based on the pointer type we only need to cast and increment
-                const Iec104M_BO_NA_1_IO_Subgroup* curIo = &apci->asdu.m_bo_na_1.subgroup + i;
-                genericIOGroup.m_bo_na_1IOSubgroup = curIo;
-
-                break;
-            }
-
-            // case IEC104_ASDU_M_BO_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            case IEC104_ASDU_M_ME_NA_1:
-            {
-                // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
-                genericIOGroup.m_me_na_1IOGroup = &apci->asdu.m_me_na_1;
-
-                // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
-                // since `i` will be 0 on the first go round this works for all iterations
-                // since C adds based on the pointer type we only need to cast and increment
-                const Iec104M_ME_NA_1_IO_Subgroup* curIo = &apci->asdu.m_me_na_1.subgroup + i;
-                genericIOGroup.m_me_na_1IOSubgroup = curIo;
-
-                break;
-            }
-
-            // case IEC104_ASDU_M_ME_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            case IEC104_ASDU_M_ME_NB_1:
-            {
-                // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
-                genericIOGroup.m_me_nb_1IOGroup = &apci->asdu.m_me_nb_1;
-
-                // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
-                // since `i` will be 0 on the first go round this works for all iterations
-                // since C adds based on the pointer type we only need to cast and increment
-                const Iec104M_ME_NB_1_IO_Subgroup* curIo = &apci->asdu.m_me_nb_1.subgroup + i;
-                genericIOGroup.m_me_nb_1IOSubgroup = curIo;
-
-                break;
-            }
-
-            // case IEC104_ASDU_M_ME_TB_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            case IEC104_ASDU_M_ME_NC_1:
-            {
-                // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
-                genericIOGroup.m_me_nc_1IOGroup = &apci->asdu.m_me_nc_1;
-
-                // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
-                // since `i` will be 0 on the first go round this works for all iterations
-                // since C adds based on the pointer type we only need to cast and increment
-                const Iec104M_ME_NC_1_IO_Subgroup* curIo = &apci->asdu.m_me_nc_1.subgroup + i;
-                genericIOGroup.m_me_nc_1IOSubgroup = curIo;
-
-                break;
-            }
-
-            // case IEC104_ASDU_M_ME_TC_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            case IEC104_ASDU_M_IT_NA_1:
-            {
-                // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
-                genericIOGroup.m_it_na_1IOGroup = &apci->asdu.m_it_na_1;
-
-                // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
-                // since `i` will be 0 on the first go round this works for all iterations
-                // since C adds based on the pointer type we only need to cast and increment
-                const Iec104M_IT_NA_1_IO_Subgroup* curIo = &apci->asdu.m_it_na_1.subgroup + i;
-                genericIOGroup.m_it_na_1IOSubgroup = curIo;
-
-                break;
-            }
-
-            // case IEC104_ASDU_M_IT_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_EP_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_EP_TB_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_EP_TC_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            case IEC104_ASDU_M_PS_NA_1:
-            {
-                // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
-                genericIOGroup.m_ps_na_1IOGroup = &apci->asdu.m_ps_na_1;
-
-                // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
-                // since `i` will be 0 on the first go round this works for all iterations
-                // since C adds based on the pointer type we only need to cast and increment
-                const Iec104M_PS_NA_1_IO_Subgroup* curIo = &apci->asdu.m_ps_na_1.subgroup + i;
-                genericIOGroup.m_ps_na_1IOSubgroup = curIo;
-
-                break;
-            }
-
-            case IEC104_ASDU_M_ME_ND_1:
-            {
-                // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
-                genericIOGroup.m_me_nd_1IOGroup = &apci->asdu.m_me_nd_1;
-
-                // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
-                // since `i` will be 0 on the first go round this works for all iterations
-                // since C adds based on the pointer type we only need to cast and increment
-                const Iec104M_ME_ND_1_IO_Subgroup* curIo = &apci->asdu.m_me_nd_1.subgroup + i;
-                genericIOGroup.m_me_nd_1IOSubgroup = curIo;
-
-                break;
-            }
-
-            // case IEC104_ASDU_M_SP_TB_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_DP_TB_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_ST_TB_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_BO_TB_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_ME_TD_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_ME_TE_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_ME_TF_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_IT_TB_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_EP_TD_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_EP_TE_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_EP_TF_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_SC_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_DC_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_RC_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_SE_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_SE_NB_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_SE_NC_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_BO_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_SC_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_DC_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_RC_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_SE_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_SE_TB_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_SE_TC_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_BO_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_M_EI_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_IC_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_CI_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_RD_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_CS_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_TS_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_RP_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_CD_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_C_TS_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            //case IEC104_ASDU_P_ME_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            //case IEC104_ASDU_P_ME_NB_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            //case IEC104_ASDU_P_ME_NC_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_P_AC_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_F_FR_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_F_SR_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_F_SC_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_F_LS_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_F_AF_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            // case IEC104_ASDU_F_SG_NA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            case IEC104_ASDU_F_DR_TA_1:
-            {
-                // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
-                genericIOGroup.f_dr_ta_1IOGroup = &apci->asdu.f_dr_ta_1;
-
-                // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
-                // since `i` will be 0 on the first go round this works for all iterations
-                // since C adds based on the pointer type we only need to cast and increment
-                const Iec104F_DR_TA_1_IO_Subgroup* curIo = &apci->asdu.f_dr_ta_1.subgroup + i;
-                genericIOGroup.f_dr_ta_1IOSubgroup = curIo;
-
-                break;
-            }
-
-            // case IEC104_ASDU_F_SC_NB_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            default:
-            {
-                // SQ1 ASDU parsing not implemented for this type
-            }
-            }
-
-            // parse the new subgroup
-            parseIec104GenericIOGroup(&genericIOGroup);
-
-        }
-        //
-        // Handle Structure Qualifier == 0
-        //
-        else
-        {
-            // the IOA should always be included for SQ0
-            genericIOGroup.includeIOA = true;
-
-            // fill genericIOGroup with the appropriate asdu depending on the type
-            switch (asduType)
-            {
-            case IEC104_ASDU_M_SP_NA_1:
-            {
-                // increment the information object block pointer by the size of the M_SP_NA_1_IO_Group struct
-                const Iec104M_SP_NA_1_IO_Group* curIo =
-                    (const Iec104M_SP_NA_1_IO_Group*) &apci->asdu.m_sp_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_sp_na_1IOGroup = curIo;
-                genericIOGroup.m_sp_na_1IOSubgroup = &curIo->subgroup;
-
-                break;
-            }
-
-            case IEC104_ASDU_M_SP_TA_1:
-            {
-                // increment the information object block pointer by the size of the M_SP_TA_1_IO_Group struct
-                const Iec104M_SP_TA_1_IO_Group* curIo =
-                    (const Iec104M_SP_TA_1_IO_Group*) &apci->asdu.m_sp_ta_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_sp_ta_1IOGroup = curIo;
-                genericIOGroup.m_sp_ta_1IOSubgroup = &curIo->subgroup;
-
-                break;
-            }
-
-            case IEC104_ASDU_M_DP_NA_1:
-            {
-                // increment the information object block pointer by the size of the M_DP_NA_1_IO_Group struct
-                const Iec104M_DP_NA_1_IO_Group* curIo =
-                    (const Iec104M_DP_NA_1_IO_Group*) &apci->asdu.m_dp_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_dp_na_1IOGroup = curIo;
-                genericIOGroup.m_dp_na_1IOSubgroup = &curIo->subgroup;
-
-                break;
-            }
+    // make sure the number of elements value is acceptable
+    if (verifiedNumberOfElements > 0 && verifiedNumberOfElements <= 255) {
+        // iterate over the reported number of elements overlaying the structures
+        for (uint32_t i = 0; i < verifiedNumberOfElements; i++)
+        {
+
+            // 
+            // Handle Structure Qualifier == 1
+            //
+            if (apci->asdu.variableStructureQualifier.sq)
+            {
+                // IOA should only be printed on the first iteration in SQ1
+                if (i == 0)
+                {
+                    genericIOGroup.includeIOA = true;
+                }
+                else
+                {
+                    genericIOGroup.includeIOA = false;
+                }
+
+                // fill genericIOGroup with the appropriate asdu depending on the type
+                switch (asduType)
+                {
+                case IEC104_ASDU_M_SP_NA_1:
+                {
+                    // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+                    genericIOGroup.m_sp_na_1IOGroup = &apci->asdu.m_sp_na_1;
+
+                    // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+                    // since `i` will be 0 on the first go round this works for all iterations
+                    // since C adds based on the pointer type we only need to cast and increment
+                    const Iec104M_SP_NA_1_IO_Subgroup* curIo = &apci->asdu.m_sp_na_1.subgroup + i;
+                    genericIOGroup.m_sp_na_1IOSubgroup = curIo;
+
+                    break;
+                }
+
+                // case IEC104_ASDU_M_SP_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
+
+                case IEC104_ASDU_M_DP_NA_1:
+                {
+                    // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+                    genericIOGroup.m_dp_na_1IOGroup = &apci->asdu.m_dp_na_1;
+
+                    // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+                    // since `i` will be 0 on the first go round this works for all iterations
+                    // since C adds based on the pointer type we only need to cast and increment
+                    const Iec104M_DP_NA_1_IO_Subgroup* curIo = &apci->asdu.m_dp_na_1.subgroup + i;
+                    genericIOGroup.m_dp_na_1IOSubgroup = curIo;
+
+                    break;
+                }
+
+                // case IEC104_ASDU_M_DP_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
+
+                case IEC104_ASDU_M_ST_NA_1:
+                {
+                    // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+                    genericIOGroup.m_st_na_1IOGroup = &apci->asdu.m_st_na_1;
+
+                    // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+                    // since `i` will be 0 on the first go round this works for all iterations
+                    // since C adds based on the pointer type we only need to cast and increment
+                    const Iec104M_ST_NA_1_IO_Subgroup* curIo = &apci->asdu.m_st_na_1.subgroup + i;
+                    genericIOGroup.m_st_na_1IOSubgroup = curIo;
+
+                    break;
+                }
+
+                // case IEC104_ASDU_M_ST_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
+
+                case IEC104_ASDU_M_BO_NA_1:
+                {
+                    // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+                    genericIOGroup.m_bo_na_1IOGroup = &apci->asdu.m_bo_na_1;
+
+                    // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+                    // since `i` will be 0 on the first go round this works for all iterations
+                    // since C adds based on the pointer type we only need to cast and increment
+                    const Iec104M_BO_NA_1_IO_Subgroup* curIo = &apci->asdu.m_bo_na_1.subgroup + i;
+                    genericIOGroup.m_bo_na_1IOSubgroup = curIo;
+
+                    break;
+                }
+
+                // case IEC104_ASDU_M_BO_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
+
+                case IEC104_ASDU_M_ME_NA_1:
+                {
+                    // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+                    genericIOGroup.m_me_na_1IOGroup = &apci->asdu.m_me_na_1;
+
+                    // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+                    // since `i` will be 0 on the first go round this works for all iterations
+                    // since C adds based on the pointer type we only need to cast and increment
+                    const Iec104M_ME_NA_1_IO_Subgroup* curIo = &apci->asdu.m_me_na_1.subgroup + i;
+                    genericIOGroup.m_me_na_1IOSubgroup = curIo;
+
+                    break;
+                }
+
+                // case IEC104_ASDU_M_ME_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
+
+                case IEC104_ASDU_M_ME_NB_1:
+                {
+                    // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+                    genericIOGroup.m_me_nb_1IOGroup = &apci->asdu.m_me_nb_1;
+
+                    // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+                    // since `i` will be 0 on the first go round this works for all iterations
+                    // since C adds based on the pointer type we only need to cast and increment
+                    const Iec104M_ME_NB_1_IO_Subgroup* curIo = &apci->asdu.m_me_nb_1.subgroup + i;
+                    genericIOGroup.m_me_nb_1IOSubgroup = curIo;
+
+                    break;
+                }
+
+                // case IEC104_ASDU_M_ME_TB_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_DP_TA_1:
-            {
-                // increment the information object block pointer by the size of the M_DP_TA_1_IO_Group struct
-                const Iec104M_DP_TA_1_IO_Group* curIo =
-                    (const Iec104M_DP_TA_1_IO_Group*) &apci->asdu.m_dp_ta_1 + i;
+                case IEC104_ASDU_M_ME_NC_1:
+                {
+                    // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+                    genericIOGroup.m_me_nc_1IOGroup = &apci->asdu.m_me_nc_1;
 
-                // print the SQ0 IO block
-                genericIOGroup.m_dp_ta_1IOGroup = curIo;
-                genericIOGroup.m_dp_ta_1IOSubgroup = &curIo->subgroup;
+                    // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+                    // since `i` will be 0 on the first go round this works for all iterations
+                    // since C adds based on the pointer type we only need to cast and increment
+                    const Iec104M_ME_NC_1_IO_Subgroup* curIo = &apci->asdu.m_me_nc_1.subgroup + i;
+                    genericIOGroup.m_me_nc_1IOSubgroup = curIo;
 
-                break;
-            }
+                    break;
+                }
 
-            case IEC104_ASDU_M_ST_NA_1:
-            {
-                // increment the information object block pointer by the size of the M_ST_NA_1_IO_Group struct
-                const Iec104M_ST_NA_1_IO_Group* curIo =
-                    (const Iec104M_ST_NA_1_IO_Group*) &apci->asdu.m_st_na_1 + i;
+                // case IEC104_ASDU_M_ME_TC_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                // print the SQ0 IO block
-                genericIOGroup.m_st_na_1IOGroup = curIo;
-                genericIOGroup.m_st_na_1IOSubgroup = &curIo->subgroup;
+                case IEC104_ASDU_M_IT_NA_1:
+                {
+                    // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+                    genericIOGroup.m_it_na_1IOGroup = &apci->asdu.m_it_na_1;
 
-                break;
-            }
+                    // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+                    // since `i` will be 0 on the first go round this works for all iterations
+                    // since C adds based on the pointer type we only need to cast and increment
+                    const Iec104M_IT_NA_1_IO_Subgroup* curIo = &apci->asdu.m_it_na_1.subgroup + i;
+                    genericIOGroup.m_it_na_1IOSubgroup = curIo;
 
-            case IEC104_ASDU_M_ST_TA_1:
-            {
-                // increment the information object block pointer by the size of the M_ST_TA_1_IO_Group struct
-                const Iec104M_ST_TA_1_IO_Group* curIo =
-                    (const Iec104M_ST_TA_1_IO_Group*) &apci->asdu.m_st_ta_1 + i;
+                    break;
+                }
 
-                // print the SQ0 IO block
-                genericIOGroup.m_st_ta_1IOGroup = curIo;
-                genericIOGroup.m_st_ta_1IOSubgroup = &curIo->subgroup;
+                // case IEC104_ASDU_M_IT_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                break;
-            }
+                // case IEC104_ASDU_M_EP_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_BO_NA_1:
-            {
-                // increment the information object block pointer by the size of the M_BO_NA_1_IO_Group struct
-                const Iec104M_BO_NA_1_IO_Group* curIo =
-                    (const Iec104M_BO_NA_1_IO_Group*) &apci->asdu.m_bo_na_1 + i;
+                // case IEC104_ASDU_M_EP_TB_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                // print the SQ0 IO block
-                genericIOGroup.m_bo_na_1IOGroup = curIo;
-                genericIOGroup.m_bo_na_1IOSubgroup = &curIo->subgroup;
+                // case IEC104_ASDU_M_EP_TC_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                break;
-            }
+                case IEC104_ASDU_M_PS_NA_1:
+                {
+                    // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+                    genericIOGroup.m_ps_na_1IOGroup = &apci->asdu.m_ps_na_1;
 
-            case IEC104_ASDU_M_BO_TA_1:
-            {
-                // increment the information object block pointer by the size of the M_BO_TA_1_IO_Group struct
-                const Iec104M_BO_TA_1_IO_Group* curIo =
-                    (const Iec104M_BO_TA_1_IO_Group*) &apci->asdu.m_bo_ta_1 + i;
+                    // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+                    // since `i` will be 0 on the first go round this works for all iterations
+                    // since C adds based on the pointer type we only need to cast and increment
+                    const Iec104M_PS_NA_1_IO_Subgroup* curIo = &apci->asdu.m_ps_na_1.subgroup + i;
+                    genericIOGroup.m_ps_na_1IOSubgroup = curIo;
 
-                // print the SQ0 IO block
-                genericIOGroup.m_bo_ta_1IOGroup = curIo;
-                genericIOGroup.m_bo_ta_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
 
-                break;
-            }
+                case IEC104_ASDU_M_ME_ND_1:
+                {
+                    // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+                    genericIOGroup.m_me_nd_1IOGroup = &apci->asdu.m_me_nd_1;
 
-            case IEC104_ASDU_M_ME_NA_1:
-            {
-                // increment the information object block pointer by the size of the M_ME_NA_1_IO_Group struct
-                const Iec104M_ME_NA_1_IO_Group* curIo =
-                    (const Iec104M_ME_NA_1_IO_Group*) &apci->asdu.m_me_na_1 + i;
+                    // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+                    // since `i` will be 0 on the first go round this works for all iterations
+                    // since C adds based on the pointer type we only need to cast and increment
+                    const Iec104M_ME_ND_1_IO_Subgroup* curIo = &apci->asdu.m_me_nd_1.subgroup + i;
+                    genericIOGroup.m_me_nd_1IOSubgroup = curIo;
 
-                // print the SQ0 IO block
-                genericIOGroup.m_me_na_1IOGroup = curIo;
-                genericIOGroup.m_me_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
 
-                break;
-            }
+                // case IEC104_ASDU_M_SP_TB_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_ME_TA_1:
-            {
-                // increment the information object block pointer by the size of the M_ME_TA_1_IO_Group struct
-                const Iec104M_ME_TA_1_IO_Group* curIo =
-                    (const Iec104M_ME_TA_1_IO_Group*) &apci->asdu.m_me_ta_1 + i;
+                // case IEC104_ASDU_M_DP_TB_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                // print the SQ0 IO block
-                genericIOGroup.m_me_ta_1IOGroup = curIo;
-                genericIOGroup.m_me_ta_1IOSubgroup = &curIo->subgroup;
+                // case IEC104_ASDU_M_ST_TB_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                break;
-            }
+                // case IEC104_ASDU_M_BO_TB_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_ME_NB_1:
-            {
-                // increment the information object block pointer by the size of the M_ME_NB_1_IO_Group struct
-                const Iec104M_ME_NB_1_IO_Group* curIo =
-                    (const Iec104M_ME_NB_1_IO_Group*) &apci->asdu.m_me_nb_1 + i;
+                // case IEC104_ASDU_M_ME_TD_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                // print the SQ0 IO block
-                genericIOGroup.m_me_nb_1IOGroup = curIo;
-                genericIOGroup.m_me_nb_1IOSubgroup = &curIo->subgroup;
+                // case IEC104_ASDU_M_ME_TE_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                break;
-            }
+                // case IEC104_ASDU_M_ME_TF_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_ME_TB_1:
-            {
-                // increment the information object block pointer by the size of the M_ME_TB_1_IO_Group struct
-                const Iec104M_ME_TB_1_IO_Group* curIo =
-                    (const Iec104M_ME_TB_1_IO_Group*) &apci->asdu.m_me_tb_1 + i;
+                // case IEC104_ASDU_M_IT_TB_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                // print the SQ0 IO block
-                genericIOGroup.m_me_tb_1IOGroup = curIo;
-                genericIOGroup.m_me_tb_1IOSubgroup = &curIo->subgroup;
+                // case IEC104_ASDU_M_EP_TD_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                break;
-            }
+                // case IEC104_ASDU_M_EP_TE_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_ME_NC_1:
-            {
-                // increment the information object block pointer by the size of the M_ME_NC_1_IO_Group struct
-                const Iec104M_ME_NC_1_IO_Group* curIo =
-                    (const Iec104M_ME_NC_1_IO_Group*) &apci->asdu.m_me_nc_1 + i;
+                // case IEC104_ASDU_M_EP_TF_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                // print the SQ0 IO block
-                genericIOGroup.m_me_nc_1IOGroup = curIo;
-                genericIOGroup.m_me_nc_1IOSubgroup = &curIo->subgroup;
+                // case IEC104_ASDU_C_SC_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-                break;
-            }
+                // case IEC104_ASDU_C_DC_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_ME_TC_1:
-            {
-                // increment the information object block pointer by the size of the M_ME_TC_1_IO_Group struct
-                const Iec104M_ME_TC_1_IO_Group* curIo =
-                    (const Iec104M_ME_TC_1_IO_Group*) &apci->asdu.m_me_tc_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_me_tc_1IOGroup = curIo;
-                genericIOGroup.m_me_tc_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_RC_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_IT_NA_1:
-            {
-                // increment the information object block pointer by the size of the M_IT_NA_1_IO_Group struct
-                const Iec104M_IT_NA_1_IO_Group* curIo =
-                    (const Iec104M_IT_NA_1_IO_Group*) &apci->asdu.m_it_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_it_na_1IOGroup = curIo;
-                genericIOGroup.m_it_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_SE_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_IT_TA_1:
-            {
-                // increment the information object block pointer by the size of the M_IT_TA_1_IO_Group struct
-                const Iec104M_IT_TA_1_IO_Group* curIo =
-                    (const Iec104M_IT_TA_1_IO_Group*) &apci->asdu.m_it_ta_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_it_ta_1IOGroup = curIo;
-                genericIOGroup.m_it_ta_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_SE_NB_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_EP_TA_1:
-            {
-                // increment the information object block pointer by the size of the M_EP_TA_1_IO_Group struct
-                const Iec104M_EP_TA_1_IO_Group* curIo =
-                    (const Iec104M_EP_TA_1_IO_Group*) &apci->asdu.m_ep_ta_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_ep_ta_1IOGroup = curIo;
-                genericIOGroup.m_ep_ta_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_SE_NC_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_EP_TB_1:
-            {
-                // increment the information object block pointer by the size of the M_EP_TB_1_IO_Group struct
-                const Iec104M_EP_TB_1_IO_Group* curIo =
-                    (const Iec104M_EP_TB_1_IO_Group*) &apci->asdu.m_ep_tb_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_ep_tb_1IOGroup = curIo;
-                genericIOGroup.m_ep_tb_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_BO_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_EP_TC_1:
-            {
-                // increment the information object block pointer by the size of the M_EP_TC_1_IO_Group struct
-                const Iec104M_EP_TC_1_IO_Group* curIo =
-                    (const Iec104M_EP_TC_1_IO_Group*) &apci->asdu.m_ep_tc_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_ep_tc_1IOGroup = curIo;
-                genericIOGroup.m_ep_tc_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_SC_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_PS_NA_1:
-            {
-                // increment the information object block pointer by the size of the M_PS_NA_1_IO_Group struct
-                const Iec104M_PS_NA_1_IO_Group* curIo =
-                    (const Iec104M_PS_NA_1_IO_Group*) &apci->asdu.m_ps_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_ps_na_1IOGroup = curIo;
-                genericIOGroup.m_ps_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_DC_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_ME_ND_1:
-            {
-                // increment the information object block pointer by the size of the M_ME_ND_1_IO_Group struct
-                const Iec104M_ME_ND_1_IO_Group* curIo =
-                    (const Iec104M_ME_ND_1_IO_Group*) &apci->asdu.m_me_nd_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_me_nd_1IOGroup = curIo;
-                genericIOGroup.m_me_nd_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_RC_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_SP_TB_1:
-            {
-                // increment the information object block pointer by the size of the M_SP_TB_1_IO_Group struct
-                const Iec104M_SP_TB_1_IO_Group* curIo =
-                    (const Iec104M_SP_TB_1_IO_Group*) &apci->asdu.m_sp_tb_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_sp_tb_1IOGroup = curIo;
-                genericIOGroup.m_sp_tb_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_SE_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_DP_TB_1:
-            {
-                // increment the information object block pointer by the size of the M_DP_TB_1_IO_Group struct
-                const Iec104M_DP_TB_1_IO_Group* curIo =
-                    (const Iec104M_DP_TB_1_IO_Group*) &apci->asdu.m_dp_tb_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_dp_tb_1IOGroup = curIo;
-                genericIOGroup.m_dp_tb_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_SE_TB_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_ST_TB_1:
-            {
-                // increment the information object block pointer by the size of the M_ST_TB_1_IO_Group struct
-                const Iec104M_ST_TB_1_IO_Group* curIo =
-                    (const Iec104M_ST_TB_1_IO_Group*) &apci->asdu.m_st_tb_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_st_tb_1IOGroup = curIo;
-                genericIOGroup.m_st_tb_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_SE_TC_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_BO_TB_1:
-            {
-                // increment the information object block pointer by the size of the M_BO_TB_1_IO_Group struct
-                const Iec104M_BO_TB_1_IO_Group* curIo =
-                    (const Iec104M_BO_TB_1_IO_Group*) &apci->asdu.m_bo_tb_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_bo_tb_1IOGroup = curIo;
-                genericIOGroup.m_bo_tb_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_BO_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_ME_TD_1:
-            {
-                // increment the information object block pointer by the size of the M_ME_TD_1_IO_Group struct
-                const Iec104M_ME_TD_1_IO_Group* curIo =
-                    (const Iec104M_ME_TD_1_IO_Group*) &apci->asdu.m_me_td_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_me_td_1IOGroup = curIo;
-                genericIOGroup.m_me_td_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_M_EI_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_ME_TE_1:
-            {
-                // increment the information object block pointer by the size of the M_ME_TE_1_IO_Group struct
-                const Iec104M_ME_TE_1_IO_Group* curIo =
-                    (const Iec104M_ME_TE_1_IO_Group*) &apci->asdu.m_me_te_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_me_te_1IOGroup = curIo;
-                genericIOGroup.m_me_te_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_IC_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_ME_TF_1:
-            {
-                // increment the information object block pointer by the size of the M_ME_TF_1_IO_Group struct
-                const Iec104M_ME_TF_1_IO_Group* curIo =
-                    (const Iec104M_ME_TF_1_IO_Group*) &apci->asdu.m_me_tf_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_me_tf_1IOGroup = curIo;
-                genericIOGroup.m_me_tf_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_CI_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_IT_TB_1:
-            {
-                // increment the information object block pointer by the size of the M_IT_TB_1_IO_Group struct
-                const Iec104M_IT_TB_1_IO_Group* curIo =
-                    (const Iec104M_IT_TB_1_IO_Group*) &apci->asdu.m_it_tb_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_it_tb_1IOGroup = curIo;
-                genericIOGroup.m_it_tb_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_RD_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_EP_TD_1:
-            {
-                // increment the information object block pointer by the size of the M_EP_TD_1_IO_Group struct
-                const Iec104M_EP_TD_1_IO_Group* curIo =
-                    (const Iec104M_EP_TD_1_IO_Group*) &apci->asdu.m_ep_td_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_ep_td_1IOGroup = curIo;
-                genericIOGroup.m_ep_td_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_CS_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_EP_TE_1:
-            {
-                // increment the information object block pointer by the size of the M_EP_TE_1_IO_Group struct
-                const Iec104M_EP_TE_1_IO_Group* curIo =
-                    (const Iec104M_EP_TE_1_IO_Group*) &apci->asdu.m_ep_te_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_ep_te_1IOGroup = curIo;
-                genericIOGroup.m_ep_te_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_TS_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_M_EP_TF_1:
-            {
-                // increment the information object block pointer by the size of the M_EP_TF_1_IO_Group struct
-                const Iec104M_EP_TF_1_IO_Group* curIo =
-                    (const Iec104M_EP_TF_1_IO_Group*) &apci->asdu.m_ep_tf_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_ep_tf_1IOGroup = curIo;
-                genericIOGroup.m_ep_tf_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_RP_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_C_SC_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_SC_NA_1_IO_Group struct
-                const Iec104C_SC_NA_1_IO_Group* curIo =
-                    (const Iec104C_SC_NA_1_IO_Group*) &apci->asdu.c_sc_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_sc_na_1IOGroup = curIo;
-                genericIOGroup.c_sc_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_CD_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_C_DC_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_DC_NA_1_IO_Group struct
-                const Iec104C_DC_NA_1_IO_Group* curIo =
-                    (const Iec104C_DC_NA_1_IO_Group*) &apci->asdu.c_dc_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_dc_na_1IOGroup = curIo;
-                genericIOGroup.c_dc_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_C_TS_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_C_RC_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_RC_NA_1_IO_Group struct
-                const Iec104C_RC_NA_1_IO_Group* curIo =
-                    (const Iec104C_RC_NA_1_IO_Group*) &apci->asdu.c_rc_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_rc_na_1IOGroup = curIo;
-                genericIOGroup.c_rc_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                //case IEC104_ASDU_P_ME_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_C_SE_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_SE_NA_1_IO_Group struct
-                const Iec104C_SE_NA_1_IO_Group* curIo =
-                    (const Iec104C_SE_NA_1_IO_Group*) &apci->asdu.c_se_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_se_na_1IOGroup = curIo;
-                genericIOGroup.c_se_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                //case IEC104_ASDU_P_ME_NB_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_C_SE_NB_1:
-            {
-                // increment the information object block pointer by the size of the C_SE_NB_1_IO_Group struct
-                const Iec104C_SE_NB_1_IO_Group* curIo =
-                    (const Iec104C_SE_NB_1_IO_Group*) &apci->asdu.c_se_nb_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_se_nb_1IOGroup = curIo;
-                genericIOGroup.c_se_nb_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                //case IEC104_ASDU_P_ME_NC_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_C_SE_NC_1:
-            {
-                // increment the information object block pointer by the size of the C_SE_NC_1_IO_Group struct
-                const Iec104C_SE_NC_1_IO_Group* curIo =
-                    (const Iec104C_SE_NC_1_IO_Group*) &apci->asdu.c_se_nc_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_se_nc_1IOGroup = curIo;
-                genericIOGroup.c_se_nc_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_P_AC_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_C_BO_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_BO_NA_1_IO_Group struct
-                const Iec104C_BO_NA_1_IO_Group* curIo =
-                    (const Iec104C_BO_NA_1_IO_Group*) &apci->asdu.c_bo_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_bo_na_1IOGroup = curIo;
-                genericIOGroup.c_bo_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_F_FR_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_C_SC_TA_1:
-            {
-                // increment the information object block pointer by the size of the C_SC_TA_1_IO_Group struct
-                const Iec104C_SC_TA_1_IO_Group* curIo =
-                    (const Iec104C_SC_TA_1_IO_Group*) &apci->asdu.c_sc_ta_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_sc_ta_1IOGroup = curIo;
-                genericIOGroup.c_sc_ta_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_DC_TA_1:
-            {
-                // increment the information object block pointer by the size of the C_DC_TA_1_IO_Group struct
-                const Iec104C_DC_TA_1_IO_Group* curIo =
-                    (const Iec104C_DC_TA_1_IO_Group*) &apci->asdu.c_dc_ta_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_dc_ta_1IOGroup = curIo;
-                genericIOGroup.c_dc_ta_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_RC_TA_1:
-            {
-                // increment the information object block pointer by the size of the C_RC_TA_1_IO_Group struct
-                const Iec104C_RC_TA_1_IO_Group* curIo =
-                    (const Iec104C_RC_TA_1_IO_Group*) &apci->asdu.c_rc_ta_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_rc_ta_1IOGroup = curIo;
-                genericIOGroup.c_rc_ta_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_SE_TA_1:
-            {
-                // increment the information object block pointer by the size of the C_SE_TA_1_IO_Group struct
-                const Iec104C_SE_TA_1_IO_Group* curIo =
-                    (const Iec104C_SE_TA_1_IO_Group*) &apci->asdu.c_se_ta_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_se_ta_1IOGroup = curIo;
-                genericIOGroup.c_se_ta_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_SE_TB_1:
-            {
-                // increment the information object block pointer by the size of the C_SE_TB_1_IO_Group struct
-                const Iec104C_SE_TB_1_IO_Group* curIo =
-                    (const Iec104C_SE_TB_1_IO_Group*) &apci->asdu.c_se_tb_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_se_tb_1IOGroup = curIo;
-                genericIOGroup.c_se_tb_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_SE_TC_1:
-            {
-                // increment the information object block pointer by the size of the C_SE_TC_1_IO_Group struct
-                const Iec104C_SE_TC_1_IO_Group* curIo =
-                    (const Iec104C_SE_TC_1_IO_Group*) &apci->asdu.c_se_tc_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_se_tc_1IOGroup = curIo;
-                genericIOGroup.c_se_tc_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_BO_TA_1:
-            {
-                // increment the information object block pointer by the size of the C_BO_TA_1_IO_Group struct
-                const Iec104C_BO_TA_1_IO_Group* curIo =
-                    (const Iec104C_BO_TA_1_IO_Group*) &apci->asdu.c_bo_ta_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_bo_ta_1IOGroup = curIo;
-                genericIOGroup.c_bo_ta_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_M_EI_NA_1:
-            {
-                // increment the information object block pointer by the size of the M_EI_NA_1_IO_Group struct
-                const Iec104M_EI_NA_1_IO_Group* curIo =
-                    (const Iec104M_EI_NA_1_IO_Group*) &apci->asdu.m_ei_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.m_ei_na_1IOGroup = curIo;
-                genericIOGroup.m_ei_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_IC_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_IC_NA_1_IO_Group struct
-                const Iec104C_IC_NA_1_IO_Group* curIo =
-                    (const Iec104C_IC_NA_1_IO_Group*) &apci->asdu.c_ic_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_ic_na_1IOGroup = curIo;
-                genericIOGroup.c_ic_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_CI_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_CI_NA_1_IO_Group struct
-                const Iec104C_CI_NA_1_IO_Group* curIo =
-                    (const Iec104C_CI_NA_1_IO_Group*) &apci->asdu.c_ci_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_ci_na_1IOGroup = curIo;
-                genericIOGroup.c_ci_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_RD_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_RD_NA_1_IO_Group struct
-                const Iec104C_RD_NA_1_IO_Group* curIo =
-                    (const Iec104C_RD_NA_1_IO_Group*) &apci->asdu.c_rd_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_rd_na_1IOGroup = curIo;
-                genericIOGroup.c_rd_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_CS_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_CS_NA_1_IO_Group struct
-                const Iec104C_CS_NA_1_IO_Group* curIo =
-                    (const Iec104C_CS_NA_1_IO_Group*) &apci->asdu.c_cs_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_cs_na_1IOGroup = curIo;
-                genericIOGroup.c_cs_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_TS_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_TS_NA_1_IO_Group struct
-                const Iec104C_TS_NA_1_IO_Group* curIo =
-                    (const Iec104C_TS_NA_1_IO_Group*) &apci->asdu.c_ts_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_ts_na_1IOGroup = curIo;
-                genericIOGroup.c_ts_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_F_SR_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_C_RP_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_RP_NA_1_IO_Group struct
-                const Iec104C_RP_NA_1_IO_Group* curIo =
-                    (const Iec104C_RP_NA_1_IO_Group*) &apci->asdu.c_rp_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_rp_na_1IOGroup = curIo;
-                genericIOGroup.c_rp_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_CD_NA_1:
-            {
-                // increment the information object block pointer by the size of the C_CD_NA_1_IO_Group struct
-                const Iec104C_CD_NA_1_IO_Group* curIo =
-                    (const Iec104C_CD_NA_1_IO_Group*) &apci->asdu.c_cd_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_cd_na_1IOGroup = curIo;
-                genericIOGroup.c_cd_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
-
-            case IEC104_ASDU_C_TS_TA_1:
-            {
-                // increment the information object block pointer by the size of the C_TS_TA_1_IO_Group struct
-                const Iec104C_TS_TA_1_IO_Group* curIo =
-                    (const Iec104C_TS_TA_1_IO_Group*) &apci->asdu.c_ts_ta_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.c_ts_ta_1IOGroup = curIo;
-                genericIOGroup.c_ts_ta_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_F_SC_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_P_ME_NA_1:
-            {
-                // increment the information object block pointer by the size of the P_ME_NA_1_IO_Group struct
-                const Iec104P_ME_NA_1_IO_Group* curIo =
-                    (const Iec104P_ME_NA_1_IO_Group*) &apci->asdu.p_me_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.p_me_na_1IOGroup = curIo;
-                genericIOGroup.p_me_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_F_LS_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_P_ME_NB_1:
-            {
-                // increment the information object block pointer by the size of the P_ME_NB_1_IO_Group struct
-                const Iec104P_ME_NB_1_IO_Group* curIo =
-                    (const Iec104P_ME_NB_1_IO_Group*) &apci->asdu.p_me_nb_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.p_me_nb_1IOGroup = curIo;
-                genericIOGroup.p_me_nb_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_F_AF_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_P_ME_NC_1:
-            {
-                // increment the information object block pointer by the size of the P_ME_NC_1_IO_Group struct
-                const Iec104P_ME_NC_1_IO_Group* curIo =
-                    (const Iec104P_ME_NC_1_IO_Group*) &apci->asdu.p_me_nc_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.p_me_nc_1IOGroup = curIo;
-                genericIOGroup.p_me_nc_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_F_SG_NA_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_P_AC_NA_1:
-            {
-                // increment the information object block pointer by the size of the P_AC_NA_1_IO_Group struct
-                const Iec104P_AC_NA_1_IO_Group* curIo =
-                    (const Iec104P_AC_NA_1_IO_Group*) &apci->asdu.p_ac_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.p_ac_na_1IOGroup = curIo;
-                genericIOGroup.p_ac_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                case IEC104_ASDU_F_DR_TA_1:
+                {
+                    // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+                    genericIOGroup.f_dr_ta_1IOGroup = &apci->asdu.f_dr_ta_1;
 
-            case IEC104_ASDU_F_FR_NA_1:
-            {
-                // increment the information object block pointer by the size of the F_FR_NA_1_IO_Group struct
-                const Iec104F_FR_NA_1_IO_Group* curIo =
-                    (const Iec104F_FR_NA_1_IO_Group*) &apci->asdu.f_fr_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.f_fr_na_1IOGroup = curIo;
-                genericIOGroup.f_fr_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                    // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+                    // since `i` will be 0 on the first go round this works for all iterations
+                    // since C adds based on the pointer type we only need to cast and increment
+                    const Iec104F_DR_TA_1_IO_Subgroup* curIo = &apci->asdu.f_dr_ta_1.subgroup + i;
+                    genericIOGroup.f_dr_ta_1IOSubgroup = curIo;
 
-            case IEC104_ASDU_F_SR_NA_1:
-            {
-                // increment the information object block pointer by the size of the F_SR_NA_1_IO_Group struct
-                const Iec104F_SR_NA_1_IO_Group* curIo =
-                    (const Iec104F_SR_NA_1_IO_Group*) &apci->asdu.f_sr_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.f_sr_na_1IOGroup = curIo;
-                genericIOGroup.f_sr_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                    break;
+                }
 
-            case IEC104_ASDU_F_SC_NA_1:
-            {
-                // increment the information object block pointer by the size of the F_SC_NA_1_IO_Group struct
-                const Iec104F_SC_NA_1_IO_Group* curIo =
-                    (const Iec104F_SC_NA_1_IO_Group*) &apci->asdu.f_sc_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.f_sc_na_1IOGroup = curIo;
-                genericIOGroup.f_sc_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // case IEC104_ASDU_F_SC_NB_1
+                // path doesn't happen as it gets caught during the ASDU check
 
-            case IEC104_ASDU_F_LS_NA_1:
-            {
-                // increment the information object block pointer by the size of the F_LS_NA_1_IO_Group struct
-                const Iec104F_LS_NA_1_IO_Group* curIo =
-                    (const Iec104F_LS_NA_1_IO_Group*) &apci->asdu.f_ls_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.f_ls_na_1IOGroup = curIo;
-                genericIOGroup.f_ls_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                default:
+                {
+                    // SQ1 ASDU parsing not implemented for this type
+                }
+                }
 
-            case IEC104_ASDU_F_AF_NA_1:
-            {
-                // increment the information object block pointer by the size of the F_AF_NA_1_IO_Group struct
-                const Iec104F_AF_NA_1_IO_Group* curIo =
-                    (const Iec104F_AF_NA_1_IO_Group*) &apci->asdu.f_af_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.f_af_na_1IOGroup = curIo;
-                genericIOGroup.f_af_na_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // parse the new subgroup
+                parseIec104GenericIOGroup(&genericIOGroup);
 
-            case IEC104_ASDU_F_SG_NA_1:
-            {
-                // increment the information object block pointer by the size of the F_SG_NA_1_IO_Group struct
-                const Iec104F_SG_NA_1_IO_Group* curIo =
-                    (const Iec104F_SG_NA_1_IO_Group*) &apci->asdu.f_sg_na_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.f_sg_na_1IOGroup = curIo;
-                genericIOGroup.f_sg_na_1IOSubgroup = &curIo->subgroup;
-                break;
             }
-
-            // case IEC104_ASDU_F_DR_TA_1
-            // path doesn't happen as it gets caught during the ASDU check
-
-            case IEC104_ASDU_F_SC_NB_1:
+            //
+            // Handle Structure Qualifier == 0
+            //
+            else
             {
-                // increment the information object block pointer by the size of the F_SC_NB_1_IO_Group struct
-                const Iec104F_SC_NB_1_IO_Group* curIo =
-                    (const Iec104F_SC_NB_1_IO_Group*) &apci->asdu.f_sc_nb_1 + i;
-
-                // print the SQ0 IO block
-                genericIOGroup.f_sc_nb_1IOGroup = curIo;
-                genericIOGroup.f_sc_nb_1IOSubgroup = &curIo->subgroup;
-                break;
-            }
+                // the IOA should always be included for SQ0
+                genericIOGroup.includeIOA = true;
 
-            default:
-            {
-                // SQ0 ASDU parsing not implemented for this type
+                // fill genericIOGroup with the appropriate asdu depending on the type
+                switch (asduType)
+                {
+                case IEC104_ASDU_M_SP_NA_1:
+                {
+                    // increment the information object block pointer by the size of the M_SP_NA_1_IO_Group struct
+                    const Iec104M_SP_NA_1_IO_Group* curIo =
+                        (const Iec104M_SP_NA_1_IO_Group*) &apci->asdu.m_sp_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_sp_na_1IOGroup = curIo;
+                    genericIOGroup.m_sp_na_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_SP_TA_1:
+                {
+                    // increment the information object block pointer by the size of the M_SP_TA_1_IO_Group struct
+                    const Iec104M_SP_TA_1_IO_Group* curIo =
+                        (const Iec104M_SP_TA_1_IO_Group*) &apci->asdu.m_sp_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_sp_ta_1IOGroup = curIo;
+                    genericIOGroup.m_sp_ta_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_DP_NA_1:
+                {
+                    // increment the information object block pointer by the size of the M_DP_NA_1_IO_Group struct
+                    const Iec104M_DP_NA_1_IO_Group* curIo =
+                        (const Iec104M_DP_NA_1_IO_Group*) &apci->asdu.m_dp_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_dp_na_1IOGroup = curIo;
+                    genericIOGroup.m_dp_na_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_DP_TA_1:
+                {
+                    // increment the information object block pointer by the size of the M_DP_TA_1_IO_Group struct
+                    const Iec104M_DP_TA_1_IO_Group* curIo =
+                        (const Iec104M_DP_TA_1_IO_Group*) &apci->asdu.m_dp_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_dp_ta_1IOGroup = curIo;
+                    genericIOGroup.m_dp_ta_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_ST_NA_1:
+                {
+                    // increment the information object block pointer by the size of the M_ST_NA_1_IO_Group struct
+                    const Iec104M_ST_NA_1_IO_Group* curIo =
+                        (const Iec104M_ST_NA_1_IO_Group*) &apci->asdu.m_st_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_st_na_1IOGroup = curIo;
+                    genericIOGroup.m_st_na_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_ST_TA_1:
+                {
+                    // increment the information object block pointer by the size of the M_ST_TA_1_IO_Group struct
+                    const Iec104M_ST_TA_1_IO_Group* curIo =
+                        (const Iec104M_ST_TA_1_IO_Group*) &apci->asdu.m_st_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_st_ta_1IOGroup = curIo;
+                    genericIOGroup.m_st_ta_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_BO_NA_1:
+                {
+                    // increment the information object block pointer by the size of the M_BO_NA_1_IO_Group struct
+                    const Iec104M_BO_NA_1_IO_Group* curIo =
+                        (const Iec104M_BO_NA_1_IO_Group*) &apci->asdu.m_bo_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_bo_na_1IOGroup = curIo;
+                    genericIOGroup.m_bo_na_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_BO_TA_1:
+                {
+                    // increment the information object block pointer by the size of the M_BO_TA_1_IO_Group struct
+                    const Iec104M_BO_TA_1_IO_Group* curIo =
+                        (const Iec104M_BO_TA_1_IO_Group*) &apci->asdu.m_bo_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_bo_ta_1IOGroup = curIo;
+                    genericIOGroup.m_bo_ta_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_ME_NA_1:
+                {
+                    // increment the information object block pointer by the size of the M_ME_NA_1_IO_Group struct
+                    const Iec104M_ME_NA_1_IO_Group* curIo =
+                        (const Iec104M_ME_NA_1_IO_Group*) &apci->asdu.m_me_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_me_na_1IOGroup = curIo;
+                    genericIOGroup.m_me_na_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_ME_TA_1:
+                {
+                    // increment the information object block pointer by the size of the M_ME_TA_1_IO_Group struct
+                    const Iec104M_ME_TA_1_IO_Group* curIo =
+                        (const Iec104M_ME_TA_1_IO_Group*) &apci->asdu.m_me_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_me_ta_1IOGroup = curIo;
+                    genericIOGroup.m_me_ta_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_ME_NB_1:
+                {
+                    // increment the information object block pointer by the size of the M_ME_NB_1_IO_Group struct
+                    const Iec104M_ME_NB_1_IO_Group* curIo =
+                        (const Iec104M_ME_NB_1_IO_Group*) &apci->asdu.m_me_nb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_me_nb_1IOGroup = curIo;
+                    genericIOGroup.m_me_nb_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_ME_TB_1:
+                {
+                    // increment the information object block pointer by the size of the M_ME_TB_1_IO_Group struct
+                    const Iec104M_ME_TB_1_IO_Group* curIo =
+                        (const Iec104M_ME_TB_1_IO_Group*) &apci->asdu.m_me_tb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_me_tb_1IOGroup = curIo;
+                    genericIOGroup.m_me_tb_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_ME_NC_1:
+                {
+                    // increment the information object block pointer by the size of the M_ME_NC_1_IO_Group struct
+                    const Iec104M_ME_NC_1_IO_Group* curIo =
+                        (const Iec104M_ME_NC_1_IO_Group*) &apci->asdu.m_me_nc_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_me_nc_1IOGroup = curIo;
+                    genericIOGroup.m_me_nc_1IOSubgroup = &curIo->subgroup;
+
+                    break;
+                }
+
+                case IEC104_ASDU_M_ME_TC_1:
+                {
+                    // increment the information object block pointer by the size of the M_ME_TC_1_IO_Group struct
+                    const Iec104M_ME_TC_1_IO_Group* curIo =
+                        (const Iec104M_ME_TC_1_IO_Group*) &apci->asdu.m_me_tc_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_me_tc_1IOGroup = curIo;
+                    genericIOGroup.m_me_tc_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_IT_NA_1:
+                {
+                    // increment the information object block pointer by the size of the M_IT_NA_1_IO_Group struct
+                    const Iec104M_IT_NA_1_IO_Group* curIo =
+                        (const Iec104M_IT_NA_1_IO_Group*) &apci->asdu.m_it_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_it_na_1IOGroup = curIo;
+                    genericIOGroup.m_it_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_IT_TA_1:
+                {
+                    // increment the information object block pointer by the size of the M_IT_TA_1_IO_Group struct
+                    const Iec104M_IT_TA_1_IO_Group* curIo =
+                        (const Iec104M_IT_TA_1_IO_Group*) &apci->asdu.m_it_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_it_ta_1IOGroup = curIo;
+                    genericIOGroup.m_it_ta_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_EP_TA_1:
+                {
+                    // increment the information object block pointer by the size of the M_EP_TA_1_IO_Group struct
+                    const Iec104M_EP_TA_1_IO_Group* curIo =
+                        (const Iec104M_EP_TA_1_IO_Group*) &apci->asdu.m_ep_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_ep_ta_1IOGroup = curIo;
+                    genericIOGroup.m_ep_ta_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_EP_TB_1:
+                {
+                    // increment the information object block pointer by the size of the M_EP_TB_1_IO_Group struct
+                    const Iec104M_EP_TB_1_IO_Group* curIo =
+                        (const Iec104M_EP_TB_1_IO_Group*) &apci->asdu.m_ep_tb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_ep_tb_1IOGroup = curIo;
+                    genericIOGroup.m_ep_tb_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_EP_TC_1:
+                {
+                    // increment the information object block pointer by the size of the M_EP_TC_1_IO_Group struct
+                    const Iec104M_EP_TC_1_IO_Group* curIo =
+                        (const Iec104M_EP_TC_1_IO_Group*) &apci->asdu.m_ep_tc_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_ep_tc_1IOGroup = curIo;
+                    genericIOGroup.m_ep_tc_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_PS_NA_1:
+                {
+                    // increment the information object block pointer by the size of the M_PS_NA_1_IO_Group struct
+                    const Iec104M_PS_NA_1_IO_Group* curIo =
+                        (const Iec104M_PS_NA_1_IO_Group*) &apci->asdu.m_ps_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_ps_na_1IOGroup = curIo;
+                    genericIOGroup.m_ps_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_ME_ND_1:
+                {
+                    // increment the information object block pointer by the size of the M_ME_ND_1_IO_Group struct
+                    const Iec104M_ME_ND_1_IO_Group* curIo =
+                        (const Iec104M_ME_ND_1_IO_Group*) &apci->asdu.m_me_nd_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_me_nd_1IOGroup = curIo;
+                    genericIOGroup.m_me_nd_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_SP_TB_1:
+                {
+                    // increment the information object block pointer by the size of the M_SP_TB_1_IO_Group struct
+                    const Iec104M_SP_TB_1_IO_Group* curIo =
+                        (const Iec104M_SP_TB_1_IO_Group*) &apci->asdu.m_sp_tb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_sp_tb_1IOGroup = curIo;
+                    genericIOGroup.m_sp_tb_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_DP_TB_1:
+                {
+                    // increment the information object block pointer by the size of the M_DP_TB_1_IO_Group struct
+                    const Iec104M_DP_TB_1_IO_Group* curIo =
+                        (const Iec104M_DP_TB_1_IO_Group*) &apci->asdu.m_dp_tb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_dp_tb_1IOGroup = curIo;
+                    genericIOGroup.m_dp_tb_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_ST_TB_1:
+                {
+                    // increment the information object block pointer by the size of the M_ST_TB_1_IO_Group struct
+                    const Iec104M_ST_TB_1_IO_Group* curIo =
+                        (const Iec104M_ST_TB_1_IO_Group*) &apci->asdu.m_st_tb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_st_tb_1IOGroup = curIo;
+                    genericIOGroup.m_st_tb_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_BO_TB_1:
+                {
+                    // increment the information object block pointer by the size of the M_BO_TB_1_IO_Group struct
+                    const Iec104M_BO_TB_1_IO_Group* curIo =
+                        (const Iec104M_BO_TB_1_IO_Group*) &apci->asdu.m_bo_tb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_bo_tb_1IOGroup = curIo;
+                    genericIOGroup.m_bo_tb_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_ME_TD_1:
+                {
+                    // increment the information object block pointer by the size of the M_ME_TD_1_IO_Group struct
+                    const Iec104M_ME_TD_1_IO_Group* curIo =
+                        (const Iec104M_ME_TD_1_IO_Group*) &apci->asdu.m_me_td_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_me_td_1IOGroup = curIo;
+                    genericIOGroup.m_me_td_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_ME_TE_1:
+                {
+                    // increment the information object block pointer by the size of the M_ME_TE_1_IO_Group struct
+                    const Iec104M_ME_TE_1_IO_Group* curIo =
+                        (const Iec104M_ME_TE_1_IO_Group*) &apci->asdu.m_me_te_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_me_te_1IOGroup = curIo;
+                    genericIOGroup.m_me_te_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_ME_TF_1:
+                {
+                    // increment the information object block pointer by the size of the M_ME_TF_1_IO_Group struct
+                    const Iec104M_ME_TF_1_IO_Group* curIo =
+                        (const Iec104M_ME_TF_1_IO_Group*) &apci->asdu.m_me_tf_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_me_tf_1IOGroup = curIo;
+                    genericIOGroup.m_me_tf_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_IT_TB_1:
+                {
+                    // increment the information object block pointer by the size of the M_IT_TB_1_IO_Group struct
+                    const Iec104M_IT_TB_1_IO_Group* curIo =
+                        (const Iec104M_IT_TB_1_IO_Group*) &apci->asdu.m_it_tb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_it_tb_1IOGroup = curIo;
+                    genericIOGroup.m_it_tb_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_EP_TD_1:
+                {
+                    // increment the information object block pointer by the size of the M_EP_TD_1_IO_Group struct
+                    const Iec104M_EP_TD_1_IO_Group* curIo =
+                        (const Iec104M_EP_TD_1_IO_Group*) &apci->asdu.m_ep_td_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_ep_td_1IOGroup = curIo;
+                    genericIOGroup.m_ep_td_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_EP_TE_1:
+                {
+                    // increment the information object block pointer by the size of the M_EP_TE_1_IO_Group struct
+                    const Iec104M_EP_TE_1_IO_Group* curIo =
+                        (const Iec104M_EP_TE_1_IO_Group*) &apci->asdu.m_ep_te_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_ep_te_1IOGroup = curIo;
+                    genericIOGroup.m_ep_te_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_EP_TF_1:
+                {
+                    // increment the information object block pointer by the size of the M_EP_TF_1_IO_Group struct
+                    const Iec104M_EP_TF_1_IO_Group* curIo =
+                        (const Iec104M_EP_TF_1_IO_Group*) &apci->asdu.m_ep_tf_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_ep_tf_1IOGroup = curIo;
+                    genericIOGroup.m_ep_tf_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_SC_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_SC_NA_1_IO_Group struct
+                    const Iec104C_SC_NA_1_IO_Group* curIo =
+                        (const Iec104C_SC_NA_1_IO_Group*) &apci->asdu.c_sc_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_sc_na_1IOGroup = curIo;
+                    genericIOGroup.c_sc_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_DC_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_DC_NA_1_IO_Group struct
+                    const Iec104C_DC_NA_1_IO_Group* curIo =
+                        (const Iec104C_DC_NA_1_IO_Group*) &apci->asdu.c_dc_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_dc_na_1IOGroup = curIo;
+                    genericIOGroup.c_dc_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_RC_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_RC_NA_1_IO_Group struct
+                    const Iec104C_RC_NA_1_IO_Group* curIo =
+                        (const Iec104C_RC_NA_1_IO_Group*) &apci->asdu.c_rc_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_rc_na_1IOGroup = curIo;
+                    genericIOGroup.c_rc_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_SE_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_SE_NA_1_IO_Group struct
+                    const Iec104C_SE_NA_1_IO_Group* curIo =
+                        (const Iec104C_SE_NA_1_IO_Group*) &apci->asdu.c_se_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_se_na_1IOGroup = curIo;
+                    genericIOGroup.c_se_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_SE_NB_1:
+                {
+                    // increment the information object block pointer by the size of the C_SE_NB_1_IO_Group struct
+                    const Iec104C_SE_NB_1_IO_Group* curIo =
+                        (const Iec104C_SE_NB_1_IO_Group*) &apci->asdu.c_se_nb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_se_nb_1IOGroup = curIo;
+                    genericIOGroup.c_se_nb_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_SE_NC_1:
+                {
+                    // increment the information object block pointer by the size of the C_SE_NC_1_IO_Group struct
+                    const Iec104C_SE_NC_1_IO_Group* curIo =
+                        (const Iec104C_SE_NC_1_IO_Group*) &apci->asdu.c_se_nc_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_se_nc_1IOGroup = curIo;
+                    genericIOGroup.c_se_nc_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_BO_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_BO_NA_1_IO_Group struct
+                    const Iec104C_BO_NA_1_IO_Group* curIo =
+                        (const Iec104C_BO_NA_1_IO_Group*) &apci->asdu.c_bo_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_bo_na_1IOGroup = curIo;
+                    genericIOGroup.c_bo_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_SC_TA_1:
+                {
+                    // increment the information object block pointer by the size of the C_SC_TA_1_IO_Group struct
+                    const Iec104C_SC_TA_1_IO_Group* curIo =
+                        (const Iec104C_SC_TA_1_IO_Group*) &apci->asdu.c_sc_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_sc_ta_1IOGroup = curIo;
+                    genericIOGroup.c_sc_ta_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_DC_TA_1:
+                {
+                    // increment the information object block pointer by the size of the C_DC_TA_1_IO_Group struct
+                    const Iec104C_DC_TA_1_IO_Group* curIo =
+                        (const Iec104C_DC_TA_1_IO_Group*) &apci->asdu.c_dc_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_dc_ta_1IOGroup = curIo;
+                    genericIOGroup.c_dc_ta_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_RC_TA_1:
+                {
+                    // increment the information object block pointer by the size of the C_RC_TA_1_IO_Group struct
+                    const Iec104C_RC_TA_1_IO_Group* curIo =
+                        (const Iec104C_RC_TA_1_IO_Group*) &apci->asdu.c_rc_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_rc_ta_1IOGroup = curIo;
+                    genericIOGroup.c_rc_ta_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_SE_TA_1:
+                {
+                    // increment the information object block pointer by the size of the C_SE_TA_1_IO_Group struct
+                    const Iec104C_SE_TA_1_IO_Group* curIo =
+                        (const Iec104C_SE_TA_1_IO_Group*) &apci->asdu.c_se_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_se_ta_1IOGroup = curIo;
+                    genericIOGroup.c_se_ta_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_SE_TB_1:
+                {
+                    // increment the information object block pointer by the size of the C_SE_TB_1_IO_Group struct
+                    const Iec104C_SE_TB_1_IO_Group* curIo =
+                        (const Iec104C_SE_TB_1_IO_Group*) &apci->asdu.c_se_tb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_se_tb_1IOGroup = curIo;
+                    genericIOGroup.c_se_tb_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_SE_TC_1:
+                {
+                    // increment the information object block pointer by the size of the C_SE_TC_1_IO_Group struct
+                    const Iec104C_SE_TC_1_IO_Group* curIo =
+                        (const Iec104C_SE_TC_1_IO_Group*) &apci->asdu.c_se_tc_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_se_tc_1IOGroup = curIo;
+                    genericIOGroup.c_se_tc_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_BO_TA_1:
+                {
+                    // increment the information object block pointer by the size of the C_BO_TA_1_IO_Group struct
+                    const Iec104C_BO_TA_1_IO_Group* curIo =
+                        (const Iec104C_BO_TA_1_IO_Group*) &apci->asdu.c_bo_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_bo_ta_1IOGroup = curIo;
+                    genericIOGroup.c_bo_ta_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_M_EI_NA_1:
+                {
+                    // increment the information object block pointer by the size of the M_EI_NA_1_IO_Group struct
+                    const Iec104M_EI_NA_1_IO_Group* curIo =
+                        (const Iec104M_EI_NA_1_IO_Group*) &apci->asdu.m_ei_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.m_ei_na_1IOGroup = curIo;
+                    genericIOGroup.m_ei_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_IC_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_IC_NA_1_IO_Group struct
+                    const Iec104C_IC_NA_1_IO_Group* curIo =
+                        (const Iec104C_IC_NA_1_IO_Group*) &apci->asdu.c_ic_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_ic_na_1IOGroup = curIo;
+                    genericIOGroup.c_ic_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_CI_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_CI_NA_1_IO_Group struct
+                    const Iec104C_CI_NA_1_IO_Group* curIo =
+                        (const Iec104C_CI_NA_1_IO_Group*) &apci->asdu.c_ci_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_ci_na_1IOGroup = curIo;
+                    genericIOGroup.c_ci_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_RD_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_RD_NA_1_IO_Group struct
+                    const Iec104C_RD_NA_1_IO_Group* curIo =
+                        (const Iec104C_RD_NA_1_IO_Group*) &apci->asdu.c_rd_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_rd_na_1IOGroup = curIo;
+                    genericIOGroup.c_rd_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_CS_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_CS_NA_1_IO_Group struct
+                    const Iec104C_CS_NA_1_IO_Group* curIo =
+                        (const Iec104C_CS_NA_1_IO_Group*) &apci->asdu.c_cs_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_cs_na_1IOGroup = curIo;
+                    genericIOGroup.c_cs_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_TS_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_TS_NA_1_IO_Group struct
+                    const Iec104C_TS_NA_1_IO_Group* curIo =
+                        (const Iec104C_TS_NA_1_IO_Group*) &apci->asdu.c_ts_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_ts_na_1IOGroup = curIo;
+                    genericIOGroup.c_ts_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_RP_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_RP_NA_1_IO_Group struct
+                    const Iec104C_RP_NA_1_IO_Group* curIo =
+                        (const Iec104C_RP_NA_1_IO_Group*) &apci->asdu.c_rp_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_rp_na_1IOGroup = curIo;
+                    genericIOGroup.c_rp_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_CD_NA_1:
+                {
+                    // increment the information object block pointer by the size of the C_CD_NA_1_IO_Group struct
+                    const Iec104C_CD_NA_1_IO_Group* curIo =
+                        (const Iec104C_CD_NA_1_IO_Group*) &apci->asdu.c_cd_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_cd_na_1IOGroup = curIo;
+                    genericIOGroup.c_cd_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_C_TS_TA_1:
+                {
+                    // increment the information object block pointer by the size of the C_TS_TA_1_IO_Group struct
+                    const Iec104C_TS_TA_1_IO_Group* curIo =
+                        (const Iec104C_TS_TA_1_IO_Group*) &apci->asdu.c_ts_ta_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.c_ts_ta_1IOGroup = curIo;
+                    genericIOGroup.c_ts_ta_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_P_ME_NA_1:
+                {
+                    // increment the information object block pointer by the size of the P_ME_NA_1_IO_Group struct
+                    const Iec104P_ME_NA_1_IO_Group* curIo =
+                        (const Iec104P_ME_NA_1_IO_Group*) &apci->asdu.p_me_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.p_me_na_1IOGroup = curIo;
+                    genericIOGroup.p_me_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_P_ME_NB_1:
+                {
+                    // increment the information object block pointer by the size of the P_ME_NB_1_IO_Group struct
+                    const Iec104P_ME_NB_1_IO_Group* curIo =
+                        (const Iec104P_ME_NB_1_IO_Group*) &apci->asdu.p_me_nb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.p_me_nb_1IOGroup = curIo;
+                    genericIOGroup.p_me_nb_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_P_ME_NC_1:
+                {
+                    // increment the information object block pointer by the size of the P_ME_NC_1_IO_Group struct
+                    const Iec104P_ME_NC_1_IO_Group* curIo =
+                        (const Iec104P_ME_NC_1_IO_Group*) &apci->asdu.p_me_nc_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.p_me_nc_1IOGroup = curIo;
+                    genericIOGroup.p_me_nc_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_P_AC_NA_1:
+                {
+                    // increment the information object block pointer by the size of the P_AC_NA_1_IO_Group struct
+                    const Iec104P_AC_NA_1_IO_Group* curIo =
+                        (const Iec104P_AC_NA_1_IO_Group*) &apci->asdu.p_ac_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.p_ac_na_1IOGroup = curIo;
+                    genericIOGroup.p_ac_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_F_FR_NA_1:
+                {
+                    // increment the information object block pointer by the size of the F_FR_NA_1_IO_Group struct
+                    const Iec104F_FR_NA_1_IO_Group* curIo =
+                        (const Iec104F_FR_NA_1_IO_Group*) &apci->asdu.f_fr_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.f_fr_na_1IOGroup = curIo;
+                    genericIOGroup.f_fr_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_F_SR_NA_1:
+                {
+                    // increment the information object block pointer by the size of the F_SR_NA_1_IO_Group struct
+                    const Iec104F_SR_NA_1_IO_Group* curIo =
+                        (const Iec104F_SR_NA_1_IO_Group*) &apci->asdu.f_sr_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.f_sr_na_1IOGroup = curIo;
+                    genericIOGroup.f_sr_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_F_SC_NA_1:
+                {
+                    // increment the information object block pointer by the size of the F_SC_NA_1_IO_Group struct
+                    const Iec104F_SC_NA_1_IO_Group* curIo =
+                        (const Iec104F_SC_NA_1_IO_Group*) &apci->asdu.f_sc_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.f_sc_na_1IOGroup = curIo;
+                    genericIOGroup.f_sc_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_F_LS_NA_1:
+                {
+                    // increment the information object block pointer by the size of the F_LS_NA_1_IO_Group struct
+                    const Iec104F_LS_NA_1_IO_Group* curIo =
+                        (const Iec104F_LS_NA_1_IO_Group*) &apci->asdu.f_ls_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.f_ls_na_1IOGroup = curIo;
+                    genericIOGroup.f_ls_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_F_AF_NA_1:
+                {
+                    // increment the information object block pointer by the size of the F_AF_NA_1_IO_Group struct
+                    const Iec104F_AF_NA_1_IO_Group* curIo =
+                        (const Iec104F_AF_NA_1_IO_Group*) &apci->asdu.f_af_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.f_af_na_1IOGroup = curIo;
+                    genericIOGroup.f_af_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                case IEC104_ASDU_F_SG_NA_1:
+                {
+                    // increment the information object block pointer by the size of the F_SG_NA_1_IO_Group struct
+                    const Iec104F_SG_NA_1_IO_Group* curIo =
+                        (const Iec104F_SG_NA_1_IO_Group*) &apci->asdu.f_sg_na_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.f_sg_na_1IOGroup = curIo;
+                    genericIOGroup.f_sg_na_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                // case IEC104_ASDU_F_DR_TA_1
+                // path doesn't happen as it gets caught during the ASDU check
+
+                case IEC104_ASDU_F_SC_NB_1:
+                {
+                    // increment the information object block pointer by the size of the F_SC_NB_1_IO_Group struct
+                    const Iec104F_SC_NB_1_IO_Group* curIo =
+                        (const Iec104F_SC_NB_1_IO_Group*) &apci->asdu.f_sc_nb_1 + i;
+
+                    // print the SQ0 IO block
+                    genericIOGroup.f_sc_nb_1IOGroup = curIo;
+                    genericIOGroup.f_sc_nb_1IOSubgroup = &curIo->subgroup;
+                    break;
+                }
+
+                default:
+                {
+                    // SQ0 ASDU parsing not implemented for this type
+                }
+                }
+
+                // parse the group
+                parseIec104GenericIOGroup(&genericIOGroup);
             }
-            }
-
-            // parse the group
-            parseIec104GenericIOGroup(&genericIOGroup);
         }
     }
 }
@@ -2607,9 +2610,7 @@ void parseIec104ApciI(const Iec104ApciI* apci)
         curAsduCheck.sq0Allowed = true;
         curAsduCheck.sq1Allowed = false;
         curAsduCheck.multipleIOAllowed = false;
-        curAsduCheck.checkCauseOfTx = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-                                        0, 0, 0, 0, 0, 0, 0 };
+        curAsduCheck.checkCauseOfTx = { };
 
         // select the appropriate asdu based on typeId value
         switch (apci->asdu.typeId)
index f956ed4b1e82ead810a16dc10c144d98c3592494..a8b2356a0186b62487e291c8c6fc23b0cfbb1b1d 100644 (file)
@@ -24,7 +24,7 @@
 
 #include "iec104_parse_information_object_elements.h"
 
-#include <math.h>
+#include <cmath>
 
 #include "detection/detection_engine.h"
 #include "events/event_queue.h"
@@ -191,18 +191,10 @@ void parseIec104Afq(const Iec104AfqType* afq)
     }
 }
 
-uint8_t parseIec104Vsq(const Iec104ApciI* apci)
+uint32_t parseIec104Vsq(const Iec104ApciI* apci)
 {
     // number of elements == 0 is caught in check apdu
-
-    // make sure the reported number of elements would not exceed the packet size
-    // * take the apci->header.length value
-    // * subtract off type id, vsq, cause of tx, and 2-byte common address sizes
-    // * if the sq bit is set, subtract off the IOA
-    // * use a switch statement with cases of each message type to get the size of one group
-    // * divide the result of the earlier calculation by this group size to get the maximum allowable groups without overflowing
-
-    uint8_t maxNumberOfElements = 0;
+    
     uint32_t informationObjectSubgroupSize = 0;
 
     // determine the size of the current message type group
@@ -545,31 +537,42 @@ uint8_t parseIec104Vsq(const Iec104ApciI* apci)
         }
     }
 
+    // make sure the reported number of elements would not exceed the packet size
+    // * take the apci->header.length value
+    // * subtract off type id, vsq, cause of tx, and 2-byte common address sizes
+    // * if the sq bit is set, subtract off the IOA
+    // * use a switch statement with cases of each message type to get the size of one group
+    // * divide the result of the earlier calculation by this group size to get the maximum allowable groups without overflowing
+    uint8_t maxNumberOfElements = 0;
+
     if (informationObjectSubgroupSize) 
     {
-        if (apci->asdu.variableStructureQualifier.sq == 0) 
-        {
-            uint32_t informationObjectGroupSize = informationObjectSubgroupSize + sizeof(const Iec104InformationObjectAddressThreeOctetType);
-            maxNumberOfElements = (apci->header.length 
-                                   - sizeof(uint8_t)  // type id
-                                   - sizeof(const Iec104VariableStructureQualifierType) 
-                                   - sizeof(const Iec104CauseOfTransmissionType)
-                                   - sizeof(const Iec104CommonAddressOfAsduType)
-                                   ) / informationObjectGroupSize;
-        } 
-        else 
-        {
-            maxNumberOfElements = (apci->header.length 
-                                   - sizeof(uint8_t)  // type id
-                                   - sizeof(const Iec104VariableStructureQualifierType) 
-                                   - sizeof(const Iec104CauseOfTransmissionType)
-                                   - sizeof(const Iec104CommonAddressOfAsduType)
-                                   - sizeof(const Iec104InformationObjectAddressThreeOctetType)
-                                   ) / informationObjectSubgroupSize;
+        uint32_t reported_msg_len = apci->header.length;
+        if (reported_msg_len >= IEC104_APCI_TYPE_I_MIN_LEN) { 
+            if (apci->asdu.variableStructureQualifier.sq == 0) 
+            {
+                uint32_t informationObjectGroupSize = informationObjectSubgroupSize + sizeof(const Iec104InformationObjectAddressThreeOctetType);
+                maxNumberOfElements = (reported_msg_len
+                                       - sizeof(uint8_t)  // type id
+                                       - sizeof(const Iec104VariableStructureQualifierType) 
+                                       - sizeof(const Iec104CauseOfTransmissionType)
+                                       - sizeof(const Iec104CommonAddressOfAsduType)
+                                       ) / informationObjectGroupSize;
+            } 
+            else 
+            {
+                maxNumberOfElements = (reported_msg_len 
+                                       - sizeof(uint8_t)  // type id
+                                       - sizeof(const Iec104VariableStructureQualifierType) 
+                                       - sizeof(const Iec104CauseOfTransmissionType)
+                                       - sizeof(const Iec104CommonAddressOfAsduType)
+                                       - sizeof(const Iec104InformationObjectAddressThreeOctetType)
+                                       ) / informationObjectSubgroupSize;
+            }
         }
     }
 
-    uint8_t verifiedNumberOfElements = apci->asdu.variableStructureQualifier.numberOfElements;
+    uint32_t verifiedNumberOfElements = apci->asdu.variableStructureQualifier.numberOfElements;
     if (verifiedNumberOfElements > 0 and verifiedNumberOfElements <= maxNumberOfElements) 
     {
         // do nothing
index 2857b6da317609e318200694a9e28dde3a2681f8..c73315d4f35a9f1333416bcc498c4419f4f5805b 100644 (file)
@@ -46,7 +46,7 @@ void parseIec104Srq(const Iec104SrqType* srq);
 void parseIec104Scq(const Iec104ScqType* scq);
 void parseIec104Lsq(const Iec104LsqType* lsq);
 void parseIec104Afq(const Iec104AfqType* afq);
-uint8_t parseIec104Vsq(const Iec104ApciI* apci);
+uint32_t parseIec104Vsq(const Iec104ApciI* apci);
 void parseIec104CauseOfTx(const Iec104ApciI* apci);
 void parseIec104TwoOctetCommonAddress(const Iec104ApciI* apci);
 void parseIec104InformationObjectAddressWithThreeOctets(
index 5bd7acade6a2e2436730e85bc243f4b12016ecc0..b38054f7b8ab058143a69fde47ec30cc1a6760b6 100644 (file)
@@ -24,7 +24,6 @@
 
 // Detection trace utility
 
-#include "framework/cursor.h"
 #include "main/snort_types.h"
 #include "main/thread.h"
 
@@ -42,7 +41,7 @@ enum
 };
 
 #ifdef DEBUG_MSGS
-#define print_debug_information(p, msg) debug_log(iec104_trace, TRACE_IEC104_IDENTIFICATION, p, msg);
+#define print_debug_information(p, msg) debug_log(iec104_trace, TRACE_IEC104_IDENTIFICATION, p, msg)
 #else
 #define print_debug_information(...)
 #endif
index 27b42d4b18ade64d26146e2dfc653bdf6abe1ffe..168d9dfc4ddb10b1194357f974b5d2498847ad81 100644 (file)
@@ -60,7 +60,7 @@ static Iec104ApciTypeMap iec104_apci_type_map[] =
     { "information_transfer_format", IEC104_APCI_TYPE_I },    // information transfer format
 };
 
-static bool get_apci_type(const char* s, long &n)
+static bool get_apci_type(const char* s, longn)
 {
     constexpr size_t max = (sizeof(iec104_apci_type_map) / sizeof(Iec104ApciTypeMap));
 
@@ -109,14 +109,14 @@ uint32_t Iec104ApciTypeOption::hash() const
     return c;
 }
 
-bool Iec104ApciTypeOption::operator==(const IpsOption &ips) const
+bool Iec104ApciTypeOption::operator==(const IpsOptionips) const
 {
     if (!IpsOption::operator==(ips))
     {
         return false;
     }
 
-    const Iec104ApciTypeOption &rhs = (const Iec104ApciTypeOption&) ips;
+    const Iec104ApciTypeOptionrhs = (const Iec104ApciTypeOption&) ips;
     return (apci_type == rhs.apci_type);
 }
 
@@ -183,7 +183,7 @@ public:
     uint8_t apci_type = IEC104_NO_APCI;
 };
 
-bool Iec104ApciTypeModule::set(const char*, Value &v, SnortConfig*)
+bool Iec104ApciTypeModule::set(const char*, Valuev, SnortConfig*)
 {
     if (!v.is("~"))
     {
index 7489e9afce7239cddc741c13bff22752c124bf33..2e2fd39620ebf55f1a1da679b0d1fdfb5e79d264 100644 (file)
@@ -127,7 +127,7 @@ static Iec104AsduFuncMap iec104_asdu_func_map[] =
     // 128-256 reserved
     };
 
-static bool get_func(const char* s, long &n)
+static bool get_func(const char* s, longn)
 {
     constexpr size_t max = (sizeof(iec104_asdu_func_map) / sizeof(Iec104AsduFuncMap));
 
@@ -176,14 +176,14 @@ uint32_t Iec104AsduFuncOption::hash() const
     return c;
 }
 
-bool Iec104AsduFuncOption::operator==(const IpsOption &ips) const
+bool Iec104AsduFuncOption::operator==(const IpsOptionips) const
 {
     if (!IpsOption::operator==(ips))
     {
         return false;
     }
 
-    const Iec104AsduFuncOption &rhs = (const Iec104AsduFuncOption&) ips;
+    const Iec104AsduFuncOptionrhs = (const Iec104AsduFuncOption&) ips;
     return (func == rhs.func);
 }
 
@@ -256,7 +256,7 @@ public:
     uint8_t func = IEC104_NO_ASDU;
 };
 
-bool Iec104AsduFuncModule::set(const char*, Value &v, SnortConfig*)
+bool Iec104AsduFuncModule::set(const char*, Valuev, SnortConfig*)
 {
     if (!v.is("~"))
     {