6 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
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.
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.
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.
32 #include "file_wrappers.h"
35 #include "catapult_dct2000.h"
37 #define MAX_FIRST_LINE_LENGTH 200
38 #define MAX_TIMESTAMP_LINE_LENGTH 100
39 #define MAX_LINE_LENGTH 65536
40 #define MAX_TIMESTAMP_LEN 32
41 #define MAX_SECONDS_CHARS 16
42 #define MAX_SUBSECOND_DECIMALS 4
43 #define MAX_CONTEXT_NAME 64
44 #define MAX_PROTOCOL_NAME 64
45 #define MAX_PORT_DIGITS 2
46 #define MAX_VARIANT_DIGITS 32
47 #define MAX_OUTHDR_NAME 256
48 #define AAL_HEADER_CHARS 12
51 - support for FP over AAL0
52 - support for IuR interface FP
56 /* 's' or 'r' of a packet as read from .out file */
57 typedef enum packet_direction_t
64 /***********************************************************************/
65 /* For each line, store (in case we need to dump): */
66 /* - String before time field */
67 /* - String beween time field and data (if NULL assume " l ") */
75 /*******************************************************************/
76 /* Information stored external to a file (wtap) needed for reading and dumping */
77 typedef struct dct2000_file_externals
79 /* Remember the time at the start of capture */
84 * The following information is needed only for dumping.
86 * XXX - Wiretap is not *supposed* to require that a packet being
87 * dumped come from a file of the same type that you currently have
88 * open; this should be fixed.
91 /* Buffer to hold first line, including magic and format number */
92 gchar firstline[MAX_FIRST_LINE_LENGTH];
93 gint firstline_length;
95 /* Buffer to hold second line with formatted file creation data/time */
96 gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
97 gint secondline_length;
99 /* Hash table to store text prefix data part of displayed packets.
100 Records (file offset -> line_prefix_info_t)
102 GHashTable *packet_prefix_table;
103 } dct2000_file_externals_t;
105 /* 'Magic number' at start of Catapult DCT2000 .out files. */
106 static const gchar catapult_dct2000_magic[] = "Session Transcript";
108 /************************************************************/
109 /* Functions called from wiretap core */
110 static gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
111 gint64 *data_offset);
112 static gboolean catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
113 union wtap_pseudo_header *pseudo_header,
114 guchar *pd, int length,
115 int *err, gchar **err_info);
116 static void catapult_dct2000_close(wtap *wth);
118 static gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
119 const union wtap_pseudo_header *pseudo_header,
120 const guchar *pd, int *err);
121 static gboolean catapult_dct2000_dump_close(wtap_dumper *wdh, int *err);
124 /************************************************************/
125 /* Private helper functions */
126 static gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length,
127 gchar *buf, size_t bufsize);
128 static gboolean parse_line(char *linebuff, gint line_length,
129 gint *seconds, gint *useconds,
130 long *before_time_offset, long *after_time_offset,
133 packet_direction_t *direction,
134 int *encap, int *is_comment,
135 gchar *aal_header_chars,
136 gchar *context_name, guint8 *context_portp,
137 gchar *protocol_name, gchar *variant_name,
139 static int write_stub_header(guchar *frame_buffer, char *timestamp_string,
140 packet_direction_t direction, int encap,
141 gchar *context_name, guint8 context_port,
142 gchar *protocol_name, gchar *variant_name,
144 static guchar hex_from_char(gchar c);
145 static gchar char_from_hex(guchar hex);
147 static void set_pseudo_header_info(wtap *wth,
150 union wtap_pseudo_header *pseudo_header,
151 packet_direction_t direction,
152 gchar *aal_header_chars);
153 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
154 packet_direction_t direction,
155 gchar *aal_header_chars);
156 static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
157 packet_direction_t direction);
158 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
159 packet_direction_t direction);
161 static gint packet_offset_equal(gconstpointer v, gconstpointer v2);
162 static guint packet_offset_hash_func(gconstpointer v);
164 static gboolean get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs);
165 static gboolean free_line_prefix_info(gpointer key, gpointer value, gpointer user_data);
169 /********************************************/
170 /* Open file (for reading) */
171 /********************************************/
172 int catapult_dct2000_open(wtap *wth, int *err, gchar **err_info _U_)
177 gint firstline_length = 0;
178 dct2000_file_externals_t *file_externals;
179 gchar linebuff[MAX_LINE_LENGTH];
181 /* Clear errno before reading from the file */
185 /********************************************************************/
186 /* First line needs to contain at least as many characters as magic */
188 read_new_line(wth->fh, &offset, &firstline_length, linebuff,
190 if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
191 firstline_length >= MAX_FIRST_LINE_LENGTH) {
196 /* This file is not for us if it doesn't match our signature */
197 if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
202 /*********************************************************************/
203 /* Need entry in file_externals table */
205 /* Allocate a new file_externals structure for this file */
206 file_externals = g_malloc(sizeof(dct2000_file_externals_t));
207 memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
209 /* Copy this first line into buffer so could write out later */
210 g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
211 file_externals->firstline_length = firstline_length;
214 /***********************************************************/
215 /* Second line contains file timestamp */
216 /* Store this offset in in file_externals */
218 read_new_line(wth->fh, &offset, &(file_externals->secondline_length),
219 linebuff, sizeof linebuff);
220 if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
221 (!get_file_time_stamp(linebuff, ×tamp, &usecs))) {
223 /* Give up if file time line wasn't valid */
224 g_free(file_externals);
228 /* Fill in timestamp */
229 file_externals->start_secs = timestamp;
230 file_externals->start_usecs = usecs;
232 /* Copy this second line into buffer so could write out later */
233 g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
236 /************************************************************/
237 /* File is for us. Fill in details so packets can be read */
239 /* Set our file type */
240 wth->file_type = WTAP_FILE_CATAPULT_DCT2000;
242 /* Use our own encapsulation to send all packets to our stub dissector */
243 wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
245 /* Callbacks for reading operations */
246 wth->subtype_read = catapult_dct2000_read;
247 wth->subtype_seek_read = catapult_dct2000_seek_read;
248 wth->subtype_close = catapult_dct2000_close;
250 /* Choose microseconds (have 4 decimal places...) */
251 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
254 /***************************************************************/
255 /* Initialise packet_prefix_table (index is offset into file) */
256 file_externals->packet_prefix_table =
257 g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
259 /* Set this wtap to point to the file_externals */
260 wth->capture.generic = (void*)file_externals;
267 /**************************************************/
268 /* Read packet function. */
269 /* Look for and read the next usable packet */
270 /* - return TRUE and details if found */
271 /**************************************************/
272 gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info _U_,
275 gint64 offset = wth->data_offset;
276 long dollar_offset, before_time_offset, after_time_offset;
277 packet_direction_t direction;
280 /* Get wtap external structure for this wtap */
281 dct2000_file_externals_t *file_externals =
282 (dct2000_file_externals_t*)wth->capture.generic;
284 /* There *has* to be an entry for this wth */
285 if (!file_externals) {
289 /* Search for a line containing a usable packet */
291 int line_length, seconds, useconds, data_chars;
292 int is_comment = FALSE;
293 gint64 this_offset = offset;
294 gchar linebuff[MAX_LINE_LENGTH+1];
295 gchar aal_header_chars[AAL_HEADER_CHARS];
296 gchar context_name[MAX_CONTEXT_NAME];
298 gchar protocol_name[MAX_PROTOCOL_NAME+1];
299 gchar variant_name[MAX_VARIANT_DIGITS+1];
300 gchar outhdr_name[MAX_OUTHDR_NAME+1];
302 /* Are looking for first packet after 2nd line */
303 if (wth->data_offset == 0) {
304 this_offset += (file_externals->firstline_length+1+
305 file_externals->secondline_length+1);
308 /* Clear errno before reading from the file */
311 /* Read a new line from file into linebuff */
312 if (read_new_line(wth->fh, &offset, &line_length, linebuff,
313 sizeof linebuff) == FALSE) {
314 /* Get out if no more lines can be read */
318 /* Try to parse the line as a frame record */
319 if (parse_line(linebuff, line_length, &seconds, &useconds,
320 &before_time_offset, &after_time_offset,
322 &data_chars, &direction, &encap, &is_comment,
324 context_name, &context_port,
325 protocol_name, variant_name, outhdr_name)) {
326 guchar *frame_buffer;
329 line_prefix_info_t *line_prefix_info;
330 char timestamp_string[MAX_TIMESTAMP_LEN+1];
333 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", seconds, useconds/100);
335 /* All packets go to Catapult DCT2000 stub dissector */
336 wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
338 /* Set data_offset to the beginning of the line we're returning.
339 This will be the seek_off parameter when this frame is re-read.
341 *data_offset = this_offset;
343 /* This is the position in the file where the next _read() will be called from */
344 wth->data_offset = this_offset + line_length + 1;
346 /* Fill in timestamp (capture base + packet offset) */
347 wth->phdr.ts.secs = file_externals->start_secs + seconds;
348 if ((file_externals->start_usecs + useconds) >= 1000000) {
352 ((file_externals->start_usecs + useconds) % 1000000) *1000;
354 /* Get buffer pointer ready */
355 buffer_assure_space(wth->frame_buffer,
356 strlen(context_name)+1 + /* Context name */
358 strlen(timestamp_string)+1 + /* timestamp */
359 strlen(variant_name)+1 + /* variant */
360 strlen(outhdr_name)+1 + /* outhdr */
361 strlen(protocol_name)+1 + /* Protocol name */
364 is_comment ? data_chars : (data_chars/2));
365 frame_buffer = buffer_start_ptr(wth->frame_buffer);
368 /*********************/
369 /* Write stub header */
370 stub_offset = write_stub_header(frame_buffer, timestamp_string,
371 direction, encap, context_name,
373 protocol_name, variant_name,
376 /* Binary data length is half bytestring length + stub header */
377 wth->phdr.len = stub_offset + (is_comment ? data_chars : (data_chars/2));
378 wth->phdr.caplen = stub_offset + (is_comment ? data_chars : (data_chars/2));
382 /****************************************************/
383 /* Copy data into buffer, converting from ascii hex */
384 for (n=0; n <= data_chars; n+=2) {
385 frame_buffer[stub_offset + n/2] =
386 (hex_from_char(linebuff[dollar_offset+n]) << 4) |
387 hex_from_char(linebuff[dollar_offset+n+1]);
391 /***********************************************************/
392 /* Copy packet data into buffer, just copying ascii chars */
393 for (n=0; n <= data_chars; n++) {
394 frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
398 /* Store the packet prefix in the hash table */
399 line_prefix_info = g_malloc(sizeof(line_prefix_info_t));
401 /* Create and use buffer for contents before time */
402 line_prefix_info->before_time = g_malloc(before_time_offset+2);
403 g_strlcpy(line_prefix_info->before_time, linebuff, before_time_offset+1);
404 line_prefix_info->before_time[before_time_offset+1] = '\0';
406 /* Create and use buffer for contents before time.
407 Do this only if it doesn't correspond to " l ", which is by far the most
409 if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
410 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0)) {
412 line_prefix_info->after_time = NULL;
415 /* Allocate & write buffer for line between timestamp and data */
416 line_prefix_info->after_time = g_malloc(dollar_offset - after_time_offset);
417 g_strlcpy(line_prefix_info->after_time, linebuff+after_time_offset,
418 dollar_offset - after_time_offset);
419 line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
422 /* Add packet entry into table */
423 pkey = g_malloc(sizeof(*pkey));
425 g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
427 /* Set pseudo-header if necessary */
428 set_pseudo_header_info(wth, encap, this_offset, &wth->pseudo_header,
429 direction, aal_header_chars);
431 /* OK, we have packet details to return */
437 /* No packet details to return... */
443 /**************************************************/
444 /* Read & seek function. */
445 /**************************************************/
447 catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
448 union wtap_pseudo_header *pseudo_header, guchar *pd,
449 int length, int *err, gchar **err_info)
451 gint64 offset = wth->data_offset;
452 long dollar_offset, before_time_offset, after_time_offset;
453 gchar linebuff[MAX_LINE_LENGTH+1];
454 gchar aal_header_chars[AAL_HEADER_CHARS];
455 gchar context_name[MAX_CONTEXT_NAME];
457 gchar protocol_name[MAX_PROTOCOL_NAME+1];
458 gchar variant_name[MAX_VARIANT_DIGITS+1];
459 gchar outhdr_name[MAX_OUTHDR_NAME+1];
460 int is_comment = FALSE;
461 packet_direction_t direction;
463 int seconds, useconds, data_chars;
468 /* Seek to beginning of packet */
469 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
473 /* Re-read whole line (this really should succeed) */
474 if (read_new_line(wth->random_fh, &offset, &length, linebuff,
475 sizeof linebuff) == FALSE) {
479 /* Try to parse this line again (should succeed as re-reading...) */
480 if (parse_line(linebuff, length, &seconds, &useconds,
481 &before_time_offset, &after_time_offset,
483 &data_chars, &direction, &encap, &is_comment,
485 context_name, &context_port,
486 protocol_name, variant_name, outhdr_name)) {
489 char timestamp_string[MAX_TIMESTAMP_LEN+1];
490 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", seconds, useconds/100);
492 /* Make sure all packets go to catapult dct2000 dissector */
493 wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
496 /*********************/
497 /* Write stub header */
498 stub_offset = write_stub_header(pd, timestamp_string,
499 direction, encap, context_name,
501 protocol_name, variant_name,
506 /***********************************************************/
507 /* Copy packet data into buffer, converting from ascii hex */
508 for (n=0; n <= data_chars; n+=2) {
509 pd[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) |
510 hex_from_char(linebuff[dollar_offset+n+1]);
514 /***********************************************************/
515 /* Copy packet data into buffer, just copying ascii chars */
516 for (n=0; n <= data_chars; n++) {
517 pd[stub_offset+n] = linebuff[dollar_offset+n];
521 /* Set packet pseudo-header if necessary */
522 set_pseudo_header_info(wth, encap, seek_off, pseudo_header, direction,
529 /* If get here, must have failed */
531 *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
532 "line at position %" G_GINT64_MODIFIER "d",
538 /***************************************************************************/
539 /* Free dct2000-specific capture info from file that was open for reading */
540 /***************************************************************************/
541 void catapult_dct2000_close(wtap *wth)
543 /* Get externals for this file */
544 dct2000_file_externals_t *file_externals =
545 (dct2000_file_externals_t*)wth->capture.generic;
547 /* The entry *has* to be found */
548 if (!file_externals) {
552 /* Free up its line prefix values */
553 g_hash_table_foreach_remove(file_externals->packet_prefix_table,
554 free_line_prefix_info, NULL);
555 /* Free up its line prefix table */
556 g_hash_table_destroy(file_externals->packet_prefix_table);
558 /* Can now free this too */
559 g_free(file_externals);
565 /***************************/
567 /***************************/
569 /*****************************************************/
570 /* The file that we are writing to has been opened. */
571 /* Set other dump callbacks. */
572 /*****************************************************/
573 gboolean catapult_dct2000_dump_open(wtap_dumper *wdh, gboolean cant_seek _U_, int *err _U_)
575 /* Fill in other dump callbacks */
576 wdh->subtype_write = catapult_dct2000_dump;
577 wdh->subtype_close = catapult_dct2000_dump_close;
582 /*********************************************************/
583 /* Respond to queries about which encap types we support */
585 /*********************************************************/
586 int catapult_dct2000_dump_can_write_encap(int encap)
589 case WTAP_ENCAP_CATAPULT_DCT2000:
590 /* We support this */
594 /* But don't write to any other formats... */
595 return WTAP_ERR_UNSUPPORTED_ENCAP;
600 /*****************************************/
601 /* Write a single packet out to the file */
602 /*****************************************/
604 static gboolean do_fwrite(const void *data, size_t size, size_t count, FILE *stream, int *err_p)
608 nwritten = fwrite(data, size, count, stream);
609 if (nwritten != count) {
610 if (nwritten == 0 && ferror(stream)) {
614 *err_p = WTAP_ERR_SHORT_WRITE;
622 gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
623 const union wtap_pseudo_header *pseudo_header,
624 const guchar *pd, int *err)
627 line_prefix_info_t *prefix = NULL;
628 gchar time_string[16];
631 /******************************************************/
632 /* Get the file_externals structure for this file */
633 /* Find wtap external structure for this wtap */
634 dct2000_file_externals_t *file_externals =
635 (dct2000_file_externals_t*)pseudo_header->dct2000.wth->capture.generic;
637 if (wdh->dump.dct2000 == NULL) {
639 /* Write out saved first line */
640 if (!do_fwrite(file_externals->firstline, 1, file_externals->firstline_length, wdh->fh, err)) {
643 if (!do_fwrite("\n", 1, 1, wdh->fh, err)) {
647 /* Also write out saved second line with timestamp corresponding to the
648 opening time of the log.
650 if (!do_fwrite(file_externals->secondline, 1, file_externals->secondline_length, wdh->fh, err)) {
653 if (!do_fwrite("\n", 1, 1, wdh->fh, err)) {
657 /* Allocate the dct2000-specific dump structure */
658 wdh->dump.dct2000 = g_malloc(sizeof(dct2000_dump_t));
660 /* Copy time of beginning of file */
661 wdh->dump.dct2000->start_time.secs = file_externals->start_secs;
662 wdh->dump.dct2000->start_time.nsecs =
663 (file_externals->start_usecs * 1000);
665 /* Set flag do don't write header out again */
666 wdh->dump.dct2000->first_packet_written = TRUE;
670 /******************************************************************/
671 /* Write out this packet's prefix, including calculated timestamp */
673 /* Look up line data prefix using stored offset */
674 prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
675 (const void*)&(pseudo_header->dct2000.seek_off));
677 /* Write out text before timestamp */
678 if (!do_fwrite(prefix->before_time, 1, strlen(prefix->before_time), wdh->fh, err)) {
682 /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
683 is_comment = (strstr(prefix->before_time, "/////") != NULL);
685 /* Calculate time of this packet to write, relative to start of dump */
686 if (phdr->ts.nsecs >= wdh->dump.dct2000->start_time.nsecs) {
687 g_snprintf(time_string, 16, "%ld.%04d",
688 (long)(phdr->ts.secs - wdh->dump.dct2000->start_time.secs),
689 (phdr->ts.nsecs - wdh->dump.dct2000->start_time.nsecs) / 100000);
692 g_snprintf(time_string, 16, "%ld.%04u",
693 (long)(phdr->ts.secs - wdh->dump.dct2000->start_time.secs-1),
694 ((1000000000 + (phdr->ts.nsecs / 100000)) - (wdh->dump.dct2000->start_time.nsecs / 100000)) % 10000);
697 /* Write out the calculated timestamp */
698 if (!do_fwrite(time_string, 1, strlen(time_string), wdh->fh, err)) {
702 /* Write out text between timestamp and start of hex data */
703 if (prefix->after_time == NULL) {
704 if (!do_fwrite(" l ", 1, strlen(" l "), wdh->fh, err)) {
709 if (!do_fwrite(prefix->after_time, 1, strlen(prefix->after_time), wdh->fh, err)) {
715 /****************************************************************/
716 /* Need to skip stub header at start of pd before we reach data */
719 for (n=0; pd[n] != '\0'; n++);
722 /* Context port number */
726 for (; pd[n] != '\0'; n++);
730 for (; pd[n] != '\0'; n++);
733 /* Variant number (as string) */
734 for (; pd[n] != '\0'; n++);
737 /* Outhdr (as string) */
738 for (; pd[n] != '\0'; n++);
741 /* Direction & encap */
745 /**************************************/
746 /* Remainder is encapsulated protocol */
747 if (!do_fwrite("$", 1, 1, wdh->fh, err)) {
752 /* Each binary byte is written out as 2 hex string chars */
753 for (; n < phdr->len; n++) {
755 c[0] = char_from_hex((guchar)(pd[n] >> 4));
756 c[1] = char_from_hex((guchar)(pd[n] & 0x0f));
758 /* Write both hex chars of byte together */
759 if (!do_fwrite(c, 1, 2, wdh->fh, err)) {
765 for (; n < phdr->len; n++) {
769 /* Write both hex chars of byte together */
770 if (!do_fwrite(c, 1, 1, wdh->fh, err)) {
777 if (!do_fwrite("\n", 1, 1, wdh->fh, err)) {
785 /******************************************************/
786 /* Close a file we've been writing to. */
787 /******************************************************/
788 static gboolean catapult_dct2000_dump_close(wtap_dumper *wdh _U_, int *err _U_)
796 /****************************/
797 /* Private helper functions */
798 /****************************/
800 /**********************************************************************/
801 /* Read a new line from the file, starting at offset. */
802 /* - writes data to its argument linebuff */
803 /* - on return 'offset' will point to the next position to read from */
804 /* - return TRUE if this read is successful */
805 /**********************************************************************/
806 gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length,
807 gchar *linebuff, size_t linebuffsize)
812 result = file_gets(linebuff, (int)linebuffsize - 1, fh);
813 if (result == NULL) {
814 /* No characters found, or error */
818 /* Set length and offset.. */
819 *length = (gint)strlen(linebuff);
820 *offset = *offset + *length;
822 /* ...but don't want to include newline in line length */
823 if (linebuff[*length-1] == '\n') {
824 linebuff[*length-1] = '\0';
825 *length = *length - 1;
832 /**********************************************************************/
833 /* Parse a line from buffer, by identifying: */
834 /* - context, port and direction of packet */
836 /* - data position and length */
837 /* Return TRUE if this packet looks valid and can be displayed */
838 /**********************************************************************/
839 static gboolean parse_line(gchar *linebuff, gint line_length,
840 gint *seconds, gint *useconds,
841 long *before_time_offset, long *after_time_offset,
842 long *data_offset, gint *data_chars,
843 packet_direction_t *direction,
844 int *encap, int *is_comment,
845 gchar *aal_header_chars,
846 gchar *context_name, guint8 *context_portp,
847 gchar *protocol_name, gchar *variant_name,
852 char port_number_string[MAX_PORT_DIGITS+1];
853 int variant_digits = 0;
855 int protocol_chars = 0;
856 int outhdr_chars = 0;
858 char seconds_buff[MAX_SECONDS_CHARS+1];
860 char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
861 int subsecond_decimals_chars;
862 int skip_first_byte = FALSE;
863 gboolean atm_header_present = FALSE;
867 /* Read context name until find '.' */
868 for (n=0; (linebuff[n] != '.') && (n < MAX_CONTEXT_NAME) && (n+1 < line_length); n++) {
869 if (linebuff[n] == '/') {
870 context_name[n] = '\0';
872 /* If not a comment (/////), not a valid line */
873 if (strncmp(linebuff+n, "/////", 5) != 0) {
877 /* There is no variant, outhdr, etc. Set protocol to be a comment */
878 g_snprintf(protocol_name, MAX_PROTOCOL_NAME, "comment");
882 if (!isalnum((guchar)linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
885 context_name[n] = linebuff[n];
887 if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
891 /* Reset strings (that won't be set be comments) */
892 g_strlcpy(variant_name, "0", MAX_VARIANT_DIGITS);
893 g_strlcpy(outhdr_name, "", MAX_OUTHDR_NAME);
894 g_strlcpy(port_number_string, "0", MAX_PORT_DIGITS);
896 if (!(*is_comment)) {
897 /* '.' must follow context name */
898 if (linebuff[n] != '.') {
901 context_name[n] = '\0';
905 /* Now read port number */
906 for (port_digits = 0;
907 (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
908 n++, port_digits++) {
910 if (!isdigit((guchar)linebuff[n])) {
913 port_number_string[port_digits] = linebuff[n];
915 if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
919 /* Slash char must follow port number */
920 if (linebuff[n] != '/')
924 port_number_string[port_digits] = '\0';
925 *context_portp = atoi(port_number_string);
929 /* Now for the protocol name */
930 for (protocol_chars = 0;
931 (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
932 n++, protocol_chars++) {
934 if (!isalnum((guchar)linebuff[n]) && linebuff[n] != '_') {
937 protocol_name[protocol_chars] = linebuff[n];
939 if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
940 /* If doesn't fit, fail rather than truncate */
943 protocol_name[protocol_chars] = '\0';
945 /* Slash char must follow protocol name */
946 if (linebuff[n] != '/') {
953 /* Following the / is the variant number. No digits indicate 1 */
954 for (variant_digits = 0;
955 (isdigit((guchar)linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
956 n++, variant_digits++) {
958 if (!isdigit((guchar)linebuff[n])) {
961 variant_name[variant_digits] = linebuff[n];
963 if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
966 if (variant_digits > 0) {
967 variant_name[variant_digits] = '\0';
968 variant = atoi(variant_name);
971 g_strlcpy(variant_name, "1", MAX_VARIANT_DIGITS+1);
975 /* Outheader values may follow */
976 outhdr_name[0] = '\0';
977 if (linebuff[n] == ',') {
981 for (outhdr_chars = 0;
982 (isdigit((guchar)linebuff[n]) || linebuff[n] == ',') &&
983 (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
984 n++, outhdr_chars++) {
986 if (!isdigit((guchar)linebuff[n]) && (linebuff[n] != ',')) {
989 outhdr_name[outhdr_chars] = linebuff[n];
991 if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
994 /* Terminate (possibly empty) string */
995 outhdr_name[outhdr_chars] = '\0';
1000 /******************************************************************/
1001 /* Now check whether we know how to use a packet of this protocol */
1003 if ((strcmp(protocol_name, "ip") == 0) ||
1004 (strcmp(protocol_name, "sctp") == 0) ||
1005 (strcmp(protocol_name, "gre") == 0) ||
1006 (strcmp(protocol_name, "mipv6") == 0) ||
1007 (strcmp(protocol_name, "igmp") == 0)) {
1009 *encap = WTAP_ENCAP_RAW_IP;
1013 /* FP may be carried over ATM, which has separate atm header to parse */
1014 if ((strcmp(protocol_name, "fp") == 0) ||
1015 (strcmp(protocol_name, "fp_r4") == 0) ||
1016 (strcmp(protocol_name, "fp_r5") == 0) ||
1017 (strcmp(protocol_name, "fp_r6") == 0) ||
1018 (strcmp(protocol_name, "fp_r7") == 0)) {
1020 if ((variant > 256) && (variant % 256 == 3)) {
1021 /* FP over udp is contained in IPPrim... */
1025 /* FP over AAL0 or AAL2 */
1026 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1027 atm_header_present = TRUE;
1030 else if (strcmp(protocol_name, "fpiur_r5") == 0) {
1031 /* FP (IuR) over AAL2 */
1032 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1033 atm_header_present = TRUE;
1038 if (strcmp(protocol_name, "ppp") == 0) {
1039 *encap = WTAP_ENCAP_PPP;
1042 if (strcmp(protocol_name, "isdn_l3") == 0) {
1043 /* TODO: find out what this byte means... */
1044 skip_first_byte = TRUE;
1045 *encap = WTAP_ENCAP_ISDN;
1048 if (strcmp(protocol_name, "isdn_l2") == 0) {
1049 *encap = WTAP_ENCAP_ISDN;
1052 if (strcmp(protocol_name, "ethernet") == 0) {
1053 *encap = WTAP_ENCAP_ETHERNET;
1056 if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1057 (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1059 *encap = DCT2000_ENCAP_SSCOP;
1062 if (strcmp(protocol_name, "frelay_l2") == 0) {
1063 *encap = WTAP_ENCAP_FRELAY;
1066 if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1067 *encap = DCT2000_ENCAP_MTP2;
1070 if ((strcmp(protocol_name, "nbap") == 0) ||
1071 (strcmp(protocol_name, "nbap_r4") == 0) ||
1072 (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
1074 /* The entire message in these cases is nbap, so use an encap value. */
1075 *encap = DCT2000_ENCAP_NBAP;
1078 /* Not a supported board port protocol/encap, but can show as raw data or
1079 in some cases find protocol embedded inside primitive */
1080 *encap = DCT2000_ENCAP_UNHANDLED;
1084 /* Find separate ATM header if necessary */
1085 if (atm_header_present) {
1086 int header_chars_seen = 0;
1088 /* Scan ahead to the next $ */
1089 for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1092 if (n+1 >= line_length) {
1096 /* Read consecutive hex chars into atm header buffer */
1098 ((linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1099 (n < line_length) &&
1100 (header_chars_seen < AAL_HEADER_CHARS));
1101 n++, header_chars_seen++) {
1103 aal_header_chars[header_chars_seen] = linebuff[n];
1104 /* Next 6 characters after '9' are mapped to a->f */
1105 if (!isdigit((guchar)linebuff[n])) {
1106 aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1110 if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
1115 /* Scan ahead to the next space */
1116 for (; (linebuff[n] != ' ') && (n+1 < line_length); n++);
1117 if (n+1 >= line_length) {
1123 /* Next character gives direction of message (must be 's' or 'r') */
1124 if (!(*is_comment)) {
1125 if (linebuff[n] == 's') {
1129 if (linebuff[n] == 'r') {
1130 *direction = received;
1139 *direction = received;
1143 /*********************************************************************/
1144 /* Find and read the timestamp */
1146 /* Now scan to the next digit, which should be the start of the timestamp */
1147 /* This will involve skipping " tm " */
1149 for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
1150 if (n >= line_length) {
1154 for (; !isdigit((guchar)linebuff[n]) && (n < line_length); n++);
1155 if (n >= line_length) {
1159 *before_time_offset = n;
1162 for (seconds_chars = 0;
1163 (linebuff[n] != '.') &&
1164 (seconds_chars <= MAX_SECONDS_CHARS) &&
1166 n++, seconds_chars++) {
1168 if (!isdigit((guchar)linebuff[n])) {
1169 /* Found a non-digit before decimal point. Fail */
1172 seconds_buff[seconds_chars] = linebuff[n];
1174 if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
1175 /* Didn't fit in buffer. Fail rather than use truncated */
1179 /* Convert found value into number */
1180 seconds_buff[seconds_chars] = '\0';
1181 *seconds = atoi(seconds_buff);
1183 /* The decimal point must follow the last of the seconds digits */
1184 if (linebuff[n] != '.') {
1190 /* Subsecond decimal digits (expect 4-digit accuracy) */
1191 for (subsecond_decimals_chars = 0;
1192 (linebuff[n] != ' ') &&
1193 (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1195 n++, subsecond_decimals_chars++) {
1197 if (!isdigit((guchar)linebuff[n])) {
1200 subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1202 if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length) {
1203 /* More numbers than expected - give up */
1206 /* Convert found value into microseconds */
1207 subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1208 *useconds = atoi(subsecond_decimals_buff) * 100;
1210 /* Space character must follow end of timestamp */
1211 if (linebuff[n] != ' ') {
1215 *after_time_offset = n;
1217 /* Now skip ahead to find start of data (marked by '$') */
1218 /* Want to avoid matching with normal sprint command output at the moment... */
1219 for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
1220 if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
1226 /* Set offset to data start within line */
1229 /* Set number of chars that comprise the hex string protocol data */
1230 *data_chars = line_length - n;
1232 /* May need to skip first byte (2 hex string chars) */
1233 if (skip_first_byte) {
1241 /*****************************************************************/
1242 /* Write the stub info to the data buffer while reading a packet */
1243 /*****************************************************************/
1244 static int write_stub_header(guchar *frame_buffer, char *timestamp_string,
1245 packet_direction_t direction, int encap,
1246 gchar *context_name, guint8 context_port,
1247 gchar *protocol_name, gchar *variant_name,
1250 int stub_offset = 0;
1252 g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1253 stub_offset += (int)(strlen(context_name) + 1);
1255 /* Context port number */
1256 frame_buffer[stub_offset] = context_port;
1259 /* Timestamp within file */
1260 g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
1261 stub_offset += (int)(strlen(timestamp_string) + 1);
1264 g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1265 stub_offset += (int)(strlen(protocol_name) + 1);
1267 /* Protocol variant number (as string) */
1268 g_strlcpy((void*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
1269 stub_offset += (int)(strlen(variant_name) + 1);
1272 g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1273 stub_offset += (int)(strlen(outhdr_name) + 1);
1276 frame_buffer[stub_offset] = direction;
1280 frame_buffer[stub_offset] = (guint8)encap;
1287 /**************************************************************/
1288 /* Set pseudo-header info depending upon packet encapsulation */
1289 /**************************************************************/
1290 static void set_pseudo_header_info(wtap *wth,
1293 union wtap_pseudo_header *pseudo_header,
1294 packet_direction_t direction,
1295 gchar *aal_header_chars)
1297 pseudo_header->dct2000.seek_off = file_offset;
1298 pseudo_header->dct2000.wth = wth;
1300 switch (pkt_encap) {
1301 case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1302 set_aal_info(pseudo_header, direction, aal_header_chars);
1304 case WTAP_ENCAP_ISDN:
1305 set_isdn_info(pseudo_header, direction);
1307 case WTAP_ENCAP_PPP:
1308 set_ppp_info(pseudo_header, direction);
1312 /* Other supported types don't need to set anything here... */
1318 /*********************************************/
1319 /* Fill in atm pseudo-header with known info */
1320 /*********************************************/
1321 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
1322 packet_direction_t direction,
1323 gchar *aal_header_chars)
1325 /* 'aal_head_chars' has this format (for AAL2 at least):
1326 Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1327 Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1328 Channel Identifier (8 bits) | ...
1331 /* Indicate that this is a reassembled PDU */
1332 pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1334 /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1335 TODO: Can we infer the correct value here?
1336 Meanwhile, just use the direction to make them distinguishable...
1338 pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1340 /* Assume always AAL2 for FP */
1341 pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1343 pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1344 pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1346 /* vpi is 8 bits (2nd & 3rd nibble) */
1347 pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1348 ((hex_from_char(aal_header_chars[1]) << 4) |
1349 hex_from_char(aal_header_chars[2]));
1351 /* vci is next 16 bits */
1352 pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1353 ((hex_from_char(aal_header_chars[3]) << 12) |
1354 (hex_from_char(aal_header_chars[4]) << 8) |
1355 (hex_from_char(aal_header_chars[5]) << 4) |
1356 hex_from_char(aal_header_chars[6]));
1358 /* 0 means we don't know how many cells the frame comprises. */
1359 pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1361 /* cid is usually last byte. Unless last char is not hex digit, in which
1362 case cid is derived from last char in ascii */
1363 if (isalnum((guchar)aal_header_chars[11])) {
1364 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1365 ((hex_from_char(aal_header_chars[10]) << 4) |
1366 hex_from_char(aal_header_chars[11]));
1369 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1370 (int)aal_header_chars[11] - 48;
1375 /**********************************************/
1376 /* Fill in isdn pseudo-header with known info */
1377 /**********************************************/
1378 void set_isdn_info(union wtap_pseudo_header *pseudo_header,
1379 packet_direction_t direction)
1381 /* This field is used to set the 'Source' and 'Destination' columns to
1382 'User' or 'Network'. If we assume that we're simulating the network,
1383 treat Received messages as being destined for the network.
1385 pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1387 /* This corresponds to the circuit ID. 0 is treated as LAPD,
1388 everything else would be treated as a B-channel
1390 pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1394 /*********************************************/
1395 /* Fill in ppp pseudo-header with known info */
1396 /*********************************************/
1397 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
1398 packet_direction_t direction)
1400 /* Set direction. */
1401 pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1405 /********************************************************/
1406 /* Return hex nibble equivalent of hex string character */
1407 /********************************************************/
1408 guchar hex_from_char(gchar c)
1410 if ((c >= '0') && (c <= '9')) {
1414 if ((c >= 'a') && (c <= 'f')) {
1415 return 0x0a + (c - 'a');
1418 /* Not a valid hex string character */
1423 /********************************************************/
1424 /* Return character corresponding to hex nibble value */
1425 /********************************************************/
1426 gchar char_from_hex(guchar hex)
1428 static char hex_lookup[16] =
1429 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1435 return hex_lookup[hex];
1438 /***********************************************/
1439 /* Equality test for packet prefix hash tables */
1440 /***********************************************/
1441 gint packet_offset_equal(gconstpointer v, gconstpointer v2)
1443 /* Dereferenced pointers must have same gint64 offset value */
1444 return (*(const gint64*)v == *(const gint64*)v2);
1448 /********************************************/
1449 /* Hash function for packet-prefix hash table */
1450 /********************************************/
1451 guint packet_offset_hash_func(gconstpointer v)
1453 /* Use low-order bits of git64 offset value */
1454 return (guint)(*(const gint64*)v);
1458 /************************************************************************/
1459 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1460 /* Set secs and usecs as output */
1461 /* Return FALSE if no valid time can be read */
1462 /************************************************************************/
1463 gboolean get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs)
1467 #define MAX_MONTH_LETTERS 9
1468 char month[MAX_MONTH_LETTERS+1];
1470 int day, year, hour, minute, second;
1473 /* If line longer than expected, file is probably not correctly formatted */
1474 if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
1478 /**************************************************************/
1479 /* First is month. Read until get a space following the month */
1480 for (n=0; (linebuff[n] != ' ') && (n < MAX_MONTH_LETTERS); n++) {
1481 month[n] = linebuff[n];
1485 if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
1486 else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
1487 else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
1488 else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
1489 else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
1490 else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
1491 else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
1492 else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
1493 else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
1494 else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
1495 else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
1496 else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
1498 /* Give up if not found a properly-formatted date */
1501 /* Skip space char */
1504 /********************************************************/
1505 /* Scan for remaining numerical fields */
1506 scan_found = sscanf(linebuff+n, "%d, %d %d:%d:%d.%u",
1507 &day, &year, &hour, &minute, &second, usecs);
1508 if (scan_found != 6) {
1509 /* Give up if not all found */
1513 /******************************************************/
1514 /* Fill in remaining fields and return it in a time_t */
1515 tm.tm_year = year - 1900;
1520 tm.tm_isdst = -1; /* daylight saving time info not known */
1522 /* Get seconds from this time */
1523 *secs = mktime(&tm);
1525 /* Multiply 4 digits given to get micro-seconds */
1526 *usecs = *usecs * 100;
1531 /* Free the data allocated inside a line_prefix_info_t */
1532 gboolean free_line_prefix_info(gpointer key, gpointer value,
1533 gpointer user_data _U_)
1535 line_prefix_info_t *info = (line_prefix_info_t*)value;
1537 /* Free the 64-bit key value */
1540 /* Free the strings inside */
1541 g_free(info->before_time);
1542 if (info->after_time) {
1543 g_free(info->after_time);
1546 /* And the structure itself */
1549 /* Item will always be removed from table */