#include <assert.h>
-bool CurseBook::mms_curse(const uint8_t* data, unsigned len, CurseTracker* tracker)
+enum PresCtx
{
- // peg the tracker to MMS
- MmsTracker& mms = tracker->mms;
+ PRES_CTX_ACSE = 1,
+ PRES_CTX_MMS = 3,
+};
- // if the state is set to MMS_STATE__SEARCH it means we most likely
- // have a split pipelined message coming through and will need to
- // reset the state
- if ( mms.state == MMS_STATE__SEARCH )
+static bool verify_search_depth_idx( unsigned len, uint32_t idx, uint32_t max_depth_idx )
+{
+ return idx + max_depth_idx < len;
+}
+
+static uint32_t search_for_osi_session_spdu( MmsTracker& mms, const uint8_t* data, uint32_t idx )
+{
+ switch ( data[idx] )
{
- mms.state = mms.last_state;
+ // check for a known MMS message tag in the event Session/Pres/ACSE aren't used
+ case MMS_CONFIRMED_REQUEST_TAG: // fallthrough intentional
+ case MMS_CONFIRMED_RESPONSE_TAG: // fallthrough intentional
+ case MMS_CONFIRMED_ERROR_TAG: // fallthrough intentional
+ case MMS_UNCONFIRMED_TAG: // fallthrough intentional
+ case MMS_REJECT_TAG: // fallthrough intentional
+ case MMS_CANCEL_REQUEST_TAG: // fallthrough intentional
+ case MMS_CANCEL_RESPONSE_TAG: // fallthrough intentional
+ case MMS_CANCEL_ERROR_TAG: // fallthrough intentional
+ case MMS_INITIATE_REQUEST_TAG: // fallthrough intentional
+ case MMS_INITIATE_RESPONSE_TAG: // fallthrough intentional
+ case MMS_INITIATE_ERROR_TAG: // fallthrough intentional
+ case MMS_CONCLUDE_REQUEST_TAG: // fallthrough intentional
+ case MMS_CONCLUDE_RESPONSE_TAG: // fallthrough intentional
+ case MMS_CONCLUDE_ERROR_TAG:
+ {
+ // when it looks like MMS this early, drop the index back one and push processing down to the full MMS parsing
+ // taking this approach as there are other paths into the MMS determination
+ if ( idx > 0 )
+ {
+ idx--;
+ mms.state = MMS_STATE__MMS;
+ break;
+ }
+
+ // default to MMS not found
+ mms.state = MMS_STATE__NOT_FOUND;
+ break;
}
- // define all known MMS tags to check for
+ // if mms isn't found, search for a supported OSI Session layer
+
+ // check for the Confirmed Request/Response path
+ case MMS_OSI_SESSION_SPDU_GT_DT:
+ {
+ mms.state = MMS_STATE__OSI_SESSION_SPDU_GT_LEN;
+ break;
+ }
+
+ // check for the Initiate Request path
+ case MMS_OSI_SESSION_SPDU_CN:
+ {
+ mms.state = MMS_STATE__OSI_SESSION_SPDU_CN_LEN;
+ break;
+ }
+
+ // check for the Initiate Response path
+ case MMS_OSI_SESSION_SPDU_AC:
+ {
+ mms.state = MMS_STATE__OSI_SESSION_SPDU_AC_LEN;
+ break;
+ }
+
+ // if neither are found, it is most likely not MMS
+ default:
+ {
+ mms.state = MMS_STATE__NOT_FOUND;
+ break;
+ }
+ }
+ return idx;
+}
+
+static uint32_t search_for_pres_ctx( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx )
+{
+ // define some constants for search windows
+ constexpr uint32_t fully_encoded_data_window = 3;
+ constexpr uint32_t pres_ctx_window = 3;
+ constexpr uint32_t max_depth_idx = fully_encoded_data_window + pres_ctx_window;
+
+ // try to determine if the ACSE layer exists
+ // len reduction is to allow space for the forward looking checks
+ bool ctx_likely = false;
+ for ( uint32_t init_byte = idx; ( init_byte < len - max_depth_idx ) && !ctx_likely; init_byte++)
+ {
+ // require the user-data fully encoded data tag ( | 61 | )
+ if ( data[init_byte] != 0x61 )
+ {
+ continue;
+ }
+
+ // make sure there is still enough space left in the buffer for the forward search
+ // allow up to two bytes for the fully encoded data field
+ if ( !verify_search_depth_idx( len, init_byte, max_depth_idx ) )
+ {
+ // not enough data to process
+ assert( !ctx_likely );
+ break;
+ }
+
+ for ( uint32_t encode_tag_shift = 1; ( encode_tag_shift < fully_encoded_data_window ) && !ctx_likely; encode_tag_shift++ )
+ {
+ uint32_t encode_tag_byte = init_byte + encode_tag_shift;
+ // look for the ' fully encoded data ' user data tag ( | 30 | );
+ if ( data[encode_tag_byte] != 0x30 )
+ {
+ continue;
+ }
+
+ for ( uint32_t pres_ctx_shift = 1; ( pres_ctx_shift < + pres_ctx_window ) && !ctx_likely; pres_ctx_shift++ )
+ {
+ // look for the presentation context tag and length ( | 02 01 | )
+ bool ctx_b1 = data[encode_tag_byte + pres_ctx_shift] == 0x02;
+ bool ctx_b2 = data[encode_tag_byte + pres_ctx_shift + 1] == 0x01;
+ if ( ctx_b1 && ctx_b2 )
+ {
+ switch ( data[encode_tag_byte + pres_ctx_shift + 2] )
+ {
+ // set the state accordingly when the OSI ACSE presentation context ( | 01 | ) has been found
+ case PresCtx::PRES_CTX_ACSE:
+ {
+ // place the index at the last byte of the presentation context
+ idx = init_byte + encode_tag_shift + pres_ctx_shift + 2;
+ mms.state = MMS_STATE__OSI_ACSE;
+ ctx_likely = true;
+ break;
+ }
+ // set the state accordingly when the MMS presentation context ( | 03 | ) has been found
+ case PresCtx::PRES_CTX_MMS:
+ {
+ // place the index at the last byte of the presentation context
+ idx = init_byte + encode_tag_shift + pres_ctx_shift + 2;
+ mms.state = MMS_STATE__MMS;
+ ctx_likely = true;
+ break;
+ }
+ // no default as we want to keep looking if an acceptable context is not found
+ }
+ }
+ }
+ }
+ }
+ return idx;
+}
+
+static uint32_t search_for_osi_session_spdu_params( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx )
+{
enum
{
- MMS_CONFIRMED_REQUEST_TAG = 0xA0,
- MMS_CONFIRMED_RESPONSE_TAG = 0xA1,
- MMS_CONFIRMED_ERROR_TAG = 0xA2,
- MMS_UNCONFIRMED_TAG = 0xA3,
- MMS_REJECT_TAG = 0xA4,
- MMS_CANCEL_REQUEST_TAG = 0x85,
- MMS_CANCEL_RESPONSE_TAG = 0x86,
- MMS_CANCEL_ERROR_TAG = 0xA7,
- MMS_INITIATE_REQUEST_TAG = 0xA8,
- MMS_INITIATE_RESPONSE_TAG = 0xA9,
- MMS_INITIATE_ERROR_TAG = 0xAA,
- MMS_CONCLUDE_REQUEST_TAG = 0x8B,
- MMS_CONCLUDE_RESPONSE_TAG = 0x8C,
- MMS_CONCLUDE_ERROR_TAG = 0xAD,
+ CN_SPDU_PARAM__CONNECT_ACCEPT_ITEM = 0x05,
+ CN_SPDU_PARAM__SESSION_REQUIREMENT = 0x14,
+ CN_SPDU_PARAM__SESSION_USER_DATA = 0xC1,
};
- uint32_t idx = 0;
- while ( idx < len )
+ // len reduction is to allow space for the forward looking checks
+ while ( idx < len - 3 )
{
- switch ( mms.state )
+ // check for the possibility of a connect accept item
+ if ( data[idx] == CN_SPDU_PARAM__CONNECT_ACCEPT_ITEM )
{
- case MMS_STATE__TPKT_VER:
+ // track that the item was found
+ mms.connect_accept_item_likely = true;
+ }
+ // check for the possibility of a session requirement
+ else if ( data[idx] == CN_SPDU_PARAM__SESSION_REQUIREMENT )
+ {
+ // track that the item was found
+ mms.session_requirement_likely = true;
+ }
+ // check for the possibility of a session user data item
+ else if ( data[idx] == CN_SPDU_PARAM__SESSION_USER_DATA )
+ {
+ // when this has been found and there is a good chance all of the other required items exist, move on to look for mms
+ if ( mms.connect_accept_item_likely &&
+ mms.session_requirement_likely )
{
- mms.state = MMS_STATE__TPKT_RES;
+ mms.state = MMS_STATE__OSI_SESSION_SPDU_USER_DATA_LEN;
break;
}
-
- case MMS_STATE__TPKT_RES:
+ // otherwise it is unlikely that this is mms
+ else
{
- mms.state = MMS_STATE__TPKT_LEN1;
+ mms.state = MMS_STATE__NOT_FOUND;
break;
}
+ }
+ // no else case as we want to allow for the possibility of non-standard items
+
+ // increment the index to look at the item length field
+ idx++;
+ // increment the index to the end of the item data
+ idx += data[idx];
+ // increment the index to the start of the next item
+ idx++;
+
+ // if the index has gotten larger than the available buffer, bail
+ if ( idx >= len )
+ {
+ mms.state = MMS_STATE__NOT_FOUND;
+ break;
+ }
+ }
+ return idx;
+}
+
+static uint32_t search_for_osi_acse_type( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx )
+{
+ enum
+ {
+ OSI_ACSE_AARQ = 0x60,
+ OSI_ACSE_AARE = 0x61,
+ };
- case MMS_STATE__TPKT_LEN1:
+ constexpr uint32_t max_search_depth_idx = 3;
+ if ( verify_search_depth_idx( len, idx, max_search_depth_idx ) )
+ {
+ // when ACSE is likely found, do processing at that layer to determine if MMS is being transported
+ // length field could be 1-2 bytes depending on encoding
+ for ( uint32_t j = 0; j < max_search_depth_idx; j++ )
+ {
+ // look for either the AARQ ( | 60 | ) or AARE ( | 61 | ) tag
+ if ( data[idx+j] == OSI_ACSE_AARQ || data[idx+j] == OSI_ACSE_AARE )
{
- mms.state = MMS_STATE__TPKT_LEN2;
+ mms.state = MMS_STATE__OSI_ACSE_DATA;
+ idx += j;
break;
}
+ }
+ }
+ return idx;
+}
- case MMS_STATE__TPKT_LEN2:
+static uint32_t search_for_osi_acse_data( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx )
+{
+ // this will likely be a good distance from the current position
+ // minus three is to give space for the forward checks for the direct and indirect reference
+ for ( uint32_t k = 0; idx+k < len-3; k++ )
+ {
+ // look for the MMS presentation context ( | 02 01 03 | )
+ if ( data[idx+k] == 0x02 && data[idx+k+1] == 0x01 && data[idx+k+2] == 0x03 )
+ {
+ mms.state = MMS_STATE__MMS;
+ // increment the index to the end of the mms context id reference
+ idx += k+2;
+ break;
+ }
+ }
+ return idx;
+}
+
+static uint32_t search_for_mms( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx )
+{
+ constexpr uint32_t max_search_depth_idx = 2;
+ if ( verify_search_depth_idx( len, idx, max_search_depth_idx ) )
+ {
+ // search within the next two bytes to determine if mms is likely
+ for ( uint32_t j = 0; j < max_search_depth_idx; j++ )
+ {
+ // for each remaining byte check to see if it is in the known tag map
+ switch ( data[idx+j] )
{
- mms.state = MMS_STATE__COTP_LEN;
+ case MMS_CONFIRMED_REQUEST_TAG:
+ {
+ mms.state = MMS_STATE__MMS_CONFIRMED_REQUEST;
+ idx += j;
break;
}
-
- case MMS_STATE__COTP_LEN:
+ case MMS_CONFIRMED_RESPONSE_TAG: // fallthrough intentional
+ case MMS_CONFIRMED_ERROR_TAG: // fallthrough intentional
+ case MMS_UNCONFIRMED_TAG: // fallthrough intentional
+ case MMS_REJECT_TAG: // fallthrough intentional
+ case MMS_CANCEL_REQUEST_TAG: // fallthrough intentional
+ case MMS_CANCEL_RESPONSE_TAG: // fallthrough intentional
+ case MMS_CANCEL_ERROR_TAG: // fallthrough intentional
+ case MMS_INITIATE_REQUEST_TAG: // fallthrough intentional
+ case MMS_INITIATE_RESPONSE_TAG: // fallthrough intentional
+ case MMS_INITIATE_ERROR_TAG: // fallthrough intentional
+ case MMS_CONCLUDE_REQUEST_TAG: // fallthrough intentional
+ case MMS_CONCLUDE_RESPONSE_TAG: // fallthrough intentional
+ case MMS_CONCLUDE_ERROR_TAG:
{
- mms.state = MMS_STATE__COTP_PDU;
+ // if an MMS tag exists in the remaining data,
+ // hand off to the MMS service inspector
+ mms.state = MMS_STATE__FOUND;
+ idx += j;
break;
}
+ // no default as we want to keep searching if a result wasn't found in this loop
+ }
- case MMS_STATE__COTP_PDU:
+ // move on to the next case when a state has been set
+ if ( mms.state != MMS_STATE__NOT_FOUND )
{
- // 7 6 5 4 3 2 1 0
- // ---------------
- // . . . . x x x x Destination Reference
- // x x x x . . . . PDU Type
- const uint32_t MMS_COTP_PDU_DT_DATA = 0x0F;
+ break;
+ }
+ }
+ }
+ return idx;
+}
- if ( data[idx] >> 0x04 != MMS_COTP_PDU_DT_DATA )
- {
- mms.state = MMS_STATE__NOT_FOUND;
- break;
- }
+static uint32_t search_for_mms_confirmed_request( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx )
+{
+ // default to MMS not found
+ mms.state = MMS_STATE__NOT_FOUND;
+
+ // confirmed service request types
+ enum
+ {
+ DEFINE_NAMED_TYPE_MESSAGE = 0xAE,
+ DEFINE_NAMED_VARIABLE_MESSAGE = 0xA7,
+ DEFINE_NAMED_VARIABLE_LIST_MESSAGE = 0xAB,
+ DEFINE_SCATTERED_ACCESS_MESSAGE = 0xA8,
+ DEFINE_SEMAPHORE_MESSAGE = 0xB5,
+ DELETE_NAMED_TYPE_MESSAGE = 0xB0,
+ DELETE_NAMED_VARIABLE_LIST_MESSAGE = 0xAD,
+ DELETE_SEMAPHORE_MESSAGE = 0xB6,
+ DELETE_VARIABLE_ACCESS_MESSAGE = 0xAA,
+ DOWNLOAD_SEGMENT_MESSAGE = 0x9B,
+ GET_NAME_LIST_MESSAGE = 0xA1,
+ GET_NAMED_TYPE_ATTRIBUTES_MESSAGE = 0xAF,
+ GET_NAMED_VARIABLE_LIST_ATTRIBUTES_MESSAGE = 0xAC,
+ GET_SCATTERED_ACCESS_ATTRIBUTES_MESSAGE = 0xA9,
+ GET_VARIABLE_ACCESS_ATTRIBUTES_MESSAGE = 0xA6,
+ IDENTIFY_MESSAGE = 0x82,
+ INITIATE_DOWNLOAD_SEQUENCE_MESSAGE = 0xBA,
+ INITIATE_UPLOAD_SEQUENCE_MESSAGE = 0x9D,
+ INPUT_MESSAGE = 0xB1,
+ OUTPUT_MESSAGE = 0xB2,
+ READ_MESSAGE = 0xA4,
+ RELINQUISH_CONTROL_MESSAGE = 0xB4,
+ RENAME_MESSAGE = 0xA3,
+ REPORT_POOL_SEMAPHORE_STATUS_MESSAGE = 0xB8,
+ REPORT_SEMAPHORE_ENTRY_STATUS_MESSAGE = 0xB9,
+ REPORT_SEMAPHORE_STATUS_MESSAGE = 0xB7,
+ STATUS_MESSAGE = 0x80,
+ TAKE_CONTROL_MESSAGE = 0xB3,
+ TERMINATE_DOWNLOAD_SEQUENCE_MESSAGE = 0xBC,
+ UPLOAD_SEGMENT_MESSAGE = 0x9E,
+ WRITE_MESSAGE = 0xA5,
+ EXPANSION_9F = 0x9F,
+ EXPANSION_BF = 0xBF,
+ };
- mms.state = MMS_STATE__COTP_TPDU_NUM;
+ // look for a known Confirmed Service Request 4-6 bytes away
+ constexpr uint32_t max_search_depth_idx = 6;
+ if ( verify_search_depth_idx( len, idx, max_search_depth_idx ) )
+ {
+ for ( uint32_t j = 3; j <= max_search_depth_idx; j++ )
+ {
+ // check for any of the single byte service tags
+ switch ( data[idx+j] )
+ {
+ case DEFINE_NAMED_TYPE_MESSAGE: // fallthrough intentional
+ case DEFINE_NAMED_VARIABLE_MESSAGE: // fallthrough intentional
+ case DEFINE_NAMED_VARIABLE_LIST_MESSAGE: // fallthrough intentional
+ case DEFINE_SCATTERED_ACCESS_MESSAGE: // fallthrough intentional
+ case DEFINE_SEMAPHORE_MESSAGE: // fallthrough intentional
+ case DELETE_NAMED_TYPE_MESSAGE: // fallthrough intentional
+ case DELETE_NAMED_VARIABLE_LIST_MESSAGE: // fallthrough intentional
+ case DELETE_SEMAPHORE_MESSAGE: // fallthrough intentional
+ case DELETE_VARIABLE_ACCESS_MESSAGE: // fallthrough intentional
+ case DOWNLOAD_SEGMENT_MESSAGE: // fallthrough intentional
+ case GET_NAME_LIST_MESSAGE: // fallthrough intentional
+ case GET_NAMED_TYPE_ATTRIBUTES_MESSAGE: // fallthrough intentional
+ case GET_NAMED_VARIABLE_LIST_ATTRIBUTES_MESSAGE: // fallthrough intentional
+ case GET_SCATTERED_ACCESS_ATTRIBUTES_MESSAGE: // fallthrough intentional
+ case GET_VARIABLE_ACCESS_ATTRIBUTES_MESSAGE: // fallthrough intentional
+ case IDENTIFY_MESSAGE: // fallthrough intentional
+ case INITIATE_DOWNLOAD_SEQUENCE_MESSAGE: // fallthrough intentional
+ case INITIATE_UPLOAD_SEQUENCE_MESSAGE: // fallthrough intentional
+ case INPUT_MESSAGE: // fallthrough intentional
+ case OUTPUT_MESSAGE: // fallthrough intentional
+ case READ_MESSAGE: // fallthrough intentional
+ case RELINQUISH_CONTROL_MESSAGE: // fallthrough intentional
+ case RENAME_MESSAGE: // fallthrough intentional
+ case REPORT_POOL_SEMAPHORE_STATUS_MESSAGE: // fallthrough intentional
+ case REPORT_SEMAPHORE_ENTRY_STATUS_MESSAGE: // fallthrough intentional
+ case REPORT_SEMAPHORE_STATUS_MESSAGE: // fallthrough intentional
+ case STATUS_MESSAGE: // fallthrough intentional
+ case TAKE_CONTROL_MESSAGE: // fallthrough intentional
+ case TERMINATE_DOWNLOAD_SEQUENCE_MESSAGE: // fallthrough intentional
+ case UPLOAD_SEGMENT_MESSAGE: // fallthrough intentional
+ case WRITE_MESSAGE: // fallthrough intentional
+ case EXPANSION_9F: // fallthrough intentional
+ case EXPANSION_BF:
+ {
+ idx += j;
+ mms.state = MMS_STATE__FOUND;
break;
}
+ // no default as we want to keep searching if a result wasn't found in this loop
+ }
- case MMS_STATE__COTP_TPDU_NUM:
+ // move on to the next case when a state has been set
+ if ( mms.state != MMS_STATE__NOT_FOUND )
{
- mms.state = MMS_STATE__OSI_SESSION_SPDU;
break;
}
+ }
+ }
- case MMS_STATE__OSI_SESSION_SPDU:
+ return idx;
+}
+
+bool CurseBook::mms_curse( const uint8_t* data, unsigned len, CurseTracker* tracker )
+{
+ // peg the tracker to MMS
+ MmsTracker& mms = tracker->mms;
+
+ // if the state is set to MMS_STATE__SEARCH it means we most likely
+ // have a split pipelined message coming through and will need to
+ // reset the state
+ if ( mms.state == MMS_STATE__SEARCH )
+ {
+ mms.state = mms.last_state;
+ }
+
+ uint32_t idx = 0;
+ while ( idx < len )
+ {
+ switch ( mms.state )
+ {
+ case MMS_STATE__TPKT_VER:
+ {
+ // MMS is only known to run over version | 03 |
+ if ( data[idx] != 0x03 )
{
- // define all known OSI Session layer SPDU tags to check
- enum
- {
- MMS_OSI_SESSION_SPDU_GT_DT = 0x01,
- MMS_OSI_SESSION_SPDU_CN = 0x0D,
- MMS_OSI_SESSION_SPDU_AC = 0x0E,
- };
+ mms.state = MMS_STATE__NOT_FOUND;
+ break;
+ }
- switch ( data[idx] )
- {
- // check for a known MMS message tag in the event Session/Pres/ACSE aren't used
- case MMS_CONFIRMED_REQUEST_TAG: // fallthrough intentional
- case MMS_CONFIRMED_RESPONSE_TAG: // fallthrough intentional
- case MMS_CONFIRMED_ERROR_TAG: // fallthrough intentional
- case MMS_UNCONFIRMED_TAG: // fallthrough intentional
- case MMS_REJECT_TAG: // fallthrough intentional
- case MMS_CANCEL_REQUEST_TAG: // fallthrough intentional
- case MMS_CANCEL_RESPONSE_TAG: // fallthrough intentional
- case MMS_CANCEL_ERROR_TAG: // fallthrough intentional
- case MMS_INITIATE_REQUEST_TAG: // fallthrough intentional
- case MMS_INITIATE_RESPONSE_TAG: // fallthrough intentional
- case MMS_INITIATE_ERROR_TAG: // fallthrough intentional
- case MMS_CONCLUDE_REQUEST_TAG: // fallthrough intentional
- case MMS_CONCLUDE_RESPONSE_TAG: // fallthrough intentional
- case MMS_CONCLUDE_ERROR_TAG:
- {
- // if an MMS tag exists in the remaining data,
- // hand off to the MMS service inspector
- mms.state = MMS_STATE__FOUND;
- break;
- }
+ mms.state = MMS_STATE__TPKT_RES;
+ break;
+ }
- // if mms isn't found, search for an OSI Session layer
- case MMS_OSI_SESSION_SPDU_GT_DT: // fallthrough intentional
- case MMS_OSI_SESSION_SPDU_CN: // fallthrough intentional
- case MMS_OSI_SESSION_SPDU_AC:
- {
- mms.state = MMS_STATE__MMS;
- break;
- }
+ case MMS_STATE__TPKT_RES:
+ {
+ mms.state = MMS_STATE__TPKT_LEN1;
+ break;
+ }
- // if neither are found, it is most likely not MMS
- default:
- {
- mms.state = MMS_STATE__NOT_FOUND;
- }
- }
+ case MMS_STATE__TPKT_LEN1:
+ {
+ mms.state = MMS_STATE__TPKT_LEN2;
+ break;
+ }
+ case MMS_STATE__TPKT_LEN2:
+ {
+ mms.state = MMS_STATE__COTP_LEN;
+ break;
+ }
+
+ case MMS_STATE__COTP_LEN:
+ {
+ mms.state = MMS_STATE__COTP_PDU;
+ break;
+ }
+
+ case MMS_STATE__COTP_PDU:
+ {
+ // 7 6 5 4 3 2 1 0
+ // ---------------
+ // . . . . x x x x Destination Reference
+ // x x x x . . . . PDU Type
+ constexpr uint32_t MMS_COTP_PDU_DT_DATA = 0x0F;
+
+ if ( data[idx] >> 0x04 != MMS_COTP_PDU_DT_DATA )
+ {
+ mms.state = MMS_STATE__NOT_FOUND;
break;
}
- case MMS_STATE__MMS:
- {
- // loop through the remaining bytes in the buffer checking for known MMS tags
- for ( uint32_t i=idx; i < len; i++ )
- {
- // for each remaining byte check to see if it is in the known tag map
- switch ( data[i] )
- {
- case MMS_CONFIRMED_REQUEST_TAG: // fallthrough intentional
- case MMS_CONFIRMED_RESPONSE_TAG: // fallthrough intentional
- case MMS_CONFIRMED_ERROR_TAG: // fallthrough intentional
- case MMS_UNCONFIRMED_TAG: // fallthrough intentional
- case MMS_REJECT_TAG: // fallthrough intentional
- case MMS_CANCEL_REQUEST_TAG: // fallthrough intentional
- case MMS_CANCEL_RESPONSE_TAG: // fallthrough intentional
- case MMS_CANCEL_ERROR_TAG: // fallthrough intentional
- case MMS_INITIATE_REQUEST_TAG: // fallthrough intentional
- case MMS_INITIATE_RESPONSE_TAG: // fallthrough intentional
- case MMS_INITIATE_ERROR_TAG: // fallthrough intentional
- case MMS_CONCLUDE_REQUEST_TAG: // fallthrough intentional
- case MMS_CONCLUDE_RESPONSE_TAG: // fallthrough intentional
- case MMS_CONCLUDE_ERROR_TAG:
- {
- // if an MMS tag exists in the remaining data,
- // hand off to the MMS service inspector
- mms.state = MMS_STATE__FOUND;
- break;
- }
- // no default here as it we don't know when we would hit
- // the first MMS tag without doing full parsing
- }
+ mms.state = MMS_STATE__COTP_TPDU_NUM;
+ break;
+ }
- // exit the loop when a state has been determined
- if ( mms.state == MMS_STATE__NOT_FOUND
- or mms.state == MMS_STATE__SEARCH
- or mms.state == MMS_STATE__FOUND )
- {
- break;
- }
- }
+ case MMS_STATE__COTP_TPDU_NUM:
+ {
+ mms.state = MMS_STATE__OSI_SESSION_SPDU;
+ break;
+ }
+
+ case MMS_STATE__OSI_SESSION_SPDU:
+ {
+ idx = search_for_osi_session_spdu(mms, data, idx);
+ break;
+ }
+
+ //
+ // State path for most MMS messages
+ //
+
+ // check the length field of a GT SPDU
+ case MMS_STATE__OSI_SESSION_SPDU_GT_LEN:
+ {
+ // length should always be zero for MMS
+ if ( data[idx] != 0x00 )
+ {
+ mms.state = MMS_STATE__NOT_FOUND;
break;
}
- case MMS_STATE__FOUND:
- {
- mms.state = MMS_STATE__TPKT_VER;
+ mms.state = MMS_STATE__OSI_SESSION_SPDU_DT;
+ break;
+ }
- return true;
+ // check for the tag of a DT SPDU
+ case MMS_STATE__OSI_SESSION_SPDU_DT:
+ {
+ // tag should always be | 01 | for DT
+ if ( data[idx] != MMS_OSI_SESSION_SPDU_GT_DT )
+ {
+ mms.state = MMS_STATE__NOT_FOUND;
+ break;
}
- case MMS_STATE__NOT_FOUND:
+ mms.state = MMS_STATE__OSI_SESSION_SPDU_DT_LEN;
+ break;
+ }
+
+ // check the length field of a DT SPDU
+ case MMS_STATE__OSI_SESSION_SPDU_DT_LEN:
+ {
+ // length should always be zero for MMS
+ if ( data[idx] != 0x00 )
{
- mms.state = MMS_STATE__TPKT_VER;
+ mms.state = MMS_STATE__NOT_FOUND;
+ break;
+ }
+
+ mms.state = MMS_STATE__OSI_PRES_USER_DATA;
+ break;
+ }
+
+ // process the User Data Presentation type
+ case MMS_STATE__OSI_PRES_USER_DATA:
+ {
+ idx = search_for_pres_ctx(mms, data, len, idx);
+ break;
+ }
+
+
+ //
+ // State path for Initiate-Request and Initiate-Response
+ //
+
+ // skip the CN SPDU length field
+ case MMS_STATE__OSI_SESSION_SPDU_CN_LEN:
+ {
+ mms.state = MMS_STATE__OSI_SESSION_SPDU_PARAMS;
+ break;
+ }
+
+ // skip the AC SPDU length field
+ case MMS_STATE__OSI_SESSION_SPDU_AC_LEN:
+ {
+ mms.state = MMS_STATE__OSI_SESSION_SPDU_PARAMS;
+ break;
+ }
+
+ // check the parameters
+ case MMS_STATE__OSI_SESSION_SPDU_PARAMS:
+ {
+ idx = search_for_osi_session_spdu_params(mms, data, len, idx);
+ break;
+ }
- return false;
+ // jump over the length field
+ case MMS_STATE__OSI_SESSION_SPDU_USER_DATA_LEN:
+ {
+ mms.state = MMS_STATE__OSI_PRES_CP_CPA;
+ break;
+ }
+
+ // process a CP or CPA presentation type
+ case MMS_STATE__OSI_PRES_CP_CPA:
+ {
+ // look for the ' CP ' or ' CPA ' presentation type tag ( | 31 | );
+ if ( data[idx] != 0x31 )
+ {
+ mms.state = MMS_STATE__NOT_FOUND;
+ break;
}
- default:
+ mms.state = MMS_STATE__OSI_PRES_CP_CPA_USER_DATA_ACSE_LOCATE;
+ break;
+ }
+
+ //
+ case MMS_STATE__OSI_PRES_CP_CPA_USER_DATA_ACSE_LOCATE:
+ {
+ idx = search_for_pres_ctx(mms, data, len, idx);
+ break;
+ }
+
+ // check for ACSE
+ case MMS_STATE__OSI_ACSE:
+ {
+ // look for the presentation data values single ASN1 type ( | A0 | );
+ if ( data[idx] != 0xA0 )
{
mms.state = MMS_STATE__NOT_FOUND;
- assert(false);
break;
}
+
+ mms.state = MMS_STATE__OSI_ACSE_TYPE;
+ break;
+ }
+
+ // look for a supported ACSE type tag
+ case MMS_STATE__OSI_ACSE_TYPE:
+ {
+ idx = search_for_osi_acse_type(mms, data, len, idx);
+ break;
+ }
+
+ //
+ case MMS_STATE__OSI_ACSE_DATA:
+ {
+ idx = search_for_osi_acse_data(mms, data, len, idx);
+ break;
+ }
+
+
+ //
+ // State path for MMS convergence
+ //
+
+ // Look for a byte known to represent a MMS tag
+ case MMS_STATE__MMS:
+ {
+ idx = search_for_mms(mms, data, len, idx);
+ break;
+ }
+
+ case MMS_STATE__MMS_CONFIRMED_REQUEST:
+ {
+ idx = search_for_mms_confirmed_request(mms, data, len, idx);
+ break;
+ }
+
+ case MMS_STATE__FOUND:
+ {
+ mms.state = MMS_STATE__TPKT_VER;
+
+ return true;
+ }
+
+ case MMS_STATE__NOT_FOUND:
+ {
+ mms.state = MMS_STATE__TPKT_VER;
+
+ return false;
+ }
+
+ default:
+ {
+ mms.state = MMS_STATE__NOT_FOUND;
+ assert( false );
+ break;
+ }
}
idx++;