Check for errors in seeks, "tell"s, and "stat()"s/"fstat()"s.
[metze/wireshark/wip.git] / wiretap / dbs-etherwatch.c
1 /* dbs-etherwatch.c
2  *
3  * $Id: dbs-etherwatch.c,v 1.5 2002/03/04 00:25:35 guy Exp $
4  *
5  * Wiretap Library
6  * Copyright (c) 2001 by Marc Milgram <mmilgram@arrayinc.com>
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 "wtap-int.h"
27 #include "buffer.h"
28 #include "dbs-etherwatch.h"
29 #include "file_wrappers.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35
36 /* This module reads the text output of the 'DBS-ETHERTRACE' command in VMS
37  * It was initially based on vms.c.
38  */
39
40 /*
41    Example 'TCPIPTRACE' output data:
42 ETHERWATCH  X5-008
43 42 names and addresses were loaded
44 Reading recorded data from PERSISTENCE
45 ------------------------------------------------------------------------------
46 From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
47 Protocol 08-00 00 00-00-00-00-00,   60 byte buffer at 10-OCT-2001 10:20:45.16
48   [E..<8.....Ò.....]-    0-[45 00 00 3C 38 93 00 00 1D 06 D2 12 80 93 11 1A]
49   [...Ö.Ò...(¤.....]-   16-[80 93 80 D6 02 D2 02 03 00 28 A4 90 00 00 00 00]
50   [.....½.....´....]-   32-[A0 02 FF FF 95 BD 00 00 02 04 05 B4 03 03 04 01]
51   [......å.....    ]-   48-[01 01 08 0A 90 90 E5 14 00 00 00 00]
52 ------------------------------------------------------------------------------
53 From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
54 Protocol 08-00 00 00-00-00-00-00,   50 byte buffer at 10-OCT-2001 10:20:45.17
55   [E..(8.....Ò%....]-    0-[45 00 00 28 38 94 00 00 1D 06 D2 25 80 93 11 1A]
56   [...Ö.Ò...(¤.Z.4w]-   16-[80 93 80 D6 02 D2 02 03 00 28 A4 91 5A 1C 34 77]
57   [P.#(Ás.....´....]-   32-[50 10 23 28 C1 73 00 00 02 04 05 B4 03 03 00 00]
58   [..              ]-   48-[02 04]
59  */
60
61 /* Magic text to check for DBS-ETHERWATCH-ness of file */
62 static const char dbs_etherwatch_hdr_magic[]  =
63 { 'E', 'T', 'H', 'E', 'R', 'W', 'A', 'T', 'C', 'H', ' ', ' '};
64 #define DBS_ETHERWATCH_HDR_MAGIC_SIZE  \
65         (sizeof dbs_etherwatch_hdr_magic  / sizeof dbs_etherwatch_hdr_magic[0])
66
67 /* Magic text for start of packet */
68 static const char dbs_etherwatch_rec_magic[]  =
69 {'F', 'r', 'o', 'm', ' '};
70 #define DBS_ETHERWATCH_REC_MAGIC_SIZE \
71         (sizeof dbs_etherwatch_rec_magic  / sizeof dbs_etherwatch_rec_magic[0])
72
73 /*
74  * XXX - is this the biggest packet we can get?
75  */
76 #define DBS_ETHERWATCH_MAX_PACKET_LEN   16384
77
78 static gboolean dbs_etherwatch_read(wtap *wth, int *err, long *data_offset);
79 static int dbs_etherwatch_seek_read(wtap *wth, long seek_off,
80         union wtap_pseudo_header *pseudo_header, guint8 *pd, int len);
81 static gboolean parse_single_hex_dump_line(char* rec, guint8 *buf, long byte_offset);
82 static int parse_dbs_etherwatch_hex_dump(FILE_T fh, int pkt_len, guint8* buf, int *err);
83 static int parse_dbs_etherwatch_rec_hdr(wtap *wth, FILE_T fh, int *err);
84
85
86 /* Seeks to the beginning of the next packet, and returns the
87    byte offset.  Returns -1 on failure, and sets "*err" to the error. */
88 static long dbs_etherwatch_seek_next_packet(wtap *wth, int *err)
89 {
90   int byte;
91   unsigned int level = 0;
92   long cur_off;
93
94   while ((byte = file_getc(wth->fh)) != EOF) {
95     if (byte == dbs_etherwatch_rec_magic[level]) {
96       level++;
97       if (level >= DBS_ETHERWATCH_REC_MAGIC_SIZE) {
98         /* note: we're leaving file pointer right after the magic characters */
99         cur_off = file_tell(wth->fh);
100         if (cur_off == -1) {
101           /* Error. */
102           *err = file_error(wth->fh);
103           return -1;
104         }
105         return cur_off + 1;
106       }
107     } else {
108       level = 0;
109     }
110   }
111   if (file_eof(wth->fh)) {
112     /* We got an EOF. */
113     *err = 0;
114   } else {
115     /* We (presumably) got an error (there's no equivalent to "ferror()"
116        in zlib, alas, so we don't have a wrapper to check for an error). */
117     *err = file_error(wth->fh);
118   }
119   return -1;
120 }
121
122 #define DBS_ETHERWATCH_HEADER_LINES_TO_CHECK    200
123 #define DBS_ETHERWATCH_LINE_LENGTH              240
124
125 /* Look through the first part of a file to see if this is
126  * a DBS Ethertrace text trace file.
127  *
128  * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
129  * if we get an I/O error, "*err" will be set to a non-zero value.
130  */
131 static gboolean dbs_etherwatch_check_file_type(wtap *wth, int *err)
132 {
133         char    buf[DBS_ETHERWATCH_LINE_LENGTH];
134         int     line, byte;
135         unsigned int reclen, i, level;
136         
137         buf[DBS_ETHERWATCH_LINE_LENGTH-1] = 0;
138
139         for (line = 0; line < DBS_ETHERWATCH_HEADER_LINES_TO_CHECK; line++) {
140                 if (file_gets(buf, DBS_ETHERWATCH_LINE_LENGTH, wth->fh)!=NULL){
141
142                         reclen = strlen(buf);
143                         if (reclen < DBS_ETHERWATCH_HDR_MAGIC_SIZE)
144                                 continue;
145
146                         level = 0;
147                         for (i = 0; i < reclen; i++) {
148                                 byte = buf[i];
149                                 if (byte == dbs_etherwatch_hdr_magic[level]) {
150                                         level++;
151                                         if (level >=
152                                               DBS_ETHERWATCH_HDR_MAGIC_SIZE) {
153                                                 return TRUE;
154                                         }
155                                 }
156                                 else
157                                         level = 0;
158                         }
159                 }
160                 else {
161                         /* EOF or error. */
162                         if (file_eof(wth->fh))
163                                 *err = 0;
164                         else
165                                 *err = file_error(wth->fh);
166                         return FALSE;
167                 }
168         }
169         *err = 0;
170         return FALSE;
171 }
172
173
174 int dbs_etherwatch_open(wtap *wth, int *err)
175 {
176         /* Look for DBS ETHERWATCH header */
177         if (!dbs_etherwatch_check_file_type(wth, err)) {
178                 if (*err == 0)
179                         return 0;
180                 else
181                         return -1;
182         }
183
184         wth->data_offset = 0;
185         wth->file_encap = WTAP_ENCAP_RAW_IP;
186         wth->file_type = WTAP_FILE_DBS_ETHERWATCH;
187         wth->snapshot_length = 0;       /* not known */
188         wth->subtype_read = dbs_etherwatch_read;
189         wth->subtype_seek_read = dbs_etherwatch_seek_read;
190
191         return 1;
192 }
193
194 /* Find the next packet and parse it; called from wtap_loop(). */
195 static gboolean dbs_etherwatch_read(wtap *wth, int *err, long *data_offset)
196 {
197         long    offset = 0;
198         guint8  *buf;
199         int     pkt_len;
200
201         /* Find the next packet */
202         offset = dbs_etherwatch_seek_next_packet(wth, err);
203         if (offset < 1)
204                 return FALSE;
205
206         /* Parse the header */
207         pkt_len = parse_dbs_etherwatch_rec_hdr(wth, wth->fh, err);
208
209         /* Make sure we have enough room for the packet */
210         buffer_assure_space(wth->frame_buffer, DBS_ETHERWATCH_MAX_PACKET_LEN);
211         buf = buffer_start_ptr(wth->frame_buffer);
212
213         /* Convert the ASCII hex dump to binary data */
214         parse_dbs_etherwatch_hex_dump(wth->fh, pkt_len, buf, err);
215
216         wth->data_offset = offset;
217         *data_offset = offset;
218         return TRUE;
219 }
220
221 /* Used to read packets in random-access fashion */
222 static int
223 dbs_etherwatch_seek_read (wtap *wth, long seek_off,
224         union wtap_pseudo_header *pseudo_header _U_,
225         guint8 *pd, int len)
226 {
227         int     pkt_len;
228         int     err;
229
230         file_seek(wth->random_fh, seek_off - 1, SEEK_SET);
231
232         pkt_len = parse_dbs_etherwatch_rec_hdr(NULL, wth->random_fh, &err);
233
234         if (pkt_len != len) {
235                 return -1;
236         }
237
238         parse_dbs_etherwatch_hex_dump(wth->random_fh, pkt_len, pd, &err);
239
240         return 0;
241 }
242
243 /* Parses a packet record header. */
244 static int
245 parse_dbs_etherwatch_rec_hdr(wtap *wth, FILE_T fh, int *err)
246 {
247         char    line[DBS_ETHERWATCH_LINE_LENGTH];
248         int     num_items_scanned;
249         int     pkt_len, csec;
250         struct tm time;
251         char mon[4];
252         guchar *p;
253         static guchar months[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
254
255         pkt_len = 0;
256
257         /* Our file pointer should be on the first line containing the
258          * summary information for a packet. Read in that line and
259          * extract the useful information
260          */
261         if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
262                 *err = file_error(fh);
263                 if (*err == 0) {
264                         *err = WTAP_ERR_SHORT_READ;
265                 }
266                 return -1;
267         }
268
269         /* But that line only contains the mac addresses, so we will ignore
270            that line for now.  Read the next line */
271         if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
272                 *err = file_error(fh);
273                 if (*err == 0) {
274                         *err = WTAP_ERR_SHORT_READ;
275                 }
276                 return -1;
277         }
278
279         num_items_scanned = sscanf(line+33, "%d byte buffer at %d-%3s-%d %d:%d:%d.%d",
280                                    &pkt_len,
281                                    &time.tm_mday, mon,
282                                    &time.tm_year, &time.tm_hour, &time.tm_min,
283                                    &time.tm_sec, &csec);
284
285         if (num_items_scanned != 8) {
286                 *err = WTAP_ERR_BAD_RECORD;
287                 return -1;
288         }
289
290         if (wth) {
291                 p = strstr(months, mon);
292                 if (p)
293                         time.tm_mon = (p - months) / 3;
294                 time.tm_year -= 1900;
295
296                 wth->phdr.ts.tv_sec = mktime(&time);
297
298                 wth->phdr.ts.tv_usec = csec * 10000;
299                 wth->phdr.caplen = pkt_len;
300                 wth->phdr.len = pkt_len;
301                 wth->phdr.pkt_encap = WTAP_ENCAP_RAW_IP;
302         }
303
304         return pkt_len;
305 }
306
307 /* Converts ASCII hex dump to binary data */
308 static int
309 parse_dbs_etherwatch_hex_dump(FILE_T fh, int pkt_len, guint8* buf, int *err)
310 {
311         guchar  line[DBS_ETHERWATCH_LINE_LENGTH];
312         int     i, hex_lines;
313
314         /* Calculate the number of hex dump lines, each
315          * containing 16 bytes of data */
316         hex_lines = pkt_len / 16 + ((pkt_len % 16) ? 1 : 0);
317
318         for (i = 0; i < hex_lines; i++) {
319                 if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
320                         *err = file_error(fh);
321                         if (*err == 0) {
322                                 *err = WTAP_ERR_SHORT_READ;
323                         }
324                         return -1;
325                 }
326                 if (!parse_single_hex_dump_line(line, buf, i * 16)) {
327                         *err = WTAP_ERR_BAD_RECORD;
328                         return -1;
329                 }
330         }
331         return 0;
332 }
333
334 /*
335           1         2         3         4
336 0123456789012345678901234567890123456789012345
337   [E..(8.....Ò.....]-    0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A]
338   [...Ö.Ò...(¤¿Z.4y]-   16-[80 93 80 D6 02 D2 02 03 00 28 A4 BF 5A 1C 34 79]
339   [P.#(ÁC...00000..]-   32-[50 10 23 28 C1 43 00 00 03 30 30 30 30 30 00 00]
340   [.0              ]-   48-[03 30]
341 */
342
343 #define START_POS       28
344 #define HEX_LENGTH      ((16 * 2) + 15) /* sixteen clumps of 2 bytes with 15 inner spaces */
345 /* Take a string representing one line from a hex dump and converts the
346  * text to binary data. We check the printed offset with the offset
347  * we are passed to validate the record. We place the bytes in the buffer
348  * at the specified offset.
349  *
350  * In the process, we're going to write all over the string.
351  *
352  * Returns TRUE if good hex dump, FALSE if bad.
353  */
354 static gboolean
355 parse_single_hex_dump_line(char* rec, guint8 *buf, long byte_offset) {
356
357         int             pos, i;
358         char            *s;
359         long            value;
360
361
362         /* Get the byte_offset directly from the record */
363         rec[26] = '\0';
364         s = rec + 21;
365         value = strtol(s, NULL, 10);
366         
367         if (value != byte_offset) {
368                 return FALSE;
369         }
370
371         /* Go through the substring representing the values and:
372          *      1. Replace any spaces with '0's
373          *      2. Place \0's every 3 bytes (to terminate the string)
374          *
375          * Then read the eight sets of hex bytes
376          */
377
378         for (pos = START_POS; pos < START_POS + HEX_LENGTH; pos++) {
379                 if (rec[pos] == ' ') {
380                         rec[pos] = '0';
381                 }
382         }
383
384         pos = START_POS;
385         for (i = 0; i < 16; i++) {
386                 rec[pos+2] = '\0';
387
388                 buf[byte_offset + i] = (guint8) strtoul(&rec[pos], NULL, 16);
389                 pos += 3;
390         }
391
392         return TRUE;
393 }