Improve the heuristic for Frame Relay, and move that heuristic after the
[obnox/wireshark/wip.git] / wiretap / ascend.c
1 /* ascend.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 #include "wtap-int.h"
27 #include "buffer.h"
28 #include "ascend.h"
29 #include "ascend-int.h"
30 #include "file_wrappers.h"
31
32 #include <errno.h>
33
34 #ifdef HAVE_SYS_STAT_H
35 #include <sys/stat.h>
36 #endif
37
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41
42 #include <ctype.h>
43 #include <string.h>
44
45 /* This module reads the output of the 'wandsession', 'wannext',
46    'wandisplay', and similar commands available on Lucent/Ascend access
47    equipment.  The output is text, with a header line followed by the
48    packet data.  Usage instructions for the commands can be found by
49    searching http://aos.ascend.com .  Ascend likes to move their pages
50    around quite a bit, otherwise I'd put a more specific URL here.
51
52    Example 'wandsess' output data:
53
54 RECV-iguana:241:(task: B02614C0, time: 1975432.85) 49 octets @ 8003BD94
55   [0000]: FF 03 00 3D C0 06 CA 22 2F 45 00 00 28 6A 3B 40
56   [0010]: 00 3F 03 D7 37 CE 41 62 12 CF 00 FB 08 20 27 00
57   [0020]: 50 E4 08 DD D7 7C 4C 71 92 50 10 7D 78 67 C8 00
58   [0030]: 00
59 XMIT-iguana:241:(task: B04E12C0, time: 1975432.85) 53 octets @ 8009EB16
60   [0000]: FF 03 00 3D C0 09 1E 31 21 45 00 00 2C 2D BD 40
61   [0010]: 00 7A 06 D8 B1 CF 00 FB 08 CE 41 62 12 00 50 20
62   [0020]: 29 7C 4C 71 9C 9A 6A 93 A4 60 12 22 38 3F 10 00
63   [0030]: 00 02 04 05 B4
64
65     Example 'wdd' output data:
66
67 Date: 01/12/1990.  Time: 12:22:33
68 Cause an attempt to place call to 14082750382
69 WD_DIALOUT_DISP: chunk 2515EE type IP.
70 (task: 251790, time: 994953.28) 44 octets @ 2782B8
71   [0000]: 00 C0 7B 71 45 6C 00 60 08 16 AA 51 08 00 45 00
72   [0010]: 00 2C 66 1C 40 00 80 06 53 F6 AC 14 00 18 CC 47
73   [0020]: C8 45 0A 31 00 50 3B D9 5B 75 00 00
74
75     (note that the capture whence this came dates back to January
76     *1999*; I presume that either the person who sent it to me
77     hadn't bothered keeping its internal clock set, or that its
78     internal clock or the date it displays in those messages
79     is only loosely connected to reality)
80
81   Note that a maximum of eight rows will be displayed (for a maximum of
82   128 bytes), no matter what the octet count is.
83
84   When reading a packet, the module prepends an ascend_pkt_hdr to the
85   data.
86
87  */
88
89 /* How far into the file we should look for packet headers */
90 #define ASCEND_MAX_SEEK 100000
91
92 /* XXX  Should we replace this with a more generalized array? */
93 /* Magic numbers for Ascend wandsession/wanopening/ether-display data */
94 static const char ascend_xmagic[]  = { 'X', 'M', 'I', 'T', '-' };
95 static const char ascend_rmagic[]  = { 'R', 'E', 'C', 'V', '-' };
96 static const char ascend_w1magic[] = { 'D', 'a', 't', 'e', ':',  };
97 static const char ascend_w2magic[] = { 'W', 'D', '_', 'D', 'I', 'A', 'L', 'O', 'U', 'T', '_', 'D', 'I', 'S', 'P', ':' };
98
99 #define ASCEND_X_SIZE  (sizeof ascend_xmagic  / sizeof ascend_xmagic[0])
100 #define ASCEND_R_SIZE  (sizeof ascend_rmagic  / sizeof ascend_rmagic[0])
101 #define ASCEND_W1_SIZE (sizeof ascend_w1magic / sizeof ascend_w1magic[0])
102 #define ASCEND_W2_SIZE (sizeof ascend_w2magic / sizeof ascend_w2magic[0])
103
104 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
105         long *data_offset);
106 static gboolean ascend_seek_read(wtap *wth, long seek_off,
107         union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
108         int *err, gchar **err_info);
109 static void ascend_close(wtap *wth);
110
111 /* Seeks to the beginning of the next packet, and returns the
112    byte offset at which the heade for that packet begins.
113    Returns -1 on failure.
114
115    If it finds a packet, then, if "wth->capture.ascend" is non-null,
116    it sets "wth->capture.ascend->next_packet_seek_start" to the point
117    at which the seek pointer should be set before this routine is called
118    to find the packet *after* the packet it finds. */
119 static long ascend_seek(wtap *wth, int max_seek, int *err)
120 {
121   int byte, bytes_read = 0;
122   long date_off = -1, cur_off, packet_off;
123   unsigned int r_level = 0, x_level = 0, w1_level = 0, w2_level = 0;
124
125   while (((byte = file_getc(wth->fh)) != EOF) && bytes_read < max_seek) {
126     if (byte == ascend_xmagic[x_level]) {
127       x_level++;
128       if (x_level >= ASCEND_X_SIZE) {
129         /* At what offset are we now? */
130         cur_off = file_tell(wth->fh);
131         if (cur_off == -1) {
132           /* Error. */
133           *err = file_error(wth->fh);
134           return -1;
135         }
136
137         /* Back up over the header we just read; that's where a read
138            of this packet should start. */
139         packet_off = cur_off - ASCEND_X_SIZE;
140         goto found;
141       }
142     } else {
143       x_level = 0;
144     }
145     if (byte == ascend_rmagic[r_level]) {
146       r_level++;
147       if (r_level >= ASCEND_R_SIZE) {
148         /* At what offset are we now? */
149         cur_off = file_tell(wth->fh);
150         if (cur_off == -1) {
151           /* Error. */
152           *err = file_error(wth->fh);
153           return -1;
154         }
155
156         /* Back up over the header we just read; that's where a read
157            of this packet should start. */
158         packet_off = cur_off - ASCEND_R_SIZE;
159         goto found;
160       }
161     } else {
162       r_level = 0;
163     }
164     if (byte == ascend_w1magic[w1_level]) {
165       w1_level++;
166       if (w1_level >= ASCEND_W1_SIZE) {
167         /* Get the offset at which the "Date:" header started. */
168         cur_off = file_tell(wth->fh);
169         if (cur_off == -1) {
170           /* Error. */
171           *err = file_error(wth->fh);
172           return -1;
173         }
174
175         date_off = cur_off - ASCEND_W1_SIZE;
176       }
177     } else {
178       w1_level = 0;
179     }
180     if (byte == ascend_w2magic[w2_level]) {
181       w2_level++;
182       if (w2_level >= ASCEND_W2_SIZE) {
183         /* At what offset are we now? */
184         cur_off = file_tell(wth->fh);
185         if (cur_off == -1) {
186           /* Error. */
187           *err = file_error(wth->fh);
188           return -1;
189         }
190
191         if (date_off != -1) {
192           /* This packet has a date/time header; a read of it should
193              start at the beginning of *that* header. */
194           packet_off = date_off;
195         } else {
196           /* This packet has only a per-packet header.
197              Back up over that header, which we just read; that's where
198              a read of this packet should start. */
199           packet_off = cur_off - ASCEND_W2_SIZE;
200         }
201         goto found;
202       }
203     } else {
204       w2_level = 0;
205     }
206     bytes_read++;
207   }
208   if (byte != EOF || file_eof(wth->fh)) {
209     /* Either we didn't find the offset, or we got an EOF. */
210     *err = 0;
211   } else {
212     /* We (presumably) got an error (there's no equivalent to "ferror()"
213        in zlib, alas, so we don't have a wrapper to check for an error). */
214     *err = file_error(wth->fh);
215   }
216   return -1;
217
218 found:
219   /*
220    * The search for the packet after this one should start right
221    * after the header for this packet.  (Ideally, it should
222    * start after the *data* for this one, but we haven't
223    * read that yet.)
224    */
225   if (wth->capture.ascend != NULL)
226     wth->capture.ascend->next_packet_seek_start = cur_off + 1;
227
228   /*
229    * Move to where the read for this packet should start, and return
230    * that seek offset.
231    */
232   if (file_seek(wth->fh, packet_off, SEEK_SET, err) == -1)
233     return -1;
234   return packet_off;
235 }
236
237 int ascend_open(wtap *wth, int *err, gchar **err_info _U_)
238 {
239   long offset;
240   struct stat statbuf;
241
242   /* We haven't yet allocated a data structure for our private stuff;
243      set the pointer to null, so that "ascend_seek()" knows not to
244      fill it in. */
245   wth->capture.ascend = NULL;
246
247   offset = ascend_seek(wth, ASCEND_MAX_SEEK, err);
248   if (offset == -1) {
249     if (*err == 0)
250       return 0;
251     else
252       return -1;
253   }
254
255   wth->data_offset = offset;
256   wth->file_encap = WTAP_ENCAP_ASCEND;
257   wth->file_type = WTAP_FILE_ASCEND;
258   wth->snapshot_length = ASCEND_MAX_PKT_LEN;
259   wth->subtype_read = ascend_read;
260   wth->subtype_seek_read = ascend_seek_read;
261   wth->subtype_close = ascend_close;
262   wth->capture.ascend = g_malloc(sizeof(ascend_t));
263
264   /* The first packet we want to read is the one that "ascend_seek()"
265      just found; start searching for it at the offset at which it
266      found it. */
267   wth->capture.ascend->next_packet_seek_start = offset;
268
269   /* MAXen and Pipelines report the time since reboot.  In order to keep
270      from reporting packet times near the epoch, we subtract the first
271      packet's timestamp from the capture file's ctime, which gives us an
272      offset that we can apply to each packet.
273    */
274   if (fstat(wtap_fd(wth), &statbuf) == -1) {
275     *err = errno;
276     g_free(wth->capture.ascend);
277     return -1;
278   }
279   wth->capture.ascend->inittime = statbuf.st_ctime;
280   wth->capture.ascend->adjusted = 0;
281
282   init_parse_ascend();
283
284   return 1;
285 }
286
287 /* Read the next packet; called from wtap_loop(). */
288 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
289         long *data_offset)
290 {
291   long offset;
292   guint8 *buf = buffer_start_ptr(wth->frame_buffer);
293   ascend_pkthdr header;
294
295   /* (f)lex reads large chunks of the file into memory, so file_tell() doesn't
296      give us the correct location of the packet.  Instead, we seek to the
297      offset after the header of the previous packet and try to find the next
298      packet.  */
299   if (file_seek(wth->fh, wth->capture.ascend->next_packet_seek_start,
300                 SEEK_SET, err) == -1)
301     return FALSE;
302   offset = ascend_seek(wth, ASCEND_MAX_SEEK, err);
303   if (offset == -1)
304     return FALSE;
305   if (! parse_ascend(wth->fh, buf, &wth->pseudo_header.ascend, &header, 0)) {
306     *err = WTAP_ERR_BAD_RECORD;
307     *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
308     return FALSE;
309   }
310
311   buffer_assure_space(wth->frame_buffer, wth->snapshot_length);
312
313   if (! wth->capture.ascend->adjusted) {
314     wth->capture.ascend->adjusted = 1;
315     if (header.start_time != 0) {
316       /*
317        * Capture file contained a date and time.
318        * We do this only if this is the very first packet we've seen -
319        * i.e., if "wth->capture.ascend->adjusted" is false - because
320        * if we get a date and time after the first packet, we can't
321        * go back and adjust the time stamps of the packets we've already
322        * processed, and basing the time stamps of this and following
323        * packets on the time stamp from the file text rather than the
324        * ctime of the capture file means times before this and after
325        * this can't be compared.
326        */
327       wth->capture.ascend->inittime = header.start_time;
328     }
329     if (wth->capture.ascend->inittime > header.secs)
330       wth->capture.ascend->inittime -= header.secs;
331   }
332   wth->phdr.ts.tv_sec = header.secs + wth->capture.ascend->inittime;
333   wth->phdr.ts.tv_usec = header.usecs;
334   wth->phdr.caplen = header.caplen;
335   wth->phdr.len = header.len;
336   wth->data_offset = offset;
337
338   *data_offset = offset;
339   return TRUE;
340 }
341
342 static gboolean ascend_seek_read(wtap *wth, long seek_off,
343         union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
344         int *err, gchar **err_info)
345 {
346   if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
347     return FALSE;
348   if (! parse_ascend(wth->random_fh, pd, &pseudo_header->ascend, NULL, len)) {
349     *err = WTAP_ERR_BAD_RECORD;
350     *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
351     return FALSE;
352   }
353   return TRUE;
354 }
355
356 static void ascend_close(wtap *wth)
357 {
358   g_free(wth->capture.ascend);
359 }