static gboolean dissect_ses_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree) { /* must check that this really is a ses packet */ int offset = 0; guint8 type; int len_len; guint16 len; /* first, check do we have at least 4 bytes (type+length) */ if (tvb_length(tvb) < 2) return FALSE; /* no */ /* can we recognize session PDU ? Return FALSE if not */ /* get SPDU type */ type = tvb_get_guint8(tvb, offset); /* check SPDU type */ if (match_strval(type, ses_vals) == NULL) { return FALSE; /* no, it isn't a session PDU */ } /* can we recognize the second session PDU if the first one was * a Give Tokens PDU? Return FALSE if not */ if(tvb_bytes_exist(tvb, 2, 2) && type == SES_GIVE_TOKENS) { /* get SPDU type */ type = tvb_get_guint8(tvb, offset+2); /* check SPDU type */ if (match_strval(type, ses_vals) == NULL) { return FALSE; /* no, it isn't a session PDU */ } } /* some Siemens SIMATIC protocols also use COTP, and shouldn't be * misinterpreted as SES. * the starter in this case is fixed to 0x32 (SES_MINOR_SYNC_ACK for SES), * so if the parameter type is unknown, it's probably SIMATIC */ if(type == 0x32 && tvb_length(tvb) >= 3) { type = tvb_get_guint8(tvb, offset+2); if (match_strval(type, param_vals) == NULL) { return FALSE; /* it's probably a SIMATIC protocol */ } } /* OK,let's check SPDU length */ /* get length of SPDU */ len = get_item_len(tvb, offset+1, &len_len); /* add header length */ len+=len_len; /* do we have enough bytes ? */ if (tvb_length(tvb) < len) return FALSE; /* no */ dissect_ses(tvb, pinfo, parent_tree); return TRUE; }
/* * Returns TRUE if there's a User Information field in this SPDU, FALSE * otherwise. */ static gboolean dissect_parameters(tvbuff_t *tvb, int offset, guint16 len, proto_tree *tree, proto_tree *ses_tree, packet_info *pinfo, guint8 *enclosure_item_flags, struct SESSION_DATA_STRUCTURE *session) { gboolean has_user_information = TRUE; proto_item *ti; proto_tree *param_tree; guint8 param_type; const char *param_str; int len_len; guint16 param_len; while (len != 0) { param_type = tvb_get_guint8(tvb, offset); param_tree = proto_tree_add_subtree(ses_tree, tvb, offset, -1, ett_ses_param, &ti, val_to_str(param_type, param_vals, "Unknown parameter type (0x%02x)")); param_str = val_to_str_const(param_type, param_vals, "Unknown"); proto_tree_add_text(param_tree, tvb, offset, 1, "Parameter type: %s", param_str); offset++; len--; param_len = get_item_len(tvb, offset, &len_len); if (len_len > len) { proto_item_set_len(ti, len + 1 ); proto_tree_add_text(param_tree, tvb, offset, len, "Parameter length doesn't fit in parameter"); return has_user_information; } len -= len_len; if (param_len > len) { proto_item_set_len(ti, len + 1 + len_len); proto_tree_add_text(param_tree, tvb, offset, len, "Parameter length: %u, should be <= %u", param_len, len); return has_user_information; } proto_item_set_len(ti, 1 + len_len + param_len); proto_tree_add_text(param_tree, tvb, offset, len_len, "Parameter length: %u", param_len); offset += len_len; if (param_str != NULL) { switch(param_type) { case Extended_User_Data: call_pres_dissector(tvb, offset, param_len, pinfo, tree, param_tree, session); break; case User_Data: call_pres_dissector(tvb, offset, param_len, pinfo, tree, param_tree, session); break; /* handle PGI's */ case Connect_Accept_Item: case Connection_Identifier: case Linking_Information: /* Yes. */ if (!dissect_parameter_group(tvb, offset, tree, param_tree, pinfo, param_len, enclosure_item_flags, session)) has_user_information = FALSE; break; /* everything else is a PI */ default: if (!dissect_parameter(tvb, offset, tree, param_tree, pinfo, param_type, param_len, enclosure_item_flags, session)) has_user_information = FALSE; break; } } offset += param_len; len -= param_len; } return has_user_information; }
/* * Dissect an SPDU. */ static int dissect_spdu(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, gboolean tokens, gboolean connectionless) { gboolean has_user_information = FALSE; guint8 type; proto_item *ti = NULL; proto_tree *ses_tree = NULL; int len_len; guint16 parameters_len; tvbuff_t *next_tvb = NULL; guint32 *pres_ctx_id = NULL; guint8 enclosure_item_flags = BEGINNING_SPDU|END_SPDU; struct SESSION_DATA_STRUCTURE session; /* * Get SPDU type. */ type = tvb_get_guint8(tvb, offset); session.spdu_type = type; session.abort_type = SESSION_NO_ABORT; session.pres_ctx_id = 0; session.ros_op = 0; session.rtse_reassemble = FALSE; if(connectionless) { col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, ses_vals, "Unknown SPDU type (0x%02x)")); if (tree) { ti = proto_tree_add_item(tree, proto_clses, tvb, offset, -1, ENC_NA); ses_tree = proto_item_add_subtree(ti, ett_ses); proto_tree_add_uint(ses_tree, hf_ses_type, tvb, offset, 1, type); } has_user_information = TRUE; } else if (tokens) { col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, ses_category0_vals, "Unknown SPDU type (0x%02x)")); if (tree) { ti = proto_tree_add_item(tree, proto_ses, tvb, offset, -1, ENC_NA); ses_tree = proto_item_add_subtree(ti, ett_ses); proto_tree_add_uint(ses_tree, hf_ses_type_0, tvb, offset, 1, type); } } else { col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, ses_vals, "Unknown SPDU type (0x%02x)")); if (tree) { ti = proto_tree_add_item(tree, proto_ses, tvb, offset, -1, ENC_NA); ses_tree = proto_item_add_subtree(ti, ett_ses); proto_tree_add_uint(ses_tree, hf_ses_type, tvb, offset, 1, type); } /* * Might this SPDU have a User Information field? */ switch (type) { case SES_DATA_TRANSFER: case SES_EXPEDITED: case SES_TYPED_DATA: has_user_information = TRUE; break; case SES_MAJOR_SYNC_POINT: pres_ctx_id = (guint32 *)p_get_proto_data(wmem_file_scope(), pinfo, proto_ses, 0); if (ses_rtse_reassemble != 0 && !pres_ctx_id) { /* First time visited - save pres_ctx_id */ pres_ctx_id = wmem_new(wmem_file_scope(), guint32); *pres_ctx_id = ses_pres_ctx_id; p_add_proto_data(wmem_file_scope(), pinfo, proto_ses, 0, pres_ctx_id); } if (pres_ctx_id) { session.pres_ctx_id = *pres_ctx_id; session.rtse_reassemble = TRUE; has_user_information = TRUE; } ses_rtse_reassemble = FALSE; break; } } offset++; /* get length of SPDU parameter field */ parameters_len = get_item_len(tvb, offset, &len_len); if (tree) proto_tree_add_uint(ses_tree, hf_ses_length, tvb, offset, len_len, parameters_len); offset += len_len; /* Dissect parameters. */ if (!dissect_parameters(tvb, offset, parameters_len, tree, ses_tree, pinfo, &enclosure_item_flags, &session)) has_user_information = FALSE; offset += parameters_len; proto_item_set_end(ti, tvb, offset); /* Dissect user information, if present */ if (!ses_desegment || enclosure_item_flags == (BEGINNING_SPDU|END_SPDU)) { if (has_user_information) { /* Not desegment or only one segment */ if (tvb_reported_length_remaining(tvb, offset) > 0 || type == SES_MAJOR_SYNC_POINT) { next_tvb = tvb_new_subset_remaining(tvb, offset); } } } else { conversation_t *conversation = NULL; fragment_head *frag_msg = NULL; gint fragment_len; guint32 ses_id = 0; /* Use conversation index as segment id */ conversation = find_conversation (pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); if (conversation != NULL) { ses_id = conversation->index; } fragment_len = tvb_reported_length_remaining (tvb, offset); ti = proto_tree_add_item (ses_tree, hf_ses_segment_data, tvb, offset, fragment_len, ENC_NA); proto_item_append_text (ti, " (%d byte%s)", fragment_len, plurality (fragment_len, "", "s")); frag_msg = fragment_add_seq_next (&ses_reassembly_table, tvb, offset, pinfo, ses_id, NULL, fragment_len, (enclosure_item_flags & END_SPDU) ? FALSE : TRUE); next_tvb = process_reassembled_data (tvb, offset, pinfo, "Reassembled SES", frag_msg, &ses_frag_items, NULL, (enclosure_item_flags & END_SPDU) ? tree : ses_tree); has_user_information = TRUE; offset += fragment_len; } if (has_user_information && next_tvb) { if (!pres_handle) { call_dissector(data_handle, next_tvb, pinfo, tree); } else { /* Pass the session pdu to the presentation dissector */ call_dissector_with_data(pres_handle, next_tvb, pinfo, tree, &session); } /* * No more SPDUs to dissect. Set the offset to the * end of the tvbuff. */ offset = tvb_captured_length(tvb); if (session.rtse_reassemble && type == SES_DATA_TRANSFER) { ses_pres_ctx_id = session.pres_ctx_id; ses_rtse_reassemble = TRUE; } } return offset; }
static gboolean dissect_parameter_group(tvbuff_t *tvb, int offset, proto_tree *tree, proto_tree *pg_tree, packet_info *pinfo, guint16 pg_len, guint8 *enclosure_item_flags, struct SESSION_DATA_STRUCTURE *session) { gboolean has_user_information = TRUE; proto_item *ti; proto_tree *param_tree; guint8 param_type; const char *param_str; int len_len; guint16 param_len; while(pg_len != 0) { param_type = tvb_get_guint8(tvb, offset); ti = proto_tree_add_text(pg_tree, tvb, offset, -1, "%s", val_to_str(param_type, param_vals, "Unknown parameter type (0x%02x)")); param_tree = proto_item_add_subtree(ti, ett_ses_param); param_str = val_to_str_const(param_type, param_vals, "Unknown"); proto_tree_add_text(param_tree, tvb, offset, 1, "Parameter type: %s", param_str); offset++; pg_len--; param_len = get_item_len(tvb, offset, &len_len); if (len_len > pg_len) { proto_item_set_len(ti, pg_len + 1); proto_tree_add_text(param_tree, tvb, offset, pg_len, "Parameter length doesn't fit in parameter"); return has_user_information; } pg_len -= len_len; if (param_len > pg_len) { proto_item_set_len(ti, pg_len + 1 + len_len); proto_tree_add_text(param_tree, tvb, offset, pg_len, "Parameter length: %u, should be <= %u", param_len, pg_len); return has_user_information; } proto_item_set_len(ti, 1 + len_len + param_len); proto_tree_add_text(param_tree, tvb, offset, len_len, "Parameter length: %u", param_len); offset += len_len; if (param_str != NULL) { switch(param_type) { /* PG's in PG's are invalid, presumably */ case Extended_User_Data: case User_Data: case Connect_Accept_Item: case Connection_Identifier: case Linking_Information: proto_tree_add_text(param_tree, tvb, offset, param_len, "Parameter group inside parameter group"); break; default: if (!dissect_parameter(tvb, offset, tree, param_tree, pinfo, param_type, param_len, enclosure_item_flags, session)) has_user_information = FALSE; break; } } offset += param_len; pg_len -= param_len; } return has_user_information; }
/* * Dissect an SPDU. */ static int dissect_spdu(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, gboolean tokens, gboolean connectionless) { gboolean has_user_information = FALSE; guint8 type; proto_item *ti = NULL; proto_tree *ses_tree = NULL; int len_len; guint16 parameters_len; tvbuff_t *next_tvb; void *save_private_data; guint32 *pres_ctx_id = NULL; struct SESSION_DATA_STRUCTURE session; /* * Get SPDU type. */ type = tvb_get_guint8(tvb, offset); session.spdu_type = type; session.abort_type = SESSION_NO_ABORT; session.ros_op = 0; session.rtse_reassemble = FALSE; if(connectionless) { if (check_col(pinfo->cinfo, COL_INFO)) col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, ses_vals, "Unknown SPDU type (0x%02x)")); if (tree) { ti = proto_tree_add_item(tree, proto_clses, tvb, offset, -1, FALSE); ses_tree = proto_item_add_subtree(ti, ett_ses); proto_tree_add_uint(ses_tree, hf_ses_type, tvb, offset, 1, type); } has_user_information = TRUE; } else if (tokens) { if (check_col(pinfo->cinfo, COL_INFO)) col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, ses_category0_vals, "Unknown SPDU type (0x%02x)")); if (tree) { ti = proto_tree_add_item(tree, proto_ses, tvb, offset, -1, FALSE); ses_tree = proto_item_add_subtree(ti, ett_ses); proto_tree_add_uint(ses_tree, hf_ses_type_0, tvb, offset, 1, type); } } else { if (check_col(pinfo->cinfo, COL_INFO)) col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, ses_vals, "Unknown SPDU type (0x%02x)")); if (tree) { ti = proto_tree_add_item(tree, proto_ses, tvb, offset, -1, FALSE); ses_tree = proto_item_add_subtree(ti, ett_ses); proto_tree_add_uint(ses_tree, hf_ses_type, tvb, offset, 1, type); } /* * Might this SPDU have a User Information field? */ switch (type) { case SES_DATA_TRANSFER: case SES_EXPEDITED: case SES_TYPED_DATA: has_user_information = TRUE; break; case SES_MAJOR_SYNC_POINT: pres_ctx_id = p_get_proto_data (pinfo->fd, proto_ses); if (ses_rtse_reassemble != 0 && !pres_ctx_id) { /* First time visited - save pres_ctx_id */ pres_ctx_id = se_alloc (sizeof (guint32)); *pres_ctx_id = ses_pres_ctx_id; p_add_proto_data (pinfo->fd, proto_ses, pres_ctx_id); } if (pres_ctx_id) { session.pres_ctx_id = *pres_ctx_id; session.rtse_reassemble = TRUE; has_user_information = TRUE; } ses_rtse_reassemble = FALSE; break; } } offset++; /* get length of SPDU parameter field */ parameters_len = get_item_len(tvb, offset, &len_len); if (tree) proto_tree_add_uint(ses_tree, hf_ses_length, tvb, offset, len_len, parameters_len); offset += len_len; /* Dissect parameters. */ if (!dissect_parameters(tvb, offset, parameters_len, tree, ses_tree, pinfo, &session)) has_user_information = FALSE; offset += parameters_len; proto_item_set_end(ti, tvb, offset); /* Dissect user information, if present */ if (has_user_information) { if (tvb_reported_length_remaining(tvb, offset) > 0 || type == SES_MAJOR_SYNC_POINT) { next_tvb = tvb_new_subset_remaining(tvb, offset); if(!pres_handle) { call_dissector(data_handle, next_tvb, pinfo, tree); } else { /* save type of session pdu. We'll need it in the presentation dissector */ save_private_data = pinfo->private_data; pinfo->private_data = &session; call_dissector(pres_handle, next_tvb, pinfo, tree); pinfo->private_data = save_private_data; } /* * No more SPDUs to dissect. Set the offset to the * end of the tvbuff. */ offset = tvb_length(tvb); if (session.rtse_reassemble && type == SES_DATA_TRANSFER) { ses_pres_ctx_id = session.pres_ctx_id; ses_rtse_reassemble = TRUE; } } } return offset; }