diff options
author | Gilbert Ramirez <gram@alumni.rice.edu> | 2004-07-18 18:06:47 +0000 |
---|---|---|
committer | Gilbert Ramirez <gram@alumni.rice.edu> | 2004-07-18 18:06:47 +0000 |
commit | 669db206cb1f270046ad400fff7655e20c63e723 (patch) | |
tree | 4eff24a2e16c8963e497e1fc575f35e6af59bd26 /epan/dissectors/packet-icq.c | |
parent | ae46c27a38700af669ef907491081f09df6f6b2c (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.c | 2222 |
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); +} |