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