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);
123 /************************************************************/
124 /* Private helper functions */
125 static gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length,
126 gchar *buf, size_t bufsize);
127 static gboolean parse_line(char *linebuff, gint line_length,
128 gint *seconds, gint *useconds,
129 long *before_time_offset, long *after_time_offset,
132 packet_direction_t *direction,
133 int *encap, int *is_comment,
134 gchar *aal_header_chars,
135 gchar *context_name, guint8 *context_portp,
136 gchar *protocol_name, gchar *variant_name,
138 static int write_stub_header(guchar *frame_buffer, char *timestamp_string,
139 packet_direction_t direction, int encap,
140 gchar *context_name, guint8 context_port,
141 gchar *protocol_name, gchar *variant_name,
143 static guchar hex_from_char(gchar c);
144 static gchar char_from_hex(guchar hex);
146 static void set_pseudo_header_info(wtap *wth,
149 union wtap_pseudo_header *pseudo_header,
150 packet_direction_t direction,
151 gchar *aal_header_chars);
152 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
153 packet_direction_t direction,
154 gchar *aal_header_chars);
155 static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
156 packet_direction_t direction);
157 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
158 packet_direction_t direction);
160 static gint packet_offset_equal(gconstpointer v, gconstpointer v2);
161 static guint packet_offset_hash_func(gconstpointer v);
163 static gboolean get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs);
164 static gboolean free_line_prefix_info(gpointer key, gpointer value, gpointer user_data);
168 /********************************************/
169 /* Open file (for reading) */
170 /********************************************/
171 int catapult_dct2000_open(wtap *wth, int *err, gchar **err_info _U_)
176 gint firstline_length = 0;
177 dct2000_file_externals_t *file_externals;
178 gchar linebuff[MAX_LINE_LENGTH];
180 /* Clear errno before reading from the file */
184 /********************************************************************/
185 /* First line needs to contain at least as many characters as magic */
187 read_new_line(wth->fh, &offset, &firstline_length, linebuff,
189 if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
190 firstline_length >= MAX_FIRST_LINE_LENGTH) {
195 /* This file is not for us if it doesn't match our signature */
196 if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
201 /*********************************************************************/
202 /* Need entry in file_externals table */
204 /* Allocate a new file_externals structure for this file */
205 file_externals = g_malloc(sizeof(dct2000_file_externals_t));
206 memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
208 /* Copy this first line into buffer so could write out later */
209 g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
210 file_externals->firstline_length = firstline_length;
213 /***********************************************************/
214 /* Second line contains file timestamp */
215 /* Store this offset in in file_externals */
217 read_new_line(wth->fh, &offset, &(file_externals->secondline_length),
218 linebuff, sizeof linebuff);
219 if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
220 (!get_file_time_stamp(linebuff, ×tamp, &usecs))) {
222 /* Give up if file time line wasn't valid */
223 g_free(file_externals);
227 /* Fill in timestamp */
228 file_externals->start_secs = timestamp;
229 file_externals->start_usecs = usecs;
231 /* Copy this second line into buffer so could write out later */
232 g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
235 /************************************************************/
236 /* File is for us. Fill in details so packets can be read */
238 /* Set our file type */
239 wth->file_type = WTAP_FILE_CATAPULT_DCT2000;
241 /* Use our own encapsulation to send all packets to our stub dissector */
242 wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
244 /* Callbacks for reading operations */
245 wth->subtype_read = catapult_dct2000_read;
246 wth->subtype_seek_read = catapult_dct2000_seek_read;
247 wth->subtype_close = catapult_dct2000_close;
249 /* Choose microseconds (have 4 decimal places...) */
250 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
253 /***************************************************************/
254 /* Initialise packet_prefix_table (index is offset into file) */
255 file_externals->packet_prefix_table =
256 g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
258 /* Set this wtap to point to the file_externals */
259 wth->priv = (void*)file_externals;
266 /**************************************************/
267 /* Read packet function. */
268 /* Look for and read the next usable packet */
269 /* - return TRUE and details if found */
270 /**************************************************/
271 gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info _U_,
274 gint64 offset = wth->data_offset;
275 long dollar_offset, before_time_offset, after_time_offset;
276 packet_direction_t direction;
279 /* Get wtap external structure for this wtap */
280 dct2000_file_externals_t *file_externals =
281 (dct2000_file_externals_t*)wth->priv;
283 /* There *has* to be an entry for this wth */
284 if (!file_externals) {
288 /* Search for a line containing a usable packet */
290 int line_length, seconds, useconds, data_chars;
291 int is_comment = FALSE;
292 gint64 this_offset = offset;
293 gchar linebuff[MAX_LINE_LENGTH+1];
294 gchar aal_header_chars[AAL_HEADER_CHARS];
295 gchar context_name[MAX_CONTEXT_NAME];
297 gchar protocol_name[MAX_PROTOCOL_NAME+1];
298 gchar variant_name[MAX_VARIANT_DIGITS+1];
299 gchar outhdr_name[MAX_OUTHDR_NAME+1];
301 /* Are looking for first packet after 2nd line */
302 if (wth->data_offset == 0) {
303 this_offset += (file_externals->firstline_length+1+
304 file_externals->secondline_length+1);
307 /* Clear errno before reading from the file */
310 /* Read a new line from file into linebuff */
311 if (read_new_line(wth->fh, &offset, &line_length, linebuff,
312 sizeof linebuff) == FALSE) {
313 /* Get out if no more lines can be read */
317 /* Try to parse the line as a frame record */
318 if (parse_line(linebuff, line_length, &seconds, &useconds,
319 &before_time_offset, &after_time_offset,
321 &data_chars, &direction, &encap, &is_comment,
323 context_name, &context_port,
324 protocol_name, variant_name, outhdr_name)) {
325 guchar *frame_buffer;
328 line_prefix_info_t *line_prefix_info;
329 char timestamp_string[MAX_TIMESTAMP_LEN+1];
332 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", seconds, useconds/100);
334 /* All packets go to Catapult DCT2000 stub dissector */
335 wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
337 /* Set data_offset to the beginning of the line we're returning.
338 This will be the seek_off parameter when this frame is re-read.
340 *data_offset = this_offset;
342 /* This is the position in the file where the next _read() will be called from */
343 wth->data_offset = this_offset + line_length + 1;
345 /* Fill in timestamp (capture base + packet offset) */
346 wth->phdr.ts.secs = file_externals->start_secs + seconds;
347 if ((file_externals->start_usecs + useconds) >= 1000000) {
351 ((file_externals->start_usecs + useconds) % 1000000) *1000;
353 /* Get buffer pointer ready */
354 buffer_assure_space(wth->frame_buffer,
355 strlen(context_name)+1 + /* Context name */
357 strlen(timestamp_string)+1 + /* timestamp */
358 strlen(variant_name)+1 + /* variant */
359 strlen(outhdr_name)+1 + /* outhdr */
360 strlen(protocol_name)+1 + /* Protocol name */
363 is_comment ? data_chars : (data_chars/2));
364 frame_buffer = buffer_start_ptr(wth->frame_buffer);
367 /*********************/
368 /* Write stub header */
369 stub_offset = write_stub_header(frame_buffer, timestamp_string,
370 direction, encap, context_name,
372 protocol_name, variant_name,
375 /* Binary data length is half bytestring length + stub header */
376 wth->phdr.len = stub_offset + (is_comment ? data_chars : (data_chars/2));
377 wth->phdr.caplen = stub_offset + (is_comment ? data_chars : (data_chars/2));
381 /****************************************************/
382 /* Copy data into buffer, converting from ascii hex */
383 for (n=0; n <= data_chars; n+=2) {
384 frame_buffer[stub_offset + n/2] =
385 (hex_from_char(linebuff[dollar_offset+n]) << 4) |
386 hex_from_char(linebuff[dollar_offset+n+1]);
390 /***********************************************************/
391 /* Copy packet data into buffer, just copying ascii chars */
392 for (n=0; n <= data_chars; n++) {
393 frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
397 /* Store the packet prefix in the hash table */
398 line_prefix_info = g_malloc(sizeof(line_prefix_info_t));
400 /* Create and use buffer for contents before time */
401 line_prefix_info->before_time = g_malloc(before_time_offset+2);
402 g_strlcpy(line_prefix_info->before_time, linebuff, before_time_offset+1);
403 line_prefix_info->before_time[before_time_offset+1] = '\0';
405 /* Create and use buffer for contents before time.
406 Do this only if it doesn't correspond to " l ", which is by far the most
408 if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
409 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0)) {
411 line_prefix_info->after_time = NULL;
414 /* Allocate & write buffer for line between timestamp and data */
415 line_prefix_info->after_time = g_malloc(dollar_offset - after_time_offset);
416 g_strlcpy(line_prefix_info->after_time, linebuff+after_time_offset,
417 dollar_offset - after_time_offset);
418 line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
421 /* Add packet entry into table */
422 pkey = g_malloc(sizeof(*pkey));
424 g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
426 /* Set pseudo-header if necessary */
427 set_pseudo_header_info(wth, encap, this_offset, &wth->pseudo_header,
428 direction, aal_header_chars);
430 /* OK, we have packet details to return */
436 /* No packet details to return... */
442 /**************************************************/
443 /* Read & seek function. */
444 /**************************************************/
446 catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
447 union wtap_pseudo_header *pseudo_header, guchar *pd,
448 int length, int *err, gchar **err_info)
450 gint64 offset = wth->data_offset;
451 long dollar_offset, before_time_offset, after_time_offset;
452 gchar linebuff[MAX_LINE_LENGTH+1];
453 gchar aal_header_chars[AAL_HEADER_CHARS];
454 gchar context_name[MAX_CONTEXT_NAME];
456 gchar protocol_name[MAX_PROTOCOL_NAME+1];
457 gchar variant_name[MAX_VARIANT_DIGITS+1];
458 gchar outhdr_name[MAX_OUTHDR_NAME+1];
459 int is_comment = FALSE;
460 packet_direction_t direction;
462 int seconds, useconds, data_chars;
467 /* Seek to beginning of packet */
468 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
472 /* Re-read whole line (this really should succeed) */
473 if (read_new_line(wth->random_fh, &offset, &length, linebuff,
474 sizeof linebuff) == FALSE) {
478 /* Try to parse this line again (should succeed as re-reading...) */
479 if (parse_line(linebuff, length, &seconds, &useconds,
480 &before_time_offset, &after_time_offset,
482 &data_chars, &direction, &encap, &is_comment,
484 context_name, &context_port,
485 protocol_name, variant_name, outhdr_name)) {
488 char timestamp_string[MAX_TIMESTAMP_LEN+1];
489 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", seconds, useconds/100);
491 /* Make sure all packets go to catapult dct2000 dissector */
492 wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
495 /*********************/
496 /* Write stub header */
497 stub_offset = write_stub_header(pd, timestamp_string,
498 direction, encap, context_name,
500 protocol_name, variant_name,
505 /***********************************************************/
506 /* Copy packet data into buffer, converting from ascii hex */
507 for (n=0; n <= data_chars; n+=2) {
508 pd[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) |
509 hex_from_char(linebuff[dollar_offset+n+1]);
513 /***********************************************************/
514 /* Copy packet data into buffer, just copying ascii chars */
515 for (n=0; n <= data_chars; n++) {
516 pd[stub_offset+n] = linebuff[dollar_offset+n];
520 /* Set packet pseudo-header if necessary */
521 set_pseudo_header_info(wth, encap, seek_off, pseudo_header, direction,
528 /* If get here, must have failed */
530 *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
531 "line at position %" G_GINT64_MODIFIER "d",
537 /***************************************************************************/
538 /* Free dct2000-specific capture info from file that was open for reading */
539 /***************************************************************************/
540 void catapult_dct2000_close(wtap *wth)
542 /* Get externals for this file */
543 dct2000_file_externals_t *file_externals =
544 (dct2000_file_externals_t*)wth->priv;
546 /* The entry *has* to be found */
547 if (!file_externals) {
551 /* Free up its line prefix values */
552 g_hash_table_foreach_remove(file_externals->packet_prefix_table,
553 free_line_prefix_info, NULL);
554 /* Free up its line prefix table */
555 g_hash_table_destroy(file_externals->packet_prefix_table);
561 /***************************/
563 /***************************/
566 gboolean first_packet_written;
567 struct wtap_nstime start_time;
570 /*****************************************************/
571 /* The file that we are writing to has been opened. */
572 /* Set other dump callbacks. */
573 /*****************************************************/
574 gboolean catapult_dct2000_dump_open(wtap_dumper *wdh, gboolean cant_seek _U_, int *err _U_)
576 /* Fill in other dump callbacks */
577 wdh->subtype_write = catapult_dct2000_dump;
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 gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
605 const union wtap_pseudo_header *pseudo_header,
606 const guchar *pd, int *err)
609 line_prefix_info_t *prefix = NULL;
610 gchar time_string[16];
612 dct2000_dump_t *dct2000;
614 /******************************************************/
615 /* Get the file_externals structure for this file */
616 /* Find wtap external structure for this wtap */
617 dct2000_file_externals_t *file_externals =
618 (dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
620 dct2000 = (dct2000_dump_t *)wdh->priv;
621 if (dct2000 == NULL) {
623 /* Write out saved first line */
624 if (!wtap_dump_file_write(wdh, file_externals->firstline,
625 file_externals->firstline_length, err)) {
628 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
632 /* Also write out saved second line with timestamp corresponding to the
633 opening time of the log.
635 if (!wtap_dump_file_write(wdh, file_externals->secondline,
636 file_externals->secondline_length, err)) {
639 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
643 /* Allocate the dct2000-specific dump structure */
644 dct2000 = (dct2000_dump_t *)g_malloc(sizeof(dct2000_dump_t));
645 wdh->priv = (void *)dct2000;
647 /* Copy time of beginning of file */
648 dct2000->start_time.secs = file_externals->start_secs;
649 dct2000->start_time.nsecs =
650 (file_externals->start_usecs * 1000);
652 /* Set flag do don't write header out again */
653 dct2000->first_packet_written = TRUE;
657 /******************************************************************/
658 /* Write out this packet's prefix, including calculated timestamp */
660 /* Look up line data prefix using stored offset */
661 prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
662 (const void*)&(pseudo_header->dct2000.seek_off));
664 /* Write out text before timestamp */
665 if (!wtap_dump_file_write(wdh, prefix->before_time,
666 strlen(prefix->before_time), err)) {
670 /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
671 is_comment = (strstr(prefix->before_time, "/////") != NULL);
673 /* Calculate time of this packet to write, relative to start of dump */
674 if (phdr->ts.nsecs >= dct2000->start_time.nsecs) {
675 g_snprintf(time_string, 16, "%ld.%04d",
676 (long)(phdr->ts.secs - dct2000->start_time.secs),
677 (phdr->ts.nsecs - dct2000->start_time.nsecs) / 100000);
680 g_snprintf(time_string, 16, "%ld.%04u",
681 (long)(phdr->ts.secs - dct2000->start_time.secs-1),
682 ((1000000000 + (phdr->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
685 /* Write out the calculated timestamp */
686 if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
690 /* Write out text between timestamp and start of hex data */
691 if (prefix->after_time == NULL) {
692 if (!wtap_dump_file_write(wdh, " l ", strlen(" l "), err)) {
697 if (!wtap_dump_file_write(wdh, prefix->after_time,
698 strlen(prefix->after_time), err)) {
704 /****************************************************************/
705 /* Need to skip stub header at start of pd before we reach data */
708 for (n=0; pd[n] != '\0'; n++);
711 /* Context port number */
715 for (; pd[n] != '\0'; n++);
719 for (; pd[n] != '\0'; n++);
722 /* Variant number (as string) */
723 for (; pd[n] != '\0'; n++);
726 /* Outhdr (as string) */
727 for (; pd[n] != '\0'; n++);
730 /* Direction & encap */
734 /**************************************/
735 /* Remainder is encapsulated protocol */
736 if (!wtap_dump_file_write(wdh, "$", 1, err)) {
741 /* Each binary byte is written out as 2 hex string chars */
742 for (; n < phdr->len; n++) {
744 c[0] = char_from_hex((guchar)(pd[n] >> 4));
745 c[1] = char_from_hex((guchar)(pd[n] & 0x0f));
747 /* Write both hex chars of byte together */
748 if (!wtap_dump_file_write(wdh, c, 2, err)) {
754 for (; n < phdr->len; n++) {
758 /* Write both hex chars of byte together */
759 if (!wtap_dump_file_write(wdh, c, 1, err)) {
766 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
774 /****************************/
775 /* Private helper functions */
776 /****************************/
778 /**********************************************************************/
779 /* Read a new line from the file, starting at offset. */
780 /* - writes data to its argument linebuff */
781 /* - on return 'offset' will point to the next position to read from */
782 /* - return TRUE if this read is successful */
783 /**********************************************************************/
784 gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length,
785 gchar *linebuff, size_t linebuffsize)
790 result = file_gets(linebuff, (int)linebuffsize - 1, fh);
791 if (result == NULL) {
792 /* No characters found, or error */
796 /* Set length and offset.. */
797 *length = (gint)strlen(linebuff);
798 *offset = *offset + *length;
800 /* ...but don't want to include newline in line length */
801 if (linebuff[*length-1] == '\n') {
802 linebuff[*length-1] = '\0';
803 *length = *length - 1;
810 /**********************************************************************/
811 /* Parse a line from buffer, by identifying: */
812 /* - context, port and direction of packet */
814 /* - data position and length */
815 /* Return TRUE if this packet looks valid and can be displayed */
816 /**********************************************************************/
817 static gboolean parse_line(gchar *linebuff, gint line_length,
818 gint *seconds, gint *useconds,
819 long *before_time_offset, long *after_time_offset,
820 long *data_offset, gint *data_chars,
821 packet_direction_t *direction,
822 int *encap, int *is_comment,
823 gchar *aal_header_chars,
824 gchar *context_name, guint8 *context_portp,
825 gchar *protocol_name, gchar *variant_name,
830 char port_number_string[MAX_PORT_DIGITS+1];
831 int variant_digits = 0;
833 int protocol_chars = 0;
834 int outhdr_chars = 0;
836 char seconds_buff[MAX_SECONDS_CHARS+1];
838 char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
839 int subsecond_decimals_chars;
840 int skip_first_byte = FALSE;
841 gboolean atm_header_present = FALSE;
845 /* Read context name until find '.' */
846 for (n=0; (linebuff[n] != '.') && (n < MAX_CONTEXT_NAME) && (n+1 < line_length); n++) {
847 if (linebuff[n] == '/') {
848 context_name[n] = '\0';
850 /* If not a comment (/////), not a valid line */
851 if (strncmp(linebuff+n, "/////", 5) != 0) {
855 /* There is no variant, outhdr, etc. Set protocol to be a comment */
856 g_snprintf(protocol_name, MAX_PROTOCOL_NAME, "comment");
860 if (!isalnum((guchar)linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
863 context_name[n] = linebuff[n];
865 if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
869 /* Reset strings (that won't be set be comments) */
870 g_strlcpy(variant_name, "0", MAX_VARIANT_DIGITS);
871 g_strlcpy(outhdr_name, "", MAX_OUTHDR_NAME);
872 g_strlcpy(port_number_string, "0", MAX_PORT_DIGITS);
874 if (!(*is_comment)) {
875 /* '.' must follow context name */
876 if (linebuff[n] != '.') {
879 context_name[n] = '\0';
883 /* Now read port number */
884 for (port_digits = 0;
885 (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
886 n++, port_digits++) {
888 if (!isdigit((guchar)linebuff[n])) {
891 port_number_string[port_digits] = linebuff[n];
893 if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
897 /* Slash char must follow port number */
898 if (linebuff[n] != '/')
902 port_number_string[port_digits] = '\0';
903 *context_portp = atoi(port_number_string);
907 /* Now for the protocol name */
908 for (protocol_chars = 0;
909 (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
910 n++, protocol_chars++) {
912 if (!isalnum((guchar)linebuff[n]) && linebuff[n] != '_') {
915 protocol_name[protocol_chars] = linebuff[n];
917 if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
918 /* If doesn't fit, fail rather than truncate */
921 protocol_name[protocol_chars] = '\0';
923 /* Slash char must follow protocol name */
924 if (linebuff[n] != '/') {
931 /* Following the / is the variant number. No digits indicate 1 */
932 for (variant_digits = 0;
933 (isdigit((guchar)linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
934 n++, variant_digits++) {
936 if (!isdigit((guchar)linebuff[n])) {
939 variant_name[variant_digits] = linebuff[n];
941 if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
944 if (variant_digits > 0) {
945 variant_name[variant_digits] = '\0';
946 variant = atoi(variant_name);
949 g_strlcpy(variant_name, "1", MAX_VARIANT_DIGITS+1);
953 /* Outheader values may follow */
954 outhdr_name[0] = '\0';
955 if (linebuff[n] == ',') {
959 for (outhdr_chars = 0;
960 (isdigit((guchar)linebuff[n]) || linebuff[n] == ',') &&
961 (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
962 n++, outhdr_chars++) {
964 if (!isdigit((guchar)linebuff[n]) && (linebuff[n] != ',')) {
967 outhdr_name[outhdr_chars] = linebuff[n];
969 if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
972 /* Terminate (possibly empty) string */
973 outhdr_name[outhdr_chars] = '\0';
978 /******************************************************************/
979 /* Now check whether we know how to use a packet of this protocol */
981 if ((strcmp(protocol_name, "ip") == 0) ||
982 (strcmp(protocol_name, "sctp") == 0) ||
983 (strcmp(protocol_name, "gre") == 0) ||
984 (strcmp(protocol_name, "mipv6") == 0) ||
985 (strcmp(protocol_name, "igmp") == 0)) {
987 *encap = WTAP_ENCAP_RAW_IP;
991 /* FP may be carried over ATM, which has separate atm header to parse */
992 if ((strcmp(protocol_name, "fp") == 0) ||
993 (strcmp(protocol_name, "fp_r4") == 0) ||
994 (strcmp(protocol_name, "fp_r5") == 0) ||
995 (strcmp(protocol_name, "fp_r6") == 0) ||
996 (strcmp(protocol_name, "fp_r7") == 0)) {
998 if ((variant > 256) && (variant % 256 == 3)) {
999 /* FP over udp is contained in IPPrim... */
1003 /* FP over AAL0 or AAL2 */
1004 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1005 atm_header_present = TRUE;
1008 else if (strcmp(protocol_name, "fpiur_r5") == 0) {
1009 /* FP (IuR) over AAL2 */
1010 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1011 atm_header_present = TRUE;
1016 if (strcmp(protocol_name, "ppp") == 0) {
1017 *encap = WTAP_ENCAP_PPP;
1020 if (strcmp(protocol_name, "isdn_l3") == 0) {
1021 /* TODO: find out what this byte means... */
1022 skip_first_byte = TRUE;
1023 *encap = WTAP_ENCAP_ISDN;
1026 if (strcmp(protocol_name, "isdn_l2") == 0) {
1027 *encap = WTAP_ENCAP_ISDN;
1030 if (strcmp(protocol_name, "ethernet") == 0) {
1031 *encap = WTAP_ENCAP_ETHERNET;
1034 if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1035 (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1037 *encap = DCT2000_ENCAP_SSCOP;
1040 if (strcmp(protocol_name, "frelay_l2") == 0) {
1041 *encap = WTAP_ENCAP_FRELAY;
1044 if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1045 *encap = DCT2000_ENCAP_MTP2;
1048 if ((strcmp(protocol_name, "nbap") == 0) ||
1049 (strcmp(protocol_name, "nbap_r4") == 0) ||
1050 (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
1052 /* The entire message in these cases is nbap, so use an encap value. */
1053 *encap = DCT2000_ENCAP_NBAP;
1056 /* Not a supported board port protocol/encap, but can show as raw data or
1057 in some cases find protocol embedded inside primitive */
1058 *encap = DCT2000_ENCAP_UNHANDLED;
1062 /* Find separate ATM header if necessary */
1063 if (atm_header_present) {
1064 int header_chars_seen = 0;
1066 /* Scan ahead to the next $ */
1067 for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1070 if (n+1 >= line_length) {
1074 /* Read consecutive hex chars into atm header buffer */
1076 ((linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1077 (n < line_length) &&
1078 (header_chars_seen < AAL_HEADER_CHARS));
1079 n++, header_chars_seen++) {
1081 aal_header_chars[header_chars_seen] = linebuff[n];
1082 /* Next 6 characters after '9' are mapped to a->f */
1083 if (!isdigit((guchar)linebuff[n])) {
1084 aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1088 if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
1093 /* Scan ahead to the next space */
1094 for (; (linebuff[n] != ' ') && (n+1 < line_length); n++);
1095 if (n+1 >= line_length) {
1101 /* Next character gives direction of message (must be 's' or 'r') */
1102 if (!(*is_comment)) {
1103 if (linebuff[n] == 's') {
1107 if (linebuff[n] == 'r') {
1108 *direction = received;
1121 /*********************************************************************/
1122 /* Find and read the timestamp */
1124 /* Now scan to the next digit, which should be the start of the timestamp */
1125 /* This will involve skipping " tm " */
1127 for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
1128 if (n >= line_length) {
1132 for (; !isdigit((guchar)linebuff[n]) && (n < line_length); n++);
1133 if (n >= line_length) {
1137 *before_time_offset = n;
1140 for (seconds_chars = 0;
1141 (linebuff[n] != '.') &&
1142 (seconds_chars <= MAX_SECONDS_CHARS) &&
1144 n++, seconds_chars++) {
1146 if (!isdigit((guchar)linebuff[n])) {
1147 /* Found a non-digit before decimal point. Fail */
1150 seconds_buff[seconds_chars] = linebuff[n];
1152 if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
1153 /* Didn't fit in buffer. Fail rather than use truncated */
1157 /* Convert found value into number */
1158 seconds_buff[seconds_chars] = '\0';
1159 *seconds = atoi(seconds_buff);
1161 /* The decimal point must follow the last of the seconds digits */
1162 if (linebuff[n] != '.') {
1168 /* Subsecond decimal digits (expect 4-digit accuracy) */
1169 for (subsecond_decimals_chars = 0;
1170 (linebuff[n] != ' ') &&
1171 (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1173 n++, subsecond_decimals_chars++) {
1175 if (!isdigit((guchar)linebuff[n])) {
1178 subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1180 if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length) {
1181 /* More numbers than expected - give up */
1184 /* Convert found value into microseconds */
1185 subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1186 *useconds = atoi(subsecond_decimals_buff) * 100;
1188 /* Space character must follow end of timestamp */
1189 if (linebuff[n] != ' ') {
1193 *after_time_offset = n;
1195 /* Now skip ahead to find start of data (marked by '$') */
1196 /* Want to avoid matching with normal sprint command output at the moment... */
1197 for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
1198 if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
1204 /* Set offset to data start within line */
1207 /* Set number of chars that comprise the hex string protocol data */
1208 *data_chars = line_length - n;
1210 /* May need to skip first byte (2 hex string chars) */
1211 if (skip_first_byte) {
1219 /*****************************************************************/
1220 /* Write the stub info to the data buffer while reading a packet */
1221 /*****************************************************************/
1222 static int write_stub_header(guchar *frame_buffer, char *timestamp_string,
1223 packet_direction_t direction, int encap,
1224 gchar *context_name, guint8 context_port,
1225 gchar *protocol_name, gchar *variant_name,
1228 int stub_offset = 0;
1230 g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1231 stub_offset += (int)(strlen(context_name) + 1);
1233 /* Context port number */
1234 frame_buffer[stub_offset] = context_port;
1237 /* Timestamp within file */
1238 g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
1239 stub_offset += (int)(strlen(timestamp_string) + 1);
1242 g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1243 stub_offset += (int)(strlen(protocol_name) + 1);
1245 /* Protocol variant number (as string) */
1246 g_strlcpy((void*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
1247 stub_offset += (int)(strlen(variant_name) + 1);
1250 g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1251 stub_offset += (int)(strlen(outhdr_name) + 1);
1254 frame_buffer[stub_offset] = direction;
1258 frame_buffer[stub_offset] = (guint8)encap;
1265 /**************************************************************/
1266 /* Set pseudo-header info depending upon packet encapsulation */
1267 /**************************************************************/
1268 static void set_pseudo_header_info(wtap *wth,
1271 union wtap_pseudo_header *pseudo_header,
1272 packet_direction_t direction,
1273 gchar *aal_header_chars)
1275 pseudo_header->dct2000.seek_off = file_offset;
1276 pseudo_header->dct2000.wth = wth;
1278 switch (pkt_encap) {
1279 case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1280 set_aal_info(pseudo_header, direction, aal_header_chars);
1282 case WTAP_ENCAP_ISDN:
1283 set_isdn_info(pseudo_header, direction);
1285 case WTAP_ENCAP_PPP:
1286 set_ppp_info(pseudo_header, direction);
1290 /* Other supported types don't need to set anything here... */
1296 /*********************************************/
1297 /* Fill in atm pseudo-header with known info */
1298 /*********************************************/
1299 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
1300 packet_direction_t direction,
1301 gchar *aal_header_chars)
1303 /* 'aal_head_chars' has this format (for AAL2 at least):
1304 Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1305 Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1306 Channel Identifier (8 bits) | ...
1309 /* Indicate that this is a reassembled PDU */
1310 pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1312 /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1313 TODO: Can we infer the correct value here?
1314 Meanwhile, just use the direction to make them distinguishable...
1316 pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1318 /* Assume always AAL2 for FP */
1319 pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1321 pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1322 pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1324 /* vpi is 8 bits (2nd & 3rd nibble) */
1325 pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1326 ((hex_from_char(aal_header_chars[1]) << 4) |
1327 hex_from_char(aal_header_chars[2]));
1329 /* vci is next 16 bits */
1330 pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1331 ((hex_from_char(aal_header_chars[3]) << 12) |
1332 (hex_from_char(aal_header_chars[4]) << 8) |
1333 (hex_from_char(aal_header_chars[5]) << 4) |
1334 hex_from_char(aal_header_chars[6]));
1336 /* 0 means we don't know how many cells the frame comprises. */
1337 pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1339 /* cid is usually last byte. Unless last char is not hex digit, in which
1340 case cid is derived from last char in ascii */
1341 if (isalnum((guchar)aal_header_chars[11])) {
1342 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1343 ((hex_from_char(aal_header_chars[10]) << 4) |
1344 hex_from_char(aal_header_chars[11]));
1347 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1348 (int)aal_header_chars[11] - 48;
1353 /**********************************************/
1354 /* Fill in isdn pseudo-header with known info */
1355 /**********************************************/
1356 void set_isdn_info(union wtap_pseudo_header *pseudo_header,
1357 packet_direction_t direction)
1359 /* This field is used to set the 'Source' and 'Destination' columns to
1360 'User' or 'Network'. If we assume that we're simulating the network,
1361 treat Received messages as being destined for the network.
1363 pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1365 /* This corresponds to the circuit ID. 0 is treated as LAPD,
1366 everything else would be treated as a B-channel
1368 pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1372 /*********************************************/
1373 /* Fill in ppp pseudo-header with known info */
1374 /*********************************************/
1375 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
1376 packet_direction_t direction)
1378 /* Set direction. */
1379 pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1383 /********************************************************/
1384 /* Return hex nibble equivalent of hex string character */
1385 /********************************************************/
1386 guchar hex_from_char(gchar c)
1388 if ((c >= '0') && (c <= '9')) {
1392 if ((c >= 'a') && (c <= 'f')) {
1393 return 0x0a + (c - 'a');
1396 /* Not a valid hex string character */
1401 /********************************************************/
1402 /* Return character corresponding to hex nibble value */
1403 /********************************************************/
1404 gchar char_from_hex(guchar hex)
1406 static char hex_lookup[16] =
1407 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1413 return hex_lookup[hex];
1416 /***********************************************/
1417 /* Equality test for packet prefix hash tables */
1418 /***********************************************/
1419 gint packet_offset_equal(gconstpointer v, gconstpointer v2)
1421 /* Dereferenced pointers must have same gint64 offset value */
1422 return (*(const gint64*)v == *(const gint64*)v2);
1426 /********************************************/
1427 /* Hash function for packet-prefix hash table */
1428 /********************************************/
1429 guint packet_offset_hash_func(gconstpointer v)
1431 /* Use low-order bits of git64 offset value */
1432 return (guint)(*(const gint64*)v);
1436 /************************************************************************/
1437 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1438 /* Set secs and usecs as output */
1439 /* Return FALSE if no valid time can be read */
1440 /************************************************************************/
1441 gboolean get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs)
1445 #define MAX_MONTH_LETTERS 9
1446 char month[MAX_MONTH_LETTERS+1];
1448 int day, year, hour, minute, second;
1451 /* If line longer than expected, file is probably not correctly formatted */
1452 if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
1456 /**************************************************************/
1457 /* First is month. Read until get a space following the month */
1458 for (n=0; (linebuff[n] != ' ') && (n < MAX_MONTH_LETTERS); n++) {
1459 month[n] = linebuff[n];
1463 if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
1464 else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
1465 else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
1466 else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
1467 else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
1468 else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
1469 else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
1470 else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
1471 else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
1472 else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
1473 else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
1474 else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
1476 /* Give up if not found a properly-formatted date */
1479 /* Skip space char */
1482 /********************************************************/
1483 /* Scan for remaining numerical fields */
1484 scan_found = sscanf(linebuff+n, "%d, %d %d:%d:%d.%u",
1485 &day, &year, &hour, &minute, &second, usecs);
1486 if (scan_found != 6) {
1487 /* Give up if not all found */
1491 /******************************************************/
1492 /* Fill in remaining fields and return it in a time_t */
1493 tm.tm_year = year - 1900;
1498 tm.tm_isdst = -1; /* daylight saving time info not known */
1500 /* Get seconds from this time */
1501 *secs = mktime(&tm);
1503 /* Multiply 4 digits given to get micro-seconds */
1504 *usecs = *usecs * 100;
1509 /* Free the data allocated inside a line_prefix_info_t */
1510 gboolean free_line_prefix_info(gpointer key, gpointer value,
1511 gpointer user_data _U_)
1513 line_prefix_info_t *info = (line_prefix_info_t*)value;
1515 /* Free the 64-bit key value */
1518 /* Free the strings inside */
1519 g_free(info->before_time);
1520 if (info->after_time) {
1521 g_free(info->after_time);
1524 /* And the structure itself */
1527 /* Item will always be removed from table */