change all file offsets from long to gint64 so we can - theoretically - handle files...
[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_UNISTD_H
36 #include <unistd.h>
37 #endif
38
39 #include <errno.h>
40
41 #include "file_util.h"
42
43 #include "wtap-int.h"
44 #include "file_wrappers.h"
45 #include "buffer.h"
46 #include "lanalyzer.h"
47 #include "airopeek9.h"
48 #include "ngsniffer.h"
49 #include "radcom.h"
50 #include "ascend.h"
51 #include "nettl.h"
52 #include "libpcap.h"
53 #include "snoop.h"
54 #include "iptrace.h"
55 #include "iseries.h"
56 #include "netmon.h"
57 #include "netxray.h"
58 #include "toshiba.h"
59 #include "eyesdn.h"
60 #include "i4btrace.h"
61 #include "csids.h"
62 #include "pppdump.h"
63 #include "etherpeek.h"
64 #include "vms.h"
65 #include "dbs-etherwatch.h"
66 #include "visual.h"
67 #include "cosine.h"
68 #include "5views.h"
69 #include "erf.h"
70 #include "hcidump.h"
71 #include "network_instruments.h"
72 #include "k12.h"
73 #include "ber.h"
74 #include "catapult_dct2000.h"
75
76 /* The open_file_* routines should return:
77  *
78  *      -1 on an I/O error;
79  *
80  *      1 if the file they're reading is one of the types it handles;
81  *
82  *      0 if the file they're reading isn't the type they're checking for.
83  *
84  * If the routine handles this type of file, it should set the "file_type"
85  * field in the "struct wtap" to the type of the file.
86  *
87  * Put the trace files that are merely saved telnet-sessions last, since it's
88  * possible that you could have captured someone a router telnet-session
89  * using another tool. So, a libpcap trace of an toshiba "snoop" session
90  * should be discovered as a libpcap file, not a toshiba file.
91  */
92
93 static int (*const open_routines[])(wtap *, int *, char **) = {
94         /* Files that have magic bytes in fixed locations. These
95          * are easy to identify.
96          */
97         libpcap_open,
98         lanalyzer_open,
99         ngsniffer_open,
100         snoop_open,
101         iptrace_open,
102         netmon_open,
103         netxray_open,
104         radcom_open,
105         nettl_open,
106         visual_open,
107         _5views_open,
108         network_instruments_open,
109         airopeek9_open,
110         dbs_etherwatch_open,
111         k12_open,
112         catapult_dct2000_open,
113         ber_open,
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         iseries_open,
123         ascend_open,
124         eyesdn_open,
125         toshiba_open,
126         i4btrace_open,
127         csids_open,
128         vms_open,
129         cosine_open,
130         erf_open,
131         hcidump_open,
132 };
133
134 #define N_FILE_TYPES    (sizeof open_routines / sizeof open_routines[0])
135
136 /*
137  * Visual C++ on Win32 systems doesn't define these.  (Old UNIX systems don't
138  * define them either.)
139  *
140  * Visual C++ on Win32 systems doesn't define S_IFIFO, it defines _S_IFIFO.
141  */
142 #ifndef S_ISREG
143 #define S_ISREG(mode)   (((mode) & S_IFMT) == S_IFREG)
144 #endif
145 #ifndef S_IFIFO
146 #define S_IFIFO _S_IFIFO
147 #endif
148 #ifndef S_ISFIFO
149 #define S_ISFIFO(mode)  (((mode) & S_IFMT) == S_IFIFO)
150 #endif
151 #ifndef S_ISDIR
152 #define S_ISDIR(mode)   (((mode) & S_IFMT) == S_IFDIR)
153 #endif
154
155 /* Opens a file and prepares a wtap struct.
156    If "do_random" is TRUE, it opens the file twice; the second open
157    allows the application to do random-access I/O without moving
158    the seek offset for sequential I/O, which is used by Wireshark
159    so that it can do sequential I/O to a capture file that's being
160    written to as new packets arrive independently of random I/O done
161    to display protocol trees for packets when they're selected. */
162 wtap* wtap_open_offline(const char *filename, int *err, char **err_info,
163     gboolean do_random)
164 {
165         struct stat statb;
166         wtap    *wth;
167         unsigned int    i;
168         gboolean use_stdin = FALSE;
169
170         /* open standard input if filename is '-' */
171         if (strcmp(filename, "-") == 0)
172                 use_stdin = TRUE;
173
174         /* First, make sure the file is valid */
175         if (use_stdin) {
176                 if (fstat(0, &statb) < 0) {
177                         *err = errno;
178                         return NULL;
179                 }
180         } else {
181                 if (eth_stat(filename, &statb) < 0) {
182                         *err = errno;
183                         return NULL;
184                 }
185         }
186         if (S_ISFIFO(statb.st_mode)) {
187                 /*
188                  * Opens of FIFOs are allowed only when not opening
189                  * for random access.
190                  *
191                  * XXX - currently, we do seeking when trying to find
192                  * out the file type, so we don't actually support
193                  * opening FIFOs.  However, we may eventually
194                  * do buffering that allows us to do at least some
195                  * file type determination even on pipes, so we
196                  * allow FIFO opens and let things fail later when
197                  * we try to seek.
198                  */
199                 if (do_random) {
200                         *err = WTAP_ERR_RANDOM_OPEN_PIPE;
201                         return NULL;
202                 }
203         } else if (S_ISDIR(statb.st_mode)) {
204                 /*
205                  * Return different errors for "this is a directory"
206                  * and "this is some random special file type", so
207                  * the user can get a potentially more helpful error.
208                  */
209                 *err = EISDIR;
210                 return NULL;
211         } else if (! S_ISREG(statb.st_mode)) {
212                 *err = WTAP_ERR_NOT_REGULAR_FILE;
213                 return NULL;
214         }
215
216         /*
217          * We need two independent descriptors for random access, so
218          * they have different file positions.  If we're opening the
219          * standard input, we can only dup it to get additional
220          * descriptors, so we can't have two independent descriptors,
221          * and thus can't do random access.
222          */
223         if (use_stdin && do_random) {
224                 *err = WTAP_ERR_RANDOM_OPEN_STDIN;
225                 return NULL;
226         }
227
228         errno = ENOMEM;
229         wth = g_malloc(sizeof(wtap));
230         if (wth == NULL) {
231                 *err = errno;
232                 return NULL;
233         }
234
235         /* Open the file */
236         errno = WTAP_ERR_CANT_OPEN;
237         if (use_stdin) {
238                 /*
239                  * We dup FD 0, so that we don't have to worry about
240                  * an fclose or gzclose of wth->fh closing the standard
241                  * input of the process.
242                  */
243                 wth->fd = eth_dup(0);
244 #ifdef _WIN32
245                 _setmode(wth->fd, O_BINARY);
246 #endif
247         } else
248                 wth->fd = eth_open(filename, O_RDONLY|O_BINARY, 0000 /* no creation so don't matter */);
249         if (wth->fd < 0) {
250                 *err = errno;
251                 g_free(wth);
252                 return NULL;
253         }
254         if (!(wth->fh = filed_open(wth->fd, "rb"))) {
255                 *err = errno;
256                 eth_close(wth->fd);
257                 g_free(wth);
258                 return NULL;
259         }
260
261         if (do_random) {
262                 if (!(wth->random_fh = file_open(filename, "rb"))) {
263                         *err = errno;
264                         file_close(wth->fh);
265                         g_free(wth);
266                         return NULL;
267                 }
268         } else
269                 wth->random_fh = NULL;
270
271         /* initialization */
272         wth->file_encap = WTAP_ENCAP_UNKNOWN;
273         wth->data_offset = 0;
274         wth->subtype_sequential_close = NULL;
275         wth->subtype_close = NULL;
276         wth->tsprecision = WTAP_FILE_TSPREC_USEC;
277
278         /* Try all file types */
279         for (i = 0; i < N_FILE_TYPES; i++) {
280                 /* Seek back to the beginning of the file; the open routine
281                    for the previous file type may have left the file
282                    position somewhere other than the beginning, and the
283                    open routine for this file type will probably want
284                    to start reading at the beginning.
285
286                    Initialize the data offset while we're at it. */
287                 if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
288                         /* I/O error - give up */
289                         if (wth->random_fh != NULL)
290                                 file_close(wth->random_fh);
291                         file_close(wth->fh);
292                         g_free(wth);
293                         return NULL;
294                 }
295                 wth->data_offset = 0;
296                 
297                 switch ((*open_routines[i])(wth, err, err_info)) {
298
299                 case -1:
300                         /* I/O error - give up */
301                         if (wth->random_fh != NULL)
302                                 file_close(wth->random_fh);
303                         file_close(wth->fh);
304                         g_free(wth);
305                         return NULL;
306
307                 case 0:
308                         /* No I/O error, but not that type of file */
309                         break;
310
311                 case 1:
312                         /* We found the file type */
313                         goto success;
314                 }
315         }
316
317         /* Well, it's not one of the types of file we know about. */
318         if (wth->random_fh != NULL)
319                 file_close(wth->random_fh);
320         file_close(wth->fh);
321         g_free(wth);
322         *err = WTAP_ERR_FILE_UNKNOWN_FORMAT;
323         return NULL;
324
325 success:
326         wth->frame_buffer = g_malloc(sizeof(struct Buffer));
327         buffer_init(wth->frame_buffer, 1500);
328         return wth;
329 }
330
331 /* Table of the file types we know about. */
332 static const struct file_type_info {
333     /* the file type name */
334     /* should be NULL for all "pseudo" types that are only internally used and not read/writeable */
335         const char *name;
336
337     /* the file type short name, used as a shortcut for the command line tools */
338     /* should be NULL for all "pseudo" types that are are only internally used and not read/writeable */
339         const char *short_name;
340     
341     /* the common file extensions for this type (seperated by semicolon) */
342     /* should be *.* if no common extension is applicable */
343     const char *file_extensions;
344     
345     /* the default file extension, used to save this type */
346     /* should be NULL if no default extension is known */
347     const char *file_extension_default;
348     
349     /* can this type be compressed with gzip? */
350         gboolean        can_compress;
351     
352     /* can this type write this encapsulation format? */
353     /* should be NULL is this file type don't have write support */
354         int     (*can_write_encap)(int);
355     
356     /* the function to open the capture file for writing */
357     /* should be NULL is this file type don't have write support */
358         int     (*dump_open)(wtap_dumper *, gboolean, int *);
359 } dump_open_table[WTAP_NUM_FILE_TYPES] = {
360         /* WTAP_FILE_UNKNOWN (only used internally for initialization) */
361         { NULL, NULL, NULL, NULL, FALSE,
362           NULL, NULL },
363
364         /* WTAP_FILE_WTAP (only used internally while capturing) */
365         { NULL, NULL, NULL, NULL, FALSE,
366           NULL, NULL },
367
368         /* WTAP_FILE_PCAP */
369         { "Wireshark/tcpdump/... - libpcap", "libpcap", "*.pcap;*.cap", ".pcap", TRUE,
370           libpcap_dump_can_write_encap, libpcap_dump_open },
371
372         /* WTAP_FILE_PCAP_NSEC */
373         { "Wireshark - nanosecond libpcap", "nseclibpcap", "*.pcap;*.cap", ".pcap", TRUE,
374           libpcap_dump_can_write_encap, libpcap_dump_open },
375
376         /* WTAP_FILE_PCAP_AIX */
377         { "AIX tcpdump - libpcap", "aixlibpcap", "*.pcap;*.cap", ".pcap", TRUE,
378           NULL, NULL },
379
380         /* WTAP_FILE_PCAP_SS991029 */
381         { "Modified tcpdump - libpcap", "modlibpcap", "*.pcap;*.cap", ".pcap", TRUE,
382           libpcap_dump_can_write_encap, libpcap_dump_open },
383
384         /* WTAP_FILE_PCAP_NOKIA */
385         { "Nokia tcpdump - libpcap ", "nokialibpcap", "*.pcap;*.cap", ".pcap", TRUE,
386           libpcap_dump_can_write_encap, libpcap_dump_open },
387
388         /* WTAP_FILE_PCAP_SS990417 */
389         { "RedHat 6.1 tcpdump - libpcap", "rh6_1libpcap", "*.pcap;*.cap", ".pcap", TRUE,
390           libpcap_dump_can_write_encap, libpcap_dump_open },
391
392         /* WTAP_FILE_PCAP_SS990915 */
393         { "SuSE 6.3 tcpdump - libpcap", "suse6_3libpcap", "*.pcap;*.cap", ".pcap", TRUE,
394           libpcap_dump_can_write_encap, libpcap_dump_open },
395
396         /* WTAP_FILE_5VIEWS */
397         { "Accellent 5Views capture", "5views", "*.5vw", ".5vw", FALSE,
398           _5views_dump_can_write_encap, _5views_dump_open },
399
400         /* WTAP_FILE_IPTRACE_1_0 */
401         { "AIX iptrace 1.0", "iptrace_1", "*.*", NULL, FALSE,
402           NULL, NULL },
403
404         /* WTAP_FILE_IPTRACE_2_0 */
405         { "AIX iptrace 2.0", "iptrace_2", "*.*", NULL, FALSE,
406           NULL, NULL },
407
408         /* WTAP_FILE_BER */
409         { "ASN.1 Basic Encoding Rules", "ber", "*.*", NULL, FALSE,
410                 NULL, NULL },
411
412         /* WTAP_FILE_HCIDUMP */
413         { "Bluetooth HCI dump", "hcidump", "*.*", NULL, FALSE,
414           NULL, NULL },
415
416         /* WTAP_FILE_CATAPULT_DCT2000 */
417         { "Catapult DCT2000 trace (.out format)", "dct2000", "*.out", ".out", FALSE,
418           catapult_dct2000_dump_can_write_encap, catapult_dct2000_dump_open },
419
420         /* WTAP_FILE_NETXRAY_OLD */
421         { "Cinco Networks NetXRay 1.x", "netxray1", "*.cap", ".cap", FALSE,
422           NULL, NULL },
423
424         /* WTAP_FILE_NETXRAY_1_0 */
425         { "Cinco Networks NetXRay 2.0 or later", "netxray2", "*.cap", ".cap", FALSE,
426           NULL, NULL },
427
428         /* WTAP_FILE_COSINE */
429         { "CoSine IPSX L2 capture", "cosine", "*.*", NULL, FALSE,
430           NULL, NULL },
431
432         /* WTAP_FILE_CSIDS */
433         { "CSIDS IPLog", "csids", "*.*", NULL, FALSE,
434           NULL, NULL },
435
436         /* WTAP_FILE_DBS_ETHERWATCH */
437         { "DBS Etherwatch (VMS)", "etherwatch", "*.*", NULL, FALSE,
438           NULL, NULL},
439
440         /* WTAP_FILE_ERF */
441         { "Endace DAG capture", "erf", "*.erf", ".erf", FALSE,
442           NULL, NULL },
443
444         /* WTAP_FILE_EYESDN */
445         { "EyeSDN USB S0/E1 ISDN trace format", "eyesdn", "*.*", NULL, FALSE,
446                 NULL, NULL },
447     
448         /* WTAP_FILE_NETTL */
449         { "HP-UX nettl trace", "nettl", "*.TRC0;*.TRC1", ".TRC0", FALSE,
450           nettl_dump_can_write_encap, nettl_dump_open },
451
452         /* WTAP_FILE_ISERIES */
453         { "IBM iSeries comm. trace (ASCII)", "iseries_ascii", "*.*", NULL, FALSE,
454           NULL, NULL },
455
456         /* WTAP_FILE_ISERIES_UNICODE */
457         { "IBM iSeries comm. trace (UNICODE)", "iseries_unicode", "*.*", NULL, FALSE,
458           NULL, NULL },
459
460         /* WTAP_FILE_I4BTRACE */
461         { "I4B ISDN trace", "i4btrace", "*.*", NULL, FALSE,
462           NULL, NULL },
463
464         /* WTAP_FILE_ASCEND */
465         { "Lucent/Ascend access server trace", "ascend", "*.*", NULL, FALSE,
466           NULL, NULL },
467
468         /* WTAP_FILE_NETMON_1_x */
469         { "Microsoft NetMon 1.x", "netmon1", "*.cap", ".cap", FALSE,
470           netmon_dump_can_write_encap, netmon_dump_open },
471
472         /* WTAP_FILE_NETMON_2_x */
473         { "Microsoft NetMon 2.x", "netmon2", "*.cap", ".cap", FALSE,
474           netmon_dump_can_write_encap, netmon_dump_open },
475
476         /* WTAP_FILE_NGSNIFFER_UNCOMPRESSED */
477         { "NA Sniffer (DOS)", "ngsniffer", "*.cap;*.enc;*.trc;*.fdc;*.syc", ".cap", FALSE,
478           ngsniffer_dump_can_write_encap, ngsniffer_dump_open },
479
480         /* WTAP_FILE_NGSNIFFER_COMPRESSED */
481         { "NA Sniffer (DOS), compressed", "ngsniffer_comp", "*.caz", ".caz", FALSE,
482           NULL, NULL },
483
484         /* WTAP_FILE_NETXRAY_1_1 */
485         { "NA Sniffer (Windows) 1.1", "ngwsniffer_1_1", "*.cap", ".cap", FALSE,
486           netxray_dump_can_write_encap_1_1, netxray_dump_open_1_1 },
487
488         /* WTAP_FILE_NETXRAY_2_00x */
489         { "NA Sniffer (Windows) 2.00x", "ngwsniffer_2_0", "*.cap", ".cap", FALSE,
490           netxray_dump_can_write_encap_2_0, netxray_dump_open_2_0 },
491
492         /* WTAP_FILE_NETWORK_INSTRUMENTS_V9 */
493         { "Network Instruments Observer (V9)", "niobserverv9", "*.bfr", ".bfr", FALSE,
494           network_instruments_dump_can_write_encap, network_instruments_dump_open },
495
496         /* WTAP_FILE_LANALYZER */
497         { "Novell LANalyzer","lanalyzer", "*.tr1", ".tr1", FALSE,
498           lanalyzer_dump_can_write_encap, lanalyzer_dump_open },
499
500         /* WTAP_FILE_PPPDUMP */
501         { "pppd log (pppdump format)", "pppd", "*.*", NULL, FALSE,
502           NULL, NULL },
503
504         /* WTAP_FILE_RADCOM */
505         { "RADCOM WAN/LAN analyzer", "radcom", "*.*", NULL, FALSE,
506           NULL, NULL },
507
508         /* WTAP_FILE_SNOOP */
509         { "Sun snoop", "snoop", "*.snoop;*.cap", ".snoop", FALSE,
510           snoop_dump_can_write_encap, snoop_dump_open },
511
512         /* WTAP_FILE_SHOMITI */
513         { "Shomiti/Finisar Surveyor", "shomiti", "*.cap", ".cap", FALSE,
514           NULL, NULL },
515
516         /* WTAP_FILE_VMS */
517         { "TCPIPtrace (VMS)", "tcpiptrace", "*.*", NULL, FALSE,
518           NULL, NULL},
519
520         /* WTAP_FILE_K12 */
521         { "Tektronix K12xx 32-bit .rf5 format", "rf5", "*.rf5", ".rf5", FALSE,
522                 k12_dump_can_write_encap, k12_dump_open },
523
524         /* WTAP_FILE_TOSHIBA */
525         { "Toshiba Compact ISDN Router snoop", "toshiba", "*.*", NULL, FALSE,
526           NULL, NULL },
527
528         /* WTAP_FILE_VISUAL_NETWORKS */
529         { "Visual Networks traffic capture", "visual", "*.*", NULL, FALSE,
530           visual_dump_can_write_encap, visual_dump_open },
531
532         /* WTAP_FILE_ETHERPEEK_V56 */
533         { "Wildpacket Ether/TokenPeek (V5 & V6)", "peek56", "*.tpc;*.apc;*.pkt;*.wpz", ".pkt", FALSE,
534           NULL, NULL },
535
536         /* WTAP_FILE_ETHERPEEK_V7 */
537         { "Wildpacket Ether/Token/AiroPeek (V7)", "peek7", "*.tpc;*.apc;*.pkt;*.wpz", ".pkt", FALSE,
538           NULL, NULL },
539
540         /* WTAP_FILE_ETHERPEEK_V9 */
541         { "Wildpacket Ether/AiroPeek (V9)", "peek9", "*.tpc;*.apc;*.pkt;*.wpz", ".pkt", FALSE,
542           NULL, NULL },
543     
544 };
545
546 /* Name that should be somewhat descriptive. */
547 const char *wtap_file_type_string(int filetype)
548 {
549         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES) {
550                 g_error("Unknown capture file type %d", filetype);
551                 return NULL;
552         } else
553                 return dump_open_table[filetype].name;
554 }
555
556 /* Name to use in, say, a command-line flag specifying the type. */
557 const char *wtap_file_type_short_string(int filetype)
558 {
559         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES)
560                 return NULL;
561         else
562                 return dump_open_table[filetype].short_name;
563 }
564
565 /* Translate a short name to a capture file type. */
566 int wtap_short_string_to_file_type(const char *short_name)
567 {
568         int filetype;
569
570         for (filetype = 0; filetype < WTAP_NUM_FILE_TYPES; filetype++) {
571                 if (dump_open_table[filetype].short_name != NULL &&
572                     strcmp(short_name, dump_open_table[filetype].short_name) == 0)
573                         return filetype;
574         }
575         return -1;      /* no such file type, or we can't write it */
576 }
577
578 /* file extensions to use. */
579 const char *wtap_file_extensions_string(int filetype)
580 {
581         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES)
582                 return NULL;
583         else
584                 return dump_open_table[filetype].file_extensions;
585 }
586
587 /* default file extension to use. */
588 const char *wtap_file_extension_default_string(int filetype)
589 {
590         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES)
591                 return NULL;
592         else
593                 return dump_open_table[filetype].file_extension_default;
594 }
595
596 gboolean wtap_dump_can_open(int filetype)
597 {
598         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES
599             || dump_open_table[filetype].dump_open == NULL)
600                 return FALSE;
601
602         return TRUE;
603 }
604
605 gboolean wtap_dump_can_write_encap(int filetype, int encap)
606 {
607         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES
608             || dump_open_table[filetype].can_write_encap == NULL)
609                 return FALSE;
610
611         if ((*dump_open_table[filetype].can_write_encap)(encap) != 0)
612                 return FALSE;
613
614         return TRUE;
615 }
616
617 gboolean wtap_dump_can_compress(int filetype)
618 {
619 #ifdef HAVE_LIBZ
620         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES
621             || dump_open_table[filetype].can_compress == FALSE)
622                 return FALSE;
623
624         return TRUE;
625 #else
626         return FALSE;
627 #endif
628 }
629
630
631 static gboolean wtap_dump_open_check(int filetype, int encap, gboolean comressed, int *err);
632 static wtap_dumper* wtap_dump_alloc_wdh(int filetype, int encap, int snaplen,
633     gboolean compressed, int *err);
634 static gboolean wtap_dump_open_finish(wtap_dumper *wdh, int filetype, gboolean compressed, int *err);
635
636 static FILE *wtap_dump_file_open(wtap_dumper *wdh, const char *filename);
637 static FILE *wtap_dump_file_fdopen(wtap_dumper *wdh, int fd);
638 static int wtap_dump_file_close(wtap_dumper *wdh);
639
640 wtap_dumper* wtap_dump_open(const char *filename, int filetype, int encap,
641                                 int snaplen, gboolean compressed, int *err)
642 {
643         wtap_dumper *wdh;
644         FILE *fh;
645
646         /* Check whether we can open a capture file with that file type
647            and that encapsulation. */
648         if (!wtap_dump_open_check(filetype, encap, compressed, err))
649                 return NULL;
650
651         /* Allocate a data structure for the output stream. */
652         wdh = wtap_dump_alloc_wdh(filetype, encap, snaplen, compressed, err);
653         if (wdh == NULL)
654                 return NULL;    /* couldn't allocate it */
655
656         /* "-" means stdout */
657         if (strcmp(filename, "-") == 0) {
658                 if(compressed) {
659                         g_free(wdh);
660                         return NULL;    /* compress won't work on stdout */
661                 }
662 #ifdef _WIN32
663                 setmode(fileno(stdout), O_BINARY);
664 #endif
665                 wdh->fh = stdout;
666         } else {
667                 /* In case "fopen()" fails but doesn't set "errno", set "errno"
668                    to a generic "the open failed" error. */
669                 errno = WTAP_ERR_CANT_OPEN;
670                 fh = wtap_dump_file_open(wdh, filename);
671                 if (fh == NULL) {
672                         *err = errno;
673                         g_free(wdh);
674                         return NULL;    /* can't create file */
675                 }
676                 wdh->fh = fh;
677         }
678
679         if (!wtap_dump_open_finish(wdh, filetype, compressed, err)) {
680                 /* Get rid of the file we created; we couldn't finish
681                    opening it. */
682                 if (wdh->fh != stdout) {
683                         wtap_dump_file_close(wdh);
684                         eth_unlink(filename);
685                 }
686                 g_free(wdh);
687                 return NULL;
688         }
689         return wdh;
690 }
691
692 wtap_dumper* wtap_dump_fdopen(int fd, int filetype, int encap, int snaplen,
693                                 gboolean compressed, int *err)
694 {
695         wtap_dumper *wdh;
696         FILE *fh;
697
698         /* Check whether we can open a capture file with that file type
699            and that encapsulation. */
700         if (!wtap_dump_open_check(filetype, encap, compressed, err))
701                 return NULL;
702
703         /* Allocate a data structure for the output stream. */
704         wdh = wtap_dump_alloc_wdh(filetype, encap, snaplen, compressed, err);
705         if (wdh == NULL)
706                 return NULL;    /* couldn't allocate it */
707
708 #ifdef _WIN32
709     if(fd == 1) {
710                 setmode(fileno(stdout), O_BINARY);
711     }
712 #endif
713
714         /* In case "fopen()" fails but doesn't set "errno", set "errno"
715            to a generic "the open failed" error. */
716         errno = WTAP_ERR_CANT_OPEN;
717         fh = wtap_dump_file_fdopen(wdh, fd);
718         if (fh == NULL) {
719                 *err = errno;
720                 g_free(wdh);
721                 return NULL;    /* can't create standard I/O stream */
722         }
723         wdh->fh = fh;
724
725         if (!wtap_dump_open_finish(wdh, filetype, compressed, err)) {
726                 wtap_dump_file_close(wdh);
727                 g_free(wdh);
728                 return NULL;
729         }
730         return wdh;
731 }
732
733 static gboolean wtap_dump_open_check(int filetype, int encap, gboolean compressed, int *err)
734 {
735         if (!wtap_dump_can_open(filetype)) {
736                 /* Invalid type, or type we don't know how to write. */
737                 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
738                 return FALSE;
739         }
740
741         /* OK, we know how to write that type; can we write the specified
742            encapsulation type? */
743         *err = (*dump_open_table[filetype].can_write_encap)(encap);
744         if (*err != 0)
745                 return FALSE;
746
747         /* if compression is wanted, do we support this for this filetype? */
748         if(compressed && !wtap_dump_can_compress(filetype)) {
749                 *err = WTAP_ERR_COMPRESSION_NOT_SUPPORTED;
750                 return FALSE;
751         }
752
753         *err = (*dump_open_table[filetype].can_write_encap)(encap);
754         if (*err != 0)
755                 return FALSE;
756
757
758         /* All systems go! */
759         return TRUE;
760 }
761
762 static wtap_dumper* wtap_dump_alloc_wdh(int filetype, int encap, int snaplen,
763                                         gboolean compressed, int *err)
764 {
765         wtap_dumper *wdh;
766
767         wdh = g_malloc(sizeof (wtap_dumper));
768         if (wdh == NULL) {
769                 *err = errno;
770                 return NULL;
771         }
772         wdh->fh = NULL;
773         wdh->file_type = filetype;
774         wdh->snaplen = snaplen;
775         wdh->encap = encap;
776         wdh->compressed = compressed;
777         wdh->bytes_dumped = 0;
778         wdh->dump.opaque = NULL;
779         wdh->subtype_write = NULL;
780         wdh->subtype_close = NULL;
781         return wdh;
782 }
783
784 static gboolean wtap_dump_open_finish(wtap_dumper *wdh, int filetype, gboolean compressed, int *err)
785 {
786         int fd;
787         gboolean cant_seek;
788
789         /* Can we do a seek on the file descriptor?
790            If not, note that fact. */
791         if(compressed) {
792                 cant_seek = TRUE;
793         } else {
794                 fd = fileno(wdh->fh);
795                 if (lseek(fd, 1, SEEK_CUR) == -1)
796                   cant_seek = TRUE;
797                 else {
798                   /* Undo the seek. */
799                   lseek(fd, 0, SEEK_SET);
800                   cant_seek = FALSE;
801                 }
802         }
803
804         /* Now try to open the file for writing. */
805         if (!(*dump_open_table[filetype].dump_open)(wdh, cant_seek, err)) {
806                 return FALSE;
807         }
808
809         return TRUE;    /* success! */
810 }
811
812 gboolean wtap_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
813     const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err)
814 {
815         return (wdh->subtype_write)(wdh, phdr, pseudo_header, pd, err);
816 }
817
818 void wtap_dump_flush(wtap_dumper *wdh)
819 {
820 #ifdef HAVE_LIBZ
821         if(wdh->compressed) {
822                 gzflush(wdh->fh, Z_SYNC_FLUSH); /* XXX - is Z_SYNC_FLUSH the right one? */
823         } else 
824 #endif
825         {
826                 fflush(wdh->fh);
827         }
828 }
829
830 gboolean wtap_dump_close(wtap_dumper *wdh, int *err)
831 {
832         gboolean ret = TRUE;
833
834         if (wdh->subtype_close != NULL) {
835                 /* There's a close routine for this dump stream. */
836                 if (!(wdh->subtype_close)(wdh, err))
837                         ret = FALSE;
838         }
839         errno = WTAP_ERR_CANT_CLOSE;
840         /* Don't close stdout */
841         if (wdh->fh != stdout) {
842                 if (wtap_dump_file_close(wdh) == EOF) {
843                         if (ret) {
844                                 /* The per-format close function succeeded,
845                                    but the fclose didn't.  Save the reason
846                                    why, if our caller asked for it. */
847                                 if (err != NULL)
848                                         *err = errno;
849                         }
850                         ret = FALSE;
851                 }
852     } else {
853         /* as we don't close stdout, at least try to flush it */
854         wtap_dump_flush(wdh);
855     }
856         if (wdh->dump.opaque != NULL)
857                 g_free(wdh->dump.opaque);
858         g_free(wdh);
859         return ret;
860 }
861
862 gint64 wtap_get_bytes_dumped(wtap_dumper *wdh)
863 {
864         return wdh->bytes_dumped;
865 }
866
867 void wtap_set_bytes_dumped(wtap_dumper *wdh, gint64 bytes_dumped)
868 {
869         wdh->bytes_dumped = bytes_dumped;
870 }
871
872
873 /* internally open a file for writing (compressed or not) */
874 static FILE *wtap_dump_file_open(wtap_dumper *wdh, const char *filename)
875 {
876 #ifdef HAVE_LIBZ
877         if(wdh->compressed) {
878                 return gzopen(filename, "wb");
879         } else 
880 #endif
881         {
882                 return eth_fopen(filename, "wb");
883         }
884 }
885
886 /* internally open a file for writing (compressed or not) */
887 static FILE *wtap_dump_file_fdopen(wtap_dumper *wdh, int fd)
888 {
889 #ifdef HAVE_LIBZ
890         if(wdh->compressed) {
891                 return gzdopen(fd, "wb");
892         } else 
893 #endif
894         {
895                 return fdopen(fd, "wb");
896         }
897 }
898
899 /* internally writing raw bytes (compressed or not) */
900 size_t wtap_dump_file_write(wtap_dumper *wdh, const void *buf, unsigned bufsize)
901 {
902 #ifdef HAVE_LIBZ
903         if(wdh->compressed) {
904                 return gzwrite(wdh->fh, buf, bufsize);
905         } else 
906 #endif
907         {
908                 return fwrite(buf, 1, bufsize, wdh->fh);
909         }
910 }
911
912 /* internally close a file for writing (compressed or not) */
913 static int wtap_dump_file_close(wtap_dumper *wdh)
914 {
915 #ifdef HAVE_LIBZ
916         if(wdh->compressed) {
917                 return gzclose(wdh->fh);
918         } else 
919 #endif
920         {
921                 return fclose(wdh->fh);
922         }
923 }
924
925 int wtap_dump_file_ferror(wtap_dumper *wdh)
926 {
927 #ifdef HAVE_LIBZ
928         int errnum;
929
930         if(wdh->compressed) {
931                 gzerror(wdh->fh, &errnum);
932
933                 if(errnum == Z_ERRNO) {
934                         return errno;
935                 } else {
936                         /* XXX - what to do with this zlib specific number? */
937                         return errnum;
938                 }
939         } else 
940 #endif
941         {
942                 return ferror(wdh->fh);
943         }
944 }
945