e016b4a20bb47986afb6872ffd45d9b9ac12c390
[obnox/wireshark/wip.git] / print.c
1 /* print.c
2  * Routines for printing packet analysis trees.
3  *
4  * $Id: print.c,v 1.65 2003/12/08 21:57:25 guy 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                         if (dfilter_string != NULL) {
333                                 chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */
334
335                                 /* XXX - Remove double-quotes. Again, once we
336                                  * can call fvalue_to_string_repr(), we can
337                                  * ask it not to produce the version for
338                                  * display-filters, and thus, no
339                                  * double-quotes. */
340                                 if (dfilter_string[strlen(dfilter_string)-1] == '"') {
341                                         dfilter_string[strlen(dfilter_string)-1] = '\0';
342                                         chop_len++;
343                                 }
344
345                                 fputs("\" show=\"", pdata->fh);
346                                 print_escaped_xml(pdata->fh, &dfilter_string[chop_len]);
347                         }
348                         if (fi->length > 0) {
349                                 fputs("\" value=\"", pdata->fh);
350                                 print_field_hex_value(pdata, fi);
351                         }
352                 }
353
354                 if (node->first_child != NULL) {
355                         fputs("\">\n", pdata->fh);
356                 }
357                 else if (fi->hfinfo->id == proto_data) {
358                         fputs("\">\n", pdata->fh);
359                 }
360                 else {
361                         fputs("\"/>\n", pdata->fh);
362                 }
363         }
364
365         /* We always pring all levels for PDML. Recurse here. */
366         if (node->first_child != NULL) {
367                 pdata->level++;
368                 proto_tree_children_foreach(node,
369                                 proto_tree_print_node_pdml, pdata);
370                 pdata->level--;
371         }
372
373         if (node->first_child != NULL) {
374                 for (i = -1; i < pdata->level; i++) {
375                         fputs("  ", pdata->fh);
376                 }
377                 if (fi->hfinfo->type == FT_PROTOCOL) {
378                         fputs("</proto>\n", pdata->fh);
379                 }
380                 else {
381                         fputs("</field>\n", pdata->fh);
382                 }
383         }
384 }
385
386 /* Print info for a 'geninfo' pseudo-protocol. This is required by
387  * the PDML spec. The information is contained in Ethereal's 'frame' protocol,
388  * but we produce a 'geninfo' protocol in the PDML to conform to spec.
389  * The 'frame' protocol follows the 'geninfo' protocol in the PDML. */
390 static void
391 print_pdml_geninfo(proto_tree *tree, print_data *pdata)
392 {
393         guint32 num, len, caplen;
394         nstime_t *timestamp;
395         GPtrArray *finfo_array;
396         field_info *frame_finfo;
397
398         /* Get frame protocol's finfo. */
399         finfo_array = proto_find_finfo(tree, proto_frame);
400         if (g_ptr_array_len(finfo_array) < 1) {
401                 return;
402         }
403         frame_finfo = finfo_array->pdata[0];
404         g_ptr_array_free(finfo_array, FALSE);
405
406         /* frame.number --> geninfo.num */
407         finfo_array = proto_find_finfo(tree, hf_frame_number);
408         if (g_ptr_array_len(finfo_array) < 1) {
409                 return;
410         }
411         num = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
412         g_ptr_array_free(finfo_array, FALSE);
413
414         /* frame.pkt_len --> geninfo.len */
415         finfo_array = proto_find_finfo(tree, hf_frame_packet_len);
416         if (g_ptr_array_len(finfo_array) < 1) {
417                 return;
418         }
419         len = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
420         g_ptr_array_free(finfo_array, FALSE);
421
422         /* frame.cap_len --> geninfo.caplen */
423         finfo_array = proto_find_finfo(tree, hf_frame_capture_len);
424         if (g_ptr_array_len(finfo_array) < 1) {
425                 return;
426         }
427         caplen = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
428         g_ptr_array_free(finfo_array, FALSE);
429
430         /* frame.time --> geninfo.timestamp */
431         finfo_array = proto_find_finfo(tree, hf_frame_arrival_time);
432         if (g_ptr_array_len(finfo_array) < 1) {
433                 return;
434         }
435         timestamp = fvalue_get(&((field_info*)finfo_array->pdata[0])->value);
436         g_ptr_array_free(finfo_array, FALSE);
437
438         /* Print geninfo start */
439         fprintf(pdata->fh,
440 "  <proto name=\"geninfo\" pos=\"1\" showname=\"General information\" size=\"%u\">\n",
441                 frame_finfo->length);
442
443         /* Print geninfo.num */
444         fprintf(pdata->fh,
445 "    <field name=\"num\" pos=\"1\" show=\"%u\" showname=\"Number\" value=\"%x\" size=\"%u\"/>\n",
446                 num, num, frame_finfo->length);
447
448         /* Print geninfo.len */
449         fprintf(pdata->fh,
450 "    <field name=\"len\" pos=\"1\" show=\"%u\" showname=\"Packet Length\" value=\"%x\" size=\"%u\"/>\n",
451                 len, len, frame_finfo->length);
452
453         /* Print geninfo.caplen */
454         fprintf(pdata->fh,
455 "    <field name=\"caplen\" pos=\"1\" show=\"%u\" showname=\"Captured Length\" value=\"%x\" size=\"%u\"/>\n",
456                 caplen, caplen, frame_finfo->length);
457
458         /* Print geninfo.timestamp */
459         fprintf(pdata->fh,
460 "    <field name=\"timestamp\" pos=\"1\" show=\"%s\" showname=\"Captured Time\" value=\"%d.%09d\" size=\"%u\"/>\n",
461                 abs_time_to_str(timestamp), (int) timestamp->secs, timestamp->nsecs, frame_finfo->length);
462
463         /* Print geninfo end */
464         fprintf(pdata->fh,
465 "  </proto>\n");
466 }
467
468
469 void print_hex_data(FILE *fh, gint format, epan_dissect_t *edt)
470 {
471         gboolean multiple_sources;
472         GSList *src_le;
473         data_source *src;
474         tvbuff_t *tvb;
475         char *name;
476         char *line;
477         const guchar *cp;
478         guint length;
479
480         /*
481          * Set "multiple_sources" iff this frame has more than one
482          * data source; if it does, we need to print the name of
483          * the data source before printing the data from the
484          * data source.
485          */
486         multiple_sources = (edt->pi.data_src->next != NULL);
487
488         for (src_le = edt->pi.data_src; src_le != NULL;
489             src_le = src_le->next) {
490                 src = src_le->data;
491                 tvb = src->tvb;
492                 if (multiple_sources) {
493                         name = src->name;
494                         print_line(fh, 0, format, "");
495                         line = g_malloc(strlen(name) + 2);      /* <name>:\0 */
496                         strcpy(line, name);
497                         strcat(line, ":");
498                         print_line(fh, 0, format, line);
499                         g_free(line);
500                 }
501                 length = tvb_length(tvb);
502                 cp = tvb_get_ptr(tvb, 0, length);
503                 print_hex_data_buffer(fh, cp, length,
504                     edt->pi.fd->flags.encoding, format);
505         }
506 }
507
508 /*
509  * This routine is based on a routine created by Dan Lasley
510  * <DLASLEY@PROMUS.com>.
511  *
512  * It was modified for Ethereal by Gilbert Ramirez and others.
513  */
514
515 #define MAX_OFFSET_LEN  8       /* max length of hex offset of bytes */
516 #define BYTES_PER_LINE  16      /* max byte values printed on a line */
517 #define HEX_DUMP_LEN    (BYTES_PER_LINE*3)
518                                 /* max number of characters hex dump takes -
519                                    2 digits plus trailing blank */
520 #define DATA_DUMP_LEN   (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
521                                 /* number of characters those bytes take;
522                                    3 characters per byte of hex dump,
523                                    2 blanks separating hex from ASCII,
524                                    1 character per byte of ASCII dump */
525 #define MAX_LINE_LEN    (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
526                                 /* number of characters per line;
527                                    offset, 2 blanks separating offset
528                                    from data dump, data dump */
529
530 static void
531 print_hex_data_buffer(FILE *fh, register const guchar *cp,
532     register guint length, char_enc encoding, gint format)
533 {
534         register unsigned int ad, i, j, k, l;
535         guchar c;
536         guchar line[MAX_LINE_LEN + 1];
537         unsigned int use_digits;
538         static guchar binhex[16] = {
539                 '0', '1', '2', '3', '4', '5', '6', '7',
540                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
541
542         print_line(fh, 0, format, "");
543
544         /*
545          * How many of the leading digits of the offset will we supply?
546          * We always supply at least 4 digits, but if the maximum offset
547          * won't fit in 4 digits, we use as many digits as will be needed.
548          */
549         if (((length - 1) & 0xF0000000) != 0)
550                 use_digits = 8; /* need all 8 digits */
551         else if (((length - 1) & 0x0F000000) != 0)
552                 use_digits = 7; /* need 7 digits */
553         else if (((length - 1) & 0x00F00000) != 0)
554                 use_digits = 6; /* need 6 digits */
555         else if (((length - 1) & 0x000F0000) != 0)
556                 use_digits = 5; /* need 5 digits */
557         else
558                 use_digits = 4; /* we'll supply 4 digits */
559
560         ad = 0;
561         i = 0;
562         j = 0;
563         k = 0;
564         while (i < length) {
565                 if ((i & 15) == 0) {
566                         /*
567                          * Start of a new line.
568                          */
569                         j = 0;
570                         k = 0;
571                         l = use_digits;
572                         do {
573                                 l--;
574                                 c = (ad >> (l*4)) & 0xF;
575                                 line[j++] = binhex[c];
576                         } while (l != 0);
577                         line[j++] = ' ';
578                         line[j++] = ' ';
579                         memset(line+j, ' ', DATA_DUMP_LEN);
580
581                         /*
582                          * Offset in line of ASCII dump.
583                          */
584                         k = j + HEX_DUMP_LEN + 2;
585                 }
586                 c = *cp++;
587                 line[j++] = binhex[c>>4];
588                 line[j++] = binhex[c&0xf];
589                 j++;
590                 if (encoding == CHAR_EBCDIC) {
591                         c = EBCDIC_to_ASCII1(c);
592                 }
593                 line[k++] = c >= ' ' && c < 0x7f ? c : '.';
594                 i++;
595                 if ((i & 15) == 0 || i == length) {
596                         /*
597                          * We'll be starting a new line, or
598                          * we're finished printing this buffer;
599                          * dump out the line we've constructed,
600                          * and advance the offset.
601                          */
602                         line[k] = '\0';
603                         print_line(fh, 0, format, line);
604                         ad += 16;
605                 }
606         }
607 }
608
609 static
610 void ps_clean_string(unsigned char *out, const unsigned char *in,
611                         int outbuf_size)
612 {
613         int rd, wr;
614         char c;
615
616         for (rd = 0, wr = 0 ; wr < outbuf_size; rd++, wr++ ) {
617                 c = in[rd];
618                 switch (c) {
619                         case '(':
620                         case ')':
621                         case '\\':
622                                 out[wr] = '\\';
623                                 out[++wr] = c;
624                                 break;
625
626                         default:
627                                 out[wr] = c;
628                                 break;
629                 }
630
631                 if (c == 0) {
632                         break;
633                 }
634         }
635 }
636
637 /* Some formats need stuff at the beginning of the output */
638 void
639 print_preamble(FILE *fh, gint format)
640 {
641         if (format == PR_FMT_PS)
642                 print_ps_preamble(fh);
643         else if (format == PR_FMT_PDML) {
644                 fputs("<?xml version=\"1.0\"?>\n", fh);
645                 fputs("<pdml version=\"" PDML_VERSION "\" ", fh);
646                 fprintf(fh, "creator=\"%s/%s\">\n", PACKAGE, VERSION);
647         }
648 }
649
650 /* Some formats need stuff at the end of the output */
651 void
652 print_finale(FILE *fh, gint format)
653 {
654         if (format == PR_FMT_PS)
655                 print_ps_finale(fh);
656         else if (format == PR_FMT_PDML) {
657                 fputs("</pdml>\n", fh);
658         }
659 }
660
661 void
662 print_line(FILE *fh, int indent, gint format, char *line)
663 {
664         char            space[MAX_INDENT+1];
665         int             i;
666         int             num_spaces;
667         char            psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
668
669         if (format == PR_FMT_PS) {
670                 ps_clean_string(psbuffer, line, MAX_PS_LINE_LENGTH);
671                 fprintf(fh, "%d (%s) putline\n", indent, psbuffer);
672         }
673         else if (format == PR_FMT_TEXT) {
674                 /* Prepare the tabs for printing, depending on tree level */
675                 num_spaces = indent * 4;
676                 if (num_spaces > MAX_INDENT) {
677                         num_spaces = MAX_INDENT;
678                 }
679                 for (i = 0; i < num_spaces; i++) {
680                         space[i] = ' ';
681                 }
682                 /* The string is NUL-terminated */
683                 space[num_spaces] = '\0';
684
685                 fputs(space, fh);
686                 fputs(line, fh);
687                 putc('\n', fh);
688         }
689         else {
690                 g_assert_not_reached();
691         }
692 }