Stuart Stanley's ISIS dissection support.
[obnox/wireshark/wip.git] / packet-icq.c
1 /* packet-icq.c
2  * Routines for ICQ packet disassembly
3  *
4  * $Id: packet-icq.c,v 1.9 1999/12/05 22:59:55 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Johan Feyaerts
8  * Copyright 1999 Johan Feyaerts
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 /*
25  * This file: by Kojak <kojak@bigwig.net>
26  *
27  * Decoding code ripped, reference to the original author at the
28  * appropriate place with the code itself.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #ifdef HAVE_SYS_TYPES_H
36 # include <sys/types.h>
37 #endif
38
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42
43 #ifdef HAVE_STDDEF_H
44 #include <stddef.h>
45 #endif
46
47 #ifdef HAVE_ARPA_INET_H
48 #include <arpa/inet.h>
49 #endif
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <assert.h>
54 #include <ctype.h>
55 #include <time.h>
56 #include <string.h>
57 #include <glib.h>
58
59 #ifdef NEED_SNPRINTF_H
60 # ifdef HAVE_STDARG_H
61 #  include <stdarg.h>
62 # else
63 #  include <varargs.h>
64 # endif
65 # include "snprintf.h"
66 #endif
67
68 #include "packet.h"
69 #include "resolv.h"
70
71 static int proto_icq = -1;
72 static int hf_icq_uin =-1;
73 static int hf_icq_cmd =-1;
74 static int hf_icq_sessionid =-1;
75 static int hf_icq_checkcode =-1;
76 static int hf_icq_decode = -1;
77 static int hf_icq_type = -1;
78
79 static gint ett_icq = -1;
80 static gint ett_icq_header = -1;
81 static gint ett_icq_decode = -1;
82 static gint ett_icq_body = -1;
83 static gint ett_icq_body_parts = -1;
84
85 enum { ICQ5_client, ICQ5_server};
86
87 void dissect_icqv5(const u_char *pd,
88                    int offset,
89                    frame_data *fd,
90                    proto_tree *tree);
91
92 static void
93 dissect_icqv5Server(const u_char *pd,
94                     int offset,
95                     frame_data *fd,
96                     proto_tree *tree,
97                     guint32 pktsize);
98
99 /* Offsets of fields in the ICQ headers */
100 /* Can be 0x0002 or 0x0005 */
101 #define ICQ_VERSION             0x00
102 /* Is either one (server) or four (client) bytes long */
103 /* Client header offsets */
104 #define ICQ5_UNKNOWN            0x02
105 #define ICQ5_CL_UIN             0x06
106 #define ICQ5_CL_SESSIONID       0x0a
107 #define ICQ5_CL_CMD             0x0e
108 #define ICQ5_CL_SEQNUM1         0x10
109 #define ICQ5_CL_SEQNUM2         0x12
110 #define ICQ5_CL_CHECKCODE       0x14
111 #define ICQ5_CL_PARAM           0x18
112 #define ICQ5_CL_HDRSIZE         0x18
113
114 /* Server header offsets */
115 #define ICQ5_SRV_SESSIONID      0x03
116 #define ICQ5_SRV_CMD            0x07
117 #define ICQ5_SRV_SEQNUM1        0x09
118 #define ICQ5_SRV_SEQNUM2        0x0b
119 #define ICQ5_SRV_UIN            0x0d
120 #define ICQ5_SRV_CHECKCODE      0x11
121 #define ICQ5_SRV_PARAM          0x15
122 #define ICQ5_SRV_HDRSIZE        0x15
123
124 typedef struct _cmdcode {
125     char* descr;
126     int code;
127 } cmdcode;
128
129 #define SRV_ACK                 0x000a
130
131 #define SRV_GO_AWAY             0x0028
132
133 #define SRV_NEW_UIN             0x0046
134
135 /* LOGIN_REPLY is very scary. It has a lot of fields that are undocumented
136  * Only the IP field makes sense */
137 #define SRV_LOGIN_REPLY         0x005a
138 #define SRV_LOGIN_REPLY_IP      0x000c
139
140 #define SRV_BAD_PASS            0x0064
141
142 #define SRV_USER_ONLINE         0x006e
143 #define SRV_USER_ONL_UIN        0x0000
144 #define SRV_USER_ONL_IP         0x0004
145 #define SRV_USER_ONL_PORT       0x0008
146 #define SRV_USER_ONL_REALIP     0x000c
147 #define SRV_USER_ONL_X1         0x0010
148 #define SRV_USER_ONL_STATUS     0x0013
149 #define SRV_USER_ONL_X2         0x0015
150
151 #define SRV_USER_OFFLINE        0x0078
152 #define SRV_USER_OFFLINE_UIN    0x0000
153
154 #define SRV_MULTI               0x0212
155 #define SRV_MULTI_NUM           0x0000
156
157 #define SRV_META_USER           0x03de
158 #define SRV_META_USER_SUBCMD    0x0000
159 #define SRV_META_USER_RESULT    0x0002
160 #define SRV_META_USER_DATA      0x0003
161
162 #define SRV_UPDATE_SUCCESS      0x01e0
163
164 #define SRV_UPDATE_FAIL         0x01ea
165
166 /*
167  * ICQv5 SRV_META_USER subcommands
168  */
169 #define META_EX_USER_FOUND      0x0190
170 #define META_USER_FOUND         0x019a
171 #define META_ABOUT              0x00e6
172 #define META_USER_INFO          0x00c8
173
174 #define SRV_RECV_MESSAGE        0x00dc
175 #define SRV_RECV_MSG_UIN        0x0000
176 #define SRV_RECV_MSG_YEAR       0x0004
177 #define SRV_RECV_MSG_MONTH      0x0006
178 #define SRV_RECV_MSG_DAY        0x0007
179 #define SRV_RECV_MSG_HOUR       0x0008
180 #define SRV_RECV_MSG_MINUTE     0x0009
181 #define SRV_RECV_MSG_MSG_TYPE   0x000a
182
183 #define SRV_RAND_USER           0x024e
184 #define SRV_RAND_USER_UIN       0x0000
185 #define SRV_RAND_USER_IP        0x0004
186 #define SRV_RAND_USER_PORT      0x0008
187 #define SRV_RAND_USER_REAL_IP   0x000c
188 #define SRV_RAND_USER_CLASS     0x0010
189 #define SRV_RAND_USER_X1        0x0011
190 #define SRV_RAND_USER_STATUS    0x0015
191 #define SRV_RAND_USER_TCP_VER   0x0019
192
193 /* This message has the same structure as cmd_send_msg */
194 #define SRV_SYS_DELIVERED_MESS  0x0104
195
196 cmdcode serverMetaSubCmdCode[] = {
197     { "META_USER_FOUND", META_USER_FOUND },
198     { "META_EX_USER_FOUND", META_EX_USER_FOUND },
199     { "META_ABOUT", META_ABOUT },
200     { "META_USER_INFO", META_USER_INFO },
201     { NULL, -1 }
202 };
203
204 cmdcode serverCmdCode[] = {
205     { "SRV_ACK", SRV_ACK },
206     { "SRV_GO_AWAY", SRV_GO_AWAY },
207     { "SRV_NEW_UIN", SRV_NEW_UIN },
208     { "SRV_LOGIN_REPLY", SRV_LOGIN_REPLY },
209     { "SRV_BAD_PASS", SRV_BAD_PASS },
210     { "SRV_USER_ONLINE", SRV_USER_ONLINE },
211     { "SRV_USER_OFFLINE", SRV_USER_OFFLINE },
212     { "SRV_QUERY", 130 },
213     { "SRV_USER_FOUND", 140 },
214     { "SRV_END_OF_SEARCH", 160 },
215     { "SRV_NEW_USER", 180 },
216     { "SRV_UPDATE_EXT", 200 },
217     { "SRV_RECV_MESSAGE", SRV_RECV_MESSAGE },
218     { "SRV_X2", 230 },
219     { "SRV_NOT_CONNECTED", 240 },
220     { "SRV_TRY_AGAIN", 250 },
221     { "SRV_SYS_DELIVERED_MESS", SRV_SYS_DELIVERED_MESS },
222     { "SRV_INFO_REPLY", 280 },
223     { "SRV_EXT_INFO_REPLY", 290 },
224     { "SRV_STATUS_UPDATE", 420 },
225     { "SRV_SYSTEM_MESSAGE", 450 },
226     { "SRV_UPDATE_SUCCESS", SRV_UPDATE_SUCCESS },
227     { "SRV_UPDATE_FAIL", SRV_UPDATE_FAIL },
228     { "SRV_AUTH_UPDATE", 500 },
229     { "SRV_MULTI_PACKET", SRV_MULTI },
230     { "SRV_X1", 540 },
231     { "SRV_RAND_USER", SRV_RAND_USER },
232     { "SRV_META_USER", SRV_META_USER },
233     { NULL, -1 }
234 };
235
236 #define MSG_TEXT                0x0001
237 #define MSG_URL                 0x0004
238 #define MSG_AUTH_REQ            0x0006
239 #define MSG_AUTH                0x0008
240 #define MSG_USER_ADDED          0x000c
241 #define MSG_EMAIL               0x000e
242 #define MSG_CONTACTS            0x0013
243
244 #define STATUS_ONLINE           0x00000000
245 #define STATUS_AWAY             0x00000001
246 #define STATUS_DND              0x00000013
247 #define STATUS_INVISIBLE        0x00000100
248 #define STATUS_OCCUPIED         0x00000010
249 #define STATUS_NA               0x00000004
250 #define STATUS_CHAT             0x00000020
251
252 /* Offsets for all packets measured from the start of the payload; i.e.
253  * with the ICQ header removed
254  */
255 #define CMD_ACK                 0x000a
256 #define CMD_ACK_RANDOM          0x0000
257
258 #define CMD_SEND_MSG            0x010E
259 #define CMD_SEND_MSG_RECV_UIN   0x0000
260 #define CMD_SEND_MSG_MSG_TYPE   0x0004
261 #define CMD_SEND_MSG_MSG_LEN    0x0006
262 #define CMD_SEND_MSG_MSG_TEXT   0x0008
263 /* The rest of the packet should be a null-term string */
264
265 #define CMD_LOGIN               0x03E8
266 #define CMD_LOGIN_TIME          0x0000
267 #define CMD_LOGIN_PORT          0x0004
268 #define CMD_LOGIN_PASSLEN       0x0008
269 #define CMD_LOGIN_PASSWD        0x000A
270 /* The password is variable length; so when we've decoded the passwd,
271  * the structure starts counting at 0 again.
272  */
273 #define CMD_LOGIN_IP            0x0004
274 #define CMD_LOGIN_STATUS        0x0009
275
276 #define CMD_CONTACT_LIST        0x0406
277 #define CMD_CONTACT_LIST_NUM    0x0000
278
279 #define CMD_USER_META           0x064a
280
281 #define CMD_REG_NEW_USER        0x03fc
282
283 #define CMD_ACK_MESSAGES        0x0442
284 #define CMD_ACK_MESSAGES_RANDOM 0x0000
285
286 #define CMD_KEEP_ALIVE          0x042e
287 #define CMD_KEEP_ALIVE_RANDOM   0x0000
288
289 #define CMD_SEND_TEXT_CODE      0x0438
290 #define CMD_SEND_TEXT_CODE_LEN  0x0000
291 #define CMD_SEND_TEXT_CODE_TEXT 0x0002
292
293 #define CMD_MSG_TO_NEW_USER     0x0456
294
295 #define CMD_QUERY_SERVERS       0x04ba
296
297 #define CMD_QUERY_ADDONS        0x04c4
298
299 #define CMD_STATUS_CHANGE       0x04d8
300 #define CMD_STATUS_CHANGE_STATUS        0x0000
301
302 #define CMD_ADD_TO_LIST         0x053c
303 #define CMD_ADD_TO_LIST_UIN     0x0000
304
305 #define CMD_RAND_SEARCH         0x056e
306 #define CMD_RAND_SEARCH_GROUP   0x0000
307
308 #define CMD_META_USER           0x064a
309
310 cmdcode msgTypeCode[] = {
311     { "MSG_TEXT", MSG_TEXT },
312     { "MSG_URL", MSG_URL },
313     { "MSG_AUTH_REQ", MSG_AUTH_REQ },
314     { "MSG_AUTH", MSG_AUTH },
315     { "MSG_USER_ADDED", MSG_USER_ADDED},
316     { "MSG_EMAIL", MSG_EMAIL},
317     { "MSG_CONTACTS", MSG_CONTACTS},
318     { NULL, 0}
319 };
320
321 cmdcode statusCode[] = {
322     { "ONLINE", STATUS_ONLINE },
323     { "AWAY", STATUS_AWAY },
324     { "DND", STATUS_DND },
325     { "INVISIBLE", STATUS_INVISIBLE },
326     { "OCCUPIED", STATUS_OCCUPIED },
327     { "NA", STATUS_NA },
328     { "Free for Chat", STATUS_CHAT },
329     { NULL, 0}
330 };
331
332 cmdcode clientCmdCode[] = {
333     { "CMD_ACK", CMD_ACK },
334     { "CMD_SEND_MESSAGE", CMD_SEND_MSG },
335     { "CMD_LOGIN", CMD_LOGIN },
336     { "CMD_REG_NEW_USER", CMD_REG_NEW_USER },
337     { "CMD_CONTACT_LIST", 1030 },
338     { "CMD_SEARCH_UIN", 1050 },
339     { "CMD_SEARCH_USER", 1060 },
340     { "CMD_KEEP_ALIVE", 1070 },
341     { "CMD_SEND_TEXT_CODE", CMD_SEND_TEXT_CODE },
342     { "CMD_ACK_MESSAGES", CMD_ACK_MESSAGES },
343     { "CMD_LOGIN_1", 1100 },
344     { "CMD_MSG_TO_NEW_USER", CMD_MSG_TO_NEW_USER },
345     { "CMD_INFO_REQ", 1120 },
346     { "CMD_EXT_INFO_REQ", 1130 },
347     { "CMD_CHANGE_PW", 1180 },
348     { "CMD_NEW_USER_INFO", 1190 },
349     { "CMD_UPDATE_EXT_INFO", 1200 },
350     { "CMD_QUERY_SERVERS", CMD_QUERY_SERVERS },
351     { "CMD_QUERY_ADDONS", CMD_QUERY_ADDONS },
352     { "CMD_STATUS_CHANGE", CMD_STATUS_CHANGE },
353     { "CMD_NEW_USER_1", 1260 },
354     { "CMD_UPDATE_INFO", 1290 },
355     { "CMD_AUTH_UPDATE", 1300 },
356     { "CMD_KEEP_ALIVE2", 1310 },
357     { "CMD_LOGIN_2", 1320 },
358     { "CMD_ADD_TO_LIST", CMD_ADD_TO_LIST },
359     { "CMD_RAND_SET", 1380 },
360     { "CMD_RAND_SEARCH", CMD_RAND_SEARCH },
361     { "CMD_META_USER", CMD_META_USER },
362     { "CMD_INVIS_LIST", 1700 },
363     { "CMD_VIS_LIST", 1710 },
364     { "CMD_UPDATE_LIST", 1720 },
365     { NULL, 0 }
366 };
367
368 /*
369  * All ICQv5 decryption code thanx to Sebastien Dault (daus01@gel.usherb.ca)
370  */
371 const u_char
372 table_v5 [] = {
373  0x59, 0x60, 0x37, 0x6B, 0x65, 0x62, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x60, 0x57, 0x5B, 0x3D,
374  0x5E, 0x34, 0x6D, 0x36, 0x50, 0x3F, 0x6F, 0x67, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x47, 0x63, 0x39,
375  0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x43, 0x69, 0x48, 0x33, 0x31, 0x64, 0x35, 0x5A, 0x4A, 0x42,
376  0x56, 0x40, 0x67, 0x53, 0x41, 0x07, 0x6C, 0x49, 0x58, 0x3B, 0x4D, 0x46, 0x68, 0x43, 0x69, 0x48,
377  0x33, 0x31, 0x44, 0x65, 0x62, 0x46, 0x48, 0x53, 0x41, 0x07, 0x6C, 0x69, 0x48, 0x33, 0x51, 0x54,
378  0x5D, 0x4E, 0x6C, 0x49, 0x38, 0x4B, 0x55, 0x4A, 0x62, 0x46, 0x48, 0x33, 0x51, 0x34, 0x6D, 0x36,
379  0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x63, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64, 0x35, 0x5A,
380  0x6A, 0x52, 0x6E, 0x3C, 0x51, 0x34, 0x6D, 0x36, 0x50, 0x5F, 0x5F, 0x3F, 0x4F, 0x37, 0x4B, 0x35,
381  0x5A, 0x4A, 0x62, 0x66, 0x58, 0x3B, 0x4D, 0x66, 0x58, 0x5B, 0x5D, 0x4E, 0x6C, 0x49, 0x58, 0x3B,
382  0x4D, 0x66, 0x58, 0x3B, 0x4D, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64,
383  0x55, 0x6A, 0x32, 0x3E, 0x44, 0x45, 0x52, 0x6E, 0x3C, 0x31, 0x64, 0x55, 0x6A, 0x52, 0x4E, 0x6C,
384  0x69, 0x48, 0x53, 0x61, 0x4C, 0x39, 0x30, 0x6F, 0x47, 0x63, 0x59, 0x60, 0x57, 0x5B, 0x3D, 0x3E,
385  0x64, 0x35, 0x3A, 0x3A, 0x5A, 0x6A, 0x52, 0x4E, 0x6C, 0x69, 0x48, 0x53, 0x61, 0x6C, 0x49, 0x58,
386  0x3B, 0x4D, 0x46, 0x68, 0x63, 0x39, 0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x67, 0x53, 0x41, 0x25, 0x41,
387  0x3C, 0x51, 0x54, 0x3D, 0x5E, 0x54, 0x5D, 0x4E, 0x4C, 0x39, 0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F,
388  0x47, 0x43, 0x69, 0x48, 0x33, 0x51, 0x54, 0x5D, 0x6E, 0x3C, 0x31, 0x64, 0x35, 0x5A, 0x00, 0x00 };
389  
390 static char*
391 findcmd(cmdcode* c, int num)
392 {
393     static char buf[16];
394     cmdcode* p = c;
395     while (p->descr != NULL) {
396         if (p->code == num) {
397             return p->descr;
398         }
399         p++;
400     }
401     snprintf(buf, sizeof(buf), "(%x)", num);
402     return buf;
403 }
404
405 static char*
406 findMsgType(int num)
407 {
408     return findcmd(msgTypeCode, num);
409 }
410
411 static char*
412 findSubCmd(int num)
413 {
414     return findcmd(serverMetaSubCmdCode, num);
415 }
416
417 static char*
418 findClientCmd(int num)
419 {
420     return findcmd(clientCmdCode, num);
421 }
422
423 static char*
424 findServerCmd(int num)
425 {
426     return findcmd(serverCmdCode, num);
427 }
428
429 static char*
430 findStatus(int num)
431 {
432     return findcmd(statusCode, num);
433 }
434
435 static void
436 proto_tree_add_hexdump(proto_tree* t,
437                        guint32 offset,
438                        const u_char *data,
439                        int size)
440 {
441     int i;
442     char buf[96];
443     int n;
444     int done = 0, line = 0;
445     int added = 0;
446
447     if (size==0)
448         return;
449     
450     line = size / 16;
451     
452     for (i=0;i<line;i++) {
453         added = 0;
454         done = 0;
455         for (n = i * 16; n < (i+1)*16; n++) {
456             added = sprintf(buf+done, "%02x", data[n]);
457             if ((n%8)==7)
458                 added += sprintf(buf + done + added, "  ");
459             else
460                 added += sprintf(buf + done + added, " ");
461             done += added;
462         }
463         for (n = i * 16; n < (i+1)*16; n++) {
464             if (isprint(data[n]))
465                 added = sprintf(buf + done, "%c", data[n]);
466             else
467                 added = sprintf(buf + done, ".");
468             done += added;
469         }
470         proto_tree_add_text(t,
471                             offset + i*16,
472                             16,
473                             buf);
474     }
475     if ((size%16)!=0) {
476         done = 0;
477         for (n = line * 16 ; n < size ; n++) {
478             added = sprintf(buf+done, "%02x", data[n]);
479             if ((n%8)==7)
480                 added += sprintf(buf + done + added, "  ");
481             else
482                 added += sprintf(buf + done + added, " ");
483             done += added;
484         }
485         for (n = size ; (n%16)!=0;n++) {
486             added = 0;
487             if ((n%8)==7)
488                 added += sprintf(buf + done + added, "    ");
489             else
490                 added += sprintf(buf + done + added, "   ");
491             done += added;
492         }
493         for (n = line * 16; n < (line+1)*16; n++) {
494             added = 0;
495             if (n<size) {
496                 if (isprint(data[n]))
497                     added = sprintf(buf + done, "%c", data[n]);
498                 else
499                     added = sprintf(buf + done, ".");
500             } else {
501                 added = sprintf(buf + done, " ");
502             }
503             done += added;
504         }
505         proto_tree_add_text(t,
506                             offset + line*16,
507                             size % 16,
508                             buf);
509     }
510 }
511
512 static guint32
513 get_v5key(const u_char* pd, int len)
514 {
515     guint32 a1, a2, a3, a4, a5;
516     guint32 code, check, key;
517
518     code = pletohl(&pd[ICQ5_CL_CHECKCODE]);
519
520     a1 = code & 0x0001f000;
521     a2 = code & 0x07c007c0;
522     a3 = code & 0x003e0001;
523     a4 = code & 0xf8000000;
524     a5 = code & 0x0000083e;
525
526     a1 = a1 >> 0x0c;
527     a2 = a2 >> 0x01;
528     a3 = a3 << 0x0a;
529     a4 = a4 >> 0x10;
530     a5 = a5 << 0x0f;
531
532     check = a5 + a1 + a2 + a3 + a4;
533     key = len * 0x68656C6C;
534     key += check;
535     return key;
536 }
537
538 static void
539 decrypt_v5(u_char *bfr, guint32 size,guint32 key)
540 {
541     guint32 i;
542     guint32 k;
543     for (i=0x0a; i < size+3; i+=4 ) {
544         k = key+table_v5[i&0xff];
545         if ( i != 0x16 ) {
546             bfr[i] ^= (u_char)(k & 0xff);
547             bfr[i+1] ^= (u_char)((k & 0xff00)>>8);
548         }
549         if ( i != 0x12 ) {
550             bfr[i+2] ^= (u_char)((k & 0xff0000)>>16);
551             bfr[i+3] ^= (u_char)((k & 0xff000000)>>24);
552         }
553     }
554 }
555
556 static void
557 dissect_icqv2(const u_char *pd,
558               int offset,
559               frame_data *fd, 
560               proto_tree *tree)
561 {
562     /* Not really implemented yet */
563     if (check_col(fd, COL_PROTOCOL)) {
564         col_add_str(fd, COL_PROTOCOL, "ICQv2 (UDP)");
565     }
566     if (check_col(fd, COL_INFO)) {
567         col_add_str(fd, COL_INFO, "ICQ Version 2 protocol");
568     }
569 }
570
571 /*
572  * Find first occurrence of ch in buf
573  * Buf is max size big.
574  */
575 static char*
576 strnchr(const u_char* buf, u_char ch, int size)
577 {
578     int i;
579     u_char* p = (u_char*) buf;
580     for (i=0;(*p) && (*p!=ch) && (i<size); p++, i++)
581         ;
582     if ((*p == '\0') || (i>=size))
583         return NULL;
584     return p;
585 }
586
587 /*
588  * The packet at pd has a (len, string) pair.
589  * Copy the string to a buffer, and display it in the tree.
590  * Observe any limits you might cross.
591  *
592  * If anything is wrong, return -1, since -1 is not a valid string
593  * length. Else, return the number of chars processed.
594  */
595 static guint16
596 proto_add_icq_attr(proto_tree* tree, /* The tree to add to */
597                    const char* pd, /* Pointer to the field */
598                    const int offset, /* Offset from the start of packet */
599                    const int size, /* The number of bytes left in pd */
600                    char* descr) /* The description to use in the tree */
601 {
602     guint16 len;
603     char* data;
604     int left = size;
605     
606     if (size<sizeof(guint16))
607         return -1;
608     len = pletohs(pd);
609     left -= sizeof(guint16);
610     if (left<len) {
611         proto_tree_add_text(tree,
612                             offset,
613                             sizeof(guint16),
614                             "Length: %d", len);
615         return -1;
616     }
617                             
618     data = g_malloc(len);
619
620     strncpy(data, pd + sizeof(guint16), len);
621     data[len - 1] = '\0';
622
623     proto_tree_add_text(tree,
624                         offset,
625                         sizeof(guint16) + len,
626                         "%s[%d]: %s", descr, len, data);
627     g_free(data);
628
629     return len + sizeof(guint16);
630 }
631
632 static void
633 icqv5_decode_msgType(proto_tree* tree,
634                      const unsigned char* pd, /* From start of messageType */
635                      int offset,
636                      int size)
637 {
638     proto_item* ti = NULL;
639     proto_tree* subtree = NULL;
640     int left = size;
641     char *msgText = NULL;
642     guint16 msgType = -1;
643     guint16 msgLen = -1;
644     int i,j,n;
645     static char* auth_req_field_descr[] = {
646         "Nickname",
647         "First name",
648         "Last name",
649         "Email address",
650         "Unknown",
651         "Reason"};
652     static char* emain_field_descr[] = {
653         "Nickname",
654         "First name",
655         "Last name",
656         "Email address",
657         "Unknown",
658         "Text\n"
659     };
660     
661     enum {OFF_MSG_TYPE=0,
662           OFF_MSG_LEN=2,
663           OFF_MSG_TEXT=4};
664
665     
666     if (left >= sizeof(guint16)) {
667         msgType = pletohs(pd + OFF_MSG_TYPE);
668         left -= sizeof(guint16);
669     }
670     if (left >= sizeof(guint16)) {
671         msgLen = pletohs(pd + OFF_MSG_LEN);
672         left -= sizeof(guint16);
673     }
674
675     ti = proto_tree_add_text(tree,
676                              offset ,
677                              2,
678                              "Type: %d (%s)", msgType, findMsgType(msgType));
679     /* Create a new subtree */
680     subtree = proto_item_add_subtree(ti, ett_icq_body_parts);
681
682     switch(msgType) {
683     case 0xffff:           /* Field unknown */
684         break;
685     default:
686         fprintf(stderr, "Unknown msgType: %d (%04x)\n", msgType, msgType);
687         break;
688     case MSG_TEXT:
689         msgText = g_malloc(left + 1);
690         strncpy(msgText, pd + OFF_MSG_TEXT, left);
691         msgText[left] = '\0';
692         proto_tree_add_text(subtree,
693                             offset + OFF_MSG_TEXT,
694                             left,
695                             "Msg: %s", msgText);
696         g_free(msgText);
697         break;
698     case MSG_URL:
699         /* Two parts, a description and the URL. Separeted by FE */
700         for (i=0;i<left;i++) {
701             if (pd[OFF_MSG_TEXT + i] == 0xfe)
702                 break;
703         }
704         msgText = g_malloc(i + 1);
705         strncpy(msgText, pd + OFF_MSG_TEXT, i);
706         if (i==left)
707             msgText[i] = '\0';
708         else
709             msgText[i-1] = '\0';
710         proto_tree_add_text(subtree,
711                             offset + OFF_MSG_TEXT,
712                             i,
713                             "Description: %s", msgText);
714         if (i==left)
715             break;
716         msgText = g_realloc(msgText, left - i);
717         strncpy(msgText, pd + OFF_MSG_TEXT + i + 1, left - i - 1);
718         msgText[left - i] = '\0';
719         proto_tree_add_text(subtree,
720                             offset + OFF_MSG_TEXT,
721                             i,
722                             "URL: %s", msgText);
723         g_free(msgText);
724         break;
725     case MSG_EMAIL:
726         i = 0;
727         j = 0;
728         msgText = NULL;
729         for (n = 0; n < 6; n++) {
730             for (;
731                  (i<left) && (pd[OFF_MSG_TEXT+i]!=0xfe);
732                  i++)
733                 ;
734             if (i>j) {
735                 msgText = g_realloc(msgText, i-j);
736                 strncpy(msgText, pd + OFF_MSG_TEXT + j, i - j - 1);
737                 msgText[i-j-1] = '\0';
738                 proto_tree_add_text(subtree,
739                                     offset + OFF_MSG_TEXT + j,
740                                     i - j - 1,
741                                     "%s: %s", emain_field_descr[n], msgText);
742             } else {
743                 proto_tree_add_text(subtree,
744                                     offset + OFF_MSG_TEXT + j,
745                                     0,
746                                     "%s: %s", emain_field_descr[n], "(empty)");
747             }
748             j = ++i;
749         }
750         if (msgText != NULL)
751             g_free(msgText);
752         break;
753         
754     case MSG_AUTH:
755     {
756         /* Three bytes, first is a char signifying success */
757         unsigned char auth_suc = pd[OFF_MSG_LEN];
758         guint16 x1 = pd[OFF_MSG_LEN+1];
759         proto_tree_add_text(subtree,
760                             offset + OFF_MSG_LEN,
761                             1,
762                             "Authorization: (%d) %s",auth_suc,
763                             (auth_suc==0)?"Denied":"Allowed");
764         proto_tree_add_text(subtree,
765                             offset + OFF_MSG_LEN + 1,
766                             sizeof(guint16),
767                             "x1: 0x%04x",x1);
768         break;
769     }
770     case MSG_AUTH_REQ:
771         /* Six parts, separated by FE */
772         i = 0;
773         j = 0;
774         msgText = g_malloc(64);
775         for (n = 0; n < 6 && i<left; n++) {
776             while (i<left && pd[OFF_MSG_TEXT+i]!=0xfe)
777                 i++;
778             if (i<=left) {
779                 /* pd[OFF_MSG_TEXT+i] == 0xfe */
780                 if (i!=j) {   
781                     /* Otherwise, it'd be a null string */
782                     msgText = g_realloc(msgText, i - j);
783                     strncpy(msgText, pd + OFF_MSG_TEXT + j, i-j);
784                     msgText[i-j] = '\0';
785                     proto_tree_add_text(subtree,
786                                         offset + OFF_MSG_TEXT + j,
787                                         i - j,
788                                         "%s: %s", auth_req_field_descr[n], msgText);
789                 } else {
790                     proto_tree_add_text(subtree,
791                                         offset + OFF_MSG_TEXT + j,
792                                         i - j,
793                                         "%s: %s", auth_req_field_descr[n], "(null)");
794                 }
795                 j = ++i;
796                 /* i and j point after the 0xfe character */
797             }
798         }    
799
800         if (msgText != NULL)
801             g_free(msgText);
802         break;
803     case MSG_USER_ADDED:
804         /* Four parts, separated by FE */
805         i = 0;
806         j = 0;
807         /* This is necessary, because g_realloc does not behave like
808              * g_malloc if the first parameter == NULL */
809         msgText = g_malloc(64);
810         for (n = 0; n < 4 && i<left; n++) {
811             while (i<left && pd[OFF_MSG_TEXT+i]!=0xfe)
812                 i++;
813             if (i<=left) {
814                 /* pd[OFF_MSG_TEXT+i] == 0xfe */
815                 if (i!=j) {   
816                     /* Otherwise, it'd be a null string */
817                     msgText = g_realloc(msgText, i - j);
818                     strncpy(msgText, pd + OFF_MSG_TEXT + j, i-j);
819                     msgText[i-j] = '\0';
820                     proto_tree_add_text(subtree,
821                                         offset + OFF_MSG_TEXT + j,
822                                         i - j,
823                                         "%s: %s", auth_req_field_descr[n], msgText);
824                 } else {
825                     proto_tree_add_text(subtree,
826                                         offset + OFF_MSG_TEXT + j,
827                                         i - j,
828                                         "%s: %s", auth_req_field_descr[n], "(null)");
829                 }
830                 j = ++i;
831                 /* i and j point after the 0xfe character */
832             }
833         }    
834         if (msgText != NULL)
835             g_free(msgText);
836         break;
837     case MSG_CONTACTS:
838     {
839         u_char* p = (u_char*) &pd[OFF_MSG_TEXT];
840         u_char* pprev = p;
841         int sz = 0;            /* Size of the current element */
842         int n = 0;             /* The nth element */
843         int done = 0;          /* Number of chars processed */
844         u_char* msgText2 = NULL;
845         msgText = NULL;
846         /* Create a new subtree */
847         subtree = proto_item_add_subtree(ti, ett_icq_body_parts);
848         while (p!=NULL) {
849             p = strnchr(pprev, 0xfe, left);
850             
851             if (p!=NULL)
852                 sz = (int)(p - pprev);
853             else
854                 sz = left;
855             msgText = g_realloc(msgText, sz+1);
856             strncpy(msgText, pprev, sz);
857             msgText[sz] = '\0';
858             
859             if (n == 0) {
860                 /* The first element is the number of Nick/UIN pairs follow */
861                 proto_tree_add_text(subtree,
862                                     offset + OFF_MSG_TEXT + done,
863                                     sz,
864                                     "Number of pairs: %s", msgText);
865                 n++;
866             } else if (p!=NULL) {
867                 int svsz = sz;
868                 left -= (sz+1);
869                 pprev = p + 1;
870                 p = strnchr(pprev, 0xfe, left);
871                 if (p!=NULL)
872                     sz = (int)(p - pprev);
873                 else
874                     sz = left;
875                 msgText2 = g_malloc(sz+1);
876                 strncpy(msgText2, pprev, sz);
877                 msgText2[sz] = '\0';
878                 
879                 proto_tree_add_text(subtree,
880                                     offset + OFF_MSG_TEXT + done,
881                                     sz + svsz + 2,
882                                     "%s:%s", msgText, msgText2);
883                 n+=2;
884                 g_free(msgText2);
885             }
886             
887             left -= (sz+1);
888             pprev = p+1;
889         }
890         if (msgText != NULL)
891             g_free(msgText);
892         break;
893     }}
894 }
895
896 /*********************************
897  * 
898  * Client commands
899  *
900  *********************************/
901 static void
902 icqv5_cmd_ack(proto_tree* tree,/* Tree to put the data in */
903                      const u_char* pd, /* Packet content */
904                      int offset, /* Offset from the start of the packet to the content */
905                      int size)  /* Number of chars left to do */
906 {
907     guint32 random = pletohl(pd + CMD_ACK_RANDOM);
908     proto_tree* subtree;
909     proto_item* ti;
910
911     if (tree){
912         ti = proto_tree_add_item_format(tree,
913                                         hf_icq_cmd,
914                                         offset,
915                                         4,
916                                         CMD_ACK,
917                                         "Body");
918         subtree = proto_item_add_subtree(ti, ett_icq_body);
919         proto_tree_add_text(subtree,
920                             offset + CMD_ACK_RANDOM,
921                             4,
922                             "Random: 0x%08lx", random);
923     }
924 }
925
926 static void
927 icqv5_cmd_rand_search(proto_tree* tree,       /* Tree to put the data in */
928                       const u_char* pd,       /* Packet content */
929                       int offset,             /* Offset from the start of the packet to the content */
930                       int size)               /* Number of chars left to do */
931 {
932     guint16 group = pletohs(pd + CMD_RAND_SEARCH_GROUP);
933     proto_tree* subtree;
934     proto_item* ti;
935
936     static const char* groups[] = {
937         "Name",
938         "General",
939         "Romance",
940         "Games",
941         "Students",
942         "20 Something",
943         "30 Something",
944         "40 Something",
945         "50 or worse",
946         "Man want women",
947         "Women want men"
948     };
949     
950     if (tree){
951         ti = proto_tree_add_item_format(tree,
952                                         hf_icq_cmd,
953                                         offset,
954                                         4,
955                                         CMD_RAND_SEARCH,
956                                         "Body");
957         subtree = proto_item_add_subtree(ti, ett_icq_body);
958         if (group>0 && (group<=sizeof(groups)/sizeof(const char*)))
959             proto_tree_add_text(subtree,
960                                 offset + CMD_RAND_SEARCH_GROUP,
961                                 4,
962                                 "Group: (%d) %s", group, groups[group-1]);
963         else
964             proto_tree_add_text(subtree,
965                                 offset + CMD_RAND_SEARCH_GROUP,
966                                 4,
967                                 "Group: (%d)", group);
968     }
969 }
970
971 static void
972 icqv5_cmd_ack_messages(proto_tree* tree,/* Tree to put the data in */
973                        const u_char* pd,      /* Packet content */
974                        int offset,              /* Offset from the start of the packet to the content */
975                        int size)                  /* Number of chars left to do */
976 {
977     guint32 random = pletohl(pd + CMD_ACK_MESSAGES_RANDOM);
978     proto_tree* subtree;
979     proto_item* ti;
980
981     if (tree){
982         ti = proto_tree_add_item_format(tree,
983                                         hf_icq_cmd,
984                                         offset,
985                                         4,
986                                         CMD_ACK_MESSAGES,
987                                         "Body");
988         subtree = proto_item_add_subtree(ti, ett_icq_body);
989         proto_tree_add_text(subtree,
990                             offset + CMD_ACK_MESSAGES_RANDOM,
991                             4,
992                             "Random: 0x%08lx", random);
993     }
994 }
995
996 static void
997 icqv5_cmd_keep_alive(proto_tree* tree,/* Tree to put the data in */
998                        const u_char* pd,      /* Packet content */
999                        int offset,              /* Offset from the start of the packet to the content */
1000                        int size)                  /* Number of chars left to do */
1001 {
1002     guint32 random = pletohl(pd + CMD_KEEP_ALIVE_RANDOM);
1003     proto_tree* subtree;
1004     proto_item* ti;
1005
1006     if (tree){
1007         ti = proto_tree_add_item_format(tree,
1008                                         hf_icq_cmd,
1009                                         offset,
1010                                         4,
1011                                         CMD_KEEP_ALIVE,
1012                                         "Body");
1013         subtree = proto_item_add_subtree(ti, ett_icq_body);
1014         proto_tree_add_text(subtree,
1015                             offset + CMD_KEEP_ALIVE_RANDOM,
1016                             4,
1017                             "Random: 0x%08lx", random);
1018     }
1019 }
1020
1021 static void
1022 icqv5_cmd_send_text_code(proto_tree* tree,/* Tree to put the data in */
1023                          const u_char* pd,      /* Packet content */
1024                          int offset,              /* Offset from the start of the packet to the content */
1025                          int size)                  /* Number of chars left to do */
1026 {
1027     proto_tree* subtree;
1028     proto_item* ti;
1029     gint16 len = -1;
1030     guint16 x1 = -1;
1031     char* text = NULL;
1032     int left = size;            /* The amount of data left to analyse */
1033
1034     if (left>=sizeof(gint16)) {
1035         len = pletohs(pd+CMD_SEND_TEXT_CODE_LEN);
1036         left -= sizeof(gint16);
1037     }
1038     if (len>=0) {
1039         len = MIN(len, left);
1040         text = g_malloc(len+1);
1041         memcpy(text, pd + CMD_SEND_TEXT_CODE_TEXT, len);
1042         text[len] = '\0';
1043         left -= len;
1044     }
1045     if (left>=sizeof(gint16)) {
1046         x1 = pletohs(pd + size - left);
1047         left -= sizeof(gint16);
1048     }
1049     if (tree){
1050         ti = proto_tree_add_item_format(tree,
1051                                         hf_icq_cmd,
1052                                         offset,
1053                                         left,
1054                                         CMD_KEEP_ALIVE,
1055                                         "Body");
1056         subtree = proto_item_add_subtree(ti, ett_icq_body);
1057         proto_tree_add_text(subtree,
1058                             offset + CMD_SEND_TEXT_CODE_LEN,
1059                             2,
1060                             "Length: %d", len);
1061         proto_tree_add_text(subtree,
1062                             offset + CMD_SEND_TEXT_CODE_TEXT,
1063                             len,
1064                             "Text: %s",text);
1065         proto_tree_add_text(subtree,
1066                             offset + CMD_SEND_TEXT_CODE_TEXT + len,
1067                             2,
1068                             "X1: 0x%04x", x1);
1069     }
1070     if (text!=NULL)
1071         g_free(text);
1072 }
1073
1074 static void
1075 icqv5_cmd_add_to_list(proto_tree* tree,/* Tree to put the data in */
1076                       const u_char* pd,      /* Packet content */
1077                       int offset,            /* Offset from the start of the packet to the content */
1078                       int size)              /* Number of chars left to do */
1079 {
1080     guint32 uin = -1;
1081     proto_tree* subtree;
1082     proto_item* ti;
1083     if (size>=4)
1084         uin = pletohl(pd + CMD_ADD_TO_LIST);
1085     if (tree){
1086         ti = proto_tree_add_item_format(tree,
1087                                         hf_icq_cmd,
1088                                         offset,
1089                                         4,
1090                                         CMD_ADD_TO_LIST,
1091                                         "Body");
1092         subtree = proto_item_add_subtree(ti, ett_icq_body);
1093         proto_tree_add_text(subtree,
1094                             offset + CMD_ADD_TO_LIST_UIN,
1095                             4,
1096                             "UIN: %ld", uin);
1097     }
1098 }
1099
1100 static void
1101 icqv5_cmd_status_change(proto_tree* tree,/* Tree to put the data in */
1102                         const u_char* pd,       /* Packet content */
1103                         int offset,                /* Offset from the start of the packet to the content */
1104                         int size)                     /* Number of chars left to do */
1105 {
1106     guint32 status = -1;
1107     proto_tree* subtree;
1108     proto_item* ti;
1109
1110     if (size >= CMD_STATUS_CHANGE_STATUS + 4)
1111         status = pletohl(pd + CMD_STATUS_CHANGE_STATUS);
1112     if (tree){
1113         ti = proto_tree_add_item_format(tree,
1114                                         hf_icq_cmd,
1115                                         offset,
1116                                         4,
1117                                         CMD_STATUS_CHANGE,
1118                                         "Body");
1119         subtree = proto_item_add_subtree(ti, ett_icq_body);
1120         if (status!=-1)
1121             proto_tree_add_text(subtree,
1122                                 offset + CMD_STATUS_CHANGE_STATUS,
1123                                 4,
1124                                 "Status: %08x (%s)", status, findStatus(status));
1125     }
1126 }
1127
1128 static void
1129 icqv5_cmd_send_msg(proto_tree* tree,
1130                    const u_char* pd,
1131                    int offset,
1132                    int size,
1133                    int cmd)
1134 {
1135     proto_tree* subtree;
1136     proto_item* ti;
1137     guint32 receiverUIN = 0xffffffff;
1138     guint16 msgType = 0xffff;
1139     guint16 msgLen = 0xffff;
1140     int left = size;            /* left chars to do */
1141     
1142     if (left < 4)
1143         return;
1144     receiverUIN = pletohl(pd + CMD_SEND_MSG_RECV_UIN);
1145     left -= 4;
1146     if (left < 2) 
1147         return;
1148     msgType = pletohs(pd + CMD_SEND_MSG_MSG_TYPE);
1149     left -= 2;
1150     if (left < 2) 
1151         return;
1152     msgLen = pletohs(pd + CMD_SEND_MSG_MSG_LEN);
1153     left -= 2;
1154
1155     if (tree) {
1156         ti = proto_tree_add_item_format(tree,
1157                                         hf_icq_cmd,
1158                                         offset,
1159                                         size,
1160                                         cmd,
1161                                         "Body");
1162         subtree = proto_item_add_subtree(ti, ett_icq_body);
1163         proto_tree_add_text(subtree,
1164                             offset + CMD_SEND_MSG_RECV_UIN,
1165                             4,
1166                             "Receiver UIN: %ld", receiverUIN);
1167         proto_tree_add_text(subtree,
1168                             offset + CMD_SEND_MSG_MSG_LEN,
1169                             2,
1170                             "Length: %d", msgLen);
1171
1172         icqv5_decode_msgType(subtree,
1173                              pd + CMD_SEND_MSG_MSG_TYPE,
1174                              offset + CMD_SEND_MSG_MSG_TYPE,
1175                              left+4); /* There are 4 bytes more... */
1176     }
1177 }
1178
1179 static void
1180 icqv5_cmd_login(proto_tree* tree,
1181                 const u_char* pd,
1182                 int offset,
1183                 int size)
1184 {
1185     proto_item* ti;
1186     proto_tree* subtree;
1187     time_t theTime = -1;
1188     guint32 port = -1;
1189     guint32 passwdLen = -1;
1190     char* password = NULL;
1191     const u_char *ipAddrp = NULL;
1192     guint32 status = -1;
1193     guint32 left = size;
1194
1195     if (left>=4) {
1196         theTime = pletohl(pd + CMD_LOGIN_TIME);
1197     }
1198     if (left>=8) {
1199         port = pletohl(pd + CMD_LOGIN_PORT);
1200     }
1201     if (left>=10) {
1202         passwdLen = pletohs(pd + CMD_LOGIN_PASSLEN);
1203     }
1204     if (left>=10+passwdLen) {
1205         password = g_malloc(passwdLen + 1);
1206         strncpy(password, pd + CMD_LOGIN_PASSWD, passwdLen);
1207         password[passwdLen] = '\0';
1208     }
1209
1210     if (left>=10+passwdLen+CMD_LOGIN_IP+4) {
1211         ipAddrp = pd + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_IP;
1212     }
1213     if (left>=10+passwdLen+CMD_LOGIN_STATUS+4) {
1214         status = pletohs(pd + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_STATUS);
1215     }
1216     if (tree) {
1217         ti = proto_tree_add_item_format(tree,
1218                                         hf_icq_cmd,
1219                                         offset,
1220                                         size,
1221                                         CMD_SEND_MSG,
1222                                         "Body");
1223         subtree = proto_item_add_subtree(ti, ett_icq_body);
1224         if (theTime!=-1)
1225             proto_tree_add_text(subtree,
1226                                 offset + CMD_LOGIN_TIME,
1227                                 4,
1228                                 "Time: %d = %s", theTime, ctime(&theTime));
1229         if (port!=-1)
1230             proto_tree_add_text(subtree,
1231                                 offset + CMD_LOGIN_PORT,
1232                                 4,
1233                                 "Port: %d", port);
1234         if ((passwdLen!=-1) && (password!=NULL))
1235             proto_tree_add_text(subtree,
1236                                 offset + CMD_LOGIN_PASSLEN,
1237                                 2 + passwdLen,
1238                                 "Passwd: %s", password);
1239         if (ipAddrp!=NULL)
1240             proto_tree_add_text(subtree,
1241                                 offset + CMD_LOGIN_PASSWD + CMD_LOGIN_IP,
1242                                 4,
1243                                 "IP: %s", ip_to_str(ipAddrp));
1244         if (status!=-1)
1245             proto_tree_add_text(subtree,
1246                                 offset + CMD_LOGIN_PASSWD + CMD_LOGIN_IP,
1247                                 4,
1248                                 "Status: %s", findStatus(status));
1249     }
1250     if (password!=NULL)
1251         g_free(password);
1252 }
1253
1254 static void
1255 icqv5_cmd_contact_list(proto_tree* tree,
1256                        const u_char* pd,
1257                        int offset,
1258                        int size)
1259 {
1260     proto_tree* subtree;
1261     proto_item* ti;
1262     unsigned char num = -1;
1263     int i, left;
1264     guint32 uin;
1265     const u_char* p = NULL;
1266
1267     if (size >= CMD_CONTACT_LIST_NUM + 1) 
1268         num = pd[CMD_CONTACT_LIST_NUM];
1269
1270     if (tree) {
1271         ti = proto_tree_add_item_format(tree,
1272                                         hf_icq_cmd,
1273                                         offset,
1274                                         size,
1275                                         CMD_CONTACT_LIST,
1276                                         "Body");
1277         subtree = proto_item_add_subtree(ti, ett_icq_body);
1278         proto_tree_add_text(subtree,
1279                             offset + CMD_CONTACT_LIST,
1280                             1,
1281                             "Number of uins: %d", num);
1282         /*
1283          * A sequence of num times UIN follows
1284          */
1285         offset += (CMD_CONTACT_LIST_NUM + 1);
1286         left = size;
1287         p = &pd[CMD_CONTACT_LIST_NUM + 1];
1288         for (i = 0; (i<num) && (left>0);i++) {
1289             if (left>=4) {
1290                 uin = pletohl(p);
1291                 proto_tree_add_text(subtree,
1292                                     offset,
1293                                     4,
1294                                     "UIN[%d]: %ld",i,uin);
1295                 p += 4;
1296                 offset += 4;
1297                 left -= 4;
1298             }
1299         }
1300     }
1301 }
1302
1303 static void
1304 icqv5_cmd_no_params(proto_tree* tree,/* Tree to put the data in */
1305                     const u_char* pd,      /* Packet content */
1306                     int offset,            /* Offset from the start of the packet to the content */
1307                     int size,              /* Number of chars left to do */
1308                     int cmd)
1309 {
1310     proto_tree* subtree;
1311     proto_item* ti;
1312
1313     if (tree){
1314         ti = proto_tree_add_item_format(tree,
1315                                         hf_icq_cmd,
1316                                         offset,
1317                                         0,
1318                                         cmd,
1319                                         "Body");
1320         subtree = proto_item_add_subtree(ti, ett_icq_body);
1321         proto_tree_add_text(subtree,
1322                             offset,
1323                             0,
1324                             "No parameters");
1325     }
1326 }
1327
1328 /**********************
1329  *
1330  * Server commands
1331  *
1332  **********************
1333  */
1334 static void
1335 icqv5_srv_no_params(proto_tree* tree,/* Tree to put the data in */
1336                     const u_char* pd,      /* Packet content */
1337                     int offset,            /* Offset from the start of the packet to the content */
1338                     int size,              /* Number of chars left to do */
1339                     int cmd)
1340 {
1341     proto_tree* subtree;
1342     proto_item* ti;
1343
1344     if (tree){
1345         ti = proto_tree_add_item_format(tree,
1346                                         hf_icq_cmd,
1347                                         offset,
1348                                         0,
1349                                         cmd,
1350                                         "Body");
1351         subtree = proto_item_add_subtree(ti, ett_icq_body);
1352         proto_tree_add_text(subtree,
1353                             offset,
1354                             0,
1355                             "No Parameters");
1356     }
1357 }
1358
1359 static void
1360 icqv5_srv_login_reply(proto_tree* tree,/* Tree to put the data in */
1361                       const u_char* pd,       /* Packet content */
1362                       int offset,                /* Offset from the start of the packet to the content */
1363                       int size)                     /* Number of chars left to do */
1364 {
1365     proto_tree* subtree;
1366     proto_item* ti;
1367     const u_char *ipAddrp = NULL;
1368
1369     if (size >= SRV_LOGIN_REPLY_IP + 4) 
1370         ipAddrp = &pd[SRV_LOGIN_REPLY_IP];
1371
1372     if (tree) {
1373         ti = proto_tree_add_item_format(tree,
1374                                         hf_icq_cmd,
1375                                         offset,
1376                                         SRV_LOGIN_REPLY_IP + 8,
1377                                         SRV_LOGIN_REPLY,
1378                                         "Body");
1379         subtree = proto_item_add_subtree(ti, ett_icq_body);
1380         proto_tree_add_text(subtree,
1381                             offset + SRV_LOGIN_REPLY_IP,
1382                             4,
1383                             "IP: %s", ip_to_str(ipAddrp));
1384     }
1385 }
1386
1387 static void
1388 icqv5_srv_user_online(proto_tree* tree,/* Tree to put the data in */
1389                       const u_char* pd,       /* Packet content */
1390                       int offset,                /* Offset from the start of the packet to the content */
1391                       int size)                     /* Number of chars left to do */
1392 {
1393     proto_tree* subtree;
1394     proto_item* ti;
1395     guint32 uin = -1;
1396     const u_char *ipAddrp = NULL;
1397     guint32 port = -1;
1398     const u_char *realipAddrp = NULL;
1399     guint32 status = -1;
1400     guint32 version = -1;
1401
1402     if (size >= SRV_USER_ONL_UIN + 4)
1403         uin = pletohl(pd + SRV_USER_ONL_UIN);
1404     
1405     if (size >= SRV_USER_ONL_IP + 4) 
1406         ipAddrp = &pd[SRV_USER_ONL_IP];
1407
1408     if (size >= SRV_USER_ONL_PORT + 4)
1409         port = pletohl(pd + SRV_USER_ONL_PORT);
1410
1411     if (size >= SRV_USER_ONL_REALIP + 4)
1412         realipAddrp = &pd[SRV_USER_ONL_REALIP];
1413
1414     if (size >= SRV_USER_ONL_STATUS + 2)
1415         status = pletohs(pd + SRV_USER_ONL_STATUS);
1416
1417     /*
1418      * Kojak: Hypothesis is that this field might be an encoding for the
1419      * version used by the UIN that changed. To test this, I included
1420      * this line to the code.
1421      */
1422     if (size >= SRV_USER_ONL_X2 + 4)
1423         version = pletohl(pd + SRV_USER_ONL_X2);
1424
1425     if (tree) {
1426         ti = proto_tree_add_item_format(tree,
1427                                         hf_icq_cmd,
1428                                         offset,
1429                                         SRV_LOGIN_REPLY_IP + 8,
1430                                         SRV_LOGIN_REPLY,
1431                                         "Body");
1432         subtree = proto_item_add_subtree(ti, ett_icq_body);
1433         proto_tree_add_text(subtree,
1434                             offset + SRV_USER_ONL_UIN,
1435                             4,
1436                             "UIN: %d", uin);
1437         proto_tree_add_text(subtree,
1438                             offset + SRV_USER_ONL_IP,
1439                             4,
1440                             "IP: %s", ip_to_str(ipAddrp));
1441         proto_tree_add_text(subtree,
1442                             offset + SRV_USER_ONL_PORT,
1443                             4,
1444                             "Port: %d", port);
1445         proto_tree_add_text(subtree,
1446                             offset + SRV_USER_ONL_REALIP,
1447                             4,
1448                             "RealIP: %s", ip_to_str(realipAddrp));
1449         proto_tree_add_text(subtree,
1450                             offset + SRV_USER_ONL_STATUS,
1451                             2,
1452                             "Status: %s", findStatus(status));
1453         proto_tree_add_text(subtree,
1454                             offset + SRV_USER_ONL_X2,
1455                             4,
1456                             "Version: %08x", version);
1457     }
1458 }
1459
1460 static void
1461 icqv5_srv_user_offline(proto_tree* tree,/* Tree to put the data in */
1462                       const u_char* pd,       /* Packet content */
1463                       int offset,                /* Offset from the start of the packet to the content */
1464                       int size)                     /* Number of chars left to do */
1465 {
1466     proto_tree* subtree;
1467     proto_item* ti;
1468     guint32 uin = -1;
1469
1470     if (size >= SRV_USER_OFFLINE + 4) 
1471         uin = pletohl(&pd[SRV_USER_OFFLINE]);
1472
1473     if (tree) {
1474         ti = proto_tree_add_item_format(tree,
1475                                         hf_icq_cmd,
1476                                         offset,
1477                                         SRV_USER_OFFLINE_UIN + 4,
1478                                         SRV_USER_OFFLINE,
1479                                         "Body");
1480         subtree = proto_item_add_subtree(ti, ett_icq_body);
1481         proto_tree_add_text(subtree,
1482                             offset + SRV_USER_OFFLINE_UIN,
1483                             4,
1484                             "UIN: %d", uin);
1485     }
1486 }
1487
1488 static void
1489 icqv5_srv_multi(proto_tree* tree,/* Tree to put the data in */
1490                 const u_char* pd,      /* Packet content */
1491                 int offset,            /* Offset from the start of the packet to the content */
1492                 int size,              /* Number of chars left to do */
1493                 frame_data* fd)
1494 {
1495     proto_tree* subtree;
1496     proto_item* ti;
1497     unsigned char num = -1;
1498     guint16 pktSz;
1499     int i, left;
1500     const u_char* p = NULL;
1501
1502     if (size >= SRV_MULTI_NUM + 1) 
1503         num = pd[SRV_MULTI_NUM];
1504
1505     if (tree) {
1506         ti = proto_tree_add_item_format(tree,
1507                                         hf_icq_cmd,
1508                                         offset,
1509                                         size,
1510                                         SRV_MULTI,
1511                                         "Body");
1512         subtree = proto_item_add_subtree(ti, ett_icq_body);
1513         proto_tree_add_text(subtree,
1514                             offset + SRV_MULTI_NUM,
1515                             1,
1516                             "Number of pkts: %d", num);
1517         /*
1518          * A sequence of num times ( pktsize, packetData) follows
1519          */
1520         offset += (SRV_MULTI_NUM + 1);
1521         left = size;
1522         p = &pd[SRV_MULTI_NUM + 1];
1523         for (i = 0; (i<num) && (left>0);i++) {
1524             if (left>=2) {
1525                 pktSz = pletohs(p);
1526                 p += 2;
1527                 offset += 2;
1528                 left -= 2;
1529                 if (left>=pktSz) {
1530                     dissect_icqv5Server(p, offset, fd, subtree, pktSz);
1531                     p += pktSz;
1532                     offset += pktSz;
1533                     left -= pktSz;
1534                 }
1535             }
1536         }
1537     }
1538 }
1539
1540 static void
1541 icqv5_srv_meta_user(proto_tree* tree,      /* Tree to put the data in */
1542                     const u_char* pd,      /* Packet content */
1543                     int offset,            /* Offset from the start of the packet to the content */
1544                     int size)              /* Number of chars left to do */
1545 {
1546 #if 0
1547     proto_tree* subtree = NULL;
1548 #endif
1549     proto_tree* sstree = NULL;
1550     proto_item* ti = NULL;
1551     int left = size;
1552     const char* p = pd;
1553
1554     guint16 subcmd = -1;
1555     unsigned char result = -1;
1556
1557     if (size>=SRV_META_USER_SUBCMD + 2)
1558         subcmd = pletohs(pd+SRV_META_USER_SUBCMD);
1559     if (size>=SRV_META_USER_RESULT + 1)
1560         result = pd[SRV_META_USER_RESULT];
1561
1562     if (tree) {
1563 #if 0
1564         ti = proto_tree_add_item_format(tree,
1565                                         hf_icq_cmd,
1566                                         offset,
1567                                         size,
1568                                         SRV_META_USER,
1569                                         "Body");
1570         subtree = proto_item_add_subtree(ti, ett_icq_body);
1571         ti = proto_tree_add_text(subtree,
1572                                  offset + SRV_META_USER_SUBCMD,
1573                                  2,
1574                                  "%s", findSubCmd(subcmd));
1575         proto_tree_add_text(subtree,
1576                             offset + SRV_META_USER_RESULT,
1577                             1,
1578                             "%s", (result==0x0a)?"Success":"Failure");
1579         sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
1580 #else
1581         ti = proto_tree_add_text(tree,
1582                                  offset + SRV_META_USER_SUBCMD,
1583                                  2,
1584                                  "%s", findSubCmd(subcmd));
1585         sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
1586         proto_tree_add_text(sstree,
1587                             offset + SRV_META_USER_RESULT,
1588                             1,
1589                             "%s", (result==0x0a)?"Success":"Failure");
1590 #endif
1591
1592         /* Skip the META_USER header */
1593         left -= 3;
1594         p += 3;
1595
1596         switch(subcmd) {
1597         case META_EX_USER_FOUND:
1598         {
1599             /* This is almost the same as META_USER_FOUND,
1600              * however, there's an extra length field
1601              */
1602             guint16 pktLen = -1;
1603
1604             /* Read the lenght field */
1605             pktLen = pletohs(p);
1606             proto_tree_add_text(sstree,
1607                                 offset + size - left,
1608                                 sizeof(guint16),
1609                                 "Length: %d", pktLen);
1610             
1611             p += sizeof(guint16); left -= sizeof(guint16);
1612         }
1613         case META_USER_FOUND:
1614         {
1615             /* The goto mentioned in this block should be local to this
1616              * block if C'd allow it.
1617              *
1618              * They are used to "implement" a poorman's exception handling
1619              */
1620             guint32 uin = -1;
1621             int len = 0;
1622             char *descr[] = {
1623                 "Nick",
1624                 "First name",
1625                 "Last name",
1626                 "Email",
1627                 NULL};
1628             char** d = descr;
1629             guint16 x2 = -1;
1630             guint32 x3 = -1;
1631             unsigned char auth;
1632             /*
1633              * Read UIN
1634              */
1635             if (left<sizeof(guint32))
1636                 break;
1637             uin = pletohl(p);
1638             proto_tree_add_text(sstree,
1639                                 offset + size - left,
1640                                 sizeof(guint32),
1641                                 "UIN: %ld", uin);
1642             p+=sizeof(guint32);left-=sizeof(guint32);
1643
1644             for ( ; *d!=NULL; d++) {
1645                 len = proto_add_icq_attr(sstree,
1646                                          p,
1647                                          offset + size - left,
1648                                          left,
1649                                          *d);
1650                 if (len == -1)
1651                     return;
1652                 p += len; left -= len;
1653             }
1654             /* Get the authorize setting */
1655             if (left<sizeof(unsigned char))
1656                 break;
1657             auth = *p;
1658             proto_tree_add_text(sstree,
1659                                 offset + size - left,
1660                                 sizeof(guint16),
1661                                 "authorization: %s", (auth==0x01)?"Neccessary":"Who needs it");
1662             p++; left--;
1663             /* Get x2 */
1664             if (left<sizeof(guint16))
1665                 break;
1666             x2 = pletohs(p);
1667             proto_tree_add_text(sstree,
1668                                 offset + size - left,
1669                                 sizeof(guint16),
1670                                 "x2: %04x", x2);
1671             p+=sizeof(guint16);left-=sizeof(guint16);
1672             /* Get x3 */
1673             if (left<sizeof(guint32))
1674                 break;
1675             x3 = pletohl(p);
1676             proto_tree_add_text(sstree,
1677                                 offset + size - left,
1678                                 sizeof(guint32),
1679                                 "x3: %08x", x3);
1680             p+=sizeof(guint32);left-=sizeof(guint32);
1681             break;
1682         }
1683         case META_ABOUT:
1684         {
1685             int len;
1686             char* about = NULL;
1687             /* Get the about information */
1688             if (left<sizeof(guint16))
1689                 break;
1690             len = pletohs(p);
1691             p+=sizeof(guint16);left-=sizeof(guint16);
1692             if ((len<=0) || (left<len))
1693                 break;
1694             about = g_malloc(len);
1695             strncpy(about, p, len);
1696             proto_tree_add_text(sstree,
1697                                 offset + size - left,
1698                                 sizeof(guint16)+len,
1699                                 "About(%d): %s", len, about);
1700             p+=len;left-=len;
1701             left -= 3;
1702             g_free(about);
1703             break;
1704         }
1705         case META_USER_INFO:
1706         {
1707             /* The goto mentioned in this block should be local to this
1708              * block if C'd allow it.
1709              *
1710              * They are used to "implement" a poorman's exception handling
1711              */
1712             static const char* descr[] = {
1713                 "Nick",
1714                 "First name",
1715                 "Last name",
1716                 "Primary email",
1717                 "Secundary email",
1718                 "Old email",
1719                 "City",
1720                 "State",
1721                 "Phone",
1722                 "Fax",
1723                 "Street",
1724                 "Cellphone",
1725                 "Zip",
1726                 NULL};
1727             const char** d = descr;
1728             char* item = NULL;
1729             guint16 country;
1730             unsigned char user_timezone = -1;
1731             unsigned char auth = -1;
1732             int len = 0;
1733 #if 0
1734             /* Get the uin */
1735             if (left<sizeof(guint32))
1736                 break;
1737             uin = pletohl(p);
1738             proto_tree_add_text(sstree,
1739                                 offset + size - left,
1740                                 sizeof(guint32),
1741                                 "UIN: %ld", uin);
1742             p+=sizeof(guint32);left-=sizeof(guint32);
1743 #endif
1744             
1745             /*
1746              * Get every field from the description
1747              */
1748             while ((*d)!=NULL) {
1749                 if (left<sizeof(guint16))
1750                     break;
1751                 len = pletohs(p);
1752                 p+=sizeof(guint16);left-=sizeof(guint16);
1753                 if ((len<0) || (left<len))
1754                     break;
1755                 if (len>0) {
1756                     item = g_malloc(len);
1757                     strncpy(item, p, len);
1758                     proto_tree_add_text(sstree,
1759                                         offset + size - left - sizeof(guint16),
1760                                         sizeof(guint16)+len,
1761                                         "%s(%d): %s",*d, len, item);
1762                     g_free(item);
1763                     p+=len;left-=len;
1764                 }
1765                 d++;
1766             }
1767             /* Get country code */
1768             if (left<sizeof(guint16))
1769                 break;
1770             country = pletohs(p);
1771             proto_tree_add_text(sstree,
1772                                 offset + size - left,
1773                                 sizeof(guint16),
1774                                 "Countrycode: %d", country);
1775             p+=sizeof(guint16); left-=sizeof(guint16);
1776             /* Get the timezone setting */
1777             if (left<sizeof(unsigned char))
1778                 break;
1779             user_timezone = *p;
1780             proto_tree_add_text(sstree,
1781                                 offset + size - left,
1782                                 sizeof(unsigned char),
1783                                 "Timezone: %d", user_timezone);
1784             p++; left--;
1785             /* Get the authorize setting */
1786             if (left<sizeof(unsigned char))
1787                 break;
1788             auth = *p;
1789             proto_tree_add_text(sstree,
1790                                 offset + size - left,
1791                                 sizeof(unsigned char),
1792                                 "Authorization: (%d) %s",
1793                                 auth, (auth==0)?"No":"Yes");
1794             p++; left--;
1795             /* Get the webaware setting */
1796             if (left<sizeof(unsigned char))
1797                 break;
1798             auth = *p;
1799             proto_tree_add_text(sstree,
1800                                 offset + size - left,
1801                                 sizeof(unsigned char),
1802                                 "Webaware: (%d) %s",
1803                                 auth, (auth==0)?"No":"Yes");
1804             p++; left--;
1805             /* Get the authorize setting */
1806             if (left<sizeof(unsigned char))
1807                 break;
1808             auth = *p;
1809             proto_tree_add_text(sstree,
1810                                 offset + size - left,
1811                                 sizeof(unsigned char),
1812                                 "HideIP: (%d) %s",
1813                                 auth, (auth==0)?"No":"Yes");
1814             p++; left--;
1815             break;
1816         }
1817         default:
1818             /* This information is already printed in the tree */
1819             fprintf(stderr, "Meta subcmd: %04x\n", subcmd);
1820             break;
1821         }
1822     }
1823 }
1824
1825 static void
1826 icqv5_srv_recv_message(proto_tree* tree,      /* Tree to put the data in */
1827                        const u_char* pd,      /* Packet content */
1828                        int offset,            /* Offset from the start of the packet to the content */
1829                        int size)              /* Number of chars left to do */
1830 {
1831     proto_tree* subtree = NULL;
1832     proto_item* ti = NULL;
1833     int left = size;
1834     guint32 uin = -1;
1835     guint16 year = -1;
1836     unsigned char month = -1;
1837     unsigned char day = -1;
1838     unsigned char hour = -1;
1839     unsigned char minute = -1;
1840     
1841     if (tree) {
1842         ti = proto_tree_add_item_format(tree,
1843                                         hf_icq_cmd,
1844                                         offset,
1845                                         4,
1846                                         SRV_RECV_MESSAGE,
1847                                         "Body");
1848         subtree = proto_item_add_subtree(ti, ett_icq_body);
1849         if (left>=sizeof(guint32)) {
1850             uin = pletohl(pd + SRV_RECV_MSG_UIN);
1851             proto_tree_add_item_format(subtree,
1852                                        hf_icq_uin,
1853                                        offset + SRV_RECV_MSG_UIN,
1854                                        sizeof(guint32),
1855                                        uin,
1856                                        "UIN: %d", uin);
1857             left -= sizeof(guint32);
1858         } else
1859             return;
1860         if (left>=(sizeof(guint16)+4*sizeof(unsigned char))) {
1861             year = pletohs(pd + SRV_RECV_MSG_YEAR);
1862             month = pd[SRV_RECV_MSG_MONTH];
1863             day = pd[SRV_RECV_MSG_DAY];
1864             hour = pd[SRV_RECV_MSG_HOUR];
1865             minute = pd[SRV_RECV_MSG_MINUTE];
1866
1867             proto_tree_add_text(subtree,
1868                                 offset + SRV_RECV_MSG_YEAR,
1869                                 sizeof(guint16) + 4*sizeof(unsigned char),
1870                                 "Time: %d-%d-%d %02d:%02d",
1871                                 day, month, year, hour, minute);
1872             
1873             left -= (sizeof(guint16)+4*sizeof(unsigned char));
1874         } else
1875             return;
1876         icqv5_decode_msgType(subtree,
1877                              pd + SRV_RECV_MSG_MSG_TYPE,
1878                              offset + SRV_RECV_MSG_MSG_TYPE,
1879                              left);
1880     }
1881 }
1882
1883 static void
1884 icqv5_srv_rand_user(proto_tree* tree,      /* Tree to put the data in */
1885                        const u_char* pd,      /* Packet content */
1886                        int offset,            /* Offset from the start of the packet to the content */
1887                        int size)              /* Number of chars left to do */
1888 {
1889     proto_tree* subtree = NULL;
1890     proto_item* ti = NULL;
1891     guint32 uin = -1;
1892     const unsigned char* IP = NULL;
1893     guint32 port = -1;
1894     const unsigned char* realIP = NULL;
1895     unsigned char commClass = -1;
1896     guint32 status;
1897     guint16 tcpVer;
1898     int left = size;
1899     
1900     if (tree) {
1901         ti = proto_tree_add_item_format(tree,
1902                                         hf_icq_cmd,
1903                                         offset,
1904                                         SRV_RAND_USER_TCP_VER + 2,
1905                                         SRV_RAND_USER,
1906                                         "Body");
1907         subtree = proto_item_add_subtree(ti, ett_icq_body);
1908         /* guint32 UIN */
1909         if (left<sizeof(guint32))
1910             return;
1911         uin = pletohl(pd + SRV_RAND_USER_UIN);
1912         proto_tree_add_text(subtree,
1913                             offset + SRV_RAND_USER_UIN,
1914                             sizeof(guint32),
1915                             "UIN: %ld", uin);
1916         left -= sizeof(guint32);
1917         /* guint32 IP */
1918         if (left<sizeof(guint32))
1919             return;
1920         IP = pd + SRV_RAND_USER_IP;
1921         proto_tree_add_text(subtree,
1922                             offset + SRV_RAND_USER_IP,
1923                             sizeof(guint32),
1924                             "IP: %s", ip_to_str(IP));
1925         left -= sizeof(guint32);
1926         /* guint32 portNum */
1927         if (left<sizeof(guint32))
1928             return;
1929         port = pletohs(pd + SRV_RAND_USER_PORT);
1930         proto_tree_add_text(subtree,
1931                             offset + SRV_RAND_USER_UIN,
1932                             sizeof(guint32),
1933                             "Port: %ld", port);
1934         left -= sizeof(guint32);
1935         /* guint32 realIP */                        
1936         if (left<sizeof(guint32))
1937             return;
1938         realIP = pd + SRV_RAND_USER_REAL_IP;
1939         proto_tree_add_text(subtree,
1940                             offset + SRV_RAND_USER_REAL_IP,
1941                             sizeof(guint32),
1942                             "RealIP: %s", ip_to_str(realIP));
1943         left -= sizeof(guint32);
1944         /* guit16 Communication Class */
1945         if (left<sizeof(unsigned char))
1946             return;
1947         commClass = pd[SRV_RAND_USER_CLASS];
1948         proto_tree_add_text(subtree,
1949                             offset + SRV_RAND_USER_CLASS,
1950                             sizeof(unsigned char),
1951                             "Class: %s", (commClass!=4)?"User to User":"Through Server");
1952         left -= sizeof(unsigned char);
1953         /* guint32 status */
1954         if (left<sizeof(guint32))
1955             return;
1956         status = pletohs(pd + SRV_RAND_USER_STATUS);
1957         proto_tree_add_text(subtree,
1958                             offset + SRV_RAND_USER_STATUS,
1959                             sizeof(guint32),
1960                             "Status: (%ld) %s", status, findStatus(status));
1961         /* guint16 tcpVersion */
1962         if (left<sizeof(guint16))
1963             return;
1964         tcpVer = pletohs(pd + SRV_RAND_USER_TCP_VER);
1965         proto_tree_add_text(subtree,
1966                             offset + SRV_RAND_USER_TCP_VER,
1967                             sizeof(guint16),
1968                             "TCPVersion: %d", tcpVer);
1969         left -= sizeof(guint16);
1970     }
1971 }
1972
1973 /*
1974  * Dissect all the v5 client traffic. This is encrypted, so be careful.
1975  */
1976 static void
1977 dissect_icqv5Client(const u_char *pd,
1978                     int offset,
1979                     frame_data *fd, 
1980                     proto_tree *tree)
1981 {
1982     proto_tree *icq_tree = NULL;
1983     proto_tree *icq_header_tree = NULL;
1984     proto_tree *icq_decode_tree = NULL;
1985     proto_item *ti = NULL;
1986
1987     guint16 version = -1, cmd = -1;
1988     guint16 seqnum1 = 0 , seqnum2 = 0;
1989     guint32 uin = -1, sessionid = -1;
1990     guint32 key = -1;
1991     guint16 pktsize = -1;       /* The size of the ICQ content */
1992     u_char decr_pd[1600];       /* Decrypted content, size should be dynamic */
1993     
1994     pktsize = fd->pkt_len - offset;
1995     /* First copy the memory, we don't want to overwrite the old content */
1996     memcpy(decr_pd, &pd[offset], pktsize);
1997     if (fd->pkt_len > fd->cap_len) {
1998         pktsize -= (fd->pkt_len - fd->cap_len);
1999     }
2000     if (pktsize>0x14) {
2001         key = get_v5key(decr_pd, pktsize);
2002         decrypt_v5(decr_pd, pktsize, key);
2003     
2004         /* This information only makes sense in the decrypted version */
2005         uin = pletohl(&decr_pd[ICQ5_CL_UIN]);
2006         cmd = pletohs(&decr_pd[ICQ5_CL_CMD]);
2007         sessionid = pletohl(&decr_pd[ICQ5_CL_SESSIONID]);
2008         version = pletohs(&decr_pd[ICQ_VERSION]);
2009         seqnum1 = pletohs(&decr_pd[ICQ5_CL_SEQNUM1]);
2010         seqnum2 = pletohs(&decr_pd[ICQ5_CL_SEQNUM2]);
2011
2012         if (check_col(fd, COL_INFO))
2013             col_add_fstr(fd, COL_INFO, "ICQv5 %s", findClientCmd(cmd));
2014     }
2015     
2016     if (tree) {
2017         ti = proto_tree_add_item_format(tree,
2018                                  proto_icq,
2019                                  offset,
2020                                  pktsize, NULL,
2021                                  "ICQv5 %s (len %d)",
2022                                  findClientCmd(cmd),
2023                                  pktsize);
2024         icq_tree = proto_item_add_subtree(ti, ett_icq);
2025         ti = proto_tree_add_item_format(icq_tree,
2026                                         hf_icq_type,
2027                                         offset,
2028                                         ICQ5_CL_HDRSIZE,
2029                                         ICQ5_client,
2030                                         "Header");
2031         icq_header_tree = proto_item_add_subtree(ti, ett_icq_header);
2032                                         
2033         proto_tree_add_item_format(icq_header_tree,
2034                                    hf_icq_sessionid,
2035                                    offset+ICQ5_CL_SESSIONID,
2036                                    4,
2037                                    sessionid,
2038                                    "Session ID: 0x%08x",
2039                                    sessionid);
2040         proto_tree_add_item_format(icq_header_tree,
2041                                    hf_icq_checkcode,
2042                                    offset+ICQ5_CL_CHECKCODE,
2043                                    4,
2044                                    key,
2045                                    "Key: 0x%08x",
2046                                    key);
2047         proto_tree_add_item_format(icq_header_tree,
2048                                    hf_icq_uin,
2049                                    offset+ICQ5_CL_UIN,
2050                                    4,
2051                                    uin,
2052                                    "UIN: %ld (0x%08X)",
2053                                    uin, uin);
2054         proto_tree_add_text(icq_header_tree,
2055                             offset + ICQ5_CL_SEQNUM1,
2056                             2,
2057                             "Seqnum1: 0x%04x", seqnum1);
2058         proto_tree_add_text(icq_header_tree,
2059                             offset + ICQ5_CL_SEQNUM1,
2060                             2,
2061                             "Seqnum2: 0x%04x", seqnum2);
2062         switch(cmd) {
2063         case CMD_ACK:
2064             icqv5_cmd_ack(icq_tree,
2065                           decr_pd + ICQ5_CL_HDRSIZE,
2066                           offset + ICQ5_CL_HDRSIZE,
2067                           pktsize - ICQ5_CL_HDRSIZE);
2068             break;
2069         case CMD_SEND_MSG:
2070         case CMD_MSG_TO_NEW_USER:
2071             icqv5_cmd_send_msg(icq_tree,
2072                                decr_pd + ICQ5_CL_HDRSIZE,
2073                                offset + ICQ5_CL_HDRSIZE,
2074                                pktsize - ICQ5_CL_HDRSIZE,
2075                                cmd);
2076             break;
2077         case CMD_RAND_SEARCH:
2078             icqv5_cmd_rand_search(icq_tree,
2079                                   decr_pd + ICQ5_CL_HDRSIZE,
2080                                   offset + ICQ5_CL_HDRSIZE,
2081                                   pktsize - ICQ5_CL_HDRSIZE);
2082             break;
2083         case CMD_LOGIN:
2084             icqv5_cmd_login(icq_tree,
2085                             decr_pd + ICQ5_CL_HDRSIZE,
2086                             offset + ICQ5_CL_HDRSIZE,
2087                             pktsize - ICQ5_CL_HDRSIZE);
2088             break;
2089         case CMD_SEND_TEXT_CODE:
2090             icqv5_cmd_send_text_code(icq_tree,
2091                                      decr_pd + ICQ5_CL_HDRSIZE,
2092                                      offset + ICQ5_CL_HDRSIZE,
2093                                      pktsize - ICQ5_CL_HDRSIZE);
2094             break;
2095         case CMD_STATUS_CHANGE:
2096             icqv5_cmd_status_change(icq_tree,
2097                                     decr_pd + ICQ5_CL_HDRSIZE,
2098                                     offset + ICQ5_CL_HDRSIZE,
2099                                     pktsize - ICQ5_CL_HDRSIZE);
2100             break;
2101         case CMD_ACK_MESSAGES:
2102             icqv5_cmd_ack_messages(icq_tree,
2103                                    decr_pd + ICQ5_CL_HDRSIZE,
2104                                    offset + ICQ5_CL_HDRSIZE,
2105                                    pktsize - ICQ5_CL_HDRSIZE);
2106             break;
2107         case CMD_KEEP_ALIVE:
2108             icqv5_cmd_keep_alive(icq_tree,
2109                                  decr_pd + ICQ5_CL_HDRSIZE,
2110                                  offset + ICQ5_CL_HDRSIZE,
2111                                  pktsize - ICQ5_CL_HDRSIZE);
2112             break;
2113         case CMD_ADD_TO_LIST:
2114             icqv5_cmd_add_to_list(icq_tree,
2115                                    decr_pd + ICQ5_CL_HDRSIZE,
2116                                    offset + ICQ5_CL_HDRSIZE,
2117                                    pktsize - ICQ5_CL_HDRSIZE);
2118             break;
2119         case CMD_CONTACT_LIST:
2120             icqv5_cmd_contact_list(icq_tree,
2121                                    decr_pd + ICQ5_CL_HDRSIZE,
2122                                    offset + ICQ5_CL_HDRSIZE,
2123                                    pktsize - ICQ5_CL_HDRSIZE);
2124             break;
2125         case CMD_META_USER:
2126         case CMD_REG_NEW_USER:
2127         case CMD_QUERY_SERVERS:
2128         case CMD_QUERY_ADDONS:
2129             icqv5_cmd_no_params(icq_tree,
2130                                 decr_pd + ICQ5_CL_HDRSIZE,
2131                                 offset + ICQ5_CL_HDRSIZE,
2132                                 pktsize - ICQ5_CL_HDRSIZE,
2133                                 cmd);
2134             break;
2135         default:
2136             proto_tree_add_item_format(icq_tree,
2137                                        hf_icq_cmd,
2138                                        offset+ICQ5_CL_CMD,
2139                                        2,
2140                                        cmd,
2141                                        "Command: %d (%s)",
2142                                        cmd, findClientCmd(cmd));
2143             fprintf(stderr,"Missing: %s\n", findClientCmd(cmd));
2144             break;
2145         }
2146         ti = proto_tree_add_text(icq_tree,
2147                                  offset,
2148                                  pktsize,
2149                                  "Decoded packet");
2150         icq_decode_tree = proto_item_add_subtree(ti,
2151                                                  ett_icq_decode);
2152         proto_tree_add_hexdump(icq_decode_tree, offset, decr_pd, pktsize);
2153
2154     }
2155 }
2156
2157 static void
2158 dissect_icqv5Server(const u_char *pd,
2159                     int offset,
2160                     frame_data *fd, 
2161                     proto_tree *tree,
2162                     guint32 pktsize)
2163 {
2164     /* Server traffic is easy, not encrypted */
2165     proto_tree *icq_tree = NULL;
2166     proto_tree *icq_header_tree = NULL;
2167     proto_tree *icq_decode_tree = NULL;
2168     proto_item *ti = NULL;
2169     const u_char* decr_pd;
2170     int changeCol = (pktsize==(guint32)-1);
2171
2172     guint16 version, cmd;
2173     guint32 uin, sessionid;
2174     guint16 seq_num1, seq_num2;
2175     guint32 checkcode;
2176     
2177     uin = pletohl(&pd[ICQ5_SRV_UIN]);
2178     sessionid = pletohl(&pd[ICQ5_SRV_SESSIONID]);
2179     cmd = pletohs(&pd[ICQ5_SRV_CMD]);
2180     version = pletohs(&pd[ICQ_VERSION]);
2181     checkcode = pletohl(&pd[ICQ5_SRV_CHECKCODE]);
2182     seq_num1 = pletohs(&pd[ICQ5_SRV_SEQNUM1]);
2183     seq_num2 = pletohs(&pd[ICQ5_SRV_SEQNUM2]);
2184     if (pktsize == -1)
2185         pktsize = fd->pkt_len - offset;
2186     decr_pd = pd;
2187     
2188     if (changeCol && check_col(fd, COL_INFO))
2189         col_add_fstr(fd, COL_INFO, "ICQv5 %s", findServerCmd(cmd));
2190
2191     if (tree) {
2192         ti = proto_tree_add_item_format(tree,
2193                                         proto_icq,
2194                                         offset,
2195                                         pktsize, NULL,
2196                                         "ICQv5 %s (len %d)",
2197                                         findServerCmd(cmd),
2198                                         pktsize);
2199         
2200         icq_tree = proto_item_add_subtree(ti, ett_icq);
2201
2202         ti = proto_tree_add_item_format(icq_tree,
2203                                         hf_icq_type,
2204                                         offset,
2205                                         ICQ5_SRV_HDRSIZE,
2206                                         ICQ5_server,
2207                                         "Header");
2208         icq_header_tree = proto_item_add_subtree(ti, ett_icq_header);
2209                                         
2210         proto_tree_add_item_format(icq_header_tree,
2211                                    hf_icq_sessionid,
2212                                    offset+ICQ5_SRV_SESSIONID,
2213                                    4,
2214                                    sessionid,
2215                                    "Session ID: 0x%08x",
2216                                    sessionid);
2217         proto_tree_add_text(icq_header_tree,
2218                             offset+ICQ5_SRV_SEQNUM1,
2219                             2,
2220                             "Seq Number1: 0x%04x",
2221                             seq_num1);
2222         proto_tree_add_text(icq_header_tree,
2223                             offset+ICQ5_SRV_SEQNUM2,
2224                             2,
2225                             "Seq Number2: 0x%04x",
2226                             seq_num2);
2227         proto_tree_add_item_format(icq_header_tree,
2228                                    hf_icq_uin,
2229                                    offset+ICQ5_SRV_UIN,
2230                                    4,
2231                                    uin,
2232                                    "UIN: %ld",
2233                                    uin);
2234         proto_tree_add_item_format(icq_header_tree,
2235                                    hf_icq_checkcode,
2236                                    offset+ICQ5_SRV_CHECKCODE,
2237                                    4,
2238                                    checkcode,
2239                                    "Checkcode: 0x%08x",
2240                                    checkcode);
2241         switch (cmd) {
2242         case SRV_RAND_USER:
2243             icqv5_srv_rand_user(icq_tree,
2244                                decr_pd + ICQ5_SRV_HDRSIZE,
2245                                offset + ICQ5_SRV_HDRSIZE,
2246                                pktsize - ICQ5_SRV_HDRSIZE);
2247             break;
2248         case SRV_SYS_DELIVERED_MESS:
2249             /* The message structures are all the same. Why not run
2250              * the same routine? */
2251             icqv5_cmd_send_msg(icq_tree,
2252                                decr_pd + ICQ5_SRV_HDRSIZE,
2253                                offset + ICQ5_SRV_HDRSIZE,
2254                                pktsize - ICQ5_SRV_HDRSIZE,
2255                                cmd);
2256             break;
2257         case SRV_USER_ONLINE:
2258             icqv5_srv_user_online(icq_tree,
2259                                decr_pd + ICQ5_SRV_HDRSIZE,
2260                                offset + ICQ5_SRV_HDRSIZE,
2261                                pktsize - ICQ5_SRV_HDRSIZE);
2262             break;
2263         case SRV_USER_OFFLINE:
2264             icqv5_srv_user_offline(icq_tree,
2265                                decr_pd + ICQ5_SRV_HDRSIZE,
2266                                offset + ICQ5_SRV_HDRSIZE,
2267                                pktsize - ICQ5_SRV_HDRSIZE);
2268             break;
2269         case SRV_LOGIN_REPLY:
2270             icqv5_srv_login_reply(icq_tree,
2271                                decr_pd + ICQ5_SRV_HDRSIZE,
2272                                offset + ICQ5_SRV_HDRSIZE,
2273                                pktsize - ICQ5_SRV_HDRSIZE);
2274             break;
2275         case SRV_META_USER:
2276             icqv5_srv_meta_user(icq_tree,
2277                                decr_pd + ICQ5_SRV_HDRSIZE,
2278                                offset + ICQ5_SRV_HDRSIZE,
2279                                pktsize - ICQ5_SRV_HDRSIZE);
2280             break;
2281         case SRV_RECV_MESSAGE:
2282             icqv5_srv_recv_message(icq_tree,
2283                                    decr_pd + ICQ5_SRV_HDRSIZE,
2284                                    offset + ICQ5_SRV_HDRSIZE,
2285                                    pktsize - ICQ5_SRV_HDRSIZE);
2286             break;
2287         case SRV_MULTI:
2288             icqv5_srv_multi(icq_tree,
2289                             decr_pd + ICQ5_SRV_HDRSIZE,
2290                             offset + ICQ5_SRV_HDRSIZE,
2291                             pktsize - ICQ5_SRV_HDRSIZE,
2292                             fd);
2293             break;
2294         case SRV_ACK:
2295         case SRV_GO_AWAY:
2296         case SRV_NEW_UIN:
2297         case SRV_BAD_PASS:
2298         case SRV_UPDATE_SUCCESS:
2299             icqv5_srv_no_params(icq_tree,
2300                                 decr_pd + ICQ5_SRV_HDRSIZE,
2301                                 offset + ICQ5_SRV_HDRSIZE,
2302                                 pktsize - ICQ5_SRV_HDRSIZE,
2303                                 cmd);
2304             break;
2305         default:
2306             proto_tree_add_item_format(icq_tree,
2307                                        hf_icq_cmd,
2308                                        offset + ICQ5_SRV_CMD,
2309                                        2,
2310                                        cmd,
2311                                        "Command: %d (%s)",
2312                                        cmd, findServerCmd(cmd));
2313             fprintf(stderr,"Missing: %s\n", findServerCmd(cmd));
2314             break;
2315         }
2316
2317         ti = proto_tree_add_text(icq_tree,
2318                                  offset,
2319                                  pktsize,
2320                                  "Decoded packet");
2321         icq_decode_tree = proto_item_add_subtree(ti,
2322                                                  ett_icq_decode);
2323         proto_tree_add_hexdump(icq_decode_tree, offset, decr_pd, pktsize);
2324     }
2325 }
2326
2327 void dissect_icqv5(const u_char *pd,
2328                    int offset,
2329                    frame_data *fd, 
2330                    proto_tree *tree)
2331 {
2332   guint32 unknown = pletohl(&pd[offset + ICQ5_UNKNOWN]);
2333   
2334   if (check_col(fd, COL_PROTOCOL))
2335       col_add_str(fd, COL_PROTOCOL, "ICQv5 (UDP)");
2336   if (check_col(fd, COL_INFO))
2337       col_add_str(fd, COL_INFO, "ICQv5 packet");
2338   if (unknown == 0x0L) {
2339       dissect_icqv5Client(pd, offset, fd, tree);
2340   } else {
2341       dissect_icqv5Server(pd + offset, offset, fd, tree, (guint32) -1);
2342   }
2343 }
2344
2345 void dissect_icq(const u_char *pd,
2346                  int offset,
2347                  frame_data *fd, 
2348                  proto_tree *tree)
2349 {
2350   int version = 0;
2351
2352   version = pletohs(&pd[offset + ICQ_VERSION]);
2353   switch (version) {
2354   case 0x0005:
2355       dissect_icqv5(pd, offset, fd, tree);
2356       break;
2357   case 0x0002:
2358       dissect_icqv2(pd, offset, fd, tree);
2359       break;
2360   default:
2361       fprintf(stderr, "ICQ: Unknown version (%d)\n", version);
2362       break;
2363   }
2364 }
2365
2366 /* registration with the filtering engine */
2367 void
2368 proto_register_icq(void)
2369 {
2370     static hf_register_info hf[] = {
2371         { &hf_icq_type,
2372           {"Type", "icq.type", FT_UINT16, BASE_DEC, NULL, 0x0, ""}},
2373         { &hf_icq_uin,
2374           {"UIN", "icq.uin", FT_UINT32, BASE_DEC, NULL, 0x0, ""}},
2375         { &hf_icq_sessionid,
2376           {"SessionID", "icq.sessionid", FT_UINT32, BASE_HEX, NULL, 0x0, ""}},
2377         { &hf_icq_cmd,
2378           {"Command", "icq.cmd", FT_UINT16, BASE_DEC, NULL, 0x0, ""}},
2379         { &hf_icq_checkcode,
2380           {"Checkcode", "icq.checkcode", FT_UINT32, BASE_HEX, NULL, 0x0, ""}},
2381         { &hf_icq_decode,
2382           {"Decode", "icq.decode", FT_STRING, BASE_NONE, NULL, 0x0, ""}}
2383     };
2384     static gint *ett[] = {
2385         &ett_icq,
2386         &ett_icq_header,
2387         &ett_icq_decode,
2388         &ett_icq_body,
2389         &ett_icq_body_parts,
2390     };
2391     
2392     proto_icq = proto_register_protocol ("ICQ Protocol", "icq");
2393     
2394     proto_register_field_array(proto_icq, hf, array_length(hf));
2395
2396     proto_register_subtree_array(ett, array_length(ett));
2397 }