Update Free Software Foundation address.
[metze/wireshark/wip.git] / wiretap / ascendtext.c
1 /* ascendtext.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 "ascendtext.h"
29 #include "ascend-int.h"
30 #include "file_wrappers.h"
31 #include <wsutil/file_util.h>
32
33 #include <errno.h>
34
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38
39 #include <ctype.h>
40 #include <string.h>
41
42 /* Last updated: Feb 03 2005: Josh Bailey (joshbailey@lucent.com).
43
44    This module reads the text hex dump output of various TAOS
45    (Lucent/Ascend Max, Max TNT, APX, etc) debug commands, including:
46
47    * pridisplay         traces primary rate ISDN
48    * ether-display      traces Ethernet packets (dangerous! CPU intensive)
49    * wanopening, wandisplay, wannext, wandsess
50                         traces PPP or other WAN connections
51
52    Please see ascend.y for examples.
53
54    Detailed documentation on TAOS products is at http://support.lucent.com.
55
56    Support for other commands will be added on an ongoing basis. */
57
58 typedef struct _ascend_magic_string {
59   guint        type;
60   const gchar   *strptr;
61 } ascend_magic_string;
62
63 #define ASCEND_MAGIC_STRINGS    11
64 #define ASCEND_DATE             "Date:"
65
66 /* these magic strings signify the headers of a supported debug commands */
67 static const ascend_magic_string ascend_magic[] = {
68   { ASCEND_PFX_ISDN_X,  "PRI-XMIT-" },
69   { ASCEND_PFX_ISDN_R,  "PRI-RCV-" },
70   { ASCEND_PFX_WDS_X,   "XMIT-" },
71   { ASCEND_PFX_WDS_R,   "RECV-" },
72   { ASCEND_PFX_WDS_X,   "XMIT:" },
73   { ASCEND_PFX_WDS_R,   "RECV:" },
74   { ASCEND_PFX_WDS_X,   "PPP-OUT" },
75   { ASCEND_PFX_WDS_R,   "PPP-IN" },
76   { ASCEND_PFX_WDD,     ASCEND_DATE },
77   { ASCEND_PFX_WDD,     "WD_DIALOUT_DISP:" },
78   { ASCEND_PFX_ETHER,   "ETHER" },
79 };
80
81 typedef struct {
82         time_t inittime;
83         int adjusted;
84         gint64 next_packet_seek_start;
85 } ascend_t;
86
87 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
88         gint64 *data_offset);
89 static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
90         union wtap_pseudo_header *pseudo_head, guint8 *pd, int len,
91         int *err, gchar **err_info);
92
93 /* Seeks to the beginning of the next packet, and returns the
94    byte offset at which the header for that packet begins.
95    Returns -1 on failure. */
96 static gint64 ascend_seek(wtap *wth, int *err, gchar **err_info)
97 {
98   int byte;
99   gint64 date_off = -1, cur_off, packet_off;
100   size_t string_level[ASCEND_MAGIC_STRINGS];
101   guint string_i = 0, type = 0;
102   guint excessive_read_count = 262144;
103
104   memset(&string_level, 0, sizeof(string_level));
105
106   while (((byte = file_getc(wth->fh)) != EOF)) {
107     excessive_read_count--;
108
109     if (!excessive_read_count) {
110       *err = 0;
111       return -1;
112     }
113
114     for (string_i = 0; string_i < ASCEND_MAGIC_STRINGS; string_i++) {
115       const gchar *strptr = ascend_magic[string_i].strptr;
116       size_t len          = strlen(strptr);
117
118       if (byte == *(strptr + string_level[string_i])) {
119         string_level[string_i]++;
120         if (string_level[string_i] >= len) {
121           cur_off = file_tell(wth->fh);
122           if (cur_off == -1) {
123             /* Error. */
124             *err = file_error(wth->fh, err_info);
125             return -1;
126           }
127
128           /* Date: header is a special case. Remember the offset,
129              but keep looking for other headers. */
130           if (strcmp(strptr, ASCEND_DATE) == 0) {
131             date_off = cur_off - len;
132           } else {
133             if (date_off == -1) {
134               /* Back up over the header we just read; that's where a read
135                  of this packet should start. */
136               packet_off = cur_off - len;
137             } else {
138               /* This packet has a date/time header; a read of it should
139                  start at the beginning of *that* header. */
140               packet_off = date_off;
141             }
142
143             type = ascend_magic[string_i].type;
144             goto found;
145           }
146         }
147       } else {
148         string_level[string_i] = 0;
149       }
150     }
151   }
152
153   if (byte != EOF || file_eof(wth->fh)) {
154     /* Either we didn't find the offset, or we got an EOF. */
155     *err = 0;
156   } else {
157     /* We (presumably) got an error (there's no equivalent to "ferror()"
158        in zlib, alas, so we don't have a wrapper to check for an error). */
159     *err = file_error(wth->fh, err_info);
160   }
161   return -1;
162
163 found:
164   /*
165    * Move to where the read for this packet should start, and return
166    * that seek offset.
167    */
168   if (file_seek(wth->fh, packet_off, SEEK_SET, err) == -1)
169     return -1;
170
171   wth->pseudo_header.ascend.type = type;
172
173   return packet_off;
174 }
175
176 int ascend_open(wtap *wth, int *err, gchar **err_info)
177 {
178   gint64 offset;
179   ws_statb64 statbuf;
180   guint8 buf[ASCEND_MAX_PKT_LEN];
181   ascend_pkthdr header;
182   gint64 dummy_seek_start;
183   ascend_t *ascend;
184
185   /* We haven't yet allocated a data structure for our private stuff;
186      set the pointer to null, so that "ascend_seek()" knows not to
187      fill it in. */
188   wth->priv = NULL;
189
190   offset = ascend_seek(wth, err, err_info);
191   if (offset == -1) {
192     if (*err == 0)
193       return 0;
194     else
195       return -1;
196   }
197
198   /* Do a trial parse of the first packet just found to see if we might really have an Ascend file */
199   init_parse_ascend();
200   if (parse_ascend(wth->fh, buf, &wth->pseudo_header.ascend, &header,
201       &dummy_seek_start) != PARSED_RECORD) {
202     return 0;
203   }
204
205   wth->file_type = WTAP_FILE_ASCEND;
206
207   switch(wth->pseudo_header.ascend.type) {
208     case ASCEND_PFX_ISDN_X:
209     case ASCEND_PFX_ISDN_R:
210       wth->file_encap = WTAP_ENCAP_ISDN;
211       break;
212
213     case ASCEND_PFX_ETHER:
214       wth->file_encap = WTAP_ENCAP_ETHERNET;
215       break;
216
217     default:
218       wth->file_encap = WTAP_ENCAP_ASCEND;
219   }
220
221   wth->snapshot_length = ASCEND_MAX_PKT_LEN;
222   wth->subtype_read = ascend_read;
223   wth->subtype_seek_read = ascend_seek_read;
224   ascend = (ascend_t *)g_malloc(sizeof(ascend_t));
225   wth->priv = (void *)ascend;
226
227   /* The first packet we want to read is the one that "ascend_seek()"
228      just found; start searching for it at the offset at which it
229      found it. */
230   ascend->next_packet_seek_start = offset;
231
232   /* MAXen and Pipelines report the time since reboot.  In order to keep
233      from reporting packet times near the epoch, we subtract the first
234      packet's timestamp from the capture file's ctime, which gives us an
235      offset that we can apply to each packet.
236    */
237   if (wtap_fstat(wth, &statbuf, err) == -1) {
238     g_free(ascend);
239     return -1;
240   }
241   ascend->inittime = statbuf.st_ctime;
242   ascend->adjusted = 0;
243   wth->tsprecision = WTAP_FILE_TSPREC_USEC;
244
245   init_parse_ascend();
246
247   return 1;
248 }
249
250 static void config_pseudo_header(union wtap_pseudo_header *pseudo_head)
251 {
252   switch(pseudo_head->ascend.type) {
253     case ASCEND_PFX_ISDN_X:
254       pseudo_head->isdn.uton = TRUE;
255       pseudo_head->isdn.channel = 0;
256       break;
257
258     case ASCEND_PFX_ISDN_R:
259       pseudo_head->isdn.uton = FALSE;
260       pseudo_head->isdn.channel = 0;
261       break;
262
263     case ASCEND_PFX_ETHER:
264       pseudo_head->eth.fcs_len = 0;
265       break;
266   }
267 }
268
269 /* Read the next packet; called from wtap_read(). */
270 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
271         gint64 *data_offset)
272 {
273   ascend_t *ascend = (ascend_t *)wth->priv;
274   gint64 offset;
275   guint8 *buf = buffer_start_ptr(wth->frame_buffer);
276   ascend_pkthdr header;
277
278   /* parse_ascend() will advance the point at which to look for the next
279      packet's header, to just after the last packet's header (ie. at the
280      start of the last packet's data). We have to get past the last
281      packet's header because we might mistake part of it for a new header. */
282   if (file_seek(wth->fh, ascend->next_packet_seek_start,
283                 SEEK_SET, err) == -1)
284     return FALSE;
285
286     offset = ascend_seek(wth, err, err_info);
287     if (offset == -1)
288       return FALSE;
289   if (parse_ascend(wth->fh, buf, &wth->pseudo_header.ascend, &header,
290       &(ascend->next_packet_seek_start)) != PARSED_RECORD) {
291     *err = WTAP_ERR_BAD_FILE;
292     *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
293     return FALSE;
294   }
295
296   buffer_assure_space(wth->frame_buffer, wth->snapshot_length);
297
298   config_pseudo_header(&wth->pseudo_header);
299
300   if (! ascend->adjusted) {
301     ascend->adjusted = 1;
302     if (header.start_time != 0) {
303       /*
304        * Capture file contained a date and time.
305        * We do this only if this is the very first packet we've seen -
306        * i.e., if "ascend->adjusted" is false - because
307        * if we get a date and time after the first packet, we can't
308        * go back and adjust the time stamps of the packets we've already
309        * processed, and basing the time stamps of this and following
310        * packets on the time stamp from the file text rather than the
311        * ctime of the capture file means times before this and after
312        * this can't be compared.
313        */
314       ascend->inittime = header.start_time;
315     }
316     if (ascend->inittime > header.secs)
317       ascend->inittime -= header.secs;
318   }
319   wth->phdr.presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
320   wth->phdr.ts.secs = header.secs + ascend->inittime;
321   wth->phdr.ts.nsecs = header.usecs * 1000;
322   wth->phdr.caplen = header.caplen;
323   wth->phdr.len = header.len;
324
325   *data_offset = offset;
326   return TRUE;
327 }
328
329 static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
330         union wtap_pseudo_header *pseudo_head, guint8 *pd, int len _U_,
331         int *err, gchar **err_info)
332 {
333   ascend_t *ascend = (ascend_t *)wth->priv;
334
335   if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
336     return FALSE;
337   if (parse_ascend(wth->random_fh, pd, &pseudo_head->ascend, NULL,
338       &(ascend->next_packet_seek_start)) != PARSED_RECORD) {
339     *err = WTAP_ERR_BAD_FILE;
340     *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
341     return FALSE;
342   }
343
344   config_pseudo_header(pseudo_head);
345   return TRUE;
346 }