aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-icq.c
diff options
context:
space:
mode:
authorGilbert Ramirez <gram@alumni.rice.edu>2004-07-18 18:06:47 +0000
committerGilbert Ramirez <gram@alumni.rice.edu>2004-07-18 18:06:47 +0000
commit669db206cb1f270046ad400fff7655e20c63e723 (patch)
tree4eff24a2e16c8963e497e1fc575f35e6af59bd26 /epan/dissectors/packet-icq.c
parentae46c27a38700af669ef907491081f09df6f6b2c (diff)
Move dissectors to epan/dissectors directory.
Also move ncp222.py, x11-fields, process-x11-fields.pl, make-reg-dotc, and make-reg-dotc.py. Adjust #include lines in files that include packet-*.h files. svn path=/trunk/; revision=11410
Diffstat (limited to 'epan/dissectors/packet-icq.c')
-rw-r--r--epan/dissectors/packet-icq.c2222
1 files changed, 2222 insertions, 0 deletions
diff --git a/epan/dissectors/packet-icq.c b/epan/dissectors/packet-icq.c
new file mode 100644
index 0000000000..407abe431f
--- /dev/null
+++ b/epan/dissectors/packet-icq.c
@@ -0,0 +1,2222 @@
+/* packet-icq.c
+ * Routines for ICQ packet disassembly
+ *
+ * $Id$
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This file: by Kojak <kojak@bigwig.net>
+ *
+ * Decoding code ripped, reference to the original author at the
+ * appropriate place with the code itself.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <ctype.h>
+#include <time.h>
+#include <string.h>
+#include <glib.h>
+
+#include <epan/packet.h>
+#include <epan/resolv.h>
+
+static int proto_icq = -1;
+static int hf_icq_uin = -1;
+static int hf_icq_client_cmd = -1;
+static int hf_icq_server_cmd = -1;
+static int hf_icq_sessionid = -1;
+static int hf_icq_checkcode = -1;
+static int hf_icq_decode = -1;
+static int hf_icq_type = -1;
+
+static gint ett_icq = -1;
+static gint ett_icq_header = -1;
+static gint ett_icq_decode = -1;
+static gint ett_icq_body = -1;
+static gint ett_icq_body_parts = -1;
+
+#define UDP_PORT_ICQ 4000
+
+enum { ICQ5_client, ICQ5_server};
+
+static void dissect_icqv5(tvbuff_t *tvb,
+ packet_info *pinfo,
+ proto_tree *tree);
+
+static void
+dissect_icqv5Server(tvbuff_t *tvb,
+ int offset,
+ packet_info *pinfo,
+ proto_tree *tree,
+ int pktsize);
+
+/* Offsets of fields in the ICQ headers */
+/* Can be 0x0002 or 0x0005 */
+#define ICQ_VERSION 0x00
+/* Is either one (server) or four (client) bytes long */
+/* Client header offsets */
+#define ICQ5_UNKNOWN 0x02
+#define ICQ5_CL_UIN 0x06
+#define ICQ5_CL_SESSIONID 0x0a
+#define ICQ5_CL_CMD 0x0e
+#define ICQ5_CL_SEQNUM1 0x10
+#define ICQ5_CL_SEQNUM2 0x12
+#define ICQ5_CL_CHECKCODE 0x14
+#define ICQ5_CL_PARAM 0x18
+#define ICQ5_CL_HDRSIZE 0x18
+
+/* Server header offsets */
+#define ICQ5_SRV_SESSIONID 0x03
+#define ICQ5_SRV_CMD 0x07
+#define ICQ5_SRV_SEQNUM1 0x09
+#define ICQ5_SRV_SEQNUM2 0x0b
+#define ICQ5_SRV_UIN 0x0d
+#define ICQ5_SRV_CHECKCODE 0x11
+#define ICQ5_SRV_PARAM 0x15
+#define ICQ5_SRV_HDRSIZE 0x15
+
+#define SRV_ACK 0x000a
+
+#define SRV_SILENT_TOO_LONG 0x001e
+
+#define SRV_GO_AWAY 0x0028
+
+#define SRV_NEW_UIN 0x0046
+
+/* LOGIN_REPLY is very scary. It has a lot of fields that are undocumented
+ * Only the IP field makes sense */
+#define SRV_LOGIN_REPLY 0x005a
+#define SRV_LOGIN_REPLY_IP 0x000c
+
+#define SRV_BAD_PASS 0x0064
+
+#define SRV_USER_ONLINE 0x006e
+#define SRV_USER_ONL_UIN 0x0000
+#define SRV_USER_ONL_IP 0x0004
+#define SRV_USER_ONL_PORT 0x0008
+#define SRV_USER_ONL_REALIP 0x000c
+#define SRV_USER_ONL_X1 0x0010
+#define SRV_USER_ONL_STATUS 0x0013
+#define SRV_USER_ONL_X2 0x0015
+
+#define SRV_USER_OFFLINE 0x0078
+#define SRV_USER_OFFLINE_UIN 0x0000
+
+#define SRV_MULTI 0x0212
+#define SRV_MULTI_NUM 0x0000
+
+#define SRV_META_USER 0x03de
+#define SRV_META_USER_SUBCMD 0x0000
+#define SRV_META_USER_RESULT 0x0002
+#define SRV_META_USER_DATA 0x0003
+
+#define SRV_UPDATE_SUCCESS 0x01e0
+
+#define SRV_UPDATE_FAIL 0x01ea
+
+/*
+ * ICQv5 SRV_META_USER subcommands
+ */
+#define META_EX_USER_FOUND 0x0190
+#define META_USER_FOUND 0x019a
+#define META_ABOUT 0x00e6
+#define META_USER_INFO 0x00c8
+
+#define SRV_RECV_MESSAGE 0x00dc
+#define SRV_RECV_MSG_UIN 0x0000
+#define SRV_RECV_MSG_YEAR 0x0004
+#define SRV_RECV_MSG_MONTH 0x0006
+#define SRV_RECV_MSG_DAY 0x0007
+#define SRV_RECV_MSG_HOUR 0x0008
+#define SRV_RECV_MSG_MINUTE 0x0009
+#define SRV_RECV_MSG_MSG_TYPE 0x000a
+
+#define SRV_RAND_USER 0x024e
+#define SRV_RAND_USER_UIN 0x0000
+#define SRV_RAND_USER_IP 0x0004
+#define SRV_RAND_USER_PORT 0x0008
+#define SRV_RAND_USER_REAL_IP 0x000c
+#define SRV_RAND_USER_CLASS 0x0010
+#define SRV_RAND_USER_X1 0x0011
+#define SRV_RAND_USER_STATUS 0x0015
+#define SRV_RAND_USER_TCP_VER 0x0019
+
+/* This message has the same structure as cmd_send_msg */
+#define SRV_SYS_DELIVERED_MESS 0x0104
+
+static const value_string serverMetaSubCmdCode[] = {
+ { META_USER_FOUND, "META_USER_FOUND" },
+ { META_EX_USER_FOUND, "META_EX_USER_FOUND" },
+ { META_ABOUT, "META_ABOUT" },
+ { META_USER_INFO, "META_USER_INFO" },
+ { 0, NULL }
+};
+
+static const value_string serverCmdCode[] = {
+ { SRV_ACK, "SRV_ACK" },
+ { SRV_SILENT_TOO_LONG, "SRV_SILENT_TOO_LONG" },
+ { SRV_GO_AWAY, "SRV_GO_AWAY" },
+ { SRV_NEW_UIN, "SRV_NEW_UIN" },
+ { SRV_LOGIN_REPLY, "SRV_LOGIN_REPLY" },
+ { SRV_BAD_PASS, "SRV_BAD_PASS" },
+ { SRV_USER_ONLINE, "SRV_USER_ONLINE" },
+ { SRV_USER_OFFLINE, "SRV_USER_OFFLINE" },
+ { 130, "SRV_QUERY" },
+ { 140, "SRV_USER_FOUND" },
+ { 160, "SRV_END_OF_SEARCH" },
+ { 180, "SRV_NEW_USER" },
+ { 200, "SRV_UPDATE_EXT" },
+ { SRV_RECV_MESSAGE, "SRV_RECV_MESSAGE" },
+ { 230, "SRV_END_OFFLINE_MESSAGES" },
+ { 240, "SRV_NOT_CONNECTED" },
+ { 250, "SRV_TRY_AGAIN" },
+ { SRV_SYS_DELIVERED_MESS, "SRV_SYS_DELIVERED_MESS" },
+ { 280, "SRV_INFO_REPLY" },
+ { 290, "SRV_EXT_INFO_REPLY" },
+ { 420, "SRV_STATUS_UPDATE" },
+ { 450, "SRV_SYSTEM_MESSAGE" },
+ { SRV_UPDATE_SUCCESS, "SRV_UPDATE_SUCCESS" },
+ { SRV_UPDATE_FAIL, "SRV_UPDATE_FAIL" },
+ { 500, "SRV_AUTH_UPDATE" },
+ { SRV_MULTI, "SRV_MULTI_PACKET" },
+ { 540, "SRV_END_CONTACTLIST_STATUS" },
+ { SRV_RAND_USER, "SRV_RAND_USER" },
+ { SRV_META_USER, "SRV_META_USER" },
+ { 0, NULL }
+};
+
+#define MSG_TEXT 0x0001
+#define MSG_URL 0x0004
+#define MSG_AUTH_REQ 0x0006
+#define MSG_AUTH 0x0008
+#define MSG_USER_ADDED 0x000c
+#define MSG_EMAIL 0x000e
+#define MSG_CONTACTS 0x0013
+
+#define STATUS_ONLINE 0x00000000
+#define STATUS_AWAY 0x00000001
+#define STATUS_DND 0x00000013
+#define STATUS_INVISIBLE 0x00000100
+#define STATUS_OCCUPIED 0x00000010
+#define STATUS_NA 0x00000004
+#define STATUS_CHAT 0x00000020
+
+/* Offsets for all packets measured from the start of the payload; i.e.
+ * with the ICQ header removed
+ */
+#define CMD_ACK 0x000a
+#define CMD_ACK_RANDOM 0x0000
+
+#define CMD_SEND_MSG 0x010E
+#define CMD_SEND_MSG_RECV_UIN 0x0000
+#define CMD_SEND_MSG_MSG_TYPE 0x0004
+#define CMD_SEND_MSG_MSG_LEN 0x0006
+#define CMD_SEND_MSG_MSG_TEXT 0x0008
+/* The rest of the packet should be a null-term string */
+
+#define CMD_LOGIN 0x03E8
+#define CMD_LOGIN_TIME 0x0000
+#define CMD_LOGIN_PORT 0x0004
+#define CMD_LOGIN_PASSLEN 0x0008
+#define CMD_LOGIN_PASSWD 0x000A
+/* The password is variable length; so when we've decoded the passwd,
+ * the structure starts counting at 0 again.
+ */
+#define CMD_LOGIN_IP 0x0004
+#define CMD_LOGIN_STATUS 0x0009
+
+#define CMD_CONTACT_LIST 0x0406
+#define CMD_CONTACT_LIST_NUM 0x0000
+
+#define CMD_USER_META 0x064a
+
+#define CMD_REG_NEW_USER 0x03fc
+
+#define CMD_ACK_MESSAGES 0x0442
+#define CMD_ACK_MESSAGES_RANDOM 0x0000
+
+#define CMD_KEEP_ALIVE 0x042e
+#define CMD_KEEP_ALIVE_RANDOM 0x0000
+
+#define CMD_SEND_TEXT_CODE 0x0438
+#define CMD_SEND_TEXT_CODE_LEN 0x0000
+#define CMD_SEND_TEXT_CODE_TEXT 0x0002
+
+#define CMD_MSG_TO_NEW_USER 0x0456
+
+#define CMD_QUERY_SERVERS 0x04ba
+
+#define CMD_QUERY_ADDONS 0x04c4
+
+#define CMD_STATUS_CHANGE 0x04d8
+#define CMD_STATUS_CHANGE_STATUS 0x0000
+
+#define CMD_ADD_TO_LIST 0x053c
+#define CMD_ADD_TO_LIST_UIN 0x0000
+
+#define CMD_RAND_SEARCH 0x056e
+#define CMD_RAND_SEARCH_GROUP 0x0000
+
+#define CMD_META_USER 0x064a
+
+static const value_string msgTypeCode[] = {
+ { MSG_TEXT, "MSG_TEXT" },
+ { MSG_URL, "MSG_URL" },
+ { MSG_AUTH_REQ, "MSG_AUTH_REQ" },
+ { MSG_AUTH, "MSG_AUTH" },
+ { MSG_USER_ADDED, "MSG_USER_ADDED" },
+ { MSG_EMAIL, "MSG_EMAIL" },
+ { MSG_CONTACTS, "MSG_CONTACTS" },
+ { 0, NULL }
+};
+
+static const value_string statusCode[] = {
+ { STATUS_ONLINE, "ONLINE" },
+ { STATUS_AWAY, "AWAY" },
+ { STATUS_DND, "DND" },
+ { STATUS_INVISIBLE, "INVISIBLE" },
+ { STATUS_OCCUPIED, "OCCUPIED" },
+ { STATUS_NA, "NA" },
+ { STATUS_CHAT, "Free for Chat" },
+ { 0, NULL }
+};
+
+static const value_string clientCmdCode[] = {
+ { CMD_ACK, "CMD_ACK" },
+ { CMD_SEND_MSG, "CMD_SEND_MESSAGE" },
+ { CMD_LOGIN, "CMD_LOGIN" },
+ { CMD_REG_NEW_USER, "CMD_REG_NEW_USER" },
+ { 1030, "CMD_CONTACT_LIST" },
+ { 1050, "CMD_SEARCH_UIN" },
+ { 1060, "CMD_SEARCH_USER" },
+ { 1070, "CMD_KEEP_ALIVE" },
+ { CMD_SEND_TEXT_CODE, "CMD_SEND_TEXT_CODE" },
+ { CMD_ACK_MESSAGES, "CMD_ACK_MESSAGES" },
+ { 1100, "CMD_LOGIN_1" },
+ { CMD_MSG_TO_NEW_USER, "CMD_MSG_TO_NEW_USER" },
+ { 1120, "CMD_INFO_REQ" },
+ { 1130, "CMD_EXT_INFO_REQ" },
+ { 1180, "CMD_CHANGE_PW" },
+ { 1190, "CMD_NEW_USER_INFO" },
+ { 1200, "CMD_UPDATE_EXT_INFO" },
+ { CMD_QUERY_SERVERS, "CMD_QUERY_SERVERS" },
+ { CMD_QUERY_ADDONS, "CMD_QUERY_ADDONS" },
+ { CMD_STATUS_CHANGE, "CMD_STATUS_CHANGE" },
+ { 1260, "CMD_NEW_USER_1" },
+ { 1290, "CMD_UPDATE_INFO" },
+ { 1300, "CMD_AUTH_UPDATE" },
+ { 1310, "CMD_KEEP_ALIVE2" },
+ { 1320, "CMD_LOGIN_2" },
+ { CMD_ADD_TO_LIST, "CMD_ADD_TO_LIST" },
+ { 1380, "CMD_RAND_SET" },
+ { CMD_RAND_SEARCH, "CMD_RAND_SEARCH" },
+ { CMD_META_USER, "CMD_META_USER" },
+ { 1700, "CMD_INVIS_LIST" },
+ { 1710, "CMD_VIS_LIST" },
+ { 1720, "CMD_UPDATE_LIST" },
+ { 0, NULL }
+};
+
+/*
+ * All ICQv5 decryption code thanx to Sebastien Dault (daus01@gel.usherb.ca)
+ */
+static const guchar
+table_v5 [] = {
+ 0x59, 0x60, 0x37, 0x6B, 0x65, 0x62, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x60, 0x57, 0x5B, 0x3D,
+ 0x5E, 0x34, 0x6D, 0x36, 0x50, 0x3F, 0x6F, 0x67, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x47, 0x63, 0x39,
+ 0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x43, 0x69, 0x48, 0x33, 0x31, 0x64, 0x35, 0x5A, 0x4A, 0x42,
+ 0x56, 0x40, 0x67, 0x53, 0x41, 0x07, 0x6C, 0x49, 0x58, 0x3B, 0x4D, 0x46, 0x68, 0x43, 0x69, 0x48,
+ 0x33, 0x31, 0x44, 0x65, 0x62, 0x46, 0x48, 0x53, 0x41, 0x07, 0x6C, 0x69, 0x48, 0x33, 0x51, 0x54,
+ 0x5D, 0x4E, 0x6C, 0x49, 0x38, 0x4B, 0x55, 0x4A, 0x62, 0x46, 0x48, 0x33, 0x51, 0x34, 0x6D, 0x36,
+ 0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x63, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64, 0x35, 0x5A,
+ 0x6A, 0x52, 0x6E, 0x3C, 0x51, 0x34, 0x6D, 0x36, 0x50, 0x5F, 0x5F, 0x3F, 0x4F, 0x37, 0x4B, 0x35,
+ 0x5A, 0x4A, 0x62, 0x66, 0x58, 0x3B, 0x4D, 0x66, 0x58, 0x5B, 0x5D, 0x4E, 0x6C, 0x49, 0x58, 0x3B,
+ 0x4D, 0x66, 0x58, 0x3B, 0x4D, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64,
+ 0x55, 0x6A, 0x32, 0x3E, 0x44, 0x45, 0x52, 0x6E, 0x3C, 0x31, 0x64, 0x55, 0x6A, 0x52, 0x4E, 0x6C,
+ 0x69, 0x48, 0x53, 0x61, 0x4C, 0x39, 0x30, 0x6F, 0x47, 0x63, 0x59, 0x60, 0x57, 0x5B, 0x3D, 0x3E,
+ 0x64, 0x35, 0x3A, 0x3A, 0x5A, 0x6A, 0x52, 0x4E, 0x6C, 0x69, 0x48, 0x53, 0x61, 0x6C, 0x49, 0x58,
+ 0x3B, 0x4D, 0x46, 0x68, 0x63, 0x39, 0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x67, 0x53, 0x41, 0x25, 0x41,
+ 0x3C, 0x51, 0x54, 0x3D, 0x5E, 0x54, 0x5D, 0x4E, 0x4C, 0x39, 0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F,
+ 0x47, 0x43, 0x69, 0x48, 0x33, 0x51, 0x54, 0x5D, 0x6E, 0x3C, 0x31, 0x64, 0x35, 0x5A, 0x00, 0x00 };
+
+static char*
+findMsgType(int num)
+{
+ return val_to_str(num, msgTypeCode, "Unknown");
+}
+
+static char*
+findSubCmd(int num)
+{
+ return val_to_str(num, serverMetaSubCmdCode, "Unknown (0x%04x)");
+}
+
+static char*
+findClientCmd(int num)
+{
+ return val_to_str(num, clientCmdCode, "Unknown (%u)");
+}
+
+static char*
+findServerCmd(int num)
+{
+ return val_to_str(num, serverCmdCode, "Unknown (%u)");
+}
+
+static char*
+findStatus(int num)
+{
+ return val_to_str(num, statusCode, "Unknown (0x%08x)");
+}
+
+static guint32
+get_v5key(tvbuff_t *tvb, int len)
+{
+ guint32 a1, a2, a3, a4, a5;
+ guint32 code, check, key;
+
+ code = tvb_get_letohl(tvb, ICQ5_CL_CHECKCODE);
+
+ a1 = code & 0x0001f000;
+ a2 = code & 0x07c007c0;
+ a3 = code & 0x003e0001;
+ a4 = code & 0xf8000000;
+ a5 = code & 0x0000083e;
+
+ a1 = a1 >> 0x0c;
+ a2 = a2 >> 0x01;
+ a3 = a3 << 0x0a;
+ a4 = a4 >> 0x10;
+ a5 = a5 << 0x0f;
+
+ check = a5 + a1 + a2 + a3 + a4;
+ key = len * 0x68656C6C;
+ key += check;
+ return key;
+}
+
+static void
+decrypt_v5(guchar *bfr, guint32 size,guint32 key)
+{
+ guint32 i;
+ guint32 k;
+
+ for (i=ICQ5_CL_SESSIONID; i < size; i+=4 ) {
+ k = key+table_v5[i&0xff];
+ if ( i != 0x16 ) {
+ bfr[i] ^= (guchar)(k & 0xff);
+ bfr[i+1] ^= (guchar)((k & 0xff00)>>8);
+ }
+ if ( i != 0x12 ) {
+ bfr[i+2] ^= (guchar)((k & 0xff0000)>>16);
+ bfr[i+3] ^= (guchar)((k & 0xff000000)>>24);
+ }
+ }
+}
+
+static void
+dissect_icqv4(tvbuff_t *tvb,
+ packet_info *pinfo,
+ proto_tree *tree)
+{
+ proto_tree *icq_tree = NULL;
+ proto_item *ti = NULL;
+
+ /* Not really implemented yet */
+ if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICQv4 (UDP)");
+ }
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_set_str(pinfo->cinfo, COL_INFO, "ICQ Version 4 protocol");
+ }
+ if (tree) {
+ ti = proto_tree_add_protocol_format(tree,
+ proto_icq,
+ tvb,
+ 0,
+ -1,
+ "ICQv4");
+ icq_tree = proto_item_add_subtree(ti, ett_icq);
+
+ proto_tree_add_text(icq_tree, tvb,
+ ICQ_VERSION,
+ 2,
+ "Version: %u",
+ tvb_get_letohs(tvb, ICQ_VERSION));
+ }
+}
+
+static void
+dissect_icqv3(tvbuff_t *tvb,
+ packet_info *pinfo,
+ proto_tree *tree)
+{
+ proto_tree *icq_tree = NULL;
+ proto_item *ti = NULL;
+
+ /* Not really implemented yet */
+ if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICQv3 (UDP)");
+ }
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_set_str(pinfo->cinfo, COL_INFO, "ICQ Version 3 protocol");
+ }
+ if (tree) {
+ ti = proto_tree_add_protocol_format(tree,
+ proto_icq,
+ tvb,
+ 0,
+ -1,
+ "ICQv3");
+ icq_tree = proto_item_add_subtree(ti, ett_icq);
+
+ proto_tree_add_text(icq_tree, tvb,
+ ICQ_VERSION,
+ 2,
+ "Version: %u",
+ tvb_get_letohs(tvb, ICQ_VERSION));
+ }
+}
+
+static void
+dissect_icqv2(tvbuff_t *tvb,
+ packet_info *pinfo,
+ proto_tree *tree)
+{
+ proto_tree *icq_tree = NULL;
+ proto_item *ti = NULL;
+
+ /* Not really implemented yet */
+ if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICQv2 (UDP)");
+ }
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_set_str(pinfo->cinfo, COL_INFO, "ICQ Version 2 protocol");
+ }
+ if (tree) {
+ ti = proto_tree_add_protocol_format(tree,
+ proto_icq,
+ tvb,
+ 0,
+ -1,
+ "ICQv2");
+ icq_tree = proto_item_add_subtree(ti, ett_icq);
+
+ proto_tree_add_text(icq_tree, tvb,
+ ICQ_VERSION,
+ 2,
+ "Version: %u",
+ tvb_get_letohs(tvb, ICQ_VERSION));
+ }
+}
+
+/*
+ * The packet has, at offset "offset" a (len, string) pair.
+ * Display the length and string in the tree.
+ *
+ * If anything is wrong, return -1, since -1 is not a valid string
+ * length. Else, return the number of chars processed.
+ */
+static guint16
+proto_add_icq_attr(proto_tree* tree, /* The tree to add to */
+ tvbuff_t *tvb, /* Tvbuff with packet */
+ const int offset, /* Offset from the start of packet of field */
+ char* descr) /* The description to use in the tree */
+{
+ guint16 len;
+
+ len = tvb_get_letohs(tvb, offset);
+ if (len > tvb_reported_length_remaining(tvb, offset))
+ return -1; /* length goes past end of packet */
+ proto_tree_add_text(tree, tvb,
+ offset,
+ sizeof(guint16) + len,
+ "%s[%u]: %.*s", descr, len, len,
+ tvb_get_ptr(tvb, offset + sizeof(guint16), len));
+ return len + sizeof(guint16);
+}
+
+static void
+icqv5_decode_msgType(proto_tree* tree,
+ tvbuff_t *tvb,
+ int offset,
+ int size)
+{
+ proto_item* ti = NULL;
+ proto_tree* subtree = NULL;
+ int left = size;
+ guint16 msgType;
+ gint sep_offset;
+ int sz; /* Size of the current element */
+ unsigned int n;
+ static char* url_field_descr[] = {
+ "Description",
+ "URL",
+ };
+#define N_URL_FIELDS (sizeof url_field_descr / sizeof url_field_descr[0])
+ static char* email_field_descr[] = {
+ "Nickname",
+ "First name",
+ "Last name",
+ "Email address",
+ "Unknown",
+ "Text"
+ };
+#define N_EMAIL_FIELDS (sizeof email_field_descr / sizeof email_field_descr[0])
+ static char* auth_req_field_descr[] = {
+ "Nickname",
+ "First name",
+ "Last name",
+ "Email address",
+ "Unknown",
+ "Reason"
+ };
+#define N_AUTH_REQ_FIELDS (sizeof auth_req_field_descr / sizeof auth_req_field_descr[0])
+ static char* user_added_field_descr[] = {
+ "Nickname",
+ "First name",
+ "Last name",
+ "Email address",
+ };
+#define N_USER_ADDED_FIELDS (sizeof user_added_field_descr / sizeof user_added_field_descr[0])
+
+ msgType = tvb_get_letohs(tvb, offset);
+ ti = proto_tree_add_text(tree, tvb,
+ offset,
+ size,
+ "Message: Type = %u (%s)", msgType, findMsgType(msgType));
+ /* Create a new subtree */
+ subtree = proto_item_add_subtree(ti, ett_icq_body_parts);
+
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ 2,
+ "Type: %u (%s)", msgType, findMsgType(msgType));
+ offset += 2;
+ left -= 2;
+ if (msgType != MSG_AUTH) {
+ /*
+ * XXX - does a MSG_AUTH message really have 3 bytes of information
+ * rather than a length field?
+ */
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ 2,
+ "Length: %u",
+ tvb_get_letohs(tvb, offset));
+ offset += 2;
+ left -= 2;
+ }
+
+ switch(msgType) {
+ case 0xffff: /* Field unknown */
+ break;
+ default:
+ fprintf(stderr, "Unknown msgType: %u (%04x)\n", msgType, msgType);
+ break;
+ case MSG_TEXT:
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ left,
+ "Msg: %.*s", left-1,
+ tvb_get_ptr(tvb, offset, left));
+ break;
+ case MSG_URL:
+ for (n = 0; n < N_URL_FIELDS; n++) {
+ if (n != N_URL_FIELDS - 1) {
+ sep_offset = tvb_find_guint8(tvb, offset, left, 0xfe);
+ sz = sep_offset - offset + 1;
+ } else
+ sz = left;
+ if (sz != 0) {
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ sz,
+ "%s: %.*s",
+ url_field_descr[n],
+ sz - 1,
+ tvb_get_ptr(tvb, offset, sz));
+ } else {
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ 0,
+ "%s: %s", url_field_descr[n], "(empty)");
+ }
+ offset += sz;
+ left -= sz;
+ }
+ break;
+ case MSG_EMAIL:
+ for (n = 0; n < N_EMAIL_FIELDS; n++) {
+ if (n != N_EMAIL_FIELDS - 1) {
+ sep_offset = tvb_find_guint8(tvb, offset, left, 0xfe);
+ sz = sep_offset - offset + 1;
+ } else
+ sz = left;
+ if (sz != 0) {
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ sz,
+ "%s: %.*s",
+ email_field_descr[n],
+ sz - 1,
+ tvb_get_ptr(tvb, offset, sz));
+ } else {
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ 0,
+ "%s: %s", email_field_descr[n], "(empty)");
+ }
+ offset += sz;
+ left -= sz;
+ }
+ break;
+
+ case MSG_AUTH:
+ {
+ /* Three bytes, first is a char signifying success */
+ unsigned char auth_suc;
+
+ auth_suc = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ 1,
+ "Authorization: (%u) %s",auth_suc,
+ (auth_suc==0)?"Denied":"Allowed");
+ offset++;
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ sizeof(guint16),
+ "x1: 0x%04x",
+ tvb_get_letohs(tvb, offset));
+ break;
+ }
+ case MSG_AUTH_REQ:
+ for (n = 0; n < N_AUTH_REQ_FIELDS; n++) {
+ if (n != N_AUTH_REQ_FIELDS - 1) {
+ sep_offset = tvb_find_guint8(tvb, offset, left, 0xfe);
+ sz = sep_offset - offset + 1;
+ } else
+ sz = left;
+ if (sz != 0) {
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ sz,
+ "%s: %.*s",
+ auth_req_field_descr[n],
+ sz - 1,
+ tvb_get_ptr(tvb, offset, sz));
+ } else {
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ 0,
+ "%s: %s", auth_req_field_descr[n], "(empty)");
+ }
+ offset += sz;
+ left -= sz;
+ }
+ break;
+ case MSG_USER_ADDED:
+ for (n = 0; n < N_USER_ADDED_FIELDS; n++) {
+ if (n != N_USER_ADDED_FIELDS - 1) {
+ sep_offset = tvb_find_guint8(tvb, offset, left, 0xfe);
+ sz = sep_offset - offset + 1;
+ } else
+ sz = left;
+ if (sz != 0) {
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ sz,
+ "%s: %.*s",
+ user_added_field_descr[n],
+ sz - 1,
+ tvb_get_ptr(tvb, offset, sz));
+ } else {
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ 0,
+ "%s: %s", user_added_field_descr[n], "(empty)");
+ }
+ offset += sz;
+ left -= sz;
+ }
+ break;
+ case MSG_CONTACTS:
+ {
+ gint sep_offset_prev;
+ int sz = 0; /* Size of the current element */
+ int n = 0; /* The nth element */
+ gboolean last = FALSE;
+
+ while (!last) {
+ sep_offset = tvb_find_guint8(tvb, offset, left, 0xfe);
+ if (sep_offset != -1)
+ sz = sep_offset - offset + 1;
+ else {
+ sz = left;
+ last = TRUE;
+ }
+ if (n == 0) {
+ /* The first element is the number of Nick/UIN pairs follow */
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ sz,
+ "Number of pairs: %.*s",
+ sz - 1,
+ tvb_get_ptr(tvb, offset, sz));
+ n++;
+ } else if (!last) {
+ int svsz = sz;
+
+ left -= sz;
+ sep_offset_prev = sep_offset;
+ sep_offset = tvb_find_guint8(tvb, sep_offset_prev, left,
+ 0xfe);
+ if (sep_offset != -1)
+ sz = sep_offset - offset + 1;
+ else {
+ sz = left;
+ last = TRUE;
+ }
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ sz + svsz,
+ "%.*s: %.*s",
+ svsz - 1,
+ tvb_get_ptr(tvb, offset, svsz),
+ sz - 1,
+ tvb_get_ptr(tvb, sep_offset_prev + 1, sz));
+ n += 2;
+ }
+
+ left -= (sz+1);
+ offset = sep_offset + 1;
+ }
+ break;
+ }
+ }
+}
+
+/*********************************
+ *
+ * Client commands
+ *
+ *********************************/
+static void
+icqv5_cmd_ack(proto_tree* tree,/* Tree to put the data in */
+ tvbuff_t *tvb, /* Tvbuff with decrypted packet data */
+ int offset) /* Offset from the start of the packet to the content */
+{
+ proto_tree* subtree;
+ proto_item* ti;
+
+ if (tree){
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ 4,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_ACK_RANDOM,
+ 4,
+ "Random: 0x%08x",
+ tvb_get_letohl(tvb, offset + CMD_ACK_RANDOM));
+ }
+}
+
+static void
+icqv5_cmd_rand_search(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t *tvb, /* Decrypted packet content */
+ int offset, /* Offset from the start of the packet to the content */
+ int size) /* Number of chars left to do */
+{
+ guint16 group;
+ proto_tree* subtree;
+ proto_item* ti;
+
+ static const char* groups[] = {
+ "Name",
+ "General",
+ "Romance",
+ "Games",
+ "Students",
+ "20 Something",
+ "30 Something",
+ "40 Something",
+ "50 or worse",
+ "Man want women",
+ "Women want men"
+ };
+
+ if (tree){
+ if (size < 4) {
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ size,
+ "Body (%d bytes, should be 4)", size);
+ return;
+ }
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ 4,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ group = tvb_get_letohs(tvb, offset + CMD_RAND_SEARCH_GROUP);
+ if (group>0 && (group<=sizeof(groups)/sizeof(const char*)))
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_RAND_SEARCH_GROUP,
+ 4,
+ "Group: (%u) %s", group, groups[group-1]);
+ else
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_RAND_SEARCH_GROUP,
+ 4,
+ "Group: (%u)", group);
+ }
+}
+
+static void
+icqv5_cmd_ack_messages(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t *tvb, /* Decrypted packet content */
+ int offset) /* Offset from the start of the packet to the content */
+{
+ proto_tree* subtree;
+ proto_item* ti;
+
+ if (tree){
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ 4,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_ACK_MESSAGES_RANDOM,
+ 4,
+ "Random: 0x%08x",
+ tvb_get_letohl(tvb, offset + CMD_ACK_MESSAGES_RANDOM));
+ }
+}
+
+static void
+icqv5_cmd_keep_alive(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t *tvb, /* Decrypted packet content */
+ int offset) /* Offset from the start of the packet to the content */
+{
+ guint32 random;
+ proto_tree* subtree;
+ proto_item* ti;
+
+ if (tree){
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ 4,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ random = tvb_get_letohl(tvb, offset + CMD_KEEP_ALIVE_RANDOM);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_KEEP_ALIVE_RANDOM,
+ 4,
+ "Random: 0x%08x", random);
+ }
+}
+
+static void
+icqv5_cmd_send_text_code(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t *tvb, /* Decrypted packet content */
+ int offset, /* Offset from the start of the packet to the content */
+ int size) /* Number of chars left to do */
+{
+ proto_tree* subtree = NULL;
+ proto_item* ti = NULL;
+ guint16 len;
+ guint16 x1;
+
+ if (tree){
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ size,
+ "Body");
+ }
+
+ len = tvb_get_letohs(tvb, offset+CMD_SEND_TEXT_CODE_LEN);
+ if (tree){
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_SEND_TEXT_CODE_LEN,
+ 2,
+ "Length: %d", len);
+ }
+
+ if (len>0) {
+ if (tree){
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_SEND_TEXT_CODE_TEXT,
+ len,
+ "Text: %.*s",
+ len,
+ tvb_get_ptr(tvb, offset + CMD_SEND_TEXT_CODE_TEXT,
+ len));
+ }
+ }
+
+ x1 = tvb_get_letohs(tvb, offset + CMD_SEND_TEXT_CODE_TEXT + len);
+ if (tree){
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_SEND_TEXT_CODE_TEXT + len,
+ 2,
+ "X1: 0x%04x", x1);
+ }
+}
+
+static void
+icqv5_cmd_add_to_list(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t *tvb, /* Decrypted packet content */
+ int offset) /* Offset from the start of the packet to the content */
+{
+ guint32 uin;
+ proto_tree* subtree;
+ proto_item* ti;
+
+ if (tree){
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ 4,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ uin = tvb_get_letohl(tvb, offset + CMD_ADD_TO_LIST);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_ADD_TO_LIST_UIN,
+ 4,
+ "UIN: %u", uin);
+ }
+}
+
+static void
+icqv5_cmd_status_change(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t *tvb, /* Decrypted packet content */
+ int offset) /* Offset from the start of the packet to the content */
+{
+ guint32 status;
+ proto_tree* subtree;
+ proto_item* ti;
+
+ if (tree){
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ 4,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ status = tvb_get_letohl(tvb, offset + CMD_STATUS_CHANGE_STATUS);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_STATUS_CHANGE_STATUS,
+ 4,
+ "Status: %s", findStatus(status));
+ }
+}
+
+static void
+icqv5_cmd_send_msg(proto_tree* tree,
+ tvbuff_t *tvb,
+ int offset,
+ int size)
+{
+ proto_tree* subtree;
+ proto_item* ti;
+ int left = size; /* left chars to do */
+
+ if (tree) {
+ if (size < 4) {
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ size,
+ "Body (%d bytes, should be >= 4)", size);
+ return;
+ }
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ size,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_SEND_MSG_RECV_UIN,
+ 4,
+ "Receiver UIN: %u",
+ tvb_get_letohl(tvb, offset + CMD_SEND_MSG_RECV_UIN));
+ left -= 4;
+
+ icqv5_decode_msgType(subtree,
+ tvb,
+ offset + CMD_SEND_MSG_MSG_TYPE,
+ left);
+ }
+}
+
+static void
+icqv5_cmd_login(proto_tree* tree,
+ tvbuff_t *tvb,
+ int offset,
+ int size)
+{
+ proto_item* ti;
+ proto_tree* subtree;
+ time_t theTime;
+ char *aTime;
+ guint32 port;
+ guint32 passwdLen;
+ const guchar *ipAddrp;
+ guint32 status;
+
+ if (tree) {
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ size,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ theTime = tvb_get_letohl(tvb, offset + CMD_LOGIN_TIME);
+ aTime = ctime(&theTime);
+ aTime[strlen(aTime)-1] = '\0';
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_LOGIN_TIME,
+ 4,
+ "Time: %ld = %s", (long)theTime, aTime);
+ port = tvb_get_letohl(tvb, offset + CMD_LOGIN_PORT);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_LOGIN_PORT,
+ 4,
+ "Port: %u", port);
+ passwdLen = tvb_get_letohs(tvb, offset + CMD_LOGIN_PASSLEN);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_LOGIN_PASSLEN,
+ 2 + passwdLen,
+ "Passwd: %.*s",
+ (int)passwdLen,
+ tvb_get_ptr(tvb, offset + CMD_LOGIN_PASSWD,
+ passwdLen));
+ ipAddrp = tvb_get_ptr(tvb,
+ offset + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_IP,
+ 4);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_IP,
+ 4,
+ "IP: %s", ip_to_str(ipAddrp));
+ status = tvb_get_letohs(tvb,
+ offset + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_STATUS);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_STATUS,
+ 4,
+ "Status: %s", findStatus(status));
+ }
+}
+
+static void
+icqv5_cmd_contact_list(proto_tree* tree,
+ tvbuff_t *tvb,
+ int offset,
+ int size)
+{
+ proto_tree* subtree;
+ proto_item* ti;
+ unsigned char num;
+ int i;
+ guint32 uin;
+
+ if (tree) {
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ size,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ num = tvb_get_guint8(tvb, offset + CMD_CONTACT_LIST_NUM);
+ proto_tree_add_text(subtree, tvb,
+ offset + CMD_CONTACT_LIST,
+ 1,
+ "Number of uins: %u", num);
+ /*
+ * A sequence of num times UIN follows
+ */
+ offset += (CMD_CONTACT_LIST_NUM + 1);
+ for (i = 0; i < num; i++) {
+ uin = tvb_get_letohl(tvb, offset);
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ 4,
+ "UIN[%d]: %u", i ,uin);
+ offset += 4;
+ }
+ }
+}
+
+static void
+icqv5_cmd_no_params(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t *tvb, /* Decrypted packet content */
+ int offset) /* Offset from the start of the packet to the content */
+{
+ proto_tree* subtree;
+ proto_item* ti;
+
+ if (tree){
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ 0,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ 0,
+ "No parameters");
+ }
+}
+
+/**********************
+ *
+ * Server commands
+ *
+ **********************
+ */
+static void
+icqv5_srv_no_params(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t *tvb, /* Packet content */
+ int offset) /* Offset from the start of the packet to the content */
+{
+ proto_tree* subtree;
+ proto_item* ti;
+
+ if (tree){
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ 0,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ proto_tree_add_text(subtree, tvb,
+ offset,
+ 0,
+ "No Parameters");
+ }
+}
+
+static void
+icqv5_srv_login_reply(proto_tree* tree,/* Tree to put the data in */
+ tvbuff_t *tvb, /* Tvbuff with packet */
+ int offset, /* Offset from the start of the packet to the content */
+ int size) /* Number of chars left to do */
+{
+ proto_tree* subtree;
+ proto_item* ti;
+ const guchar *ipAddrp;
+
+ if (tree) {
+ if (size < SRV_LOGIN_REPLY_IP + 8) {
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ size,
+ "Body (%d bytes, should be %d)", size,
+ SRV_LOGIN_REPLY_IP + 8);
+ return;
+ }
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ SRV_LOGIN_REPLY_IP + 8,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ ipAddrp = tvb_get_ptr(tvb, offset + SRV_LOGIN_REPLY_IP, 4);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_LOGIN_REPLY_IP,
+ 4,
+ "IP: %s", ip_to_str(ipAddrp));
+ }
+}
+
+static void
+icqv5_srv_user_online(proto_tree* tree,/* Tree to put the data in */
+ tvbuff_t *tvb, /* Tvbuff with packet */
+ int offset, /* Offset from the start of the packet to the content */
+ int size) /* Number of chars left to do */
+{
+ proto_tree* subtree;
+ proto_item* ti;
+ const guchar *ipAddrp;
+ const guchar *realipAddrp;
+ guint32 status;
+
+ if (tree) {
+ if (size < SRV_LOGIN_REPLY_IP + 8) {
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ size,
+ "Body (%d bytes, should be %d)", size,
+ SRV_LOGIN_REPLY_IP + 8);
+ return;
+ }
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ SRV_LOGIN_REPLY_IP + 8,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_USER_ONL_UIN,
+ 4,
+ "UIN: %u",
+ tvb_get_letohl(tvb, offset + SRV_USER_ONL_UIN));
+ ipAddrp = tvb_get_ptr(tvb, offset + SRV_USER_ONL_IP, 4);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_USER_ONL_IP,
+ 4,
+ "IP: %s", ip_to_str(ipAddrp));
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_USER_ONL_PORT,
+ 4,
+ "Port: %u",
+ tvb_get_letohl(tvb, offset + SRV_USER_ONL_PORT));
+ realipAddrp = tvb_get_ptr(tvb, offset + SRV_USER_ONL_REALIP, 4);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_USER_ONL_REALIP,
+ 4,
+ "RealIP: %s", ip_to_str(realipAddrp));
+ status = tvb_get_letohs(tvb, offset + SRV_USER_ONL_STATUS);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_USER_ONL_STATUS,
+ 2,
+ "Status: %s", findStatus(status));
+ /*
+ * Kojak: Hypothesis is that this field might be an encoding for the
+ * version used by the UIN that changed. To test this, I included
+ * this line to the code.
+ */
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_USER_ONL_X2,
+ 4,
+ "Version: %08x",
+ tvb_get_letohl(tvb, offset + SRV_USER_ONL_X2));
+ }
+}
+
+static void
+icqv5_srv_user_offline(proto_tree* tree,/* Tree to put the data in */
+ tvbuff_t *tvb, /* Tvbuff with packet */
+ int offset, /* Offset from the start of the packet to the content */
+ int size) /* Number of chars left to do */
+{
+ proto_tree* subtree;
+ proto_item* ti;
+
+ if (tree) {
+ if (size < SRV_USER_OFFLINE_UIN + 4) {
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ size,
+ "Body (%d bytes, should be %d)", size,
+ SRV_USER_OFFLINE_UIN + 4);
+ return;
+ }
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ SRV_USER_OFFLINE_UIN + 4,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_USER_OFFLINE_UIN,
+ 4,
+ "UIN: %u",
+ tvb_get_letohl(tvb, offset + SRV_USER_OFFLINE));
+ }
+}
+
+static void
+icqv5_srv_multi(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t *tvb, /* Packet content */
+ int offset, /* Offset from the start of the packet to the content */
+ int size, /* Number of chars left to do */
+ packet_info* pinfo)
+{
+ proto_tree* subtree;
+ proto_item* ti;
+ guint8 num;
+ guint16 pktSz;
+ int i;
+
+ if (tree) {
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ size,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ num = tvb_get_guint8(tvb, offset + SRV_MULTI_NUM);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_MULTI_NUM,
+ 1,
+ "Number of pkts: %u", num);
+ /*
+ * A sequence of num times ( pktsize, packetData) follows
+ */
+ offset += (SRV_MULTI_NUM + 1);
+ for (i = 0; i < num; i++) {
+ pktSz = tvb_get_letohs(tvb, offset);
+ offset += 2;
+ dissect_icqv5Server(tvb, offset, pinfo, subtree, pktSz);
+ offset += pktSz;
+ }
+ }
+}
+
+static void
+icqv5_srv_meta_user(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t *tvb, /* Tvbuff with packet */
+ int offset, /* Offset from the start of the packet to the content */
+ int size) /* Number of chars left to do */
+{
+#if 0
+ proto_tree* subtree = NULL;
+#endif
+ proto_tree* sstree = NULL;
+ proto_item* ti = NULL;
+ int left = size;
+ guint16 subcmd;
+ unsigned char result;
+
+ if (tree) {
+#if 0
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ size,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ subcmd = tvb_get_letohs(tvb, offset + SRV_META_USER_SUBCMD);
+ ti = proto_tree_add_text(subtree, tvb,
+ offset + SRV_META_USER_SUBCMD,
+ 2,
+ "%s", findSubCmd(subcmd));
+ result = tvb_get_guint8(tvb, offset + SRV_META_USER_RESULT);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_META_USER_RESULT,
+ 1,
+ "%s", (result==0x0a)?"Success":"Failure");
+ sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
+#else
+ subcmd = tvb_get_letohs(tvb, offset + SRV_META_USER_SUBCMD);
+ ti = proto_tree_add_text(tree, tvb,
+ offset + SRV_META_USER_SUBCMD,
+ 2,
+ "%s", findSubCmd(subcmd));
+ sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
+ result = tvb_get_guint8(tvb, offset + SRV_META_USER_RESULT);
+ proto_tree_add_text(sstree, tvb,
+ offset + SRV_META_USER_RESULT,
+ 1,
+ "%s", (result==0x0a)?"Success":"Failure");
+#endif
+
+ /* Skip the META_USER header */
+ left -= 3;
+ offset += 3;
+
+ switch(subcmd) {
+ case META_EX_USER_FOUND:
+ {
+ /* This is almost the same as META_USER_FOUND,
+ * however, there's an extra length field
+ */
+ guint16 pktLen;
+
+ /* Read the length field */
+ pktLen = tvb_get_letohs(tvb, offset);
+ proto_tree_add_text(sstree, tvb,
+ offset,
+ sizeof(guint16),
+ "Length: %u", pktLen);
+
+ offset += sizeof(guint16); left -= sizeof(guint16);
+ }
+ case META_USER_FOUND:
+ {
+ /* The goto mentioned in this block should be local to this
+ * block if C'd allow it.
+ *
+ * They are used to "implement" a poorman's exception handling
+ */
+ int len = 0;
+ char *descr[] = {
+ "Nick",
+ "First name",
+ "Last name",
+ "Email",
+ NULL};
+ char** d = descr;
+ unsigned char auth;
+ /*
+ * Read UIN
+ */
+ proto_tree_add_text(sstree, tvb,
+ offset,
+ sizeof(guint32),
+ "UIN: %u",
+ tvb_get_letohl(tvb, offset));
+ offset+=sizeof(guint32);left-=sizeof(guint32);
+
+ for ( ; *d!=NULL; d++) {
+ len = proto_add_icq_attr(sstree,
+ tvb,
+ offset,
+ *d);
+ if (len == -1)
+ return;
+ offset += len; left -= len;
+ }
+ /* Get the authorize setting */
+ auth = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(sstree, tvb,
+ offset,
+ 1,
+ "authorization: %s", (auth==0x01)?"Neccessary":"Who needs it");
+ offset++; left--;
+ /* Get x2 */
+ proto_tree_add_text(sstree, tvb,
+ offset,
+ sizeof(guint16),
+ "x2: 0x%04x",
+ tvb_get_letohs(tvb, offset));
+ offset+=sizeof(guint16);left-=sizeof(guint16);
+ /* Get x3 */
+ proto_tree_add_text(sstree, tvb,
+ offset,
+ sizeof(guint32),
+ "x3: 0x%08x",
+ tvb_get_letohl(tvb, offset));
+ offset+=sizeof(guint32);left-=sizeof(guint32);
+ break;
+ }
+ case META_ABOUT:
+ {
+ int len;
+
+ /* Get the about information */
+ len = tvb_get_letohs(tvb, offset);
+ offset+=sizeof(guint16);left-=sizeof(guint16);
+ proto_tree_add_text(sstree, tvb,
+ offset - sizeof(guint16),
+ sizeof(guint16)+len,
+ "About(%d): %.*s", len,
+ len, tvb_get_ptr(tvb, offset, len));
+ offset+=len;left-=len;
+ break;
+ }
+ case META_USER_INFO:
+ {
+ /* The goto mentioned in this block should be local to this
+ * block if C'd allow it.
+ *
+ * They are used to "implement" a poorman's exception handling
+ */
+ static const char* descr[] = {
+ "Nick",
+ "First name",
+ "Last name",
+ "Primary email",
+ "Secundary email",
+ "Old email",
+ "City",
+ "State",
+ "Phone",
+ "Fax",
+ "Street",
+ "Cellphone",
+ "Zip",
+ NULL};
+ const char** d = descr;
+ guint16 country;
+ guint8 user_timezone;
+ guint8 auth;
+ int len = 0;
+#if 0
+ /* Get the uin */
+ uin = tvb_get_letohl(tvb, offset);
+ proto_tree_add_text(sstree, tvb,
+ offset,
+ sizeof(guint32),
+ "UIN: %u", uin);
+ offset+=sizeof(guint32);left-=sizeof(guint32);
+#endif
+
+ /*
+ * Get every field from the description
+ */
+ while ((*d)!=NULL) {
+ len = tvb_get_letohs(tvb, offset);
+ offset+=sizeof(guint16);left-=sizeof(guint16);
+ if (len>0) {
+ proto_tree_add_text(sstree, tvb,
+ offset - sizeof(guint16),
+ sizeof(guint16)+len,
+ "%s(%d): %.*s",*d, len,
+ len - 1,
+ tvb_get_ptr(tvb, offset, len - 1));
+ offset+=len;left-=len;
+ }
+ d++;
+ }
+ /* Get country code */
+ country = tvb_get_letohs(tvb, offset);
+ proto_tree_add_text(sstree, tvb,
+ offset,
+ sizeof(guint16),
+ "Countrycode: %u", country);
+ offset+=sizeof(guint16); left-=sizeof(guint16);
+ /* Get the timezone setting */
+ user_timezone = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(sstree, tvb,
+ offset,
+ sizeof(unsigned char),
+ "Timezone: %u", user_timezone);
+ offset++; left--;
+ /* Get the authorize setting */
+ auth = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(sstree, tvb,
+ offset,
+ sizeof(unsigned char),
+ "Authorization: (%u) %s",
+ auth, (auth==0)?"No":"Yes");
+ offset++; left--;
+ /* Get the webaware setting */
+ auth = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(sstree, tvb,
+ offset,
+ sizeof(unsigned char),
+ "Webaware: (%u) %s",
+ auth, (auth==0)?"No":"Yes");
+ offset++; left--;
+ /* Get the authorize setting */
+ auth = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(sstree, tvb,
+ offset,
+ sizeof(unsigned char),
+ "HideIP: (%u) %s",
+ auth, (auth==0)?"No":"Yes");
+ offset++; left--;
+ break;
+ }
+ default:
+ /* This information is already printed in the tree */
+ fprintf(stderr, "Meta subcmd: %04x\n", subcmd);
+ break;
+ }
+ }
+}
+
+static void
+icqv5_srv_recv_message(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t* tvb, /* Packet content */
+ int offset, /* Offset from the start of the packet to the content */
+ int size) /* Number of chars left to do */
+{
+ proto_tree* subtree = NULL;
+ proto_item* ti = NULL;
+ int left = size;
+ guint16 year;
+ guint8 month;
+ guint8 day;
+ guint8 hour;
+ guint8 minute;
+
+ if (tree) {
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ 4,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ proto_tree_add_item(subtree,
+ hf_icq_uin,
+ tvb,
+ offset + SRV_RECV_MSG_UIN,
+ sizeof(guint32),
+ TRUE);
+ year = tvb_get_letohs(tvb, offset + SRV_RECV_MSG_YEAR);
+ month = tvb_get_guint8(tvb, offset + SRV_RECV_MSG_MONTH);
+ day = tvb_get_guint8(tvb, offset + SRV_RECV_MSG_DAY);
+ hour = tvb_get_guint8(tvb, offset + SRV_RECV_MSG_HOUR);
+ minute = tvb_get_guint8(tvb, offset + SRV_RECV_MSG_MINUTE);
+
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_RECV_MSG_YEAR,
+ sizeof(guint16) + 4*sizeof(unsigned char),
+ "Time: %u-%u-%u %02u:%02u",
+ day, month, year, hour, minute);
+ icqv5_decode_msgType(subtree,
+ tvb,
+ offset + SRV_RECV_MSG_MSG_TYPE,
+ left);
+ }
+}
+
+static void
+icqv5_srv_rand_user(proto_tree* tree, /* Tree to put the data in */
+ tvbuff_t *tvb, /* Tvbuff with packet */
+ int offset) /* Offset from the start of the packet to the content */
+{
+ proto_tree* subtree = NULL;
+ proto_item* ti = NULL;
+ guint32 uin;
+ const unsigned char* IP = NULL;
+ guint32 port;
+ const unsigned char* realIP = NULL;
+ guint8 commClass;
+ guint32 status;
+ guint16 tcpVer;
+
+ if (tree) {
+ ti = proto_tree_add_text(tree,
+ tvb,
+ offset,
+ SRV_RAND_USER_TCP_VER + 2,
+ "Body");
+ subtree = proto_item_add_subtree(ti, ett_icq_body);
+ /* guint32 UIN */
+ uin = tvb_get_letohl(tvb, offset + SRV_RAND_USER_UIN);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_RAND_USER_UIN,
+ sizeof(guint32),
+ "UIN: %u", uin);
+ /* guint32 IP */
+ IP = tvb_get_ptr(tvb, offset + SRV_RAND_USER_IP, 4);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_RAND_USER_IP,
+ sizeof(guint32),
+ "IP: %s",
+ ip_to_str(IP));
+ /* guint16 portNum */
+ /* XXX - 16 bits, or 32 bits? */
+ port = tvb_get_letohs(tvb, offset + SRV_RAND_USER_PORT);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_RAND_USER_UIN,
+ sizeof(guint32),
+ "Port: %u", port);
+ /* guint32 realIP */
+ realIP = tvb_get_ptr(tvb, offset + SRV_RAND_USER_REAL_IP, 4);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_RAND_USER_REAL_IP,
+ sizeof(guint32),
+ "RealIP: %s", ip_to_str(realIP));
+ /* guint8 Communication Class */
+ commClass = tvb_get_guint8(tvb, offset + SRV_RAND_USER_CLASS);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_RAND_USER_CLASS,
+ sizeof(guint8),
+ "Class: %s", (commClass!=4)?"User to User":"Through Server");
+ /* guint32 status */
+ /* XXX - 16 bits, or 32 bits? */
+ status = tvb_get_letohs(tvb, offset + SRV_RAND_USER_STATUS);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_RAND_USER_STATUS,
+ sizeof(guint32),
+ "Status: %s", findStatus(status));
+ /* guint16 tcpVersion */
+ tcpVer = tvb_get_letohs(tvb, offset + SRV_RAND_USER_TCP_VER);
+ proto_tree_add_text(subtree, tvb,
+ offset + SRV_RAND_USER_TCP_VER,
+ sizeof(guint16),
+ "TCPVersion: %u", tcpVer);
+ }
+}
+
+/*
+ * Dissect all the v5 client traffic. This is encrypted, so be careful.
+ */
+static void
+dissect_icqv5Client(tvbuff_t *tvb,
+ packet_info *pinfo,
+ proto_tree *tree)
+{
+ proto_tree *icq_tree = NULL;
+ proto_tree *icq_header_tree = NULL;
+ proto_item *ti = NULL;
+
+ int pktsize; /* The actual size of the ICQ content */
+ int capturedsize; /* The captured size of the ICQ content */
+ guint32 rounded_size;
+ guint32 key;
+ guint16 cmd;
+ guint8 *decr_pd; /* Decrypted content */
+ tvbuff_t *decr_tvb;
+
+ pktsize = tvb_reported_length(tvb);
+ capturedsize = tvb_length(tvb);
+
+ /* Get the encryption key */
+ key = get_v5key(tvb, pktsize);
+
+ /*
+ * Make a copy of the packet data, and decrypt it.
+ * The decryption processes 4 bytes at a time, and starts at
+ * an offset of ICQ5_CL_SESSIONID (which isn't a multiple of 4),
+ * so we make sure that there are
+ *
+ * (ICQ5_CL_SESSIONID + a multiple of 4)
+ *
+ * bytes in the buffer.
+ */
+ rounded_size = ((((capturedsize - ICQ5_CL_SESSIONID) + 3)/4)*4) + ICQ5_CL_SESSIONID;
+ decr_pd = g_malloc(rounded_size);
+ tvb_memcpy(tvb, decr_pd, 0, capturedsize);
+ decrypt_v5(decr_pd, rounded_size, key);
+
+ /* Allocate a new tvbuff, referring to the decrypted data. */
+ decr_tvb = tvb_new_real_data(decr_pd, capturedsize, pktsize);
+
+ /* Arrange that the allocated packet data copy be freed when the
+ tvbuff is freed. */
+ tvb_set_free_cb(decr_tvb, g_free);
+
+ /* Add the tvbuff to the list of tvbuffs to which the tvbuff we
+ were handed refers, so it'll get cleaned up when that tvbuff
+ is cleaned up. */
+ tvb_set_child_real_data_tvbuff(tvb, decr_tvb);
+
+ /* Add the decrypted data to the data source list. */
+ add_new_data_source(pinfo, decr_tvb, "Decrypted");
+
+ cmd = tvb_get_letohs(decr_tvb, ICQ5_CL_CMD);
+
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_add_fstr(pinfo->cinfo, COL_INFO, "ICQv5 %s", findClientCmd(cmd));
+
+ if (tree) {
+ ti = proto_tree_add_protocol_format(tree,
+ proto_icq,
+ tvb,
+ 0,
+ -1,
+ "ICQv5 %s (len %u)",
+ findClientCmd(cmd),
+ pktsize);
+ icq_tree = proto_item_add_subtree(ti, ett_icq);
+ ti = proto_tree_add_uint_format(icq_tree,
+ hf_icq_type,
+ tvb,
+ 0,
+ ICQ5_CL_HDRSIZE,
+ ICQ5_client,
+ "Header");
+ icq_header_tree = proto_item_add_subtree(ti, ett_icq_header);
+
+ proto_tree_add_text(icq_header_tree, tvb,
+ ICQ_VERSION,
+ 2,
+ "Version: %u",
+ tvb_get_letohs(tvb, ICQ_VERSION));
+ proto_tree_add_item(icq_header_tree,
+ hf_icq_uin,
+ tvb,
+ ICQ5_CL_UIN,
+ 4,
+ TRUE);
+ proto_tree_add_item(icq_header_tree,
+ hf_icq_sessionid,
+ decr_tvb,
+ ICQ5_CL_SESSIONID,
+ 4,
+ TRUE);
+ proto_tree_add_uint_format(icq_header_tree,
+ hf_icq_client_cmd,
+ decr_tvb,
+ ICQ5_CL_CMD,
+ 2,
+ cmd,
+ "Command: %s (%u)",
+ val_to_str(cmd, clientCmdCode, "Unknown"), cmd);
+ proto_tree_add_text(icq_header_tree, decr_tvb,
+ ICQ5_CL_SEQNUM1,
+ 2,
+ "Seq Number 1: 0x%04x",
+ tvb_get_letohs(decr_tvb, ICQ5_CL_SEQNUM1));
+ proto_tree_add_text(icq_header_tree, decr_tvb,
+ ICQ5_CL_SEQNUM2,
+ 2,
+ "Seq Number 2: 0x%04x",
+ tvb_get_letohs(decr_tvb, ICQ5_CL_SEQNUM2));
+ proto_tree_add_uint_format(icq_header_tree,
+ hf_icq_checkcode,
+ tvb,
+ ICQ5_CL_CHECKCODE,
+ 4,
+ key,
+ "Key: 0x%08x",
+ key);
+ switch(cmd) {
+ case CMD_ACK:
+ icqv5_cmd_ack(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE);
+ break;
+ case CMD_SEND_MSG:
+ case CMD_MSG_TO_NEW_USER:
+ icqv5_cmd_send_msg(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE,
+ pktsize - ICQ5_CL_HDRSIZE);
+ break;
+ case CMD_RAND_SEARCH:
+ icqv5_cmd_rand_search(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE,
+ pktsize - ICQ5_CL_HDRSIZE);
+ break;
+ case CMD_LOGIN:
+ icqv5_cmd_login(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE,
+ pktsize - ICQ5_CL_HDRSIZE);
+ break;
+ case CMD_SEND_TEXT_CODE:
+ icqv5_cmd_send_text_code(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE,
+ pktsize - ICQ5_CL_HDRSIZE);
+ break;
+ case CMD_STATUS_CHANGE:
+ icqv5_cmd_status_change(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE);
+ break;
+ case CMD_ACK_MESSAGES:
+ icqv5_cmd_ack_messages(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE);
+ break;
+ case CMD_KEEP_ALIVE:
+ icqv5_cmd_keep_alive(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE);
+ break;
+ case CMD_ADD_TO_LIST:
+ icqv5_cmd_add_to_list(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE);
+ break;
+ case CMD_CONTACT_LIST:
+ icqv5_cmd_contact_list(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE,
+ pktsize - ICQ5_CL_HDRSIZE);
+ break;
+ case CMD_META_USER:
+ case CMD_REG_NEW_USER:
+ case CMD_QUERY_SERVERS:
+ case CMD_QUERY_ADDONS:
+ icqv5_cmd_no_params(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE);
+ break;
+ default:
+ proto_tree_add_text(icq_tree,
+ decr_tvb,
+ ICQ5_CL_HDRSIZE,
+ pktsize - ICQ5_CL_HDRSIZE,
+ "Body");
+ fprintf(stderr,"Missing: %s\n", findClientCmd(cmd));
+ break;
+ }
+ }
+}
+
+static void
+dissect_icqv5Server(tvbuff_t *tvb,
+ int offset,
+ packet_info *pinfo,
+ proto_tree *tree,
+ int pktsize)
+{
+ /* Server traffic is easy, not encrypted */
+ proto_tree *icq_tree = NULL;
+ proto_tree *icq_header_tree = NULL;
+ proto_item *ti = NULL;
+ int changeCol = (pktsize==-1);
+
+ guint16 cmd;
+
+ cmd = tvb_get_letohs(tvb, offset + ICQ5_SRV_CMD);
+ if (changeCol && check_col(pinfo->cinfo, COL_INFO))
+ col_add_fstr(pinfo->cinfo, COL_INFO, "ICQv5 %s", findServerCmd(cmd));
+
+ if (pktsize == -1)
+ pktsize = tvb_reported_length(tvb);
+
+ if (tree) {
+ ti = proto_tree_add_protocol_format(tree,
+ proto_icq,
+ tvb,
+ offset,
+ pktsize,
+ "ICQv5 %s (len %u)",
+ findServerCmd(cmd),
+ pktsize);
+
+ icq_tree = proto_item_add_subtree(ti, ett_icq);
+
+ ti = proto_tree_add_uint_format(icq_tree,
+ hf_icq_type,
+ tvb,
+ offset,
+ ICQ5_SRV_HDRSIZE,
+ ICQ5_server,
+ "Header");
+ icq_header_tree = proto_item_add_subtree(ti, ett_icq_header);
+
+ proto_tree_add_text(icq_header_tree, tvb,
+ offset + ICQ_VERSION,
+ 2,
+ "Version: %u",
+ tvb_get_letohs(tvb, ICQ_VERSION));
+ proto_tree_add_item(icq_header_tree,
+ hf_icq_sessionid,
+ tvb,
+ offset + ICQ5_SRV_SESSIONID,
+ 4,
+ TRUE);
+ proto_tree_add_uint_format(icq_header_tree,
+ hf_icq_server_cmd,
+ tvb,
+ offset + ICQ5_SRV_CMD,
+ 2,
+ cmd,
+ "Command: %s (%u)",
+ val_to_str(cmd, serverCmdCode, "Unknown"), cmd);
+ proto_tree_add_text(icq_header_tree, tvb,
+ offset + ICQ5_SRV_SEQNUM1,
+ 2,
+ "Seq Number 1: 0x%04x",
+ tvb_get_letohs(tvb, offset + ICQ5_SRV_SEQNUM1));
+ proto_tree_add_text(icq_header_tree, tvb,
+ offset + ICQ5_SRV_SEQNUM2,
+ 2,
+ "Seq Number 2: 0x%04x",
+ tvb_get_letohs(tvb, offset + ICQ5_SRV_SEQNUM2));
+ proto_tree_add_item(icq_header_tree,
+ hf_icq_uin,
+ tvb,
+ offset + ICQ5_SRV_UIN,
+ 4,
+ TRUE);
+ proto_tree_add_item(icq_header_tree,
+ hf_icq_checkcode,
+ tvb,
+ offset + ICQ5_SRV_CHECKCODE,
+ 4,
+ TRUE);
+ switch (cmd) {
+ case SRV_RAND_USER:
+ icqv5_srv_rand_user(icq_tree,
+ tvb,
+ offset + ICQ5_SRV_HDRSIZE);
+ break;
+ case SRV_SYS_DELIVERED_MESS:
+ /* The message structures are all the same. Why not run
+ * the same routine? */
+ icqv5_cmd_send_msg(icq_tree,
+ tvb,
+ offset + ICQ5_SRV_HDRSIZE,
+ pktsize - ICQ5_SRV_HDRSIZE);
+ break;
+ case SRV_USER_ONLINE:
+ icqv5_srv_user_online(icq_tree,
+ tvb,
+ offset + ICQ5_SRV_HDRSIZE,
+ pktsize - ICQ5_SRV_HDRSIZE);
+ break;
+ case SRV_USER_OFFLINE:
+ icqv5_srv_user_offline(icq_tree,
+ tvb,
+ offset + ICQ5_SRV_HDRSIZE,
+ pktsize - ICQ5_SRV_HDRSIZE);
+ break;
+ case SRV_LOGIN_REPLY:
+ icqv5_srv_login_reply(icq_tree,
+ tvb,
+ offset + ICQ5_SRV_HDRSIZE,
+ pktsize - ICQ5_SRV_HDRSIZE);
+ break;
+ case SRV_META_USER:
+ icqv5_srv_meta_user(icq_tree,
+ tvb,
+ offset + ICQ5_SRV_HDRSIZE,
+ pktsize - ICQ5_SRV_HDRSIZE);
+ break;
+ case SRV_RECV_MESSAGE:
+ icqv5_srv_recv_message(icq_tree,
+ tvb,
+ offset + ICQ5_SRV_HDRSIZE,
+ pktsize - ICQ5_SRV_HDRSIZE);
+ break;
+ case SRV_MULTI:
+ icqv5_srv_multi(icq_tree,
+ tvb,
+ offset + ICQ5_SRV_HDRSIZE,
+ pktsize - ICQ5_SRV_HDRSIZE,
+ pinfo);
+ break;
+ case SRV_ACK:
+ case SRV_SILENT_TOO_LONG:
+ case SRV_GO_AWAY:
+ case SRV_NEW_UIN:
+ case SRV_BAD_PASS:
+ case SRV_UPDATE_SUCCESS:
+ icqv5_srv_no_params(icq_tree,
+ tvb,
+ offset + ICQ5_SRV_HDRSIZE);
+ break;
+ default:
+ proto_tree_add_text(icq_tree,
+ tvb,
+ offset + ICQ5_SRV_HDRSIZE,
+ pktsize - ICQ5_SRV_HDRSIZE,
+ "Body");
+ fprintf(stderr,"Missing: %s\n", findServerCmd(cmd));
+ break;
+ }
+ }
+}
+
+static void dissect_icqv5(tvbuff_t *tvb,
+ packet_info *pinfo,
+ proto_tree *tree)
+{
+ guint32 unknown;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICQv5 (UDP)");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "ICQv5 packet");
+
+ unknown = tvb_get_letohl(tvb, ICQ5_UNKNOWN);
+
+ if (unknown == 0x0L) {
+ dissect_icqv5Client(tvb, pinfo, tree);
+ } else {
+ dissect_icqv5Server(tvb, 0, pinfo, tree, -1);
+ }
+}
+
+static void dissect_icq(tvbuff_t *tvb,
+ packet_info *pinfo,
+ proto_tree *tree)
+{
+ int version;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICQ");
+ }
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_clear(pinfo->cinfo, COL_INFO);
+ }
+
+ version = tvb_get_letohs(tvb, ICQ_VERSION);
+ switch (version) {
+ case 0x0005:
+ dissect_icqv5(tvb, pinfo, tree);
+ break;
+ case 0x0004:
+ dissect_icqv4(tvb, pinfo, tree);
+ break;
+ case 0x0003:
+ dissect_icqv3(tvb, pinfo, tree);
+ break;
+ case 0x0002:
+ dissect_icqv2(tvb, pinfo, tree);
+ break;
+ default:
+ fprintf(stderr, "ICQ: Unknown version (%d)\n", version);
+ break;
+ }
+}
+
+/* registration with the filtering engine */
+void
+proto_register_icq(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_icq_type,
+ {"Type", "icq.type", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
+ { &hf_icq_uin,
+ {"UIN", "icq.uin", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
+ { &hf_icq_sessionid,
+ {"Session ID", "icq.sessionid", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
+ { &hf_icq_client_cmd,
+ {"Client command", "icq.client_cmd", FT_UINT16, BASE_HEX, VALS(clientCmdCode), 0x0, "", HFILL }},
+ { &hf_icq_server_cmd,
+ {"Server command", "icq.server_cmd", FT_UINT16, BASE_DEC, VALS(serverCmdCode), 0x0, "", HFILL }},
+ { &hf_icq_checkcode,
+ {"Checkcode", "icq.checkcode", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
+ { &hf_icq_decode,
+ {"Decode", "icq.decode", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }}
+ };
+ static gint *ett[] = {
+ &ett_icq,
+ &ett_icq_header,
+ &ett_icq_decode,
+ &ett_icq_body,
+ &ett_icq_body_parts,
+ };
+
+ proto_icq = proto_register_protocol("ICQ Protocol", "ICQ", "icq");
+
+ proto_register_field_array(proto_icq, hf, array_length(hf));
+
+ proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_icq(void)
+{
+ dissector_handle_t icq_handle;
+
+ icq_handle = create_dissector_handle(dissect_icq, proto_icq);
+ dissector_add("udp.port", UDP_PORT_ICQ, icq_handle);
+}