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