Added FT_ABSOLUTE_TIME and FT_RELATIVE_TIME to custom columns.
[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 #include "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-grammar.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 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
82         gint64 *data_offset);
83 static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
84         union wtap_pseudo_header *pseudo_head, guint8 *pd, int len,
85         int *err, gchar **err_info);
86 static void ascend_close(wtap *wth);
87
88 /* Seeks to the beginning of the next packet, and returns the
89    byte offset at which the header for that packet begins.
90    Returns -1 on failure. */
91 static gint64 ascend_seek(wtap *wth, int *err)
92 {
93   int byte;
94   gint64 date_off = -1, cur_off, packet_off;
95   guint string_level[ASCEND_MAGIC_STRINGS];
96   guint string_i = 0, type = 0;
97   guint excessive_read_count = 262144;
98
99   memset(&string_level, 0, sizeof(string_level));
100
101   while (((byte = file_getc(wth->fh)) != EOF)) {
102     excessive_read_count--;
103   
104     if (!excessive_read_count) {
105       return -1;
106     }
107
108     for (string_i = 0; string_i < ASCEND_MAGIC_STRINGS; string_i++) {
109       const gchar *strptr = ascend_magic[string_i].strptr;
110       guint len           = strlen(strptr);
111
112       if (byte == *(strptr + string_level[string_i])) {
113         string_level[string_i]++;
114         if (string_level[string_i] >= len) {
115           cur_off = file_tell(wth->fh);
116           if (cur_off == -1) {
117             /* Error. */
118             *err = file_error(wth->fh);
119             return -1;
120           }
121
122           /* Date: header is a special case. Remember the offset,
123              but keep looking for other headers. */
124           if (strcmp(strptr, ASCEND_DATE) == 0) {
125             date_off = cur_off - len;
126           } else {
127             if (date_off == -1) { 
128               /* Back up over the header we just read; that's where a read
129                  of this packet should start. */
130               packet_off = cur_off - len;
131             } else {
132               /* This packet has a date/time header; a read of it should
133                  start at the beginning of *that* header. */
134               packet_off = date_off;
135             }
136
137             type = ascend_magic[string_i].type;
138             goto found;
139           }
140         }
141       } else {
142         string_level[string_i] = 0;
143       }
144     }
145   }
146
147   if (byte != EOF || file_eof(wth->fh)) {
148     /* Either we didn't find the offset, or we got an EOF. */
149     *err = 0;
150   } else {
151     /* We (presumably) got an error (there's no equivalent to "ferror()"
152        in zlib, alas, so we don't have a wrapper to check for an error). */
153     *err = file_error(wth->fh);
154   }
155   return -1;
156
157 found:
158   /*
159    * Move to where the read for this packet should start, and return
160    * that seek offset.
161    */
162   if (file_seek(wth->fh, packet_off, SEEK_SET, err) == -1)
163     return -1;
164
165   wth->pseudo_header.ascend.type = type;
166
167   return packet_off;
168 }
169
170 int ascend_open(wtap *wth, int *err, gchar **err_info _U_)
171 {
172   gint64 offset;
173   struct stat statbuf;
174   guint8 buf[ASCEND_MAX_PKT_LEN];
175   ascend_pkthdr header;
176   gint64 dummy_seek_start;
177
178   /* We haven't yet allocated a data structure for our private stuff;
179      set the pointer to null, so that "ascend_seek()" knows not to
180      fill it in. */
181   wth->capture.ascend = NULL;
182
183   offset = ascend_seek(wth, err);
184   if (offset == -1) {
185     if (*err == 0)
186       return 0;
187     else
188       return -1;
189   }
190
191   /* Do a trial parse of the first packet just found to see if we might really have an Ascend file */
192   init_parse_ascend();
193   if (! parse_ascend(wth->fh, buf, &wth->pseudo_header.ascend, &header, &dummy_seek_start)) {
194     return 0;
195   }
196
197   wth->data_offset = offset;
198   wth->file_type = WTAP_FILE_ASCEND;
199
200   switch(wth->pseudo_header.ascend.type) {
201     case ASCEND_PFX_ISDN_X:
202     case ASCEND_PFX_ISDN_R:
203       wth->file_encap = WTAP_ENCAP_ISDN;
204       break;
205
206     case ASCEND_PFX_ETHER:
207       wth->file_encap = WTAP_ENCAP_ETHERNET;
208       break;
209
210     default:
211       wth->file_encap = WTAP_ENCAP_ASCEND;
212   }
213
214   wth->snapshot_length = ASCEND_MAX_PKT_LEN;
215   wth->subtype_read = ascend_read;
216   wth->subtype_seek_read = ascend_seek_read;
217   wth->subtype_close = ascend_close;
218   wth->capture.ascend = g_malloc(sizeof(ascend_t));
219
220   /* The first packet we want to read is the one that "ascend_seek()"
221      just found; start searching for it at the offset at which it
222      found it. */
223   wth->capture.ascend->next_packet_seek_start = offset;
224
225   /* MAXen and Pipelines report the time since reboot.  In order to keep
226      from reporting packet times near the epoch, we subtract the first
227      packet's timestamp from the capture file's ctime, which gives us an
228      offset that we can apply to each packet.
229    */
230   if (fstat(wth->fd, &statbuf) == -1) {
231     *err = errno;
232     g_free(wth->capture.ascend);
233     return -1;
234   }
235   wth->capture.ascend->inittime = statbuf.st_ctime;
236   wth->capture.ascend->adjusted = 0;
237   wth->tsprecision = WTAP_FILE_TSPREC_USEC;
238
239   init_parse_ascend();
240
241   return 1;
242 }
243
244 static void config_pseudo_header(union wtap_pseudo_header *pseudo_head)
245 {
246   switch(pseudo_head->ascend.type) {
247     case ASCEND_PFX_ISDN_X:
248       pseudo_head->isdn.uton = TRUE;
249       pseudo_head->isdn.channel = 0;
250       break;
251
252     case ASCEND_PFX_ISDN_R:
253       pseudo_head->isdn.uton = FALSE;
254       pseudo_head->isdn.channel = 0;
255       break;
256
257     case ASCEND_PFX_ETHER:
258       pseudo_head->eth.fcs_len = 0;
259       break;
260   }
261 }
262
263 /* Read the next packet; called from wtap_read(). */
264 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
265         gint64 *data_offset)
266 {
267   gint64 offset;
268   guint8 *buf = buffer_start_ptr(wth->frame_buffer);
269   ascend_pkthdr header;
270
271   /* parse_ascend() will advance the point at which to look for the next
272      packet's header, to just after the last packet's header (ie. at the
273      start of the last packet's data). We have to get past the last
274      packet's header because we might mistake part of it for a new header. */
275   if (file_seek(wth->fh, wth->capture.ascend->next_packet_seek_start,
276                 SEEK_SET, err) == -1)
277     return FALSE;
278
279     offset = ascend_seek(wth, err);
280     if (offset == -1)
281       return FALSE;
282   if (! parse_ascend(wth->fh, buf, &wth->pseudo_header.ascend, &header, &(wth->capture.ascend->next_packet_seek_start))) {
283     *err = WTAP_ERR_BAD_RECORD;
284     *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
285     return FALSE;
286   }
287
288   buffer_assure_space(wth->frame_buffer, wth->snapshot_length);
289
290   config_pseudo_header(&wth->pseudo_header);
291
292   if (! wth->capture.ascend->adjusted) {
293     wth->capture.ascend->adjusted = 1;
294     if (header.start_time != 0) {
295       /*
296        * Capture file contained a date and time.
297        * We do this only if this is the very first packet we've seen -
298        * i.e., if "wth->capture.ascend->adjusted" is false - because
299        * if we get a date and time after the first packet, we can't
300        * go back and adjust the time stamps of the packets we've already
301        * processed, and basing the time stamps of this and following
302        * packets on the time stamp from the file text rather than the
303        * ctime of the capture file means times before this and after
304        * this can't be compared.
305        */
306       wth->capture.ascend->inittime = header.start_time;
307     }
308     if (wth->capture.ascend->inittime > header.secs)
309       wth->capture.ascend->inittime -= header.secs;
310   }
311   wth->phdr.ts.secs = header.secs + wth->capture.ascend->inittime;
312   wth->phdr.ts.nsecs = header.usecs * 1000;
313   wth->phdr.caplen = header.caplen;
314   wth->phdr.len = header.len;
315   wth->data_offset = offset;
316
317   *data_offset = offset;
318   return TRUE;
319 }
320
321 static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
322         union wtap_pseudo_header *pseudo_head, guint8 *pd, int len,
323         int *err, gchar **err_info)
324 {
325   /* don't care for length. */
326   (void) len;
327
328   if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
329     return FALSE;
330   if (! parse_ascend(wth->random_fh, pd, &pseudo_head->ascend, NULL, &(wth->capture.ascend->next_packet_seek_start))) {
331     *err = WTAP_ERR_BAD_RECORD;
332     *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
333     return FALSE;
334   }
335
336   config_pseudo_header(pseudo_head);
337   return TRUE;
338 }
339
340 static void ascend_close(wtap *wth)
341 {
342   g_free(wth->capture.ascend);
343 }