Put back an unused variable, as a reminder that the routine it's in
[obnox/wireshark/wip.git] / packet-beep.c
1 /* packet-beep.c
2  * Routines for BEEP packet disassembly
3  *
4  * $Id: packet-beep.c,v 1.8 2002/04/29 09:40:05 guy Exp $
5  *
6  * Copyright (c) 2000 by Richard Sharpe <rsharpe@ns.aus.com>
7  * Modified 2001 Darren New <dnew@invisible.net> for BEEP.
8  *
9  * Original BXXP dissector developed with funding from InvisibleWorlds
10  * (www.invisibleworlds.com) via Collab.Net.
11  *
12  * Ethereal - Network traffic analyzer
13  * By Gerald Combs
14  * Copyright 1999 Gerald Combs
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version 2
19  * of the License, or (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
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 <stdio.h>
44 #include <stdlib.h>
45 #include <ctype.h>
46 #include <time.h>
47 #include <glib.h>
48 #include <string.h>
49 #include <epan/packet.h>
50 #include <epan/resolv.h>
51 #include "prefs.h"
52 #include <epan/conversation.h>
53
54 #define TCP_PORT_BEEP 10288
55 void proto_reg_handoff_beep(void);
56
57
58 static int global_beep_tcp_port = TCP_PORT_BEEP;
59 static int global_beep_strict_term = TRUE;
60
61 static int proto_beep = -1;
62
63 static int hf_beep_req = -1;
64 static int hf_beep_req_chan = -1;
65 static int hf_beep_rsp_chan = -1;
66 static int hf_beep_seq_chan = -1;
67 static int hf_beep_rsp = -1;
68 static int hf_beep_seq = -1;
69 static int hf_beep_end = -1;
70 static int hf_beep_proto_viol = -1;
71 static int hf_beep_complete = -1;   /* No More data follows */
72 static int hf_beep_intermediate = -1; /* More data follows */
73 static int hf_beep_msgno = -1;
74 static int hf_beep_ansno = -1;
75 static int hf_beep_seqno = -1;
76 static int hf_beep_size = -1;
77 static int hf_beep_channel = -1;
78 static int hf_beep_positive = -1;
79 static int hf_beep_negative = -1;
80 static int hf_beep_ackno = -1;
81 static int hf_beep_window = -1;
82
83 /* Arrays of hf entry pointers for some routines to use. If you want more
84  * hidden items added for a field, add them to the list before the NULL, 
85  * and the various routines that these are passed to will add them.
86  */
87
88 static int *req_msgno_hfa[] = { &hf_beep_msgno, NULL };
89 static int *req_ansno_hfa[] = { &hf_beep_ansno, NULL };
90 static int *req_seqno_hfa[]  = { &hf_beep_seqno, NULL };
91 static int *req_size_hfa[]   = { &hf_beep_size, NULL };
92 static int *req_chan_hfa[]   = { &hf_beep_channel, &hf_beep_req_chan, NULL };
93 /*
94 static int *rsp_msgno_hfa[] = { &hf_beep_msgno, NULL };
95 static int *rsp_seqno_hfa[]  = { &hf_beep_seqno, NULL };
96 static int *rsp_size_hfa[]   = { &hf_beep_size, NULL };
97 */
98 static int *seq_chan_hfa[]   = { &hf_beep_channel, &hf_beep_seq_chan, NULL };
99 static int *seq_ackno_hfa[]  = { &hf_beep_ackno, NULL };
100 static int *seq_window_hfa[] = { &hf_beep_window, NULL };
101
102 static int ett_beep = -1;
103 static int ett_mime_header = -1;
104 static int ett_header = -1;
105 static int ett_trailer = -1;
106
107 static unsigned int tcp_port = 0;
108
109 /* Get the state of the more flag ... */
110
111 #define BEEP_VIOL         0
112 #define BEEP_INTERMEDIATE 1
113 #define BEEP_COMPLETE     2
114
115 /*
116  * Per-frame data
117  *
118  * pl_left is the amount of data in this packet that belongs to another
119  * frame ...
120  * 
121  * It relies on TCP segments not being re-ordered too much ...
122  */
123 struct beep_proto_data {
124   int pl_left;   /* Payload at beginning of frame */
125   int pl_size;   /* Payload in current message ...*/
126   int mime_hdr;  /* Whether we expect a mime header. 1 on first, 0 on rest 
127                   * in a message
128                   */
129 };
130
131 /*
132  * Conversation stuff
133  */
134 static int beep_packet_init_count = 100;
135
136 struct beep_request_key {
137   guint32 conversation;
138 };
139
140 struct beep_request_val {
141   guint16 processed;     /* Have we processed this conversation? */
142   int size;              /* Size of the message                  */
143                          /* We need an indication in each dirn of
144                           * whether on not a mime header is expected 
145                           */
146   int c_mime_hdr, s_mime_hdr;
147 };
148
149 static GHashTable *beep_request_hash = NULL;
150 static GMemChunk  *beep_request_keys = NULL;
151 static GMemChunk  *beep_request_vals = NULL;
152 static GMemChunk  *beep_packet_infos = NULL;
153
154 /* Hash Functions */
155 static gint
156 beep_equal(gconstpointer v, gconstpointer w)
157 {
158   struct beep_request_key *v1 = (struct beep_request_key *)v;
159   struct beep_request_key *v2 = (struct beep_request_key *)w;
160
161 #if defined(DEBUG_BEEP_HASH)
162   printf("Comparing %08X\n      and %08X\n",
163          v1->conversation, v2->conversation);
164 #endif
165
166   if (v1->conversation == v2->conversation)
167     return 1;
168
169   return 0;
170
171 }
172
173 static guint
174 beep_hash(gconstpointer v)
175 {
176   struct beep_request_key *key = (struct beep_request_key *)v;
177   guint val;
178
179   val = key->conversation;
180
181 #if defined(DEBUG_BEEP_HASH)
182   printf("BEEP Hash calculated as %u\n", val);
183 #endif
184
185   return val;
186
187 }
188
189 static void
190 beep_init_protocol(void)
191 {
192 #if defined(DEBUG_BEEP_HASH)
193   fprintf(stderr, "Initializing BEEP hashtable area\n");
194 #endif
195
196   if (beep_request_hash)
197     g_hash_table_destroy(beep_request_hash);
198   if (beep_request_keys)
199     g_mem_chunk_destroy(beep_request_keys);
200   if (beep_request_vals)
201     g_mem_chunk_destroy(beep_request_vals);
202   if (beep_packet_infos)
203     g_mem_chunk_destroy(beep_packet_infos);
204
205   beep_request_hash = g_hash_table_new(beep_hash, beep_equal);
206   beep_request_keys = g_mem_chunk_new("beep_request_keys",
207                                        sizeof(struct beep_request_key),
208                                        beep_packet_init_count * sizeof(struct beep_request_key), G_ALLOC_AND_FREE);
209   beep_request_vals = g_mem_chunk_new("beep_request_vals", 
210                                       sizeof(struct beep_request_val),
211                                       beep_packet_init_count * sizeof(struct beep_request_val), G_ALLOC_AND_FREE);
212   beep_packet_infos = g_mem_chunk_new("beep_packet_infos",
213                                       sizeof(struct beep_proto_data),
214                                       beep_packet_init_count * sizeof(struct beep_proto_data), G_ALLOC_AND_FREE);
215 }
216
217 /*
218  * BEEP routines
219  */
220
221 static int beep_get_more(char more)
222 {
223
224   if (more == '.')
225     return BEEP_COMPLETE;
226   else if (more == '*')
227     return BEEP_INTERMEDIATE;
228
229   return BEEP_VIOL;
230 }
231
232 /* dissect the more flag, and return a value of:
233  *  1 -> more
234  *  0 -> no more
235  *  -1 -> Proto violation
236  */
237
238 static int
239 dissect_beep_more(tvbuff_t *tvb, int offset,
240                   proto_tree *tree)
241 {
242
243
244   switch (beep_get_more(tvb_get_guint8(tvb, offset))) {
245
246   case BEEP_COMPLETE:
247
248     if (tree) {
249       proto_tree_add_boolean_hidden(tree, hf_beep_complete, tvb, offset, 1, TRUE);
250       proto_tree_add_text(tree, tvb, offset, 1, "More: Complete");
251     }
252
253     return 0;
254
255     break;
256
257   case BEEP_INTERMEDIATE:
258
259     if (tree) {
260       proto_tree_add_boolean_hidden(tree, hf_beep_intermediate, tvb, offset, 1, TRUE);
261       proto_tree_add_text(tree, tvb, offset, 1, "More: Intermediate");
262     }
263
264     return 1;
265
266     break;
267
268   default:  
269
270     if (tree) {
271       proto_tree_add_boolean_hidden(tree, hf_beep_proto_viol, tvb, offset, 1, TRUE);
272       proto_tree_add_text(tree, tvb, offset, 1, "PROTOCOL VIOLATION: Expected More Flag (* or .)");
273     }
274
275     return -1;
276
277     break;
278   }
279
280 }
281
282 static void dissect_beep_status(tvbuff_t *tvb, int offset,
283                                 proto_tree *tree)
284 {
285
286   /* FIXME: We should return a value to indicate all OK. */
287   
288   switch(tvb_get_guint8(tvb, offset)) {
289
290   case '+':
291
292     if (tree) {
293       proto_tree_add_boolean_hidden(tree, hf_beep_positive, tvb, offset, 1, TRUE);
294       proto_tree_add_text(tree, tvb, offset, 1, "Status: Positive");
295     }
296
297     break;
298
299   case '-':
300
301     if (tree) {
302       proto_tree_add_boolean_hidden(tree, hf_beep_negative, tvb, offset, 1, TRUE);
303       proto_tree_add_text(tree, tvb, offset, 1, "Status: Negative");
304     }
305
306     break;
307
308   default:  /* Proto violation: FIXME */
309
310     break;
311
312   }
313
314 }
315
316 static int num_len(tvbuff_t *tvb, int offset)
317 {
318   unsigned int i = 0;
319
320   while (isdigit(tvb_get_guint8(tvb, offset + i))) i++;
321
322   return i;
323
324 }
325
326 /*
327  * We check for a terminator. This can be CRLF, which will be recorded
328  * as a terminator, or CR or LF by itself, which will be redorded as
329  * an incorrect terminator ... We build the tree at this point
330  * However, we depend on the variable beep_strict_term
331  */ 
332
333 static int 
334 check_term(tvbuff_t *tvb, int offset, proto_tree *tree)
335 {
336
337   /* First, check for CRLF, or, if global_beep_strict_term is false, 
338    * one of CR or LF ... If neither of these hold, we add an element
339    * that complains of a protocol violation, and return -1, else
340    * we add a terminator to the tree (possibly non-standard) and return
341    * the count of characters we saw ... This may throw off the rest of the 
342    * dissection ... so-be-it!
343    */
344
345   if ((tvb_get_guint8(tvb, offset) == 0x0d && 
346        tvb_get_guint8(tvb, offset + 1) == 0x0a)){ /* Correct terminator */
347
348     if (tree) {
349       proto_tree_add_text(tree, tvb, offset, 2, "Terminator: CRLF");
350     }
351     return 2;
352
353   }
354   else if ((tvb_get_guint8(tvb, offset) == 0x0d) && !global_beep_strict_term) {
355
356     if (tree) {
357       proto_tree_add_text(tree, tvb, offset, 1, "Nonstandard Terminator: CR");
358       proto_tree_add_boolean_hidden(tree, hf_beep_proto_viol, tvb, offset, 1, TRUE);
359     }
360     return 1;
361
362   }
363   else if ((tvb_get_guint8(tvb, offset) == 0x0a) && !global_beep_strict_term) {
364
365     if (tree) {
366       proto_tree_add_text(tree, tvb, offset, 1, "Nonstandard Terminator: LF");
367       proto_tree_add_boolean_hidden(tree, hf_beep_proto_viol, tvb, offset, 1, TRUE);
368     }
369     return 1;
370
371   }
372   else {    
373
374     if (tree) {
375       proto_tree_add_text(tree, tvb, offset, 2, "PROTOCOL VIOLATION, Invalid Terminator: %s", tvb_format_text(tvb, offset, 2));
376       proto_tree_add_boolean_hidden(tree, hf_beep_proto_viol, tvb, offset, 2, TRUE);
377     }
378     return -1;
379
380   }
381
382 }
383
384 /* Get the header length, up to CRLF or CR or LF */
385 static int header_len(tvbuff_t *tvb, int offset)
386 {
387   int i = 0;
388   guint8 sc;
389
390   /* FIXME: Have to make sure we stop looking at the end of the tvb ... */
391
392   /* We look for CRLF, or CR or LF if global_beep_strict_term is 
393    * not set.
394    */
395
396   while (1) {
397
398     if (tvb_ensure_length_remaining(tvb, offset + i) < 1)
399       return i;   /* Not enough characters left ... */
400
401     if ((sc = tvb_get_guint8(tvb, offset + i)) == 0x0d 
402         && tvb_get_guint8(tvb, offset + i + 1) == 0x0a)
403       return i;   /* Done here ... */
404
405     if (!global_beep_strict_term && (sc == 0x0d || sc == 0x0a))
406       return i;   /* Done here also ... */
407           
408     i++;
409
410   }
411 }
412
413 static int
414 dissect_beep_mime_header(tvbuff_t *tvb, int offset, 
415                          struct beep_proto_data *frame_data,
416                          proto_tree *tree)
417 {
418   proto_tree    *ti = NULL, *mime_tree = NULL;
419   int           mime_length = header_len(tvb, offset), cc = 0;
420
421   if (frame_data && !frame_data->mime_hdr) return 0;
422
423   if (tree) {
424
425     /* FIXME: Should calculate the whole length of the mime headers */
426
427     ti = proto_tree_add_text(tree, tvb, offset, mime_length, "Mime header: %s", tvb_format_text(tvb, offset, mime_length));
428     mime_tree = proto_item_add_subtree(ti, ett_mime_header);
429   }
430
431   if (mime_length == 0) { /* Default header */
432
433     if (tree) {
434       proto_tree_add_text(mime_tree, tvb, offset, 0, "Default values");
435     }
436
437     if ((cc = check_term(tvb, offset, mime_tree)) <= 0) {
438
439       /* Ignore it, it will cause funnies in the rest of the dissect */
440
441     }
442
443   }
444   else {  /* FIXME: Process the headers */
445
446     if (tree) {
447       proto_tree_add_text(mime_tree, tvb, offset, mime_length, "Header: %s", 
448                           tvb_format_text(tvb, offset, mime_length));
449     }
450
451     if ((cc = check_term(tvb, offset + mime_length, mime_tree)) <= 0) {
452
453       /* Ignore it, it will cause funnies in the rest of the dissect */
454
455     }
456
457   }
458
459   return mime_length + cc;  /* FIXME: Check that the CRLF is there */
460
461 }
462
463 static int
464 dissect_beep_int(tvbuff_t *tvb, int offset,
465                     proto_tree *tree, int hf, int *val, int *hfa[])
466 {
467   int ival, ind = 0;
468   unsigned int i = num_len(tvb, offset);
469   guint8 int_buff[100];
470
471   memset(int_buff, '\0', sizeof(int_buff));
472
473   tvb_memcpy(tvb, int_buff, offset, MIN(sizeof(int_buff), i));
474
475   sscanf(int_buff, "%d", &ival);  /* FIXME: Dangerous */
476
477   if (tree) {
478     proto_tree_add_uint(tree, hf, tvb, offset, i, ival);
479   }
480
481   while (hfa[ind]) {
482
483     proto_tree_add_uint_hidden(tree, *hfa[ind], tvb, offset, i, ival);
484     ind++;
485
486   }
487
488   *val = ival;  /* Return the value */
489
490   return i;
491
492 }
493
494 static void
495 set_mime_hdr_flags(int more, struct beep_request_val *request_val, 
496                    struct beep_proto_data *frame_data, packet_info *pinfo)
497 {
498
499   if (!request_val) return; /* Nothing to do ??? */
500
501   if (pinfo->destport == tcp_port) { /* Going to the server ... client */
502
503     if (request_val->c_mime_hdr) {
504
505       frame_data->mime_hdr = 0;
506
507       if (!more) request_val->c_mime_hdr = 0; 
508
509     }
510     else {
511
512       frame_data->mime_hdr = 1;
513
514       if (more) request_val->c_mime_hdr = 1;
515
516     }
517
518   }
519   else {
520
521     if (request_val->s_mime_hdr) {
522
523       frame_data->mime_hdr = 0;
524
525       if (!more) request_val->s_mime_hdr = 0; 
526
527     }
528     else {
529
530       frame_data->mime_hdr = 1;
531
532       if (more) request_val->s_mime_hdr = 1;
533
534     }
535
536   }
537
538 }
539
540 /* Build the tree
541  *
542  * A return value of <= 0 says we bailed out, skip the rest of this message,
543  * if any.
544  *
545  * A return value > 0 is the count of bytes we consumed ...
546  */
547
548 static int
549 dissect_beep_tree(tvbuff_t *tvb, int offset, packet_info *pinfo, 
550                   proto_tree *tree, struct beep_request_val *request_val, 
551                   struct beep_proto_data *frame_data)
552 {
553   proto_tree     *ti = NULL, *hdr = NULL;
554   int            st_offset, msgno, ansno, seqno, size, channel, ackno, window, cc,
555                  more;
556
557   char * cmd_temp = NULL;
558   int is_ANS = 0;
559   st_offset = offset;
560
561   if (tvb_strneql(tvb, offset, "MSG ", 4) == 0) 
562     cmd_temp = "Command: MSG";
563   if (tvb_strneql(tvb, offset, "RPY ", 4) == 0) 
564     cmd_temp = "Command: RPY";
565   if (tvb_strneql(tvb, offset, "ERR ", 4) == 0) 
566     cmd_temp = "Command: ERR";
567   if (tvb_strneql(tvb, offset, "NUL ", 4) == 0) 
568     cmd_temp = "Command: NUL";
569   if (tvb_strneql(tvb, offset, "ANS ", 4) == 0) {
570     cmd_temp = "Command: ANS";
571     is_ANS = 1;
572   }
573
574   if (cmd_temp != NULL) {
575
576     if (tree) {
577       ti = proto_tree_add_text(tree, tvb, offset, header_len(tvb, offset) + 2, "Header");
578
579       hdr = proto_item_add_subtree(ti, ett_header);
580
581       proto_tree_add_boolean_hidden(hdr, hf_beep_req, tvb, offset, 3, TRUE);
582       proto_tree_add_text(hdr, tvb, offset, 3, cmd_temp);
583     }
584
585     offset += 4;
586
587     /* Get the channel */
588     offset += dissect_beep_int(tvb, offset, hdr, hf_beep_channel, &channel, req_chan_hfa);
589     offset += 1; /* Skip the space */
590       
591     /* Dissect the message number */
592     offset += dissect_beep_int(tvb, offset, hdr, hf_beep_msgno, &msgno, req_msgno_hfa);
593     offset += 1; /* skip the space */
594
595     /* Insert the more elements ... */
596     if ((more = dissect_beep_more(tvb, offset, hdr)) >= 0) {
597       /* Figure out which direction this is in and what mime_hdr flag to 
598        * add to the frame_data. If there are missing segments, this code
599        * will get it wrong!
600        */
601       set_mime_hdr_flags(more, request_val, frame_data, pinfo);
602     }
603     else {  /* Protocol violation, so dissect rest as undisectable */
604       if (tree) {
605         proto_tree_add_text(hdr, tvb, offset, 
606                             tvb_length_remaining(tvb, offset),
607                             "Undissected Payload: %s",
608                             tvb_format_text(tvb, offset,
609                                             tvb_length_remaining(tvb, offset)
610                                             )
611                             );
612
613       }
614       return -1;
615     }
616
617     offset += 2; /* Skip the flag and the space ... */
618
619     /* now for the seqno */
620     offset += dissect_beep_int(tvb, offset, hdr, hf_beep_seqno, &seqno, req_seqno_hfa);
621     offset += 1; /* skip the space */
622
623     offset += dissect_beep_int(tvb, offset, hdr, hf_beep_size, &size, req_size_hfa);
624     if (request_val)   /* FIXME, is this the right order ... */
625       request_val -> size = size;  /* Stash this away */
626     else {
627       frame_data->pl_size = size;
628       if (frame_data->pl_size < 0) frame_data->pl_size = 0; /* FIXME: OK? */
629     }
630     /* offset += 1; skip the space */
631
632     if (is_ANS) { /* We need to put in the ansno */
633         offset += 1; /* skip the space */
634         /* Dissect the message number */
635         offset += dissect_beep_int(tvb, offset, hdr, hf_beep_ansno, &ansno, req_ansno_hfa);
636     }
637
638     if ((cc = check_term(tvb, offset, hdr)) <= 0) {
639
640       /* We dissect the rest as data and bail ... */
641
642       if (tree) {
643         proto_tree_add_text(hdr, tvb, offset, 
644                             tvb_length_remaining(tvb, offset),
645                             "Undissected Payload: %s", 
646                             tvb_format_text(tvb, offset, 
647                                             tvb_length_remaining(tvb, offset)
648                                             )
649                             );
650       }
651
652       return -1;
653
654     }
655
656     offset += cc;
657     
658     /* Insert MIME header ... */
659
660     if (frame_data && frame_data->mime_hdr)
661       offset += dissect_beep_mime_header(tvb, offset, frame_data, hdr);
662
663     /* Now for the payload, if any */
664
665     if (tvb_length_remaining(tvb, offset) > 0) { /* Dissect what is left as payload */
666
667       int pl_size = MIN(size, tvb_length_remaining(tvb, offset));
668
669       /* Except, check the payload length, and only dissect that much */
670
671       /* We need to keep track, in the conversation, of how much is left 
672        * so in the next packet, we can figure out what is part of the payload
673        * and what is the next message
674        */
675
676       if (tree) {
677         proto_tree_add_text(tree, tvb, offset, pl_size, "Payload: %s", tvb_format_text(tvb, offset, pl_size));
678
679       }
680
681       offset += pl_size;
682
683       if (request_val) {
684         request_val->size -= pl_size;
685         if (request_val->size < 0) request_val->size = 0;
686       }
687       else {
688         frame_data->pl_size -= pl_size;
689         if (frame_data->pl_size < 0) frame_data->pl_size = 0;
690       }
691     }
692       
693     /* If anything else left, dissect it ... */
694
695     if (tvb_length_remaining(tvb, offset) > 0)
696       offset += dissect_beep_tree(tvb, offset, pinfo, tree, request_val, frame_data);
697
698   } else if (tvb_strneql(tvb, offset, "SEQ ", 4) == 0) {
699
700     if (tree) {
701       proto_tree_add_boolean_hidden(tree, hf_beep_seq, tvb, offset, 3, TRUE);
702       proto_tree_add_text(tree, tvb, offset, 3, "Command: SEQ");
703     }
704
705     offset += 3;
706
707     /* Now check the space: FIXME */
708
709     offset += 1;
710
711     offset += dissect_beep_int(tvb, offset, tree, hf_beep_channel, &channel, seq_chan_hfa);
712
713     /* Check the space: FIXME */
714
715     offset += 1;
716
717     offset += dissect_beep_int(tvb, offset, tree, hf_beep_ackno, &ackno, seq_ackno_hfa);
718
719     /* Check the space: FIXME */
720
721     offset += 1;
722
723     offset += dissect_beep_int(tvb, offset, tree, hf_beep_window, &window, seq_window_hfa);
724
725     if ((cc = check_term(tvb, offset, tree)) <= 0) {
726
727       /* We dissect the rest as data and bail ... */
728
729       if (tree) {
730         proto_tree_add_text(tree, tvb, offset, 
731                             tvb_length_remaining(tvb, offset),
732                             "Undissected Payload: %s", 
733                             tvb_format_text(tvb, offset, 
734                                             tvb_length_remaining(tvb, offset)
735                                             )
736                             );
737       }
738
739       return -1;
740
741     }
742
743     offset += cc;
744
745   } else if (tvb_strneql(tvb, offset, "END", 3) == 0) {
746
747     proto_tree *tr = NULL;
748
749     if (tree) {
750       ti = proto_tree_add_text(tree, tvb, offset, MIN(5, tvb_length_remaining(tvb, offset)), "Trailer");
751
752       tr = proto_item_add_subtree(ti, ett_trailer);
753
754       proto_tree_add_boolean_hidden(tr, hf_beep_end, tvb, offset, 3, TRUE);
755       proto_tree_add_text(tr, tvb, offset, 3, "Command: END");
756
757     }
758
759     offset += 3;
760
761     if ((cc = check_term(tvb, offset, tr)) <= 0) {
762
763       /* We dissect the rest as data and bail ... */
764
765       if (tree) { 
766         proto_tree_add_text(tr, tvb, offset, tvb_length_remaining(tvb, offset),
767                             "Undissected Payload: %s", 
768                             tvb_format_text(tvb, offset, 
769                                             tvb_length_remaining(tvb, offset)
770                                             )
771                             );
772       }
773
774       return -1;
775
776     }
777
778     offset += cc;
779
780   }
781
782   if (tvb_length_remaining(tvb, offset) > 0) { /* Dissect anything left over */
783
784     int pl_size = 0;
785
786     if (request_val) {
787
788       pl_size = MIN(request_val->size, tvb_length_remaining(tvb, offset));
789
790       if (pl_size == 0) { /* The whole of the rest must be payload */
791       
792         pl_size = tvb_length_remaining(tvb, offset); /* Right place ? */
793       
794       }
795
796     } else if (frame_data) {
797       pl_size = MIN(frame_data->pl_size, tvb_length_remaining(tvb, offset));
798     } else { /* Just in case */
799       pl_size = tvb_length_remaining(tvb, offset);
800     }
801
802     /* Take care here to handle the payload correctly, and if there is 
803      * another message here, then handle it correctly as well.
804      */
805
806     /* If the pl_size == 0 and the offset == 0?, then we have not processed
807      * anything in this frame above, so we better treat all this data as 
808      * payload to avoid recursion loops
809      */
810
811     if (pl_size == 0 && offset == st_offset) 
812       pl_size = tvb_length_remaining(tvb, offset);
813
814     if (pl_size > 0) {
815
816       if (tree) {
817         proto_tree_add_text(tree, tvb, offset, pl_size, "Payload: %s",
818                             tvb_format_text(tvb, offset, pl_size));
819       }
820
821       offset += pl_size;            /* Advance past the payload */
822
823       if (request_val){
824         request_val->size -= pl_size; /* Reduce payload by what we added */
825         if (request_val->size < 0) request_val->size = 0;
826       }
827       else {
828         frame_data->pl_size -= pl_size;
829         if (frame_data->pl_size < 0) frame_data->pl_size = 0;
830       }
831     }
832
833     if (tvb_length_remaining(tvb, offset) > 0) {
834       offset += dissect_beep_tree(tvb, offset, pinfo, tree, request_val, frame_data);
835     }
836   }
837
838   return offset - st_offset;
839
840 }
841
842 static void
843 dissect_beep(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
844 {
845   int offset;
846   struct beep_proto_data  *frame_data = NULL;
847   proto_tree              *beep_tree = NULL, *ti = NULL;
848   conversation_t          *conversation = NULL;
849   struct beep_request_key request_key, *new_request_key;
850   struct beep_request_val *request_val = NULL;
851
852   offset = 0;
853
854   /* If we have per frame data, use that, else, we must have lost the per-
855    * frame data, and we have to do a full dissect pass again.
856    *
857    * The per-frame data tells us how much of this frame is left over from a
858    * previous frame, so we dissect it as payload and then try to dissect the
859    * rest.
860    * 
861    * We use the conversation to build up info on the first pass over the
862    * packets of type BEEP, and record anything that is needed if the user
863    * does random dissects of packets in per packet data.
864    *
865    * Once we have per-packet data, we don't need the conversation stuff 
866    * anymore, but if per-packet data and conversation stuff gets deleted, as 
867    * it does under some circumstances when a rescan is done, it all gets 
868    * rebuilt.
869    */
870
871   /* Find out what conversation this packet is part of ... but only
872    * if we have no information on this packet, so find the per-frame 
873    * info first.
874    */
875
876   frame_data = p_get_proto_data(pinfo->fd, proto_beep);
877
878   if (!frame_data) {
879
880     conversation = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
881                                        pinfo->srcport, pinfo->destport, 0);
882     if (conversation == NULL) { /* No conversation, create one */
883         conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
884                                         pinfo->srcport, pinfo->destport, 0);
885
886       }
887
888       /* 
889        * Check for and insert an entry in the request table if does not exist
890        */
891       request_key.conversation = conversation->index;
892
893       request_val = (struct beep_request_val *)g_hash_table_lookup(beep_request_hash, &request_key);
894       
895       if (!request_val) { /* Create one */
896
897         new_request_key = g_mem_chunk_alloc(beep_request_keys);
898         new_request_key->conversation = conversation->index;
899
900         request_val = g_mem_chunk_alloc(beep_request_vals);
901         request_val->processed = 0;
902         request_val->size = 0;
903
904         g_hash_table_insert(beep_request_hash, new_request_key, request_val);
905
906       }
907     }
908
909   if (check_col(pinfo->cinfo, COL_PROTOCOL))
910     col_set_str(pinfo->cinfo, COL_PROTOCOL, "BEEP");
911
912   if (check_col(pinfo->cinfo, COL_INFO)) {  /* Check the type ... */
913
914     /* "tvb_format_text()" is passed a value that won't go past the end
915      * of the packet, so it won't throw an exception. */
916     col_add_str(pinfo->cinfo, COL_INFO, tvb_format_text(tvb, offset, tvb_length_remaining(tvb, offset)));
917
918   }
919
920   /* Here, we parse the message so we can retrieve the info we need, which 
921    * is that there is some payload left from a previous segment on the 
922    * front of this segment ... This all depends on TCP segments not getting
923    * out of order ... 
924    *
925    * As a huge kludge, we push the checking for the tree down into the code
926    * and process as if we were given a tree but not call the routines that
927    * adorn the protocol tree if they were NULL.
928    */
929
930   if (tree) {  /* Build the tree info ... */
931
932     ti = proto_tree_add_item(tree, proto_beep, tvb, offset, -1, FALSE);
933
934     beep_tree = proto_item_add_subtree(ti, ett_beep);
935
936   }
937   
938   /* Check the per-frame data and the conversation for any left-over 
939    * payload from the previous frame 
940    *
941    * We check that per-frame data exists first, and if so, use it,
942    * else we use the conversation data.
943    *
944    * We create per-frame data here as well, but we must ensure we create it
945    * after we have done the check for per-frame or conversation data.
946    *
947    * We also depend on the first frame in a group having a pl_size of 0.
948    */
949
950   if (frame_data && frame_data->pl_left > 0) {
951
952     int pl_left = frame_data->pl_left;
953
954     pl_left = MIN(pl_left, tvb_length_remaining(tvb, offset));
955
956     /* Add the payload bit, only if we have a tree */
957     if (tree) {
958       proto_tree_add_text(beep_tree, tvb, offset, pl_left, "Payload: %s",
959                           tvb_format_text(tvb, offset, pl_left));
960     }
961     offset += pl_left;
962   }
963   else if (request_val && request_val->size > 0) {
964
965     int pl_left = request_val->size;
966
967     request_val->size = 0;
968
969     /* We create the frame data here for this case, and 
970      * elsewhere for other frames
971      */
972
973     frame_data = g_mem_chunk_alloc(beep_packet_infos);
974
975     frame_data->pl_left = pl_left;
976     frame_data->pl_size = 0;
977     frame_data->mime_hdr = 0;
978       
979     p_add_proto_data(pinfo->fd, proto_beep, frame_data);
980
981   }
982
983   /* Set up the per-frame data here if not already done so
984    * This _must_ come after the checks above ...
985    */
986
987   if (frame_data == NULL) { 
988
989     frame_data = g_mem_chunk_alloc(beep_packet_infos);
990
991     frame_data->pl_left = 0;
992     frame_data->pl_size = 0;
993     frame_data->mime_hdr = 0;
994
995     p_add_proto_data(pinfo->fd, proto_beep, frame_data);
996         
997   }
998
999   if (tvb_length_remaining(tvb, offset) > 0) {
1000
1001     offset += dissect_beep_tree(tvb, offset, pinfo, beep_tree, request_val, frame_data);
1002
1003   }
1004
1005 }
1006
1007 /* Register all the bits needed with the filtering engine */
1008
1009 void 
1010 proto_register_beep(void)
1011 {
1012   static hf_register_info hf[] = {
1013     { &hf_beep_proto_viol,
1014       { "Protocol Violation", "beep.violation", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
1015
1016     { &hf_beep_req,
1017       { "Request", "beep.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
1018
1019     { &hf_beep_req_chan,
1020       { "Request Channel Number", "beep.req.channel", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1021
1022     { &hf_beep_rsp,
1023       { "Response", "beep.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
1024
1025     { &hf_beep_rsp_chan,
1026       { "Response Channel Number", "beep.rsp.channel", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1027
1028     { &hf_beep_seq,
1029       { "Sequence", "beep.seq", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
1030
1031     { &hf_beep_seq_chan,
1032       { "Sequence Channel Number", "beep.seq.channel", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1033
1034     { &hf_beep_end,
1035       { "End", "beep.end", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
1036
1037     { &hf_beep_complete,
1038       { "Complete", "beep.more.complete", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
1039
1040     { &hf_beep_intermediate,
1041       { "Intermediate", "beep.more.intermediate", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
1042
1043     { &hf_beep_msgno,
1044       { "Msgno", "beep.msgno", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1045
1046     { &hf_beep_ansno,
1047       { "Ansno", "beep.ansno", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1048
1049     { &hf_beep_seqno,
1050       { "Seqno", "beep.seqno", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1051
1052     { &hf_beep_size,
1053       { "Size", "beep.size", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1054
1055     { &hf_beep_channel,
1056       { "Channel", "beep.channel", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1057
1058     { &hf_beep_negative,
1059       { "Negative", "beep.status.negative", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
1060
1061     { &hf_beep_positive,
1062       { "Positive", "beep.status.positive", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
1063
1064     { &hf_beep_ackno,
1065       { "Ackno", "beep.seq.ackno", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1066
1067     { &hf_beep_window,
1068       { "Window", "beep.seq.window", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1069
1070   };
1071   static gint *ett[] = {
1072     &ett_beep,
1073     &ett_mime_header,
1074     &ett_header,
1075     &ett_trailer,
1076   };
1077   module_t *beep_module; 
1078
1079   proto_beep = proto_register_protocol("Blocks Extensible Exchange Protocol",
1080                                        "BEEP", "beep");
1081
1082   proto_register_field_array(proto_beep, hf, array_length(hf));
1083   proto_register_subtree_array(ett, array_length(ett));
1084   register_init_routine(&beep_init_protocol);
1085
1086   /* Register our configuration options for BEEP, particularly our port */
1087
1088   beep_module = prefs_register_protocol(proto_beep, proto_reg_handoff_beep);
1089
1090   prefs_register_uint_preference(beep_module, "tcp.port", "BEEP TCP Port",
1091                                  "Set the port for BEEP messages (if other"
1092                                  " than the default of 10288)",
1093                                  10, &global_beep_tcp_port);
1094
1095   prefs_register_bool_preference(beep_module, "strict_header_terminator", 
1096                                  "BEEP Header Requires CRLF", 
1097                                  "Specifies that BEEP requires CRLF as a "
1098                                  "terminator, and not just CR or LF",
1099                                  &global_beep_strict_term);
1100 }
1101
1102 /* The registration hand-off routine */
1103 void
1104 proto_reg_handoff_beep(void)
1105 {
1106   static int beep_prefs_initialized = FALSE;
1107   static dissector_handle_t beep_handle;
1108
1109   if (!beep_prefs_initialized) {
1110
1111     beep_handle = create_dissector_handle(dissect_beep, proto_beep);
1112
1113     beep_prefs_initialized = TRUE;
1114
1115   }
1116   else {
1117
1118     dissector_delete("tcp.port", tcp_port, beep_handle);
1119
1120   }
1121
1122   /* Set our port number for future use */
1123
1124   tcp_port = global_beep_tcp_port;
1125
1126   dissector_add("tcp.port", global_beep_tcp_port, beep_handle);
1127
1128 }