wireshark/packet-icq.c

2201 lines
56 KiB
C
Raw Normal View History

/* packet-icq.c
* Routines for ICQ packet disassembly
*
* $Id: packet-icq.c,v 1.28 2001/03/27 02:01:31 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Johan Feyaerts
* Copyright 1999 Johan Feyaerts
* 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_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_STDDEF_H
#include <stddef.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <time.h>
#include <string.h>
#include <glib.h>
#ifdef NEED_SNPRINTF_H
# include "snprintf.h"
#endif
#include "packet.h"
#include "resolv.h"
static int proto_icq = -1;
static int hf_icq_uin =-1;
static int hf_icq_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,
guint32 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
typedef struct _cmdcode {
char* descr;
int code;
} cmdcode;
#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
cmdcode 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 },
{ NULL, -1 }
};
cmdcode 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 },
{ "SRV_QUERY", 130 },
{ "SRV_USER_FOUND", 140 },
{ "SRV_END_OF_SEARCH", 160 },
{ "SRV_NEW_USER", 180 },
{ "SRV_UPDATE_EXT", 200 },
{ "SRV_RECV_MESSAGE", SRV_RECV_MESSAGE },
{ "SRV_END_OFFLINE_MESSAGES", 230 },
{ "SRV_NOT_CONNECTED", 240 },
{ "SRV_TRY_AGAIN", 250 },
{ "SRV_SYS_DELIVERED_MESS", SRV_SYS_DELIVERED_MESS },
{ "SRV_INFO_REPLY", 280 },
{ "SRV_EXT_INFO_REPLY", 290 },
{ "SRV_STATUS_UPDATE", 420 },
{ "SRV_SYSTEM_MESSAGE", 450 },
{ "SRV_UPDATE_SUCCESS", SRV_UPDATE_SUCCESS },
{ "SRV_UPDATE_FAIL", SRV_UPDATE_FAIL },
{ "SRV_AUTH_UPDATE", 500 },
{ "SRV_MULTI_PACKET", SRV_MULTI },
{ "SRV_END_CONTACTLIST_STATUS", 540 },
{ "SRV_RAND_USER", SRV_RAND_USER },
{ "SRV_META_USER", SRV_META_USER },
{ NULL, -1 }
};
#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
cmdcode 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},
{ NULL, 0}
};
cmdcode statusCode[] = {
{ "ONLINE", STATUS_ONLINE },
{ "AWAY", STATUS_AWAY },
{ "DND", STATUS_DND },
{ "INVISIBLE", STATUS_INVISIBLE },
{ "OCCUPIED", STATUS_OCCUPIED },
{ "NA", STATUS_NA },
{ "Free for Chat", STATUS_CHAT },
{ NULL, 0}
};
cmdcode clientCmdCode[] = {
{ "CMD_ACK", CMD_ACK },
{ "CMD_SEND_MESSAGE", CMD_SEND_MSG },
{ "CMD_LOGIN", CMD_LOGIN },
{ "CMD_REG_NEW_USER", CMD_REG_NEW_USER },
{ "CMD_CONTACT_LIST", 1030 },
{ "CMD_SEARCH_UIN", 1050 },
{ "CMD_SEARCH_USER", 1060 },
{ "CMD_KEEP_ALIVE", 1070 },
{ "CMD_SEND_TEXT_CODE", CMD_SEND_TEXT_CODE },
{ "CMD_ACK_MESSAGES", CMD_ACK_MESSAGES },
{ "CMD_LOGIN_1", 1100 },
{ "CMD_MSG_TO_NEW_USER", CMD_MSG_TO_NEW_USER },
{ "CMD_INFO_REQ", 1120 },
{ "CMD_EXT_INFO_REQ", 1130 },
{ "CMD_CHANGE_PW", 1180 },
{ "CMD_NEW_USER_INFO", 1190 },
{ "CMD_UPDATE_EXT_INFO", 1200 },
{ "CMD_QUERY_SERVERS", CMD_QUERY_SERVERS },
{ "CMD_QUERY_ADDONS", CMD_QUERY_ADDONS },
{ "CMD_STATUS_CHANGE", CMD_STATUS_CHANGE },
{ "CMD_NEW_USER_1", 1260 },
{ "CMD_UPDATE_INFO", 1290 },
{ "CMD_AUTH_UPDATE", 1300 },
{ "CMD_KEEP_ALIVE2", 1310 },
{ "CMD_LOGIN_2", 1320 },
{ "CMD_ADD_TO_LIST", CMD_ADD_TO_LIST },
{ "CMD_RAND_SET", 1380 },
{ "CMD_RAND_SEARCH", CMD_RAND_SEARCH },
{ "CMD_META_USER", CMD_META_USER },
{ "CMD_INVIS_LIST", 1700 },
{ "CMD_VIS_LIST", 1710 },
{ "CMD_UPDATE_LIST", 1720 },
{ NULL, 0 }
};
/*
* All ICQv5 decryption code thanx to Sebastien Dault (daus01@gel.usherb.ca)
*/
const u_char
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*
findcmd(cmdcode* c, int num)
{
static char buf[16];
cmdcode* p = c;
while (p->descr != NULL) {
if (p->code == num) {
return p->descr;
}
p++;
}
snprintf(buf, sizeof(buf), "(%x)", num);
return buf;
}
static char*
findMsgType(int num)
{
return findcmd(msgTypeCode, num);
}
static char*
findSubCmd(int num)
{
return findcmd(serverMetaSubCmdCode, num);
}
static char*
findClientCmd(int num)
{
return findcmd(clientCmdCode, num);
}
static char*
findServerCmd(int num)
{
return findcmd(serverCmdCode, num);
}
static char*
findStatus(int num)
{
return findcmd(statusCode, num);
}
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(u_char *bfr, guint32 size,guint32 key)
{
guint32 i;
guint32 k;
for (i=ICQ5_CL_SESSIONID; i < size+3; i+=4 ) {
k = key+table_v5[i&0xff];
if ( i != 0x16 ) {
bfr[i] ^= (u_char)(k & 0xff);
bfr[i+1] ^= (u_char)((k & 0xff00)>>8);
}
if ( i != 0x12 ) {
bfr[i+2] ^= (u_char)((k & 0xff0000)>>16);
bfr[i+3] ^= (u_char)((k & 0xff000000)>>24);
}
}
}
static void
dissect_icqv4(tvbuff_t *tvb,
packet_info *pinfo,
proto_tree *tree)
{
/* Not really implemented yet */
if (check_col(pinfo->fd, COL_PROTOCOL)) {
col_set_str(pinfo->fd, COL_PROTOCOL, "ICQv4 (UDP)");
}
if (check_col(pinfo->fd, COL_INFO)) {
col_set_str(pinfo->fd, COL_INFO, "ICQ Version 4 protocol");
}
}
static void
dissect_icqv3(tvbuff_t *tvb,
packet_info *pinfo,
proto_tree *tree)
{
/* Not really implemented yet */
if (check_col(pinfo->fd, COL_PROTOCOL)) {
col_set_str(pinfo->fd, COL_PROTOCOL, "ICQv3 (UDP)");
}
if (check_col(pinfo->fd, COL_INFO)) {
col_set_str(pinfo->fd, COL_INFO, "ICQ Version 3 protocol");
}
}
static void
dissect_icqv2(tvbuff_t *tvb,
packet_info *pinfo,
proto_tree *tree)
{
/* Not really implemented yet */
if (check_col(pinfo->fd, COL_PROTOCOL)) {
col_set_str(pinfo->fd, COL_PROTOCOL, "ICQv2 (UDP)");
}
if (check_col(pinfo->fd, COL_INFO)) {
col_set_str(pinfo->fd, COL_INFO, "ICQ Version 2 protocol");
}
}
/*
* 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 */
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_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
4,
CMD_ACK,
"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){
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
4,
CMD_RAND_SEARCH,
"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_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
4,
CMD_ACK_MESSAGES,
"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_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
4,
CMD_KEEP_ALIVE,
"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 = 0;
guint16 x1 = -1;
if (tree){
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
size,
CMD_KEEP_ALIVE,
"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 = -1;
proto_tree* subtree;
proto_item* ti;
if (tree){
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
4,
CMD_ADD_TO_LIST,
"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_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
4,
CMD_STATUS_CHANGE,
"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: %08x (%s)", status, findStatus(status));
}
}
static void
icqv5_cmd_send_msg(proto_tree* tree,
tvbuff_t *tvb,
int offset,
int size,
int cmd)
{
proto_tree* subtree;
proto_item* ti;
int left = size; /* left chars to do */
if (tree) {
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
size,
cmd,
"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 u_char *ipAddrp;
guint32 status;
if (tree) {
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
size,
CMD_SEND_MSG,
"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_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
size,
CMD_CONTACT_LIST,
"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 */
int cmd)
{
proto_tree* subtree;
proto_item* ti;
if (tree){
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
0,
cmd,
"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 */
int size, /* Number of chars left to do */
int cmd)
{
proto_tree* subtree;
proto_item* ti;
if (tree){
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
0,
cmd,
"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 u_char *ipAddrp;
if (tree) {
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
SRV_LOGIN_REPLY_IP + 8,
SRV_LOGIN_REPLY,
"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 u_char *ipAddrp;
const u_char *realipAddrp;
guint32 status;
if (tree) {
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
SRV_LOGIN_REPLY_IP + 8,
SRV_LOGIN_REPLY,
"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) {
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
SRV_USER_OFFLINE_UIN + 4,
SRV_USER_OFFLINE,
"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;
unsigned char num = -1;
guint16 pktSz;
int i, left;
if (tree) {
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
size,
SRV_MULTI,
"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);
left = size;
for (i = 0; (i<num) && (left>0);i++) {
if (left>=2) {
pktSz = tvb_get_letohs(tvb, offset);
offset += 2;
left -= 2;
if (left>=pktSz) {
dissect_icqv5Server(tvb, offset, pinfo, subtree, pktSz);
offset += pktSz;
left -= 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_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
size,
SRV_META_USER,
"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 = -1;
/* 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;
unsigned char user_timezone = -1;
unsigned char auth = -1;
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;
guint32 uin = -1;
guint16 year = -1;
unsigned char month = -1;
unsigned char day = -1;
unsigned char hour = -1;
unsigned char minute = -1;
if (tree) {
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
4,
SRV_RECV_MESSAGE,
"Body");
subtree = proto_item_add_subtree(ti, ett_icq_body);
uin = tvb_get_letohl(tvb, offset + SRV_RECV_MSG_UIN);
proto_tree_add_uint_format(subtree,
hf_icq_uin,
tvb,
offset + SRV_RECV_MSG_UIN,
sizeof(guint32),
uin,
"UIN: %u", uin);
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 = -1;
const unsigned char* IP = NULL;
guint32 port = -1;
const unsigned char* realIP = NULL;
unsigned char commClass = -1;
guint32 status;
guint16 tcpVer;
if (tree) {
ti = proto_tree_add_uint_format(tree,
hf_icq_cmd,
tvb,
offset,
SRV_RAND_USER_TCP_VER + 2,
SRV_RAND_USER,
"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));
/* guint32 portNum */
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));
/* guit16 Communication Class */
commClass = tvb_get_guint8(tvb, offset + SRV_RAND_USER_CLASS);
proto_tree_add_text(subtree, tvb,
offset + SRV_RAND_USER_CLASS,
sizeof(unsigned char),
"Class: %s", (commClass!=4)?"User to User":"Through Server");
/* guint32 status */
status = tvb_get_letohs(tvb, offset + SRV_RAND_USER_STATUS);
proto_tree_add_text(subtree, tvb,
offset + SRV_RAND_USER_STATUS,
sizeof(guint32),
"Status: (%u) %s", status, 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;
guint16 pktsize; /* The size of the ICQ content */
guint32 key;
guint16 cmd;
static u_char *decr_pd = NULL; /* Decrypted content */
static int decr_size = 0; /* Size of decrypted-content buffer */
tvbuff_t *decr_tvb;
pktsize = tvb_length(tvb);
if (decr_size == 0) {
decr_size = sizeof(u_char) * 128;
decr_pd = g_malloc(decr_size);
}
while (decr_size < pktsize + 3) {
decr_size *= 2;
decr_pd = g_realloc(decr_pd, decr_size);
}
/* Get the encryption key */
key = get_v5key(tvb, pktsize);
/* Make a copy of the packet data, and decrypt it */
tvb_memcpy(tvb, decr_pd, 0, pktsize);
decrypt_v5(decr_pd, pktsize, key);
/* Allocate a new tvbuff, referring to the decrypted data. */
decr_tvb = tvb_new_real_data(decr_pd, pktsize, tvb_reported_length(tvb),
"Decrypted");
/* 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. */
pinfo->fd->data_src = g_slist_append(pinfo->fd->data_src, decr_tvb);
cmd = tvb_get_letohs(decr_tvb, ICQ5_CL_CMD);
if (check_col(pinfo->fd, COL_INFO))
col_add_fstr(pinfo->fd, COL_INFO, "ICQv5 %s", findClientCmd(cmd));
if (tree) {
ti = proto_tree_add_protocol_format(tree,
proto_icq,
tvb,
0,
pktsize,
"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_text(icq_header_tree, decr_tvb,
ICQ5_CL_CMD,
2,
"Command: %s (%u)", findClientCmd(cmd), 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,
cmd);
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,
cmd);
break;
default:
proto_tree_add_uint_format(icq_tree,
hf_icq_cmd,
decr_tvb,
ICQ5_CL_CMD,
2,
cmd,
"Command: %u (%s)",
cmd, findClientCmd(cmd));
fprintf(stderr,"Missing: %s\n", findClientCmd(cmd));
break;
}
}
}
static void
dissect_icqv5Server(tvbuff_t *tvb,
int offset,
packet_info *pinfo,
proto_tree *tree,
guint32 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==(guint32)-1);
guint16 cmd;
cmd = tvb_get_letohs(tvb, offset + ICQ5_SRV_CMD);
if (changeCol && check_col(pinfo->fd, COL_INFO))
col_add_fstr(pinfo->fd, 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_text(icq_header_tree, tvb,
offset + ICQ5_SRV_CMD,
2,
"Command: %s (%u)", findServerCmd(cmd), 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,
cmd);
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,
pktsize - ICQ5_SRV_HDRSIZE,
cmd);
break;
default:
proto_tree_add_uint_format(icq_tree,
hf_icq_cmd,
tvb,
ICQ5_SRV_CMD,
2,
cmd,
"Command: %u (%s)",
cmd, findServerCmd(cmd));
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->fd, COL_PROTOCOL))
col_set_str(pinfo->fd, COL_PROTOCOL, "ICQv5 (UDP)");
if (check_col(pinfo->fd, COL_INFO))
col_set_str(pinfo->fd, 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, (guint32) -1);
}
}
static void dissect_icq(tvbuff_t *tvb,
packet_info *pinfo,
proto_tree *tree)
{
int version;
if (check_col(pinfo->fd, COL_PROTOCOL)) {
col_set_str(pinfo->fd, COL_PROTOCOL, "ICQ");
}
if (check_col(pinfo->fd, COL_INFO)) {
col_clear(pinfo->fd, 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, ""}},
{ &hf_icq_uin,
{"UIN", "icq.uin", FT_UINT32, BASE_DEC, NULL, 0x0, ""}},
{ &hf_icq_sessionid,
{"Session ID", "icq.sessionid", FT_UINT32, BASE_HEX, NULL, 0x0, ""}},
{ &hf_icq_cmd,
{"Command", "icq.cmd", FT_UINT16, BASE_DEC, NULL, 0x0, ""}},
{ &hf_icq_checkcode,
{"Checkcode", "icq.checkcode", FT_UINT32, BASE_HEX, NULL, 0x0, ""}},
{ &hf_icq_decode,
{"Decode", "icq.decode", FT_STRING, BASE_NONE, NULL, 0x0, ""}}
};
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_add("udp.port", UDP_PORT_ICQ, dissect_icq, proto_icq);
}