2 * Routines for AIM Instant Messenger (OSCAR) dissection
3 * Copyright 2000, Ralf Hoelzer <ralf@well.com>
5 * $Id: packet-aim.c,v 1.17 2002/08/28 21:00:07 jmayer Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
37 #include <epan/packet.h>
38 #include <epan/strutil.h>
40 #define TCP_PORT_AIM 5190
41 #define MAX_BUDDYNAME_LENGTH 30
46 #define CHANNEL_NEW_CONN 0x01
47 #define CHANNEL_SNAC_DATA 0x02
48 #define CHANNEL_FLAP_ERR 0x03
49 #define CHANNEL_CLOSE_CONN 0x04
52 #define FAMILY_GENERIC 0x0001
53 #define FAMILY_LOCATION 0x0002
54 #define FAMILY_BUDDYLIST 0x0003
55 #define FAMILY_MESSAGING 0x0004
56 #define FAMILY_ADVERTS 0x0005
57 #define FAMILY_INVITATION 0x0006
58 #define FAMILY_ADMIN 0x0007
59 #define FAMILY_POPUP 0x0008
60 #define FAMILY_BOS 0x0009
61 #define FAMILY_USERLOOKUP 0x000A
62 #define FAMILY_STATS 0x000B
63 #define FAMILY_TRANSLATE 0x000C
64 #define FAMILY_CHAT_NAV 0x000D
65 #define FAMILY_CHAT 0x000E
66 #define FAMILY_SIGNON 0x0017
69 #define MSG_TO_CLIENT 0x006
70 #define MSG_FROM_CLIENT 0x007
72 static void dissect_aim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
74 static void get_message( guchar *msg, tvbuff_t *tvb, int msg_offset, int msg_length);
75 static int get_buddyname( char *name, tvbuff_t *tvb, int len_offset, int name_offset);
77 /* Initialize the protocol and registered fields */
78 static int proto_aim = -1;
79 static int hf_aim_cmd_start = -1;
80 static int hf_aim_channel = -1;
81 static int hf_aim_seqno = -1;
82 static int hf_aim_data_len = -1;
83 static int hf_aim_fnac_family = -1;
84 static int hf_aim_fnac_subtype = -1;
86 /* Initialize the subtree pointers */
87 static gint ett_aim = -1;
88 static gint ett_aim_fnac = -1;
90 /* Code to actually dissect the packets */
91 static void dissect_aim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
94 unsigned char hdr_channel; /* channel ID */
95 unsigned short hdr_sequence_no; /* Internal frame sequence number, not needed */
96 unsigned short hdr_data_field_length; /* length of data within frame */
100 guint8 buddyname_length = 0;
101 char buddyname[MAX_BUDDYNAME_LENGTH];
104 /* Set up structures we will need to add the protocol subtree and manage it */
107 proto_tree *aim_tree = NULL;
108 proto_tree *aim_tree_fnac = NULL;
110 /* check, if this is really an AIM packet, they start with 0x2a */
112 if(!(tvb_get_guint8(tvb, 0) == 0x2a)) {
113 /* Not an instant messenger packet, just happened to use the same port */
117 /* Make entries in Protocol column and Info column on summary display */
118 if (check_col(pinfo->cinfo, COL_PROTOCOL))
119 col_set_str(pinfo->cinfo, COL_PROTOCOL, "AIM");
121 if (check_col(pinfo->cinfo, COL_INFO))
122 col_add_str(pinfo->cinfo, COL_INFO, "AOL Instant Messenger");
124 /* get relevant header information */
126 hdr_channel = tvb_get_guint8(tvb, 1);
127 hdr_sequence_no = tvb_get_ntohs(tvb, 2);
128 hdr_data_field_length = tvb_get_ntohs(tvb, 4);
130 /* In the interest of speed, if "tree" is NULL, don't do any work not
131 necessary to generate protocol tree items. */
134 ti = proto_tree_add_item(tree, proto_aim, tvb, 0, -1, FALSE);
135 aim_tree = proto_item_add_subtree(ti, ett_aim);
136 proto_tree_add_uint(aim_tree, hf_aim_cmd_start, tvb, 0, 1, '*');
137 proto_tree_add_uint(aim_tree, hf_aim_channel, tvb, 1, 1, hdr_channel);
138 proto_tree_add_uint(aim_tree, hf_aim_seqno, tvb, 2, 2, hdr_sequence_no);
139 proto_tree_add_uint(aim_tree, hf_aim_data_len, tvb, 4, 2, hdr_data_field_length);
147 /* New connection request */
148 case CHANNEL_NEW_CONN:
149 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "New Connection");
152 /* SNAC channel. Most packets are of this type, such as messages or buddy list
155 case CHANNEL_SNAC_DATA:
156 family = tvb_get_ntohs(tvb, 6);
157 subtype = tvb_get_ntohs(tvb, 8);
159 if (check_col(pinfo->cinfo, COL_INFO)) {
160 col_add_fstr(pinfo->cinfo, COL_INFO, "SNAC data");
164 ti1 = proto_tree_add_text(aim_tree, tvb, 6, tvb_length(tvb) - 6, "FNAC");
165 aim_tree_fnac = proto_item_add_subtree(ti1, ett_aim_fnac);
166 proto_tree_add_uint(aim_tree_fnac, hf_aim_fnac_family, tvb, 6, 2, family);
167 proto_tree_add_uint(aim_tree_fnac, hf_aim_fnac_subtype, tvb, 8, 2, subtype);
172 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Family: 0x%04x - Subtype: 0x%04x (unknown)", family, subtype);
180 buddyname_length = get_buddyname( buddyname, tvb, 19, 20 );
182 if (check_col(pinfo->cinfo, COL_INFO)) {
183 col_add_fstr(pinfo->cinfo, COL_INFO, "Login");
184 col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", buddyname);
189 proto_tree_add_text(aim_tree_fnac, tvb, 20, buddyname_length, "Screen Name: %s", buddyname);
194 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Login information reply");
197 buddyname_length = get_buddyname( buddyname, tvb, 19, 20 );
199 if (check_col(pinfo->cinfo, COL_INFO)) {
200 col_add_fstr(pinfo->cinfo, COL_INFO, "Sign-on");
201 col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", buddyname);
206 proto_tree_add_text(aim_tree_fnac, tvb, 20, buddyname_length, "Screen Name: %s", buddyname);
211 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Sign-on reply");
220 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Client is now online and ready for normal function");
223 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Server is now ready for normal functions");
226 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Request for new service (server will redirect client)");
229 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Redirect response");
232 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Request Rate Information");
235 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Rate information response");
238 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Rate Information Response Ack");
241 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "No-op");
246 case FAMILY_BUDDYLIST:
250 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Buddylist - Error");
254 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Request Rights information");
258 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Rights information");
262 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Add to Buddylist");
266 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Remove from Buddylist");
270 buddyname_length = get_buddyname( buddyname, tvb, 16, 17 );
272 if (check_col(pinfo->cinfo, COL_INFO)) {
273 col_add_fstr(pinfo->cinfo, COL_INFO, "Oncoming Buddy");
274 col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", buddyname);
279 proto_tree_add_text(aim_tree_fnac, tvb, 17, buddyname_length, "Screen Name: %s", buddyname);
286 buddyname_length = get_buddyname( buddyname, tvb, 16, 17 );
288 if (check_col(pinfo->cinfo, COL_INFO)) {
289 col_add_fstr(pinfo->cinfo, COL_INFO, "Offgoing Buddy");
290 col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", buddyname);
295 proto_tree_add_text(aim_tree_fnac, tvb, 17, buddyname_length, "Screen Name: %s", buddyname);
303 case FAMILY_LOCATION:
307 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Location - Error");
310 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Request Rights Information");
313 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Rights Information");
316 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Set User Information");
319 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Request User Information");
322 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "User Information");
325 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Watcher Subrequest");
328 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Watcher Notification");
337 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Advertisements - Error");
340 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Advertisement Request");
343 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Advertisement data (GIF)");
348 case FAMILY_USERLOOKUP:
352 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Search - Error (could be: not found)");
355 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Search for Screen Name by e-mail");
358 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Screen Name Search Result");
367 /* channel message from client */
368 get_message( msg, tvb, 40 + buddyname_length, tvb_length(tvb) - 40 - buddyname_length );
370 if (check_col(pinfo->cinfo, COL_INFO)) {
371 col_add_fstr(pinfo->cinfo, COL_INFO, "Chat Message ");
372 col_append_fstr(pinfo->cinfo, COL_INFO, " -> %s", msg);
377 /* channel message to client */
378 buddyname_length = get_buddyname( buddyname, tvb, 30, 31 );
379 get_message( msg, tvb, 36 + buddyname_length, tvb_length(tvb) - 36 - buddyname_length );
381 if (check_col(pinfo->cinfo, COL_INFO)) {
382 col_add_fstr(pinfo->cinfo, COL_INFO, "Chat Message ");
383 col_append_fstr(pinfo->cinfo, COL_INFO, "from: %s", buddyname);
384 col_append_fstr(pinfo->cinfo, COL_INFO, " -> %s", msg);
389 proto_tree_add_text(aim_tree_fnac, tvb, 31, buddyname_length, "Screen Name: %s", buddyname);
396 case FAMILY_MESSAGING:
400 buddyname_length = get_buddyname( buddyname, tvb, 26, 27 );
402 get_message( msg, tvb, 36 + buddyname_length, tvb_length(tvb) - 36 - buddyname_length );
404 if (check_col(pinfo->cinfo, COL_INFO)) {
405 col_add_fstr(pinfo->cinfo, COL_INFO, "Message ");
406 col_append_fstr(pinfo->cinfo, COL_INFO, "to: %s", buddyname);
407 col_append_fstr(pinfo->cinfo, COL_INFO, " -> %s", msg);
412 proto_tree_add_text(aim_tree_fnac, tvb, 27, buddyname_length, "Screen Name: %s", buddyname);
417 case MSG_FROM_CLIENT:
418 buddyname_length = get_buddyname( buddyname, tvb, 26, 27 );
420 get_message( msg, tvb, 36 + buddyname_length, tvb_length(tvb) - 36 - buddyname_length);
422 if (check_col(pinfo->cinfo, COL_INFO)) {
423 col_add_fstr(pinfo->cinfo, COL_INFO, "Message");
424 col_append_fstr(pinfo->cinfo, COL_INFO, " from: %s", buddyname);
426 col_append_fstr(pinfo->cinfo, COL_INFO, " -> %s", msg);
431 proto_tree_add_text(aim_tree_fnac, tvb, 27, buddyname_length, "Screen Name: %s", buddyname);
443 case CHANNEL_FLAP_ERR:
444 if (check_col(pinfo->cinfo, COL_INFO)) {
445 col_add_fstr(pinfo->cinfo, COL_INFO, "FLAP error");
449 case CHANNEL_CLOSE_CONN:
450 if (check_col(pinfo->cinfo, COL_INFO)) {
451 col_add_fstr(pinfo->cinfo, COL_INFO, "Close Connection");
456 if (check_col(pinfo->cinfo, COL_INFO)) {
457 col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown Channel: %d", hdr_channel );
467 static int get_buddyname( char *name, tvbuff_t *tvb, int len_offset, int name_offset)
469 guint8 buddyname_length;
471 buddyname_length = tvb_get_guint8(tvb, len_offset);
473 if(buddyname_length > MAX_BUDDYNAME_LENGTH ) buddyname_length = MAX_BUDDYNAME_LENGTH;
474 memset( name, '\0', sizeof(name));
475 tvb_get_nstringz0(tvb, name_offset, buddyname_length, name);
477 return buddyname_length;
481 static void get_message( guchar *msg, tvbuff_t *tvb, int msg_offset, int msg_length)
485 int max, tagchars = 0;
486 int new_offset = msg_offset;
487 int new_length = msg_length;
491 /* make sure nothing bigger than 1000 bytes is printed */
492 if( msg_length > 999 ) return;
494 memset( msg, '\0', 1000);
498 /* loop until HTML tag is reached - quick&dirty way to find start of message
499 * (it is nearly impossible to find the correct start offset for all client versions) */
500 while( (tagchars < 6) && (new_length > 5) )
502 j = tvb_get_guint8(tvb, new_offset);
503 if( ( (j == '<') && (tagchars == 0) ) ||
504 ( (j == 'h') && (tagchars == 1) ) ||
505 ( (j == 'H') && (tagchars == 1) ) ||
506 ( (j == 't') && (tagchars == 2) ) ||
507 ( (j == 'T') && (tagchars == 2) ) ||
508 ( (j == 'm') && (tagchars == 3) ) ||
509 ( (j == 'M') && (tagchars == 3) ) ||
510 ( (j == 'l') && (tagchars == 4) ) ||
511 ( (j == 'L') && (tagchars == 4) ) ||
512 ( (j == '>') && (tagchars == 5) ) ) tagchars++;
517 /* set offset and length of message to after the first HTML tag */
518 msg_offset = new_offset;
519 msg_length = new_length;
520 max = msg_length - 1;
523 /* find the rest of the message until either a </html> is reached or the end of the frame.
524 * All other HTML tags are stripped to display only the raw message (printable characters) */
525 while( (c < max) && (tagchars < 7) )
527 j = tvb_get_guint8(tvb, msg_offset+c);
530 /* make sure this is an HTML tag by checking the order of the chars */
531 if( ( (j == '<') && (tagchars == 0) ) ||
532 ( (j == '/') && (tagchars == 1) ) ||
533 ( (j == 'h') && (tagchars == 2) ) ||
534 ( (j == 'H') && (tagchars == 2) ) ||
535 ( (j == 't') && (tagchars == 3) ) ||
536 ( (j == 'T') && (tagchars == 3) ) ||
537 ( (j == 'm') && (tagchars == 4) ) ||
538 ( (j == 'M') && (tagchars == 4) ) ||
539 ( (j == 'l') && (tagchars == 5) ) ||
540 ( (j == 'L') && (tagchars == 5) ) ||
541 ( (j == '>') && (tagchars == 6) ) ) tagchars++;
544 if( j == '<' ) bracket = TRUE;
545 if( j == '>' ) bracket = FALSE;
546 if( (isprint(j) ) && (bracket == FALSE) && (j != '>'))
559 /* Register the protocol with Ethereal */
561 proto_register_aim(void)
564 /* Setup list of header fields */
565 static hf_register_info hf[] = {
567 { "Command Start", "aim.cmd_start", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }
570 { "Channel ID", "aim.channel", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }
573 { "Sequence Number", "aim.seqno", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }
576 { "Data Field Length", "aim.datalen", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }
578 { &hf_aim_fnac_family,
579 { "FNAC Family ID", "aim.fnac.family", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }
581 { &hf_aim_fnac_subtype,
582 { "FNAC Subtype ID", "aim.fnac.subtype", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }
586 /* Setup protocol subtree array */
587 static gint *ett[] = {
592 /* Register the protocol name and description */
593 proto_aim = proto_register_protocol("AOL Instant Messenger", "AIM", "aim");
595 /* Required function calls to register the header fields and subtrees used */
596 proto_register_field_array(proto_aim, hf, array_length(hf));
597 proto_register_subtree_array(ett, array_length(ett));
601 proto_reg_handoff_aim(void)
603 dissector_handle_t aim_handle;
605 aim_handle = create_dissector_handle(dissect_aim, proto_aim);
606 dissector_add("tcp.port", TCP_PORT_AIM, aim_handle);