Updates to allow CR and LF to be used (incorrectly) as terminators and to
[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.5 2000/10/03 10:59:10 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 /*
295  * We check for a terminator. This can be CRLF, which will be recorded
296  * as a terminator, or CR or LF by itself, which will be redorded as
297  * an incorrect terminator ... We build the tree at this point
298  * However, we depend on the variable bxxp_strict_term
299  */ 
300
301 int 
302 check_term(tvbuff_t *tvb, int offset, proto_tree *tree)
303 {
304
305   /* First, check for CRLF, or, if global_bxxp_strict_term is false, 
306    * one of CR or LF ... If neither of these hold, we add an element
307    * that complains of a protocol violation, and return -1, else
308    * we add a terminator to the tree (possibly non-standard) and return
309    * the count of characters we saw ... This may throw off the rest of the 
310    * dissection ... so-be-it!
311    */
312
313   if ((tvb_get_guint8(tvb, offset) == 0x0d && 
314        tvb_get_guint8(tvb, offset + 1) == 0x0a)){ /* Correct terminator */
315
316     if (tree) {
317       proto_tree_add_text(tree, tvb, offset, 2, "Terminator: CRLF");
318     }
319     return 2;
320
321   }
322   else if ((tvb_get_guint8(tvb, offset) == 0x0d) && !global_bxxp_strict_term) {
323
324     if (tree) {
325       proto_tree_add_text(tree, tvb, offset, 1, "Nonstandard Terminator: CR");
326     }
327     return 1;
328
329   }
330   else if ((tvb_get_guint8(tvb, offset) == 0x0a) && !global_bxxp_strict_term) {
331
332     if (tree) {
333       proto_tree_add_text(tree, tvb, offset, 1, "Nonstandard Terminator: LF");
334     }
335     return 1;
336
337   }
338   else {    
339
340     if (tree) {
341       proto_tree_add_text(tree, tvb, offset, 2, "PROTOCOL VIOLATION, Invalid Terminator: %s", tvb_format_text(tvb, offset, 2));
342     }
343     return -1;
344
345   }
346
347 }
348
349 /* Get the MIME header length */
350 int header_len(tvbuff_t *tvb, int offset)
351 {
352   int i = 0;
353   guint8 sc;
354
355   /* FIXME: Have to make sure we stop looking at the end of the tvb ... */
356
357   /* We look for CRLF, or CR or LF if global_bxxp_strict_term is 
358    * not set.
359    */
360
361   while (1) {
362
363     if (tvb_length_remaining(tvb, offset + i) < 1)
364       return i;   /* Not enough characters left ... */
365
366     if ((sc = tvb_get_guint8(tvb, offset + i)) == 0x0d 
367         && tvb_get_guint8(tvb, offset + i + 1) == 0x0a)
368       return i;   /* Done here ... */
369
370     if (!global_bxxp_strict_term && (sc == 0x0d || sc == 0x0a))
371       return i;   /* Done here also ... */
372           
373     i++;
374
375   }
376 }
377
378 int
379 dissect_bxxp_mime_header(tvbuff_t *tvb, int offset, frame_data *fd,
380                          proto_tree *tree)
381 {
382   proto_tree    *ti = NULL, *mime_tree = NULL;
383   int           mime_length = header_len(tvb, offset), cc = 0;
384
385   if (tree) {
386
387     /* FIXME: Should calculate the whole length of the mime headers */
388
389     ti = proto_tree_add_text(tree, tvb, offset, mime_length, "Mime header: %s", tvb_format_text(tvb, offset, mime_length));
390     mime_tree = proto_item_add_subtree(ti, ett_mime_header);
391   }
392
393   if (mime_length == 0) { /* Default header */
394
395     if (tree) {
396       proto_tree_add_text(mime_tree, tvb, offset, 0, "Default values");
397     }
398
399     if ((cc = check_term(tvb, offset, mime_tree)) <= 0) {
400
401       /* Ignore it, it will cause funnies in the rest of the dissect */
402
403     }
404
405   }
406   else {  /* FIXME: Process the headers */
407
408     if (tree) {
409       proto_tree_add_text(mime_tree, tvb, offset, mime_length, "Header: %s", 
410                           tvb_format_text(tvb, offset, mime_length));
411     }
412
413     if ((cc = check_term(tvb, offset + mime_length, mime_tree)) <= 0) {
414
415       /* Ignore it, it will cause funnies in the rest of the dissect */
416
417     }
418
419   }
420
421   return mime_length + cc;  /* FIXME: Check that the CRLF is there */
422
423 }
424
425 int
426 dissect_bxxp_int(tvbuff_t *tvb, int offset, frame_data *fd,
427                     proto_tree *tree, int hf, int *val, int *hfa[])
428 {
429   int ival, ind = 0, i = num_len(tvb, offset);
430   guint8 int_buff[100];
431
432   memset(int_buff, '\0', sizeof(int_buff));
433
434   tvb_memcpy(tvb, int_buff, offset, MIN(sizeof(int_buff), i));
435
436   sscanf(int_buff, "%d", &ival);  /* FIXME: Dangerous */
437
438   if (tree) {
439     proto_tree_add_uint(tree, hf, tvb, offset, i, ival);
440   }
441
442   while (hfa[ind]) {
443
444     proto_tree_add_uint_hidden(tree, *hfa[ind], tvb, offset, i, ival);
445     ind++;
446
447   }
448
449   *val = ival;  /* Return the value */
450
451   return i;
452
453 }
454
455 /* Build the tree
456  *
457  * A return value of <= 0 says we bailed out, skip the rest of this message,
458  * if any.
459  *
460  * A return value > 0 is the count of bytes we consumed ...
461  */
462
463 int
464 dissect_bxxp_tree(tvbuff_t *tvb, int offset, packet_info *pinfo, 
465                   proto_tree *tree, struct bxxp_request_val *request_val, 
466                   struct bxxp_proto_data *frame_data)
467 {
468   proto_tree     *ti = NULL, *hdr = NULL;
469   int            st_offset, serial, seqno, size, channel, ackno, window, cc;
470
471   st_offset = offset;
472
473   if (tvb_strneql(tvb, offset, "REQ ", 4) == 0) {
474
475     if (tree) {
476       ti = proto_tree_add_text(tree, tvb, offset, header_len(tvb, offset) + 2, "Header");
477
478       hdr = proto_item_add_subtree(ti, ett_header);
479
480       proto_tree_add_boolean_hidden(hdr, hf_bxxp_req, tvb, offset, 3, TRUE);
481       proto_tree_add_text(hdr, NullTVB, offset, 3, "Command: REQ");
482     }
483
484     offset += 3;
485
486 #if 0
487     if (tvb_get_guint8(tvb, offset) != ' ') { /* Protocol violation */
488
489       /* Hmm, FIXME ... Add some code here ... */
490
491     }
492 #endif
493
494     offset += 1;  /* Skip the space */
495
496     /* Insert the more elements ... */
497
498     dissect_bxxp_more(tvb, offset, pinfo->fd, hdr);
499     offset += 1;
500       
501     /* Check the space ... */
502
503     offset += 1;
504
505     /* Dissect the serial */
506
507     offset += dissect_bxxp_int(tvb, offset, pinfo->fd, hdr, hf_bxxp_serial, &serial, req_serial_hfa);
508     /* skip the space */
509
510     offset += 1;
511
512     /* now for the seqno */
513
514     offset += dissect_bxxp_int(tvb, offset, pinfo->fd, hdr, hf_bxxp_seqno, &seqno, req_seqno_hfa);
515
516     /* skip the space */
517
518     offset += 1;
519
520     offset += dissect_bxxp_int(tvb, offset, pinfo->fd, hdr, hf_bxxp_size, &size, req_size_hfa);
521     if (request_val)
522       request_val -> size = size;  /* Stash this away */
523     else {
524       frame_data->pl_size = size;
525       if (frame_data->pl_size < 0) frame_data->pl_size = 0;
526     }
527
528     /* Check the space */
529
530     offset += 1;
531
532     /* Get the channel */
533
534     offset += dissect_bxxp_int(tvb, offset, pinfo->fd, hdr, hf_bxxp_channel, &channel, req_chan_hfa);
535       
536     if ((cc = check_term(tvb, offset, hdr)) <= 0) {
537
538       /* We dissect the rest as data and bail ... */
539
540       if (tree) {
541         proto_tree_add_text(hdr, tvb, offset, 
542                             tvb_length_remaining(tvb, offset),
543                             "Undissected Payload: %s", 
544                             tvb_format_text(tvb, offset, 
545                                             tvb_length_remaining(tvb, offset)
546                                             )
547                             );
548       }
549
550       return -1;
551
552     }
553
554     offset += cc;
555     
556     /* Insert MIME header ... */
557
558     offset += dissect_bxxp_mime_header(tvb, offset, pinfo->fd, hdr);
559
560     /* Now for the payload, if any */
561
562     if (tvb_length_remaining(tvb, offset) > 0) { /* Dissect what is left as payload */
563
564       int pl_size = MIN(size, tvb_length_remaining(tvb, offset));
565
566       /* Except, check the payload length, and only dissect that much */
567
568       /* We need to keep track, in the conversation, of how much is left 
569        * so in the next packet, we can figure out what is part of the payload
570        * and what is the next message
571        */
572
573       if (tree) {
574         proto_tree_add_text(tree, tvb, offset, pl_size, "Payload: %s", tvb_format_text(tvb, offset, pl_size));
575
576       }
577
578       offset += pl_size;
579
580       if (request_val) {
581         request_val->size -= pl_size;
582         if (request_val->size < 0) request_val->size = 0;
583       }
584       else {
585         frame_data->pl_size -= pl_size;
586         if (frame_data->pl_size < 0) frame_data->pl_size = 0;
587       }
588     }
589       
590     /* If anything else left, dissect it ... */
591
592     if (tvb_length_remaining(tvb, offset) > 0)
593       offset += dissect_bxxp_tree(tvb, offset, pinfo, tree, request_val, frame_data);
594
595   } else if (tvb_strneql(tvb, offset, "RSP ", 4) == 0) {
596
597     if (tree) {
598
599       ti = proto_tree_add_text(tree, tvb, offset, header_len(tvb, offset) + 2, "Header");
600
601       hdr = proto_item_add_subtree(ti, ett_header);
602
603       proto_tree_add_boolean_hidden(hdr, hf_bxxp_rsp, NullTVB, offset, 3, TRUE);
604       proto_tree_add_text(hdr, tvb, offset, 3, "Command: RSP");
605
606     }
607
608     offset += 3;
609
610     /* Now check the space: FIXME */
611
612     offset += 1;
613
614     /* Next, the 'more' flag ... */
615
616     dissect_bxxp_more(tvb, offset, pinfo->fd, hdr);
617     offset += 1;
618
619     /* Check the space */
620
621     offset += 1;
622
623     offset += dissect_bxxp_int(tvb, offset, pinfo->fd, hdr, hf_bxxp_serial, &serial, rsp_serial_hfa);
624     /* skip the space */
625
626     offset += 1;
627
628     /* now for the seqno */
629
630     offset += dissect_bxxp_int(tvb, offset, pinfo->fd, hdr, hf_bxxp_seqno, &seqno, rsp_seqno_hfa);
631
632     /* skip the space */
633
634     offset += 1;
635
636     offset += dissect_bxxp_int(tvb, offset, pinfo->fd, hdr, hf_bxxp_size, &size, rsp_size_hfa);
637     if (request_val)
638       request_val->size = size;
639     else
640       frame_data->pl_size = size;
641
642     /* Check the space ... */
643
644     offset += 1;
645
646     dissect_bxxp_status(tvb, offset, pinfo->fd, hdr);
647
648     offset += 1;
649
650     if ((cc = check_term(tvb, offset, hdr)) <= 0) {
651
652       /* We dissect the rest as data and bail ... */
653
654       if (tree) {
655         proto_tree_add_text(hdr, tvb, offset, 
656                             tvb_length_remaining(tvb, offset),
657                             "Undissected Payload: %s", 
658                             tvb_format_text(tvb, offset, 
659                                             tvb_length_remaining(tvb, offset)
660                                             )
661                             );
662       }
663
664       return -1;
665
666     }
667
668     offset += cc;
669     
670     /* Insert MIME header ... */
671
672     offset += dissect_bxxp_mime_header(tvb, offset, pinfo->fd, hdr);
673
674     /* Now for the payload, if any */
675
676     if (tvb_length_remaining(tvb, offset) > 0) { /* Dissect what is left as payload */
677
678       int pl_size = MIN(size, tvb_length_remaining(tvb, offset));
679       
680       /* Except, check the payload length, and only dissect that much */
681
682       if (tree) {
683         proto_tree_add_text(tree, tvb, offset, pl_size, "Payload: %s", tvb_format_text(tvb, offset, pl_size));
684       }
685
686       offset += pl_size;
687
688       if (request_val) {
689         request_val->size -= pl_size;
690         if (request_val->size < 0) request_val->size = 0;
691       }
692       else {
693         frame_data->pl_size -= pl_size;
694         if (frame_data->pl_size < 0) frame_data->pl_size = 0;
695       }
696     }
697
698     /* If anything else left, dissect it ... As what? */
699
700     if (tvb_length_remaining(tvb, offset) > 0)
701       offset += dissect_bxxp_tree(tvb, offset, pinfo, tree, request_val, frame_data);
702
703   } else if (tvb_strneql(tvb, offset, "SEQ ", 4) == 0) {
704
705     if (tree) {
706       proto_tree_add_boolean_hidden(tree, hf_bxxp_seq, NullTVB, offset, 3, TRUE);
707       proto_tree_add_text(tree, tvb, offset, 3, "Command: SEQ");
708     }
709
710     offset += 3;
711
712     /* Now check the space: FIXME */
713
714     offset += 1;
715
716     offset += dissect_bxxp_int(tvb, offset, pinfo->fd, tree, hf_bxxp_channel, &channel, seq_chan_hfa);
717
718     /* Check the space: FIXME */
719
720     offset += 1;
721
722     offset += dissect_bxxp_int(tvb, offset, pinfo->fd, tree, hf_bxxp_ackno, &ackno, seq_ackno_hfa);
723
724     /* Check the space: FIXME */
725
726     offset += 1;
727
728     offset += dissect_bxxp_int(tvb, offset, pinfo->fd, tree, hf_bxxp_window, &window, seq_window_hfa);
729
730     if ((cc = check_term(tvb, offset, tree)) <= 0) {
731
732       /* We dissect the rest as data and bail ... */
733
734       if (tree) {
735         proto_tree_add_text(tree, tvb, offset, 
736                             tvb_length_remaining(tvb, offset),
737                             "Undissected Payload: %s", 
738                             tvb_format_text(tvb, offset, 
739                                             tvb_length_remaining(tvb, offset)
740                                             )
741                             );
742       }
743
744       return -1;
745
746     }
747
748     offset += cc;
749
750   } else if (tvb_strneql(tvb, offset, "END", 3) == 0) {
751
752     proto_tree *tr = NULL;
753
754     if (tree) {
755       ti = proto_tree_add_text(tree, tvb, offset, MIN(5, tvb_length_remaining(tvb, offset)), "Trailer");
756
757       tr = proto_item_add_subtree(ti, ett_trailer);
758
759       proto_tree_add_boolean_hidden(tr, hf_bxxp_end, NullTVB, offset, 3, TRUE);
760       proto_tree_add_text(tr, tvb, offset, 3, "Command: END");
761
762     }
763
764     offset += 3;
765
766     if ((cc = check_term(tvb, offset, tr)) <= 0) {
767
768       /* We dissect the rest as data and bail ... */
769
770       if (tree) { 
771         proto_tree_add_text(tr, tvb, offset, tvb_length_remaining(tvb, offset),
772                             "Undissected Payload: %s", 
773                             tvb_format_text(tvb, offset, 
774                                             tvb_length_remaining(tvb, offset)
775                                             )
776                             );
777       }
778
779       return -1;
780
781     }
782
783     offset += cc;
784
785   }
786
787   if (tvb_length_remaining(tvb, offset) > 0) { /* Dissect anything left over */
788
789     int pl_size = 0;
790
791     if (request_val) {
792
793       pl_size = MIN(request_val->size, tvb_length_remaining(tvb, offset));
794
795       if (pl_size == 0) { /* The whole of the rest must be payload */
796       
797         pl_size = tvb_length_remaining(tvb, offset); /* Right place ? */
798       
799       }
800
801     } else if (frame_data) {
802       pl_size = MIN(frame_data->pl_size, tvb_length_remaining(tvb, offset));
803     } else { /* Just in case */
804       pl_size = tvb_length_remaining(tvb, offset);
805     }
806
807     /* Take care here to handle the payload correctly, and if there is 
808      * another message here, then handle it correctly as well.
809      */
810
811     /* If the pl_size == 0 and the offset == 0?, then we have not processed
812      * anything in this frame above, so we better treat all this data as 
813      * payload to avoid recursion loops
814      */
815
816     if (pl_size == 0 && offset == st_offset) 
817       pl_size = tvb_length_remaining(tvb, offset);
818
819     if (pl_size > 0) {
820
821       if (tree) {
822         proto_tree_add_text(tree, tvb, offset, pl_size, "Payload: %s",
823                             tvb_format_text(tvb, offset, pl_size));
824       }
825
826       offset += pl_size;            /* Advance past the payload */
827
828       if (request_val){
829         request_val->size -= pl_size; /* Reduce payload by what we added */
830         if (request_val->size < 0) request_val->size = 0;
831       }
832       else {
833         frame_data->pl_size -= pl_size;
834         if (frame_data->pl_size < 0) frame_data->pl_size = 0;
835       }
836     }
837
838     if (tvb_length_remaining(tvb, offset) > 0) {
839       offset += dissect_bxxp_tree(tvb, offset, pinfo, tree, request_val, frame_data);
840     }
841   }
842
843   return offset - st_offset;
844
845 }
846
847 static void
848 dissect_bxxp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
849 {
850   int offset;
851   struct bxxp_proto_data  *frame_data = NULL;
852   proto_tree              *bxxp_tree = NULL, *ti = NULL;
853   conversation_t          *conversation = NULL;
854   struct bxxp_request_key request_key, *new_request_key;
855   struct bxxp_request_val *request_val = NULL;
856
857   CHECK_DISPLAY_AS_DATA(proto_bxxp, tvb, pinfo, tree);
858
859   offset = 0;
860
861   /* If we have per frame data, use that, else, we must have lost the per-
862    * frame data, and we have to do a full dissect pass again.
863    *
864    * The per-frame data tells us how much of this frame is left over from a
865    * previous frame, so we dissect it as payload and then try to dissect the
866    * rest.
867    * 
868    * We use the conversation to build up info on the first pass over the
869    * packets of type BXXP, and record anything that is needed if the user
870    * does random dissects of packets in per packet data.
871    *
872    * Once we have per-packet data, we don't need the conversation stuff 
873    * anymore, but if per-packet data and conversation stuff gets deleted, as 
874    * it does under some circumstances when a rescan is done, it all gets 
875    * rebuilt.
876    */
877
878   /* Find out what conversation this packet is part of ... but only
879    * if we have no information on this packet, so find the per-frame 
880    * info first.
881    */
882
883   frame_data = p_get_proto_data(pinfo->fd, proto_bxxp);
884
885   if (!frame_data) {
886
887     conversation = find_conversation(&pinfo->src, &pinfo->dst, pi.ptype,
888                                        pinfo->srcport, pinfo->destport);
889     if (conversation == NULL) { /* No conversation, create one */
890         conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
891                                         pinfo->srcport, pinfo->destport, NULL);
892
893       }
894
895       /* 
896        * Check for and insert an entry in the request table if does not exist
897        */
898       request_key.conversation = conversation->index;
899
900       request_val = (struct bxxp_request_val *)g_hash_table_lookup(bxxp_request_hash, &request_key);
901       
902       if (!request_val) { /* Create one */
903
904         new_request_key = g_mem_chunk_alloc(bxxp_request_keys);
905         new_request_key->conversation = conversation->index;
906
907         request_val = g_mem_chunk_alloc(bxxp_request_vals);
908         request_val->processed = 0;
909         request_val->size = 0;
910
911         g_hash_table_insert(bxxp_request_hash, new_request_key, request_val);
912
913       }
914     }
915
916   if (check_col(pinfo->fd, COL_PROTOCOL))
917     col_add_str(pinfo->fd, COL_PROTOCOL, "BXXP");
918
919   if (check_col(pinfo->fd, COL_INFO)) {  /* Check the type ... */
920
921     col_add_fstr(pinfo->fd, COL_INFO, "%s", tvb_format_text(tvb, offset, tvb_length_remaining(tvb, offset)));
922
923   }
924
925   /* Here, we parse the message so we can retrieve the info we need, which 
926    * is that there is some payload left from a previous segment on the 
927    * front of this segment ... This all depends on TCP segments not getting
928    * out of order ... 
929    *
930    * As a huge kludge, we push the checking for the tree down into the code
931    * and process as if we were given a tree but not call the routines that
932    * adorn the protocol tree if they were NULL.
933    */
934
935   if (tree) {  /* Build the tree info ... */
936
937     ti = proto_tree_add_item(tree, proto_bxxp, tvb, offset, tvb_length(tvb), FALSE);
938
939     bxxp_tree = proto_item_add_subtree(ti, ett_bxxp);
940
941   }
942   
943   /* Check the per-frame data and the conversation for any left-over 
944    * payload from the previous frame 
945    */
946
947   /* FIXME: This conditional is not quite correct */
948   if ((frame_data && frame_data->pl_left > 0) ||
949       (request_val && request_val->size > 0)) {
950     int pl_left = 0;
951
952     if (frame_data) {
953       pl_left = frame_data->pl_left;
954     }
955     else {
956       pl_left = request_val->size;
957       request_val->size = 0;
958
959       /* We create the frame data here for this case, and 
960        * elsewhere for other frames
961        */
962
963       frame_data = g_mem_chunk_alloc(bxxp_packet_infos);
964
965       frame_data->pl_left = pl_left;
966       frame_data->pl_size = 0;
967
968       p_add_proto_data(pinfo->fd, proto_bxxp, frame_data);
969
970     }
971
972     pl_left = MIN(pl_left, tvb_length_remaining(tvb, offset));
973
974     /* Add the payload bit, only if we have a tree */
975     if (tree) {
976       proto_tree_add_text(bxxp_tree, tvb, offset, pl_left, "Payload: %s",
977                           tvb_format_text(tvb, offset, pl_left));
978     }
979     offset += pl_left;
980   }
981
982   if (tvb_length_remaining(tvb, offset) > 0) {
983
984     offset += dissect_bxxp_tree(tvb, offset, pinfo, bxxp_tree, request_val, frame_data);
985
986   }
987
988   /* Set up the per-frame data here if not already done so */
989
990   if (frame_data == NULL) { 
991
992     frame_data = g_mem_chunk_alloc(bxxp_packet_infos);
993
994     frame_data->pl_left = 0;
995     frame_data->pl_size = 0;
996
997     p_add_proto_data(pinfo->fd, proto_bxxp, frame_data);
998         
999   }
1000
1001 }
1002
1003 /* Register all the bits needed with the filtering engine */
1004
1005 void 
1006 proto_register_bxxp(void)
1007 {
1008   static hf_register_info hf[] = {
1009     { &hf_bxxp_proto_viol,
1010       { "Protocol Violation", "bxxp.violation", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "" }},
1011
1012     { &hf_bxxp_req,
1013       { "Request", "bxxp.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "" }},
1014
1015     { &hf_bxxp_req_chan,
1016       { "Request Channel Number", "bxxp.req.channel", FT_UINT32, BASE_DEC, NULL, 0x0, ""}},
1017
1018     { &hf_bxxp_rsp,
1019       { "Response", "bxxp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "" }},
1020
1021     { &hf_bxxp_rsp_chan,
1022       { "Response Channel Number", "bxxp.rsp.channel", FT_UINT32, BASE_DEC, NULL, 0x0, ""}},
1023
1024     { &hf_bxxp_seq,
1025       { "Sequence", "bxxp.seq", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "" }},
1026
1027     { &hf_bxxp_seq_chan,
1028       { "Sequence Channel Number", "bxxp.seq.channel", FT_UINT32, BASE_DEC, NULL, 0x0, ""}},
1029
1030     { &hf_bxxp_end,
1031       { "End", "bxxp.end", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "" }},
1032
1033     { &hf_bxxp_complete,
1034       { "Complete", "bxxp.more.complete", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "" }},
1035
1036     { &hf_bxxp_intermediate,
1037       { "Intermediate", "bxxp.more.intermediate", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "" }},
1038
1039     { &hf_bxxp_serial,
1040       { "Serial", "bxxp.serial", FT_UINT32, BASE_DEC, NULL, 0x0, "" }},
1041
1042     { &hf_bxxp_seqno,
1043       { "Seqno", "bxxp.seqno", FT_UINT32, BASE_DEC, NULL, 0x0, "" }},
1044
1045     { &hf_bxxp_size,
1046       { "Size", "bxxp.size", FT_UINT32, BASE_DEC, NULL, 0x0, "" }},
1047
1048     { &hf_bxxp_channel,
1049       { "Channel", "bxxp.channel", FT_UINT32, BASE_DEC, NULL, 0x0, "" }},
1050
1051     { &hf_bxxp_negative,
1052       { "Negative", "bxxp.status.negative", FT_BOOLEAN, BASE_NONE, NULL, 0x0, ""}},
1053
1054     { &hf_bxxp_positive,
1055       { "Positive", "bxxp.status.positive", FT_BOOLEAN, BASE_NONE, NULL, 0x0, ""}},
1056
1057     { &hf_bxxp_ackno,
1058       { "Ackno", "bxxp.seq.ackno", FT_UINT32, BASE_DEC, NULL, 0x0, ""}},
1059
1060     { &hf_bxxp_window,
1061       { "Window", "bxxp.seq.window", FT_UINT32, BASE_DEC, NULL, 0x0, ""}},
1062
1063   };
1064   static gint *ett[] = {
1065     &ett_bxxp,
1066     &ett_mime_header,
1067     &ett_header,
1068     &ett_trailer,
1069   };
1070   module_t *bxxp_module; 
1071
1072   /* Register our configuration options for BXXP, particularly our port */
1073
1074   bxxp_module = prefs_register_module("bxxp", "BXXP", proto_reg_handoff_bxxp);
1075
1076   prefs_register_uint_preference(bxxp_module, "tcp.port", "BXXP TCP Port",
1077                                  "Set the port for BXXP messages (if other"
1078                                  " than the default of 10288)",
1079                                  10, &global_bxxp_tcp_port);
1080
1081   prefs_register_bool_preference(bxxp_module, "strict_header_terminator", 
1082                                  "BXXP Header Requires CRLF", 
1083                                  "Specifies that BXXP requires CRLF as a "
1084                                  "terminator, and not just CR or LF",
1085                                  &global_bxxp_strict_term);
1086
1087   proto_bxxp = proto_register_protocol("Blocks eXtensible eXchange Protocol",
1088                                        "bxxp");
1089
1090   proto_register_field_array(proto_bxxp, hf, array_length(hf));
1091   proto_register_subtree_array(ett, array_length(ett));
1092   register_init_routine(&bxxp_init_protocol);
1093
1094 }
1095
1096 /* The registration hand-off routine */
1097 void
1098 proto_reg_handoff_bxxp(void)
1099 {
1100   static int bxxp_prefs_initialized = FALSE;
1101
1102   if (bxxp_prefs_initialized) {
1103
1104     dissector_delete("tcp.port", tcp_port, dissect_bxxp);
1105
1106   }
1107   else {
1108
1109     bxxp_prefs_initialized = TRUE;
1110
1111   }
1112
1113   /* Set our port number for future use */
1114
1115   tcp_port = global_bxxp_tcp_port;
1116
1117   dissector_add("tcp.port", global_bxxp_tcp_port, dissect_bxxp);
1118
1119 }