Remove write capabilities from wtap_optionblocks.
[metze/wireshark/wip.git] / wiretap / ascendtext.c
1 /* ascendtext.c
2  *
3  * Wiretap Library
4  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include "config.h"
22 #include "wtap-int.h"
23 #include "ascendtext.h"
24 #include "ascend-int.h"
25 #include "file_wrappers.h"
26
27 #include <errno.h>
28
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32
33 #include <string.h>
34
35 /* Last updated: Feb 03 2005: Josh Bailey (joshbailey@lucent.com).
36
37    This module reads the text hex dump output of various TAOS
38    (Lucent/Ascend Max, Max TNT, APX, etc) debug commands, including:
39
40    * pridisplay         traces primary rate ISDN
41    * ether-display      traces Ethernet packets (dangerous! CPU intensive)
42    * wanopening, wandisplay, wannext, wandsess
43                         traces PPP or other WAN connections
44
45    Please see ascend.y for examples.
46
47    Detailed documentation on TAOS products is at http://support.lucent.com.
48
49    Support for other commands will be added on an ongoing basis. */
50
51 typedef struct _ascend_magic_string {
52   guint        type;
53   const gchar *strptr;
54   size_t       strlength;
55 } ascend_magic_string;
56
57 /* these magic strings signify the headers of a supported debug commands */
58 #define ASCEND_MAGIC_ENTRY(type, string) \
59   { type, string, sizeof string - 1 } /* strlen of a constant string */
60 static const ascend_magic_string ascend_magic[] = {
61   ASCEND_MAGIC_ENTRY(ASCEND_PFX_ISDN_X,  "PRI-XMIT-"),
62   ASCEND_MAGIC_ENTRY(ASCEND_PFX_ISDN_R,  "PRI-RCV-"),
63   ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X,   "XMIT-"),
64   ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R,   "RECV-"),
65   ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X,   "XMIT:"),
66   ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R,   "RECV:"),
67   ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X,   "PPP-OUT"),
68   ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R,   "PPP-IN"),
69   ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDD,     "WD_DIALOUT_DISP:"),
70   ASCEND_MAGIC_ENTRY(ASCEND_PFX_ETHER,   "ETHER"),
71 };
72
73 #define ASCEND_MAGIC_STRINGS    G_N_ELEMENTS(ascend_magic)
74
75 #define ASCEND_DATE             "Date:"
76
77 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
78         gint64 *data_offset);
79 static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
80         struct wtap_pkthdr *phdr, Buffer *buf,
81         int *err, gchar **err_info);
82
83 /* Seeks to the beginning of the next packet, and returns the
84    byte offset at which the header for that packet begins.
85    Returns -1 on failure. */
86 static gint64 ascend_seek(wtap *wth, int *err, gchar **err_info)
87 {
88   int byte;
89   gint64 date_off = -1, cur_off, packet_off;
90   size_t string_level[ASCEND_MAGIC_STRINGS];
91   guint string_i = 0, type = 0;
92   static const gchar ascend_date[] = ASCEND_DATE;
93   size_t ascend_date_len           = sizeof ascend_date - 1; /* strlen of a constant string */
94   size_t ascend_date_string_level;
95   guint excessive_read_count = 262144;
96
97   memset(&string_level, 0, sizeof(string_level));
98   ascend_date_string_level = 0;
99
100   while (((byte = file_getc(wth->fh)) != EOF)) {
101     excessive_read_count--;
102
103     if (!excessive_read_count) {
104       *err = 0;
105       return -1;
106     }
107
108     /*
109      * See whether this is the string_level[string_i]th character of
110      * Ascend magic string string_i.
111      */
112     for (string_i = 0; string_i < ASCEND_MAGIC_STRINGS; string_i++) {
113       const gchar *strptr = ascend_magic[string_i].strptr;
114       size_t len          = ascend_magic[string_i].strlength;
115
116       if (byte == *(strptr + string_level[string_i])) {
117         /*
118          * Yes, it is, so we need to check for the next character of
119          * that string.
120          */
121         string_level[string_i]++;
122
123         /*
124          * Have we matched the entire string?
125          */
126         if (string_level[string_i] >= len) {
127           /*
128            * Yes.
129            */
130           cur_off = file_tell(wth->fh);
131           if (cur_off == -1) {
132             /* Error. */
133             *err = file_error(wth->fh, err_info);
134             return -1;
135           }
136
137           /* We matched some other type of header. */
138           if (date_off == -1) {
139             /* We haven't yet seen a date header, so this packet
140                doesn't have one.
141                Back up over the header we just read; that's where a read
142                of this packet should start. */
143             packet_off = cur_off - len;
144           } else {
145             /* This packet has a date/time header; a read of it should
146                start at the beginning of *that* header. */
147             packet_off = date_off;
148           }
149
150           type = ascend_magic[string_i].type;
151           goto found;
152         }
153       } else {
154         /*
155          * Not a match for this string, so reset the match process.
156          */
157         string_level[string_i] = 0;
158       }
159     }
160
161     /*
162      * See whether this is the date_string_level'th character of
163      * ASCEND_DATE.
164      */
165     if (byte == *(ascend_date + ascend_date_string_level)) {
166       /*
167        * Yes, it is, so we need to check for the next character of
168        * that string.
169        */
170       ascend_date_string_level++;
171
172       /*
173        * Have we matched the entire string?
174        */
175       if (ascend_date_string_level >= ascend_date_len) {
176         /* We matched a Date: header.  It's a special case;
177            remember the offset, but keep looking for other
178            headers.
179
180            Reset the amount of Date: header that we've matched,
181            so that we start the process of matching a Date:
182            header all over again.
183
184            XXX - what if we match multiple Date: headers before
185            matching some other header? */
186         cur_off = file_tell(wth->fh);
187         if (cur_off == -1) {
188           /* Error. */
189           *err = file_error(wth->fh, err_info);
190           return -1;
191         }
192
193         date_off = cur_off - ascend_date_len;
194         ascend_date_string_level = 0;
195       }
196     } else {
197       /*
198        * Not a match for the Date: string, so reset the match process.
199        */
200       ascend_date_string_level = 0;
201     }
202   }
203
204   *err = file_error(wth->fh, err_info);
205   return -1;
206
207 found:
208   /*
209    * Move to where the read for this packet should start, and return
210    * that seek offset.
211    */
212   if (file_seek(wth->fh, packet_off, SEEK_SET, err) == -1)
213     return -1;
214
215   wth->phdr.pseudo_header.ascend.type = type;
216
217   return packet_off;
218 }
219
220 wtap_open_return_val ascend_open(wtap *wth, int *err, gchar **err_info)
221 {
222   gint64 offset;
223   guint8 buf[ASCEND_MAX_PKT_LEN];
224   ascend_state_t parser_state;
225   ws_statb64 statbuf;
226   ascend_t *ascend;
227
228   /* We haven't yet allocated a data structure for our private stuff;
229      set the pointer to null, so that "ascend_seek()" knows not to
230      fill it in. */
231   wth->priv = NULL;
232
233   offset = ascend_seek(wth, err, err_info);
234   if (offset == -1) {
235     if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
236       return WTAP_OPEN_ERROR;
237     return WTAP_OPEN_NOT_MINE;
238   }
239
240   /* Do a trial parse of the first packet just found to see if we might really have an Ascend file */
241   if (run_ascend_parser(wth->fh, &wth->phdr, buf, &parser_state, err, err_info) != 0) {
242     if (*err != 0) {
243         /* An I/O error. */
244         return WTAP_OPEN_ERROR;
245     }
246   }
247
248   /* if we got at least some data, and didn't get an I/O error, return
249      success even if the parser reported an error. This is because the
250      debug header gives the number of bytes on the wire, not actually
251      how many bytes are in the trace.  We won't know where the data ends
252      until we run into the next packet. */
253   if (parser_state.caplen == 0) {
254     return WTAP_OPEN_NOT_MINE;
255   }
256
257   wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_ASCEND;
258
259   switch(wth->phdr.pseudo_header.ascend.type) {
260     case ASCEND_PFX_ISDN_X:
261     case ASCEND_PFX_ISDN_R:
262       wth->file_encap = WTAP_ENCAP_ISDN;
263       break;
264
265     case ASCEND_PFX_ETHER:
266       wth->file_encap = WTAP_ENCAP_ETHERNET;
267       break;
268
269     default:
270       wth->file_encap = WTAP_ENCAP_ASCEND;
271   }
272
273   wth->snapshot_length = ASCEND_MAX_PKT_LEN;
274   wth->subtype_read = ascend_read;
275   wth->subtype_seek_read = ascend_seek_read;
276   ascend = (ascend_t *)g_malloc(sizeof(ascend_t));
277   wth->priv = (void *)ascend;
278
279   /* The first packet we want to read is the one that "ascend_seek()"
280      just found; start searching for it at the offset at which it
281      found it. */
282   ascend->next_packet_seek_start = offset;
283
284   /* MAXen and Pipelines report the time since reboot.  In order to keep
285      from reporting packet times near the epoch, we subtract the first
286      packet's timestamp from the capture file's ctime, which gives us an
287      offset that we can apply to each packet.
288    */
289   if (wtap_fstat(wth, &statbuf, err) == -1) {
290     return WTAP_OPEN_ERROR;
291   }
292   ascend->inittime = statbuf.st_ctime;
293   ascend->adjusted = FALSE;
294   wth->file_tsprec = WTAP_TSPREC_USEC;
295
296   return WTAP_OPEN_MINE;
297 }
298
299 typedef enum {
300     PARSED_RECORD,
301     PARSED_NONRECORD,
302     PARSE_FAILED
303 } parse_t;
304
305 /* Parse the capture file.
306    Returns:
307      PARSED_RECORD if we got a packet
308      PARSED_NONRECORD if the parser succeeded but didn't see a packet
309      PARSE_FAILED if the parser failed. */
310 static parse_t
311 parse_ascend(ascend_t *ascend, FILE_T fh, struct wtap_pkthdr *phdr, Buffer *buf,
312              guint length, int *err, gchar **err_info)
313 {
314   ascend_state_t parser_state;
315   int retval;
316
317   ws_buffer_assure_space(buf, length);
318   retval = run_ascend_parser(fh, phdr, ws_buffer_start_ptr(buf), &parser_state,
319                              err, err_info);
320
321   /* did we see any data (hex bytes)? if so, tip off ascend_seek()
322      as to where to look for the next packet, if any. If we didn't,
323      maybe this record was broken. Advance so we don't get into
324      an infinite loop reading a broken trace. */
325   if (parser_state.first_hexbyte) {
326     ascend->next_packet_seek_start = parser_state.first_hexbyte;
327   } else {
328     /* Sometimes, a header will be printed but the data will be omitted, or
329        worse -- two headers will be printed, followed by the data for each.
330        Because of this, we need to be fairly tolerant of what we accept
331        here.  If we didn't find any hex bytes, skip over what we've read so
332        far so we can try reading a new packet. */
333     ascend->next_packet_seek_start = file_tell(fh);
334     retval = 0;
335   }
336
337   /* if we got at least some data, return success even if the parser
338      reported an error. This is because the debug header gives the number
339      of bytes on the wire, not actually how many bytes are in the trace.
340      We won't know where the data ends until we run into the next packet. */
341   if (parser_state.caplen) {
342     if (! ascend->adjusted) {
343       ascend->adjusted = TRUE;
344       if (parser_state.saw_timestamp) {
345         /*
346          * Capture file contained a date and time.
347          * We do this only if this is the very first packet we've seen -
348          * i.e., if "ascend->adjusted" is false - because
349          * if we get a date and time after the first packet, we can't
350          * go back and adjust the time stamps of the packets we've already
351          * processed, and basing the time stamps of this and following
352          * packets on the time stamp from the file text rather than the
353          * ctime of the capture file means times before this and after
354          * this can't be compared.
355          */
356         ascend->inittime = parser_state.timestamp;
357       }
358       if (ascend->inittime > parser_state.secs)
359         ascend->inittime -= parser_state.secs;
360     }
361     phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
362     phdr->ts.secs = parser_state.secs + ascend->inittime;
363     phdr->ts.nsecs = parser_state.usecs * 1000;
364     phdr->caplen = parser_state.caplen;
365     phdr->len = parser_state.wirelen;
366
367     /*
368      * For these types, the encapsulation we use is not WTAP_ENCAP_ASCEND,
369      * so set the pseudo-headers appropriately for the type (WTAP_ENCAP_ISDN
370      * or WTAP_ENCAP_ETHERNET).
371      */
372     switch(phdr->pseudo_header.ascend.type) {
373       case ASCEND_PFX_ISDN_X:
374         phdr->pseudo_header.isdn.uton = TRUE;
375         phdr->pseudo_header.isdn.channel = 0;
376         break;
377
378       case ASCEND_PFX_ISDN_R:
379         phdr->pseudo_header.isdn.uton = FALSE;
380         phdr->pseudo_header.isdn.channel = 0;
381         break;
382
383       case ASCEND_PFX_ETHER:
384         phdr->pseudo_header.eth.fcs_len = 0;
385         break;
386     }
387     return PARSED_RECORD;
388   }
389
390   /* Didn't see any data. Still, perhaps the parser was happy.  */
391   if (retval) {
392     if (*err == 0) {
393       /* Not a bad record, so a parse error.  Return WTAP_ERR_BAD_FILE,
394          with the parse error as the error string. */
395       *err = WTAP_ERR_BAD_FILE;
396       *err_info = g_strdup((parser_state.ascend_parse_error != NULL) ? parser_state.ascend_parse_error : "parse error");
397     }
398     return PARSE_FAILED;
399   } else
400     return PARSED_NONRECORD;
401 }
402
403 /* Read the next packet; called from wtap_read(). */
404 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
405         gint64 *data_offset)
406 {
407   ascend_t *ascend = (ascend_t *)wth->priv;
408   gint64 offset;
409
410   /* parse_ascend() will advance the point at which to look for the next
411      packet's header, to just after the last packet's header (ie. at the
412      start of the last packet's data). We have to get past the last
413      packet's header because we might mistake part of it for a new header. */
414   if (file_seek(wth->fh, ascend->next_packet_seek_start,
415                 SEEK_SET, err) == -1)
416     return FALSE;
417
418   offset = ascend_seek(wth, err, err_info);
419   if (offset == -1)
420     return FALSE;
421   if (parse_ascend(ascend, wth->fh, &wth->phdr, wth->frame_buffer,
422                    wth->snapshot_length, err, err_info) != PARSED_RECORD)
423     return FALSE;
424
425   *data_offset = offset;
426   return TRUE;
427 }
428
429 static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
430         struct wtap_pkthdr *phdr, Buffer *buf,
431         int *err, gchar **err_info)
432 {
433   ascend_t *ascend = (ascend_t *)wth->priv;
434
435   if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
436     return FALSE;
437   if (parse_ascend(ascend, wth->random_fh, phdr, buf,
438                    wth->snapshot_length, err, err_info) != PARSED_RECORD)
439     return FALSE;
440
441   return TRUE;
442 }
443
444 /*
445  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
446  *
447  * Local Variables:
448  * c-basic-offset: 2
449  * tab-width: 8
450  * indent-tabs-mode: nil
451  * End:
452  *
453  * vi: set shiftwidth=2 tabstop=8 expandtab:
454  * :indentSize=2:tabSize=8:noTabs=true:
455  */