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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 guint8 *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 guint8 *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(guint8 *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 guint8 hex_from_char(gchar c);
144 static void prepare_hex_byte_from_chars_table(void);
145 static guint8 hex_byte_from_chars(gchar *c);
146 static gchar char_from_hex(guint8 hex);
148 static void set_pseudo_header_info(wtap *wth,
151 union wtap_pseudo_header *pseudo_header,
152 packet_direction_t direction,
153 gchar *aal_header_chars);
154 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
155 packet_direction_t direction,
156 gchar *aal_header_chars);
157 static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
158 packet_direction_t direction);
159 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
160 packet_direction_t direction);
162 static gint packet_offset_equal(gconstpointer v, gconstpointer v2);
163 static guint packet_offset_hash_func(gconstpointer v);
165 static gboolean get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs);
166 static gboolean free_line_prefix_info(gpointer key, gpointer value, gpointer user_data);
170 /********************************************/
171 /* Open file (for reading) */
172 /********************************************/
174 catapult_dct2000_open(wtap *wth, int *err, gchar **err_info _U_)
179 gint firstline_length = 0;
180 dct2000_file_externals_t *file_externals;
181 static gchar linebuff[MAX_LINE_LENGTH];
182 static gboolean hex_byte_table_values_set = FALSE;
184 /* Clear errno before reading from the file */
188 /********************************************************************/
189 /* First line needs to contain at least as many characters as magic */
191 read_new_line(wth->fh, &offset, &firstline_length, linebuff,
193 if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
194 firstline_length >= MAX_FIRST_LINE_LENGTH) {
199 /* This file is not for us if it doesn't match our signature */
200 if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
204 /* Make sure table is ready for use */
205 if (!hex_byte_table_values_set) {
206 prepare_hex_byte_from_chars_table();
207 hex_byte_table_values_set = TRUE;
210 /*********************************************************************/
211 /* Need entry in file_externals table */
213 /* Allocate a new file_externals structure for this file */
214 file_externals = g_new(dct2000_file_externals_t,1);
215 memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
217 /* Copy this first line into buffer so could write out later */
218 g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
219 file_externals->firstline_length = firstline_length;
222 /***********************************************************/
223 /* Second line contains file timestamp */
224 /* Store this offset in in file_externals */
226 read_new_line(wth->fh, &offset, &(file_externals->secondline_length),
227 linebuff, sizeof linebuff);
228 if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
229 (!get_file_time_stamp(linebuff, ×tamp, &usecs))) {
231 /* Give up if file time line wasn't valid */
232 g_free(file_externals);
236 /* Fill in timestamp */
237 file_externals->start_secs = timestamp;
238 file_externals->start_usecs = usecs;
240 /* Copy this second line into buffer so could write out later */
241 g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
244 /************************************************************/
245 /* File is for us. Fill in details so packets can be read */
247 /* Set our file type */
248 wth->file_type = WTAP_FILE_CATAPULT_DCT2000;
250 /* Use our own encapsulation to send all packets to our stub dissector */
251 wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
253 /* Callbacks for reading operations */
254 wth->subtype_read = catapult_dct2000_read;
255 wth->subtype_seek_read = catapult_dct2000_seek_read;
256 wth->subtype_close = catapult_dct2000_close;
258 /* Choose microseconds (have 4 decimal places...) */
259 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
262 /***************************************************************/
263 /* Initialise packet_prefix_table (index is offset into file) */
264 file_externals->packet_prefix_table =
265 g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
267 /* Set this wtap to point to the file_externals */
268 wth->priv = (void*)file_externals;
275 /**************************************************/
276 /* Read packet function. */
277 /* Look for and read the next usable packet */
278 /* - return TRUE and details if found */
279 /**************************************************/
281 catapult_dct2000_read(wtap *wth, int *err, gchar **err_info _U_,
284 gint64 offset = file_tell(wth->fh);
285 long dollar_offset, before_time_offset, after_time_offset;
286 packet_direction_t direction;
289 /* Get wtap external structure for this wtap */
290 dct2000_file_externals_t *file_externals =
291 (dct2000_file_externals_t*)wth->priv;
293 /* There *has* to be an entry for this wth */
294 if (!file_externals) {
298 /* Search for a line containing a usable packet */
300 int line_length, seconds, useconds, data_chars;
301 int is_comment = FALSE;
302 gint64 this_offset = offset;
303 static gchar linebuff[MAX_LINE_LENGTH+1];
304 gchar aal_header_chars[AAL_HEADER_CHARS];
305 gchar context_name[MAX_CONTEXT_NAME];
306 guint8 context_port = 0;
307 gchar protocol_name[MAX_PROTOCOL_NAME+1];
308 gchar variant_name[MAX_VARIANT_DIGITS+1];
309 gchar outhdr_name[MAX_OUTHDR_NAME+1];
311 /* Are looking for first packet after 2nd line */
312 if (file_tell(wth->fh) == 0) {
313 this_offset += (file_externals->firstline_length+1+
314 file_externals->secondline_length+1);
317 /* Clear errno before reading from the file */
320 /* Read a new line from file into linebuff */
321 if (read_new_line(wth->fh, &offset, &line_length, linebuff,
322 sizeof linebuff) == FALSE) {
323 /* Get out if no more lines can be read */
327 /* Try to parse the line as a frame record */
328 if (parse_line(linebuff, line_length, &seconds, &useconds,
329 &before_time_offset, &after_time_offset,
331 &data_chars, &direction, &encap, &is_comment,
333 context_name, &context_port,
334 protocol_name, variant_name, outhdr_name)) {
335 guint8 *frame_buffer;
338 line_prefix_info_t *line_prefix_info;
339 char timestamp_string[MAX_TIMESTAMP_LEN+1];
342 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", seconds, useconds/100);
344 wth->phdr.presence_flags = WTAP_HAS_TS;
346 /* All packets go to Catapult DCT2000 stub dissector */
347 wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
349 /* Set data_offset to the beginning of the line we're returning.
350 This will be the seek_off parameter when this frame is re-read.
352 *data_offset = this_offset;
354 /* Fill in timestamp (capture base + packet offset) */
355 wth->phdr.ts.secs = file_externals->start_secs + seconds;
356 if ((file_externals->start_usecs + useconds) >= 1000000) {
360 ((file_externals->start_usecs + useconds) % 1000000) *1000;
362 /* Get buffer pointer ready */
363 buffer_assure_space(wth->frame_buffer,
364 strlen(context_name)+1 + /* Context name */
366 strlen(timestamp_string)+1 + /* timestamp */
367 strlen(variant_name)+1 + /* variant */
368 strlen(outhdr_name)+1 + /* outhdr */
369 strlen(protocol_name)+1 + /* Protocol name */
372 (is_comment ? data_chars : (data_chars/2)));
373 frame_buffer = buffer_start_ptr(wth->frame_buffer);
376 /*********************/
377 /* Write stub header */
378 stub_offset = write_stub_header(frame_buffer, timestamp_string,
379 direction, encap, context_name,
381 protocol_name, variant_name,
384 /* Binary data length is half bytestring length + stub header */
385 wth->phdr.len = stub_offset + (is_comment ? data_chars : (data_chars/2));
386 wth->phdr.caplen = stub_offset + (is_comment ? data_chars : (data_chars/2));
390 /****************************************************/
391 /* Copy data into buffer, converting from ascii hex */
392 for (n=0; n < data_chars; n+=2) {
393 frame_buffer[stub_offset + n/2] =
394 hex_byte_from_chars(linebuff+dollar_offset+n);
398 /***********************************************************/
399 /* Copy packet data into buffer, just copying ascii chars */
400 for (n=0; n < data_chars; n++) {
401 frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
405 /* Store the packet prefix in the hash table */
406 line_prefix_info = g_new(line_prefix_info_t,1);
408 /* Create and use buffer for contents before time */
409 line_prefix_info->before_time = g_malloc(before_time_offset+2);
410 memcpy(line_prefix_info->before_time, linebuff, before_time_offset+1);
411 line_prefix_info->before_time[before_time_offset+1] = '\0';
413 /* Create and use buffer for contents before time.
414 Do this only if it doesn't correspond to " l ", which is by far the most
416 if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
417 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0)) {
419 line_prefix_info->after_time = NULL;
422 /* Allocate & write buffer for line between timestamp and data */
423 line_prefix_info->after_time = g_malloc(dollar_offset - after_time_offset);
424 memcpy(line_prefix_info->after_time, linebuff+after_time_offset, dollar_offset - after_time_offset);
425 line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
428 /* Add packet entry into table */
429 pkey = g_malloc(sizeof(*pkey));
431 g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
433 /* Set pseudo-header if necessary */
434 set_pseudo_header_info(wth, encap, this_offset, &wth->pseudo_header,
435 direction, aal_header_chars);
437 /* OK, we have packet details to return */
443 /* No packet details to return... */
449 /**************************************************/
450 /* Read & seek function. */
451 /**************************************************/
453 catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
454 union wtap_pseudo_header *pseudo_header, guint8 *pd,
455 int length, int *err, gchar **err_info)
458 long dollar_offset, before_time_offset, after_time_offset;
459 static gchar linebuff[MAX_LINE_LENGTH+1];
460 gchar aal_header_chars[AAL_HEADER_CHARS];
461 gchar context_name[MAX_CONTEXT_NAME];
462 guint8 context_port = 0;
463 gchar protocol_name[MAX_PROTOCOL_NAME+1];
464 gchar variant_name[MAX_VARIANT_DIGITS+1];
465 gchar outhdr_name[MAX_OUTHDR_NAME+1];
466 int is_comment = FALSE;
467 packet_direction_t direction;
469 int seconds, useconds, data_chars;
474 /* Seek to beginning of packet */
475 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
479 /* Re-read whole line (this really should succeed) */
480 if (read_new_line(wth->random_fh, &offset, &length, linebuff,
481 sizeof linebuff) == FALSE) {
485 /* Try to parse this line again (should succeed as re-reading...) */
486 if (parse_line(linebuff, length, &seconds, &useconds,
487 &before_time_offset, &after_time_offset,
489 &data_chars, &direction, &encap, &is_comment,
491 context_name, &context_port,
492 protocol_name, variant_name, outhdr_name)) {
495 char timestamp_string[MAX_TIMESTAMP_LEN+1];
496 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", seconds, useconds/100);
498 /* Make sure all packets go to catapult dct2000 dissector */
499 wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
502 /*********************/
503 /* Write stub header */
504 stub_offset = write_stub_header(pd, timestamp_string,
505 direction, encap, context_name,
507 protocol_name, variant_name,
512 /***********************************************************/
513 /* Copy packet data into buffer, converting from ascii hex */
514 for (n=0; n <= data_chars; n+=2) {
515 pd[stub_offset + n/2] = hex_byte_from_chars(linebuff+dollar_offset+n);
519 /***********************************************************/
520 /* Copy packet data into buffer, just copying ascii chars */
521 for (n=0; n <= data_chars; n++) {
522 pd[stub_offset+n] = linebuff[dollar_offset+n];
526 /* Set packet pseudo-header if necessary */
527 set_pseudo_header_info(wth, encap, seek_off, pseudo_header, direction,
534 /* If get here, must have failed */
536 *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
537 "line at position %" G_GINT64_MODIFIER "d",
543 /***************************************************************************/
544 /* Free dct2000-specific capture info from file that was open for reading */
545 /***************************************************************************/
547 catapult_dct2000_close(wtap *wth)
549 /* Get externals for this file */
550 dct2000_file_externals_t *file_externals =
551 (dct2000_file_externals_t*)wth->priv;
553 /* The entry *has* to be found */
554 if (!file_externals) {
558 /* Free up its line prefix values */
559 g_hash_table_foreach_remove(file_externals->packet_prefix_table,
560 free_line_prefix_info, NULL);
561 /* Free up its line prefix table */
562 g_hash_table_destroy(file_externals->packet_prefix_table);
568 /***************************/
570 /***************************/
573 gboolean first_packet_written;
574 struct wtap_nstime start_time;
577 /*****************************************************/
578 /* The file that we are writing to has been opened. */
579 /* Set other dump callbacks. */
580 /*****************************************************/
582 catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_)
584 /* Fill in other dump callbacks */
585 wdh->subtype_write = catapult_dct2000_dump;
590 /*********************************************************/
591 /* Respond to queries about which encap types we support */
593 /*********************************************************/
595 catapult_dct2000_dump_can_write_encap(int encap)
598 case WTAP_ENCAP_CATAPULT_DCT2000:
599 /* We support this */
603 /* But don't write to any other formats... */
604 return WTAP_ERR_UNSUPPORTED_ENCAP;
609 /*****************************************/
610 /* Write a single packet out to the file */
611 /*****************************************/
614 catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
615 const union wtap_pseudo_header *pseudo_header,
616 const guint8 *pd, int *err)
619 line_prefix_info_t *prefix = NULL;
620 gchar time_string[16];
622 dct2000_dump_t *dct2000;
624 /******************************************************/
625 /* Get the file_externals structure for this file */
626 /* Find wtap external structure for this wtap */
627 dct2000_file_externals_t *file_externals =
628 (dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
630 dct2000 = (dct2000_dump_t *)wdh->priv;
631 if (dct2000 == NULL) {
633 /* Write out saved first line */
634 if (!wtap_dump_file_write(wdh, file_externals->firstline,
635 file_externals->firstline_length, err)) {
638 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
642 /* Also write out saved second line with timestamp corresponding to the
643 opening time of the log.
645 if (!wtap_dump_file_write(wdh, file_externals->secondline,
646 file_externals->secondline_length, err)) {
649 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
653 /* Allocate the dct2000-specific dump structure */
654 dct2000 = (dct2000_dump_t *)g_malloc(sizeof(dct2000_dump_t));
655 wdh->priv = (void *)dct2000;
657 /* Copy time of beginning of file */
658 dct2000->start_time.secs = file_externals->start_secs;
659 dct2000->start_time.nsecs =
660 (file_externals->start_usecs * 1000);
662 /* Set flag do don't write header out again */
663 dct2000->first_packet_written = TRUE;
667 /******************************************************************/
668 /* Write out this packet's prefix, including calculated timestamp */
670 /* Look up line data prefix using stored offset */
671 prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
672 (const void*)&(pseudo_header->dct2000.seek_off));
674 /* Write out text before timestamp */
675 if (!wtap_dump_file_write(wdh, prefix->before_time,
676 strlen(prefix->before_time), err)) {
680 /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
681 is_comment = (strstr(prefix->before_time, "/////") != NULL);
683 /* Calculate time of this packet to write, relative to start of dump */
684 if (phdr->ts.nsecs >= dct2000->start_time.nsecs) {
685 g_snprintf(time_string, 16, "%ld.%04d",
686 (long)(phdr->ts.secs - dct2000->start_time.secs),
687 (phdr->ts.nsecs - dct2000->start_time.nsecs) / 100000);
690 g_snprintf(time_string, 16, "%ld.%04u",
691 (long)(phdr->ts.secs - dct2000->start_time.secs-1),
692 ((1000000000 + (phdr->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
695 /* Write out the calculated timestamp */
696 if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
700 /* Write out text between timestamp and start of hex data */
701 if (prefix->after_time == NULL) {
702 if (!wtap_dump_file_write(wdh, " l ", strlen(" l "), err)) {
707 if (!wtap_dump_file_write(wdh, prefix->after_time,
708 strlen(prefix->after_time), err)) {
714 /****************************************************************/
715 /* Need to skip stub header at start of pd before we reach data */
718 for (n=0; pd[n] != '\0'; n++);
721 /* Context port number */
725 for (; pd[n] != '\0'; n++);
729 for (; pd[n] != '\0'; n++);
732 /* Variant number (as string) */
733 for (; pd[n] != '\0'; n++);
736 /* Outhdr (as string) */
737 for (; pd[n] != '\0'; n++);
740 /* Direction & encap */
744 /**************************************/
745 /* Remainder is encapsulated protocol */
746 if (!wtap_dump_file_write(wdh, "$", 1, err)) {
751 /* Each binary byte is written out as 2 hex string chars */
752 for (; n < phdr->len; n++) {
754 c[0] = char_from_hex((guint8)(pd[n] >> 4));
755 c[1] = char_from_hex((guint8)(pd[n] & 0x0f));
757 /* Write both hex chars of byte together */
758 if (!wtap_dump_file_write(wdh, c, 2, err)) {
764 for (; n < phdr->len; n++) {
768 /* Write both hex chars of byte together */
769 if (!wtap_dump_file_write(wdh, c, 1, err)) {
776 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
784 /****************************/
785 /* Private helper functions */
786 /****************************/
788 /**********************************************************************/
789 /* Read a new line from the file, starting at offset. */
790 /* - writes data to its argument linebuff */
791 /* - on return 'offset' will point to the next position to read from */
792 /* - return TRUE if this read is successful */
793 /**********************************************************************/
795 read_new_line(FILE_T fh, gint64 *offset, gint *length,
796 gchar *linebuff, size_t linebuffsize)
799 gint64 pos_before = file_tell(fh);
800 char *result = file_gets(linebuff, (int)linebuffsize - 1, fh);
801 if (result == NULL) {
802 /* No characters found, or error */
806 /* Set length (avoiding strlen()) and offset.. */
807 *length = (gint)(file_tell(fh) - pos_before);
808 *offset = *offset + *length;
810 /* ...but don't want to include newline in line length */
811 if (linebuff[*length-1] == '\n') {
812 linebuff[*length-1] = '\0';
813 *length = *length - 1;
815 /* Nor do we want '\r' (as will be written when log is created on windows) */
816 if (linebuff[*length-1] == '\r') {
817 linebuff[*length-1] = '\0';
818 *length = *length - 1;
825 /**********************************************************************/
826 /* Parse a line from buffer, by identifying: */
827 /* - context, port and direction of packet */
829 /* - data position and length */
830 /* Return TRUE if this packet looks valid and can be displayed */
831 /**********************************************************************/
833 parse_line(gchar *linebuff, gint line_length,
834 gint *seconds, gint *useconds,
835 long *before_time_offset, long *after_time_offset,
836 long *data_offset, gint *data_chars,
837 packet_direction_t *direction,
838 int *encap, int *is_comment,
839 gchar *aal_header_chars,
840 gchar *context_name, guint8 *context_portp,
841 gchar *protocol_name, gchar *variant_name,
846 char port_number_string[MAX_PORT_DIGITS+1];
847 int variant_digits = 0;
849 int protocol_chars = 0;
850 int outhdr_chars = 0;
852 char seconds_buff[MAX_SECONDS_CHARS+1];
854 char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
855 int subsecond_decimals_chars;
856 int skip_first_byte = FALSE;
857 gboolean atm_header_present = FALSE;
861 /* Read context name until find '.' */
862 for (n=0; (linebuff[n] != '.') && (n < MAX_CONTEXT_NAME) && (n+1 < line_length); n++) {
863 if (linebuff[n] == '/') {
864 context_name[n] = '\0';
866 /* If not a comment (/////), not a valid line */
867 if (strncmp(linebuff+n, "/////", 5) != 0) {
871 /* There is no variant, outhdr, etc. Set protocol to be a comment */
872 g_snprintf(protocol_name, MAX_PROTOCOL_NAME, "comment");
876 if (!isalnum((guchar)linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
879 context_name[n] = linebuff[n];
881 if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
885 /* Reset strings (that won't be set by comments) */
886 variant_name[0] = '\0';
887 outhdr_name[0] = '\0';
888 port_number_string[0] = '\0';
890 if (!(*is_comment)) {
891 /* '.' must follow context name */
892 if (linebuff[n] != '.') {
895 context_name[n] = '\0';
899 /* Now read port number */
900 for (port_digits = 0;
901 (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
902 n++, port_digits++) {
904 if (!isdigit((guchar)linebuff[n])) {
907 port_number_string[port_digits] = linebuff[n];
909 if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
913 /* Slash char must follow port number */
914 if (linebuff[n] != '/')
918 port_number_string[port_digits] = '\0';
919 *context_portp = atoi(port_number_string);
923 /* Now for the protocol name */
924 for (protocol_chars = 0;
925 (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
926 n++, protocol_chars++) {
928 if (!isalnum((guchar)linebuff[n]) && linebuff[n] != '_') {
931 protocol_name[protocol_chars] = linebuff[n];
933 if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
934 /* If doesn't fit, fail rather than truncate */
937 protocol_name[protocol_chars] = '\0';
939 /* Slash char must follow protocol name */
940 if (linebuff[n] != '/') {
947 /* Following the / is the variant number. No digits indicate 1 */
948 for (variant_digits = 0;
949 (isdigit((guchar)linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
950 n++, variant_digits++) {
952 if (!isdigit((guchar)linebuff[n])) {
955 variant_name[variant_digits] = linebuff[n];
957 if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
960 if (variant_digits > 0) {
961 variant_name[variant_digits] = '\0';
962 variant = atoi(variant_name);
965 g_strlcpy(variant_name, "1", MAX_VARIANT_DIGITS+1);
969 /* Outheader values may follow */
970 outhdr_name[0] = '\0';
971 if (linebuff[n] == ',') {
975 for (outhdr_chars = 0;
976 (isdigit((guchar)linebuff[n]) || linebuff[n] == ',') &&
977 (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
978 n++, outhdr_chars++) {
980 if (!isdigit((guchar)linebuff[n]) && (linebuff[n] != ',')) {
983 outhdr_name[outhdr_chars] = linebuff[n];
985 if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
988 /* Terminate (possibly empty) string */
989 outhdr_name[outhdr_chars] = '\0';
994 /******************************************************************/
995 /* Now check whether we know how to use a packet of this protocol */
997 if ((strcmp(protocol_name, "ip") == 0) ||
998 (strcmp(protocol_name, "sctp") == 0) ||
999 (strcmp(protocol_name, "gre") == 0) ||
1000 (strcmp(protocol_name, "mipv6") == 0) ||
1001 (strcmp(protocol_name, "igmp") == 0)) {
1003 *encap = WTAP_ENCAP_RAW_IP;
1007 /* FP may be carried over ATM, which has separate atm header to parse */
1008 if ((strcmp(protocol_name, "fp") == 0) ||
1009 (strcmp(protocol_name, "fp_r4") == 0) ||
1010 (strcmp(protocol_name, "fp_r5") == 0) ||
1011 (strcmp(protocol_name, "fp_r6") == 0) ||
1012 (strcmp(protocol_name, "fp_r7") == 0) ||
1013 (strcmp(protocol_name, "fp_r8") == 0)) {
1015 if ((variant > 256) && (variant % 256 == 3)) {
1016 /* FP over udp is contained in IPPrim... */
1020 /* FP over AAL0 or AAL2 */
1021 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1022 atm_header_present = TRUE;
1025 else if (strcmp(protocol_name, "fpiur_r5") == 0) {
1026 /* FP (IuR) over AAL2 */
1027 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1028 atm_header_present = TRUE;
1032 if (strcmp(protocol_name, "ppp") == 0) {
1033 *encap = WTAP_ENCAP_PPP;
1036 if (strcmp(protocol_name, "isdn_l3") == 0) {
1037 /* TODO: find out what this byte means... */
1038 skip_first_byte = TRUE;
1039 *encap = WTAP_ENCAP_ISDN;
1042 if (strcmp(protocol_name, "isdn_l2") == 0) {
1043 *encap = WTAP_ENCAP_ISDN;
1046 if (strcmp(protocol_name, "ethernet") == 0) {
1047 *encap = WTAP_ENCAP_ETHERNET;
1050 if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1051 (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1053 *encap = DCT2000_ENCAP_SSCOP;
1056 if (strcmp(protocol_name, "frelay_l2") == 0) {
1057 *encap = WTAP_ENCAP_FRELAY;
1060 if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1061 *encap = DCT2000_ENCAP_MTP2;
1064 if ((strcmp(protocol_name, "nbap") == 0) ||
1065 (strcmp(protocol_name, "nbap_r4") == 0) ||
1066 (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
1068 /* The entire message in these cases is nbap, so use an encap value. */
1069 *encap = DCT2000_ENCAP_NBAP;
1072 /* Not a supported board port protocol/encap, but can show as raw data or
1073 in some cases find protocol embedded inside primitive */
1074 *encap = DCT2000_ENCAP_UNHANDLED;
1078 /* Find separate ATM header if necessary */
1079 if (atm_header_present) {
1080 int header_chars_seen = 0;
1082 /* Scan ahead to the next $ */
1083 for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1086 if (n+1 >= line_length) {
1090 /* Read consecutive hex chars into atm header buffer */
1092 ((linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1093 (n < line_length) &&
1094 (header_chars_seen < AAL_HEADER_CHARS));
1095 n++, header_chars_seen++) {
1097 aal_header_chars[header_chars_seen] = linebuff[n];
1098 /* Next 6 characters after '9' are mapped to a->f */
1099 if (!isdigit((guchar)linebuff[n])) {
1100 aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1104 if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
1112 /* If there is a number, skip all info to next '/'.
1113 TODO: for IP encapsulation, should store PDCP ueid, drb in pseudo info
1114 and display dct2000 dissector... */
1115 if (isdigit(linebuff[n])) {
1116 while ((n+1 < line_length) && linebuff[n] != '/') {
1122 while ((n+1 < line_length) && linebuff[n] == '/') {
1126 /* Skip a space that may happen here */
1127 if ((n+1 < line_length) && linebuff[n] == ' ') {
1131 /* Next character gives direction of message (must be 's' or 'r') */
1132 if (!(*is_comment)) {
1133 if (linebuff[n] == 's') {
1137 if (linebuff[n] == 'r') {
1138 *direction = received;
1151 /*********************************************************************/
1152 /* Find and read the timestamp */
1154 /* Now scan to the next digit, which should be the start of the timestamp */
1155 /* This will involve skipping " tm " */
1157 for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
1158 if (n >= line_length) {
1162 for (; !isdigit((guchar)linebuff[n]) && (n < line_length); n++);
1163 if (n >= line_length) {
1167 *before_time_offset = n;
1170 for (seconds_chars = 0;
1171 (linebuff[n] != '.') &&
1172 (seconds_chars <= MAX_SECONDS_CHARS) &&
1174 n++, seconds_chars++) {
1176 if (!isdigit((guchar)linebuff[n])) {
1177 /* Found a non-digit before decimal point. Fail */
1180 seconds_buff[seconds_chars] = linebuff[n];
1182 if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
1183 /* Didn't fit in buffer. Fail rather than use truncated */
1187 /* Convert found value into number */
1188 seconds_buff[seconds_chars] = '\0';
1189 *seconds = atoi(seconds_buff);
1191 /* The decimal point must follow the last of the seconds digits */
1192 if (linebuff[n] != '.') {
1198 /* Subsecond decimal digits (expect 4-digit accuracy) */
1199 for (subsecond_decimals_chars = 0;
1200 (linebuff[n] != ' ') &&
1201 (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1203 n++, subsecond_decimals_chars++) {
1205 if (!isdigit((guchar)linebuff[n])) {
1208 subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1210 if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length) {
1211 /* More numbers than expected - give up */
1214 /* Convert found value into microseconds */
1215 subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1216 *useconds = atoi(subsecond_decimals_buff) * 100;
1218 /* Space character must follow end of timestamp */
1219 if (linebuff[n] != ' ') {
1223 *after_time_offset = n;
1225 /* Now skip ahead to find start of data (marked by '$') */
1226 /* Want to avoid matching with normal sprint command output at the moment... */
1227 for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
1228 if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
1234 /* Set offset to data start within line */
1237 /* Set number of chars that comprise the hex string protocol data */
1238 *data_chars = line_length - n;
1240 /* May need to skip first byte (2 hex string chars) */
1241 if (skip_first_byte) {
1249 /*****************************************************************/
1250 /* Write the stub info to the data buffer while reading a packet */
1251 /*****************************************************************/
1253 write_stub_header(guint8 *frame_buffer, char *timestamp_string,
1254 packet_direction_t direction, int encap,
1255 gchar *context_name, guint8 context_port,
1256 gchar *protocol_name, gchar *variant_name,
1259 int stub_offset = 0;
1262 length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1263 stub_offset += (int)(length + 1);
1265 /* Context port number */
1266 frame_buffer[stub_offset] = context_port;
1269 /* Timestamp within file */
1270 length = g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
1271 stub_offset += (int)(length + 1);
1274 length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1275 stub_offset += (int)(length + 1);
1277 /* Protocol variant number (as string) */
1278 length = g_strlcpy((void*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
1279 stub_offset += (int)(length + 1);
1282 length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1283 stub_offset += (int)(length + 1);
1286 frame_buffer[stub_offset] = direction;
1290 frame_buffer[stub_offset] = (guint8)encap;
1297 /**************************************************************/
1298 /* Set pseudo-header info depending upon packet encapsulation */
1299 /**************************************************************/
1301 set_pseudo_header_info(wtap *wth,
1304 union wtap_pseudo_header *pseudo_header,
1305 packet_direction_t direction,
1306 gchar *aal_header_chars)
1308 pseudo_header->dct2000.seek_off = file_offset;
1309 pseudo_header->dct2000.wth = wth;
1311 switch (pkt_encap) {
1312 case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1313 set_aal_info(pseudo_header, direction, aal_header_chars);
1315 case WTAP_ENCAP_ISDN:
1316 set_isdn_info(pseudo_header, direction);
1318 case WTAP_ENCAP_PPP:
1319 set_ppp_info(pseudo_header, direction);
1323 /* Other supported types don't need to set anything here... */
1329 /*********************************************/
1330 /* Fill in atm pseudo-header with known info */
1331 /*********************************************/
1333 set_aal_info(union wtap_pseudo_header *pseudo_header,
1334 packet_direction_t direction,
1335 gchar *aal_header_chars)
1337 /* 'aal_head_chars' has this format (for AAL2 at least):
1338 Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1339 Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1340 Channel Identifier (8 bits) | ...
1343 /* Indicate that this is a reassembled PDU */
1344 pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1346 /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1347 TODO: Can we infer the correct value here?
1348 Meanwhile, just use the direction to make them distinguishable...
1350 pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1352 /* Assume always AAL2 for FP */
1353 pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1355 pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1356 pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1358 /* vpi is 8 bits (2nd & 3rd nibble) */
1359 pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1360 hex_byte_from_chars(aal_header_chars+1);
1362 /* vci is next 16 bits */
1363 pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1364 ((hex_from_char(aal_header_chars[3]) << 12) |
1365 (hex_from_char(aal_header_chars[4]) << 8) |
1366 (hex_from_char(aal_header_chars[5]) << 4) |
1367 hex_from_char(aal_header_chars[6]));
1369 /* 0 means we don't know how many cells the frame comprises. */
1370 pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1372 /* cid is usually last byte. Unless last char is not hex digit, in which
1373 case cid is derived from last char in ascii */
1374 if (isalnum((guchar)aal_header_chars[11])) {
1375 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1376 hex_byte_from_chars(aal_header_chars+10);
1379 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1380 (int)aal_header_chars[11] - '0';
1385 /**********************************************/
1386 /* Fill in isdn pseudo-header with known info */
1387 /**********************************************/
1389 set_isdn_info(union wtap_pseudo_header *pseudo_header,
1390 packet_direction_t direction)
1392 /* This field is used to set the 'Source' and 'Destination' columns to
1393 'User' or 'Network'. If we assume that we're simulating the network,
1394 treat Received messages as being destined for the network.
1396 pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1398 /* This corresponds to the circuit ID. 0 is treated as LAPD,
1399 everything else would be treated as a B-channel
1401 pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1405 /*********************************************/
1406 /* Fill in ppp pseudo-header with known info */
1407 /*********************************************/
1409 set_ppp_info(union wtap_pseudo_header *pseudo_header,
1410 packet_direction_t direction)
1412 /* Set direction. */
1413 pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1417 /********************************************************/
1418 /* Return hex nibble equivalent of hex string character */
1419 /********************************************************/
1421 hex_from_char(gchar c)
1423 if ((c >= '0') && (c <= '9')) {
1427 if ((c >= 'a') && (c <= 'f')) {
1428 return 0x0a + (c - 'a');
1431 /* Not a valid hex string character */
1437 /* Table allowing fast lookup from a pair of ascii hex characters to a guint8 */
1438 static guint8 s_tableValues[255][255];
1440 /* Prepare table values so ready so don't need to check inside hex_byte_from_chars() */
1441 static void prepare_hex_byte_from_chars_table(void)
1443 guchar hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1444 'a', 'b', 'c', 'd', 'e', 'f' };
1447 for (i=0; i < 16; i++) {
1448 for (j=0; j < 16; j++) {
1449 s_tableValues[hex_char_array[i]][hex_char_array[j]] = i*16 + j;
1454 /* Extract and return a byte value from 2 ascii hex chars, starting from the given pointer */
1455 static guint8 hex_byte_from_chars(gchar *c)
1457 /* Return value from quick table lookup */
1458 return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
1463 /********************************************************/
1464 /* Return character corresponding to hex nibble value */
1465 /********************************************************/
1467 char_from_hex(guint8 hex)
1469 static char hex_lookup[16] =
1470 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1476 return hex_lookup[hex];
1479 /***********************************************/
1480 /* Equality test for packet prefix hash tables */
1481 /***********************************************/
1483 packet_offset_equal(gconstpointer v, gconstpointer v2)
1485 /* Dereferenced pointers must have same gint64 offset value */
1486 return (*(const gint64*)v == *(const gint64*)v2);
1490 /********************************************/
1491 /* Hash function for packet-prefix hash table */
1492 /********************************************/
1494 packet_offset_hash_func(gconstpointer v)
1496 /* Use low-order bits of gint64 offset value */
1497 return (guint)(*(const gint64*)v);
1501 /************************************************************************/
1502 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1503 /* Set secs and usecs as output */
1504 /* Return FALSE if no valid time can be read */
1505 /************************************************************************/
1507 get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs)
1511 #define MAX_MONTH_LETTERS 9
1512 char month[MAX_MONTH_LETTERS+1];
1514 int day, year, hour, minute, second;
1517 /* If line longer than expected, file is probably not correctly formatted */
1518 if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
1522 /**************************************************************/
1523 /* First is month. Read until get a space following the month */
1524 for (n=0; (linebuff[n] != ' ') && (n < MAX_MONTH_LETTERS); n++) {
1525 month[n] = linebuff[n];
1529 if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
1530 else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
1531 else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
1532 else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
1533 else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
1534 else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
1535 else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
1536 else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
1537 else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
1538 else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
1539 else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
1540 else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
1542 /* Give up if not found a properly-formatted date */
1545 /* Skip space char */
1548 /********************************************************/
1549 /* Scan for remaining numerical fields */
1550 scan_found = sscanf(linebuff+n, "%2d, %4d %2d:%2d:%2d.%4u",
1551 &day, &year, &hour, &minute, &second, usecs);
1552 if (scan_found != 6) {
1553 /* Give up if not all found */
1557 /******************************************************/
1558 /* Fill in remaining fields and return it in a time_t */
1559 tm.tm_year = year - 1900;
1564 tm.tm_isdst = -1; /* daylight saving time info not known */
1566 /* Get seconds from this time */
1567 *secs = mktime(&tm);
1569 /* Multiply 4 digits given to get micro-seconds */
1570 *usecs = *usecs * 100;
1575 /* Free the data allocated inside a line_prefix_info_t */
1577 free_line_prefix_info(gpointer key, gpointer value,
1578 gpointer user_data _U_)
1580 line_prefix_info_t *info = (line_prefix_info_t*)value;
1582 /* Free the 64-bit key value */
1585 /* Free the strings inside */
1586 g_free(info->before_time);
1587 if (info->after_time) {
1588 g_free(info->after_time);
1591 /* And the structure itself */
1594 /* Item will always be removed from table */