First checkin of asn2eth generated gsm ss dissector - work in progress not ready...
[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 /* Last updated: Feb 03 2005: Josh Bailey (joshbailey@lucent.com).
46
47    This module reads the text hex dump output of various TAOS
48    (Lucent/Ascend Max, Max TNT, APX, etc) debug commands, including:
49
50    * pridisplay         traces primary rate ISDN
51    * ether-display      traces Ethernet packets (dangerous! CPU intensive)
52    * wanopening, wandisplay, wannext, wandsess
53                         traces PPP or other WAN connections
54
55    Please see ascend-grammar.y for examples.
56
57    Detailed documentation on TAOS products is at http://support.lucent.com.
58
59    Support for other commands will be added on an ongoing basis. */
60
61 /* How far into the file we should look for packet headers */
62 #define ASCEND_MAX_SEEK 100000
63
64 typedef struct _ascend_magic_string {
65   guint        type;
66   const gchar   *strptr; 
67 } ascend_magic_string;
68
69 #define ASCEND_MAGIC_STRINGS    11
70 #define ASCEND_DATE             "Date:"
71
72 /* these magic strings signify the headers of a supported debug commands */
73 static const ascend_magic_string ascend_magic[] = {
74   { ASCEND_PFX_ISDN_X,  "PRI-XMIT-" },
75   { ASCEND_PFX_ISDN_R,  "PRI-RCV-" },
76   { ASCEND_PFX_WDS_X,   "XMIT-" },
77   { ASCEND_PFX_WDS_R,   "RECV-" },
78   { ASCEND_PFX_WDS_X,   "XMIT:" },
79   { ASCEND_PFX_WDS_R,   "RECV:" },
80   { ASCEND_PFX_WDS_X,   "PPP-OUT" },
81   { ASCEND_PFX_WDS_R,   "PPP-IN" },
82   { ASCEND_PFX_WDD,     ASCEND_DATE },
83   { ASCEND_PFX_WDD,     "WD_DIALOUT_DISP:" },
84   { ASCEND_PFX_ETHER,   "ETHER" },
85 };
86
87 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
88         long *data_offset);
89 static gboolean ascend_seek_read(wtap *wth, long seek_off,
90         union wtap_pseudo_header *pseudo_head, guint8 *pd, int len,
91         int *err, gchar **err_info);
92 static void ascend_close(wtap *wth);
93
94 /* Seeks to the beginning of the next packet, and returns the
95    byte offset at which the header for that packet begins.
96    Returns -1 on failure. */
97 static long ascend_seek(wtap *wth, int max_seek, int *err)
98 {
99   int byte, bytes_read = 0;
100   long date_off = -1, cur_off, packet_off;
101   guint string_level[ASCEND_MAGIC_STRINGS];
102   guint string_i = 0, type = 0;
103
104   memset(&string_level, 0, sizeof(string_level));
105
106   while (((byte = file_getc(wth->fh)) != EOF) && bytes_read < max_seek) {
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   long offset;
173   struct stat statbuf;
174
175   /* We haven't yet allocated a data structure for our private stuff;
176      set the pointer to null, so that "ascend_seek()" knows not to
177      fill it in. */
178   wth->capture.ascend = NULL;
179
180   offset = ascend_seek(wth, ASCEND_MAX_SEEK, err);
181   if (offset == -1) {
182     if (*err == 0)
183       return 0;
184     else
185       return -1;
186   }
187
188   wth->data_offset = offset;
189   wth->file_type = WTAP_FILE_ASCEND;
190
191   switch(wth->pseudo_header.ascend.type) {
192     case ASCEND_PFX_ISDN_X:
193     case ASCEND_PFX_ISDN_R:
194       wth->file_encap = WTAP_ENCAP_ISDN;
195       break;
196
197     case ASCEND_PFX_ETHER:
198       wth->file_encap = WTAP_ENCAP_ETHERNET;
199       break;
200
201     default:
202       wth->file_encap = WTAP_ENCAP_ASCEND;
203   }
204
205   wth->snapshot_length = ASCEND_MAX_PKT_LEN;
206   wth->subtype_read = ascend_read;
207   wth->subtype_seek_read = ascend_seek_read;
208   wth->subtype_close = ascend_close;
209   wth->capture.ascend = g_malloc(sizeof(ascend_t));
210
211   /* The first packet we want to read is the one that "ascend_seek()"
212      just found; start searching for it at the offset at which it
213      found it. */
214   wth->capture.ascend->next_packet_seek_start = offset;
215
216   /* MAXen and Pipelines report the time since reboot.  In order to keep
217      from reporting packet times near the epoch, we subtract the first
218      packet's timestamp from the capture file's ctime, which gives us an
219      offset that we can apply to each packet.
220    */
221   if (fstat(wtap_fd(wth), &statbuf) == -1) {
222     *err = errno;
223     g_free(wth->capture.ascend);
224     return -1;
225   }
226   wth->capture.ascend->inittime = statbuf.st_ctime;
227   wth->capture.ascend->adjusted = 0;
228
229   init_parse_ascend();
230
231   return 1;
232 }
233
234 static void config_pseudo_header(union wtap_pseudo_header *pseudo_head)
235 {
236   switch(pseudo_head->ascend.type) {
237     case ASCEND_PFX_ISDN_X:
238       pseudo_head->isdn.uton = TRUE;
239       pseudo_head->isdn.channel = 0;
240       break;
241
242     case ASCEND_PFX_ISDN_R:
243       pseudo_head->isdn.uton = FALSE;
244       pseudo_head->isdn.channel = 0;
245       break;
246
247     case ASCEND_PFX_ETHER:
248       pseudo_head->eth.fcs_len = 0;
249       break;
250   }
251 }
252
253 /* Read the next packet; called from wtap_read(). */
254 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
255         long *data_offset)
256 {
257   long offset;
258   guint8 *buf = buffer_start_ptr(wth->frame_buffer);
259   ascend_pkthdr header;
260
261   /* parse_ascend() will advance the point at which to look for the next
262      packet's header, to just after the last packet's header (ie. at the
263      start of the last packet's data). We have to get past the last
264      packet's header because we might mistake part of it for a new header. */
265   if (file_seek(wth->fh, wth->capture.ascend->next_packet_seek_start,
266                 SEEK_SET, err) == -1)
267     return FALSE;
268
269     offset = ascend_seek(wth, ASCEND_MAX_SEEK, err);
270     if (offset == -1)
271       return FALSE;
272   if (! parse_ascend(wth->fh, buf, &wth->pseudo_header.ascend, &header, &(wth->capture.ascend->next_packet_seek_start))) {
273     *err = WTAP_ERR_BAD_RECORD;
274     *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
275     return FALSE;
276   }
277
278   buffer_assure_space(wth->frame_buffer, wth->snapshot_length);
279
280   config_pseudo_header(&wth->pseudo_header);
281
282   if (! wth->capture.ascend->adjusted) {
283     wth->capture.ascend->adjusted = 1;
284     if (header.start_time != 0) {
285       /*
286        * Capture file contained a date and time.
287        * We do this only if this is the very first packet we've seen -
288        * i.e., if "wth->capture.ascend->adjusted" is false - because
289        * if we get a date and time after the first packet, we can't
290        * go back and adjust the time stamps of the packets we've already
291        * processed, and basing the time stamps of this and following
292        * packets on the time stamp from the file text rather than the
293        * ctime of the capture file means times before this and after
294        * this can't be compared.
295        */
296       wth->capture.ascend->inittime = header.start_time;
297     }
298     if (wth->capture.ascend->inittime > header.secs)
299       wth->capture.ascend->inittime -= header.secs;
300   }
301   wth->phdr.ts.tv_sec = header.secs + wth->capture.ascend->inittime;
302   wth->phdr.ts.tv_usec = header.usecs;
303   wth->phdr.caplen = header.caplen;
304   wth->phdr.len = header.len;
305   wth->data_offset = offset;
306
307   *data_offset = offset;
308   return TRUE;
309 }
310
311 static gboolean ascend_seek_read(wtap *wth, long seek_off,
312         union wtap_pseudo_header *pseudo_head, guint8 *pd, int len,
313         int *err, gchar **err_info)
314 {
315   /* don't care for length. */
316   (void) len;
317
318   if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
319     return FALSE;
320   if (! parse_ascend(wth->random_fh, pd, &pseudo_head->ascend, NULL, &(wth->capture.ascend->next_packet_seek_start))) {
321     *err = WTAP_ERR_BAD_RECORD;
322     *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
323     return FALSE;
324   }
325
326   config_pseudo_header(pseudo_head);
327   return TRUE;
328 }
329
330 static void ascend_close(wtap *wth)
331 {
332   g_free(wth->capture.ascend);
333 }