Use WTAP_ERR_UNSUPPORTED_ENCAP for all attempts to open or read a
[obnox/wireshark/wip.git] / wiretap / netmon.c
1 /* netmon.c
2  *
3  * $Id: netmon.c,v 1.25 2000/02/19 08:00:05 guy Exp $
4  *
5  * Wiretap Library
6  * Copyright (c) 1998 by Gilbert Ramirez <gram@xiexie.org>
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 #include <errno.h>
27 #include <time.h>
28 #include <string.h>
29 #include "wtap.h"
30 #include "file_wrappers.h"
31 #include "buffer.h"
32 #include "netmon.h"
33
34 /* The file at
35  *
36  *      ftp://ftp.microsoft.com/developr/drg/cifs/cifs/Bhfile.zip
37  *
38  * contains "STRUCT.H", which declares the typedef CAPTUREFILE_HEADER
39  * for the header of a Microsoft Network Monitor capture file.
40  */
41
42 /* Capture file header, *including* magic number, is padded to 128 bytes. */
43 #define CAPTUREFILE_HEADER_SIZE 128
44
45 /* Magic number in Network Monitor 1.x files. */
46 static const char netmon_1_x_magic[] = {
47         'R', 'T', 'S', 'S'
48 };
49
50 /* Magic number in Network Monitor 2.x files. */
51 static const char netmon_2_x_magic[] = {
52         'G', 'M', 'B', 'U'
53 };
54
55 /* Network Monitor file header (minus magic number). */
56 struct netmon_hdr {
57         guint8  ver_minor;      /* minor version number */
58         guint8  ver_major;      /* major version number */
59         guint16 network;        /* network type */
60         guint16 ts_year;        /* year of capture start */
61         guint16 ts_month;       /* month of capture start (January = 1) */
62         guint16 ts_dow;         /* day of week of capture start (Sun = 0) */
63         guint16 ts_day;         /* day of month of capture start */
64         guint16 ts_hour;        /* hour of capture start */
65         guint16 ts_min;         /* minute of capture start */
66         guint16 ts_sec;         /* second of capture start */
67         guint16 ts_msec;        /* millisecond of capture start */
68         guint32 frametableoffset;       /* frame index table offset */
69         guint32 frametablelength;       /* frame index table size */
70         guint32 userdataoffset;         /* user data offset */
71         guint32 userdatalength;         /* user data size */
72         guint32 commentdataoffset;      /* comment data offset */
73         guint32 commentdatalength;      /* comment data size */
74         guint32 statisticsoffset;       /* offset to statistics structure */
75         guint32 statisticslength;       /* length of statistics structure */
76         guint32 networkinfooffset;      /* offset to network info structure */
77         guint32 networkinfolength;      /* length of network info structure */
78 };
79
80 /* Network Monitor 1.x record header; not defined in STRUCT.H, but deduced by
81  * looking at capture files. */
82 struct netmonrec_1_x_hdr {
83         guint32 ts_delta;       /* time stamp - msecs since start of capture */
84         guint16 orig_len;       /* actual length of packet */
85         guint16 incl_len;       /* number of octets captured in file */
86 };
87
88 /* Network Monitor 2.x record header; not defined in STRUCT.H, but deduced by
89  * looking at capture files. */
90 struct netmonrec_2_x_hdr {
91         guint32 ts_delta_lo;    /* time stamp - usecs since start of capture */
92         guint32 ts_delta_hi;    /* time stamp - usecs since start of capture */
93         guint32 orig_len;       /* actual length of packet */
94         guint32 incl_len;       /* number of octets captured in file */
95 };
96
97 static int netmon_read(wtap *wth, int *err);
98 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
99     const u_char *pd, int *err);
100 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err);
101
102 int netmon_open(wtap *wth, int *err)
103 {
104         int bytes_read;
105         char magic[sizeof netmon_1_x_magic];
106         struct netmon_hdr hdr;
107         int file_type;
108         static const int netmon_encap[] = {
109                 WTAP_ENCAP_UNKNOWN,
110                 WTAP_ENCAP_ETHERNET,
111                 WTAP_ENCAP_TR,
112                 WTAP_ENCAP_FDDI_BITSWAPPED,
113                 WTAP_ENCAP_UNKNOWN,     /* WAN */
114                 WTAP_ENCAP_UNKNOWN,     /* LocalTalk */
115                 WTAP_ENCAP_UNKNOWN,     /* "DIX" - should not occur */
116                 WTAP_ENCAP_UNKNOWN,     /* ARCNET raw */
117                 WTAP_ENCAP_UNKNOWN,     /* ARCNET 878.2 */
118                 WTAP_ENCAP_UNKNOWN,     /* ATM */
119                 WTAP_ENCAP_UNKNOWN,     /* Wireless WAN */
120                 WTAP_ENCAP_UNKNOWN      /* IrDA */
121         };
122         #define NUM_NETMON_ENCAPS (sizeof netmon_encap / sizeof netmon_encap[0])
123         struct tm tm;
124         guint32 frame_table_length;
125         guint32 first_frame_table_entry;
126
127         /* Read in the string that should be at the start of a Network
128          * Monitor file */
129         file_seek(wth->fh, 0, SEEK_SET);
130         errno = WTAP_ERR_CANT_READ;
131         bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
132         if (bytes_read != sizeof magic) {
133                 *err = file_error(wth->fh);
134                 if (*err != 0)
135                         return -1;
136                 return 0;
137         }
138
139         if (memcmp(magic, netmon_1_x_magic, sizeof netmon_1_x_magic) != 0
140          && memcmp(magic, netmon_2_x_magic, sizeof netmon_1_x_magic) != 0) {
141                 return 0;
142         }
143
144         /* Read the rest of the header. */
145         errno = WTAP_ERR_CANT_READ;
146         bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
147         if (bytes_read != sizeof hdr) {
148                 *err = file_error(wth->fh);
149                 if (*err != 0)
150                         return -1;
151                 return 0;
152         }
153
154         switch (hdr.ver_major) {
155
156         case 1:
157                 file_type = WTAP_FILE_NETMON_1_x;
158                 break;
159
160         case 2:
161                 file_type = WTAP_FILE_NETMON_2_x;
162                 break;
163
164         default:
165                 g_message("netmon: major version %u unsupported", hdr.ver_major);
166                 *err = WTAP_ERR_UNSUPPORTED;
167                 return -1;
168         }
169
170         hdr.network = pletohs(&hdr.network);
171         if (hdr.network >= NUM_NETMON_ENCAPS
172             || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
173                 g_message("netmon: network type %u unknown or unsupported",
174                     hdr.network);
175                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
176                 return -1;
177         }
178
179         /* This is a netmon file */
180         wth->file_type = file_type;
181         wth->capture.netmon = g_malloc(sizeof(netmon_t));
182         wth->subtype_read = netmon_read;
183         wth->file_encap = netmon_encap[hdr.network];
184         wth->snapshot_length = 16384;   /* XXX - not available in header */
185         /*
186          * Convert the time stamp to a "time_t" and a number of
187          * milliseconds.
188          */
189         tm.tm_year = pletohs(&hdr.ts_year) - 1900;
190         tm.tm_mon = pletohs(&hdr.ts_month) - 1;
191         tm.tm_mday = pletohs(&hdr.ts_day);
192         tm.tm_hour = pletohs(&hdr.ts_hour);
193         tm.tm_min = pletohs(&hdr.ts_min);
194         tm.tm_sec = pletohs(&hdr.ts_sec);
195         tm.tm_isdst = -1;
196         wth->capture.netmon->start_secs = mktime(&tm);
197         /*
198          * XXX - what if "secs" is -1?  Unlikely, but if the capture was
199          * done in a time zone that switches between standard and summer
200          * time sometime other than when we do, and thus the time was one
201          * that doesn't exist here because a switch from standard to summer
202          * time zips over it, it could happen.
203          *
204          * On the other hand, if the capture was done in a different time
205          * zone, this won't work right anyway; unfortunately, the time
206          * zone isn't stored in the capture file (why the hell didn't
207          * they stuff a FILETIME, which is the number of 100-nanosecond
208          * intervals since 1601-01-01 00:00:00 "UTC", there, instead
209          * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?).
210          */
211         wth->capture.netmon->start_usecs = pletohs(&hdr.ts_msec)*1000;
212
213         wth->capture.netmon->version_major = hdr.ver_major;
214
215         /*
216          * The "frame index table" appears to come after the last
217          * packet; remember its offset, so we know when we have no
218          * more packets to read.
219          */
220         wth->capture.netmon->end_offset = pletohl(&hdr.frametableoffset);
221
222         /*
223          * It appears that some NetMon 2.x files don't have the
224          * first packet starting exactly 128 bytes into the file.
225          * So we read the first entry from the frame table, and
226          * use that as the offset of the first packet.
227          *
228          * First, make sure the frame table has at least one entry
229          * in it....
230          */
231         frame_table_length = pletohl(&hdr.frametablelength);
232         if (frame_table_length < sizeof first_frame_table_entry) {
233                 g_message("netmon: frame table length is %u, which means it's less than one entry in size",
234                     frame_table_length);
235                 *err = WTAP_ERR_UNSUPPORTED;
236                 return -1;
237         }
238
239         /*
240          * Now read that entry.  (It appears that the N+1st frame immediately
241          * follows the Nth frame, so we don't need any entries after the
242          * first entry.)
243          */
244         errno = WTAP_ERR_CANT_READ;
245         file_seek(wth->fh, wth->capture.netmon->end_offset, SEEK_SET);
246         bytes_read = file_read(&first_frame_table_entry, 1,
247             sizeof first_frame_table_entry, wth->fh);
248         if (bytes_read != sizeof first_frame_table_entry) {
249                 *err = file_error(wth->fh);
250                 if (*err != 0)
251                         return -1;
252                 return 0;
253         }
254
255         /* Seek to the beginning of the data records. */
256         wth->data_offset = pletohl(&first_frame_table_entry);
257         file_seek(wth->fh, wth->data_offset, SEEK_SET);
258
259         return 1;
260 }
261
262 /* Read the next packet */
263 static int netmon_read(wtap *wth, int *err)
264 {
265         guint32 packet_size = 0;
266         int     bytes_read;
267         union {
268                 struct netmonrec_1_x_hdr hdr_1_x;
269                 struct netmonrec_2_x_hdr hdr_2_x;
270         }       hdr;
271         int     hdr_size = 0;
272         int     data_offset;
273         time_t  secs;
274         guint32 usecs;
275         double  t;
276
277         /* Have we reached the end of the packet data? */
278         if (wth->data_offset >= wth->capture.netmon->end_offset) {
279                 /* Yes. */
280                 return 0;
281         }
282         /* Read record header. */
283         switch (wth->capture.netmon->version_major) {
284
285         case 1:
286                 hdr_size = sizeof (struct netmonrec_1_x_hdr);
287                 break;
288
289         case 2:
290                 hdr_size = sizeof (struct netmonrec_2_x_hdr);
291                 break;
292         }
293         errno = WTAP_ERR_CANT_READ;
294         bytes_read = file_read(&hdr, 1, hdr_size, wth->fh);
295         if (bytes_read != hdr_size) {
296                 *err = file_error(wth->fh);
297                 if (*err != 0)
298                         return -1;
299                 if (bytes_read != 0) {
300                         *err = WTAP_ERR_SHORT_READ;
301                         return -1;
302                 }
303                 return 0;
304         }
305         wth->data_offset += hdr_size;
306
307         switch (wth->capture.netmon->version_major) {
308
309         case 1:
310                 packet_size = pletohs(&hdr.hdr_1_x.incl_len);
311                 break;
312
313         case 2:
314                 packet_size = pletohl(&hdr.hdr_2_x.incl_len);
315                 break;
316         }
317         if (packet_size > WTAP_MAX_PACKET_SIZE) {
318                 /*
319                  * Probably a corrupt capture file; don't blow up trying
320                  * to allocate space for an immensely-large packet.
321                  */
322                 g_message("netmon: File has %u-byte packet, bigger than maximum of %u",
323                     packet_size, WTAP_MAX_PACKET_SIZE);
324                 *err = WTAP_ERR_BAD_RECORD;
325                 return -1;
326         }
327         buffer_assure_space(wth->frame_buffer, packet_size);
328         data_offset = wth->data_offset;
329         errno = WTAP_ERR_CANT_READ;
330         bytes_read = file_read(buffer_start_ptr(wth->frame_buffer), 1,
331                         packet_size, wth->fh);
332
333         if (bytes_read != packet_size) {
334                 *err = file_error(wth->fh);
335                 if (*err == 0)
336                         *err = WTAP_ERR_SHORT_READ;
337                 return -1;
338         }
339         wth->data_offset += packet_size;
340
341         t = (double)wth->capture.netmon->start_usecs;
342         switch (wth->capture.netmon->version_major) {
343
344         case 1:
345                 t += ((double)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
346                 break;
347
348         case 2:
349                 t += (double)pletohl(&hdr.hdr_2_x.ts_delta_lo)
350                     + (double)pletohl(&hdr.hdr_2_x.ts_delta_hi)*4294967296.0;
351                 break;
352         }
353         secs = (time_t)(t/1000000);
354         usecs = (guint32)(t - secs*1000000);
355         wth->phdr.ts.tv_sec = wth->capture.netmon->start_secs + secs;
356         wth->phdr.ts.tv_usec = usecs;
357         wth->phdr.caplen = packet_size;
358         switch (wth->capture.netmon->version_major) {
359
360         case 1:
361                 wth->phdr.len = pletohs(&hdr.hdr_1_x.orig_len);
362                 break;
363
364         case 2:
365                 wth->phdr.len = pletohl(&hdr.hdr_2_x.orig_len);
366                 break;
367         }
368         wth->phdr.pkt_encap = wth->file_encap;
369
370         return data_offset;
371 }
372
373 static const int wtap_encap[] = {
374         -1,             /* WTAP_ENCAP_UNKNOWN -> unsupported */
375         1,              /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
376         2,              /* WTAP_ENCAP_TR -> NDIS Token Ring */
377         -1,             /* WTAP_ENCAP_SLIP -> unsupported */
378         -1,             /* WTAP_ENCAP_PPP -> unsupported */
379         3,              /* WTAP_ENCAP_FDDI -> NDIS FDDI */
380         3,              /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
381         -1,             /* WTAP_ENCAP_RAW_IP -> unsupported */
382         -1,             /* WTAP_ENCAP_ARCNET -> unsupported */
383         -1,             /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
384         -1,             /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
385         -1,             /* WTAP_ENCAP_LAPB -> unsupported*/
386         -1,             /* WTAP_ENCAP_ATM_SNIFFER -> unsupported */
387         -1              /* WTAP_ENCAP_NULL -> unsupported */
388 };
389 #define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
390
391 /* Returns 0 if we could write the specified encapsulation type,
392    an error indication otherwise. */
393 int netmon_dump_can_write_encap(int filetype, int encap)
394 {
395         /* Per-packet encapsulations aren't supported. */
396         if (encap == WTAP_ENCAP_PER_PACKET)
397                 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
398
399         if (encap < 0 || encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
400                 return WTAP_ERR_UNSUPPORTED_ENCAP;
401
402         return 0;
403 }
404
405 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
406    failure */
407 gboolean netmon_dump_open(wtap_dumper *wdh, int *err)
408 {
409         /* This is a netmon file */
410         wdh->subtype_write = netmon_dump;
411         wdh->subtype_close = netmon_dump_close;
412
413         /* We can't fill in all the fields in the file header, as we
414            haven't yet written any packets.  As we'll have to rewrite
415            the header when we've written out all the packets, we just
416            skip over the header for now. */
417         fseek(wdh->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET);
418
419         wdh->private.netmon = g_malloc(sizeof(netmon_dump_t));
420         wdh->private.netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE;
421         wdh->private.netmon->got_first_record_time = FALSE;
422         wdh->private.netmon->frame_table = NULL;
423         wdh->private.netmon->frame_table_index = 0;
424         wdh->private.netmon->frame_table_size = 0;
425
426         return TRUE;
427 }
428
429 /* Write a record for a packet to a dump file.
430    Returns TRUE on success, FALSE on failure. */
431 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
432     const u_char *pd, int *err)
433 {
434         netmon_dump_t *priv = wdh->private.netmon;
435         struct netmonrec_1_x_hdr rec_1_x_hdr;
436         struct netmonrec_2_x_hdr rec_2_x_hdr;
437         char *hdrp;
438         int hdr_size;
439         int nwritten;
440
441         /* NetMon files have a capture start time in the file header,
442            and have times relative to that in the packet headers;
443            pick the time of the first packet as the capture start
444            time. */
445         if (!priv->got_first_record_time) {
446                 priv->first_record_time = phdr->ts;
447                 priv->got_first_record_time = TRUE;
448         }
449         
450         switch (wdh->file_type) {
451
452         case WTAP_FILE_NETMON_1_x:
453                 rec_1_x_hdr.ts_delta = htolel(
454                     (phdr->ts.tv_sec - priv->first_record_time.tv_sec)*1000
455                   + (phdr->ts.tv_usec - priv->first_record_time.tv_usec + 500)/1000);
456                 rec_1_x_hdr.orig_len = htoles(phdr->len);
457                 rec_1_x_hdr.incl_len = htoles(phdr->caplen);
458                 hdrp = (char *)&rec_1_x_hdr;
459                 hdr_size = sizeof rec_1_x_hdr;
460                 break;
461
462         case WTAP_FILE_NETMON_2_x:
463                 /* XXX - fill in 64-bit time diff in microseconds */
464                 rec_2_x_hdr.orig_len = htolel(phdr->len);
465                 rec_2_x_hdr.incl_len = htolel(phdr->caplen);
466                 hdrp = (char *)&rec_2_x_hdr;
467                 hdr_size = sizeof rec_2_x_hdr;
468                 break;
469
470         default:
471                 /* We should never get here - our open routine
472                    should only get called for the types above. */
473                 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
474                 return FALSE;
475         }
476
477         nwritten = fwrite(hdrp, 1, hdr_size, wdh->fh);
478         if (nwritten != hdr_size) {
479                 if (nwritten < 0)
480                         *err = errno;
481                 else
482                         *err = WTAP_ERR_SHORT_WRITE;
483                 return FALSE;
484         }
485         nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
486         if (nwritten != phdr->caplen) {
487                 if (nwritten < 0)
488                         *err = errno;
489                 else
490                         *err = WTAP_ERR_SHORT_WRITE;
491                 return FALSE;
492         }
493
494         /*
495          * Stash the file offset of this frame.
496          */
497         if (priv->frame_table_size == 0) {
498                 /*
499                  * Haven't yet allocated the buffer for the frame table.
500                  */
501                 priv->frame_table = g_malloc(1024 * sizeof *priv->frame_table);
502                 priv->frame_table_size = 1024;
503         } else {
504                 /*
505                  * We've allocated it; are we at the end?
506                  */
507                 if (priv->frame_table_index >= priv->frame_table_size) {
508                         /*
509                          * Yes - double the size of the frame table.
510                          */
511                         priv->frame_table_size *= 2;
512                         priv->frame_table = g_realloc(priv->frame_table,
513                             priv->frame_table_size * sizeof *priv->frame_table);
514                 }
515         }
516         priv->frame_table[priv->frame_table_index] =
517             htolel(priv->frame_table_offset);
518         priv->frame_table_index++;
519         priv->frame_table_offset += hdr_size + phdr->caplen;
520
521         return TRUE;
522 }
523
524 /* Finish writing to a dump file.
525    Returns TRUE on success, FALSE on failure. */
526 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
527 {
528         netmon_dump_t *priv = wdh->private.netmon;
529         int n_to_write;
530         int nwritten;
531         struct netmon_hdr file_hdr;
532         const char *magicp;
533         int magic_size;
534         struct tm *tm;
535
536         /* Write out the frame table.  "priv->frame_table_index" is
537            the number of entries we've put into it. */
538         n_to_write = priv->frame_table_index * sizeof *priv->frame_table;
539         nwritten = fwrite(priv->frame_table, 1, n_to_write, wdh->fh);
540         if (nwritten != n_to_write) {
541                 if (nwritten < 0)
542                         *err = errno;
543                 else
544                         *err = WTAP_ERR_SHORT_WRITE;
545                 return FALSE;
546         }
547
548         /* Now go fix up the file header. */
549         fseek(wdh->fh, 0, SEEK_SET);
550         memset(&file_hdr, '\0', sizeof file_hdr);
551         switch (wdh->file_type) {
552
553         case WTAP_FILE_NETMON_1_x:
554                 magicp = netmon_1_x_magic;
555                 magic_size = sizeof netmon_1_x_magic;
556                 /* current NetMon version, for 1.x, is 1.1 */
557                 file_hdr.ver_minor = 1;
558                 file_hdr.ver_major = 1;
559                 break;
560
561         case WTAP_FILE_NETMON_2_x:
562                 magicp = netmon_2_x_magic;
563                 magic_size = sizeof netmon_2_x_magic;
564                 /* XXX - fill in V2 stuff. */
565                 break;
566
567         default:
568                 /* We should never get here - our open routine
569                    should only get called for the types above. */
570                 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
571                 return FALSE;
572         }
573         nwritten = fwrite(magicp, 1, magic_size, wdh->fh);
574         if (nwritten != magic_size) {
575                 if (nwritten < 0)
576                         *err = errno;
577                 else
578                         *err = WTAP_ERR_SHORT_WRITE;
579                 return FALSE;
580         }
581
582         file_hdr.network = htoles(wtap_encap[wdh->encap]);
583         tm = localtime(&priv->first_record_time.tv_sec);
584         file_hdr.ts_year = htoles(1900 + tm->tm_year);
585         file_hdr.ts_month = htoles(tm->tm_mon + 1);
586         file_hdr.ts_dow = htoles(tm->tm_wday);
587         file_hdr.ts_day = htoles(tm->tm_mday);
588         file_hdr.ts_hour = htoles(tm->tm_hour);
589         file_hdr.ts_min = htoles(tm->tm_min);
590         file_hdr.ts_sec = htoles(tm->tm_sec);
591         file_hdr.ts_msec = htoles(priv->first_record_time.tv_usec/1000);
592                 /* XXX - what about rounding? */
593         file_hdr.frametableoffset = htolel(priv->frame_table_offset);
594         file_hdr.frametablelength =
595             htolel(priv->frame_table_index * sizeof *priv->frame_table);
596         nwritten = fwrite(&file_hdr, 1, sizeof file_hdr, wdh->fh);
597         if (nwritten != sizeof file_hdr) {
598                 if (nwritten < 0)
599                         *err = errno;
600                 else
601                         *err = WTAP_ERR_SHORT_WRITE;
602                 return FALSE;
603         }
604
605         return TRUE;
606 }