7a09966eae5ff27ea577af6e77a0fbfb14c9c7a3
[obnox/wireshark/wip.git] / packet-aim.c
1 /* packet-aim.c
2  * Routines for AIM Instant Messenger (OSCAR) dissection
3  * Copyright 2000, Ralf Hoelzer <ralf@well.com>
4  *
5  * $Id: packet-aim.c,v 1.12 2002/01/20 23:42:51 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
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.
15  * 
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.
20  * 
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.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
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 #include <glib.h>
44
45 #ifdef NEED_SNPRINTF_H
46 # include "snprintf.h"
47 #endif
48
49 #include "packet.h"
50 #include "strutil.h"
51
52 #define TCP_PORT_AIM 5190
53 #define MAX_BUDDYNAME_LENGTH 30
54
55 #define STRIP_TAGS 1
56
57 /* channels */
58 #define CHANNEL_NEW_CONN    0x01
59 #define CHANNEL_SNAC_DATA   0x02
60 #define CHANNEL_FLAP_ERR    0x03
61 #define CHANNEL_CLOSE_CONN  0x04
62
63 /* SNAC families */
64 #define FAMILY_GENERIC    0x0001
65 #define FAMILY_LOCATION   0x0002
66 #define FAMILY_BUDDYLIST  0x0003
67 #define FAMILY_MESSAGING  0x0004
68 #define FAMILY_ADVERTS    0x0005
69 #define FAMILY_INVITATION 0x0006
70 #define FAMILY_ADMIN      0x0007
71 #define FAMILY_POPUP      0x0008
72 #define FAMILY_BOS        0x0009
73 #define FAMILY_USERLOOKUP 0x000A
74 #define FAMILY_STATS      0x000B
75 #define FAMILY_TRANSLATE  0x000C
76 #define FAMILY_CHAT_NAV   0x000D
77 #define FAMILY_CHAT       0x000E
78 #define FAMILY_SIGNON     0x0017
79
80 /* messaging */
81 #define MSG_TO_CLIENT     0x006
82 #define MSG_FROM_CLIENT   0x007
83
84 static void dissect_aim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
85
86 static void get_message( u_char *msg, tvbuff_t *tvb, int msg_offset, int msg_length);
87 static int get_buddyname( char *name, tvbuff_t *tvb, int len_offset, int name_offset);
88
89 /* Initialize the protocol and registered fields */
90 static int proto_aim = -1;
91 static int hf_aim_cmd_start = -1;
92 static int hf_aim_channel = -1;
93 static int hf_aim_seqno = -1;
94 static int hf_aim_data_len = -1;
95 static int hf_aim_fnac_family = -1;
96 static int hf_aim_fnac_subtype = -1;
97
98 /* Initialize the subtree pointers */
99 static gint ett_aim          = -1;
100 static gint ett_aim_fnac     = -1;
101
102 /* Code to actually dissect the packets */
103 static void dissect_aim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
104 {
105   /* Header fields */
106   unsigned char  hdr_channel;           /* channel ID */
107   unsigned short hdr_sequence_no;       /* Internal frame sequence number, not needed */
108   unsigned short hdr_data_field_length; /* length of data within frame */
109
110   guint16 family;
111   guint16 subtype;
112   guint8 buddyname_length = 0;
113   char buddyname[MAX_BUDDYNAME_LENGTH];
114   u_char msg[1000];
115
116 /* Set up structures we will need to add the protocol subtree and manage it */
117   proto_item *ti;
118   proto_item *ti1;
119   proto_tree *aim_tree = NULL;
120   proto_tree *aim_tree_fnac = NULL;
121
122 /* check, if this is really an AIM packet, they start with 0x2a */
123
124   if(!(tvb_get_guint8(tvb, 0) == 0x2a)) {
125     /* Not an instant messenger packet, just happened to use the same port */
126     return;
127   }
128   
129 /* Make entries in Protocol column and Info column on summary display */
130   if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
131     col_set_str(pinfo->cinfo, COL_PROTOCOL, "AIM");
132     
133   if (check_col(pinfo->cinfo, COL_INFO)) 
134     col_add_str(pinfo->cinfo, COL_INFO, "AOL Instant Messenger");
135
136 /* get relevant header information */
137
138   hdr_channel           = tvb_get_guint8(tvb, 1);
139   hdr_sequence_no       = tvb_get_ntohs(tvb, 2);     
140   hdr_data_field_length = tvb_get_ntohs(tvb, 4);     
141
142 /* In the interest of speed, if "tree" is NULL, don't do any work not
143    necessary to generate protocol tree items. */
144   if (tree) {
145     
146     ti = proto_tree_add_item(tree, proto_aim, tvb, 0, tvb_length(tvb), FALSE); 
147     aim_tree = proto_item_add_subtree(ti, ett_aim);
148     proto_tree_add_uint(aim_tree, hf_aim_cmd_start, tvb, 0, 1, '*');  
149     proto_tree_add_uint(aim_tree, hf_aim_channel, tvb, 1, 1, hdr_channel);
150     proto_tree_add_uint(aim_tree, hf_aim_seqno, tvb, 2, 2, hdr_sequence_no);
151     proto_tree_add_uint(aim_tree, hf_aim_data_len, tvb, 4, 2, hdr_data_field_length);
152
153   }
154
155
156
157   switch(hdr_channel)
158   {
159     /* New connection request */
160     case CHANNEL_NEW_CONN:
161       if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "New Connection");
162       break;
163      
164     /* SNAC channel. Most packets are of this type, such as messages or buddy list
165      * management.
166      */
167     case CHANNEL_SNAC_DATA:
168       family = tvb_get_ntohs(tvb, 6);     
169       subtype = tvb_get_ntohs(tvb, 8);     
170
171       if (check_col(pinfo->cinfo, COL_INFO)) {
172         col_add_fstr(pinfo->cinfo, COL_INFO, "SNAC data");
173       }        
174       if( tree )
175       {
176         ti1 = proto_tree_add_text(aim_tree, tvb, 6, tvb_length(tvb) - 6, "FNAC");
177         aim_tree_fnac = proto_item_add_subtree(ti1, ett_aim_fnac);
178         proto_tree_add_uint(aim_tree_fnac, hf_aim_fnac_family, tvb, 6, 2, family);
179         proto_tree_add_uint(aim_tree_fnac, hf_aim_fnac_subtype, tvb, 8, 2, subtype);
180       }
181
182
183
184       if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Family: 0x%04x - Subtype: 0x%04x (unknown)", family, subtype);
185       
186         switch(family)
187         {
188           case FAMILY_SIGNON:
189             switch(subtype)
190             {
191               case 0x0002:
192                 buddyname_length = get_buddyname( buddyname, tvb, 19, 20 );
193
194                 if (check_col(pinfo->cinfo, COL_INFO)) {
195                   col_add_fstr(pinfo->cinfo, COL_INFO, "Login");
196                   col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", buddyname);
197                 }        
198
199                 if( tree  )
200                 {
201                   proto_tree_add_text(aim_tree_fnac, tvb, 20, buddyname_length, "Screen Name: %s", buddyname);       
202                 }
203
204                 break;
205               case 0x0003:
206                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Login information reply");        
207                 break;
208               case 0x0006:
209                 buddyname_length = get_buddyname( buddyname, tvb, 19, 20 );
210
211                 if (check_col(pinfo->cinfo, COL_INFO)) {
212                   col_add_fstr(pinfo->cinfo, COL_INFO, "Sign-on");
213                   col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", buddyname);
214                 }        
215                 
216                 if( tree )
217                 {
218                   proto_tree_add_text(aim_tree_fnac, tvb, 20, buddyname_length, "Screen Name: %s", buddyname);       
219                 }
220
221                 break;
222               case 0x0007:
223                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Sign-on reply");        
224                 break;
225             }
226             break;
227
228           case FAMILY_GENERIC:
229             switch(subtype)
230             {
231               case 0x0002:
232                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Client is now online and ready for normal function");        
233                 break;
234               case 0x0003:
235                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Server is now ready for normal functions");        
236                 break;
237               case 0x0004:
238                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Request for new service (server will redirect client)");        
239                 break;
240               case 0x0005:
241                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Redirect response");        
242                 break;
243               case 0x0006:
244                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Request Rate Information");        
245                 break;
246               case 0x0007:
247                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Rate information response");        
248                 break;
249               case 0x0008:
250                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Rate Information Response Ack");        
251                 break;
252               case 0x0016:
253                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "No-op");        
254                 break;
255             }
256             break;
257
258           case FAMILY_BUDDYLIST:
259             switch(subtype)
260             {
261               case 0x0001:
262                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Buddylist - Error");        
263                 break;
264
265               case 0x0002:
266                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Request Rights information");        
267                 break;
268
269               case 0x0003:
270                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Rights information");        
271                 break;
272
273               case 0x0004:
274                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Add to Buddylist");        
275                 break;
276
277               case 0x0005:
278                 if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Remove from Buddylist");        
279                 break;
280
281               case 0x000b:
282                 buddyname_length = get_buddyname( buddyname, tvb, 16, 17 );
283
284                 if (check_col(pinfo->cinfo, COL_INFO)) {
285                   col_add_fstr(pinfo->cinfo, COL_INFO, "Oncoming Buddy");
286                   col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", buddyname);
287                 }        
288                 
289                 if( tree )
290                 {
291                   proto_tree_add_text(aim_tree_fnac, tvb, 17, buddyname_length, "Screen Name: %s", buddyname);       
292                 }
293
294                 break;
295
296               case 0x000c:
297
298                 buddyname_length = get_buddyname( buddyname, tvb, 16, 17 );
299
300                 if (check_col(pinfo->cinfo, COL_INFO)) {
301                   col_add_fstr(pinfo->cinfo, COL_INFO, "Offgoing Buddy");
302                   col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", buddyname);
303                 }        
304                 
305                 if( tree )
306                 {
307                   proto_tree_add_text(aim_tree_fnac, tvb, 17, buddyname_length, "Screen Name: %s", buddyname);       
308                 }
309
310
311                 break;
312             }
313           break;
314
315         case FAMILY_LOCATION:
316           switch(subtype)
317           {
318             case 0x0001:
319               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Location - Error");        
320               break;
321             case 0x0002:
322               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Request Rights Information");        
323               break;
324             case 0x0003:
325               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Rights Information");        
326               break;
327             case 0x0004:
328               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Set User Information");        
329               break;
330             case 0x0005:
331               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Request User Information");        
332               break;
333             case 0x0006:
334               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "User Information");        
335               break;
336             case 0x0007:
337               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Watcher Subrequest");        
338               break;
339             case 0x0008:
340               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Watcher Notification");        
341               break;
342           }
343           break;
344
345         case FAMILY_ADVERTS:
346           switch(subtype)
347           {
348             case 0x0001:
349               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Advertisements - Error");        
350               break;
351             case 0x0002:
352               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Advertisement Request");        
353               break;
354             case 0x0003:
355               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Advertisement data (GIF)");        
356               break;
357           }
358           break;
359
360         case FAMILY_USERLOOKUP:
361           switch(subtype)
362           {
363             case 0x0001:
364               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Search - Error (could be: not found)");        
365               break;
366             case 0x0002:
367               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Search for Screen Name by e-mail");        
368               break;
369             case 0x0003:
370               if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Screen Name Search Result");        
371               break;
372           }
373           break;
374
375         case FAMILY_CHAT:
376           switch(subtype)
377           {
378             case 0x005:
379               /* channel message from client */
380               get_message( msg, tvb, 40 + buddyname_length, tvb_length(tvb) - 40 - buddyname_length );
381
382               if (check_col(pinfo->cinfo, COL_INFO)) {
383                 col_add_fstr(pinfo->cinfo, COL_INFO, "Chat Message ");
384                 col_append_fstr(pinfo->cinfo, COL_INFO, " -> %s", msg);
385               }        
386               break;
387             
388             case 0x006:
389               /* channel message to client */
390               buddyname_length = get_buddyname( buddyname, tvb, 30, 31 );
391               get_message( msg, tvb, 36 + buddyname_length, tvb_length(tvb) - 36 - buddyname_length );
392               
393               if (check_col(pinfo->cinfo, COL_INFO)) {
394                 col_add_fstr(pinfo->cinfo, COL_INFO, "Chat Message ");
395                 col_append_fstr(pinfo->cinfo, COL_INFO, "from: %s", buddyname);
396                 col_append_fstr(pinfo->cinfo, COL_INFO, " -> %s", msg);
397               }        
398               
399               if( tree )
400               {
401                 proto_tree_add_text(aim_tree_fnac, tvb, 31, buddyname_length, "Screen Name: %s", buddyname);       
402               }
403               break;
404           }
405           break;
406
407
408         case FAMILY_MESSAGING:
409           switch(subtype)
410           {
411             case MSG_TO_CLIENT:
412               buddyname_length = get_buddyname( buddyname, tvb, 26, 27 );
413
414               get_message( msg, tvb, 36 + buddyname_length, tvb_length(tvb) - 36 - buddyname_length );
415               
416               if (check_col(pinfo->cinfo, COL_INFO)) {
417                 col_add_fstr(pinfo->cinfo, COL_INFO, "Message ");
418                 col_append_fstr(pinfo->cinfo, COL_INFO, "to: %s", buddyname);
419                 col_append_fstr(pinfo->cinfo, COL_INFO, " -> %s", msg);
420               }        
421
422               if( tree )
423               {
424                 proto_tree_add_text(aim_tree_fnac, tvb, 27, buddyname_length, "Screen Name: %s", buddyname);       
425               }
426
427               break;
428
429             case MSG_FROM_CLIENT:
430               buddyname_length = get_buddyname( buddyname, tvb, 26, 27 );
431
432               get_message( msg, tvb, 36 + buddyname_length,  tvb_length(tvb) - 36 - buddyname_length);
433
434               if (check_col(pinfo->cinfo, COL_INFO)) {
435                 col_add_fstr(pinfo->cinfo, COL_INFO, "Message");
436                 col_append_fstr(pinfo->cinfo, COL_INFO, " from: %s", buddyname);
437
438                 col_append_fstr(pinfo->cinfo, COL_INFO, " -> %s", msg);
439               }        
440               
441               if( tree )
442               {
443                 proto_tree_add_text(aim_tree_fnac, tvb, 27, buddyname_length, "Screen Name: %s", buddyname);       
444               }
445               break;
446           }
447
448           break;
449       }
450       
451
452       
453       break;
454     
455     case CHANNEL_FLAP_ERR:
456       if (check_col(pinfo->cinfo, COL_INFO)) {
457         col_add_fstr(pinfo->cinfo, COL_INFO, "FLAP error");
458       }        
459       break;
460     
461     case CHANNEL_CLOSE_CONN:
462       if (check_col(pinfo->cinfo, COL_INFO)) {
463         col_add_fstr(pinfo->cinfo, COL_INFO, "Close Connection");
464       }        
465       break;
466     
467     default:
468       if (check_col(pinfo->cinfo, COL_INFO)) {
469         col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown Channel: %d", hdr_channel );
470       }        
471       break;
472   }
473
474
475
476 }
477
478
479 static int get_buddyname( char *name, tvbuff_t *tvb, int len_offset, int name_offset)
480 {
481   guint8 buddyname_length;
482   
483   buddyname_length = tvb_get_guint8(tvb, len_offset);
484
485   if(buddyname_length > MAX_BUDDYNAME_LENGTH ) buddyname_length = MAX_BUDDYNAME_LENGTH;
486   memset( name, '\0', sizeof(name));
487   tvb_get_nstringz0(tvb, name_offset, buddyname_length, name);
488
489   return buddyname_length;
490 }
491
492
493 static void get_message( u_char *msg, tvbuff_t *tvb, int msg_offset, int msg_length)
494 {
495   int i,j,c;
496   int bracket = FALSE;
497   int max, tagchars = 0;
498   int new_offset = msg_offset;
499   int new_length = msg_length;
500
501
502
503   /* make sure nothing bigger than 1000 bytes is printed */
504   if( msg_length > 999 ) return;
505
506   memset( msg, '\0', 1000);
507   i = 0;
508   c = 0;
509
510   /* loop until HTML tag is reached - quick&dirty way to find start of message
511    * (it is nearly impossible to find the correct start offset for all client versions) */
512   while( (tagchars < 6) && (new_length > 5) )
513   {
514      j = tvb_get_guint8(tvb, new_offset);
515      if( ( (j == '<') && (tagchars == 0) ) ||
516          ( (j == 'h') && (tagchars == 1) ) ||
517          ( (j == 'H') && (tagchars == 1) ) ||
518          ( (j == 't') && (tagchars == 2) ) ||
519          ( (j == 'T') && (tagchars == 2) ) ||
520          ( (j == 'm') && (tagchars == 3) ) ||
521          ( (j == 'M') && (tagchars == 3) ) ||
522          ( (j == 'l') && (tagchars == 4) ) ||
523          ( (j == 'L') && (tagchars == 4) ) ||
524          ( (j == '>') && (tagchars == 5) ) ) tagchars++;
525      new_offset++;
526      new_length--;
527   }
528  
529   /* set offset and length of message to after the first HTML tag */
530   msg_offset = new_offset;
531   msg_length = new_length;
532   max = msg_length - 1;
533   tagchars = 0;
534   
535   /* find the rest of the message until either a </html> is reached or the end of the frame.
536    * All other HTML tags are stripped to display only the raw message (printable characters) */
537   while( (c < max) && (tagchars < 7) )
538   {
539      j = tvb_get_guint8(tvb, msg_offset+c);
540
541      
542      /* make sure this is an HTML tag by checking the order of the chars */
543      if( ( (j == '<') && (tagchars == 0) ) ||
544          ( (j == '/') && (tagchars == 1) ) ||
545          ( (j == 'h') && (tagchars == 2) ) ||
546          ( (j == 'H') && (tagchars == 2) ) ||
547          ( (j == 't') && (tagchars == 3) ) ||
548          ( (j == 'T') && (tagchars == 3) ) ||
549          ( (j == 'm') && (tagchars == 4) ) ||
550          ( (j == 'M') && (tagchars == 4) ) ||
551          ( (j == 'l') && (tagchars == 5) ) ||
552          ( (j == 'L') && (tagchars == 5) ) ||
553          ( (j == '>') && (tagchars == 6) ) ) tagchars++;
554
555 #ifdef STRIP_TAGS
556      if( j == '<' ) bracket = TRUE;
557      if( j == '>' ) bracket = FALSE; 
558      if( (isprint(j) ) && (bracket == FALSE) && (j != '>'))
559 #else
560      if( isprint(j) )
561 #endif
562      {
563        msg[i] = j;
564        i++;
565      }
566      c++;
567   }
568 }
569              
570
571 /* Register the protocol with Ethereal */
572 void 
573 proto_register_aim(void)
574 {                 
575
576 /* Setup list of header fields */
577   static hf_register_info hf[] = {
578     { &hf_aim_cmd_start,
579       { "Command Start", "aim.cmd_start", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }
580     },
581     { &hf_aim_channel,
582       { "Channel ID", "aim.channel", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }
583     },
584     { &hf_aim_seqno,
585       { "Sequence Number", "aim.seqno", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }
586     },
587     { &hf_aim_data_len,
588       { "Data Field Length", "aim.datalen", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }
589     },
590     { &hf_aim_fnac_family,
591       { "FNAC Family ID", "aim.fnac.family", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }
592     },
593     { &hf_aim_fnac_subtype,
594       { "FNAC Subtype ID", "aim.fnac.subtype", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }
595     },
596   };
597
598 /* Setup protocol subtree array */
599   static gint *ett[] = {
600     &ett_aim,
601     &ett_aim_fnac,
602   };
603
604 /* Register the protocol name and description */
605   proto_aim = proto_register_protocol("AOL Instant Messenger", "AIM", "aim");
606
607 /* Required function calls to register the header fields and subtrees used */
608   proto_register_field_array(proto_aim, hf, array_length(hf));
609   proto_register_subtree_array(ett, array_length(ett));
610 };
611
612 void
613 proto_reg_handoff_aim(void)
614 {
615   dissector_handle_t aim_handle;
616
617   aim_handle = create_dissector_handle(dissect_aim, proto_aim);
618   dissector_add("tcp.port", TCP_PORT_AIM, aim_handle);
619 }