/* * Dissect a standard (reliable) ts2 packet, reassembling if required. */ static void ts2_standard_dissect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ts2_tree, ts2_conversation *conversation_data) { guint8 save_fragmented; tvbuff_t *new_tvb, *next_tvb; fragment_data *frag_msg ; guint16 fragment_number; ts2_frag *frag; gboolean outoforder; guint16 type = tvb_get_letohs(tvb, 2); /*guint16 class = tvb_get_letohs(tvb, 0);*/ proto_tree_add_item(ts2_tree, hf_ts2_seqnum, tvb, 12, 4, TRUE); /* XXX: Following fragmentation stuff should be separate from the GUI stuff ?? */ /* Get our stored fragmentation data or create one! */ if ( ! ( frag = p_get_proto_data(pinfo->fd, proto_ts2) ) ) { frag = se_alloc(sizeof(ts2_frag)); frag->frag_num=0; } /* decide if the packet is server to client or client to server * then check its fragmentation */ if(!(pinfo->fd->flags.visited)) { if(conversation_data->server_port == pinfo->srcport) { frag->fragmented = ts2_standard_find_fragments(tvb, &conversation_data->last_inorder_server_frame, &conversation_data->server_frag_size, &conversation_data->server_frag_num, &outoforder); frag->frag_num=conversation_data->server_frag_num; frag->frag_size=conversation_data->server_frag_size; } else { frag->fragmented = ts2_standard_find_fragments(tvb, &conversation_data->last_inorder_client_frame, &conversation_data->client_frag_size, &conversation_data->client_frag_num, &outoforder); frag->frag_num=conversation_data->client_frag_num; frag->frag_size=conversation_data->client_frag_size; } frag->outoforder=outoforder; p_add_proto_data(pinfo->fd, proto_ts2, frag); } /* Get our stored fragmentation data */ frag = p_get_proto_data(pinfo->fd, proto_ts2); proto_tree_add_item(ts2_tree, hf_ts2_resend_count, tvb, 16, 2, TRUE); proto_tree_add_item(ts2_tree, hf_ts2_fragmentnumber, tvb, 18, 2, TRUE); ts2_add_checked_crc32(ts2_tree, hf_ts2_crc32, tvb, 20, tvb_get_letohl(tvb, 20)); /* Reassemble the packet if its fragmented */ new_tvb = NULL; if(frag->fragmented) { save_fragmented = pinfo->fragmented; frag_msg = NULL; pinfo->fragmented = TRUE; fragment_number = tvb_get_letohs(tvb, 18); frag_msg = fragment_add_seq_check(tvb, 24, pinfo, type, msg_fragment_table, msg_reassembled_table, frag->frag_num, tvb_length_remaining(tvb, 24), fragment_number); new_tvb = process_reassembled_data(tvb, 24, pinfo,"Reassembled Message", frag_msg, &msg_frag_items, NULL, ts2_tree); if (frag_msg) { /* Reassembled */ if (check_col(pinfo->cinfo, COL_INFO)) col_append_str(pinfo->cinfo, COL_INFO, " (Message Reassembled)"); } else { /* Not last packet of reassembled Short Message */ if (check_col(pinfo->cinfo, COL_INFO))col_append_fstr(pinfo->cinfo, COL_INFO," (Message fragment %u)", frag->frag_num); } if (new_tvb) next_tvb = new_tvb; else next_tvb = tvb_new_subset_remaining(tvb, 24); pinfo->fragmented = save_fragmented; } else next_tvb = tvb_new_subset_remaining(tvb, 24); /* If we have a full packet now dissect it */ if((new_tvb || !frag->fragmented) && !frag->outoforder) { switch(type) { case TS2T_LOGINPART2: ts2_parse_loginpart2(next_tvb, ts2_tree); break; case TS2T_CHANNELLIST: ts2_parse_channellist(next_tvb, ts2_tree); break; case TS2T_PLAYERLIST: ts2_parse_playerlist(next_tvb, ts2_tree); break; case TS2T_NEWPLAYERJOINED: ts2_parse_newplayerjoined(next_tvb, ts2_tree); break; case TS2T_KNOWNPLAYERUPDATE: ts2_parse_knownplayerupdate(next_tvb, ts2_tree); break; case TS2T_PLAYERLEFT: ts2_parse_playerleft(next_tvb, ts2_tree); break; case TS2T_PLAYERKICKED: ts2_parse_playerleft(next_tvb, ts2_tree); break; case TS2T_LOGINEND: ts2_parse_loginend(next_tvb, ts2_tree); break; case TS2T_CHANGESTATUS: ts2_parse_changestatus(next_tvb, ts2_tree); break; case TS2T_SWITCHCHANNEL: ts2_parse_switchchannel(next_tvb, ts2_tree); break; case TS2T_CHANNELCHANGE: ts2_parse_channelchange(next_tvb, ts2_tree); break; } } /* The packet is out of order, update the cinfo and ignore the packet */ if(frag->outoforder) if (check_col(pinfo->cinfo, COL_INFO)) col_append_str(pinfo->cinfo, COL_INFO, " (Out Of Order, ignored)"); }
/* Dissect a TS2 packet */ static void dissect_ts2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { ts2_conversation *conversation_data; guint16 type = tvb_get_letohs(tvb, 2); guint16 klass = tvb_get_letohs(tvb, 0); conversation_data = ts2_get_conversation(pinfo); col_set_str(pinfo->cinfo, COL_PROTOCOL, "TS2"); if (check_col(pinfo->cinfo, COL_INFO)) { if(klass==TS2C_ACK) col_add_fstr(pinfo->cinfo, COL_INFO, "Class: %s", val_to_str(klass, classnames, "Unknown (0x%02x)")); else col_add_fstr(pinfo->cinfo, COL_INFO, "Type: %s, Class: %s", val_to_str(type, typenames, "Unknown (0x%02x)"), val_to_str(klass, classnames, "Unknown (0x%02x)")); } /* XXX: We need to do all the non GUI stuff whether or not if(tree) */ /* Do only once by checking visited ? */ /* ToDo: Rewrite ?? */ if (!tree) { switch(klass) { case TS2C_CONNECTION: switch(type) { case TS2T_LOGINREQUEST: conversation_data->server_port=pinfo->destport; conversation_data->server_addr=pinfo->dst; break; } break; case TS2C_STANDARD: ts2_standard_dissect(tvb, pinfo, tree, conversation_data); break; } } if (tree) { /* we are being asked for details */ proto_item *ti = NULL; proto_tree *ts2_tree = NULL; ti = proto_tree_add_item(tree, proto_ts2, tvb, 0, -1, ENC_NA); ts2_tree = proto_item_add_subtree(ti, ett_ts2); proto_tree_add_item(ts2_tree, hf_ts2_class, tvb, 0, 2, ENC_LITTLE_ENDIAN); if(klass==TS2C_ACK) proto_tree_add_item(ts2_tree, hf_ts2_resend_count, tvb, 2, 2, ENC_LITTLE_ENDIAN); else proto_tree_add_item(ts2_tree, hf_ts2_type, tvb, 2, 2, ENC_LITTLE_ENDIAN); proto_tree_add_item(ts2_tree, hf_ts2_sessionkey, tvb, 4, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(ts2_tree, hf_ts2_clientid, tvb, 8, 4, ENC_LITTLE_ENDIAN); switch(klass) { case TS2C_CONNECTION: proto_tree_add_item(ts2_tree, hf_ts2_seqnum, tvb, 12, 4, ENC_LITTLE_ENDIAN); ts2_add_checked_crc32(ts2_tree, hf_ts2_crc32, tvb, 16, tvb_get_letohl(tvb, 16)); switch(type) { case TS2T_PING: break; case TS2T_PINGREPLY: proto_tree_add_item(ts2_tree, hf_ts2_ackto, tvb, 20, 4, ENC_LITTLE_ENDIAN); break; case TS2T_LOGINREQUEST: proto_tree_add_item(ts2_tree, hf_ts2_protocol_string, tvb, 20, 1, ENC_ASCII|ENC_NA); proto_tree_add_item(ts2_tree, hf_ts2_platform_string, tvb, 50, 1, ENC_ASCII|ENC_NA); proto_tree_add_item(ts2_tree, hf_ts2_unknown, tvb, 80, 9, ENC_NA); proto_tree_add_item(ts2_tree, hf_ts2_registeredlogin, tvb, 90, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(ts2_tree, hf_ts2_name, tvb, 90, 1, ENC_ASCII|ENC_NA); proto_tree_add_item(ts2_tree, hf_ts2_password, tvb, 120, 1, ENC_ASCII|ENC_NA); proto_tree_add_item(ts2_tree, hf_ts2_nick, tvb, 150, 1, ENC_ASCII|ENC_NA); conversation_data->server_port=pinfo->destport; conversation_data->server_addr=pinfo->dst; break; case TS2T_LOGINREPLY: proto_tree_add_item(ts2_tree, hf_ts2_server_name, tvb, 20, 1, ENC_ASCII|ENC_NA); proto_tree_add_item(ts2_tree, hf_ts2_platform_string, tvb, 50, 1, ENC_ASCII|ENC_NA); proto_tree_add_item(ts2_tree, hf_ts2_unknown, tvb, 80, 9, ENC_NA); proto_tree_add_item(ts2_tree, hf_ts2_badlogin, tvb, 89, 3, ENC_LITTLE_ENDIAN); proto_tree_add_item(ts2_tree, hf_ts2_unknown, tvb, 92, 80, ENC_NA); proto_tree_add_item(ts2_tree, hf_ts2_sessionkey, tvb, 172, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(ts2_tree, hf_ts2_unknown, tvb, 178, 3, ENC_NA); proto_tree_add_item(ts2_tree, hf_ts2_server_welcome_message, tvb, 180, 1, ENC_ASCII|ENC_NA); break; } break; case TS2C_ACK: /* Ignore the type for ACK, it's always zero and clashes with CELP_5_1 */ proto_tree_add_item(ts2_tree, hf_ts2_seqnum, tvb, 12, 4, ENC_LITTLE_ENDIAN); break; case TS2C_STANDARD: ts2_standard_dissect(tvb, pinfo, ts2_tree, conversation_data); break; } } /* if (tree) */ }