--- /dev/null
+/* packet-aim.c
+ * Routines for AIM Instant Messenger (OSCAR) dissection
+ * Copyright 2000, Ralf Hoelzer <ralf@well.com>
+ *
+ * $Id: packet-aim.c,v 1.1 2000/11/06 00:07:04 guy Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@unicom.net>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+#include <glib.h>
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
+#include "packet.h"
+#include "strutil.h"
+
+#define TCP_PORT_AIM 5190
+#define MAX_BUDDYNAME_LENGTH 30
+
+#define STRIP_TAGS 1
+
+/* channels */
+#define CHANNEL_NEW_CONN 0x01
+#define CHANNEL_SNAC_DATA 0x02
+#define CHANNEL_FLAP_ERR 0x03
+#define CHANNEL_CLOSE_CONN 0x04
+
+/* SNAC families */
+#define FAMILY_GENERIC 0x0001
+#define FAMILY_LOCATION 0x0002
+#define FAMILY_BUDDYLIST 0x0003
+#define FAMILY_MESSAGING 0x0004
+#define FAMILY_ADVERTS 0x0005
+#define FAMILY_INVITATION 0x0006
+#define FAMILY_ADMIN 0x0007
+#define FAMILY_POPUP 0x0008
+#define FAMILY_BOS 0x0009
+#define FAMILY_USERLOOKUP 0x000A
+#define FAMILY_STATS 0x000B
+#define FAMILY_TRANSLATE 0x000C
+#define FAMILY_CHAT_NAV 0x000D
+#define FAMILY_CHAT 0x000E
+
+/* messaging */
+#define MSG_FROM_CLIENT 0x006
+#define MSG_TO_CLIENT 0x007
+
+static void dissect_aim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
+void get_message( u_char *msg, tvbuff_t *tvb, int msg_offset, int msg_length);
+int get_buddyname( char *name, tvbuff_t *tvb, int len_offset, int name_offset);
+
+/* Initialize the protocol and registered fields */
+static int proto_aim = -1;
+static int hf_aim_cmd_start = -1;
+static int hf_aim_channel = -1;
+static int hf_aim_seqno = -1;
+static int hf_aim_data_len = -1;
+static int hf_aim_fnac_family = -1;
+static int hf_aim_fnac_subtype = -1;
+
+/* Initialize the subtree pointers */
+static gint ett_aim = -1;
+static gint ett_aim_fnac = -1;
+
+/* Code to actually dissect the packets */
+static void dissect_aim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ /* Header fields */
+ unsigned char hdr_channel; /* channel ID */
+ unsigned short hdr_sequence_no; /* Internal frame sequence number, not needed */
+ unsigned short hdr_data_field_length; /* length of data within frame */
+
+
+ guint16 family;
+ guint16 subtype;
+ guint8 buddyname_length = 0;
+ char buddyname[MAX_BUDDYNAME_LENGTH];
+ u_char msg[1000];
+
+/* Set up structures we will need to add the protocol subtree and manage it */
+ proto_item *ti;
+ proto_item *ti1;
+ proto_tree *aim_tree = NULL;
+ proto_tree *aim_tree_fnac = NULL;
+
+/* Check if protocol decoding is enabled else decode as data and return */
+
+ CHECK_DISPLAY_AS_DATA(proto_aim, tvb, pinfo, tree);
+
+/* load the display labels */
+ pinfo->current_proto = "aim";
+
+/* check, if this is really an AIM packet, they start with 0x2a */
+
+ if(!(tvb_get_guint8(tvb, 0) == 0x2a)) {
+ /* Not an instant messenger packet, just happened to use the same port */
+ return;
+ }
+
+/* Make entries in Protocol column and Info column on summary display */
+ if (check_col(pinfo->fd, COL_PROTOCOL))
+ col_add_str(pinfo->fd, COL_PROTOCOL, "AIM");
+
+ if (check_col(pinfo->fd, COL_INFO))
+ col_add_str(pinfo->fd, COL_INFO, "AOL Instant Messenger");
+
+/* get relevant header information */
+
+ hdr_channel = tvb_get_guint8(tvb, 1);
+ hdr_sequence_no = tvb_get_ntohs(tvb, 2);
+ hdr_data_field_length = tvb_get_ntohs(tvb, 4);
+
+/* In the interest of speed, if "tree" is NULL, don't do any work not
+ necessary to generate protocol tree items. */
+ if (tree) {
+
+ ti = proto_tree_add_item(tree, proto_aim, tvb, 0, tvb_length(tvb), FALSE);
+ aim_tree = proto_item_add_subtree(ti, ett_aim);
+ proto_tree_add_uint(aim_tree, hf_aim_cmd_start, tvb, 0, 1, '*');
+ proto_tree_add_uint(aim_tree, hf_aim_channel, tvb, 1, 1, hdr_channel);
+ proto_tree_add_uint(aim_tree, hf_aim_seqno, tvb, 2, 2, hdr_sequence_no);
+ proto_tree_add_uint(aim_tree, hf_aim_data_len, tvb, 4, 2, hdr_data_field_length);
+
+ }
+
+
+
+ switch(hdr_channel)
+ {
+ /* New connection request */
+ case CHANNEL_NEW_CONN:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "New Connection");
+ break;
+
+ /* SNAC channel. Most packets are of this type, such as messages or buddy list
+ * management.
+ */
+ case CHANNEL_SNAC_DATA:
+ family = tvb_get_ntohs(tvb, 6);
+ subtype = tvb_get_ntohs(tvb, 8);
+
+ if (check_col(pinfo->fd, COL_INFO)) {
+ col_add_fstr(pinfo->fd, COL_INFO, "SNAC data");
+ }
+ if( tree )
+ {
+ ti1 = proto_tree_add_text(aim_tree, tvb, 6, tvb_length(tvb), "FNAC");
+ aim_tree_fnac = proto_item_add_subtree(ti1, ett_aim_fnac);
+ proto_tree_add_uint(aim_tree_fnac, hf_aim_fnac_family, tvb, 6, 2, family);
+ proto_tree_add_uint(aim_tree_fnac, hf_aim_fnac_subtype, tvb, 8, 2, subtype);
+ }
+
+
+
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Family: %d - Subtype: %d (unknown)", family, subtype);
+
+ switch(family)
+ {
+ case FAMILY_GENERIC:
+ switch(subtype)
+ {
+ case 0x0002:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Client is now online and ready for normal function");
+ break;
+ case 0x0003:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Server is now ready for normal functions");
+ break;
+ case 0x0004:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Request for new service (server will redirect client)");
+ break;
+ case 0x0005:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Redirect response");
+ break;
+ case 0x0006:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Request Rate Information");
+ break;
+ case 0x0007:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Rate information response");
+ break;
+ case 0x0008:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Rate Information Response Ack");
+ break;
+ case 0x0016:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "No-op");
+ break;
+ }
+ break;
+
+ case FAMILY_BUDDYLIST:
+ switch(subtype)
+ {
+ case 0x0001:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Buddylist - Error");
+ break;
+
+ case 0x0002:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Request Rights information");
+ break;
+
+ case 0x0003:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Rights information");
+ break;
+
+ case 0x0004:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Add to Buddylist");
+ break;
+
+ case 0x0005:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Remove from Buddylist");
+ break;
+
+ case 0x000b:
+ buddyname_length = get_buddyname( buddyname, tvb, 16, 17 );
+
+ if (check_col(pinfo->fd, COL_INFO)) {
+ col_add_fstr(pinfo->fd, COL_INFO, "Oncoming Buddy");
+ col_append_fstr(pinfo->fd, COL_INFO, ": %s", buddyname);
+ }
+
+ if( tree )
+ {
+ proto_tree_add_text(aim_tree_fnac, tvb, 17, buddyname_length, "Screen Name: %s", buddyname);
+ }
+
+ break;
+
+ case 0x000c:
+
+ buddyname_length = get_buddyname( buddyname, tvb, 16, 17 );
+
+ if (check_col(pinfo->fd, COL_INFO)) {
+ col_add_fstr(pinfo->fd, COL_INFO, "Offgoing Buddy");
+ col_append_fstr(pinfo->fd, COL_INFO, ": %s", buddyname);
+ }
+
+ if( tree )
+ {
+ proto_tree_add_text(aim_tree_fnac, tvb, 17, buddyname_length, "Screen Name: %s", buddyname);
+ }
+
+
+ break;
+ }
+ break;
+
+ case FAMILY_LOCATION:
+ switch(subtype)
+ {
+ case 0x0001:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Location - Error");
+ break;
+ case 0x0002:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Request Rights Information");
+ break;
+ case 0x0003:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Rights Information");
+ break;
+ case 0x0004:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Set User Information");
+ break;
+ case 0x0005:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Request User Information");
+ break;
+ case 0x0006:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "User Information");
+ break;
+ case 0x0007:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Watcher Subrequest");
+ break;
+ case 0x0008:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Watcher Notification");
+ break;
+ }
+ break;
+
+ case FAMILY_ADVERTS:
+ switch(subtype)
+ {
+ case 0x0001:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Advertisements - Error");
+ break;
+ case 0x0002:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Advertisement Request");
+ break;
+ case 0x0003:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Advertisement data (GIF)");
+ break;
+ }
+ break;
+
+ case FAMILY_USERLOOKUP:
+ switch(subtype)
+ {
+ case 0x0001:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Search - Error (could be: not found)");
+ break;
+ case 0x0002:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Search for Screen Name by e-mail");
+ break;
+ case 0x0003:
+ if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Screen Name Search Result");
+ break;
+ }
+ break;
+
+ case FAMILY_MESSAGING:
+ switch(subtype)
+ {
+ case MSG_FROM_CLIENT:
+ buddyname_length = get_buddyname( buddyname, tvb, 26, 27 );
+
+ get_message( msg, tvb, 36 + buddyname_length, tvb_length(tvb) - 36 - buddyname_length );
+
+ if (check_col(pinfo->fd, COL_INFO)) {
+ col_add_fstr(pinfo->fd, COL_INFO, "Message ");
+ col_append_fstr(pinfo->fd, COL_INFO, "from: %s", buddyname);
+ col_append_fstr(pinfo->fd, COL_INFO, " -> %s", msg);
+ }
+
+ if( tree )
+ {
+ proto_tree_add_text(aim_tree_fnac, tvb, 27, buddyname_length, "Screen Name: %s", buddyname);
+ }
+
+ break;
+
+ case MSG_TO_CLIENT:
+ buddyname_length = get_buddyname( buddyname, tvb, 26, 27 );
+
+ get_message( msg, tvb, 36 + buddyname_length, tvb_length(tvb) - 36 - buddyname_length);
+
+ if (check_col(pinfo->fd, COL_INFO)) {
+ col_add_fstr(pinfo->fd, COL_INFO, "Message");
+ col_append_fstr(pinfo->fd, COL_INFO, " to: %s", buddyname);
+
+ col_append_fstr(pinfo->fd, COL_INFO, " -> %s", msg);
+ }
+
+ if( tree )
+ {
+ proto_tree_add_text(aim_tree_fnac, tvb, 27, buddyname_length, "Screen Name: %s", buddyname);
+ }
+ break;
+ }
+
+ break;
+ }
+
+
+
+ break;
+
+ case CHANNEL_FLAP_ERR:
+ if (check_col(pinfo->fd, COL_INFO)) {
+ col_add_fstr(pinfo->fd, COL_INFO, "FLAP error");
+ }
+ break;
+
+ case CHANNEL_CLOSE_CONN:
+ if (check_col(pinfo->fd, COL_INFO)) {
+ col_add_fstr(pinfo->fd, COL_INFO, "Close Connection");
+ }
+ break;
+
+ default:
+ if (check_col(pinfo->fd, COL_INFO)) {
+ col_add_fstr(pinfo->fd, COL_INFO, "Unknown Channel: %d", hdr_channel );
+ }
+ break;
+ }
+
+
+
+}
+
+
+int get_buddyname( char *name, tvbuff_t *tvb, int len_offset, int name_offset)
+{
+ guint8 buddyname_length;
+
+ buddyname_length = tvb_get_guint8(tvb, len_offset);
+
+ if(buddyname_length > MAX_BUDDYNAME_LENGTH ) buddyname_length = MAX_BUDDYNAME_LENGTH;
+ memset( name, '\0', sizeof(name));
+ tvb_get_nstringz0(tvb, name_offset, buddyname_length, name);
+
+ return buddyname_length;
+}
+
+
+void get_message( u_char *msg, tvbuff_t *tvb, int msg_offset, int msg_length)
+{
+ int i,j,c;
+ int bracket = FALSE;
+ int max, tagchars = 0;
+ int new_offset = msg_offset;
+ int new_length = msg_length;
+
+
+
+ /* make sure nothing bigger than 1000 bytes is printed */
+ if( msg_length > 999 ) return;
+
+ memset( msg, '\0', 1000);
+ i = 0;
+ c = 0;
+
+ /* loop until HTML tag is reached - quick&dirty way to find start of message
+ * (it is nearly impossible to find the correct start offset for all client versions) */
+ while( (tagchars < 6) && (new_length > 5) )
+ {
+ j = tvb_get_guint8(tvb, new_offset);
+ if( ( (j == '<') && (tagchars == 0) ) ||
+ ( (j == 'h') && (tagchars == 1) ) ||
+ ( (j == 'H') && (tagchars == 1) ) ||
+ ( (j == 't') && (tagchars == 2) ) ||
+ ( (j == 'T') && (tagchars == 2) ) ||
+ ( (j == 'm') && (tagchars == 3) ) ||
+ ( (j == 'M') && (tagchars == 3) ) ||
+ ( (j == 'l') && (tagchars == 4) ) ||
+ ( (j == 'L') && (tagchars == 4) ) ||
+ ( (j == '>') && (tagchars == 5) ) ) tagchars++;
+ new_offset++;
+ new_length--;
+ }
+
+ /* set offset and length of message to after the first HTML tag */
+ msg_offset = new_offset;
+ msg_length = new_length;
+ max = msg_length - 1;
+ tagchars = 0;
+
+ /* find the rest of the message until either a </html> is reached or the end of the frame.
+ * All other HTML tags are stripped to display only the raw message (printable characters) */
+ while( (c < max) && (tagchars < 7) )
+ {
+ j = tvb_get_guint8(tvb, msg_offset+c);
+
+
+ /* make sure this is an HTML tag by checking the order of the chars */
+ if( ( (j == '<') && (tagchars == 0) ) ||
+ ( (j == '/') && (tagchars == 1) ) ||
+ ( (j == 'h') && (tagchars == 2) ) ||
+ ( (j == 'H') && (tagchars == 2) ) ||
+ ( (j == 't') && (tagchars == 3) ) ||
+ ( (j == 'T') && (tagchars == 3) ) ||
+ ( (j == 'm') && (tagchars == 4) ) ||
+ ( (j == 'M') && (tagchars == 4) ) ||
+ ( (j == 'l') && (tagchars == 5) ) ||
+ ( (j == 'L') && (tagchars == 5) ) ||
+ ( (j == '>') && (tagchars == 6) ) ) tagchars++;
+
+#ifdef STRIP_TAGS
+ if( j == '<' ) bracket = TRUE;
+ if( j == '>' ) bracket = FALSE;
+ if( (isprint(j) ) && (bracket == FALSE) && (j != '>'))
+#else
+ if( isprint(j) )
+#endif
+ {
+ msg[i] = j;
+ i++;
+ }
+ c++;
+ }
+}
+
+
+/* Register the protocol with Ethereal */
+void proto_register_aim(void)
+{
+
+/* Setup list of header fields */
+ static hf_register_info hf[] = {
+ { &hf_aim_cmd_start,
+ { "Command Start", "aim.cmd_start", FT_UINT8, BASE_HEX, NULL, 0x0, "" }
+ },
+ { &hf_aim_channel,
+ { "Channel ID", "aim.channel", FT_UINT8, BASE_HEX, NULL, 0x0, "" }
+ },
+ { &hf_aim_seqno,
+ { "Sequence Number", "aim.seqno", FT_UINT16, BASE_DEC, NULL, 0x0, "" }
+ },
+ { &hf_aim_data_len,
+ { "Data Field Length", "aim.datalen", FT_UINT16, BASE_DEC, NULL, 0x0, "" }
+ },
+ { &hf_aim_fnac_family,
+ { "FNAC Family ID", "aim.fnac.family", FT_UINT16, BASE_HEX, NULL, 0x0, "" }
+ },
+ { &hf_aim_fnac_subtype,
+ { "FNAC Subtype ID", "aim.fnac.subtype", FT_UINT16, BASE_HEX, NULL, 0x0, "" }
+ },
+ };
+
+/* Setup protocol subtree array */
+ static gint *ett[] = {
+ &ett_aim,
+ &ett_aim_fnac,
+ };
+
+/* Register the protocol name and description */
+ proto_aim = proto_register_protocol("AOL Instant Messenger", "aim");
+
+/* Required function calls to register the header fields and subtrees used */
+ proto_register_field_array(proto_aim, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+};
+
+void
+proto_reg_handoff_aim(void)
+{
+ dissector_add("tcp.port", TCP_PORT_AIM, &dissect_aim);
+}