wireshark/packet-aim.c

617 lines
21 KiB
C
Raw Normal View History

/* packet-aim.c
* Routines for AIM Instant Messenger (OSCAR) dissection
* Copyright 2000, Ralf Hoelzer <ralf@well.com>
*
Remove more "CHECK_DISPLAY_AS_DATA()" calls and "pinfo->current_proto =" statements. Move the setting of the Protocol column in various dissectors before anything is fetched from the packet, and also clear the Info column at that point in those and some other dissectors, so that if an exception is thrown, the columns don't reflect the previous protocol. Don't use col_add_fstr(..., "%s", string); Use col_add_str(..., string); as it does the same thing, but doesn't drag all the heavy *printf machinery in. Fix the DDTP dissector to set the Info column regardless of whether we're building a protocol tree or not, and to set it to "Encrypted payload" if the payload is encrypted. Also fix a typo in a field name. Register the FTP data dissector as being associated with the FTP data protocol, not the FTP protocol (the removed "CHECK_DISPLAY_AS_DATA()" call checked "proto_ftp_data", and the removed "pinfo->current_proto =" line set it to "FTP-DATA", so it should be associated with "proto_ftp_data"). Make the H1 dissector check whether the frame has at least 2 bytes in it before checking the first two bytes; heuristic dissectors must not throw exceptions until they've accepted the packet as one of theirs. Use "tvb_format_text()" rather than "tvb_get_ptr()" and "format_text()" in some dissectors where the result of "tvb_get_ptr()" is used only in the "format_text()" call. In the Quake dissector, don't check whether there are at least 4 bytes in the packet - if we return, the packet won't be dissected at all (it's not as if some other dissector will get to handle it), and, if we don't return, we'll throw an exception if there aren't at least 4 bytes in the packet, so the packet will be marked as short or malformed, as appropriate. In the RIPng dissector, associate the table of strings for the command field with the command field, so that the dissector doesn't have to format the string for the protocol tree entry itself, and so that the filter construction dialog box can let you select "Request" or "Response" from a list rather than requiring you to know the values for "Request" and "Response". Make "dissect_rpc()" static, as it's called only through a heuristic dissector list. Use "col_set_str()" to set the COL_PROTOCOL column for RPC protocols; the string used is from a table provided by the dissector, and is a string constant. Don't format the Info column for WSP into a buffer and then format that buffer into the column with "%s" - "col_add_fstr()" can do the formatting for you, without having to allocate your own buffer (or run through the *printf machinery twice). Don't fetch fields from the WTP packet until you're ready to use them, so that you don't throw an exception before you even set the Protocol column or clear the Info column. Use "pinfo->destport", not "pi.destport", in the Zebra dissector when checking whether the packet is a request or reply, and do the check by comparing with "pinfo->match_port" rather than TCP_PORT_ZEBRA (so that if the dissector is ever registered on another port, it still correctly determines whether the packet is a request or reply - the Network Monitor HTTP dissector has port 80 wired into its brain, which is a bit irritating if you're trying to get it to dissect HTTP proxy traffic on port 3128 or proxy administration UI traffic on port 3132). svn path=/trunk/; revision=2931
2001-01-22 08:03:46 +00:00
* $Id: packet-aim.c,v 1.7 2001/01/22 08:03:44 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
#define FAMILY_SIGNON 0x0017
/* messaging */
#define MSG_TO_CLIENT 0x006
#define MSG_FROM_CLIENT 0x007
static void dissect_aim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
static void get_message( u_char *msg, tvbuff_t *tvb, int msg_offset, int msg_length);
static 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 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_set_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) - 6, "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_SIGNON:
switch(subtype)
{
case 0x0002:
buddyname_length = get_buddyname( buddyname, tvb, 19, 20 );
if (check_col(pinfo->fd, COL_INFO)) {
col_add_fstr(pinfo->fd, COL_INFO, "Login");
col_append_fstr(pinfo->fd, COL_INFO, ": %s", buddyname);
}
if( tree )
{
proto_tree_add_text(aim_tree_fnac, tvb, 20, buddyname_length, "Screen Name: %s", buddyname);
}
break;
case 0x0003:
if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Login information reply");
break;
case 0x0006:
buddyname_length = get_buddyname( buddyname, tvb, 19, 20 );
if (check_col(pinfo->fd, COL_INFO)) {
col_add_fstr(pinfo->fd, COL_INFO, "Sign-on");
col_append_fstr(pinfo->fd, COL_INFO, ": %s", buddyname);
}
if( tree )
{
proto_tree_add_text(aim_tree_fnac, tvb, 20, buddyname_length, "Screen Name: %s", buddyname);
}
break;
case 0x0007:
if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "Sign-on reply");
break;
}
break;
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_CHAT:
switch(subtype)
{
case 0x005:
/* channel message from client */
get_message( msg, tvb, 40 + buddyname_length, tvb_length(tvb) - 40 - buddyname_length );
if (check_col(pinfo->fd, COL_INFO)) {
col_add_fstr(pinfo->fd, COL_INFO, "Chat Message ");
col_append_fstr(pinfo->fd, COL_INFO, " -> %s", msg);
}
break;
case 0x006:
/* channel message to client */
buddyname_length = get_buddyname( buddyname, tvb, 30, 31 );
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, "Chat 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, 31, buddyname_length, "Screen Name: %s", buddyname);
}
break;
}
break;
case FAMILY_MESSAGING:
switch(subtype)
{
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;
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;
}
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;
}
}
static 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;
}
static 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", "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, proto_aim);
}