Ethereal-dev: [Ethereal-dev] AIM / OSCAR dissector

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Ralf Holzer <ralf@xxxxxxxx>
Date: Thu, 2 Nov 2000 01:07:50 -0500
Hi,

I am new to this list, so please excuse my ignorance :).

I just wrote a dissector for the OSCAR protocol used by AOL Instant 
Messenger. (OSCAR is the closed protocol used by the "official" clients).

The code is probably not quite ready for prime time, but it dissects the most 
important parts of OSCAR traffic. There are still some issues with the 
correct offsets for the messages, because they vary from client to client.

I know there is lots of room for improvement, but I would appreciate it, if I 
could get some feedback on it.

The code is attached.

regards,
Ralf

/* packet-aim.c
 * Routines for AIM Instant Messenger (OSCAR) dissection
 * Copyright 2000, Ralf Hoelzer <ralf@xxxxxxxx>
 *
 * $Id: README.developer,v 1.17 2000/10/22 20:01:14 guy Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxx>
 * 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>

#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 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(const u_char *pd, int offset, frame_data *fd, proto_tree *tree); 
void get_message( u_char *msg, const u_char *pd, int msg_offset, int msg_length);

/* 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(const u_char *pd, int offset, frame_data *fd, 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 */


  int family;
  int subtype;
  int buddyname_length ;
  int msg_length ;
  int client_type;
  int msg_offset = 0;
  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;
  proto_tree *aim_tree_fnac;

/* Check if protocol decoding is enabled else decode as data and return */

  OLD_CHECK_DISPLAY_AS_DATA(proto_aim, pd, offset, fd, tree);

/* check, if this is really an AIM packet, they start with 0x2a */

  if(!(pd[offset] == 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(fd, COL_PROTOCOL)) 
    col_add_str(fd, COL_PROTOCOL, "AIM");
    
  if (check_col(fd, COL_INFO)) 
    col_add_str(fd, COL_INFO, "AOL Instant Messenger");

/* get relevant header information */

  hdr_channel           = pd[offset + 1];
  hdr_sequence_no       = pntohs(&pd[offset + 2]);     
  hdr_data_field_length = pntohs(&pd[offset + 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, NullTVB, offset, END_OF_FRAME, FALSE);
    aim_tree = proto_item_add_subtree(ti, ett_aim);
    proto_tree_add_uint(aim_tree, hf_aim_cmd_start, NullTVB, offset, 1, '*');
    proto_tree_add_uint(aim_tree, hf_aim_channel, NullTVB, offset + 1, 1, hdr_channel);
    proto_tree_add_uint(aim_tree, hf_aim_seqno, NullTVB, offset + 2, 2, hdr_sequence_no);
    proto_tree_add_uint(aim_tree, hf_aim_data_len, NullTVB, offset + 4, 2, hdr_data_field_length);

  }



  switch(hdr_channel)
  {
    /* New connection request */
    case CHANNEL_NEW_CONN:
      if (check_col(fd, COL_INFO)) col_add_fstr(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 = pntohs(&pd[offset + 6]);     
      subtype = pntohs(&pd[offset + 8]);     

      if (check_col(fd, COL_INFO)) {
        col_add_fstr(fd, COL_INFO, "SNAC data");
      }        
      if( tree )
      {
        ti1 = proto_tree_add_text(aim_tree, NullTVB, offset + 6, END_OF_FRAME, "FNAC");
        aim_tree_fnac = proto_item_add_subtree(ti1, ett_aim_fnac);
        proto_tree_add_uint(aim_tree_fnac, hf_aim_fnac_family, NullTVB, offset + 6, 2, family);
        proto_tree_add_uint(aim_tree_fnac, hf_aim_fnac_subtype, NullTVB, offset + 8, 2, subtype);
      }



      if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Family: %d - Subtype: %d", family, subtype);
      
        switch(family)
        {
          case FAMILY_GENERIC:
            switch(subtype)
            {
              case 0x0002:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Client is now online and ready for normal function");        
                break;
              case 0x0003:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Server is now ready for normal functions");        
                break;
              case 0x0004:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Request for new service (server will redirect client)");        
                break;
              case 0x0005:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Redirect response");        
                break;
              case 0x0006:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Request Rate Information");        
                break;
              case 0x0007:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Rate information response");        
                break;
              case 0x0008:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Rate Information Response Ack");        
                break;
              case 0x0016:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "No-op");        
                break;
            }
            break;

          case FAMILY_BUDDYLIST:
            switch(subtype)
            {
              case 0x0001:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Buddylist - Error");        
                break;

              case 0x0002:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Request Rights information");        
                break;

              case 0x0003:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Rights information");        
                break;

              case 0x0004:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Add to Buddylist");        
                break;

              case 0x0005:
                if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Remove from Buddylist");        
                break;

              case 0x000b:
                buddyname_length = pd[offset + 16];

                if (check_col(fd, COL_INFO)) {
                  col_add_fstr(fd, COL_INFO, "Oncoming Buddy");
                  col_append_fstr(fd, COL_INFO, ": %s", format_text(&pd[offset+17], buddyname_length));
                }        
                break;

              case 0x000c:
                buddyname_length = pd[offset + 16];

                if (check_col(fd, COL_INFO)) {
                  col_add_fstr(fd, COL_INFO, "Offgoing Buddy");
                  col_append_fstr(fd, COL_INFO, ": %s", format_text(&pd[offset+17], buddyname_length));
                }        
                break;
            }
          break;

        case FAMILY_LOCATION:
          switch(subtype)
          {
            case 0x0001:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Location - Error");        
              break;
            case 0x0002:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Request Rights Information");        
              break;
            case 0x0003:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Rights Information");        
              break;
            case 0x0004:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Set User Information");        
              break;
            case 0x0005:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Request User Information");        
              break;
            case 0x0006:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "User Information");        
              break;
            case 0x0007:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Watcher Subrequest");        
              break;
            case 0x0008:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Watcher Notification");        
              break;
          }
          break;

        case FAMILY_ADVERTS:
          switch(subtype)
          {
            case 0x0001:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Advertisements - Error");        
              break;
            case 0x0002:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Advertisement Request");        
              break;
            case 0x0003:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Advertisement data (GIF)");        
              break;
          }
          break;

        case FAMILY_USERLOOKUP:
          switch(subtype)
          {
            case 0x0001:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Search - Error (could be: not found)");        
              break;
            case 0x0002:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Search for Screen Name by e-mail");        
              break;
            case 0x0003:
              if (check_col(fd, COL_INFO)) col_add_fstr(fd, COL_INFO, "Screen Name Search Result");        
              break;
          }
          break;

        case FAMILY_MESSAGING:
          switch(subtype)
          {
            case MSG_FROM_CLIENT:
              buddyname_length = pd[offset + 26];

              msg_length = hdr_data_field_length - 6 - 21 - buddyname_length - 14;

              get_message( msg, pd, offset + 6 +  hdr_data_field_length - msg_length,  msg_length);
              
              if (check_col(fd, COL_INFO)) {
                col_add_fstr(fd, COL_INFO, "Message ");
                col_append_fstr(fd, COL_INFO, "from: %s", format_text(&pd[offset+27], buddyname_length));
                col_append_fstr(fd, COL_INFO, " -> %s", msg);
              }        

              if( tree )
              {
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27, buddyname_length, "Screen Name: %s",
                  format_text(&pd[offset + 27], buddyname_length));       
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 6 + hdr_data_field_length - msg_length, 
                    msg_length, "Message");
              }

              break;

            case MSG_TO_CLIENT:
              buddyname_length = pd[offset + 26];
              client_type = pntohs(&pd[offset + 6 + 21 + buddyname_length + 2]);     
              
              
              switch( client_type )
              {
                case 0x0004:
                  msg_offset = 46;
                  break;

                case 0x0005:
                  msg_offset = 53;
                  break;

                case 0x0006:
                  msg_offset = 79;
                  break;

                default:
                  msg_offset = 0;
                  break;
              }

              msg_length = hdr_data_field_length - 6 - 21 - buddyname_length - msg_offset;

              /* only display message if offset is known for client type */
              if( msg_offset != 0) get_message( msg, pd, offset + 6 + hdr_data_field_length - msg_length,  msg_length);

              if (check_col(fd, COL_INFO)) {
                col_add_fstr(fd, COL_INFO, "Message");
                col_append_fstr(fd, COL_INFO, " to: %s", format_text(&pd[offset+27], buddyname_length));
                col_append_fstr(fd, COL_INFO, " len: %d", msg_length);
                col_append_fstr(fd, COL_INFO, " type: %d", client_type);

                /* only display message if offset is known for cleint type */
                if( msg_offset != 0 ) col_append_fstr(fd, COL_INFO, " -> %s", msg);
              }        
              
              if( tree )
              {
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27, buddyname_length, "Screen Name: %s",
                  format_text(&pd[offset + 27], buddyname_length));       
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27 + buddyname_length, 2, "Warning Level");
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27 + buddyname_length + 2, 2, "Class (0x0004 for Free, 0x0003 for AOL)");
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27 + buddyname_length + 4, 2, "0x001");
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27 + buddyname_length + 6, 2, "0x002");
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27 + buddyname_length + 8, 2, "Class Part Two (0x0010 for Free, 0x0004 for AOL)");
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27 + buddyname_length + 10, 2, "0x004");
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27 + buddyname_length + 12, 2, "0x002");
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27 + buddyname_length + 14, 4, "member since");
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27 + buddyname_length + 18, 2, "0x003");
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27 + buddyname_length + 20, 2, "0x004");
                proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 27 + buddyname_length + 22, 4, "on since");
                if( msg_offset != 0 ) 
                  proto_tree_add_text(aim_tree_fnac, NullTVB, offset + 6 + hdr_data_field_length - msg_length, msg_length, "Message");
              }
              break;
          }

          break;
      }
      

      
      break;
    
    case CHANNEL_FLAP_ERR:
      if (check_col(fd, COL_INFO)) {
        col_add_fstr(fd, COL_INFO, "FLAP error");
      }        
      break;
    
    case CHANNEL_CLOSE_CONN:
      if (check_col(fd, COL_INFO)) {
        col_add_fstr(fd, COL_INFO, "Close Connection");
      }        
      break;
    
    default:
      if (check_col(fd, COL_INFO)) {
        col_add_fstr(fd, COL_INFO, "Unknown Channel: %d", hdr_channel );
      }        
      break;
  }



}

void get_message( u_char *msg, const u_char *pd, int msg_offset, int msg_length)
{
  int i,j,c;
  int bracket = FALSE;
  int max;

  max = msg_length - 1;

  /* make sure nothing bigger than 1000 bytes is printed */
  if( msg_length > 999 ) return;

  memset( msg, '\0', 1000);
  i = 0;
  c = 0;
  while( c < max )
  {
     j = pd[msg_offset+c];
#ifdef STRIP_TAGS
     if( j == '<' ) bracket = TRUE;
     if( j == '>' ) bracket = FALSE; 
     if( ( j!= '\0' && bracket == FALSE) && (j != '>'))
#else
     if( j != '\0' )
#endif
     {
       msg[i] = j;
       i++;
     }
     else
     {
       printf("Warning: null byte\n");
       fflush(NULL);
     }
     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)
{
  old_dissector_add("tcp.port", TCP_PORT_AIM, &dissect_aim);
}