Qt/ByteViewTab: document potential pitfall
[metze/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  * Wiretap Library
6  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10
11 #include "config.h"
12 #include <errno.h>
13 #include <string.h>
14 #include "wtap-int.h"
15 #include "file_wrappers.h"
16 #include "visual.h"
17
18 /*
19  * A Visual Networks traffic capture file contains three sections. The
20  * first is a 192 octet file header.  This is followed by the captured
21  * packet header, and for ATM captures, there is an additional atm packet header.
22  * The data follows the packet header. The last section is the packet index block.
23  * The index block contains one 4 octet pointer for each captured packet.
24  * The first packet index is (4 * num_pkts) octets from the end of the file
25  * and the last index is in the last four octets of the file.
26  *
27  * All integer and time values are stored in little-endian format, except for
28  *  the ATM Packet Header, which is stored in network byte order.
29  *
30  * [ File Header ]
31  *
32  *
33  * [ Packet Header 1 ] [(opt) ATM Packet Header] [ Data ]
34  * ...
35  * [ Packet Header n ] [(opt) ATM Packet Header] [ Data ]
36  *
37  *
38  * [ Index Block 1 ] ... [ Index Block n ]
39  */
40
41 /* Capture file header, INCLUDING the magic number, is 192 bytes. */
42 #define CAPTUREFILE_HEADER_SIZE 192
43
44 /* Magic number for Visual Networks traffic capture files. */
45 static const char visual_magic[] = {
46     5, 'V', 'N', 'F'
47 };
48
49
50 /* Visual File Header (minus magic number). */
51 /* This structure is used to extract information */
52 struct visual_file_hdr
53 {
54     guint32 num_pkts;           /* Number of packets in the file */
55     guint32 start_time;         /* Capture start time in PC format */
56     guint16 media_type;         /* IANA ifType of packet source */
57     guint16 max_length;         /* Max allowable stored packet length */
58     guint16 file_flags;         /* File type flags */
59                                 /*   Bit 0 indicates indexes present */
60     guint16 file_version;       /* Version number of this file format */
61     guint32 media_speed;        /* ifSpeed of packet source in bits/sec. */
62     guint16 media_param;        /* Media-specific extra parameter. */
63     char    RESERVED_[102];     /* MUST BE ALL ZEROS FOR FUTURE COMPATABILITY */
64     char    description[64];    /* File description (null terminated) */
65 };
66
67
68 /* Packet status bits */
69 #define PS_LONG             0x01
70 #define PS_SHORT            0x02
71 #define PS_ERRORED          0x04
72 #define PS_1ST_AFTER_DROP   0x08
73 #define PS_APPROX_ORDER     0x10
74 #define PS_SENT             0x40
75 #define PS_ABORTED          0x80
76
77 /* Visual Packet Header */
78 /* This structure is used to extract information */
79 struct visual_pkt_hdr
80 {
81     guint32 ts_delta;           /* Time stamp - msecs since start of capture */
82     guint16 orig_len;           /* Actual length of packet */
83     guint16 incl_len;           /* Number of octets captured in file */
84     guint32 status;             /* Packet status flags (media specific) */
85     guint8  encap_hint;         /* Encapsulation type hint */
86     guint8  encap_skip;         /* Number of bytes to skip before decoding */
87     char    RESERVED_[6];       /* RESERVED - must be zero */
88 };
89
90 /* Optional Visual ATM Packet Header */
91 /* This structure is used to extract information */
92 struct visual_atm_hdr
93 {
94    guint16 vpi;           /* 4 bits of zeros; 12 bits of ATM VPI */
95    guint16 vci;           /* ATM VCI */
96    guint8  info;          /* 4 bits version; 3 bits unused-zero; 1 bit direction */
97    guint8  category;      /* indicates type of traffic. 4 bits of status + 4 bits of type */
98    guint16 cell_count;    /* number of cells that make up this pdu */
99    guint32 data_length;   /* PDU data length for AAL-5 PDUs, all others - cellcount * 48 */
100    guint32 ts_secs;       /* seonds value of sysUpTime when the last cell of this PDU was captured */
101    guint32 ts_nsec;       /* nanoseonds value of sysUpTime when the last cell of this PDU was captured */
102
103 };
104
105 /* visual_atm_hdr info bit definitions */
106 #define FROM_NETWORK       0x01
107 #define ATM_VER_MASK       0xf0  /* Not currently displayed */
108
109 /* visual_atm_hdr category definitions */
110 /* High nibble - not currently displayed */
111 #define VN_INCOMPLETE      0x40
112 #define VN_BAD_CRC         0x80
113 #define VN_CAT_STAT_MASK   0xf0
114 /* Low nibble */
115 #define VN_UNKNOWN         0x00
116 #define VN_AAL1            0x01
117 #define VN_AAL2            0x02
118 #define VN_AAL34           0x03
119 #define VN_O191            0x04
120 #define VN_AAL5            0x05
121 #define VN_OAM             0x0a
122 #define VN_RM              0x0b
123 #define VN_IDLE            0x0c
124 #define VN_CAT_TYPE_MASK   0x0f
125
126
127 /* Additional information for reading Visual files */
128 struct visual_read_info
129 {
130     guint32 num_pkts;           /* Number of pkts in the file */
131     guint32 current_pkt;        /* Next packet to be read */
132     time_t  start_time;         /* Capture start time in seconds */
133 };
134
135
136 /* Additional information for writing Visual files */
137 struct visual_write_info
138 {
139     time_t  start_time;         /* Capture start time in seconds */
140     int     index_table_index;  /* Index of the next index entry */
141     int     index_table_size;   /* Allocated size of the index table */
142     guint32 * index_table;      /* File offsets for the packets */
143     guint32 next_offset;        /* Offset of next packet */
144 };
145
146
147 /* Local functions to handle file reads and writes */
148 static gboolean visual_read(wtap *wth, int *err, gchar **err_info,
149     gint64 *data_offset);
150 static gboolean visual_seek_read(wtap *wth, gint64 seek_off,
151     wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
152 static gboolean visual_read_packet(wtap *wth, FILE_T fh,
153     wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
154 static gboolean visual_dump(wtap_dumper *wdh, const wtap_rec *rec,
155     const guint8 *pd, int *err, gchar **err_info);
156 static gboolean visual_dump_finish(wtap_dumper *wdh, int *err);
157 static void visual_dump_free(wtap_dumper *wdh);
158
159
160 /* Open a file for reading */
161 wtap_open_return_val visual_open(wtap *wth, int *err, gchar **err_info)
162 {
163     char magic[sizeof visual_magic];
164     struct visual_file_hdr vfile_hdr;
165     struct visual_read_info * visual;
166     int encap;
167
168     /* Check the magic string at the start of the file */
169     if (!wtap_read_bytes(wth->fh, magic, sizeof magic, err, err_info))
170     {
171         if (*err != WTAP_ERR_SHORT_READ)
172             return WTAP_OPEN_ERROR;
173         return WTAP_OPEN_NOT_MINE;
174     }
175     if (memcmp(magic, visual_magic, sizeof visual_magic) != 0)
176     {
177         return WTAP_OPEN_NOT_MINE;
178     }
179
180     /* Read the rest of the file header. */
181     if (!wtap_read_bytes(wth->fh, &vfile_hdr, sizeof vfile_hdr, err, err_info))
182     {
183         return WTAP_OPEN_ERROR;
184     }
185
186     /* Verify the file version is known */
187     vfile_hdr.file_version = pletoh16(&vfile_hdr.file_version);
188     if (vfile_hdr.file_version != 1)
189     {
190         *err = WTAP_ERR_UNSUPPORTED;
191         *err_info = g_strdup_printf("visual: file version %u unsupported", vfile_hdr.file_version);
192         return WTAP_OPEN_ERROR;
193     }
194
195     /* Translate the encapsulation type; these values are SNMP ifType
196        values, as found in http://www.iana.org/assignments/smi-numbers.
197
198        Note that a file with media type 22 ("propPointToPointSerial") may
199        contain Cisco HDLC or PPP over HDLC.  This will get sorted out after
200        the first packet is read.
201
202        XXX - should we use WTAP_ENCAP_PER_PACKET for that? */
203     switch (pletoh16(&vfile_hdr.media_type))
204     {
205     case  6:    /* ethernet-csmacd */
206         encap = WTAP_ENCAP_ETHERNET;
207         break;
208
209     case  9:    /* IEEE802.5 */
210         encap = WTAP_ENCAP_TOKEN_RING;
211         break;
212
213     case 16:    /* lapb */
214         encap = WTAP_ENCAP_LAPB;
215         break;
216
217     case 22:    /* propPointToPointSerial */
218     case 118:   /* HDLC */
219         encap = WTAP_ENCAP_CHDLC_WITH_PHDR;
220         break;
221
222     case 32:    /* frame-relay */
223         encap = WTAP_ENCAP_FRELAY_WITH_PHDR;
224         break;
225
226     case 37:    /* ATM */
227        encap = WTAP_ENCAP_ATM_PDUS;
228        break;
229
230     default:
231         *err = WTAP_ERR_UNSUPPORTED;
232         *err_info = g_strdup_printf("visual: network type %u unknown or unsupported",
233                                      vfile_hdr.media_type);
234         return WTAP_OPEN_ERROR;
235     }
236
237     /* Fill in the wiretap struct with data from the file header */
238     wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_VISUAL_NETWORKS;
239     wth->file_encap = encap;
240     wth->snapshot_length = pletoh16(&vfile_hdr.max_length);
241
242     /* Set up the pointers to the handlers for this file type */
243     wth->subtype_read = visual_read;
244     wth->subtype_seek_read = visual_seek_read;
245     wth->file_tsprec = WTAP_TSPREC_MSEC;
246
247     /* Add Visual-specific information to the wiretap struct for later use. */
248     visual = (struct visual_read_info *)g_malloc(sizeof(struct visual_read_info));
249     wth->priv = (void *)visual;
250     visual->num_pkts = pletoh32(&vfile_hdr.num_pkts);
251     visual->start_time = pletoh32(&vfile_hdr.start_time);
252     visual->current_pkt = 1;
253
254     return WTAP_OPEN_MINE;
255 }
256
257
258 /* Read the next available packet from the file.  This is called
259    in a loop to sequentially read the entire file one time.  After
260    the file has been read once, any Future access to the packets is
261    done through seek_read. */
262 static gboolean visual_read(wtap *wth, int *err, gchar **err_info,
263     gint64 *data_offset)
264 {
265     struct visual_read_info *visual = (struct visual_read_info *)wth->priv;
266
267     /* Check for the end of the packet data.  Note that a check for file EOF
268        will not work because there are index values stored after the last
269        packet's data. */
270     if (visual->current_pkt > visual->num_pkts)
271     {
272         *err = 0;   /* it's just an EOF, not an error */
273         return FALSE;
274     }
275     visual->current_pkt++;
276
277     *data_offset = file_tell(wth->fh);
278
279     return visual_read_packet(wth, wth->fh, &wth->rec, wth->rec_data,
280             err, err_info);
281 }
282
283 /* Read packet header and data for random access. */
284 static gboolean visual_seek_read(wtap *wth, gint64 seek_off,
285     wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
286 {
287     /* Seek to the packet header */
288     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
289         return FALSE;
290
291     /* Read the packet. */
292     if (!visual_read_packet(wth, wth->random_fh, rec, buf, err, err_info)) {
293         if (*err == 0)
294             *err = WTAP_ERR_SHORT_READ;
295         return FALSE;
296     }
297     return TRUE;
298 }
299
300 static gboolean
301 visual_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
302         Buffer *buf, int *err, gchar **err_info)
303 {
304     struct visual_read_info *visual = (struct visual_read_info *)wth->priv;
305     struct visual_pkt_hdr vpkt_hdr;
306     guint32 packet_size;
307     struct visual_atm_hdr vatm_hdr;
308     guint32 relmsecs;
309     guint32 packet_status;
310     guint8 *pd;
311
312     /* Read the packet header. */
313     if (!wtap_read_bytes_or_eof(fh, &vpkt_hdr, (unsigned int)sizeof vpkt_hdr, err, err_info))
314     {
315         return FALSE;
316     }
317
318     /* Get the included length of data. This includes extra headers + payload */
319     packet_size = pletoh16(&vpkt_hdr.incl_len);
320
321     rec->rec_type = REC_TYPE_PACKET;
322     rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
323
324     /* Set the packet time and length. */
325     relmsecs = pletoh32(&vpkt_hdr.ts_delta);
326     rec->ts.secs = visual->start_time + relmsecs/1000;
327     rec->ts.nsecs = (relmsecs % 1000)*1000000;
328
329     rec->rec_header.packet_header.len = pletoh16(&vpkt_hdr.orig_len);
330
331     packet_status = pletoh32(&vpkt_hdr.status);
332
333     /* Do encapsulation-specific processing.
334
335        Most Visual capture types include the FCS in the original length
336        value, but don't include the FCS as part of the payload or captured
337        length.  This is different from the model used in most other capture
338        file formats, including pcap and pcapng in cases where the FCS isn't
339        captured (which are the typical cases), and causes the RTP audio
340        payload save to fail since then captured len != orig len.
341
342        We adjust the original length to remove the FCS bytes we counted based
343        on the file encapsualtion type.  The only downside to this fix is
344        throughput calculations will be slightly lower as it won't include
345        the FCS bytes.  However, as noted, that problem also exists with
346        other capture formats.
347
348        We also set status flags.  The only status currently supported for
349        all encapsulations is direction.  This either goes in the p2p or the
350        X.25 pseudo header.  It would probably be better to move this up
351        into the phdr. */
352     switch (wth->file_encap)
353     {
354     case WTAP_ENCAP_ETHERNET:
355         /* Ethernet has a 4-byte FCS. */
356         if (rec->rec_header.packet_header.len < 4)
357         {
358             *err = WTAP_ERR_BAD_FILE;
359             *err_info = g_strdup_printf("visual: Ethernet packet has %u-byte original packet, less than the FCS length",
360                         rec->rec_header.packet_header.len);
361             return FALSE;
362         }
363         rec->rec_header.packet_header.len -= 4;
364
365         /* XXX - the above implies that there's never an FCS; should this
366            set the FCS length to 0? */
367         rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1;
368         break;
369
370     case WTAP_ENCAP_CHDLC_WITH_PHDR:
371         /* This has a 2-byte FCS. */
372         if (rec->rec_header.packet_header.len < 2)
373         {
374             *err = WTAP_ERR_BAD_FILE;
375             *err_info = g_strdup_printf("visual: Cisco HDLC packet has %u-byte original packet, less than the FCS length",
376                         rec->rec_header.packet_header.len);
377             return FALSE;
378         }
379         rec->rec_header.packet_header.len -= 2;
380
381         rec->rec_header.packet_header.pseudo_header.p2p.sent = (packet_status & PS_SENT) ? TRUE : FALSE;
382         break;
383
384     case WTAP_ENCAP_PPP_WITH_PHDR:
385         /* No FCS.
386            XXX - true?  Note that PPP can negotiate no FCS, a 2-byte FCS,
387            or a 4-byte FCS. */
388         rec->rec_header.packet_header.pseudo_header.p2p.sent = (packet_status & PS_SENT) ? TRUE : FALSE;
389         break;
390
391     case WTAP_ENCAP_FRELAY_WITH_PHDR:
392         /* This has a 2-byte FCS. */
393         if (rec->rec_header.packet_header.len < 2)
394         {
395             *err = WTAP_ERR_BAD_FILE;
396             *err_info = g_strdup_printf("visual: Frame Relay packet has %u-byte original packet, less than the FCS length",
397                         rec->rec_header.packet_header.len);
398             return FALSE;
399         }
400         rec->rec_header.packet_header.len -= 2;
401
402         rec->rec_header.packet_header.pseudo_header.dte_dce.flags =
403             (packet_status & PS_SENT) ? 0x00 : FROM_DCE;
404         break;
405
406     case WTAP_ENCAP_LAPB:
407         /* This has a 2-byte FCS. */
408         if (rec->rec_header.packet_header.len < 2)
409         {
410             *err = WTAP_ERR_BAD_FILE;
411             *err_info = g_strdup_printf("visual: Frame Relay packet has %u-byte original packet, less than the FCS length",
412                         rec->rec_header.packet_header.len);
413             return FALSE;
414         }
415         rec->rec_header.packet_header.len -= 2;
416
417         rec->rec_header.packet_header.pseudo_header.dte_dce.flags =
418             (packet_status & PS_SENT) ? 0x00 : FROM_DCE;
419         break;
420
421     case WTAP_ENCAP_ATM_PDUS:
422         /* ATM original length doesn't include any FCS. Do nothing to
423            the packet length.
424
425            ATM packets have an additional packet header; read and
426            process it. */
427         if (!wtap_read_bytes(fh, &vatm_hdr, (unsigned int)sizeof vatm_hdr, err, err_info))
428         {
429             return FALSE;
430         }
431
432         /* Remove ATM header from length of included bytes in capture, as
433            this header was appended by the processor doing the packet
434            reassembly, and was not transmitted across the wire */
435         packet_size -= (guint32)sizeof vatm_hdr;
436
437         /* Set defaults */
438         rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_UNKNOWN;
439         rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
440         rec->rec_header.packet_header.pseudo_header.atm.aal5t_len = 0;
441
442         /* Next two items not supported. Defaulting to zero */
443         rec->rec_header.packet_header.pseudo_header.atm.aal5t_u2u = 0;
444         rec->rec_header.packet_header.pseudo_header.atm.aal5t_chksum = 0;
445
446         /* Flags appear only to convey that packet is a raw cell. Set to 0 */
447         rec->rec_header.packet_header.pseudo_header.atm.flags = 0;
448
449         /* Not supported. Defaulting to zero */
450         rec->rec_header.packet_header.pseudo_header.atm.aal2_cid = 0;
451
452         switch(vatm_hdr.category & VN_CAT_TYPE_MASK )
453         {
454         case VN_AAL1:
455             rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_1;
456             break;
457
458         case VN_AAL2:
459             rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_2;
460             break;
461
462         case VN_AAL34:
463             rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_3_4;
464             break;
465
466         case VN_AAL5:
467             rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_5;
468             rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_LLCMX;
469             rec->rec_header.packet_header.pseudo_header.atm.aal5t_len = pntoh32(&vatm_hdr.data_length);
470             break;
471
472         case VN_OAM:
473         /* Marking next 3 as OAM versus unknown */
474         case VN_O191:
475         case VN_IDLE:
476         case VN_RM:
477             rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_OAMCELL;
478             break;
479
480         case VN_UNKNOWN:
481         default:
482             rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_UNKNOWN;
483             break;
484         }
485         rec->rec_header.packet_header.pseudo_header.atm.vpi = pntoh16(&vatm_hdr.vpi) & 0x0FFF;
486         rec->rec_header.packet_header.pseudo_header.atm.vci = pntoh16(&vatm_hdr.vci);
487         rec->rec_header.packet_header.pseudo_header.atm.cells = pntoh16(&vatm_hdr.cell_count);
488
489         /* Using bit value of 1 (DCE -> DTE) to indicate From Network */
490         rec->rec_header.packet_header.pseudo_header.atm.channel = vatm_hdr.info & FROM_NETWORK;
491         break;
492
493     /* Not sure about token ring. Just leaving alone for now. */
494     case WTAP_ENCAP_TOKEN_RING:
495     default:
496         break;
497     }
498
499     rec->rec_header.packet_header.caplen = packet_size;
500
501     /* Check for too-large packet. */
502     if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD)
503     {
504         /* Probably a corrupt capture file; don't blow up trying
505           to allocate space for an immensely-large packet. */
506         *err = WTAP_ERR_BAD_FILE;
507         *err_info = g_strdup_printf("visual: File has %u-byte packet, bigger than maximum of %u",
508             packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
509         return FALSE;
510     }
511
512     /* Read the packet data */
513     if (!wtap_read_packet_bytes(fh, buf, packet_size, err, err_info))
514         return FALSE;
515
516     if (wth->file_encap == WTAP_ENCAP_CHDLC_WITH_PHDR)
517     {
518         /* Fill in the encapsulation.  Visual files have a media type in the
519            file header and an encapsulation type in each packet header.  Files
520            with a media type of HDLC can be either Cisco EtherType or PPP.
521
522            The encapsulation hint values we've seen are:
523
524              2 - seen in an Ethernet capture
525              13 - seen in a PPP capture; possibly also seen in Cisco HDLC
526                   captures
527              14 - seen in a PPP capture; probably seen only for PPP.
528
529            According to bug 2005, the collection probe can be configured
530            for PPP, in which case the encapsulation hint is 14, or can
531            be configured for auto-detect, in which case the encapsulation
532            hint is 13, and the encapsulation must be guessed from the
533            packet contents.  Auto-detect is the default. */
534         pd = ws_buffer_start_ptr(buf);
535
536         /* If PPP is specified in the encap hint, then use that */
537         if (vpkt_hdr.encap_hint == 14)
538         {
539             /* But first we need to examine the first three octets to
540                try to determine the proper encapsulation, see RFC 2364. */
541             if (packet_size >= 3 &&
542                 (0xfe == pd[0]) && (0xfe == pd[1]) && (0x03 == pd[2]))
543             {
544                 /* It is actually LLC encapsulated PPP */
545                 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ATM_RFC1483;
546             }
547             else
548             {
549                 /* It is actually PPP */
550                 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_PPP_WITH_PHDR;
551             }
552         }
553         else
554         {
555             /* Otherwise, we need to examine the first two octets to
556                try to determine the encapsulation. */
557             if (packet_size >= 2 && (0xff == pd[0]) && (0x03 == pd[1]))
558             {
559                 /* It is actually PPP */
560                 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_PPP_WITH_PHDR;
561             }
562         }
563     }
564
565     return TRUE;
566 }
567
568 /* Check for media types that may be written in Visual file format.
569    Returns 0 if the specified encapsulation type is supported,
570    an error indication otherwise. */
571 int visual_dump_can_write_encap(int encap)
572 {
573     /* Per-packet encapsulations aren't supported. */
574     if (encap == WTAP_ENCAP_PER_PACKET)
575         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
576
577     /* Check for supported encapsulation types */
578     switch (encap)
579     {
580     case WTAP_ENCAP_ETHERNET:
581     case WTAP_ENCAP_TOKEN_RING:
582     case WTAP_ENCAP_LAPB:
583     case WTAP_ENCAP_CHDLC_WITH_PHDR:
584     case WTAP_ENCAP_FRELAY_WITH_PHDR:
585     case WTAP_ENCAP_PPP:
586     case WTAP_ENCAP_PPP_WITH_PHDR:
587         return 0;
588     }
589
590     return WTAP_ERR_UNWRITABLE_ENCAP;
591 }
592
593
594 /* Open a file for writing.
595    Returns TRUE on success, FALSE on failure; sets "*err" to an
596    error code on failure */
597 gboolean visual_dump_open(wtap_dumper *wdh, int *err)
598 {
599     struct visual_write_info *visual;
600
601     /* Set the write routines for a visual file. */
602     wdh->subtype_write = visual_dump;
603     wdh->subtype_finish = visual_dump_finish;
604
605     /* Create a struct to hold file information for the duration
606        of the write */
607     visual = (struct visual_write_info *)g_malloc(sizeof(struct visual_write_info));
608     wdh->priv = (void *)visual;
609     visual->index_table_index = 0;
610     visual->index_table_size = 1024;
611     visual->index_table = 0;
612     visual->next_offset = CAPTUREFILE_HEADER_SIZE;
613
614     /* All of the fields in the file header aren't known yet so
615        just skip over it for now.  It will be created after all
616        of the packets have been written. */
617     if (wtap_dump_file_seek(wdh, CAPTUREFILE_HEADER_SIZE, SEEK_SET, err) == -1)
618         return FALSE;
619
620     return TRUE;
621 }
622
623
624 /* Write a packet to a Visual dump file.
625    Returns TRUE on success, FALSE on failure. */
626 static gboolean visual_dump(wtap_dumper *wdh, const wtap_rec *rec,
627     const guint8 *pd, int *err, gchar **err_info _U_)
628 {
629     const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
630     struct visual_write_info * visual = (struct visual_write_info *)wdh->priv;
631     struct visual_pkt_hdr vpkt_hdr;
632     size_t hdr_size = sizeof vpkt_hdr;
633     guint delta_msec;
634     guint32 packet_status;
635
636     /* We can only write packet records. */
637     if (rec->rec_type != REC_TYPE_PACKET) {
638         *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
639         return FALSE;
640     }
641
642     /* Don't write anything we're not willing to read. */
643     if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
644         *err = WTAP_ERR_PACKET_TOO_LARGE;
645         return FALSE;
646     }
647
648     /* If the visual structure was never allocated then nothing useful
649        can be done. */
650     if (visual == 0)
651         return FALSE;
652
653     /* Zero out unused and reserved fields in the packet header. */
654     memset(&vpkt_hdr, 0, hdr_size);
655
656     /* Visual UpTime capture files have a capture start time in the
657        file header.  Each packet has a capture time (in msec) relative
658        to the file start time.  Use the time of the first packet as the
659        file start time. */
660     if (visual->index_table_index == 0)
661     {
662         /* This is the first packet.  Save its start time as the file time. */
663         visual->start_time = rec->ts.secs;
664
665         /* Initialize the index table */
666         visual->index_table = (guint32 *)g_malloc(1024 * sizeof *visual->index_table);
667         visual->index_table_size = 1024;
668     }
669
670     /* Calculate milliseconds since capture start. */
671     delta_msec = rec->ts.nsecs / 1000000;
672     delta_msec += (guint32)((rec->ts.secs - visual->start_time) * 1000);
673     vpkt_hdr.ts_delta = GUINT32_TO_LE(delta_msec);
674
675     /* Fill in the length fields. */
676     vpkt_hdr.orig_len = GUINT16_TO_LE(rec->rec_header.packet_header.len);
677     vpkt_hdr.incl_len = GUINT16_TO_LE(rec->rec_header.packet_header.caplen);
678
679     /* Fill in the encapsulation hint for the file's media type. */
680     switch (wdh->encap)
681     {
682     case WTAP_ENCAP_ETHERNET:   /* Ethernet */
683         vpkt_hdr.encap_hint = 2;
684         break;
685     case WTAP_ENCAP_TOKEN_RING: /* Token Ring */
686         vpkt_hdr.encap_hint = 3;
687         break;
688     case WTAP_ENCAP_PPP:        /* PPP */
689     case WTAP_ENCAP_PPP_WITH_PHDR:
690         vpkt_hdr.encap_hint = 14;
691         break;
692     case WTAP_ENCAP_CHDLC_WITH_PHDR:      /* HDLC Router */
693         vpkt_hdr.encap_hint = 13;
694         break;
695     case WTAP_ENCAP_FRELAY_WITH_PHDR:     /* Frame Relay Auto-detect */
696         vpkt_hdr.encap_hint = 12;
697         break;
698     case WTAP_ENCAP_LAPB:       /* Unknown */
699     default:
700         vpkt_hdr.encap_hint = 1;
701         break;
702     }
703
704     /* Set status flags.  The only status currently supported for all
705        encapsulations is direction.  This either goes in the p2p or the
706        X.25 pseudo header.  It would probably be better to move this up
707        into the phdr. */
708     packet_status = 0;
709     switch (wdh->encap)
710     {
711     case WTAP_ENCAP_CHDLC_WITH_PHDR:
712         packet_status |= (pseudo_header->p2p.sent ? PS_SENT : 0x00);
713         break;
714
715     case WTAP_ENCAP_FRELAY_WITH_PHDR:
716     case WTAP_ENCAP_LAPB:
717         packet_status |=
718             ((pseudo_header->dte_dce.flags & FROM_DCE) ? 0x00 : PS_SENT);
719         break;
720     }
721     vpkt_hdr.status = GUINT32_TO_LE(packet_status);
722
723     /* Write the packet header. */
724     if (!wtap_dump_file_write(wdh, &vpkt_hdr, hdr_size, err))
725         return FALSE;
726
727     /* Write the packet data */
728     if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
729         return FALSE;
730
731     /* Store the frame offset in the index table. */
732     if (visual->index_table_index >= visual->index_table_size)
733     {
734         /* End of table reached.  Reallocate with a larger size */
735         visual->index_table_size *= 2;
736         visual->index_table = (guint32 *)g_realloc(visual->index_table,
737             visual->index_table_size * sizeof *visual->index_table);
738     }
739     visual->index_table[visual->index_table_index] = GUINT32_TO_LE(visual->next_offset);
740
741     /* Update the table index and offset for the next frame. */
742     visual->index_table_index++;
743     visual->next_offset += (guint32) hdr_size + rec->rec_header.packet_header.caplen;
744
745     return TRUE;
746 }
747
748
749 /* Finish writing to a dump file.
750    Returns TRUE on success, FALSE on failure. */
751 static gboolean visual_dump_finish(wtap_dumper *wdh, int *err)
752 {
753     struct visual_write_info * visual = (struct visual_write_info *)wdh->priv;
754     size_t n_to_write;
755     struct visual_file_hdr vfile_hdr;
756     const char *magicp;
757     size_t magic_size;
758
759     /* If the visual structure was never allocated then nothing useful
760        can be done. */
761     if (visual == 0)
762         return FALSE;
763
764     /* Write out the frame table at the end of the file. */
765     if (visual->index_table)
766     {
767         /* Write the index table to the file. */
768         n_to_write = visual->index_table_index * sizeof *visual->index_table;
769         if (!wtap_dump_file_write(wdh, visual->index_table, n_to_write, err))
770         {
771             visual_dump_free(wdh);
772             return FALSE;
773         }
774     }
775
776     /* Write the magic number at the start of the file. */
777     if (wtap_dump_file_seek(wdh, 0, SEEK_SET, err) == -1)
778         return FALSE;
779     magicp = visual_magic;
780     magic_size = sizeof visual_magic;
781     if (!wtap_dump_file_write(wdh, magicp, magic_size, err))
782     {
783         visual_dump_free(wdh);
784         return FALSE;
785     }
786
787     /* Initialize the file header with zeroes for the reserved fields. */
788     memset(&vfile_hdr, '\0', sizeof vfile_hdr);
789     vfile_hdr.num_pkts = GUINT32_TO_LE(visual->index_table_index);
790     vfile_hdr.start_time = GUINT32_TO_LE(visual->start_time);
791     vfile_hdr.max_length = GUINT16_TO_LE(65535);
792     vfile_hdr.file_flags = GUINT16_TO_LE(1);  /* indexes are present */
793     vfile_hdr.file_version = GUINT16_TO_LE(1);
794     g_strlcpy(vfile_hdr.description, "Wireshark file", 64);
795
796     /* Translate the encapsulation type */
797     switch (wdh->encap)
798     {
799     case WTAP_ENCAP_ETHERNET:
800         vfile_hdr.media_type = GUINT16_TO_LE(6);
801         break;
802
803     case WTAP_ENCAP_TOKEN_RING:
804         vfile_hdr.media_type = GUINT16_TO_LE(9);
805         break;
806
807     case WTAP_ENCAP_LAPB:
808         vfile_hdr.media_type = GUINT16_TO_LE(16);
809         break;
810
811     case WTAP_ENCAP_PPP:        /* PPP is differentiated from CHDLC in PktHdr */
812     case WTAP_ENCAP_PPP_WITH_PHDR:
813     case WTAP_ENCAP_CHDLC_WITH_PHDR:
814         vfile_hdr.media_type = GUINT16_TO_LE(22);
815         break;
816
817     case WTAP_ENCAP_FRELAY_WITH_PHDR:
818         vfile_hdr.media_type = GUINT16_TO_LE(32);
819         break;
820     }
821
822     /* Write the file header following the magic bytes. */
823     if (!wtap_dump_file_write(wdh, &vfile_hdr, sizeof vfile_hdr, err))
824     {
825         visual_dump_free(wdh);
826         return FALSE;
827     }
828
829     /* Deallocate the file write data */
830     visual_dump_free(wdh);
831     return TRUE;
832 }
833
834
835 /* Free the memory allocated by a visual file writer. */
836 static void visual_dump_free(wtap_dumper *wdh)
837 {
838     struct visual_write_info * visual = (struct visual_write_info *)wdh->priv;
839
840     if (visual)
841     {
842         /* Free the index table memory. */
843         g_free(visual->index_table);
844     }
845 }
846
847 /*
848  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
849  *
850  * Local variables:
851  * c-basic-offset: 4
852  * tab-width: 8
853  * indent-tabs-mode: nil
854  * End:
855  *
856  * vi: set shiftwidth=4 tabstop=8 expandtab:
857  * :indentSize=4:tabSize=8:noTabs=true:
858  */