DCERPC: factor out proto_tree_add_dcerpc_drep()
[wireshark-wip.git] / wiretap / catapult_dct2000.c
blob22a04f6f925a92b4714acb21ea5f0eaed5b38490
1 /* catapult_dct2000.c
3 * $Id$
5 * Wiretap Library
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.
23 #include "config.h"
24 #include <errno.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <ctype.h>
29 #include "wtap-int.h"
30 #include "file_wrappers.h"
31 #include "buffer.h"
33 #include "catapult_dct2000.h"
35 #define MAX_FIRST_LINE_LENGTH 200
36 #define MAX_TIMESTAMP_LINE_LENGTH 100
37 #define MAX_LINE_LENGTH 65536
38 #define MAX_TIMESTAMP_LEN 32
39 #define MAX_SECONDS_CHARS 16
40 #define MAX_SUBSECOND_DECIMALS 4
41 #define MAX_CONTEXT_NAME 64
42 #define MAX_PROTOCOL_NAME 64
43 #define MAX_PORT_DIGITS 2
44 #define MAX_VARIANT_DIGITS 32
45 #define MAX_OUTHDR_NAME 256
46 #define AAL_HEADER_CHARS 12
48 /* TODO:
49 - support for FP over AAL0
50 - support for IuR interface FP
51 - support for x.25?
54 /* 's' or 'r' of a packet as read from .out file */
55 typedef enum packet_direction_t
57 sent,
58 received
59 } packet_direction_t;
62 /***********************************************************************/
63 /* For each line, store (in case we need to dump): */
64 /* - String before time field */
65 /* - String beween time field and data (if NULL assume " l ") */
66 typedef struct
68 gchar *before_time;
69 gchar *after_time;
70 } line_prefix_info_t;
73 /*******************************************************************/
74 /* Information stored external to a file (wtap) needed for reading and dumping */
75 typedef struct dct2000_file_externals
77 /* Remember the time at the start of capture */
78 time_t start_secs;
79 guint32 start_usecs;
82 * The following information is needed only for dumping.
84 * XXX - Wiretap is not *supposed* to require that a packet being
85 * dumped come from a file of the same type that you currently have
86 * open; this should be fixed.
89 /* Buffer to hold first line, including magic and format number */
90 gchar firstline[MAX_FIRST_LINE_LENGTH];
91 gint firstline_length;
93 /* Buffer to hold second line with formatted file creation data/time */
94 gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
95 gint secondline_length;
97 /* Hash table to store text prefix data part of displayed packets.
98 Records (file offset -> line_prefix_info_t)
100 GHashTable *packet_prefix_table;
101 } dct2000_file_externals_t;
103 /* 'Magic number' at start of Catapult DCT2000 .out files. */
104 static const gchar catapult_dct2000_magic[] = "Session Transcript";
106 /************************************************************/
107 /* Functions called from wiretap core */
108 static gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
109 gint64 *data_offset);
110 static gboolean catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
111 struct wtap_pkthdr *phdr,
112 Buffer *buf, int length,
113 int *err, gchar **err_info);
114 static void catapult_dct2000_close(wtap *wth);
116 static gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
117 const guint8 *pd, int *err);
120 /************************************************************/
121 /* Private helper functions */
122 static gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length,
123 gchar *buf, size_t bufsize, int *err,
124 gchar **err_info);
125 static gboolean parse_line(char *linebuff, gint line_length,
126 gint *seconds, gint *useconds,
127 long *before_time_offset, long *after_time_offset,
128 long *data_offset,
129 gint *data_chars,
130 packet_direction_t *direction,
131 int *encap, int *is_comment, int *is_sprint,
132 gchar *aal_header_chars,
133 gchar *context_name, guint8 *context_portp,
134 gchar *protocol_name, gchar *variant_name,
135 gchar *outhdr_name);
136 static void process_parsed_line(wtap *wth,
137 dct2000_file_externals_t *file_externals,
138 struct wtap_pkthdr *phdr,
139 Buffer *buf, gint64 file_offset,
140 char *linebuff, long dollar_offset,
141 int seconds, int useconds, gchar *timestamp_string,
142 packet_direction_t direction, int encap,
143 gchar *context_name, guint8 context_port,
144 gchar *protocol_name, gchar *variant_name,
145 gchar *outhdr_name, gchar *aal_header_chars,
146 gboolean is_comment, int data_chars);
147 static guint8 hex_from_char(gchar c);
148 static void prepare_hex_byte_from_chars_table(void);
149 static guint8 hex_byte_from_chars(gchar *c);
150 static gchar char_from_hex(guint8 hex);
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 /********************************************/
172 catapult_dct2000_open(wtap *wth, int *err, gchar **err_info)
174 gint64 offset = 0;
175 time_t timestamp;
176 guint32 usecs;
177 gint firstline_length = 0;
178 dct2000_file_externals_t *file_externals;
179 static gchar linebuff[MAX_LINE_LENGTH];
180 static gboolean hex_byte_table_values_set = FALSE;
182 /* Clear errno before reading from the file */
183 errno = 0;
186 /********************************************************************/
187 /* First line needs to contain at least as many characters as magic */
189 if (!read_new_line(wth->fh, &offset, &firstline_length, linebuff,
190 sizeof linebuff, err, err_info)) {
191 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
192 return -1;
193 return 0;
195 if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
196 firstline_length >= MAX_FIRST_LINE_LENGTH) {
198 return 0;
201 /* This file is not for us if it doesn't match our signature */
202 if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
203 return 0;
206 /* Make sure table is ready for use */
207 if (!hex_byte_table_values_set) {
208 prepare_hex_byte_from_chars_table();
209 hex_byte_table_values_set = TRUE;
212 /*********************************************************************/
213 /* Need entry in file_externals table */
215 /* Allocate a new file_externals structure for this file */
216 file_externals = g_new(dct2000_file_externals_t,1);
217 memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
219 /* Copy this first line into buffer so could write out later */
220 g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
221 file_externals->firstline_length = firstline_length;
224 /***********************************************************/
225 /* Second line contains file timestamp */
226 /* Store this offset in in file_externals */
228 if (!read_new_line(wth->fh, &offset, &(file_externals->secondline_length),
229 linebuff, sizeof linebuff, err, err_info)) {
230 g_free(file_externals);
231 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
232 return -1;
233 return 0;
235 if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
236 (!get_file_time_stamp(linebuff, &timestamp, &usecs))) {
238 /* Give up if file time line wasn't valid */
239 g_free(file_externals);
240 return 0;
243 /* Fill in timestamp */
244 file_externals->start_secs = timestamp;
245 file_externals->start_usecs = usecs;
247 /* Copy this second line into buffer so could write out later */
248 g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
251 /************************************************************/
252 /* File is for us. Fill in details so packets can be read */
254 /* Set our file type */
255 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_CATAPULT_DCT2000;
257 /* Use our own encapsulation to send all packets to our stub dissector */
258 wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
260 /* Callbacks for reading operations */
261 wth->subtype_read = catapult_dct2000_read;
262 wth->subtype_seek_read = catapult_dct2000_seek_read;
263 wth->subtype_close = catapult_dct2000_close;
265 /* Choose microseconds (have 4 decimal places...) */
266 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
269 /***************************************************************/
270 /* Initialise packet_prefix_table (index is offset into file) */
271 file_externals->packet_prefix_table =
272 g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
274 /* Set this wtap to point to the file_externals */
275 wth->priv = (void*)file_externals;
277 *err = errno;
278 return 1;
281 /* Ugly, but much faster than using g_snprintf! */
282 static void write_timestamp_string(char *timestamp_string, int secs, int tenthousandths)
284 int idx = 0;
286 /* Secs */
287 if (secs < 10) {
288 timestamp_string[idx++] = ((secs % 10)) + '0';
290 else if (secs < 100) {
291 timestamp_string[idx++] = ( secs / 10) + '0';
292 timestamp_string[idx++] = ((secs % 10)) + '0';
294 else if (secs < 1000) {
295 timestamp_string[idx++] = ((secs) / 100) + '0';
296 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
297 timestamp_string[idx++] = ((secs % 10)) + '0';
299 else if (secs < 10000) {
300 timestamp_string[idx++] = ((secs) / 1000) + '0';
301 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
302 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
303 timestamp_string[idx++] = ((secs % 10)) + '0';
305 else if (secs < 100000) {
306 timestamp_string[idx++] = ((secs) / 10000) + '0';
307 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
308 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
309 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
310 timestamp_string[idx++] = ((secs % 10)) + '0';
312 else if (secs < 1000000) {
313 timestamp_string[idx++] = ((secs) / 100000) + '0';
314 timestamp_string[idx++] = ((secs % 100000)) / 10000 + '0';
315 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
316 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
317 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
318 timestamp_string[idx++] = ((secs % 10)) + '0';
320 else {
321 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", secs, tenthousandths);
322 return;
325 timestamp_string[idx++] = '.';
326 timestamp_string[idx++] = ( tenthousandths / 1000) + '0';
327 timestamp_string[idx++] = ((tenthousandths % 1000) / 100) + '0';
328 timestamp_string[idx++] = ((tenthousandths % 100) / 10) + '0';
329 timestamp_string[idx++] = ((tenthousandths % 10)) + '0';
330 timestamp_string[idx++] = '\0';
333 /**************************************************/
334 /* Read packet function. */
335 /* Look for and read the next usable packet */
336 /* - return TRUE and details if found */
337 /**************************************************/
338 static gboolean
339 catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
340 gint64 *data_offset)
342 gint64 offset = file_tell(wth->fh);
343 long dollar_offset, before_time_offset, after_time_offset;
344 packet_direction_t direction;
345 int encap;
347 /* Get wtap external structure for this wtap */
348 dct2000_file_externals_t *file_externals =
349 (dct2000_file_externals_t*)wth->priv;
351 /* Search for a line containing a usable packet */
352 while (1) {
353 int line_length, seconds, useconds, data_chars;
354 int is_comment = FALSE;
355 int is_sprint = FALSE;
356 gint64 this_offset = offset;
357 static gchar linebuff[MAX_LINE_LENGTH+1];
358 gchar aal_header_chars[AAL_HEADER_CHARS];
359 gchar context_name[MAX_CONTEXT_NAME];
360 guint8 context_port = 0;
361 gchar protocol_name[MAX_PROTOCOL_NAME+1];
362 gchar variant_name[MAX_VARIANT_DIGITS+1];
363 gchar outhdr_name[MAX_OUTHDR_NAME+1];
365 /* Are looking for first packet after 2nd line */
366 if (file_tell(wth->fh) == 0) {
367 this_offset += (file_externals->firstline_length+1+
368 file_externals->secondline_length+1);
371 /* Read a new line from file into linebuff */
372 if (!read_new_line(wth->fh, &offset, &line_length, linebuff,
373 sizeof linebuff, err, err_info)) {
374 if (*err != 0)
375 return FALSE; /* error */
376 /* No more lines can be read, so quit. */
377 break;
380 /* Try to parse the line as a frame record */
381 if (parse_line(linebuff, line_length, &seconds, &useconds,
382 &before_time_offset, &after_time_offset,
383 &dollar_offset,
384 &data_chars, &direction, &encap, &is_comment, &is_sprint,
385 aal_header_chars,
386 context_name, &context_port,
387 protocol_name, variant_name, outhdr_name)) {
388 line_prefix_info_t *line_prefix_info;
389 char timestamp_string[MAX_TIMESTAMP_LEN+1];
390 gint64 *pkey = NULL;
392 write_timestamp_string(timestamp_string, seconds, useconds/100);
394 /* Set data_offset to the beginning of the line we're returning.
395 This will be the seek_off parameter when this frame is re-read.
397 *data_offset = this_offset;
399 process_parsed_line(wth, file_externals,
400 &wth->phdr,
401 wth->frame_buffer, this_offset,
402 linebuff, dollar_offset,
403 seconds, useconds, timestamp_string,
404 direction, encap,
405 context_name, context_port,
406 protocol_name, variant_name,
407 outhdr_name, aal_header_chars,
408 is_comment, data_chars);
410 /* Store the packet prefix in the hash table */
411 line_prefix_info = g_new(line_prefix_info_t,1);
413 /* Create and use buffer for contents before time */
414 line_prefix_info->before_time = (gchar *)g_malloc(before_time_offset+1);
415 memcpy(line_prefix_info->before_time, linebuff, before_time_offset);
416 line_prefix_info->before_time[before_time_offset] = '\0';
418 /* Create and use buffer for contents before time.
419 Do this only if it doesn't correspond to " l ", which is by far the most
420 common case. */
421 if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
422 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0)) {
424 line_prefix_info->after_time = NULL;
426 else {
427 /* Allocate & write buffer for line between timestamp and data */
428 line_prefix_info->after_time = (gchar *)g_malloc(dollar_offset - after_time_offset);
429 memcpy(line_prefix_info->after_time, linebuff+after_time_offset, dollar_offset - after_time_offset);
430 line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
433 /* Add packet entry into table */
434 pkey = (gint64 *)g_malloc(sizeof(*pkey));
435 *pkey = this_offset;
436 g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
438 /* OK, we have packet details to return */
439 return TRUE;
443 /* No packet details to return... */
444 return FALSE;
448 /**************************************************/
449 /* Read & seek function. */
450 /**************************************************/
451 static gboolean
452 catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
453 struct wtap_pkthdr *phdr, Buffer *buf,
454 int length, int *err, gchar **err_info)
456 gint64 offset = 0;
457 long dollar_offset, before_time_offset, after_time_offset;
458 static gchar linebuff[MAX_LINE_LENGTH+1];
459 gchar aal_header_chars[AAL_HEADER_CHARS];
460 gchar context_name[MAX_CONTEXT_NAME];
461 guint8 context_port = 0;
462 gchar protocol_name[MAX_PROTOCOL_NAME+1];
463 gchar variant_name[MAX_VARIANT_DIGITS+1];
464 gchar outhdr_name[MAX_OUTHDR_NAME+1];
465 int is_comment = FALSE;
466 int is_sprint = FALSE;
467 packet_direction_t direction;
468 int encap;
469 int seconds, useconds, data_chars;
471 /* Get wtap external structure for this wtap */
472 dct2000_file_externals_t *file_externals =
473 (dct2000_file_externals_t*)wth->priv;
475 /* Reset errno */
476 *err = errno = 0;
478 /* Seek to beginning of packet */
479 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
480 return FALSE;
483 /* Re-read whole line (this really should succeed) */
484 if (!read_new_line(wth->random_fh, &offset, &length, linebuff,
485 sizeof linebuff, err, err_info)) {
486 return FALSE;
489 /* Try to parse this line again (should succeed as re-reading...) */
490 if (parse_line(linebuff, length, &seconds, &useconds,
491 &before_time_offset, &after_time_offset,
492 &dollar_offset,
493 &data_chars, &direction, &encap, &is_comment, &is_sprint,
494 aal_header_chars,
495 context_name, &context_port,
496 protocol_name, variant_name, outhdr_name)) {
497 char timestamp_string[MAX_TIMESTAMP_LEN+1];
499 write_timestamp_string(timestamp_string, seconds, useconds/100);
501 process_parsed_line(wth, file_externals,
502 phdr, buf, seek_off,
503 linebuff, dollar_offset,
504 seconds, useconds, timestamp_string,
505 direction, encap,
506 context_name, context_port,
507 protocol_name, variant_name,
508 outhdr_name, aal_header_chars,
509 is_comment, data_chars);
511 *err = errno = 0;
512 return TRUE;
515 /* If get here, must have failed */
516 *err = errno;
517 *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
518 "line at position %" G_GINT64_MODIFIER "d",
519 seek_off);
520 return FALSE;
524 /***************************************************************************/
525 /* Free dct2000-specific capture info from file that was open for reading */
526 /***************************************************************************/
527 static void
528 catapult_dct2000_close(wtap *wth)
530 /* Get externals for this file */
531 dct2000_file_externals_t *file_externals =
532 (dct2000_file_externals_t*)wth->priv;
534 /* Free up its line prefix values */
535 g_hash_table_foreach_remove(file_externals->packet_prefix_table,
536 free_line_prefix_info, NULL);
537 /* Free up its line prefix table */
538 g_hash_table_destroy(file_externals->packet_prefix_table);
544 /***************************/
545 /* Dump functions */
546 /***************************/
548 typedef struct {
549 gboolean first_packet_written;
550 nstime_t start_time;
551 } dct2000_dump_t;
553 /*****************************************************/
554 /* The file that we are writing to has been opened. */
555 /* Set other dump callbacks. */
556 /*****************************************************/
557 gboolean
558 catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_)
560 /* Fill in other dump callbacks */
561 wdh->subtype_write = catapult_dct2000_dump;
563 return TRUE;
566 /*********************************************************/
567 /* Respond to queries about which encap types we support */
568 /* writing to. */
569 /*********************************************************/
571 catapult_dct2000_dump_can_write_encap(int encap)
573 switch (encap) {
574 case WTAP_ENCAP_CATAPULT_DCT2000:
575 /* We support this */
576 return 0;
578 default:
579 /* But don't write to any other formats... */
580 return WTAP_ERR_UNSUPPORTED_ENCAP;
585 /*****************************************/
586 /* Write a single packet out to the file */
587 /*****************************************/
589 static gboolean
590 catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
591 const guint8 *pd, int *err)
593 const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
594 guint32 n;
595 line_prefix_info_t *prefix = NULL;
596 gchar time_string[16];
597 gboolean is_comment;
598 gboolean is_sprint = FALSE;
599 dct2000_dump_t *dct2000;
600 int consecutive_slashes=0;
601 char *p_c;
603 /******************************************************/
604 /* Get the file_externals structure for this file */
605 /* Find wtap external structure for this wtap */
606 dct2000_file_externals_t *file_externals =
607 (dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
609 dct2000 = (dct2000_dump_t *)wdh->priv;
610 if (dct2000 == NULL) {
612 /* Write out saved first line */
613 if (!wtap_dump_file_write(wdh, file_externals->firstline,
614 file_externals->firstline_length, err)) {
615 return FALSE;
617 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
618 return FALSE;
621 /* Also write out saved second line with timestamp corresponding to the
622 opening time of the log.
624 if (!wtap_dump_file_write(wdh, file_externals->secondline,
625 file_externals->secondline_length, err)) {
626 return FALSE;
628 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
629 return FALSE;
632 /* Allocate the dct2000-specific dump structure */
633 dct2000 = (dct2000_dump_t *)g_malloc(sizeof(dct2000_dump_t));
634 wdh->priv = (void *)dct2000;
636 /* Copy time of beginning of file */
637 dct2000->start_time.secs = file_externals->start_secs;
638 dct2000->start_time.nsecs =
639 (file_externals->start_usecs * 1000);
641 /* Set flag so don't write header out again */
642 dct2000->first_packet_written = TRUE;
646 /******************************************************************/
647 /* Write out this packet's prefix, including calculated timestamp */
649 /* Look up line data prefix using stored offset */
650 prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
651 (const void*)&(pseudo_header->dct2000.seek_off));
653 /* Write out text before timestamp */
654 if (!wtap_dump_file_write(wdh, prefix->before_time,
655 strlen(prefix->before_time), err)) {
656 return FALSE;
659 /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
660 /* This is much faster than strstr() for "/////" */
661 p_c = prefix->before_time;
662 while (p_c && (*p_c != '/')) {
663 p_c++;
665 while (p_c && (*p_c == '/')) {
666 consecutive_slashes++;
667 p_c++;
669 is_comment = (consecutive_slashes == 5);
671 /* Calculate time of this packet to write, relative to start of dump */
672 if (phdr->ts.nsecs >= dct2000->start_time.nsecs) {
673 write_timestamp_string(time_string,
674 (int)(phdr->ts.secs - dct2000->start_time.secs),
675 (phdr->ts.nsecs - dct2000->start_time.nsecs) / 100000);
677 else {
678 write_timestamp_string(time_string,
679 (int)(phdr->ts.secs - dct2000->start_time.secs-1),
680 ((1000000000 + (phdr->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
683 /* Write out the calculated timestamp */
684 if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
685 return FALSE;
688 /* Write out text between timestamp and start of hex data */
689 if (prefix->after_time == NULL) {
690 if (!wtap_dump_file_write(wdh, " l ", strlen(" l "), err)) {
691 return FALSE;
694 else {
695 if (!wtap_dump_file_write(wdh, prefix->after_time,
696 strlen(prefix->after_time), err)) {
697 return FALSE;
702 /****************************************************************/
703 /* Need to skip stub header at start of pd before we reach data */
705 /* Context name */
706 for (n=0; pd[n] != '\0'; n++);
707 n++;
709 /* Context port number */
710 n++;
712 /* Timestamp */
713 for (; pd[n] != '\0'; n++);
714 n++;
716 /* Protocol name */
717 if (is_comment) {
718 is_sprint = strcmp((const char *)pd+n, "sprint") == 0;
720 for (; pd[n] != '\0'; n++);
721 n++;
723 /* Variant number (as string) */
724 for (; pd[n] != '\0'; n++);
725 n++;
727 /* Outhdr (as string) */
728 for (; pd[n] != '\0'; n++);
729 n++;
731 /* Direction & encap */
732 n += 2;
735 /**************************************/
736 /* Remainder is encapsulated protocol */
737 if (!wtap_dump_file_write(wdh, is_sprint ? " " : "$", 1, err)) {
738 return FALSE;
741 if (!is_comment) {
742 /* Each binary byte is written out as 2 hex string chars */
743 for (; n < phdr->len; n++) {
744 gchar c[2];
745 c[0] = char_from_hex((guint8)(pd[n] >> 4));
746 c[1] = char_from_hex((guint8)(pd[n] & 0x0f));
748 /* Write both hex chars of byte together */
749 if (!wtap_dump_file_write(wdh, c, 2, err)) {
750 return FALSE;
754 else {
755 for (; n < phdr->len; n++) {
756 char c[1];
757 c[0] = pd[n];
759 /* Write both hex chars of byte together */
760 if (!wtap_dump_file_write(wdh, c, 1, err)) {
761 return FALSE;
766 /* End the line */
767 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
768 return FALSE;
771 return TRUE;
775 /****************************/
776 /* Private helper functions */
777 /****************************/
779 /**********************************************************************/
780 /* Read a new line from the file, starting at offset. */
781 /* - writes data to its argument linebuff */
782 /* - on return 'offset' will point to the next position to read from */
783 /* - return TRUE if this read is successful */
784 /**********************************************************************/
785 static gboolean
786 read_new_line(FILE_T fh, gint64 *offset, gint *length,
787 gchar *linebuff, size_t linebuffsize, int *err, gchar **err_info)
789 /* Read in a line */
790 gint64 pos_before = file_tell(fh);
792 if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
793 /* No characters found, or error */
794 *err = file_error(fh, err_info);
795 return FALSE;
798 /* Set length (avoiding strlen()) and offset.. */
799 *length = (gint)(file_tell(fh) - pos_before);
800 *offset = *offset + *length;
802 /* ...but don't want to include newline in line length */
803 if (linebuff[*length-1] == '\n') {
804 linebuff[*length-1] = '\0';
805 *length = *length - 1;
807 /* Nor do we want '\r' (as will be written when log is created on windows) */
808 if (linebuff[*length-1] == '\r') {
809 linebuff[*length-1] = '\0';
810 *length = *length - 1;
813 return TRUE;
817 /**********************************************************************/
818 /* Parse a line from buffer, by identifying: */
819 /* - context, port and direction of packet */
820 /* - timestamp */
821 /* - data position and length */
822 /* Return TRUE if this packet looks valid and can be displayed */
823 /**********************************************************************/
824 static gboolean
825 parse_line(gchar *linebuff, gint line_length,
826 gint *seconds, gint *useconds,
827 long *before_time_offset, long *after_time_offset,
828 long *data_offset, gint *data_chars,
829 packet_direction_t *direction,
830 int *encap, int *is_comment, int *is_sprint,
831 gchar *aal_header_chars,
832 gchar *context_name, guint8 *context_portp,
833 gchar *protocol_name, gchar *variant_name,
834 gchar *outhdr_name)
836 int n = 0;
837 int port_digits;
838 char port_number_string[MAX_PORT_DIGITS+1];
839 int variant_digits = 0;
840 int variant = 1;
841 int protocol_chars = 0;
842 int outhdr_chars = 0;
844 char seconds_buff[MAX_SECONDS_CHARS+1];
845 int seconds_chars;
846 char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
847 int subsecond_decimals_chars;
848 int skip_first_byte = FALSE;
849 gboolean atm_header_present = FALSE;
851 *is_comment = FALSE;
852 *is_sprint = FALSE;
854 /* Read context name until find '.' */
855 for (n=0; (linebuff[n] != '.') && (n < MAX_CONTEXT_NAME) && (n+1 < line_length); n++) {
856 if (linebuff[n] == '/') {
857 context_name[n] = '\0';
859 /* If not a comment (/////), not a valid line */
860 if (strncmp(linebuff+n, "/////", 5) != 0) {
861 return FALSE;
864 /* There is no variant, outhdr, etc. Set protocol to be a comment */
865 g_strlcpy(protocol_name, "comment", MAX_PROTOCOL_NAME);
866 *is_comment = TRUE;
867 break;
869 if (!isalnum((guchar)linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
870 return FALSE;
872 context_name[n] = linebuff[n];
874 if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
875 return FALSE;
878 /* Reset strings (that won't be set by comments) */
879 variant_name[0] = '\0';
880 outhdr_name[0] = '\0';
881 port_number_string[0] = '\0';
883 if (!(*is_comment)) {
884 /* '.' must follow context name */
885 if (linebuff[n] != '.') {
886 return FALSE;
888 context_name[n] = '\0';
889 /* Skip it */
890 n++;
892 /* Now read port number */
893 for (port_digits = 0;
894 (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
895 n++, port_digits++) {
897 if (!isdigit((guchar)linebuff[n])) {
898 return FALSE;
900 port_number_string[port_digits] = linebuff[n];
902 if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
903 return FALSE;
906 /* Slash char must follow port number */
907 if (linebuff[n] != '/')
909 return FALSE;
911 port_number_string[port_digits] = '\0';
912 if (port_digits == 1) {
913 *context_portp = port_number_string[0] - '0';
915 else {
916 *context_portp = atoi(port_number_string);
918 /* Skip it */
919 n++;
921 /* Now for the protocol name */
922 for (protocol_chars = 0;
923 (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
924 n++, protocol_chars++) {
926 if (!isalnum((guchar)linebuff[n]) && linebuff[n] != '_') {
927 return FALSE;
929 protocol_name[protocol_chars] = linebuff[n];
931 if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
932 /* If doesn't fit, fail rather than truncate */
933 return FALSE;
935 protocol_name[protocol_chars] = '\0';
937 /* Slash char must follow protocol name */
938 if (linebuff[n] != '/') {
939 return FALSE;
941 /* Skip it */
942 n++;
945 /* Following the / is the variant number. No digits indicate 1 */
946 for (variant_digits = 0;
947 (isdigit((guchar)linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
948 n++, variant_digits++) {
950 if (!isdigit((guchar)linebuff[n])) {
951 return FALSE;
953 variant_name[variant_digits] = linebuff[n];
955 if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
956 return FALSE;
959 if (variant_digits > 0) {
960 variant_name[variant_digits] = '\0';
961 if (variant_digits == 1) {
962 variant = variant_name[0] - '0';
964 else {
965 variant = atoi(variant_name);
968 else {
969 variant_name[0] = '1';
970 variant_name[1] = '\0';
974 /* Outheader values may follow */
975 outhdr_name[0] = '\0';
976 if (linebuff[n] == ',') {
977 /* Skip , */
978 n++;
980 for (outhdr_chars = 0;
981 (isdigit((guchar)linebuff[n]) || linebuff[n] == ',') &&
982 (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
983 n++, outhdr_chars++) {
985 if (!isdigit((guchar)linebuff[n]) && (linebuff[n] != ',')) {
986 return FALSE;
988 outhdr_name[outhdr_chars] = linebuff[n];
990 if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
991 return FALSE;
993 /* Terminate (possibly empty) string */
994 outhdr_name[outhdr_chars] = '\0';
999 /******************************************************************/
1000 /* Now check whether we know how to use a packet of this protocol */
1002 if ((strcmp(protocol_name, "ip") == 0) ||
1003 (strcmp(protocol_name, "sctp") == 0) ||
1004 (strcmp(protocol_name, "gre") == 0) ||
1005 (strcmp(protocol_name, "mipv6") == 0) ||
1006 (strcmp(protocol_name, "igmp") == 0)) {
1008 *encap = WTAP_ENCAP_RAW_IP;
1010 else
1012 /* FP may be carried over ATM, which has separate atm header to parse */
1013 if ((strcmp(protocol_name, "fp") == 0) ||
1014 (strncmp(protocol_name, "fp_r", 4) == 0)) {
1016 if ((variant > 256) && (variant % 256 == 3)) {
1017 /* FP over udp is contained in IPPrim... */
1018 *encap = 0;
1020 else {
1021 /* FP over AAL0 or AAL2 */
1022 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1023 atm_header_present = TRUE;
1026 else if (strcmp(protocol_name, "fpiur_r5") == 0) {
1027 /* FP (IuR) over AAL2 */
1028 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1029 atm_header_present = TRUE;
1032 else
1033 if (strcmp(protocol_name, "ppp") == 0) {
1034 *encap = WTAP_ENCAP_PPP;
1036 else
1037 if (strcmp(protocol_name, "isdn_l3") == 0) {
1038 /* TODO: find out what this byte means... */
1039 skip_first_byte = TRUE;
1040 *encap = WTAP_ENCAP_ISDN;
1042 else
1043 if (strcmp(protocol_name, "isdn_l2") == 0) {
1044 *encap = WTAP_ENCAP_ISDN;
1046 else
1047 if (strcmp(protocol_name, "ethernet") == 0) {
1048 *encap = WTAP_ENCAP_ETHERNET;
1050 else
1051 if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1052 (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1054 *encap = DCT2000_ENCAP_SSCOP;
1056 else
1057 if (strcmp(protocol_name, "frelay_l2") == 0) {
1058 *encap = WTAP_ENCAP_FRELAY;
1060 else
1061 if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1062 *encap = DCT2000_ENCAP_MTP2;
1064 else
1065 if ((strcmp(protocol_name, "nbap") == 0) ||
1066 (strcmp(protocol_name, "nbap_r4") == 0) ||
1067 (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
1069 /* The entire message in these cases is nbap, so use an encap value. */
1070 *encap = DCT2000_ENCAP_NBAP;
1072 else {
1073 /* Not a supported board port protocol/encap, but can show as raw data or
1074 in some cases find protocol embedded inside primitive */
1075 *encap = DCT2000_ENCAP_UNHANDLED;
1079 /* Find separate ATM header if necessary */
1080 if (atm_header_present) {
1081 int header_chars_seen = 0;
1083 /* Scan ahead to the next $ */
1084 for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1085 /* Skip it */
1086 n++;
1087 if (n+1 >= line_length) {
1088 return FALSE;
1091 /* Read consecutive hex chars into atm header buffer */
1092 for (;
1093 ((linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1094 (n < line_length) &&
1095 (header_chars_seen < AAL_HEADER_CHARS));
1096 n++, header_chars_seen++) {
1098 aal_header_chars[header_chars_seen] = linebuff[n];
1099 /* Next 6 characters after '9' are mapped to a->f */
1100 if (!isdigit((guchar)linebuff[n])) {
1101 aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1105 if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
1106 return FALSE;
1110 /* Skip next '/' */
1111 n++;
1113 /* If there is a number, skip all info to next '/'.
1114 TODO: for IP encapsulation, should store PDCP ueid, drb in pseudo info
1115 and display dct2000 dissector... */
1116 if (isdigit(linebuff[n])) {
1117 while ((n+1 < line_length) && linebuff[n] != '/') {
1118 n++;
1122 /* Skip '/' */
1123 while ((n+1 < line_length) && linebuff[n] == '/') {
1124 n++;
1127 /* Skip a space that may happen here */
1128 if ((n+1 < line_length) && linebuff[n] == ' ') {
1129 n++;
1132 /* Next character gives direction of message (must be 's' or 'r') */
1133 if (!(*is_comment)) {
1134 if (linebuff[n] == 's') {
1135 *direction = sent;
1137 else
1138 if (linebuff[n] == 'r') {
1139 *direction = received;
1141 else {
1142 return FALSE;
1144 /* Skip it */
1145 n++;
1147 else {
1148 *direction = sent;
1152 /*********************************************************************/
1153 /* Find and read the timestamp */
1155 /* Now scan to the next digit, which should be the start of the timestamp */
1156 /* This will involve skipping " tm " */
1158 for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
1159 if (n >= line_length) {
1160 return FALSE;
1163 for (; !isdigit((guchar)linebuff[n]) && (n < line_length); n++);
1164 if (n >= line_length) {
1165 return FALSE;
1168 *before_time_offset = n;
1170 /* Seconds */
1171 for (seconds_chars = 0;
1172 (linebuff[n] != '.') &&
1173 (seconds_chars <= MAX_SECONDS_CHARS) &&
1174 (n < line_length);
1175 n++, seconds_chars++) {
1177 if (!isdigit((guchar)linebuff[n])) {
1178 /* Found a non-digit before decimal point. Fail */
1179 return FALSE;
1181 seconds_buff[seconds_chars] = linebuff[n];
1183 if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
1184 /* Didn't fit in buffer. Fail rather than use truncated */
1185 return FALSE;
1188 /* Convert found value into number */
1189 seconds_buff[seconds_chars] = '\0';
1190 *seconds = atoi(seconds_buff);
1192 /* The decimal point must follow the last of the seconds digits */
1193 if (linebuff[n] != '.') {
1194 return FALSE;
1196 /* Skip it */
1197 n++;
1199 /* Subsecond decimal digits (expect 4-digit accuracy) */
1200 for (subsecond_decimals_chars = 0;
1201 (linebuff[n] != ' ') &&
1202 (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1203 (n < line_length);
1204 n++, subsecond_decimals_chars++) {
1206 if (!isdigit((guchar)linebuff[n])) {
1207 return FALSE;
1209 subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1211 if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length) {
1212 /* More numbers than expected - give up */
1213 return FALSE;
1215 /* Convert found value into microseconds */
1216 subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1217 *useconds = atoi(subsecond_decimals_buff) * 100;
1219 /* Space character must follow end of timestamp */
1220 if (linebuff[n] != ' ') {
1221 return FALSE;
1224 *after_time_offset = n++;
1226 /* If we have a string message, it could either be a comment (with '$') or
1227 a sprint line (no '$') */
1228 if (*is_comment) {
1229 if (strncmp(linebuff+n, "l $", 3) != 0) {
1230 *is_sprint = TRUE;
1231 g_strlcpy(protocol_name, "sprint", MAX_PROTOCOL_NAME);
1235 if (!(*is_sprint)) {
1236 /* Now skip ahead to find start of data (marked by '$') */
1237 for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
1238 if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
1239 return FALSE;
1241 /* Skip it */
1242 n++;
1245 /* Set offset to data start within line */
1246 *data_offset = n;
1248 /* Set number of chars that comprise the hex string protocol data */
1249 *data_chars = line_length - n;
1251 /* May need to skip first byte (2 hex string chars) */
1252 if (skip_first_byte) {
1253 *data_offset += 2;
1254 *data_chars -= 2;
1257 return TRUE;
1260 /***********************************/
1261 /* Process results of parse_line() */
1262 /***********************************/
1263 static void
1264 process_parsed_line(wtap *wth, dct2000_file_externals_t *file_externals,
1265 struct wtap_pkthdr *phdr,
1266 Buffer *buf, gint64 file_offset,
1267 char *linebuff, long dollar_offset,
1268 int seconds, int useconds, gchar *timestamp_string,
1269 packet_direction_t direction, int encap,
1270 gchar *context_name, guint8 context_port,
1271 gchar *protocol_name, gchar *variant_name,
1272 gchar *outhdr_name, gchar *aal_header_chars,
1273 gboolean is_comment, int data_chars)
1275 int n;
1276 int stub_offset = 0;
1277 gsize length;
1278 guint8 *frame_buffer;
1280 phdr->presence_flags = WTAP_HAS_TS;
1282 /* Make sure all packets go to Catapult DCT2000 dissector */
1283 phdr->pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
1285 /* Fill in timestamp (capture base + packet offset) */
1286 phdr->ts.secs = file_externals->start_secs + seconds;
1287 if ((file_externals->start_usecs + useconds) >= 1000000) {
1288 phdr->ts.secs++;
1290 phdr->ts.nsecs =
1291 ((file_externals->start_usecs + useconds) % 1000000) *1000;
1293 /*****************************/
1294 /* Get the data buffer ready */
1295 buffer_assure_space(buf,
1296 strlen(context_name)+1 + /* Context name */
1297 1 + /* port */
1298 strlen(timestamp_string)+1 + /* timestamp */
1299 strlen(variant_name)+1 + /* variant */
1300 strlen(outhdr_name)+1 + /* outhdr */
1301 strlen(protocol_name)+1 + /* Protocol name */
1302 1 + /* direction */
1303 1 + /* encap */
1304 (is_comment ? data_chars : (data_chars/2)));
1305 frame_buffer = buffer_start_ptr(buf);
1307 /******************************************/
1308 /* Write the stub info to the data buffer */
1310 /* Context name */
1311 length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1312 stub_offset += (int)(length + 1);
1314 /* Context port number */
1315 frame_buffer[stub_offset] = context_port;
1316 stub_offset++;
1318 /* Timestamp within file */
1319 length = g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
1320 stub_offset += (int)(length + 1);
1322 /* Protocol name */
1323 length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1324 stub_offset += (int)(length + 1);
1326 /* Protocol variant number (as string) */
1327 length = g_strlcpy((gchar*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
1328 stub_offset += (int)(length + 1);
1330 /* Outhdr */
1331 length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1332 stub_offset += (int)(length + 1);
1334 /* Direction */
1335 frame_buffer[stub_offset] = direction;
1336 stub_offset++;
1338 /* Encap */
1339 frame_buffer[stub_offset] = (guint8)encap;
1340 stub_offset++;
1342 /* Binary data length is half bytestring length + stub header */
1343 phdr->len = stub_offset + (is_comment ? data_chars : (data_chars/2));
1344 phdr->caplen = stub_offset + (is_comment ? data_chars : (data_chars/2));
1346 if (!is_comment) {
1347 /***********************************************************/
1348 /* Copy packet data into buffer, converting from ascii hex */
1349 for (n=0; n < data_chars; n+=2) {
1350 frame_buffer[stub_offset + n/2] =
1351 hex_byte_from_chars(linebuff+dollar_offset+n);
1354 else {
1355 /***********************************************************/
1356 /* Copy packet data into buffer, just copying ascii chars */
1357 for (n=0; n < data_chars; n++) {
1358 frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
1362 /*****************************************/
1363 /* Set packet pseudo-header if necessary */
1364 phdr->pseudo_header.dct2000.seek_off = file_offset;
1365 phdr->pseudo_header.dct2000.wth = wth;
1367 switch (encap) {
1368 case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1369 set_aal_info(&phdr->pseudo_header, direction, aal_header_chars);
1370 break;
1371 case WTAP_ENCAP_ISDN:
1372 set_isdn_info(&phdr->pseudo_header, direction);
1373 break;
1374 case WTAP_ENCAP_PPP:
1375 set_ppp_info(&phdr->pseudo_header, direction);
1376 break;
1378 default:
1379 /* Other supported types don't need to set anything here... */
1380 break;
1384 /*********************************************/
1385 /* Fill in atm pseudo-header with known info */
1386 /*********************************************/
1387 static void
1388 set_aal_info(union wtap_pseudo_header *pseudo_header,
1389 packet_direction_t direction,
1390 gchar *aal_header_chars)
1392 /* 'aal_head_chars' has this format (for AAL2 at least):
1393 Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1394 Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1395 Channel Identifier (8 bits) | ...
1398 /* Indicate that this is a reassembled PDU */
1399 pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1401 /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1402 TODO: Can we infer the correct value here?
1403 Meanwhile, just use the direction to make them distinguishable...
1405 pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1407 /* Assume always AAL2 for FP */
1408 pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1410 pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1411 pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1413 /* vpi is 8 bits (2nd & 3rd nibble) */
1414 pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1415 hex_byte_from_chars(aal_header_chars+1);
1417 /* vci is next 16 bits */
1418 pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1419 ((hex_from_char(aal_header_chars[3]) << 12) |
1420 (hex_from_char(aal_header_chars[4]) << 8) |
1421 (hex_from_char(aal_header_chars[5]) << 4) |
1422 hex_from_char(aal_header_chars[6]));
1424 /* 0 means we don't know how many cells the frame comprises. */
1425 pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1427 /* cid is usually last byte. Unless last char is not hex digit, in which
1428 case cid is derived from last char in ascii */
1429 if (isalnum((guchar)aal_header_chars[11])) {
1430 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1431 hex_byte_from_chars(aal_header_chars+10);
1433 else {
1434 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1435 (int)aal_header_chars[11] - '0';
1440 /**********************************************/
1441 /* Fill in isdn pseudo-header with known info */
1442 /**********************************************/
1443 static void
1444 set_isdn_info(union wtap_pseudo_header *pseudo_header,
1445 packet_direction_t direction)
1447 /* This field is used to set the 'Source' and 'Destination' columns to
1448 'User' or 'Network'. If we assume that we're simulating the network,
1449 treat Received messages as being destined for the network.
1451 pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1453 /* This corresponds to the circuit ID. 0 is treated as LAPD,
1454 everything else would be treated as a B-channel
1456 pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1460 /*********************************************/
1461 /* Fill in ppp pseudo-header with known info */
1462 /*********************************************/
1463 static void
1464 set_ppp_info(union wtap_pseudo_header *pseudo_header,
1465 packet_direction_t direction)
1467 /* Set direction. */
1468 pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1472 /********************************************************/
1473 /* Return hex nibble equivalent of hex string character */
1474 /********************************************************/
1475 static guint8
1476 hex_from_char(gchar c)
1478 if ((c >= '0') && (c <= '9')) {
1479 return c - '0';
1482 if ((c >= 'a') && (c <= 'f')) {
1483 return 0x0a + (c - 'a');
1486 /* Not a valid hex string character */
1487 return 0xff;
1492 /* Table allowing fast lookup from a pair of ascii hex characters to a guint8 */
1493 static guint8 s_tableValues[255][255];
1495 /* Prepare table values so ready so don't need to check inside hex_byte_from_chars() */
1496 static void prepare_hex_byte_from_chars_table(void)
1498 guchar hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1499 'a', 'b', 'c', 'd', 'e', 'f' };
1501 gint i, j;
1502 for (i=0; i < 16; i++) {
1503 for (j=0; j < 16; j++) {
1504 s_tableValues[hex_char_array[i]][hex_char_array[j]] = i*16 + j;
1509 /* Extract and return a byte value from 2 ascii hex chars, starting from the given pointer */
1510 static guint8 hex_byte_from_chars(gchar *c)
1512 /* Return value from quick table lookup */
1513 return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
1518 /********************************************************/
1519 /* Return character corresponding to hex nibble value */
1520 /********************************************************/
1521 static gchar
1522 char_from_hex(guint8 hex)
1524 static const char hex_lookup[16] =
1525 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1527 if (hex > 15) {
1528 return '?';
1531 return hex_lookup[hex];
1534 /***********************************************/
1535 /* Equality test for packet prefix hash tables */
1536 /***********************************************/
1537 static gint
1538 packet_offset_equal(gconstpointer v, gconstpointer v2)
1540 /* Dereferenced pointers must have same gint64 offset value */
1541 return (*(const gint64*)v == *(const gint64*)v2);
1545 /********************************************/
1546 /* Hash function for packet-prefix hash table */
1547 /********************************************/
1548 static guint
1549 packet_offset_hash_func(gconstpointer v)
1551 /* Use low-order bits of gint64 offset value */
1552 return (guint)(*(const gint64*)v);
1556 /************************************************************************/
1557 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1558 /* Set secs and usecs as output */
1559 /* Return FALSE if no valid time can be read */
1560 /************************************************************************/
1561 static gboolean
1562 get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs)
1564 int n;
1565 struct tm tm;
1566 #define MAX_MONTH_LETTERS 9
1567 char month[MAX_MONTH_LETTERS+1];
1569 int day, year, hour, minute, second;
1570 int scan_found;
1572 /* If line longer than expected, file is probably not correctly formatted */
1573 if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
1574 return FALSE;
1577 /**************************************************************/
1578 /* First is month. Read until get a space following the month */
1579 for (n=0; (linebuff[n] != ' ') && (n < MAX_MONTH_LETTERS); n++) {
1580 month[n] = linebuff[n];
1582 month[n] = '\0';
1584 if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
1585 else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
1586 else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
1587 else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
1588 else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
1589 else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
1590 else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
1591 else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
1592 else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
1593 else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
1594 else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
1595 else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
1596 else {
1597 /* Give up if not found a properly-formatted date */
1598 return FALSE;
1600 /* Skip space char */
1601 n++;
1603 /********************************************************/
1604 /* Scan for remaining numerical fields */
1605 scan_found = sscanf(linebuff+n, "%2d, %4d %2d:%2d:%2d.%4u",
1606 &day, &year, &hour, &minute, &second, usecs);
1607 if (scan_found != 6) {
1608 /* Give up if not all found */
1609 return FALSE;
1612 /******************************************************/
1613 /* Fill in remaining fields and return it in a time_t */
1614 tm.tm_year = year - 1900;
1615 tm.tm_mday = day;
1616 tm.tm_hour = hour;
1617 tm.tm_min = minute;
1618 tm.tm_sec = second;
1619 tm.tm_isdst = -1; /* daylight saving time info not known */
1621 /* Get seconds from this time */
1622 *secs = mktime(&tm);
1624 /* Multiply 4 digits given to get micro-seconds */
1625 *usecs = *usecs * 100;
1627 return TRUE;
1630 /* Free the data allocated inside a line_prefix_info_t */
1631 static gboolean
1632 free_line_prefix_info(gpointer key, gpointer value,
1633 gpointer user_data _U_)
1635 line_prefix_info_t *info = (line_prefix_info_t*)value;
1637 /* Free the 64-bit key value */
1638 g_free(key);
1640 /* Free the strings inside */
1641 g_free(info->before_time);
1642 if (info->after_time) {
1643 g_free(info->after_time);
1646 /* And the structure itself */
1647 g_free(info);
1649 /* Item will always be removed from table */
1650 return TRUE;