Add the ability to print packet dissections in PDML (an XML-based format)
[obnox/wireshark/wip.git] / print.c
1 /* print.c
2  * Routines for printing packet analysis trees.
3  *
4  * $Id: print.c,v 1.64 2003/12/06 06:09:10 gram Exp $
5  *
6  * Gilbert Ramirez <gram@alumni.rice.edu>
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@ethereal.com>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <stdio.h>
32 #include <string.h>
33
34 #include <epan/epan.h>
35 #include <epan/epan_dissect.h>
36 #include <epan/tvbuff.h>
37 #include <epan/packet.h>
38
39 #include "print.h"
40 #include "ps.h"
41 #include "util.h"
42 #include "packet-data.h"
43 #include "packet-frame.h"
44
45 #define PDML_VERSION "0"
46
47 static void proto_tree_print_node(proto_node *node, gpointer data);
48 static void proto_tree_print_node_pdml(proto_node *node, gpointer data);
49 static void print_hex_data_buffer(FILE *fh, register const guchar *cp,
50     register guint length, char_enc encoding, gint format);
51 static void ps_clean_string(unsigned char *out, const unsigned char *in,
52                         int outbuf_size);
53
54 typedef struct {
55         int             level;
56         FILE            *fh;
57         GSList          *src_list;
58         gboolean        print_all_levels;
59         gboolean        print_hex_for_data;
60         char_enc        encoding;
61         gint            format;
62         epan_dissect_t  *edt;
63 } print_data;
64
65 static void print_pdml_geninfo(proto_tree *tree, print_data *pdata);
66
67 FILE *open_print_dest(int to_file, const char *dest)
68 {
69         FILE    *fh;
70
71         /* Open the file or command for output */
72         if (to_file)
73                 fh = fopen(dest, "w");
74         else
75                 fh = popen(dest, "w");
76
77         return fh;
78 }
79
80 void close_print_dest(int to_file, FILE *fh)
81 {
82         /* Close the file or command */
83         if (to_file)
84                 fclose(fh);
85         else
86                 pclose(fh);
87 }
88
89
90 void
91 proto_tree_print(print_args_t *print_args, epan_dissect_t *edt,
92     FILE *fh)
93 {
94         print_data data;
95
96         /* Create the output */
97         data.level = 0;
98         data.fh = fh;
99         data.src_list = edt->pi.data_src;
100         data.encoding = edt->pi.fd->flags.encoding;
101         data.print_all_levels = print_args->expand_all;
102         data.print_hex_for_data = !print_args->print_hex;
103             /* If we're printing the entire packet in hex, don't
104                print uninterpreted data fields in hex as well. */
105         data.format = print_args->format;
106         data.edt = edt;
107
108         if (data.format == PR_FMT_PDML) {
109
110                 fprintf(fh, "<packet>\n");
111
112                 /* Print a "geninfo" protocol as required by PDML */
113                 print_pdml_geninfo(edt->tree, &data);
114
115                 proto_tree_children_foreach(edt->tree, proto_tree_print_node_pdml, &data);
116
117                 fprintf(fh, "</packet>\n\n");
118         }
119         else {
120                 proto_tree_children_foreach(edt->tree, proto_tree_print_node, &data);
121         }
122 }
123
124 /*
125  * Find the data source for a specified field, and return a pointer
126  * to the data in it.
127  */
128 static const guint8 *
129 get_field_data(GSList *src_list, field_info *fi)
130 {
131         GSList *src_le;
132         data_source *src;
133         tvbuff_t *src_tvb;
134
135         for (src_le = src_list; src_le != NULL; src_le = src_le->next) {
136                 src = src_le->data;
137                 src_tvb = src->tvb;
138                 if (fi->ds_tvb == src_tvb) {
139                         /*
140                          * Found it.
141                          */
142                         return tvb_get_ptr(src_tvb, fi->start, fi->length);
143                 }
144         }
145         g_assert_not_reached();
146         return NULL;    /* not found */
147 }
148
149 #define MAX_INDENT      160
150
151 #define MAX_PS_LINE_LENGTH 256
152
153 /* Print a tree's data, and any child nodes. */
154 static
155 void proto_tree_print_node(proto_node *node, gpointer data)
156 {
157         field_info      *fi = PITEM_FINFO(node);
158         print_data      *pdata = (print_data*) data;
159         const guint8    *pd;
160         gchar           label_str[ITEM_LABEL_LENGTH];
161         gchar           *label_ptr;
162
163         /* Don't print invisible entries. */
164         if (!fi->visible)
165                 return;
166
167         /* was a free format label produced? */
168         if (fi->rep) {
169                 label_ptr = fi->rep->representation;
170         }
171         else { /* no, make a generic label */
172                 label_ptr = label_str;
173                 proto_item_fill_label(fi, label_str);
174         }
175
176         print_line(pdata->fh, pdata->level, pdata->format, label_ptr);
177
178         /* If it's uninterpreted data, dump it (unless our caller will
179            be printing the entire packet in hex). */
180         if (fi->hfinfo->id == proto_data && pdata->print_hex_for_data) {
181                 /*
182                  * Find the data for this field.
183                  */
184                 pd = get_field_data(pdata->src_list, fi);
185                 print_hex_data_buffer(pdata->fh, pd, fi->length,
186                     pdata->encoding, pdata->format);
187         }
188
189         /* If we're printing all levels, or if this node is one with a
190            subtree and its subtree is expanded, recurse into the subtree,
191            if it exists. */
192         g_assert(fi->tree_type >= -1 && fi->tree_type < num_tree_types);
193         if (pdata->print_all_levels ||
194             (fi->tree_type >= 0 && tree_is_expanded[fi->tree_type])) {
195                 if (node->first_child != NULL) {
196                         pdata->level++;
197                         proto_tree_children_foreach(node,
198                                 proto_tree_print_node, pdata);
199                         pdata->level--;
200                 }
201         }
202 }
203
204 /* Print a string, escaping out certain characters that need to
205  * escaped out for XML. */
206 static void
207 print_escaped_xml(FILE *fh, char *unescaped_string)
208 {
209         unsigned char *p;
210
211         for (p = unescaped_string; *p != '\0'; p++) {
212                 switch (*p) {
213                         case '&':
214                                 fputs("&amp;", fh);
215                                 break;
216                         case '<':
217                                 fputs("&lt;", fh);
218                                 break;
219                         case '>':
220                                 fputs("&gt;", fh);
221                                 break;
222                         case '"':
223                                 fputs("&quot;", fh);
224                                 break;
225                         default:
226                                 fputc(*p, fh);
227                 }
228         }
229 }
230
231 static void
232 print_field_hex_value(print_data *pdata, field_info *fi)
233 {
234         int i;
235         const guint8 *pd;
236
237         /* Find the data for this field. */
238         pd = get_field_data(pdata->src_list, fi);
239
240         /* Print a simple hex dump */
241         for (i = 0 ; i < fi->length; i++) {
242                 fprintf(pdata->fh, "%02x", pd[i]);
243         }
244 }
245
246
247 /* Print a tree's data, and any child nodes, as PDML */
248 static void
249 proto_tree_print_node_pdml(proto_node *node, gpointer data)
250 {
251         field_info      *fi = PITEM_FINFO(node);
252         print_data      *pdata = (print_data*) data;
253         gchar           *label_ptr;
254         gchar           label_str[ITEM_LABEL_LENGTH];
255         char            *dfilter_string;
256         int             chop_len;
257         int             i;
258
259         for (i = -1; i < pdata->level; i++) {
260                 fputs("  ", pdata->fh);
261         }
262
263         /* Text label. It's printed as a field with no name. */
264         if (fi->hfinfo->id == hf_text_only) {
265                 /* Get the text */
266                 if (fi->rep) {
267                         label_ptr = fi->rep->representation;
268                 }
269                 else {
270                         label_ptr = "";
271                 }
272
273                 fputs("<field show=\"", pdata->fh);
274                 print_escaped_xml(pdata->fh, label_ptr);
275
276                 fprintf(pdata->fh, "\" size=\"%d", fi->length);
277                 fprintf(pdata->fh, "\" pos=\"%d", fi->start);
278
279                 fputs("\" value=\"", pdata->fh);
280                 print_field_hex_value(pdata, fi);
281
282                 if (node->first_child != NULL) {
283                         fputs("\">\n", pdata->fh);
284                 }
285                 else {
286                         fputs("\"/>\n", pdata->fh);
287                 }
288         }
289         /* Uninterpreted data, i.e., the "Data" protocol, is
290          * printed as a field instead of a protocol. */
291         else if (fi->hfinfo->id == proto_data) {
292
293                 fputs("<field name=\"data\" value=\"", pdata->fh);
294
295                 print_field_hex_value(pdata, fi);
296
297                 fputs("\"/>\n", pdata->fh);
298
299         }
300         /* Normal protocols and fields */
301         else {
302                 if (fi->hfinfo->type == FT_PROTOCOL) {
303                         fputs("<proto name=\"", pdata->fh);
304                 }
305                 else {
306                         fputs("<field name=\"", pdata->fh);
307                 }
308                 print_escaped_xml(pdata->fh, fi->hfinfo->abbrev);
309
310                 if (fi->rep) {
311                         fputs("\" showname=\"", pdata->fh);
312                         print_escaped_xml(pdata->fh, fi->rep->representation);
313                 }
314                 else {
315                         label_ptr = label_str;
316                         proto_item_fill_label(fi, label_str);
317                         fputs("\" showname=\"", pdata->fh);
318                         print_escaped_xml(pdata->fh, label_ptr);
319                 }
320
321                 fprintf(pdata->fh, "\" size=\"%d", fi->length);
322                 fprintf(pdata->fh, "\" pos=\"%d", fi->start);
323 /*              fprintf(pdata->fh, "\" id=\"%d", fi->hfinfo->id);*/
324
325                 if (fi->hfinfo->type != FT_PROTOCOL) {
326                         /* Field */
327
328                         /* XXX - this is a hack until we can juse call
329                          * fvalue_to_string_repr() for *all* FT_* types. */
330                         dfilter_string = proto_construct_dfilter_string(fi,
331                                         pdata->edt);
332                         chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */
333
334                         /* XXX - Remove double-quotes. Again, once we can call
335                          * fvalue_to_string_repr(), we can ask it not to
336                          * produce the version for display-filters, and thus,
337                          * no double-quotes. */
338                         if (dfilter_string[strlen(dfilter_string)-1] == '"') {
339                                 dfilter_string[strlen(dfilter_string)-1] = '\0';
340                                 chop_len++;
341                         }
342
343                         fputs("\" show=\"", pdata->fh);
344                         print_escaped_xml(pdata->fh, &dfilter_string[chop_len]);
345                 }
346
347                 if (fi->hfinfo->type != FT_PROTOCOL && fi->length > 0) {
348                         fputs("\" value=\"", pdata->fh);
349                         print_field_hex_value(pdata, fi);
350                 }
351
352                 if (node->first_child != NULL) {
353                         fputs("\">\n", pdata->fh);
354                 }
355                 else if (fi->hfinfo->id == proto_data) {
356                         fputs("\">\n", pdata->fh);
357                 }
358                 else {
359                         fputs("\"/>\n", pdata->fh);
360                 }
361         }
362
363         /* We always pring all levels for PDML. Recurse here. */
364         if (node->first_child != NULL) {
365                 pdata->level++;
366                 proto_tree_children_foreach(node,
367                                 proto_tree_print_node_pdml, pdata);
368                 pdata->level--;
369         }
370
371         if (node->first_child != NULL) {
372                 for (i = -1; i < pdata->level; i++) {
373                         fputs("  ", pdata->fh);
374                 }
375                 if (fi->hfinfo->type == FT_PROTOCOL) {
376                         fputs("</proto>\n", pdata->fh);
377                 }
378                 else {
379                         fputs("</field>\n", pdata->fh);
380                 }
381         }
382 }
383
384 /* Print info for a 'geninfo' pseudo-protocol. This is required by
385  * the PDML spec. The information is contained in Ethereal's 'frame' protocol,
386  * but we produce a 'geninfo' protocol in the PDML to conform to spec.
387  * The 'frame' protocol follows the 'geninfo' protocol in the PDML. */
388 static void
389 print_pdml_geninfo(proto_tree *tree, print_data *pdata)
390 {
391         guint32 num, len, caplen;
392         nstime_t *timestamp;
393         GPtrArray *finfo_array;
394         field_info *frame_finfo;
395
396         /* Get frame protocol's finfo. */
397         finfo_array = proto_find_finfo(tree, proto_frame);
398         if (g_ptr_array_len(finfo_array) < 1) {
399                 return;
400         }
401         frame_finfo = finfo_array->pdata[0];
402         g_ptr_array_free(finfo_array, FALSE);
403
404         /* frame.number --> geninfo.num */
405         finfo_array = proto_find_finfo(tree, hf_frame_number);
406         if (g_ptr_array_len(finfo_array) < 1) {
407                 return;
408         }
409         num = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
410         g_ptr_array_free(finfo_array, FALSE);
411
412         /* frame.pkt_len --> geninfo.len */
413         finfo_array = proto_find_finfo(tree, hf_frame_packet_len);
414         if (g_ptr_array_len(finfo_array) < 1) {
415                 return;
416         }
417         len = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
418         g_ptr_array_free(finfo_array, FALSE);
419
420         /* frame.cap_len --> geninfo.caplen */
421         finfo_array = proto_find_finfo(tree, hf_frame_capture_len);
422         if (g_ptr_array_len(finfo_array) < 1) {
423                 return;
424         }
425         caplen = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
426         g_ptr_array_free(finfo_array, FALSE);
427
428         /* frame.time --> geninfo.timestamp */
429         finfo_array = proto_find_finfo(tree, hf_frame_arrival_time);
430         if (g_ptr_array_len(finfo_array) < 1) {
431                 return;
432         }
433         timestamp = fvalue_get(&((field_info*)finfo_array->pdata[0])->value);
434         g_ptr_array_free(finfo_array, FALSE);
435
436         /* Print geninfo start */
437         fprintf(pdata->fh,
438 "  <proto name=\"geninfo\" pos=\"1\" showname=\"General information\" size=\"%u\">\n",
439                 frame_finfo->length);
440
441         /* Print geninfo.num */
442         fprintf(pdata->fh,
443 "    <field name=\"num\" pos=\"1\" show=\"%u\" showname=\"Number\" value=\"%x\" size=\"%u\"/>\n",
444                 num, num, frame_finfo->length);
445
446         /* Print geninfo.len */
447         fprintf(pdata->fh,
448 "    <field name=\"len\" pos=\"1\" show=\"%u\" showname=\"Packet Length\" value=\"%x\" size=\"%u\"/>\n",
449                 len, len, frame_finfo->length);
450
451         /* Print geninfo.caplen */
452         fprintf(pdata->fh,
453 "    <field name=\"caplen\" pos=\"1\" show=\"%u\" showname=\"Captured Length\" value=\"%x\" size=\"%u\"/>\n",
454                 caplen, caplen, frame_finfo->length);
455
456         /* Print geninfo.timestamp */
457         fprintf(pdata->fh,
458 "    <field name=\"timestamp\" pos=\"1\" show=\"%s\" showname=\"Captured Time\" value=\"%d.%09d\" size=\"%u\"/>\n",
459                 abs_time_to_str(timestamp), (int) timestamp->secs, timestamp->nsecs, frame_finfo->length);
460
461         /* Print geninfo end */
462         fprintf(pdata->fh,
463 "  </proto>\n");
464 }
465
466
467 void print_hex_data(FILE *fh, gint format, epan_dissect_t *edt)
468 {
469         gboolean multiple_sources;
470         GSList *src_le;
471         data_source *src;
472         tvbuff_t *tvb;
473         char *name;
474         char *line;
475         const guchar *cp;
476         guint length;
477
478         /*
479          * Set "multiple_sources" iff this frame has more than one
480          * data source; if it does, we need to print the name of
481          * the data source before printing the data from the
482          * data source.
483          */
484         multiple_sources = (edt->pi.data_src->next != NULL);
485
486         for (src_le = edt->pi.data_src; src_le != NULL;
487             src_le = src_le->next) {
488                 src = src_le->data;
489                 tvb = src->tvb;
490                 if (multiple_sources) {
491                         name = src->name;
492                         print_line(fh, 0, format, "");
493                         line = g_malloc(strlen(name) + 2);      /* <name>:\0 */
494                         strcpy(line, name);
495                         strcat(line, ":");
496                         print_line(fh, 0, format, line);
497                         g_free(line);
498                 }
499                 length = tvb_length(tvb);
500                 cp = tvb_get_ptr(tvb, 0, length);
501                 print_hex_data_buffer(fh, cp, length,
502                     edt->pi.fd->flags.encoding, format);
503         }
504 }
505
506 /*
507  * This routine is based on a routine created by Dan Lasley
508  * <DLASLEY@PROMUS.com>.
509  *
510  * It was modified for Ethereal by Gilbert Ramirez and others.
511  */
512
513 #define MAX_OFFSET_LEN  8       /* max length of hex offset of bytes */
514 #define BYTES_PER_LINE  16      /* max byte values printed on a line */
515 #define HEX_DUMP_LEN    (BYTES_PER_LINE*3)
516                                 /* max number of characters hex dump takes -
517                                    2 digits plus trailing blank */
518 #define DATA_DUMP_LEN   (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
519                                 /* number of characters those bytes take;
520                                    3 characters per byte of hex dump,
521                                    2 blanks separating hex from ASCII,
522                                    1 character per byte of ASCII dump */
523 #define MAX_LINE_LEN    (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
524                                 /* number of characters per line;
525                                    offset, 2 blanks separating offset
526                                    from data dump, data dump */
527
528 static void
529 print_hex_data_buffer(FILE *fh, register const guchar *cp,
530     register guint length, char_enc encoding, gint format)
531 {
532         register unsigned int ad, i, j, k, l;
533         guchar c;
534         guchar line[MAX_LINE_LEN + 1];
535         unsigned int use_digits;
536         static guchar binhex[16] = {
537                 '0', '1', '2', '3', '4', '5', '6', '7',
538                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
539
540         print_line(fh, 0, format, "");
541
542         /*
543          * How many of the leading digits of the offset will we supply?
544          * We always supply at least 4 digits, but if the maximum offset
545          * won't fit in 4 digits, we use as many digits as will be needed.
546          */
547         if (((length - 1) & 0xF0000000) != 0)
548                 use_digits = 8; /* need all 8 digits */
549         else if (((length - 1) & 0x0F000000) != 0)
550                 use_digits = 7; /* need 7 digits */
551         else if (((length - 1) & 0x00F00000) != 0)
552                 use_digits = 6; /* need 6 digits */
553         else if (((length - 1) & 0x000F0000) != 0)
554                 use_digits = 5; /* need 5 digits */
555         else
556                 use_digits = 4; /* we'll supply 4 digits */
557
558         ad = 0;
559         i = 0;
560         j = 0;
561         k = 0;
562         while (i < length) {
563                 if ((i & 15) == 0) {
564                         /*
565                          * Start of a new line.
566                          */
567                         j = 0;
568                         k = 0;
569                         l = use_digits;
570                         do {
571                                 l--;
572                                 c = (ad >> (l*4)) & 0xF;
573                                 line[j++] = binhex[c];
574                         } while (l != 0);
575                         line[j++] = ' ';
576                         line[j++] = ' ';
577                         memset(line+j, ' ', DATA_DUMP_LEN);
578
579                         /*
580                          * Offset in line of ASCII dump.
581                          */
582                         k = j + HEX_DUMP_LEN + 2;
583                 }
584                 c = *cp++;
585                 line[j++] = binhex[c>>4];
586                 line[j++] = binhex[c&0xf];
587                 j++;
588                 if (encoding == CHAR_EBCDIC) {
589                         c = EBCDIC_to_ASCII1(c);
590                 }
591                 line[k++] = c >= ' ' && c < 0x7f ? c : '.';
592                 i++;
593                 if ((i & 15) == 0 || i == length) {
594                         /*
595                          * We'll be starting a new line, or
596                          * we're finished printing this buffer;
597                          * dump out the line we've constructed,
598                          * and advance the offset.
599                          */
600                         line[k] = '\0';
601                         print_line(fh, 0, format, line);
602                         ad += 16;
603                 }
604         }
605 }
606
607 static
608 void ps_clean_string(unsigned char *out, const unsigned char *in,
609                         int outbuf_size)
610 {
611         int rd, wr;
612         char c;
613
614         for (rd = 0, wr = 0 ; wr < outbuf_size; rd++, wr++ ) {
615                 c = in[rd];
616                 switch (c) {
617                         case '(':
618                         case ')':
619                         case '\\':
620                                 out[wr] = '\\';
621                                 out[++wr] = c;
622                                 break;
623
624                         default:
625                                 out[wr] = c;
626                                 break;
627                 }
628
629                 if (c == 0) {
630                         break;
631                 }
632         }
633 }
634
635 /* Some formats need stuff at the beginning of the output */
636 void
637 print_preamble(FILE *fh, gint format)
638 {
639         if (format == PR_FMT_PS)
640                 print_ps_preamble(fh);
641         else if (format == PR_FMT_PDML) {
642                 fputs("<?xml version=\"1.0\"?>\n", fh);
643                 fputs("<pdml version=\"" PDML_VERSION "\" ", fh);
644                 fprintf(fh, "creator=\"%s/%s\">\n", PACKAGE, VERSION);
645         }
646 }
647
648 /* Some formats need stuff at the end of the output */
649 void
650 print_finale(FILE *fh, gint format)
651 {
652         if (format == PR_FMT_PS)
653                 print_ps_finale(fh);
654         else if (format == PR_FMT_PDML) {
655                 fputs("</pdml>\n", fh);
656         }
657 }
658
659 void
660 print_line(FILE *fh, int indent, gint format, char *line)
661 {
662         char            space[MAX_INDENT+1];
663         int             i;
664         int             num_spaces;
665         char            psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
666
667         if (format == PR_FMT_PS) {
668                 ps_clean_string(psbuffer, line, MAX_PS_LINE_LENGTH);
669                 fprintf(fh, "%d (%s) putline\n", indent, psbuffer);
670         }
671         else if (format == PR_FMT_TEXT) {
672                 /* Prepare the tabs for printing, depending on tree level */
673                 num_spaces = indent * 4;
674                 if (num_spaces > MAX_INDENT) {
675                         num_spaces = MAX_INDENT;
676                 }
677                 for (i = 0; i < num_spaces; i++) {
678                         space[i] = ' ';
679                 }
680                 /* The string is NUL-terminated */
681                 space[num_spaces] = '\0';
682
683                 fputs(space, fh);
684                 fputs(line, fh);
685                 putc('\n', fh);
686         }
687         else {
688                 g_assert_not_reached();
689         }
690 }