Change dissect_ppp() to accept offset.
[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.13 2000/03/14 07:12:23 gram 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: %u", 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[%u]: %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: %u (%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: %u (%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: (%u) %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_uint_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%08x", 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_uint_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: (%u) %s", group, groups[group-1]);
963         else
964             proto_tree_add_text(subtree,
965                                 offset + CMD_RAND_SEARCH_GROUP,
966                                 4,
967                                 "Group: (%u)", 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_uint_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%08x", 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_uint_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%08x", 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 = NULL;
1028     proto_item* ti = NULL;
1029     guint16 len = 0;
1030     guint16 x1 = -1;
1031     char* text;
1032     int left = size;            /* The amount of data left to analyse */
1033
1034     if (tree){
1035         ti = proto_tree_add_uint_format(tree,
1036                                         hf_icq_cmd,
1037                                         offset,
1038                                         left,
1039                                         CMD_KEEP_ALIVE,
1040                                         "Body");
1041     }
1042
1043     if (left<sizeof(guint16))
1044         return;
1045     len = pletohs(pd+CMD_SEND_TEXT_CODE_LEN);
1046     left -= sizeof(gint16);
1047     if (tree){
1048         subtree = proto_item_add_subtree(ti, ett_icq_body);
1049         proto_tree_add_text(subtree,
1050                             offset + CMD_SEND_TEXT_CODE_LEN,
1051                             2,
1052                             "Length: %d", len);
1053     }
1054
1055     if (len>0) {
1056         len = MIN(len, left);
1057         text = g_malloc(len+1);
1058         memcpy(text, pd + CMD_SEND_TEXT_CODE_TEXT, len);
1059         text[len] = '\0';
1060         left -= len;
1061         if (tree){
1062             proto_tree_add_text(subtree,
1063                             offset + CMD_SEND_TEXT_CODE_TEXT,
1064                             len,
1065                             "Text: %s",text);
1066         }
1067         g_free(text);
1068     }
1069
1070     if (left<sizeof(gint16))
1071         return;
1072
1073     x1 = pletohs(pd + size - left);
1074     left -= sizeof(gint16);
1075     if (tree){
1076         proto_tree_add_text(subtree,
1077                             offset + CMD_SEND_TEXT_CODE_TEXT + len,
1078                             2,
1079                             "X1: 0x%04x", x1);
1080     }
1081 }
1082
1083 static void
1084 icqv5_cmd_add_to_list(proto_tree* tree,/* Tree to put the data in */
1085                       const u_char* pd,      /* Packet content */
1086                       int offset,            /* Offset from the start of the packet to the content */
1087                       int size)              /* Number of chars left to do */
1088 {
1089     guint32 uin = -1;
1090     proto_tree* subtree;
1091     proto_item* ti;
1092     if (size>=4)
1093         uin = pletohl(pd + CMD_ADD_TO_LIST);
1094     if (tree){
1095         ti = proto_tree_add_uint_format(tree,
1096                                         hf_icq_cmd,
1097                                         offset,
1098                                         4,
1099                                         CMD_ADD_TO_LIST,
1100                                         "Body");
1101         subtree = proto_item_add_subtree(ti, ett_icq_body);
1102         proto_tree_add_text(subtree,
1103                             offset + CMD_ADD_TO_LIST_UIN,
1104                             4,
1105                             "UIN: %u", uin);
1106     }
1107 }
1108
1109 static void
1110 icqv5_cmd_status_change(proto_tree* tree,/* Tree to put the data in */
1111                         const u_char* pd,       /* Packet content */
1112                         int offset,                /* Offset from the start of the packet to the content */
1113                         int size)                     /* Number of chars left to do */
1114 {
1115     guint32 status = -1;
1116     proto_tree* subtree;
1117     proto_item* ti;
1118
1119     if (size >= CMD_STATUS_CHANGE_STATUS + 4)
1120         status = pletohl(pd + CMD_STATUS_CHANGE_STATUS);
1121     if (tree){
1122         ti = proto_tree_add_uint_format(tree,
1123                                         hf_icq_cmd,
1124                                         offset,
1125                                         4,
1126                                         CMD_STATUS_CHANGE,
1127                                         "Body");
1128         subtree = proto_item_add_subtree(ti, ett_icq_body);
1129         if (status!=-1)
1130             proto_tree_add_text(subtree,
1131                                 offset + CMD_STATUS_CHANGE_STATUS,
1132                                 4,
1133                                 "Status: %08x (%s)", status, findStatus(status));
1134     }
1135 }
1136
1137 static void
1138 icqv5_cmd_send_msg(proto_tree* tree,
1139                    const u_char* pd,
1140                    int offset,
1141                    int size,
1142                    int cmd)
1143 {
1144     proto_tree* subtree;
1145     proto_item* ti;
1146     guint32 receiverUIN = 0xffffffff;
1147     guint16 msgType = 0xffff;
1148     guint16 msgLen = 0xffff;
1149     int left = size;            /* left chars to do */
1150     
1151     if (left < 4)
1152         return;
1153     receiverUIN = pletohl(pd + CMD_SEND_MSG_RECV_UIN);
1154     left -= 4;
1155     if (left < 2) 
1156         return;
1157     msgType = pletohs(pd + CMD_SEND_MSG_MSG_TYPE);
1158     left -= 2;
1159     if (left < 2) 
1160         return;
1161     msgLen = pletohs(pd + CMD_SEND_MSG_MSG_LEN);
1162     left -= 2;
1163
1164     if (tree) {
1165         ti = proto_tree_add_uint_format(tree,
1166                                         hf_icq_cmd,
1167                                         offset,
1168                                         size,
1169                                         cmd,
1170                                         "Body");
1171         subtree = proto_item_add_subtree(ti, ett_icq_body);
1172         proto_tree_add_text(subtree,
1173                             offset + CMD_SEND_MSG_RECV_UIN,
1174                             4,
1175                             "Receiver UIN: %u", receiverUIN);
1176         proto_tree_add_text(subtree,
1177                             offset + CMD_SEND_MSG_MSG_LEN,
1178                             2,
1179                             "Length: %u", msgLen);
1180
1181         icqv5_decode_msgType(subtree,
1182                              pd + CMD_SEND_MSG_MSG_TYPE,
1183                              offset + CMD_SEND_MSG_MSG_TYPE,
1184                              left+4); /* There are 4 bytes more... */
1185     }
1186 }
1187
1188 static void
1189 icqv5_cmd_login(proto_tree* tree,
1190                 const u_char* pd,
1191                 int offset,
1192                 int size)
1193 {
1194     proto_item* ti;
1195     proto_tree* subtree;
1196     time_t theTime = -1;
1197     guint32 port = -1;
1198     guint32 passwdLen = -1;
1199     char* password = NULL;
1200     const u_char *ipAddrp = NULL;
1201     guint32 status = -1;
1202     guint32 left = size;
1203
1204     if (left>=4) {
1205         theTime = pletohl(pd + CMD_LOGIN_TIME);
1206     }
1207     if (left>=8) {
1208         port = pletohl(pd + CMD_LOGIN_PORT);
1209     }
1210     if (left>=10) {
1211         passwdLen = pletohs(pd + CMD_LOGIN_PASSLEN);
1212     }
1213     if (left>=10+passwdLen) {
1214         password = g_malloc(passwdLen + 1);
1215         strncpy(password, pd + CMD_LOGIN_PASSWD, passwdLen);
1216         password[passwdLen] = '\0';
1217     }
1218
1219     if (left>=10+passwdLen+CMD_LOGIN_IP+4) {
1220         ipAddrp = pd + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_IP;
1221     }
1222     if (left>=10+passwdLen+CMD_LOGIN_STATUS+4) {
1223         status = pletohs(pd + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_STATUS);
1224     }
1225     if (tree) {
1226         ti = proto_tree_add_uint_format(tree,
1227                                         hf_icq_cmd,
1228                                         offset,
1229                                         size,
1230                                         CMD_SEND_MSG,
1231                                         "Body");
1232         subtree = proto_item_add_subtree(ti, ett_icq_body);
1233         if (theTime!=-1)
1234             proto_tree_add_text(subtree,
1235                                 offset + CMD_LOGIN_TIME,
1236                                 4,
1237                                 "Time: %ld = %s", (long)theTime, ctime(&theTime));
1238         if (port!=-1)
1239             proto_tree_add_text(subtree,
1240                                 offset + CMD_LOGIN_PORT,
1241                                 4,
1242                                 "Port: %u", port);
1243         if ((passwdLen!=-1) && (password!=NULL))
1244             proto_tree_add_text(subtree,
1245                                 offset + CMD_LOGIN_PASSLEN,
1246                                 2 + passwdLen,
1247                                 "Passwd: %s", password);
1248         if (ipAddrp!=NULL)
1249             proto_tree_add_text(subtree,
1250                                 offset + CMD_LOGIN_PASSWD + CMD_LOGIN_IP,
1251                                 4,
1252                                 "IP: %s", ip_to_str(ipAddrp));
1253         if (status!=-1)
1254             proto_tree_add_text(subtree,
1255                                 offset + CMD_LOGIN_PASSWD + CMD_LOGIN_IP,
1256                                 4,
1257                                 "Status: %s", findStatus(status));
1258     }
1259     if (password!=NULL)
1260         g_free(password);
1261 }
1262
1263 static void
1264 icqv5_cmd_contact_list(proto_tree* tree,
1265                        const u_char* pd,
1266                        int offset,
1267                        int size)
1268 {
1269     proto_tree* subtree;
1270     proto_item* ti;
1271     unsigned char num = -1;
1272     int i, left;
1273     guint32 uin;
1274     const u_char* p = NULL;
1275
1276     if (size >= CMD_CONTACT_LIST_NUM + 1) 
1277         num = pd[CMD_CONTACT_LIST_NUM];
1278
1279     if (tree) {
1280         ti = proto_tree_add_uint_format(tree,
1281                                         hf_icq_cmd,
1282                                         offset,
1283                                         size,
1284                                         CMD_CONTACT_LIST,
1285                                         "Body");
1286         subtree = proto_item_add_subtree(ti, ett_icq_body);
1287         proto_tree_add_text(subtree,
1288                             offset + CMD_CONTACT_LIST,
1289                             1,
1290                             "Number of uins: %u", num);
1291         /*
1292          * A sequence of num times UIN follows
1293          */
1294         offset += (CMD_CONTACT_LIST_NUM + 1);
1295         left = size;
1296         p = &pd[CMD_CONTACT_LIST_NUM + 1];
1297         for (i = 0; (i<num) && (left>0);i++) {
1298             if (left>=4) {
1299                 uin = pletohl(p);
1300                 proto_tree_add_text(subtree,
1301                                     offset,
1302                                     4,
1303                                     "UIN[%d]: %u",i,uin);
1304                 p += 4;
1305                 offset += 4;
1306                 left -= 4;
1307             }
1308         }
1309     }
1310 }
1311
1312 static void
1313 icqv5_cmd_no_params(proto_tree* tree,/* Tree to put the data in */
1314                     const u_char* pd,      /* Packet content */
1315                     int offset,            /* Offset from the start of the packet to the content */
1316                     int size,              /* Number of chars left to do */
1317                     int cmd)
1318 {
1319     proto_tree* subtree;
1320     proto_item* ti;
1321
1322     if (tree){
1323         ti = proto_tree_add_uint_format(tree,
1324                                         hf_icq_cmd,
1325                                         offset,
1326                                         0,
1327                                         cmd,
1328                                         "Body");
1329         subtree = proto_item_add_subtree(ti, ett_icq_body);
1330         proto_tree_add_text(subtree,
1331                             offset,
1332                             0,
1333                             "No parameters");
1334     }
1335 }
1336
1337 /**********************
1338  *
1339  * Server commands
1340  *
1341  **********************
1342  */
1343 static void
1344 icqv5_srv_no_params(proto_tree* tree,/* Tree to put the data in */
1345                     const u_char* pd,      /* Packet content */
1346                     int offset,            /* Offset from the start of the packet to the content */
1347                     int size,              /* Number of chars left to do */
1348                     int cmd)
1349 {
1350     proto_tree* subtree;
1351     proto_item* ti;
1352
1353     if (tree){
1354         ti = proto_tree_add_uint_format(tree,
1355                                         hf_icq_cmd,
1356                                         offset,
1357                                         0,
1358                                         cmd,
1359                                         "Body");
1360         subtree = proto_item_add_subtree(ti, ett_icq_body);
1361         proto_tree_add_text(subtree,
1362                             offset,
1363                             0,
1364                             "No Parameters");
1365     }
1366 }
1367
1368 static void
1369 icqv5_srv_login_reply(proto_tree* tree,/* Tree to put the data in */
1370                       const u_char* pd,       /* Packet content */
1371                       int offset,                /* Offset from the start of the packet to the content */
1372                       int size)                     /* Number of chars left to do */
1373 {
1374     proto_tree* subtree;
1375     proto_item* ti;
1376     const u_char *ipAddrp = NULL;
1377
1378     if (size >= SRV_LOGIN_REPLY_IP + 4) 
1379         ipAddrp = &pd[SRV_LOGIN_REPLY_IP];
1380
1381     if (tree) {
1382         ti = proto_tree_add_uint_format(tree,
1383                                         hf_icq_cmd,
1384                                         offset,
1385                                         SRV_LOGIN_REPLY_IP + 8,
1386                                         SRV_LOGIN_REPLY,
1387                                         "Body");
1388         subtree = proto_item_add_subtree(ti, ett_icq_body);
1389         proto_tree_add_text(subtree,
1390                             offset + SRV_LOGIN_REPLY_IP,
1391                             4,
1392                             "IP: %s", ip_to_str(ipAddrp));
1393     }
1394 }
1395
1396 static void
1397 icqv5_srv_user_online(proto_tree* tree,/* Tree to put the data in */
1398                       const u_char* pd,       /* Packet content */
1399                       int offset,                /* Offset from the start of the packet to the content */
1400                       int size)                     /* Number of chars left to do */
1401 {
1402     proto_tree* subtree;
1403     proto_item* ti;
1404     guint32 uin = -1;
1405     const u_char *ipAddrp = NULL;
1406     guint32 port = -1;
1407     const u_char *realipAddrp = NULL;
1408     guint32 status = -1;
1409     guint32 version = -1;
1410
1411     if (size >= SRV_USER_ONL_UIN + 4)
1412         uin = pletohl(pd + SRV_USER_ONL_UIN);
1413     
1414     if (size >= SRV_USER_ONL_IP + 4) 
1415         ipAddrp = &pd[SRV_USER_ONL_IP];
1416
1417     if (size >= SRV_USER_ONL_PORT + 4)
1418         port = pletohl(pd + SRV_USER_ONL_PORT);
1419
1420     if (size >= SRV_USER_ONL_REALIP + 4)
1421         realipAddrp = &pd[SRV_USER_ONL_REALIP];
1422
1423     if (size >= SRV_USER_ONL_STATUS + 2)
1424         status = pletohs(pd + SRV_USER_ONL_STATUS);
1425
1426     /*
1427      * Kojak: Hypothesis is that this field might be an encoding for the
1428      * version used by the UIN that changed. To test this, I included
1429      * this line to the code.
1430      */
1431     if (size >= SRV_USER_ONL_X2 + 4)
1432         version = pletohl(pd + SRV_USER_ONL_X2);
1433
1434     if (tree) {
1435         ti = proto_tree_add_uint_format(tree,
1436                                         hf_icq_cmd,
1437                                         offset,
1438                                         SRV_LOGIN_REPLY_IP + 8,
1439                                         SRV_LOGIN_REPLY,
1440                                         "Body");
1441         subtree = proto_item_add_subtree(ti, ett_icq_body);
1442         proto_tree_add_text(subtree,
1443                             offset + SRV_USER_ONL_UIN,
1444                             4,
1445                             "UIN: %u", uin);
1446         proto_tree_add_text(subtree,
1447                             offset + SRV_USER_ONL_IP,
1448                             4,
1449                             "IP: %s", ip_to_str(ipAddrp));
1450         proto_tree_add_text(subtree,
1451                             offset + SRV_USER_ONL_PORT,
1452                             4,
1453                             "Port: %u", port);
1454         proto_tree_add_text(subtree,
1455                             offset + SRV_USER_ONL_REALIP,
1456                             4,
1457                             "RealIP: %s", ip_to_str(realipAddrp));
1458         proto_tree_add_text(subtree,
1459                             offset + SRV_USER_ONL_STATUS,
1460                             2,
1461                             "Status: %s", findStatus(status));
1462         proto_tree_add_text(subtree,
1463                             offset + SRV_USER_ONL_X2,
1464                             4,
1465                             "Version: %08x", version);
1466     }
1467 }
1468
1469 static void
1470 icqv5_srv_user_offline(proto_tree* tree,/* Tree to put the data in */
1471                       const u_char* pd,       /* Packet content */
1472                       int offset,                /* Offset from the start of the packet to the content */
1473                       int size)                     /* Number of chars left to do */
1474 {
1475     proto_tree* subtree;
1476     proto_item* ti;
1477     guint32 uin = -1;
1478
1479     if (size >= SRV_USER_OFFLINE + 4) 
1480         uin = pletohl(&pd[SRV_USER_OFFLINE]);
1481
1482     if (tree) {
1483         ti = proto_tree_add_uint_format(tree,
1484                                         hf_icq_cmd,
1485                                         offset,
1486                                         SRV_USER_OFFLINE_UIN + 4,
1487                                         SRV_USER_OFFLINE,
1488                                         "Body");
1489         subtree = proto_item_add_subtree(ti, ett_icq_body);
1490         proto_tree_add_text(subtree,
1491                             offset + SRV_USER_OFFLINE_UIN,
1492                             4,
1493                             "UIN: %u", uin);
1494     }
1495 }
1496
1497 static void
1498 icqv5_srv_multi(proto_tree* tree,/* Tree to put the data in */
1499                 const u_char* pd,      /* Packet content */
1500                 int offset,            /* Offset from the start of the packet to the content */
1501                 int size,              /* Number of chars left to do */
1502                 frame_data* fd)
1503 {
1504     proto_tree* subtree;
1505     proto_item* ti;
1506     unsigned char num = -1;
1507     guint16 pktSz;
1508     int i, left;
1509     const u_char* p = NULL;
1510
1511     if (size >= SRV_MULTI_NUM + 1) 
1512         num = pd[SRV_MULTI_NUM];
1513
1514     if (tree) {
1515         ti = proto_tree_add_uint_format(tree,
1516                                         hf_icq_cmd,
1517                                         offset,
1518                                         size,
1519                                         SRV_MULTI,
1520                                         "Body");
1521         subtree = proto_item_add_subtree(ti, ett_icq_body);
1522         proto_tree_add_text(subtree,
1523                             offset + SRV_MULTI_NUM,
1524                             1,
1525                             "Number of pkts: %u", num);
1526         /*
1527          * A sequence of num times ( pktsize, packetData) follows
1528          */
1529         offset += (SRV_MULTI_NUM + 1);
1530         left = size;
1531         p = &pd[SRV_MULTI_NUM + 1];
1532         for (i = 0; (i<num) && (left>0);i++) {
1533             if (left>=2) {
1534                 pktSz = pletohs(p);
1535                 p += 2;
1536                 offset += 2;
1537                 left -= 2;
1538                 if (left>=pktSz) {
1539                     dissect_icqv5Server(p, offset, fd, subtree, pktSz);
1540                     p += pktSz;
1541                     offset += pktSz;
1542                     left -= pktSz;
1543                 }
1544             }
1545         }
1546     }
1547 }
1548
1549 static void
1550 icqv5_srv_meta_user(proto_tree* tree,      /* Tree to put the data in */
1551                     const u_char* pd,      /* Packet content */
1552                     int offset,            /* Offset from the start of the packet to the content */
1553                     int size)              /* Number of chars left to do */
1554 {
1555 #if 0
1556     proto_tree* subtree = NULL;
1557 #endif
1558     proto_tree* sstree = NULL;
1559     proto_item* ti = NULL;
1560     int left = size;
1561     const char* p = pd;
1562
1563     guint16 subcmd = -1;
1564     unsigned char result = -1;
1565
1566     if (size>=SRV_META_USER_SUBCMD + 2)
1567         subcmd = pletohs(pd+SRV_META_USER_SUBCMD);
1568     if (size>=SRV_META_USER_RESULT + 1)
1569         result = pd[SRV_META_USER_RESULT];
1570
1571     if (tree) {
1572 #if 0
1573         ti = proto_tree_add_uint_format(tree,
1574                                         hf_icq_cmd,
1575                                         offset,
1576                                         size,
1577                                         SRV_META_USER,
1578                                         "Body");
1579         subtree = proto_item_add_subtree(ti, ett_icq_body);
1580         ti = proto_tree_add_text(subtree,
1581                                  offset + SRV_META_USER_SUBCMD,
1582                                  2,
1583                                  "%s", findSubCmd(subcmd));
1584         proto_tree_add_text(subtree,
1585                             offset + SRV_META_USER_RESULT,
1586                             1,
1587                             "%s", (result==0x0a)?"Success":"Failure");
1588         sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
1589 #else
1590         ti = proto_tree_add_text(tree,
1591                                  offset + SRV_META_USER_SUBCMD,
1592                                  2,
1593                                  "%s", findSubCmd(subcmd));
1594         sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
1595         proto_tree_add_text(sstree,
1596                             offset + SRV_META_USER_RESULT,
1597                             1,
1598                             "%s", (result==0x0a)?"Success":"Failure");
1599 #endif
1600
1601         /* Skip the META_USER header */
1602         left -= 3;
1603         p += 3;
1604
1605         switch(subcmd) {
1606         case META_EX_USER_FOUND:
1607         {
1608             /* This is almost the same as META_USER_FOUND,
1609              * however, there's an extra length field
1610              */
1611             guint16 pktLen = -1;
1612
1613             /* Read the lenght field */
1614             pktLen = pletohs(p);
1615             proto_tree_add_text(sstree,
1616                                 offset + size - left,
1617                                 sizeof(guint16),
1618                                 "Length: %u", pktLen);
1619             
1620             p += sizeof(guint16); left -= sizeof(guint16);
1621         }
1622         case META_USER_FOUND:
1623         {
1624             /* The goto mentioned in this block should be local to this
1625              * block if C'd allow it.
1626              *
1627              * They are used to "implement" a poorman's exception handling
1628              */
1629             guint32 uin = -1;
1630             int len = 0;
1631             char *descr[] = {
1632                 "Nick",
1633                 "First name",
1634                 "Last name",
1635                 "Email",
1636                 NULL};
1637             char** d = descr;
1638             guint16 x2 = -1;
1639             guint32 x3 = -1;
1640             unsigned char auth;
1641             /*
1642              * Read UIN
1643              */
1644             if (left<sizeof(guint32))
1645                 break;
1646             uin = pletohl(p);
1647             proto_tree_add_text(sstree,
1648                                 offset + size - left,
1649                                 sizeof(guint32),
1650                                 "UIN: %u", uin);
1651             p+=sizeof(guint32);left-=sizeof(guint32);
1652
1653             for ( ; *d!=NULL; d++) {
1654                 len = proto_add_icq_attr(sstree,
1655                                          p,
1656                                          offset + size - left,
1657                                          left,
1658                                          *d);
1659                 if (len == -1)
1660                     return;
1661                 p += len; left -= len;
1662             }
1663             /* Get the authorize setting */
1664             if (left<sizeof(unsigned char))
1665                 break;
1666             auth = *p;
1667             proto_tree_add_text(sstree,
1668                                 offset + size - left,
1669                                 sizeof(guint16),
1670                                 "authorization: %s", (auth==0x01)?"Neccessary":"Who needs it");
1671             p++; left--;
1672             /* Get x2 */
1673             if (left<sizeof(guint16))
1674                 break;
1675             x2 = pletohs(p);
1676             proto_tree_add_text(sstree,
1677                                 offset + size - left,
1678                                 sizeof(guint16),
1679                                 "x2: %04x", x2);
1680             p+=sizeof(guint16);left-=sizeof(guint16);
1681             /* Get x3 */
1682             if (left<sizeof(guint32))
1683                 break;
1684             x3 = pletohl(p);
1685             proto_tree_add_text(sstree,
1686                                 offset + size - left,
1687                                 sizeof(guint32),
1688                                 "x3: %08x", x3);
1689             p+=sizeof(guint32);left-=sizeof(guint32);
1690             break;
1691         }
1692         case META_ABOUT:
1693         {
1694             int len;
1695             char* about = NULL;
1696             /* Get the about information */
1697             if (left<sizeof(guint16))
1698                 break;
1699             len = pletohs(p);
1700             p+=sizeof(guint16);left-=sizeof(guint16);
1701             if ((len<=0) || (left<len))
1702                 break;
1703             about = g_malloc(len);
1704             strncpy(about, p, len);
1705             proto_tree_add_text(sstree,
1706                                 offset + size - left,
1707                                 sizeof(guint16)+len,
1708                                 "About(%d): %s", len, about);
1709             p+=len;left-=len;
1710             left -= 3;
1711             g_free(about);
1712             break;
1713         }
1714         case META_USER_INFO:
1715         {
1716             /* The goto mentioned in this block should be local to this
1717              * block if C'd allow it.
1718              *
1719              * They are used to "implement" a poorman's exception handling
1720              */
1721             static const char* descr[] = {
1722                 "Nick",
1723                 "First name",
1724                 "Last name",
1725                 "Primary email",
1726                 "Secundary email",
1727                 "Old email",
1728                 "City",
1729                 "State",
1730                 "Phone",
1731                 "Fax",
1732                 "Street",
1733                 "Cellphone",
1734                 "Zip",
1735                 NULL};
1736             const char** d = descr;
1737             char* item = NULL;
1738             guint16 country;
1739             unsigned char user_timezone = -1;
1740             unsigned char auth = -1;
1741             int len = 0;
1742 #if 0
1743             /* Get the uin */
1744             if (left<sizeof(guint32))
1745                 break;
1746             uin = pletohl(p);
1747             proto_tree_add_text(sstree,
1748                                 offset + size - left,
1749                                 sizeof(guint32),
1750                                 "UIN: %u", uin);
1751             p+=sizeof(guint32);left-=sizeof(guint32);
1752 #endif
1753             
1754             /*
1755              * Get every field from the description
1756              */
1757             while ((*d)!=NULL) {
1758                 if (left<sizeof(guint16))
1759                     break;
1760                 len = pletohs(p);
1761                 p+=sizeof(guint16);left-=sizeof(guint16);
1762                 if ((len<0) || (left<len))
1763                     break;
1764                 if (len>0) {
1765                     item = g_malloc(len);
1766                     strncpy(item, p, len);
1767                     proto_tree_add_text(sstree,
1768                                         offset + size - left - sizeof(guint16),
1769                                         sizeof(guint16)+len,
1770                                         "%s(%d): %s",*d, len, item);
1771                     g_free(item);
1772                     p+=len;left-=len;
1773                 }
1774                 d++;
1775             }
1776             /* Get country code */
1777             if (left<sizeof(guint16))
1778                 break;
1779             country = pletohs(p);
1780             proto_tree_add_text(sstree,
1781                                 offset + size - left,
1782                                 sizeof(guint16),
1783                                 "Countrycode: %u", country);
1784             p+=sizeof(guint16); left-=sizeof(guint16);
1785             /* Get the timezone setting */
1786             if (left<sizeof(unsigned char))
1787                 break;
1788             user_timezone = *p;
1789             proto_tree_add_text(sstree,
1790                                 offset + size - left,
1791                                 sizeof(unsigned char),
1792                                 "Timezone: %u", user_timezone);
1793             p++; left--;
1794             /* Get the authorize setting */
1795             if (left<sizeof(unsigned char))
1796                 break;
1797             auth = *p;
1798             proto_tree_add_text(sstree,
1799                                 offset + size - left,
1800                                 sizeof(unsigned char),
1801                                 "Authorization: (%u) %s",
1802                                 auth, (auth==0)?"No":"Yes");
1803             p++; left--;
1804             /* Get the webaware setting */
1805             if (left<sizeof(unsigned char))
1806                 break;
1807             auth = *p;
1808             proto_tree_add_text(sstree,
1809                                 offset + size - left,
1810                                 sizeof(unsigned char),
1811                                 "Webaware: (%u) %s",
1812                                 auth, (auth==0)?"No":"Yes");
1813             p++; left--;
1814             /* Get the authorize setting */
1815             if (left<sizeof(unsigned char))
1816                 break;
1817             auth = *p;
1818             proto_tree_add_text(sstree,
1819                                 offset + size - left,
1820                                 sizeof(unsigned char),
1821                                 "HideIP: (%u) %s",
1822                                 auth, (auth==0)?"No":"Yes");
1823             p++; left--;
1824             break;
1825         }
1826         default:
1827             /* This information is already printed in the tree */
1828             fprintf(stderr, "Meta subcmd: %04x\n", subcmd);
1829             break;
1830         }
1831     }
1832 }
1833
1834 static void
1835 icqv5_srv_recv_message(proto_tree* tree,      /* Tree to put the data in */
1836                        const u_char* pd,      /* Packet content */
1837                        int offset,            /* Offset from the start of the packet to the content */
1838                        int size)              /* Number of chars left to do */
1839 {
1840     proto_tree* subtree = NULL;
1841     proto_item* ti = NULL;
1842     int left = size;
1843     guint32 uin = -1;
1844     guint16 year = -1;
1845     unsigned char month = -1;
1846     unsigned char day = -1;
1847     unsigned char hour = -1;
1848     unsigned char minute = -1;
1849     
1850     if (tree) {
1851         ti = proto_tree_add_uint_format(tree,
1852                                         hf_icq_cmd,
1853                                         offset,
1854                                         4,
1855                                         SRV_RECV_MESSAGE,
1856                                         "Body");
1857         subtree = proto_item_add_subtree(ti, ett_icq_body);
1858         if (left>=sizeof(guint32)) {
1859             uin = pletohl(pd + SRV_RECV_MSG_UIN);
1860             proto_tree_add_uint_format(subtree,
1861                                        hf_icq_uin,
1862                                        offset + SRV_RECV_MSG_UIN,
1863                                        sizeof(guint32),
1864                                        uin,
1865                                        "UIN: %u", uin);
1866             left -= sizeof(guint32);
1867         } else
1868             return;
1869         if (left>=(sizeof(guint16)+4*sizeof(unsigned char))) {
1870             year = pletohs(pd + SRV_RECV_MSG_YEAR);
1871             month = pd[SRV_RECV_MSG_MONTH];
1872             day = pd[SRV_RECV_MSG_DAY];
1873             hour = pd[SRV_RECV_MSG_HOUR];
1874             minute = pd[SRV_RECV_MSG_MINUTE];
1875
1876             proto_tree_add_text(subtree,
1877                                 offset + SRV_RECV_MSG_YEAR,
1878                                 sizeof(guint16) + 4*sizeof(unsigned char),
1879                                 "Time: %u-%u-%u %02u:%02u",
1880                                 day, month, year, hour, minute);
1881             
1882             left -= (sizeof(guint16)+4*sizeof(unsigned char));
1883         } else
1884             return;
1885         icqv5_decode_msgType(subtree,
1886                              pd + SRV_RECV_MSG_MSG_TYPE,
1887                              offset + SRV_RECV_MSG_MSG_TYPE,
1888                              left);
1889     }
1890 }
1891
1892 static void
1893 icqv5_srv_rand_user(proto_tree* tree,      /* Tree to put the data in */
1894                        const u_char* pd,      /* Packet content */
1895                        int offset,            /* Offset from the start of the packet to the content */
1896                        int size)              /* Number of chars left to do */
1897 {
1898     proto_tree* subtree = NULL;
1899     proto_item* ti = NULL;
1900     guint32 uin = -1;
1901     const unsigned char* IP = NULL;
1902     guint32 port = -1;
1903     const unsigned char* realIP = NULL;
1904     unsigned char commClass = -1;
1905     guint32 status;
1906     guint16 tcpVer;
1907     int left = size;
1908     
1909     if (tree) {
1910         ti = proto_tree_add_uint_format(tree,
1911                                         hf_icq_cmd,
1912                                         offset,
1913                                         SRV_RAND_USER_TCP_VER + 2,
1914                                         SRV_RAND_USER,
1915                                         "Body");
1916         subtree = proto_item_add_subtree(ti, ett_icq_body);
1917         /* guint32 UIN */
1918         if (left<sizeof(guint32))
1919             return;
1920         uin = pletohl(pd + SRV_RAND_USER_UIN);
1921         proto_tree_add_text(subtree,
1922                             offset + SRV_RAND_USER_UIN,
1923                             sizeof(guint32),
1924                             "UIN: %u", uin);
1925         left -= sizeof(guint32);
1926         /* guint32 IP */
1927         if (left<sizeof(guint32))
1928             return;
1929         IP = pd + SRV_RAND_USER_IP;
1930         proto_tree_add_text(subtree,
1931                             offset + SRV_RAND_USER_IP,
1932                             sizeof(guint32),
1933                             "IP: %s", ip_to_str(IP));
1934         left -= sizeof(guint32);
1935         /* guint32 portNum */
1936         if (left<sizeof(guint32))
1937             return;
1938         port = pletohs(pd + SRV_RAND_USER_PORT);
1939         proto_tree_add_text(subtree,
1940                             offset + SRV_RAND_USER_UIN,
1941                             sizeof(guint32),
1942                             "Port: %u", port);
1943         left -= sizeof(guint32);
1944         /* guint32 realIP */                        
1945         if (left<sizeof(guint32))
1946             return;
1947         realIP = pd + SRV_RAND_USER_REAL_IP;
1948         proto_tree_add_text(subtree,
1949                             offset + SRV_RAND_USER_REAL_IP,
1950                             sizeof(guint32),
1951                             "RealIP: %s", ip_to_str(realIP));
1952         left -= sizeof(guint32);
1953         /* guit16 Communication Class */
1954         if (left<sizeof(unsigned char))
1955             return;
1956         commClass = pd[SRV_RAND_USER_CLASS];
1957         proto_tree_add_text(subtree,
1958                             offset + SRV_RAND_USER_CLASS,
1959                             sizeof(unsigned char),
1960                             "Class: %s", (commClass!=4)?"User to User":"Through Server");
1961         left -= sizeof(unsigned char);
1962         /* guint32 status */
1963         if (left<sizeof(guint32))
1964             return;
1965         status = pletohs(pd + SRV_RAND_USER_STATUS);
1966         proto_tree_add_text(subtree,
1967                             offset + SRV_RAND_USER_STATUS,
1968                             sizeof(guint32),
1969                             "Status: (%u) %s", status, findStatus(status));
1970         /* guint16 tcpVersion */
1971         if (left<sizeof(guint16))
1972             return;
1973         tcpVer = pletohs(pd + SRV_RAND_USER_TCP_VER);
1974         proto_tree_add_text(subtree,
1975                             offset + SRV_RAND_USER_TCP_VER,
1976                             sizeof(guint16),
1977                             "TCPVersion: %u", tcpVer);
1978         left -= sizeof(guint16);
1979     }
1980 }
1981
1982 /*
1983  * Dissect all the v5 client traffic. This is encrypted, so be careful.
1984  */
1985 static void
1986 dissect_icqv5Client(const u_char *pd,
1987                     int offset,
1988                     frame_data *fd, 
1989                     proto_tree *tree)
1990 {
1991     proto_tree *icq_tree = NULL;
1992     proto_tree *icq_header_tree = NULL;
1993     proto_tree *icq_decode_tree = NULL;
1994     proto_item *ti = NULL;
1995
1996     guint16 version = -1, cmd = -1;
1997     guint16 seqnum1 = 0 , seqnum2 = 0;
1998     guint32 uin = -1, sessionid = -1;
1999     guint32 key = -1;
2000     guint16 pktsize = -1;       /* The size of the ICQ content */
2001     u_char decr_pd[1600];       /* Decrypted content, size should be dynamic */
2002     
2003     pktsize = END_OF_FRAME;
2004     /* First copy the memory, we don't want to overwrite the old content */
2005     memcpy(decr_pd, &pd[offset], pktsize);
2006     if (pktsize>0x14) {
2007         key = get_v5key(decr_pd, pktsize);
2008         decrypt_v5(decr_pd, pktsize, key);
2009     
2010         /* This information only makes sense in the decrypted version */
2011         uin = pletohl(&decr_pd[ICQ5_CL_UIN]);
2012         cmd = pletohs(&decr_pd[ICQ5_CL_CMD]);
2013         sessionid = pletohl(&decr_pd[ICQ5_CL_SESSIONID]);
2014         version = pletohs(&decr_pd[ICQ_VERSION]);
2015         seqnum1 = pletohs(&decr_pd[ICQ5_CL_SEQNUM1]);
2016         seqnum2 = pletohs(&decr_pd[ICQ5_CL_SEQNUM2]);
2017
2018         if (check_col(fd, COL_INFO))
2019             col_add_fstr(fd, COL_INFO, "ICQv5 %s", findClientCmd(cmd));
2020     }
2021     
2022     if (tree) {
2023         ti = proto_tree_add_protocol_format(tree,
2024                                  proto_icq,
2025                                  offset,
2026                                  pktsize,
2027                                  "ICQv5 %s (len %u)",
2028                                  findClientCmd(cmd),
2029                                  pktsize);
2030         icq_tree = proto_item_add_subtree(ti, ett_icq);
2031         ti = proto_tree_add_uint_format(icq_tree,
2032                                         hf_icq_type,
2033                                         offset,
2034                                         ICQ5_CL_HDRSIZE,
2035                                         ICQ5_client,
2036                                         "Header");
2037         icq_header_tree = proto_item_add_subtree(ti, ett_icq_header);
2038                                         
2039         proto_tree_add_uint_format(icq_header_tree,
2040                                    hf_icq_sessionid,
2041                                    offset+ICQ5_CL_SESSIONID,
2042                                    4,
2043                                    sessionid,
2044                                    "Session ID: 0x%08x",
2045                                    sessionid);
2046         proto_tree_add_uint_format(icq_header_tree,
2047                                    hf_icq_checkcode,
2048                                    offset+ICQ5_CL_CHECKCODE,
2049                                    4,
2050                                    key,
2051                                    "Key: 0x%08x",
2052                                    key);
2053         proto_tree_add_uint_format(icq_header_tree,
2054                                    hf_icq_uin,
2055                                    offset+ICQ5_CL_UIN,
2056                                    4,
2057                                    uin,
2058                                    "UIN: %u (0x%08X)",
2059                                    uin, uin);
2060         proto_tree_add_text(icq_header_tree,
2061                             offset + ICQ5_CL_SEQNUM1,
2062                             2,
2063                             "Seqnum1: 0x%04x", seqnum1);
2064         proto_tree_add_text(icq_header_tree,
2065                             offset + ICQ5_CL_SEQNUM1,
2066                             2,
2067                             "Seqnum2: 0x%04x", seqnum2);
2068         switch(cmd) {
2069         case CMD_ACK:
2070             icqv5_cmd_ack(icq_tree,
2071                           decr_pd + ICQ5_CL_HDRSIZE,
2072                           offset + ICQ5_CL_HDRSIZE,
2073                           pktsize - ICQ5_CL_HDRSIZE);
2074             break;
2075         case CMD_SEND_MSG:
2076         case CMD_MSG_TO_NEW_USER:
2077             icqv5_cmd_send_msg(icq_tree,
2078                                decr_pd + ICQ5_CL_HDRSIZE,
2079                                offset + ICQ5_CL_HDRSIZE,
2080                                pktsize - ICQ5_CL_HDRSIZE,
2081                                cmd);
2082             break;
2083         case CMD_RAND_SEARCH:
2084             icqv5_cmd_rand_search(icq_tree,
2085                                   decr_pd + ICQ5_CL_HDRSIZE,
2086                                   offset + ICQ5_CL_HDRSIZE,
2087                                   pktsize - ICQ5_CL_HDRSIZE);
2088             break;
2089         case CMD_LOGIN:
2090             icqv5_cmd_login(icq_tree,
2091                             decr_pd + ICQ5_CL_HDRSIZE,
2092                             offset + ICQ5_CL_HDRSIZE,
2093                             pktsize - ICQ5_CL_HDRSIZE);
2094             break;
2095         case CMD_SEND_TEXT_CODE:
2096             icqv5_cmd_send_text_code(icq_tree,
2097                                      decr_pd + ICQ5_CL_HDRSIZE,
2098                                      offset + ICQ5_CL_HDRSIZE,
2099                                      pktsize - ICQ5_CL_HDRSIZE);
2100             break;
2101         case CMD_STATUS_CHANGE:
2102             icqv5_cmd_status_change(icq_tree,
2103                                     decr_pd + ICQ5_CL_HDRSIZE,
2104                                     offset + ICQ5_CL_HDRSIZE,
2105                                     pktsize - ICQ5_CL_HDRSIZE);
2106             break;
2107         case CMD_ACK_MESSAGES:
2108             icqv5_cmd_ack_messages(icq_tree,
2109                                    decr_pd + ICQ5_CL_HDRSIZE,
2110                                    offset + ICQ5_CL_HDRSIZE,
2111                                    pktsize - ICQ5_CL_HDRSIZE);
2112             break;
2113         case CMD_KEEP_ALIVE:
2114             icqv5_cmd_keep_alive(icq_tree,
2115                                  decr_pd + ICQ5_CL_HDRSIZE,
2116                                  offset + ICQ5_CL_HDRSIZE,
2117                                  pktsize - ICQ5_CL_HDRSIZE);
2118             break;
2119         case CMD_ADD_TO_LIST:
2120             icqv5_cmd_add_to_list(icq_tree,
2121                                    decr_pd + ICQ5_CL_HDRSIZE,
2122                                    offset + ICQ5_CL_HDRSIZE,
2123                                    pktsize - ICQ5_CL_HDRSIZE);
2124             break;
2125         case CMD_CONTACT_LIST:
2126             icqv5_cmd_contact_list(icq_tree,
2127                                    decr_pd + ICQ5_CL_HDRSIZE,
2128                                    offset + ICQ5_CL_HDRSIZE,
2129                                    pktsize - ICQ5_CL_HDRSIZE);
2130             break;
2131         case CMD_META_USER:
2132         case CMD_REG_NEW_USER:
2133         case CMD_QUERY_SERVERS:
2134         case CMD_QUERY_ADDONS:
2135             icqv5_cmd_no_params(icq_tree,
2136                                 decr_pd + ICQ5_CL_HDRSIZE,
2137                                 offset + ICQ5_CL_HDRSIZE,
2138                                 pktsize - ICQ5_CL_HDRSIZE,
2139                                 cmd);
2140             break;
2141         default:
2142             proto_tree_add_uint_format(icq_tree,
2143                                        hf_icq_cmd,
2144                                        offset+ICQ5_CL_CMD,
2145                                        2,
2146                                        cmd,
2147                                        "Command: %u (%s)",
2148                                        cmd, findClientCmd(cmd));
2149             fprintf(stderr,"Missing: %s\n", findClientCmd(cmd));
2150             break;
2151         }
2152         ti = proto_tree_add_text(icq_tree,
2153                                  offset,
2154                                  pktsize,
2155                                  "Decoded packet");
2156         icq_decode_tree = proto_item_add_subtree(ti,
2157                                                  ett_icq_decode);
2158         proto_tree_add_hexdump(icq_decode_tree, offset, decr_pd, pktsize);
2159
2160     }
2161 }
2162
2163 static void
2164 dissect_icqv5Server(const u_char *pd,
2165                     int offset,
2166                     frame_data *fd, 
2167                     proto_tree *tree,
2168                     guint32 pktsize)
2169 {
2170     /* Server traffic is easy, not encrypted */
2171     proto_tree *icq_tree = NULL;
2172     proto_tree *icq_header_tree = NULL;
2173     proto_tree *icq_decode_tree = NULL;
2174     proto_item *ti = NULL;
2175     const u_char* decr_pd;
2176     int changeCol = (pktsize==(guint32)-1);
2177
2178     guint16 version, cmd;
2179     guint32 uin, sessionid;
2180     guint16 seq_num1, seq_num2;
2181     guint32 checkcode;
2182     
2183     uin = pletohl(&pd[ICQ5_SRV_UIN]);
2184     sessionid = pletohl(&pd[ICQ5_SRV_SESSIONID]);
2185     cmd = pletohs(&pd[ICQ5_SRV_CMD]);
2186     version = pletohs(&pd[ICQ_VERSION]);
2187     checkcode = pletohl(&pd[ICQ5_SRV_CHECKCODE]);
2188     seq_num1 = pletohs(&pd[ICQ5_SRV_SEQNUM1]);
2189     seq_num2 = pletohs(&pd[ICQ5_SRV_SEQNUM2]);
2190     if (pktsize == -1)
2191         pktsize = fd->pkt_len - offset;
2192     decr_pd = pd;
2193     
2194     if (changeCol && check_col(fd, COL_INFO))
2195         col_add_fstr(fd, COL_INFO, "ICQv5 %s", findServerCmd(cmd));
2196
2197     if (tree) {
2198         ti = proto_tree_add_protocol_format(tree,
2199                                         proto_icq,
2200                                         offset,
2201                                         pktsize,
2202                                         "ICQv5 %s (len %u)",
2203                                         findServerCmd(cmd),
2204                                         pktsize);
2205         
2206         icq_tree = proto_item_add_subtree(ti, ett_icq);
2207
2208         ti = proto_tree_add_uint_format(icq_tree,
2209                                         hf_icq_type,
2210                                         offset,
2211                                         ICQ5_SRV_HDRSIZE,
2212                                         ICQ5_server,
2213                                         "Header");
2214         icq_header_tree = proto_item_add_subtree(ti, ett_icq_header);
2215                                         
2216         proto_tree_add_uint_format(icq_header_tree,
2217                                    hf_icq_sessionid,
2218                                    offset+ICQ5_SRV_SESSIONID,
2219                                    4,
2220                                    sessionid,
2221                                    "Session ID: 0x%08x",
2222                                    sessionid);
2223         proto_tree_add_text(icq_header_tree,
2224                             offset+ICQ5_SRV_SEQNUM1,
2225                             2,
2226                             "Seq Number1: 0x%04x",
2227                             seq_num1);
2228         proto_tree_add_text(icq_header_tree,
2229                             offset+ICQ5_SRV_SEQNUM2,
2230                             2,
2231                             "Seq Number2: 0x%04x",
2232                             seq_num2);
2233         proto_tree_add_uint_format(icq_header_tree,
2234                                    hf_icq_uin,
2235                                    offset+ICQ5_SRV_UIN,
2236                                    4,
2237                                    uin,
2238                                    "UIN: %u",
2239                                    uin);
2240         proto_tree_add_uint_format(icq_header_tree,
2241                                    hf_icq_checkcode,
2242                                    offset+ICQ5_SRV_CHECKCODE,
2243                                    4,
2244                                    checkcode,
2245                                    "Checkcode: 0x%08x",
2246                                    checkcode);
2247         switch (cmd) {
2248         case SRV_RAND_USER:
2249             icqv5_srv_rand_user(icq_tree,
2250                                decr_pd + ICQ5_SRV_HDRSIZE,
2251                                offset + ICQ5_SRV_HDRSIZE,
2252                                pktsize - ICQ5_SRV_HDRSIZE);
2253             break;
2254         case SRV_SYS_DELIVERED_MESS:
2255             /* The message structures are all the same. Why not run
2256              * the same routine? */
2257             icqv5_cmd_send_msg(icq_tree,
2258                                decr_pd + ICQ5_SRV_HDRSIZE,
2259                                offset + ICQ5_SRV_HDRSIZE,
2260                                pktsize - ICQ5_SRV_HDRSIZE,
2261                                cmd);
2262             break;
2263         case SRV_USER_ONLINE:
2264             icqv5_srv_user_online(icq_tree,
2265                                decr_pd + ICQ5_SRV_HDRSIZE,
2266                                offset + ICQ5_SRV_HDRSIZE,
2267                                pktsize - ICQ5_SRV_HDRSIZE);
2268             break;
2269         case SRV_USER_OFFLINE:
2270             icqv5_srv_user_offline(icq_tree,
2271                                decr_pd + ICQ5_SRV_HDRSIZE,
2272                                offset + ICQ5_SRV_HDRSIZE,
2273                                pktsize - ICQ5_SRV_HDRSIZE);
2274             break;
2275         case SRV_LOGIN_REPLY:
2276             icqv5_srv_login_reply(icq_tree,
2277                                decr_pd + ICQ5_SRV_HDRSIZE,
2278                                offset + ICQ5_SRV_HDRSIZE,
2279                                pktsize - ICQ5_SRV_HDRSIZE);
2280             break;
2281         case SRV_META_USER:
2282             icqv5_srv_meta_user(icq_tree,
2283                                decr_pd + ICQ5_SRV_HDRSIZE,
2284                                offset + ICQ5_SRV_HDRSIZE,
2285                                pktsize - ICQ5_SRV_HDRSIZE);
2286             break;
2287         case SRV_RECV_MESSAGE:
2288             icqv5_srv_recv_message(icq_tree,
2289                                    decr_pd + ICQ5_SRV_HDRSIZE,
2290                                    offset + ICQ5_SRV_HDRSIZE,
2291                                    pktsize - ICQ5_SRV_HDRSIZE);
2292             break;
2293         case SRV_MULTI:
2294             icqv5_srv_multi(icq_tree,
2295                             decr_pd + ICQ5_SRV_HDRSIZE,
2296                             offset + ICQ5_SRV_HDRSIZE,
2297                             pktsize - ICQ5_SRV_HDRSIZE,
2298                             fd);
2299             break;
2300         case SRV_ACK:
2301         case SRV_GO_AWAY:
2302         case SRV_NEW_UIN:
2303         case SRV_BAD_PASS:
2304         case SRV_UPDATE_SUCCESS:
2305             icqv5_srv_no_params(icq_tree,
2306                                 decr_pd + ICQ5_SRV_HDRSIZE,
2307                                 offset + ICQ5_SRV_HDRSIZE,
2308                                 pktsize - ICQ5_SRV_HDRSIZE,
2309                                 cmd);
2310             break;
2311         default:
2312             proto_tree_add_uint_format(icq_tree,
2313                                        hf_icq_cmd,
2314                                        offset + ICQ5_SRV_CMD,
2315                                        2,
2316                                        cmd,
2317                                        "Command: %u (%s)",
2318                                        cmd, findServerCmd(cmd));
2319             fprintf(stderr,"Missing: %s\n", findServerCmd(cmd));
2320             break;
2321         }
2322
2323         ti = proto_tree_add_text(icq_tree,
2324                                  offset,
2325                                  pktsize,
2326                                  "Decoded packet");
2327         icq_decode_tree = proto_item_add_subtree(ti,
2328                                                  ett_icq_decode);
2329         proto_tree_add_hexdump(icq_decode_tree, offset, decr_pd, pktsize);
2330     }
2331 }
2332
2333 void dissect_icqv5(const u_char *pd,
2334                    int offset,
2335                    frame_data *fd, 
2336                    proto_tree *tree)
2337 {
2338   guint32 unknown = pletohl(&pd[offset + ICQ5_UNKNOWN]);
2339   
2340   if (check_col(fd, COL_PROTOCOL))
2341       col_add_str(fd, COL_PROTOCOL, "ICQv5 (UDP)");
2342   if (check_col(fd, COL_INFO))
2343       col_add_str(fd, COL_INFO, "ICQv5 packet");
2344   if (unknown == 0x0L) {
2345       dissect_icqv5Client(pd, offset, fd, tree);
2346   } else {
2347       dissect_icqv5Server(pd + offset, offset, fd, tree, (guint32) -1);
2348   }
2349 }
2350
2351 void dissect_icq(const u_char *pd,
2352                  int offset,
2353                  frame_data *fd, 
2354                  proto_tree *tree)
2355 {
2356   int version = 0;
2357
2358   version = pletohs(&pd[offset + ICQ_VERSION]);
2359   switch (version) {
2360   case 0x0005:
2361       dissect_icqv5(pd, offset, fd, tree);
2362       break;
2363   case 0x0002:
2364       dissect_icqv2(pd, offset, fd, tree);
2365       break;
2366   default:
2367       fprintf(stderr, "ICQ: Unknown version (%d)\n", version);
2368       break;
2369   }
2370 }
2371
2372 /* registration with the filtering engine */
2373 void
2374 proto_register_icq(void)
2375 {
2376     static hf_register_info hf[] = {
2377         { &hf_icq_type,
2378           {"Type", "icq.type", FT_UINT16, BASE_DEC, NULL, 0x0, ""}},
2379         { &hf_icq_uin,
2380           {"UIN", "icq.uin", FT_UINT32, BASE_DEC, NULL, 0x0, ""}},
2381         { &hf_icq_sessionid,
2382           {"SessionID", "icq.sessionid", FT_UINT32, BASE_HEX, NULL, 0x0, ""}},
2383         { &hf_icq_cmd,
2384           {"Command", "icq.cmd", FT_UINT16, BASE_DEC, NULL, 0x0, ""}},
2385         { &hf_icq_checkcode,
2386           {"Checkcode", "icq.checkcode", FT_UINT32, BASE_HEX, NULL, 0x0, ""}},
2387         { &hf_icq_decode,
2388           {"Decode", "icq.decode", FT_STRING, BASE_NONE, NULL, 0x0, ""}}
2389     };
2390     static gint *ett[] = {
2391         &ett_icq,
2392         &ett_icq_header,
2393         &ett_icq_decode,
2394         &ett_icq_body,
2395         &ett_icq_body_parts,
2396     };
2397     
2398     proto_icq = proto_register_protocol ("ICQ Protocol", "icq");
2399     
2400     proto_register_field_array(proto_icq, hf, array_length(hf));
2401
2402     proto_register_subtree_array(ett, array_length(ett));
2403 }