Have Wiretap set the snapshot length to 0 if it can't be derived from
[obnox/wireshark/wip.git] / wiretap / vms.c
1 /* vms.c
2  *
3  * $Id: vms.c,v 1.6 2002/02/08 10:07:41 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 "vms.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 output of the 'TCPIPTRACE' command in VMS
37  * It was initially based on toshiba.c.
38  */
39
40 /*
41    Example 'TCPIPTRACE' output data:
42    TCPIPtrace full display RCV packet 8 at 10-JUL-2001 14:54:19.56
43
44    IP Version = 4,  IHL = 5,  TOS = 00,   Total Length = 84 = ^x0054
45    IP Identifier  = ^x178F,  Flags (0=0,DF=0,MF=0),
46          Fragment Offset = 0 = ^x0000,   Calculated Offset = 0 = ^x0000
47    IP TTL = 64 = ^x40,  Protocol = 17 = ^x11,  Header Checksum = ^x4C71
48    IP Source Address      = 10.12.1.80
49    IP Destination Address = 10.12.1.50
50
51    UDP Source Port = 731,   UDP Destination Port = 111
52    UDP Header and Datagram Length = 64 = ^x0040,   Checksum = ^xB6C0
53
54    50010C0A   714C1140   00008F17   54000045    0000    E..T....@.Lq...P
55    27E54C3C | C0B64000   6F00DB02 | 32010C0A    0010    ...2...o.@..<L.'
56    02000000   A0860100   02000000   00000000    0020    ................
57    00000000   00000000   00000000   03000000    0030    ................
58    06000000   01000000   A5860100   00000000    0040    ................
59                                     00000000    0050    ....
60
61 --------------------------------------------------------------------------------
62
63  */
64
65 /* Magic text to check for VMS-ness of file */
66 static const char vms_hdr_magic[]  =
67 { 'T', 'C', 'P', 'I', 'P', 't', 'r', 'a', 'c', 'e', ' '};
68 #define VMS_HDR_MAGIC_SIZE  (sizeof vms_hdr_magic  / sizeof vms_hdr_magic[0])
69
70 /* Magic text for start of packet */
71 #define vms_rec_magic vms_hdr_magic
72 #define VMS_REC_MAGIC_SIZE  (sizeof vms_rec_magic  / sizeof vms_rec_magic[0])
73
74 static gboolean vms_read(wtap *wth, int *err, long *data_offset);
75 static int vms_seek_read(wtap *wth, long seek_off,
76     union wtap_pseudo_header *pseudo_header, guint8 *pd, int len);
77 static gboolean parse_single_hex_dump_line(char* rec, guint8 *buf, long byte_offset, int in_off, int remaining_bytes);
78 static int parse_vms_hex_dump(FILE_T fh, int pkt_len, guint8* buf, int *err);
79 static int parse_vms_rec_hdr(wtap *wth, FILE_T fh, int *err);
80
81
82 /* Seeks to the beginning of the next packet, and returns the
83    byte offset.  Returns -1 on failure. */
84 /* XXX - Handle I/O errors. */
85 static long vms_seek_next_packet(wtap *wth)
86 {
87   int byte;
88   unsigned int level = 0;
89
90   while ((byte = file_getc(wth->fh)) != EOF) {
91     if ((level == 3) && (byte != vms_rec_magic[level]))
92       level += 2;  /* Accept TCPtrace as well as TCPIPtrace */
93     if (byte == vms_rec_magic[level]) {
94       level++;
95       if (level >= VMS_REC_MAGIC_SIZE) {
96           /* note: we're leaving file pointer right after the magic characters */
97         return file_tell(wth->fh) + 1;
98       }
99     } else {
100       level = 0;
101     }
102   }
103   return -1;
104 }
105
106 #define VMS_HEADER_LINES_TO_CHECK    200
107 #define VMS_LINE_LENGTH        240
108
109 /* Look through the first part of a file to see if this is
110  * a VMS trace file.
111  *
112  * Returns TRUE if it is, FALSE if it isn't.
113  *
114  * Leaves file handle at begining of line that contains the VMS Magic
115  * identifier.
116  */
117 static gboolean vms_check_file_type(wtap *wth)
118 {
119     char    buf[VMS_LINE_LENGTH];
120     int    line, byte;
121     unsigned int reclen, i, level;
122     long mpos;
123    
124     buf[VMS_LINE_LENGTH-1] = 0;
125
126     for (line = 0; line < VMS_HEADER_LINES_TO_CHECK; line++) {
127         mpos = file_tell(wth->fh);
128         if (file_gets(buf, VMS_LINE_LENGTH, wth->fh) != NULL) {
129
130             reclen = strlen(buf);
131             if (reclen < VMS_HDR_MAGIC_SIZE)
132                 continue;
133
134             level = 0;
135             for (i = 0; i < reclen; i++) {
136                 byte = buf[i];
137                 if ((level == 3) && (byte != vms_hdr_magic[level]))
138                     level += 2; /* Accept TCPIPtrace as well as TCPtrace */
139                 if (byte == vms_hdr_magic[level]) {
140                     level++;
141                     if (level >= VMS_HDR_MAGIC_SIZE) {
142                         file_seek(wth->fh, mpos, SEEK_SET);
143                         return TRUE;
144                     }
145                 }
146                 else
147                     level = 0;
148             }
149         }
150         else
151             return FALSE;
152     }
153     return FALSE;
154 }
155
156
157 /* XXX - return -1 on I/O error and actually do something with 'err'. */
158 int vms_open(wtap *wth, int *err)
159 {
160     /* Look for VMS header */
161     if (!vms_check_file_type(wth)) {
162         return 0;
163     }
164
165     wth->data_offset = 0;
166     wth->file_encap = WTAP_ENCAP_RAW_IP;
167     wth->file_type = WTAP_FILE_VMS;
168     wth->snapshot_length = 0; /* not known */
169     wth->subtype_read = vms_read;
170     wth->subtype_seek_read = vms_seek_read;
171
172     return 1;
173 }
174
175 /* Find the next packet and parse it; called from wtap_loop(). */
176 static gboolean vms_read(wtap *wth, int *err, long *data_offset)
177 {
178     long   offset = 0;
179     guint8    *buf;
180     int    pkt_len;
181
182     /* Find the next packet */
183     offset = vms_seek_next_packet(wth);
184     if (offset < 1) {
185         *err = 0;    /* XXX - assume, for now, that it's an EOF */
186         return FALSE;
187     }
188
189     /* Parse the header */
190     pkt_len = parse_vms_rec_hdr(wth, wth->fh, err);
191
192     if (pkt_len == -1)
193         return FALSE;
194
195     /* Make sure we have enough room for the packet */
196     buffer_assure_space(wth->frame_buffer, pkt_len);
197     buf = buffer_start_ptr(wth->frame_buffer);
198
199     /* Convert the ASCII hex dump to binary data */
200     parse_vms_hex_dump(wth->fh, pkt_len, buf, err);
201
202     wth->data_offset = offset;
203     *data_offset = offset;
204     return TRUE;
205 }
206
207 /* Used to read packets in random-access fashion */
208 static int
209 vms_seek_read (wtap *wth, long seek_off, union wtap_pseudo_header *pseudo_header,
210     guint8 *pd, int len)
211 {
212     int    pkt_len;
213     int    err;
214
215     file_seek(wth->random_fh, seek_off - 1, SEEK_SET);
216
217     pkt_len = parse_vms_rec_hdr(NULL, wth->random_fh, &err);
218
219     if (pkt_len != len) {
220         return -1;
221     }
222
223     parse_vms_hex_dump(wth->random_fh, pkt_len, pd, &err);
224
225     return 0;
226 }
227
228 /* isdumpline assumes that dump lines start with some spaces followed by a
229  * hex number.
230  */
231 static int
232 isdumpline( guchar *line )
233 {
234     int i = 0;
235
236     while (i<VMS_LINE_LENGTH && !isalnum(line[i]))
237         i++;
238
239     if (! isxdigit(line[i]))
240         return 0;
241
242     while (i<VMS_LINE_LENGTH && isxdigit(line[i]))
243         i++;
244
245     return isspace(line[i]);
246 }
247
248 /* Parses a packet record header. */
249 static int
250 parse_vms_rec_hdr(wtap *wth, FILE_T fh, int *err)
251 {
252     char    line[VMS_LINE_LENGTH];
253     int    num_items_scanned;
254     int    pkt_len = 0;
255     int    pktnum;
256     int    csec = 101;
257     struct tm time;
258     char mon[4] = {'J', 'A', 'N', 0};
259     guchar *p;
260     static guchar months[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
261
262     time.tm_year = 1970;
263     time.tm_hour = 1;
264     time.tm_min = 1;
265     time.tm_sec = 1;
266
267
268     /* Skip lines until one starts with a hex number */
269     do {
270         if (file_gets(line, VMS_LINE_LENGTH, fh) == NULL) {
271             *err = file_error(fh);
272             if ((*err == 0) && (csec != 101)) {
273                 *err = WTAP_ERR_SHORT_READ;
274             }
275             return -1;
276         }
277         if ((csec == 101) && (p = strstr(line, "packet "))
278             && (! strstr(line, "could not save "))) {
279             /* Find text in line starting with "packet ". */
280             num_items_scanned = sscanf(p,
281                                        "packet %d at %d-%3s-%d %d:%d:%d.%d",
282                                        &pktnum, &time.tm_mday, mon,
283                                        &time.tm_year, &time.tm_hour,
284                                        &time.tm_min, &time.tm_sec, &csec);
285
286             if (num_items_scanned != 8) {
287                 *err = WTAP_ERR_BAD_RECORD;
288                 return -1;
289             }
290         }
291         if ( (! pkt_len) && (p = strstr(line, "Length"))) {
292             p += sizeof("Length ");
293             while (*p && ! isdigit(*p))
294                 p++;
295
296             if ( !*p ) {
297                 *err = WTAP_ERR_BAD_RECORD;
298                 return -1;
299             }
300
301             pkt_len = atoi(p);
302             break;
303         }
304     } while (! isdumpline(line));
305
306     if (wth) {
307         p = strstr(months, mon);
308         if (p)
309             time.tm_mon = (p - months) / 3;
310         time.tm_year -= 1900;
311
312         wth->phdr.ts.tv_sec = mktime(&time);
313
314         wth->phdr.ts.tv_usec = csec * 10000;
315         wth->phdr.caplen = pkt_len;
316         wth->phdr.len = pkt_len;
317         wth->phdr.pkt_encap = WTAP_ENCAP_RAW_IP;
318     }
319
320     return pkt_len;
321 }
322
323 /* Converts ASCII hex dump to binary data */
324 static int
325 parse_vms_hex_dump(FILE_T fh, int pkt_len, guint8* buf, int *err)
326 {
327     guchar line[VMS_LINE_LENGTH];
328     int    i;
329     int    offset = 0;
330
331     for (i = 0; i < pkt_len; i += 16) {
332         if (file_gets(line, VMS_LINE_LENGTH, fh) == NULL) {
333             *err = file_error(fh);
334             if (*err == 0) {
335                 *err = WTAP_ERR_SHORT_READ;
336             }
337             return -1;
338         }
339         if (i == 0) {
340             while (! isdumpline(line)) /* advance to start of hex data */
341                 if (file_gets(line, VMS_LINE_LENGTH, fh) == NULL) {
342                     *err = file_error(fh);
343                     if (*err == 0) {
344                         *err = WTAP_ERR_SHORT_READ;
345                     }
346                     return -1;
347                 }
348             while (line[offset] && !isxdigit(line[offset]))
349                 offset++;
350         }
351         if (!parse_single_hex_dump_line(line, buf, i,
352                                         offset, pkt_len - i)) {
353             *err = WTAP_ERR_BAD_RECORD;
354             return -1;
355         }
356     }
357     /* Avoid TCPIPTRACE-W-BUFFERSFUL, TCPIPtrace could not save n packets.
358      * errors. */
359     file_gets(line, VMS_LINE_LENGTH, fh);
360     return 0;
361 }
362
363 /*
364           1         2         3         4
365 0123456789012345678901234567890123456789012345
366    50010C0A   A34C0640   00009017   2C000045    0000    E..,....@.L....P
367    00000000   14945E52   0A00DC02 | 32010C0A    0010    ...2....R^......
368        0000 | B4050402   00003496   00020260    0020    `....4........
369 */
370
371 #define START_POS    7
372 #define HEX_LENGTH    ((8 * 4) + 7) /* eight clumps of 4 bytes with 7 inner spaces */
373 /* Take a string representing one line from a hex dump and converts the
374  * text to binary data. We check the printed offset with the offset
375  * we are passed to validate the record. We place the bytes in the buffer
376  * at the specified offset.
377  *
378  * Returns TRUE if good hex dump, FALSE if bad.
379  */
380 static gboolean
381 parse_single_hex_dump_line(char* rec, guint8 *buf, long byte_offset,
382                int in_off, int remaining) {
383
384     int        i;
385     char        *s;
386     int        value;
387     static int offsets[16] = {39,37,35,33,28,26,24,22,17,15,13,11,6,4,2,0};
388     char lbuf[3] = {0,0,0};
389    
390
391     /* Get the byte_offset directly from the record */
392     s = rec;
393     value = strtoul(s + 45 + in_off, NULL, 16);
394    
395     if (value != byte_offset) {
396         return FALSE;
397     }
398
399     if (remaining > 16)
400         remaining = 16;
401
402     /* Read the octets right to left, as that is how they are displayed
403      * in VMS.
404      */
405
406     for (i = 0; i < remaining; i++) {
407         lbuf[0] = rec[offsets[i] + in_off];
408         lbuf[1] = rec[offsets[i] + 1 + in_off];
409
410         buf[byte_offset + i] = (guint8) strtoul(lbuf, NULL, 16);
411     }
412
413     return TRUE;
414 }