The DBS Etherwatch file handler does look for a magic number (the word
[obnox/wireshark/wip.git] / wiretap / file_access.c
1 /* file_access.c
2  *
3  * $Id$
4  *
5  * Wiretap Library
6  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #ifdef HAVE_FCNTL_H
32 #include <fcntl.h>
33 #endif
34
35 #ifdef HAVE_SYS_STAT_H
36 #include <sys/stat.h>
37 #endif
38
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42
43 #include <errno.h>
44
45 #ifdef HAVE_IO_H
46 #include <io.h> /* open/close on win32 */
47 #endif
48
49 #include "wtap-int.h"
50 #include "file_wrappers.h"
51 #include "buffer.h"
52 #include "lanalyzer.h"
53 #include "airopeek9.h"
54 #include "ngsniffer.h"
55 #include "radcom.h"
56 #include "ascend.h"
57 #include "nettl.h"
58 #include "libpcap.h"
59 #include "snoop.h"
60 #include "iptrace.h"
61 #include "netmon.h"
62 #include "netxray.h"
63 #include "toshiba.h"
64 #include "eyesdn.h"
65 #include "i4btrace.h"
66 #include "csids.h"
67 #include "pppdump.h"
68 #include "etherpeek.h"
69 #include "vms.h"
70 #include "dbs-etherwatch.h"
71 #include "visual.h"
72 #include "cosine.h"
73 #include "5views.h"
74 #include "erf.h"
75 #include "hcidump.h"
76 #include "network_instruments.h"
77
78 /* The open_file_* routines should return:
79  *
80  *      -1 on an I/O error;
81  *
82  *      1 if the file they're reading is one of the types it handles;
83  *
84  *      0 if the file they're reading isn't the type they're checking for.
85  *
86  * If the routine handles this type of file, it should set the "file_type"
87  * field in the "struct wtap" to the type of the file.
88  *
89  * Put the trace files that are merely saved telnet-sessions last, since it's
90  * possible that you could have captured someone a router telnet-session
91  * using another tool. So, a libpcap trace of an toshiba "snoop" session
92  * should be discovered as a libpcap file, not a toshiba file.
93  */
94
95 static int (*const open_routines[])(wtap *, int *, char **) = {
96         /* Files that have magic bytes in fixed locations. These
97          * are easy to identify.
98          */
99         libpcap_open,
100         lanalyzer_open,
101         ngsniffer_open,
102         snoop_open,
103         iptrace_open,
104         netmon_open,
105         netxray_open,
106         radcom_open,
107         nettl_open,
108         visual_open,
109         _5views_open,
110         network_instruments_open,
111         airopeek9_open,
112         dbs_etherwatch_open,
113
114         /* Files that don't have magic bytes at a fixed location,
115          * but that instead require a heuristic of some sort to
116          * identify them.  This includes the ASCII trace files that
117          * would be, for example, saved copies of a Telnet session
118          * to some box.
119          */
120         etherpeek_open,
121         pppdump_open,
122         ascend_open,
123         eyesdn_open,
124         toshiba_open,
125         i4btrace_open,
126         csids_open,
127         vms_open,
128         cosine_open,
129         erf_open,
130         hcidump_open,
131 };
132
133 #define N_FILE_TYPES    (sizeof open_routines / sizeof open_routines[0])
134
135 /*
136  * Visual C++ on Win32 systems doesn't define these.  (Old UNIX systems don't
137  * define them either.)
138  *
139  * Visual C++ on Win32 systems doesn't define S_IFIFO, it defines _S_IFIFO.
140  */
141 #ifndef S_ISREG
142 #define S_ISREG(mode)   (((mode) & S_IFMT) == S_IFREG)
143 #endif
144 #ifndef S_IFIFO
145 #define S_IFIFO _S_IFIFO
146 #endif
147 #ifndef S_ISFIFO
148 #define S_ISFIFO(mode)  (((mode) & S_IFMT) == S_IFIFO)
149 #endif
150 #ifndef S_ISDIR
151 #define S_ISDIR(mode)   (((mode) & S_IFMT) == S_IFDIR)
152 #endif
153
154 /* Opens a file and prepares a wtap struct.
155    If "do_random" is TRUE, it opens the file twice; the second open
156    allows the application to do random-access I/O without moving
157    the seek offset for sequential I/O, which is used by Ethereal
158    so that it can do sequential I/O to a capture file that's being
159    written to as new packets arrive independently of random I/O done
160    to display protocol trees for packets when they're selected. */
161 wtap* wtap_open_offline(const char *filename, int *err, char **err_info,
162     gboolean do_random)
163 {
164         struct stat statb;
165         wtap    *wth;
166         unsigned int    i;
167         gboolean use_stdin = FALSE;
168
169         /* open standard input if filename is '-' */
170         if (strcmp(filename, "-") == 0)
171                 use_stdin = TRUE;
172
173         /* First, make sure the file is valid */
174         if (use_stdin) {
175                 if (fstat(0, &statb) < 0) {
176                         *err = errno;
177                         return NULL;
178                 }
179         } else {
180                 if (stat(filename, &statb) < 0) {
181                         *err = errno;
182                         return NULL;
183                 }
184         }
185         if (S_ISFIFO(statb.st_mode)) {
186                 /*
187                  * Opens of FIFOs are allowed only when not opening
188                  * for random access.
189                  *
190                  * XXX - currently, we do seeking when trying to find
191                  * out the file type, so we don't actually support
192                  * opening FIFOs.  However, we may eventually
193                  * do buffering that allows us to do at least some
194                  * file type determination even on pipes, so we
195                  * allow FIFO opens and let things fail later when
196                  * we try to seek.
197                  */
198                 if (do_random) {
199                         *err = WTAP_ERR_RANDOM_OPEN_PIPE;
200                         return NULL;
201                 }
202         } else if (S_ISDIR(statb.st_mode)) {
203                 /*
204                  * Return different errors for "this is a directory"
205                  * and "this is some random special file type", so
206                  * the user can get a potentially more helpful error.
207                  */
208                 *err = EISDIR;
209                 return NULL;
210         } else if (! S_ISREG(statb.st_mode)) {
211                 *err = WTAP_ERR_NOT_REGULAR_FILE;
212                 return NULL;
213         }
214
215         /*
216          * We need two independent descriptors for random access, so
217          * they have different file positions.  If we're opening the
218          * standard input, we can only dup it to get additional
219          * descriptors, so we can't have two independent descriptors,
220          * and thus can't do random access.
221          */
222         if (use_stdin && do_random) {
223                 *err = WTAP_ERR_RANDOM_OPEN_STDIN;
224                 return NULL;
225         }
226
227         errno = ENOMEM;
228         wth = g_malloc(sizeof(wtap));
229         if (wth == NULL) {
230                 *err = errno;
231                 return NULL;
232         }
233
234 /* Win32 needs the O_BINARY flag for open() */
235 #ifndef O_BINARY
236 #define O_BINARY        0
237 #endif
238
239         /* Open the file */
240         errno = WTAP_ERR_CANT_OPEN;
241         if (use_stdin) {
242                 /*
243                  * We dup FD 0, so that we don't have to worry about
244                  * an fclose or gzclose of wth->fh closing the standard
245                  * input of the process.
246                  */
247                 wth->fd = dup(0);
248 #ifdef _WIN32
249                 _setmode(wth->fd, O_BINARY);
250 #endif
251         } else
252                 wth->fd = open(filename, O_RDONLY|O_BINARY);
253         if (wth->fd < 0) {
254                 *err = errno;
255                 g_free(wth);
256                 return NULL;
257         }
258         if (!(wth->fh = filed_open(wth->fd, "rb"))) {
259                 *err = errno;
260                 close(wth->fd);
261                 g_free(wth);
262                 return NULL;
263         }
264
265         if (do_random) {
266                 if (!(wth->random_fh = file_open(filename, "rb"))) {
267                         *err = errno;
268                         file_close(wth->fh);
269                         g_free(wth);
270                         return NULL;
271                 }
272         } else
273                 wth->random_fh = NULL;
274
275         /* initialization */
276         wth->file_encap = WTAP_ENCAP_UNKNOWN;
277         wth->data_offset = 0;
278         wth->subtype_sequential_close = NULL;
279         wth->subtype_close = NULL;
280
281         /* Try all file types */
282         for (i = 0; i < N_FILE_TYPES; i++) {
283                 /* Seek back to the beginning of the file; the open routine
284                    for the previous file type may have left the file
285                    position somewhere other than the beginning, and the
286                    open routine for this file type will probably want
287                    to start reading at the beginning.
288
289                    Initialize the data offset while we're at it. */
290                 if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
291                         /* I/O error - give up */
292                         if (wth->random_fh != NULL)
293                                 file_close(wth->random_fh);
294                         file_close(wth->fh);
295                         g_free(wth);
296                         return NULL;
297                 }
298                 wth->data_offset = 0;
299                 switch ((*open_routines[i])(wth, err, err_info)) {
300
301                 case -1:
302                         /* I/O error - give up */
303                         if (wth->random_fh != NULL)
304                                 file_close(wth->random_fh);
305                         file_close(wth->fh);
306                         g_free(wth);
307                         return NULL;
308
309                 case 0:
310                         /* No I/O error, but not that type of file */
311                         break;
312
313                 case 1:
314                         /* We found the file type */
315                         goto success;
316                 }
317         }
318
319         /* Well, it's not one of the types of file we know about. */
320         if (wth->random_fh != NULL)
321                 file_close(wth->random_fh);
322         file_close(wth->fh);
323         g_free(wth);
324         *err = WTAP_ERR_FILE_UNKNOWN_FORMAT;
325         return NULL;
326
327 success:
328         wth->frame_buffer = g_malloc(sizeof(struct Buffer));
329         buffer_init(wth->frame_buffer, 1500);
330         return wth;
331 }
332
333 /* Table of the file types we know about. */
334 static const struct file_type_info {
335         const char *name;
336         const char *short_name;
337         int     (*can_write_encap)(int);
338         int     (*dump_open)(wtap_dumper *, gboolean, int *);
339 } dump_open_table[WTAP_NUM_FILE_TYPES] = {
340         /* WTAP_FILE_UNKNOWN */
341         { NULL, NULL,
342           NULL, NULL },
343
344         /* WTAP_FILE_WTAP */
345         { "Wiretap (Ethereal)", NULL,
346           NULL, NULL },
347
348         /* WTAP_FILE_PCAP */
349         { "libpcap (tcpdump, Ethereal, etc.)", "libpcap",
350           libpcap_dump_can_write_encap, libpcap_dump_open },
351
352         /* WTAP_FILE_PCAP_SS990417 */
353         { "RedHat Linux 6.1 libpcap (tcpdump)", "rh6_1libpcap",
354           libpcap_dump_can_write_encap, libpcap_dump_open },
355
356         /* WTAP_FILE_PCAP_SS990915 */
357         { "SuSE Linux 6.3 libpcap (tcpdump)", "suse6_3libpcap",
358           libpcap_dump_can_write_encap, libpcap_dump_open },
359
360         /* WTAP_FILE_PCAP_SS991029 */
361         { "modified libpcap (tcpdump)", "modlibpcap",
362           libpcap_dump_can_write_encap, libpcap_dump_open },
363
364         /* WTAP_FILE_PCAP_NOKIA */
365         { "Nokia libpcap (tcpdump)", "nokialibpcap",
366           libpcap_dump_can_write_encap, libpcap_dump_open },
367
368         /* WTAP_FILE_PCAP_AIX */
369         { "AIX libpcap (tcpdump)", NULL,
370           NULL, NULL },
371
372         /* WTAP_FILE_LANALYZER */
373         { "Novell LANalyzer","lanalyzer",
374           lanalyzer_dump_can_write_encap, lanalyzer_dump_open },
375
376         /* WTAP_FILE_NGSNIFFER_UNCOMPRESSED */
377         { "Network Associates Sniffer (DOS-based)", "ngsniffer",
378           ngsniffer_dump_can_write_encap, ngsniffer_dump_open },
379
380         /* WTAP_FILE_NGSNIFFER_COMPRESSED */
381         { "Network Associates Sniffer (DOS-based), compressed", "ngsniffer_comp",
382           NULL, NULL },
383
384         /* WTAP_FILE_SNOOP */
385         { "Sun snoop", "snoop",
386           snoop_dump_can_write_encap, snoop_dump_open },
387
388         /* WTAP_FILE_SHOMITI */
389         { "Shomiti/Finisar Surveyor", "shomiti",
390           NULL, NULL },
391
392         /* WTAP_FILE_IPTRACE_1_0 */
393         { "AIX iptrace 1.0", NULL,
394           NULL, NULL },
395
396         /* WTAP_FILE_IPTRACE_2_0 */
397         { "AIX iptrace 2.0", NULL,
398           NULL, NULL },
399
400         /* WTAP_FILE_NETMON_1_x */
401         { "Microsoft Network Monitor 1.x", "netmon1",
402           netmon_dump_can_write_encap, netmon_dump_open },
403
404         /* WTAP_FILE_NETMON_2_x */
405         { "Microsoft Network Monitor 2.x", "netmon2",
406           netmon_dump_can_write_encap, netmon_dump_open },
407
408         /* WTAP_FILE_NETXRAY_OLD */
409         { "Cinco Networks NetXRay 1.x", NULL,
410           NULL, NULL },
411
412         /* WTAP_FILE_NETXRAY_1_0 */
413         { "Cinco Networks NetXRay 2.0 or later", NULL,
414           NULL, NULL },
415
416         /* WTAP_FILE_NETXRAY_1_1 */
417         { "Network Associates Sniffer (Windows-based) 1.1", "ngwsniffer_1_1",
418           netxray_dump_can_write_encap_1_1, netxray_dump_open_1_1 },
419
420         /* WTAP_FILE_NETXRAY_2_00x */
421         { "Network Associates Sniffer (Windows-based) 2.00x", "ngwsniffer_2_0",
422           netxray_dump_can_write_encap_2_0, netxray_dump_open_2_0 },
423
424         /* WTAP_FILE_RADCOM */
425         { "RADCOM WAN/LAN analyzer", NULL,
426           NULL, NULL },
427
428         /* WTAP_FILE_ASCEND */
429         { "Lucent/Ascend access server trace", NULL,
430           NULL, NULL },
431
432         /* WTAP_FILE_NETTL */
433         { "HP-UX nettl trace", "nettl",
434           nettl_dump_can_write_encap, nettl_dump_open },
435
436         /* WTAP_FILE_TOSHIBA */
437         { "Toshiba Compact ISDN Router snoop trace", NULL,
438           NULL, NULL },
439
440         /* WTAP_FILE_I4BTRACE */
441         { "I4B ISDN trace", NULL,
442           NULL, NULL },
443
444         /* WTAP_FILE_CSIDS */
445         { "CSIDS IPLog", NULL,
446           NULL, NULL },
447
448         /* WTAP_FILE_PPPDUMP */
449         { "pppd log (pppdump format)", NULL,
450           NULL, NULL },
451
452         /* WTAP_FILE_ETHERPEEK_V56 */
453         { "EtherPeek/TokenPeek trace (V5 & V6 file format)", NULL,
454           NULL, NULL },
455
456         /* WTAP_FILE_ETHERPEEK_V7 */
457         { "EtherPeek/TokenPeek/AiroPeek trace (V7 file format)", NULL,
458           NULL, NULL },
459
460         /* WTAP_FILE_VMS */
461         { "TCPIPtrace (VMS)", NULL,
462           NULL, NULL},
463
464         /* WTAP_FILE_DBS_ETHERWATCH */
465         { "DBS Etherwatch (VMS)", NULL,
466           NULL, NULL},
467
468         /* WTAP_FILE_VISUAL_NETWORKS */
469         { "Visual Networks traffic capture", "visual",
470           visual_dump_can_write_encap, visual_dump_open },
471
472         /* WTAP_FILE_COSINE */
473         { "CoSine IPSX L2 capture", "cosine",
474           NULL, NULL },
475
476         /* WTAP_FILE_5VIEWS */
477         { "Accellent 5Views capture", "5views",
478           _5views_dump_can_write_encap, _5views_dump_open },
479
480         /* WTAP_FILE_ERF */
481         { "Endace DAG capture", "erf",
482           NULL, NULL },
483
484         /* WTAP_FILE_HCIDUMP */
485         { "Bluetooth HCI dump", "hcidump",
486           NULL, NULL },
487
488         /* WTAP_FILE_NETWORK_INSTRUMENTS_V9 */
489         { "Network Instruments Observer version 9", "niobserverv9",
490           network_instruments_dump_can_write_encap, network_instruments_dump_open },
491
492         /* WTAP_FILE_AIROPEEK_V9 */
493         { "EtherPeek/AiroPeek trace (V9 file format)", NULL,
494           NULL, NULL },
495     
496         /* WTAP_FILE_EYESDN */
497         { "EyeSDN USB S0/E1 ISDN trace format", NULL,
498           NULL, NULL },
499 };
500
501 /* Name that should be somewhat descriptive. */
502 const char *wtap_file_type_string(int filetype)
503 {
504         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES) {
505                 g_error("Unknown capture file type %d", filetype);
506                 return NULL;
507         } else
508                 return dump_open_table[filetype].name;
509 }
510
511 /* Name to use in, say, a command-line flag specifying the type. */
512 const char *wtap_file_type_short_string(int filetype)
513 {
514         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES)
515                 return NULL;
516         else
517                 return dump_open_table[filetype].short_name;
518 }
519
520 /* Translate a short name to a capture file type. */
521 int wtap_short_string_to_file_type(const char *short_name)
522 {
523         int filetype;
524
525         for (filetype = 0; filetype < WTAP_NUM_FILE_TYPES; filetype++) {
526                 if (dump_open_table[filetype].short_name != NULL &&
527                     strcmp(short_name, dump_open_table[filetype].short_name) == 0)
528                         return filetype;
529         }
530         return -1;      /* no such file type, or we can't write it */
531 }
532
533 gboolean wtap_dump_can_open(int filetype)
534 {
535         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES
536             || dump_open_table[filetype].dump_open == NULL)
537                 return FALSE;
538
539         return TRUE;
540 }
541
542 gboolean wtap_dump_can_write_encap(int filetype, int encap)
543 {
544         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES
545             || dump_open_table[filetype].can_write_encap == NULL)
546                 return FALSE;
547
548         if ((*dump_open_table[filetype].can_write_encap)(encap) != 0)
549                 return FALSE;
550
551         return TRUE;
552 }
553
554 static gboolean wtap_dump_open_check(int filetype, int encap, int *err);
555 static wtap_dumper* wtap_dump_alloc_wdh(int filetype, int encap, int snaplen,
556     int *err);
557 static gboolean wtap_dump_open_finish(wtap_dumper *wdh, int filetype, int *err);
558
559 wtap_dumper* wtap_dump_open(const char *filename, int filetype, int encap,
560                                 int snaplen, int *err)
561 {
562         wtap_dumper *wdh;
563         FILE *fh;
564
565         /* Check whether we can open a capture file with that file type
566            and that encapsulation. */
567         if (!wtap_dump_open_check(filetype, encap, err))
568                 return NULL;
569
570         /* Allocate a data structure for the output stream. */
571         wdh = wtap_dump_alloc_wdh(filetype, encap, snaplen, err);
572         if (wdh == NULL)
573                 return NULL;    /* couldn't allocate it */
574
575         /* Empty filename means stdout */
576         if (*filename == '\0') {
577 #ifdef _WIN32
578                 setmode(fileno(stdout), O_BINARY);
579 #endif
580                 wdh->fh = stdout;
581         } else {
582                 /* In case "fopen()" fails but doesn't set "errno", set "errno"
583                    to a generic "the open failed" error. */
584                 errno = WTAP_ERR_CANT_OPEN;
585                 fh = fopen(filename, "wb");
586                 if (fh == NULL) {
587                         *err = errno;
588                         return NULL;    /* can't create file */
589                 }
590                 wdh->fh = fh;
591         }
592
593         if (!wtap_dump_open_finish(wdh, filetype, err)) {
594                 /* Get rid of the file we created; we couldn't finish
595                    opening it. */
596                 if (wdh->fh != stdout)
597                         unlink(filename);
598                 return NULL;
599         }
600         return wdh;
601 }
602
603 wtap_dumper* wtap_dump_fdopen(int fd, int filetype, int encap, int snaplen,
604                                 int *err)
605 {
606         wtap_dumper *wdh;
607         FILE *fh;
608
609         /* Check whether we can open a capture file with that file type
610            and that encapsulation. */
611         if (!wtap_dump_open_check(filetype, encap, err))
612                 return NULL;
613
614         /* Allocate a data structure for the output stream. */
615         wdh = wtap_dump_alloc_wdh(filetype, encap, snaplen, err);
616         if (wdh == NULL)
617                 return NULL;    /* couldn't allocate it */
618
619         /* In case "fopen()" fails but doesn't set "errno", set "errno"
620            to a generic "the open failed" error. */
621         errno = WTAP_ERR_CANT_OPEN;
622         fh = fdopen(fd, "wb");
623         if (fh == NULL) {
624                 *err = errno;
625                 return NULL;    /* can't create standard I/O stream */
626         }
627         wdh->fh = fh;
628
629         if (!wtap_dump_open_finish(wdh, filetype, err))
630                 return NULL;
631         return wdh;
632 }
633
634 static gboolean wtap_dump_open_check(int filetype, int encap, int *err)
635 {
636         if (!wtap_dump_can_open(filetype)) {
637                 /* Invalid type, or type we don't know how to write. */
638                 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
639                 return FALSE;
640         }
641
642         /* OK, we know how to write that type; can we write the specified
643            encapsulation type? */
644         *err = (*dump_open_table[filetype].can_write_encap)(encap);
645         if (*err != 0)
646                 return FALSE;
647
648         /* All systems go! */
649         return TRUE;
650 }
651
652 static wtap_dumper* wtap_dump_alloc_wdh(int filetype, int encap, int snaplen,
653                                         int *err)
654 {
655         wtap_dumper *wdh;
656
657         wdh = g_malloc(sizeof (wtap_dumper));
658         if (wdh == NULL) {
659                 *err = errno;
660                 return NULL;
661         }
662         wdh->fh = NULL;
663         wdh->file_type = filetype;
664         wdh->snaplen = snaplen;
665         wdh->encap = encap;
666         wdh->bytes_dumped = 0;
667         wdh->dump.opaque = NULL;
668         wdh->subtype_write = NULL;
669         wdh->subtype_close = NULL;
670         return wdh;
671 }
672
673 static gboolean wtap_dump_open_finish(wtap_dumper *wdh, int filetype, int *err)
674 {
675         int fd;
676         gboolean cant_seek;
677
678         /* Can we do a seek on the file descriptor?
679            If not, note that fact. */
680         fd = fileno(wdh->fh);
681         if (lseek(fd, 1, SEEK_CUR) == -1)
682           cant_seek = TRUE;
683         else {
684           /* Undo the seek. */
685           lseek(fd, 0, SEEK_SET);
686           cant_seek = FALSE;
687         }
688
689         /* Now try to open the file for writing. */
690         if (!(*dump_open_table[filetype].dump_open)(wdh, cant_seek, err)) {
691                 /* The attempt failed.  Close the stream for the file.
692                    NOTE: this means the FD handed to "wtap_dump_fdopen()"
693                    will be closed if the open fails. */
694                 if (wdh->fh != stdout)
695                         fclose(wdh->fh);
696
697                 /* Now free up the dumper handle. */
698                 g_free(wdh);
699                 return FALSE;
700         }
701
702         return TRUE;    /* success! */
703 }
704
705 FILE* wtap_dump_file(wtap_dumper *wdh)
706 {
707         return wdh->fh;
708 }
709
710 gboolean wtap_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
711     const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err)
712 {
713         return (wdh->subtype_write)(wdh, phdr, pseudo_header, pd, err);
714 }
715
716 gboolean wtap_dump_close(wtap_dumper *wdh, int *err)
717 {
718         gboolean ret = TRUE;
719
720         if (wdh->subtype_close != NULL) {
721                 /* There's a close routine for this dump stream. */
722                 if (!(wdh->subtype_close)(wdh, err))
723                         ret = FALSE;
724         }
725         errno = WTAP_ERR_CANT_CLOSE;
726         /* Don't close stdout */
727         if (wdh->fh != stdout) {
728                 if (fclose(wdh->fh) == EOF) {
729                         if (ret) {
730                                 /* The per-format close function succeeded,
731                                    but the fclose didn't.  Save the reason
732                                    why, if our caller asked for it. */
733                                 if (err != NULL)
734                                         *err = errno;
735                         }
736                         ret = FALSE;
737                 }
738         }
739         if (wdh->dump.opaque != NULL)
740                 g_free(wdh->dump.opaque);
741         g_free(wdh);
742         return ret;
743 }
744
745 long wtap_get_bytes_dumped(wtap_dumper *wdh)
746 {
747         return wdh->bytes_dumped;
748 }
749
750 void wtap_set_bytes_dumped(wtap_dumper *wdh, long bytes_dumped)
751 {
752         wdh->bytes_dumped = bytes_dumped;
753 }
754