Fix a wrong name in a debug print statement.
[obnox/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., 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 "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->data_offset = offset;
206   wth->file_type = WTAP_FILE_ASCEND;
207
208   switch(wth->pseudo_header.ascend.type) {
209     case ASCEND_PFX_ISDN_X:
210     case ASCEND_PFX_ISDN_R:
211       wth->file_encap = WTAP_ENCAP_ISDN;
212       break;
213
214     case ASCEND_PFX_ETHER:
215       wth->file_encap = WTAP_ENCAP_ETHERNET;
216       break;
217
218     default:
219       wth->file_encap = WTAP_ENCAP_ASCEND;
220   }
221
222   wth->snapshot_length = ASCEND_MAX_PKT_LEN;
223   wth->subtype_read = ascend_read;
224   wth->subtype_seek_read = ascend_seek_read;
225   ascend = (ascend_t *)g_malloc(sizeof(ascend_t));
226   wth->priv = (void *)ascend;
227
228   /* The first packet we want to read is the one that "ascend_seek()"
229      just found; start searching for it at the offset at which it
230      found it. */
231   ascend->next_packet_seek_start = offset;
232
233   /* MAXen and Pipelines report the time since reboot.  In order to keep
234      from reporting packet times near the epoch, we subtract the first
235      packet's timestamp from the capture file's ctime, which gives us an
236      offset that we can apply to each packet.
237    */
238   if (wtap_fstat(wth, &statbuf, err) == -1) {
239     g_free(ascend);
240     return -1;
241   }
242   ascend->inittime = statbuf.st_ctime;
243   ascend->adjusted = 0;
244   wth->tsprecision = WTAP_FILE_TSPREC_USEC;
245
246   init_parse_ascend();
247
248   return 1;
249 }
250
251 static void config_pseudo_header(union wtap_pseudo_header *pseudo_head)
252 {
253   switch(pseudo_head->ascend.type) {
254     case ASCEND_PFX_ISDN_X:
255       pseudo_head->isdn.uton = TRUE;
256       pseudo_head->isdn.channel = 0;
257       break;
258
259     case ASCEND_PFX_ISDN_R:
260       pseudo_head->isdn.uton = FALSE;
261       pseudo_head->isdn.channel = 0;
262       break;
263
264     case ASCEND_PFX_ETHER:
265       pseudo_head->eth.fcs_len = 0;
266       break;
267   }
268 }
269
270 /* Read the next packet; called from wtap_read(). */
271 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
272         gint64 *data_offset)
273 {
274   ascend_t *ascend = (ascend_t *)wth->priv;
275   gint64 offset;
276   guint8 *buf = buffer_start_ptr(wth->frame_buffer);
277   ascend_pkthdr header;
278
279   /* parse_ascend() will advance the point at which to look for the next
280      packet's header, to just after the last packet's header (ie. at the
281      start of the last packet's data). We have to get past the last
282      packet's header because we might mistake part of it for a new header. */
283   if (file_seek(wth->fh, ascend->next_packet_seek_start,
284                 SEEK_SET, err) == -1)
285     return FALSE;
286
287     offset = ascend_seek(wth, err, err_info);
288     if (offset == -1)
289       return FALSE;
290   if (parse_ascend(wth->fh, buf, &wth->pseudo_header.ascend, &header,
291       &(ascend->next_packet_seek_start)) != PARSED_RECORD) {
292     *err = WTAP_ERR_BAD_RECORD;
293     *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
294     return FALSE;
295   }
296
297   buffer_assure_space(wth->frame_buffer, wth->snapshot_length);
298
299   config_pseudo_header(&wth->pseudo_header);
300
301   if (! ascend->adjusted) {
302     ascend->adjusted = 1;
303     if (header.start_time != 0) {
304       /*
305        * Capture file contained a date and time.
306        * We do this only if this is the very first packet we've seen -
307        * i.e., if "ascend->adjusted" is false - because
308        * if we get a date and time after the first packet, we can't
309        * go back and adjust the time stamps of the packets we've already
310        * processed, and basing the time stamps of this and following
311        * packets on the time stamp from the file text rather than the
312        * ctime of the capture file means times before this and after
313        * this can't be compared.
314        */
315       ascend->inittime = header.start_time;
316     }
317     if (ascend->inittime > header.secs)
318       ascend->inittime -= header.secs;
319   }
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   wth->data_offset = offset;
325
326   *data_offset = offset;
327   return TRUE;
328 }
329
330 static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
331         union wtap_pseudo_header *pseudo_head, guint8 *pd, int len _U_,
332         int *err, gchar **err_info)
333 {
334   ascend_t *ascend = (ascend_t *)wth->priv;
335
336   if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
337     return FALSE;
338   if (parse_ascend(wth->random_fh, pd, &pseudo_head->ascend, NULL,
339       &(ascend->next_packet_seek_start)) != PARSED_RECORD) {
340     *err = WTAP_ERR_BAD_RECORD;
341     *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
342     return FALSE;
343   }
344
345   config_pseudo_header(pseudo_head);
346   return TRUE;
347 }