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