bda0548630c3f8ca97e26119c96659bf49cb6fd6
[obnox/wireshark/wip.git] / wiretap / visual.c
1 /* visual.c
2  * File read and write routines for Visual Networks cap files.
3  * Copyright (c) 2001, Tom Nisbet  tnisbet@visualnetworks.com
4  *
5  * $Id: visual.c,v 1.10 2002/07/29 06:09:59 guy Exp $
6  *
7  * Wiretap Library
8  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
9  * 
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 #include <errno.h>
29 #include <string.h>
30 #include "wtap-int.h"
31 #include "file_wrappers.h"
32 #include "buffer.h"
33 #include "visual.h"
34
35 /*
36  * A Visual Networks traffic capture file contains three sections.  The
37  * first is a 192 octet file header.  This is followed by the captured
38  * packet headers and data.  The last section is the packet index block.
39  * The index block contains one 4 octet pointer for each captured packet.
40  * The first packet index is (4 * num_pkts) octets from the end of the file
41  * and the last index is in the last four octets of the file.
42  *
43  * All integer and time values are stored in little-endian format.
44  */
45
46 /* Capture file header, INCLUDING the magic number, is 192 bytes. */
47 #define CAPTUREFILE_HEADER_SIZE 192
48
49 /* Magic number for Visual Networks traffic capture files. */
50 static const char visual_magic[] = {
51     5, 'V', 'N', 'F'
52 };
53
54
55 /* Visual file header (minus magic number). */
56 struct visual_file_hdr
57 {
58     guint32 num_pkts;           /* Number of packets in the file */
59     guint32 start_time;         /* Capture start time in PC format */
60     guint16 media_type;         /* IANA ifType of packet source */
61     guint16 max_length;         /* Max allowable stored packet length */
62     guint16 file_flags;         /* File type flags */
63                                 /*   Bit 0 indicates indexes present */
64     guint16 file_version;       /* Version number of this file format */
65     guint32 media_speed;        /* ifSpeed of packet source in bits/sec. */
66     guint16 media_param;        /* Media-specific extra parameter. */
67     char    RESERVED_[102];     /* MUST BE ALL ZEROS FOR FUTURE COMPATABILITY */
68     char    description[64];    /* File description (null terminated) */
69 };
70
71
72 /* Packet status bits */
73 #define PS_LONG             0x01
74 #define PS_SHORT            0x02
75 #define PS_ERRORED          0x04
76 #define PS_1ST_AFTER_DROP   0x08
77 #define PS_APPROX_ORDER     0x10
78 #define PS_SENT             0x40
79 #define PS_ABORTED          0x80
80
81 /* Visual packet header */
82 struct visual_pkt_hdr
83 {
84     guint32 ts_delta;           /* Time stamp - msecs since start of capture */
85     guint16 orig_len;           /* Actual length of packet */
86     guint16 incl_len;           /* Number of octets captured in file */
87     guint32 status;             /* Packet status flags (media specific) */
88     guint8  encap_hint;         /* Encapsulation type hint */
89     guint8  encap_skip;         /* Number of bytes to skip before decoding */
90     char    RESERVED_[6];       /* RESERVED - must be zero */
91 };
92
93
94 /* Additional information for reading Visual files */
95 struct visual_read_info
96 {
97     guint32 num_pkts;           /* Number of pkts in the file */
98     guint32 current_pkt;        /* Next packet to be read */
99     double  start_time;         /* Capture start time in microseconds */
100 };
101
102
103 /* Additional information for writing Visual files */
104 struct visual_write_info
105 {
106     unsigned start_time;        /* Capture start time in seconds */
107     int     index_table_index;  /* Index of the next index entry */
108     int     index_table_size;   /* Allocated size of the index table */
109     guint32 * index_table;      /* File offsets for the packets */
110     guint32 next_offset;        /* Offset of next packet */
111 };
112
113
114 /* Local functions to handle file reads and writes */
115 static gboolean visual_read(wtap *wth, int *err, long *data_offset);
116 static void visual_close(wtap *wth);
117 static gboolean visual_seek_read(wtap *wth, long seek_off,
118     union wtap_pseudo_header *pseudo_header, guchar *pd, int packet_size,
119     int *err);
120 static gboolean visual_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
121     const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err);
122 static gboolean visual_dump_close(wtap_dumper *wdh, int *err);
123 static void visual_dump_free(wtap_dumper *wdh);
124
125
126 /* Open a file for reading */
127 int visual_open(wtap *wth, int *err)
128 {
129     int bytes_read;
130     char magic[sizeof visual_magic];
131     struct visual_file_hdr vfile_hdr;
132     struct visual_read_info * visual;
133     int encap;
134
135     /* Check the magic string at the start of the file */
136     errno = WTAP_ERR_CANT_READ;
137     bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
138     if (bytes_read != sizeof magic)
139     {
140         *err = file_error(wth->fh);
141         if (*err != 0)
142             return -1;
143         return 0;
144     }
145     if (memcmp(magic, visual_magic, sizeof visual_magic) != 0)
146     {
147         return 0;
148     }
149
150     /* Read the rest of the file header. */
151     errno = WTAP_ERR_CANT_READ;
152     bytes_read = file_read(&vfile_hdr, 1, sizeof vfile_hdr, wth->fh);
153     if (bytes_read != sizeof vfile_hdr)
154     {
155         *err = file_error(wth->fh);
156         if (*err != 0)
157             return -1;
158         return 0;
159     }
160
161     /* Verify the file version is known */
162     vfile_hdr.file_version = pletohs(&vfile_hdr.file_version);
163     if (vfile_hdr.file_version != 1)
164     {
165         g_message("visual: file version %u unsupported", vfile_hdr.file_version);
166         *err = WTAP_ERR_UNSUPPORTED;
167         return -1;
168     }
169
170     /* Translate the encapsulation type.  Note that a file with media
171        type 22 may contain Cisco HDLC or PPP over HDLC.  This will
172        get sorted out after the first packet is read. */
173     switch (pletohs(&vfile_hdr.media_type))
174     {
175     case  6: encap = WTAP_ENCAP_ETHERNET;   break;
176     case  9: encap = WTAP_ENCAP_TOKEN_RING; break;
177     case 16: encap = WTAP_ENCAP_LAPB;       break;
178     case 22: encap = WTAP_ENCAP_CHDLC;      break;
179     case 32: encap = WTAP_ENCAP_FRELAY;     break;
180     default:
181         g_message("visual: network type %u unknown or unsupported",
182                   vfile_hdr.media_type);
183         *err = WTAP_ERR_UNSUPPORTED_ENCAP;
184         return -1;
185     }
186
187     /* Fill in the wiretap struct with data from the file header */
188     wth->file_type = WTAP_FILE_VISUAL_NETWORKS;
189     wth->file_encap = encap;
190     wth->snapshot_length = pletohs(&vfile_hdr.max_length);
191
192     /* Save the pointer to the beginning of the packet data so
193        that the later seek_reads work correctly. */
194     wth->data_offset = CAPTUREFILE_HEADER_SIZE;
195
196     /* Set up the pointers to the handlers for this file type */
197     wth->subtype_read = visual_read;
198     wth->subtype_seek_read = visual_seek_read;
199     wth->subtype_close = visual_close;
200
201     /* Add Visual-specific information to the wiretap struct for later use. */
202     visual = g_malloc(sizeof(struct visual_read_info));
203     wth->capture.generic = visual;
204     visual->num_pkts = pletohl(&vfile_hdr.num_pkts);
205     visual->start_time = ((double) pletohl(&vfile_hdr.start_time)) * 1000000;
206     visual->current_pkt = 1;
207
208     return 1;
209 }
210
211
212 /* Read the next available packet from the file.  This is called
213    in a loop to sequentially read the entire file one time.  After
214    the file has been read once, any Future access to the packets is
215    done through seek_read. */
216 static gboolean visual_read(wtap *wth, int *err, long *data_offset)
217 {
218     struct visual_read_info *visual = wth->capture.generic;
219     guint32 packet_size = 0;
220     guint32 packet_status;
221     int bytes_read;
222     struct visual_pkt_hdr vpkt_hdr;
223     int phdr_size = sizeof(vpkt_hdr);
224     time_t  secs;
225     guint32 usecs;
226     double  t;
227
228     /* Check for the end of the packet data.  Note that a check for file EOF
229        will not work because there are index values stored after the last
230        packet's data. */
231     if (visual->current_pkt > visual->num_pkts) 
232     {
233         *err = 0;   /* it's just an EOF, not an error */
234         return FALSE;
235     }
236     visual->current_pkt++;
237
238     /* Read the packet header. */
239     errno = WTAP_ERR_CANT_READ;
240     bytes_read = file_read(&vpkt_hdr, 1, phdr_size, wth->fh);
241     if (bytes_read != phdr_size) 
242     {
243         *err = file_error(wth->fh);
244         if (*err == 0 && bytes_read != 0) 
245         {
246             *err = WTAP_ERR_SHORT_READ;
247         }
248         return FALSE;
249     }
250     wth->data_offset += phdr_size;
251
252     /* Read the packet data. */
253     packet_size = pletohs(&vpkt_hdr.incl_len);
254     if (packet_size > WTAP_MAX_PACKET_SIZE) 
255     {
256         /* Probably a corrupt capture file; don't blow up trying
257           to allocate space for an immensely-large packet. */
258         g_message("visual: File has %u-byte packet, bigger than maximum of %u",
259             packet_size, WTAP_MAX_PACKET_SIZE);
260         *err = WTAP_ERR_BAD_RECORD;
261         return FALSE;
262     }
263     buffer_assure_space(wth->frame_buffer, packet_size);
264     *data_offset = wth->data_offset;
265     errno = WTAP_ERR_CANT_READ;
266     bytes_read = file_read(buffer_start_ptr(wth->frame_buffer), 1,
267             packet_size, wth->fh);
268
269     if (bytes_read != (int) packet_size) 
270     {
271         *err = file_error(wth->fh);
272         if (*err == 0)
273             *err = WTAP_ERR_SHORT_READ;
274         return FALSE;
275     }
276     wth->data_offset += packet_size;
277
278     /* Set the packet time and length. */
279     t = visual->start_time;
280     t += ((double)pletohl(&vpkt_hdr.ts_delta))*1000;
281     secs = (time_t)(t/1000000);
282     usecs = (guint32)(t - secs*1000000);
283     wth->phdr.ts.tv_sec = secs;
284     wth->phdr.ts.tv_usec = usecs;
285     wth->phdr.caplen = packet_size;
286     wth->phdr.len = pletohs(&vpkt_hdr.orig_len);
287
288     /* Set status flags.  The only status currently supported for all
289        encapsulations is direction.  This either goes in the p2p or the
290        X.25 pseudo header.  It would probably be better to move this up
291        into the phdr. */
292     packet_status = pletohl(&vpkt_hdr.status);
293     switch (wth->file_encap)
294     {
295     case WTAP_ENCAP_CHDLC:
296     case WTAP_ENCAP_PPP_WITH_PHDR:
297         wth->pseudo_header.p2p.sent = (packet_status & PS_SENT) ? TRUE : FALSE;
298         break;
299
300     case WTAP_ENCAP_FRELAY:
301     case WTAP_ENCAP_LAPB:
302         wth->pseudo_header.x25.flags =
303             (packet_status & PS_SENT) ? 0x00 : FROM_DCE;
304         break;
305     }
306
307     /* Fill in the encapsulation.  Visual files have a media type in the
308        file header and an encapsulation type in each packet header.  Files
309        with a media type of HDLC can be either Cisco EtherType or PPP. */
310     if ((wth->file_encap == WTAP_ENCAP_CHDLC) && (vpkt_hdr.encap_hint == 14))
311         wth->phdr.pkt_encap = WTAP_ENCAP_PPP_WITH_PHDR;
312     else
313         wth->phdr.pkt_encap = wth->file_encap;
314
315     return TRUE;
316 }
317
318
319 /* Close a file opened for reading. */
320 static void visual_close(wtap *wth)
321 {
322     /* Free the info allocated by a Visual file reader. */
323     if (wth->capture.generic)
324         g_free(wth->capture.generic);
325     wth->capture.generic = 0;
326 }
327
328
329 /* Read packet data for random access.
330    This gets the packet data and rebuilds the pseudo header so that
331    the direction flag works. */
332 static gboolean visual_seek_read (wtap *wth, long seek_off, 
333     union wtap_pseudo_header *pseudo_header, guint8 *pd, int len, int *err)
334 {
335     struct visual_pkt_hdr vpkt_hdr;
336     int phdr_size = sizeof(vpkt_hdr);
337     guint32 packet_status;
338     int bytes_read;
339
340     /* Seek to the packet header */
341     if (file_seek(wth->random_fh, seek_off - sizeof(struct visual_pkt_hdr),
342                   SEEK_SET, err) == -1)
343         return FALSE;
344
345     /* Read the packet header to get the status flags. */
346     errno = WTAP_ERR_CANT_READ;
347     bytes_read = file_read(&vpkt_hdr, 1, phdr_size, wth->random_fh);
348     if (bytes_read != phdr_size) {
349         *err = file_error(wth->random_fh);
350         if (*err == 0)
351             *err = WTAP_ERR_SHORT_READ;
352         return FALSE;
353     }
354
355     /* Read the packet data. */
356     errno = WTAP_ERR_CANT_READ;
357     bytes_read = file_read(pd, sizeof(guint8), len, wth->random_fh);
358     if (bytes_read != len) {
359         if (*err == 0)
360             *err = WTAP_ERR_SHORT_READ;
361         return FALSE;
362     }
363
364     /* Set status flags.  The only status currently supported for all
365        encapsulations is direction.  This either goes in the p2p or the
366        X.25 pseudo header.  It would probably be better to move this up
367        into the phdr. */
368     packet_status = pletohl(&vpkt_hdr.status);
369     switch (wth->file_encap)
370     {
371     case WTAP_ENCAP_CHDLC:
372     case WTAP_ENCAP_PPP_WITH_PHDR:
373         pseudo_header->p2p.sent = (packet_status & PS_SENT) ? TRUE : FALSE;
374         break;
375
376     case WTAP_ENCAP_FRELAY:
377     case WTAP_ENCAP_LAPB:
378         pseudo_header->x25.flags = (packet_status & PS_SENT) ? 0x00 : FROM_DCE;
379         break;
380     }
381
382     return TRUE;
383 }
384
385
386 /* Check for media types that may be written in Visual file format. 
387    Returns 0 if the specified encapsulation type is supported,
388    an error indication otherwise. */
389 int visual_dump_can_write_encap(int encap)
390 {
391     /* Per-packet encapsulations aren't supported. */
392     if (encap == WTAP_ENCAP_PER_PACKET)
393         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
394
395     /* Check for supported encapsulation types */
396     switch (encap)
397     {
398     case WTAP_ENCAP_ETHERNET:
399     case WTAP_ENCAP_TOKEN_RING:
400     case WTAP_ENCAP_LAPB:
401     case WTAP_ENCAP_CHDLC:
402     case WTAP_ENCAP_FRELAY:
403     case WTAP_ENCAP_PPP:
404     case WTAP_ENCAP_PPP_WITH_PHDR:
405         return 0;
406     }
407
408     return WTAP_ERR_UNSUPPORTED_ENCAP;
409 }
410
411
412 /* Open a file for writing.
413    Returns TRUE on success, FALSE on failure; sets "*err" to an
414    error code on failure */
415 gboolean visual_dump_open(wtap_dumper *wdh, gboolean cant_seek, int *err)
416 {
417     struct visual_write_info *visual;
418
419     /* We can't fill in some fields in the header until all the packets
420        have been written, so we can't write to a pipe. */
421     if (cant_seek) {
422         *err = WTAP_ERR_CANT_WRITE_TO_PIPE;
423         return FALSE;
424     }
425
426     /* Set the write routines for a visual file. */
427     wdh->subtype_write = visual_dump;
428     wdh->subtype_close = visual_dump_close;
429
430     /* Create a struct to hold file information for the duration
431        of the write */
432     visual = g_malloc(sizeof(struct visual_write_info));
433     wdh->dump.opaque = visual;
434     visual->index_table_index = 0;
435     visual->index_table_size = 1024;
436     visual->index_table = 0;
437     visual->next_offset = CAPTUREFILE_HEADER_SIZE;
438
439     /* All of the fields in the file header aren't known yet so
440        just skip over it for now.  It will be created after all
441        of the packets have been written. */
442     if (fseek(wdh->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET) == -1) {
443         *err = errno;
444         return FALSE;
445     }
446
447     return TRUE;
448 }
449
450
451 /* Write a packet to a Visual dump file.
452    Returns TRUE on success, FALSE on failure. */
453 static gboolean visual_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
454     const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err)
455 {
456     struct visual_write_info * visual = wdh->dump.opaque;
457     struct visual_pkt_hdr vpkt_hdr;
458     size_t hdr_size = sizeof vpkt_hdr;
459     size_t nwritten;
460     unsigned delta_msec;
461     guint32 packet_status;
462
463     /* If the visual structure was never allocated then nothing useful
464        can be done. */
465     if (visual == 0)
466         return FALSE;
467
468     /* Zero out unused and reserved fields in the packet header. */
469     memset(&vpkt_hdr, 0, hdr_size);
470
471     /* Visual UpTime capture files have a capture start time in the
472        file header.  Each packet has a capture time (in msec) relative
473        to the file start time.  Use the time of the first packet as the
474        file start time. */
475     if (visual->index_table_index == 0) 
476     {
477         /* This is the first packet.  Save its start time as the file time. */
478         visual->start_time = phdr->ts.tv_sec;
479
480         /* Initialize the index table */
481         visual->index_table = g_malloc(1024 * sizeof *visual->index_table);
482         visual->index_table_size = 1024;
483     }
484     
485     /* Calculate milliseconds since capture start. */
486     delta_msec = phdr->ts.tv_usec / 1000;
487     delta_msec += (phdr->ts.tv_sec - visual->start_time) * 1000;
488     vpkt_hdr.ts_delta = htolel(delta_msec);
489
490     /* Fill in the length fields. */
491     vpkt_hdr.orig_len = htoles(phdr->len);
492     vpkt_hdr.incl_len = htoles(phdr->caplen);
493
494     /* Fill in the encapsulation hint for the file's media type. */
495     switch (wdh->encap)
496     {
497     case WTAP_ENCAP_ETHERNET:   /* Ethernet */
498         vpkt_hdr.encap_hint = 2;    
499         break;
500     case WTAP_ENCAP_TOKEN_RING: /* Token Ring */
501         vpkt_hdr.encap_hint = 3;    
502         break;
503     case WTAP_ENCAP_PPP:        /* PPP */
504     case WTAP_ENCAP_PPP_WITH_PHDR:
505         vpkt_hdr.encap_hint = 14;   
506         break;
507     case WTAP_ENCAP_CHDLC:      /* HDLC Router */
508         vpkt_hdr.encap_hint = 13;   
509         break;
510     case WTAP_ENCAP_FRELAY:     /* Frame Relay Auto-detect */
511         vpkt_hdr.encap_hint = 12;   
512         break;
513     case WTAP_ENCAP_LAPB:       /* Unknown */
514     default:
515         vpkt_hdr.encap_hint = 1;
516         break;
517     }
518
519     /* Set status flags.  The only status currently supported for all
520        encapsulations is direction.  This either goes in the p2p or the
521        X.25 pseudo header.  It would probably be better to move this up
522        into the phdr. */
523     packet_status = 0;
524     switch (wdh->encap)
525     {
526     case WTAP_ENCAP_CHDLC:
527         packet_status |= (pseudo_header->p2p.sent ? PS_SENT : 0x00);
528         break;
529
530     case WTAP_ENCAP_FRELAY:
531     case WTAP_ENCAP_LAPB:
532         packet_status |=
533             ((pseudo_header->x25.flags & FROM_DCE) ? 0x00 : PS_SENT);
534         break;
535     }
536     vpkt_hdr.status = htolel(packet_status);
537
538     /* Write the packet header. */
539     nwritten = fwrite(&vpkt_hdr, 1, hdr_size, wdh->fh);
540     if (nwritten != hdr_size) 
541     {
542         if (nwritten == 0 && ferror(wdh->fh))
543             *err = errno;
544         else
545             *err = WTAP_ERR_SHORT_WRITE;
546         return FALSE;
547     }
548
549     /* Write the packet data */
550     nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
551     if (nwritten != phdr->caplen) 
552     {
553         if (nwritten == 0 && ferror(wdh->fh))
554             *err = errno;
555         else
556             *err = WTAP_ERR_SHORT_WRITE;
557         return FALSE;
558     }
559
560     /* Store the frame offset in the index table. */
561     if (visual->index_table_index >= visual->index_table_size) 
562     {
563         /* End of table reached.  Reallocate with a larger size */
564         visual->index_table_size *= 2;
565         visual->index_table = g_realloc(visual->index_table,
566             visual->index_table_size * sizeof *visual->index_table);
567     }
568     visual->index_table[visual->index_table_index] = htolel(visual->next_offset);
569
570     /* Update the table index and offset for the next frame. */
571     visual->index_table_index++;
572     visual->next_offset += hdr_size + phdr->caplen;
573
574     return TRUE;
575 }
576
577
578 /* Finish writing to a dump file.
579    Returns TRUE on success, FALSE on failure. */
580 static gboolean visual_dump_close(wtap_dumper *wdh, int *err)
581 {
582     struct visual_write_info * visual = wdh->dump.opaque;
583     size_t n_to_write;
584     size_t nwritten;
585     struct visual_file_hdr vfile_hdr;
586     const char *magicp;
587     size_t magic_size;
588
589     /* If the visual structure was never allocated then nothing useful
590        can be done. */
591     if (visual == 0)
592         return FALSE;
593
594     /* Write out the frame table at the end of the file. */
595     if (visual->index_table)
596     {
597         /* Write the index table to the file. */
598         n_to_write = visual->index_table_index * sizeof *visual->index_table;
599         nwritten = fwrite(visual->index_table, 1, n_to_write, wdh->fh);
600         if (nwritten != n_to_write) 
601         {
602             if (err != NULL)
603             {
604                 if (nwritten == 0 && ferror(wdh->fh))
605                     *err = errno;
606                 else
607                     *err = WTAP_ERR_SHORT_WRITE;
608             }
609             visual_dump_free(wdh);
610             return FALSE;
611         }
612     }
613
614     /* Write the magic number at the start of the file. */
615     fseek(wdh->fh, 0, SEEK_SET);
616     magicp = visual_magic;
617     magic_size = sizeof visual_magic;
618     nwritten = fwrite(magicp, 1, magic_size, wdh->fh);
619     if (nwritten != magic_size)
620     {
621         if (err != NULL)
622         {
623             if (nwritten == 0 && ferror(wdh->fh))
624                 *err = errno;
625             else
626                 *err = WTAP_ERR_SHORT_WRITE;
627         }
628         visual_dump_free(wdh);
629         return FALSE;
630     }
631
632     /* Initialize the file header with zeroes for the reserved fields. */
633     memset(&vfile_hdr, '\0', sizeof vfile_hdr);
634     vfile_hdr.num_pkts = htolel(visual->index_table_index);
635     vfile_hdr.start_time = htolel(visual->start_time);
636     vfile_hdr.max_length = htoles(65535);
637     vfile_hdr.file_flags = htoles(1);  /* indexes are present */
638     vfile_hdr.file_version = htoles(1);
639     strcpy(vfile_hdr.description, "Ethereal file");
640
641     /* Translate the encapsulation type */
642     switch (wdh->encap)
643     {
644     case WTAP_ENCAP_ETHERNET:   vfile_hdr.media_type = htoles(6);   break;
645     case WTAP_ENCAP_TOKEN_RING: vfile_hdr.media_type = htoles(9);   break;
646     case WTAP_ENCAP_LAPB:       vfile_hdr.media_type = htoles(16);  break;
647     case WTAP_ENCAP_PPP:        /* PPP is differentiated from CHDLC in PktHdr */
648     case WTAP_ENCAP_PPP_WITH_PHDR:
649     case WTAP_ENCAP_CHDLC:      vfile_hdr.media_type = htoles(22);  break;
650     case WTAP_ENCAP_FRELAY:     vfile_hdr.media_type = htoles(32);  break;
651     }
652
653     /* Write the file header following the magic bytes. */
654     nwritten = fwrite(&vfile_hdr, 1, sizeof vfile_hdr, wdh->fh);
655     if (nwritten != sizeof vfile_hdr) 
656     {
657         if (err != NULL)
658         {
659             if (nwritten == 0 && ferror(wdh->fh))
660                 *err = errno;
661             else
662                 *err = WTAP_ERR_SHORT_WRITE;
663         }
664         visual_dump_free(wdh);
665         return FALSE;
666     }
667
668     /* Deallocate the file write data */
669     visual_dump_free(wdh);
670     return TRUE;
671 }
672
673
674 /* Free the memory allocated by a visual file writer. */
675 static void visual_dump_free(wtap_dumper *wdh)
676 {
677     struct visual_write_info * visual = wdh->dump.opaque;
678
679     if (visual)
680     {
681         /* Free the index table memory. */
682         if (visual->index_table)
683             g_free(visual->index_table);
684
685         /* Free the write file info struct. */
686         g_free(visual);
687         wdh->dump.opaque = 0;
688     }
689 }