NEEDED??? epan/dissectors/packet-dcerpc.c dissect_ndr_c_and_or_v_array_core dissect_n...
[wireshark-sm.git] / wiretap / ascendtext.c
blob4546c3b99cd523a08b966283b6f7c1c686994fc1
1 /* ascendtext.c
3 * Wiretap Library
4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
9 #include "config.h"
10 #include "ascendtext.h"
12 #include "wtap-int.h"
13 #include "ascend-int.h"
14 #include "file_wrappers.h"
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
20 #include <string.h>
22 /* Last updated: Feb 03 2005: Josh Bailey (joshbailey@lucent.com).
24 This module reads the text hex dump output of various TAOS
25 (Avaya/Alcatel/Lucent/Ascend Max, Max TNT, APX, etc) debug commands, including:
27 * pridisplay traces primary rate ISDN
28 * ether-display traces Ethernet packets (dangerous! CPU intensive)
29 * wanopening, wandisplay, wannext, wandsess
30 traces PPP or other WAN connections
32 Please see ascend_parser.lemon for examples.
34 Detailed documentation on TAOS products was at http://support.lucent.com;
35 that no longer works, and appears not to be available on the Wayback
36 Machine.
38 Some online manuals and other information include:
40 MAX Administration Guide:
41 https://downloads.avaya.com/elmodocs2/definity/def_r10_new/max/0678_002.pdf
43 Other MAX documentation:
44 https://support.avaya.com/products/P1192/max
45 https://web.archive.org/web/20201127014004/https://support.avaya.com/products/P1192/max#Tab4
47 Ascend Router Information:
48 http://maxrouter.rde.net/
49 https://web.archive.org/web/20200807215418/http://maxrouter.rde.net/
53 typedef struct _ascend_magic_string {
54 unsigned type;
55 const char *strptr;
56 size_t strlength;
57 } ascend_magic_string;
59 /* these magic strings signify the headers of a supported debug commands */
60 #define ASCEND_MAGIC_ENTRY(type, string) \
61 { type, string, sizeof string - 1 } /* strlen of a constant string */
62 static const ascend_magic_string ascend_magic[] = {
63 ASCEND_MAGIC_ENTRY(ASCEND_PFX_ISDN_X, "PRI-XMIT-"),
64 ASCEND_MAGIC_ENTRY(ASCEND_PFX_ISDN_R, "PRI-RCV-"),
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, "XMIT:"),
68 ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R, "RECV:"),
69 ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X, "PPP-OUT"),
70 ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R, "PPP-IN"),
71 ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDD, "WD_DIALOUT_DISP:"),
72 ASCEND_MAGIC_ENTRY(ASCEND_PFX_ETHER, "ETHER"),
75 #define ASCEND_MAGIC_STRINGS G_N_ELEMENTS(ascend_magic)
77 #define ASCEND_DATE "Date:"
79 static bool ascend_read(wtap *wth, wtap_rec *rec, Buffer *buf,
80 int *err, char **err_info, int64_t *data_offset);
81 static bool ascend_seek_read(wtap *wth, int64_t seek_off,
82 wtap_rec *rec, Buffer *buf,
83 int *err, char **err_info);
85 static int ascend_file_type_subtype = -1;
87 void register_ascend(void);
89 /* Seeks to the beginning of the next packet, and returns the
90 byte offset at which the header for that packet begins.
91 Returns -1 on failure. */
92 static int64_t ascend_find_next_packet(wtap *wth, int *err, char **err_info)
94 int byte;
95 int64_t date_off = -1, cur_off, packet_off;
96 size_t string_level[ASCEND_MAGIC_STRINGS];
97 unsigned string_i = 0;
98 static const char ascend_date[] = ASCEND_DATE;
99 size_t ascend_date_len = sizeof ascend_date - 1; /* strlen of a constant string */
100 size_t ascend_date_string_level;
101 unsigned excessive_read_count = 262144;
103 memset(&string_level, 0, sizeof(string_level));
104 ascend_date_string_level = 0;
106 while (((byte = file_getc(wth->fh)) != EOF)) {
107 excessive_read_count--;
109 if (!excessive_read_count) {
110 *err = 0;
111 return -1;
115 * See whether this is the string_level[string_i]th character of
116 * Ascend magic string string_i.
118 for (string_i = 0; string_i < ASCEND_MAGIC_STRINGS; string_i++) {
119 const char *strptr = ascend_magic[string_i].strptr;
120 size_t len = ascend_magic[string_i].strlength;
122 if (byte == *(strptr + string_level[string_i])) {
124 * Yes, it is, so we need to check for the next character of
125 * that string.
127 string_level[string_i]++;
130 * Have we matched the entire string?
132 if (string_level[string_i] >= len) {
134 * Yes.
136 cur_off = file_tell(wth->fh);
137 if (cur_off == -1) {
138 /* Error. */
139 *err = file_error(wth->fh, err_info);
140 return -1;
143 /* We matched some other type of header. */
144 if (date_off == -1) {
145 /* We haven't yet seen a date header, so this packet
146 doesn't have one.
147 Back up over the header we just read; that's where a read
148 of this packet should start. */
149 packet_off = cur_off - len;
150 } else {
151 /* This packet has a date/time header; a read of it should
152 start at the beginning of *that* header. */
153 packet_off = date_off;
156 goto found;
158 } else {
160 * Not a match for this string, so reset the match process.
162 string_level[string_i] = 0;
167 * See whether this is the date_string_level'th character of
168 * ASCEND_DATE.
170 if (byte == *(ascend_date + ascend_date_string_level)) {
172 * Yes, it is, so we need to check for the next character of
173 * that string.
175 ascend_date_string_level++;
178 * Have we matched the entire string?
180 if (ascend_date_string_level >= ascend_date_len) {
181 /* We matched a Date: header. It's a special case;
182 remember the offset, but keep looking for other
183 headers.
185 Reset the amount of Date: header that we've matched,
186 so that we start the process of matching a Date:
187 header all over again.
189 XXX - what if we match multiple Date: headers before
190 matching some other header? */
191 cur_off = file_tell(wth->fh);
192 if (cur_off == -1) {
193 /* Error. */
194 *err = file_error(wth->fh, err_info);
195 return -1;
198 date_off = cur_off - ascend_date_len;
199 ascend_date_string_level = 0;
201 } else {
203 * Not a match for the Date: string, so reset the match process.
205 ascend_date_string_level = 0;
209 *err = file_error(wth->fh, err_info);
210 return -1;
212 found:
214 * Move to where the read for this packet should start, and return
215 * that seek offset.
217 if (file_seek(wth->fh, packet_off, SEEK_SET, err) == -1)
218 return -1;
220 return packet_off;
223 wtap_open_return_val ascend_open(wtap *wth, int *err, char **err_info)
225 int64_t offset;
226 uint8_t buf[ASCEND_MAX_PKT_LEN];
227 ascend_state_t parser_state = {0};
228 ws_statb64 statbuf;
229 ascend_t *ascend;
230 wtap_rec rec;
232 /* We haven't yet allocated a data structure for our private stuff;
233 set the pointer to null, so that "ascend_find_next_packet()" knows
234 not to fill it in. */
235 wth->priv = NULL;
237 offset = ascend_find_next_packet(wth, err, err_info);
238 if (offset == -1) {
239 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
240 return WTAP_OPEN_ERROR; /* read error */
241 return WTAP_OPEN_NOT_MINE; /* EOF */
244 /* Do a trial parse of the first packet just found to see if we might
245 really have an Ascend file. If it fails with an actual error,
246 fail; those will be I/O errors. */
247 parser_state.fh = wth->fh;
248 parser_state.pseudo_header = &rec.rec_header.packet_header.pseudo_header.ascend;
249 if (run_ascend_parser(buf, &parser_state, err, err_info) != 0 && *err != 0) {
250 /* An I/O error. */
251 return WTAP_OPEN_ERROR;
254 /* Either the parse succeeded, or it failed but didn't get an I/O
255 error.
257 If we got at least some data, return success even if the parser
258 reported an error. This is because the debug header gives the
259 number of bytes on the wire, not actually how many bytes are in
260 the trace. We won't know where the data ends until we run into
261 the next packet. */
262 if (parser_state.caplen == 0) {
263 /* We read no data, so this presumably isn't an Ascend file. */
264 return WTAP_OPEN_NOT_MINE;
267 wth->file_type_subtype = ascend_file_type_subtype;
268 wth->file_encap = WTAP_ENCAP_ASCEND;
270 wth->snapshot_length = ASCEND_MAX_PKT_LEN;
271 wth->subtype_read = ascend_read;
272 wth->subtype_seek_read = ascend_seek_read;
273 ascend = g_new(ascend_t, 1);
274 wth->priv = (void *)ascend;
276 /* The first packet we want to read is the one that
277 "ascend_find_next_packet()" just found; start searching
278 for it at the offset at which it found it. */
279 ascend->next_packet_seek_start = offset;
281 /* MAXen and Pipelines report the time since reboot. In order to keep
282 from reporting packet times near the epoch, we subtract the first
283 packet's timestamp from the capture file's ctime, which gives us an
284 offset that we can apply to each packet.
286 if (wtap_fstat(wth, &statbuf, err) == -1) {
287 return WTAP_OPEN_ERROR;
289 ascend->inittime = statbuf.st_ctime;
290 ascend->adjusted = false;
291 wth->file_tsprec = WTAP_TSPREC_USEC;
294 * Add an IDB; we don't know how many interfaces were
295 * involved, so we just say one interface, about which
296 * we only know the link-layer type, snapshot length,
297 * and time stamp resolution.
299 wtap_add_generated_idb(wth);
301 return WTAP_OPEN_MINE;
304 /* Parse the capture file.
305 Returns true if we got a packet, false otherwise. */
306 static bool
307 parse_ascend(ascend_t *ascend, FILE_T fh, wtap_rec *rec, Buffer *buf,
308 unsigned length, int64_t *next_packet_seek_start_ret,
309 int *err, char **err_info)
311 ascend_state_t parser_state = {0};
312 int retval;
314 ws_buffer_assure_space(buf, length);
315 parser_state.fh = fh;
316 parser_state.pseudo_header = &rec->rec_header.packet_header.pseudo_header.ascend;
318 retval = run_ascend_parser(ws_buffer_start_ptr(buf), &parser_state, err, err_info);
320 /* Did we see any data (hex bytes)? */
321 if (parser_state.first_hexbyte) {
322 /* Yes. Provide the offset of the first byte so that our caller can
323 tip off ascend_find_next_packet() as to where to look for the next
324 packet, if any. */
325 if (next_packet_seek_start_ret != NULL)
326 *next_packet_seek_start_ret = parser_state.first_hexbyte;
327 } else {
328 /* No. Maybe this record was broken; sometimes, a header will be
329 printed but the data will be omitted, or worse -- two headers will
330 be printed, followed by the data for each.
332 Because of this, we need to be fairly tolerant of what we accept
333 here. Provide our current offset so that our caller can tell
334 ascend_find_next_packet() to skip over what we've read so far so
335 we can try reading a new packet.
337 . That keeps us from getting into an infinite loop reading a broken
338 trace. */
339 if (next_packet_seek_start_ret != NULL)
340 *next_packet_seek_start_ret = file_tell(fh);
342 /* Don't treat that as a fatal error; pretend the parse succeeded. */
343 retval = 0;
346 /* if we got at least some data, return success even if the parser
347 reported an error. This is because the debug header gives the number
348 of bytes on the wire, not actually how many bytes are in the trace.
349 We won't know where the data ends until we run into the next packet. */
350 if (parser_state.caplen) {
351 if (! ascend->adjusted) {
352 ascend->adjusted = true;
353 if (parser_state.saw_timestamp) {
355 * Capture file contained a date and time.
356 * We do this only if this is the very first packet we've seen -
357 * i.e., if "ascend->adjusted" is false - because
358 * if we get a date and time after the first packet, we can't
359 * go back and adjust the time stamps of the packets we've already
360 * processed, and basing the time stamps of this and following
361 * packets on the time stamp from the file text rather than the
362 * ctime of the capture file means times before this and after
363 * this can't be compared.
365 ascend->inittime = parser_state.timestamp;
367 if (ascend->inittime > parser_state.secs)
368 ascend->inittime -= parser_state.secs;
370 rec->rec_type = REC_TYPE_PACKET;
371 rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
372 rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
373 rec->ts.secs = parser_state.secs + ascend->inittime;
374 rec->ts.nsecs = parser_state.usecs * 1000;
375 rec->rec_header.packet_header.caplen = parser_state.caplen;
376 rec->rec_header.packet_header.len = parser_state.wirelen;
378 return true;
381 /* Didn't see any data. Still, perhaps the parser was happy. */
382 if (retval) {
383 if (*err == 0) {
384 /* Parser failed, but didn't report an I/O error, so a parse error.
385 Return WTAP_ERR_BAD_FILE, with the parse error as the error string. */
386 *err = WTAP_ERR_BAD_FILE;
387 *err_info = g_strdup((parser_state.ascend_parse_error != NULL) ? parser_state.ascend_parse_error : "parse error");
389 } else {
390 if (*err == 0) {
391 /* Parser succeeded, but got no data, and didn't report an I/O error.
392 Return WTAP_ERR_BAD_FILE, with a "got no data" error string. */
393 *err = WTAP_ERR_BAD_FILE;
394 *err_info = g_strdup("no data returned by parse");
397 return false;
400 /* Read the next packet; called from wtap_read(). */
401 static bool ascend_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
402 char **err_info, int64_t *data_offset)
404 ascend_t *ascend = (ascend_t *)wth->priv;
405 int64_t offset;
407 /* parse_ascend() will advance the point at which to look for the next
408 packet's header, to just after the last packet's header (ie. at the
409 start of the last packet's data). We have to get past the last
410 packet's header because we might mistake part of it for a new header. */
411 if (file_seek(wth->fh, ascend->next_packet_seek_start,
412 SEEK_SET, err) == -1)
413 return false;
415 offset = ascend_find_next_packet(wth, err, err_info);
416 if (offset == -1) {
417 /* EOF or read error */
418 return false;
420 if (!parse_ascend(ascend, wth->fh, rec, buf, wth->snapshot_length,
421 &ascend->next_packet_seek_start, err, err_info))
422 return false;
424 /* Flex might have gotten an EOF and caused *err to be set to
425 WTAP_ERR_SHORT_READ. If so, that's not an error, as the parser
426 didn't return an error; set *err to 0, and get rid of any error
427 string. */
428 *err = 0;
429 if (*err_info != NULL) {
430 g_free(*err_info);
431 *err_info = NULL;
433 *data_offset = offset;
434 return true;
437 static bool ascend_seek_read(wtap *wth, int64_t seek_off,
438 wtap_rec *rec, Buffer *buf,
439 int *err, char **err_info)
441 ascend_t *ascend = (ascend_t *)wth->priv;
443 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
444 return false;
445 if (!parse_ascend(ascend, wth->random_fh, rec, buf,
446 wth->snapshot_length, NULL, err, err_info))
447 return false;
449 /* Flex might have gotten an EOF and caused *err to be set to
450 WTAP_ERR_SHORT_READ. If so, that's not an error, as the parser
451 didn't return an error; set *err to 0, and get rid of any error
452 string. */
453 *err = 0;
454 if (*err_info != NULL) {
455 g_free(*err_info);
456 *err_info = NULL;
458 return true;
461 static const struct supported_block_type ascend_blocks_supported[] = {
463 * We support packet blocks, with no comments or other options.
465 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
468 static const struct file_type_subtype_info ascend_info = {
469 "Lucent/Ascend access server trace", "ascend", "txt", NULL,
470 false, BLOCKS_SUPPORTED(ascend_blocks_supported),
471 NULL, NULL, NULL
474 void register_ascend(void)
476 ascend_file_type_subtype = wtap_register_file_type_subtype(&ascend_info);
479 * Register name for backwards compatibility with the
480 * wtap_filetypes table in Lua.
482 wtap_register_backwards_compatibility_lua_name("ASCEND",
483 ascend_file_type_subtype);