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